video.novtt.js 695 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827
  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. (function (global, factory) {
  9. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  10. typeof define === 'function' && define.amd ? define(factory) :
  11. (global.videojs = factory());
  12. }(this, (function () {
  13. var version = "6.13.0";
  14. var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
  15. function createCommonjsModule(fn, module) {
  16. return module = { exports: {} }, fn(module, module.exports), module.exports;
  17. }
  18. var win;
  19. if (typeof window !== "undefined") {
  20. win = window;
  21. } else if (typeof commonjsGlobal !== "undefined") {
  22. win = commonjsGlobal;
  23. } else if (typeof self !== "undefined"){
  24. win = self;
  25. } else {
  26. win = {};
  27. }
  28. var window_1 = win;
  29. var empty = {};
  30. var empty$1 = (Object.freeze || Object)({
  31. 'default': empty
  32. });
  33. var minDoc = ( empty$1 && empty ) || empty$1;
  34. var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal :
  35. typeof window !== 'undefined' ? window : {};
  36. var doccy;
  37. if (typeof document !== 'undefined') {
  38. doccy = document;
  39. } else {
  40. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
  41. if (!doccy) {
  42. doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
  43. }
  44. }
  45. var document_1 = doccy;
  46. /**
  47. * @file browser.js
  48. * @module browser
  49. */
  50. var USER_AGENT = window_1.navigator && window_1.navigator.userAgent || '';
  51. var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
  52. var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
  53. /*
  54. * Device is an iPhone
  55. *
  56. * @type {Boolean}
  57. * @constant
  58. * @private
  59. */
  60. var IS_IPAD = /iPad/i.test(USER_AGENT);
  61. // The Facebook app's UIWebView identifies as both an iPhone and iPad, so
  62. // to identify iPhones, we need to exclude iPads.
  63. // http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
  64. var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
  65. var IS_IPOD = /iPod/i.test(USER_AGENT);
  66. var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
  67. var IOS_VERSION = function () {
  68. var match = USER_AGENT.match(/OS (\d+)_/i);
  69. if (match && match[1]) {
  70. return match[1];
  71. }
  72. return null;
  73. }();
  74. var IS_ANDROID = /Android/i.test(USER_AGENT);
  75. var ANDROID_VERSION = function () {
  76. // This matches Android Major.Minor.Patch versions
  77. // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
  78. var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
  79. if (!match) {
  80. return null;
  81. }
  82. var major = match[1] && parseFloat(match[1]);
  83. var minor = match[2] && parseFloat(match[2]);
  84. if (major && minor) {
  85. return parseFloat(match[1] + '.' + match[2]);
  86. } else if (major) {
  87. return major;
  88. }
  89. return null;
  90. }();
  91. // Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
  92. var IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3;
  93. var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
  94. var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
  95. var IS_EDGE = /Edge/i.test(USER_AGENT);
  96. var IS_CHROME = !IS_EDGE && (/Chrome/i.test(USER_AGENT) || /CriOS/i.test(USER_AGENT));
  97. var CHROME_VERSION = function () {
  98. var match = USER_AGENT.match(/(Chrome|CriOS)\/(\d+)/);
  99. if (match && match[2]) {
  100. return parseFloat(match[2]);
  101. }
  102. return null;
  103. }();
  104. var IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT);
  105. var IE_VERSION = function () {
  106. var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
  107. var version = result && parseFloat(result[1]);
  108. if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
  109. // IE 11 has a different user agent string than other IE versions
  110. version = 11.0;
  111. }
  112. return version;
  113. }();
  114. var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
  115. var IS_ANY_SAFARI = (IS_SAFARI || IS_IOS) && !IS_CHROME;
  116. var TOUCH_ENABLED = isReal() && ('ontouchstart' in window_1 || window_1.navigator.maxTouchPoints || window_1.DocumentTouch && window_1.document instanceof window_1.DocumentTouch);
  117. var BACKGROUND_SIZE_SUPPORTED = isReal() && 'backgroundSize' in window_1.document.createElement('video').style;
  118. var browser = (Object.freeze || Object)({
  119. IS_IPAD: IS_IPAD,
  120. IS_IPHONE: IS_IPHONE,
  121. IS_IPOD: IS_IPOD,
  122. IS_IOS: IS_IOS,
  123. IOS_VERSION: IOS_VERSION,
  124. IS_ANDROID: IS_ANDROID,
  125. ANDROID_VERSION: ANDROID_VERSION,
  126. IS_OLD_ANDROID: IS_OLD_ANDROID,
  127. IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
  128. IS_FIREFOX: IS_FIREFOX,
  129. IS_EDGE: IS_EDGE,
  130. IS_CHROME: IS_CHROME,
  131. CHROME_VERSION: CHROME_VERSION,
  132. IS_IE8: IS_IE8,
  133. IE_VERSION: IE_VERSION,
  134. IS_SAFARI: IS_SAFARI,
  135. IS_ANY_SAFARI: IS_ANY_SAFARI,
  136. TOUCH_ENABLED: TOUCH_ENABLED,
  137. BACKGROUND_SIZE_SUPPORTED: BACKGROUND_SIZE_SUPPORTED
  138. });
  139. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  140. return typeof obj;
  141. } : function (obj) {
  142. return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
  143. };
  144. var classCallCheck = function (instance, Constructor) {
  145. if (!(instance instanceof Constructor)) {
  146. throw new TypeError("Cannot call a class as a function");
  147. }
  148. };
  149. var inherits = function (subClass, superClass) {
  150. if (typeof superClass !== "function" && superClass !== null) {
  151. throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  152. }
  153. subClass.prototype = Object.create(superClass && superClass.prototype, {
  154. constructor: {
  155. value: subClass,
  156. enumerable: false,
  157. writable: true,
  158. configurable: true
  159. }
  160. });
  161. if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
  162. };
  163. var possibleConstructorReturn = function (self, call) {
  164. if (!self) {
  165. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  166. }
  167. return call && (typeof call === "object" || typeof call === "function") ? call : self;
  168. };
  169. var taggedTemplateLiteralLoose = function (strings, raw) {
  170. strings.raw = raw;
  171. return strings;
  172. };
  173. /**
  174. * @file obj.js
  175. * @module obj
  176. */
  177. /**
  178. * @callback obj:EachCallback
  179. *
  180. * @param {Mixed} value
  181. * The current key for the object that is being iterated over.
  182. *
  183. * @param {string} key
  184. * The current key-value for object that is being iterated over
  185. */
  186. /**
  187. * @callback obj:ReduceCallback
  188. *
  189. * @param {Mixed} accum
  190. * The value that is accumulating over the reduce loop.
  191. *
  192. * @param {Mixed} value
  193. * The current key for the object that is being iterated over.
  194. *
  195. * @param {string} key
  196. * The current key-value for object that is being iterated over
  197. *
  198. * @return {Mixed}
  199. * The new accumulated value.
  200. */
  201. var toString = Object.prototype.toString;
  202. /**
  203. * Get the keys of an Object
  204. *
  205. * @param {Object}
  206. * The Object to get the keys from
  207. *
  208. * @return {string[]}
  209. * An array of the keys from the object. Returns an empty array if the
  210. * object passed in was invalid or had no keys.
  211. *
  212. * @private
  213. */
  214. var keys = function keys(object) {
  215. return isObject(object) ? Object.keys(object) : [];
  216. };
  217. /**
  218. * Array-like iteration for objects.
  219. *
  220. * @param {Object} object
  221. * The object to iterate over
  222. *
  223. * @param {obj:EachCallback} fn
  224. * The callback function which is called for each key in the object.
  225. */
  226. function each(object, fn) {
  227. keys(object).forEach(function (key) {
  228. return fn(object[key], key);
  229. });
  230. }
  231. /**
  232. * Array-like reduce for objects.
  233. *
  234. * @param {Object} object
  235. * The Object that you want to reduce.
  236. *
  237. * @param {Function} fn
  238. * A callback function which is called for each key in the object. It
  239. * receives the accumulated value and the per-iteration value and key
  240. * as arguments.
  241. *
  242. * @param {Mixed} [initial = 0]
  243. * Starting value
  244. *
  245. * @return {Mixed}
  246. * The final accumulated value.
  247. */
  248. function reduce(object, fn) {
  249. var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
  250. return keys(object).reduce(function (accum, key) {
  251. return fn(accum, object[key], key);
  252. }, initial);
  253. }
  254. /**
  255. * Object.assign-style object shallow merge/extend.
  256. *
  257. * @param {Object} target
  258. * @param {Object} ...sources
  259. * @return {Object}
  260. */
  261. function assign(target) {
  262. for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  263. sources[_key - 1] = arguments[_key];
  264. }
  265. if (Object.assign) {
  266. return Object.assign.apply(Object, [target].concat(sources));
  267. }
  268. sources.forEach(function (source) {
  269. if (!source) {
  270. return;
  271. }
  272. each(source, function (value, key) {
  273. target[key] = value;
  274. });
  275. });
  276. return target;
  277. }
  278. /**
  279. * Returns whether a value is an object of any kind - including DOM nodes,
  280. * arrays, regular expressions, etc. Not functions, though.
  281. *
  282. * This avoids the gotcha where using `typeof` on a `null` value
  283. * results in `'object'`.
  284. *
  285. * @param {Object} value
  286. * @return {Boolean}
  287. */
  288. function isObject(value) {
  289. return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object';
  290. }
  291. /**
  292. * Returns whether an object appears to be a "plain" object - that is, a
  293. * direct instance of `Object`.
  294. *
  295. * @param {Object} value
  296. * @return {Boolean}
  297. */
  298. function isPlain(value) {
  299. return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
  300. }
  301. /**
  302. * @file create-logger.js
  303. * @module create-logger
  304. */
  305. // This is the private tracking variable for the logging history.
  306. var history = [];
  307. /**
  308. * Log messages to the console and history based on the type of message
  309. *
  310. * @private
  311. * @param {string} type
  312. * The name of the console method to use.
  313. *
  314. * @param {Array} args
  315. * The arguments to be passed to the matching console method.
  316. */
  317. var LogByTypeFactory = function LogByTypeFactory(name, log) {
  318. return function (type, level, args, stringify) {
  319. var lvl = log.levels[level];
  320. var lvlRegExp = new RegExp('^(' + lvl + ')$');
  321. if (type !== 'log') {
  322. // Add the type to the front of the message when it's not "log".
  323. args.unshift(type.toUpperCase() + ':');
  324. }
  325. // Add console prefix after adding to history.
  326. args.unshift(name + ':');
  327. // Add a clone of the args at this point to history.
  328. if (history) {
  329. history.push([].concat(args));
  330. }
  331. // If there's no console then don't try to output messages, but they will
  332. // still be stored in history.
  333. if (!window_1.console) {
  334. return;
  335. }
  336. // Was setting these once outside of this function, but containing them
  337. // in the function makes it easier to test cases where console doesn't exist
  338. // when the module is executed.
  339. var fn = window_1.console[type];
  340. if (!fn && type === 'debug') {
  341. // Certain browsers don't have support for console.debug. For those, we
  342. // should default to the closest comparable log.
  343. fn = window_1.console.info || window_1.console.log;
  344. }
  345. // Bail out if there's no console or if this type is not allowed by the
  346. // current logging level.
  347. if (!fn || !lvl || !lvlRegExp.test(type)) {
  348. return;
  349. }
  350. // IEs previous to 11 log objects uselessly as "[object Object]"; so, JSONify
  351. // objects and arrays for those less-capable browsers.
  352. if (stringify) {
  353. args = args.map(function (a) {
  354. if (isObject(a) || Array.isArray(a)) {
  355. try {
  356. return JSON.stringify(a);
  357. } catch (x) {
  358. return String(a);
  359. }
  360. }
  361. // Cast to string before joining, so we get null and undefined explicitly
  362. // included in output (as we would in a modern console).
  363. return String(a);
  364. }).join(' ');
  365. }
  366. // Old IE versions do not allow .apply() for console methods (they are
  367. // reported as objects rather than functions).
  368. if (!fn.apply) {
  369. fn(args);
  370. } else {
  371. fn[Array.isArray(args) ? 'apply' : 'call'](window_1.console, args);
  372. }
  373. };
  374. };
  375. function createLogger$1(name) {
  376. // This is the private tracking variable for logging level.
  377. var level = 'info';
  378. // the curried logByType bound to the specific log and history
  379. var logByType = void 0;
  380. /**
  381. * Logs plain debug messages. Similar to `console.log`.
  382. *
  383. * Due to [limitations](https://github.com/jsdoc3/jsdoc/issues/955#issuecomment-313829149)
  384. * of our JSDoc template, we cannot properly document this as both a function
  385. * and a namespace, so its function signature is documented here.
  386. *
  387. * #### Arguments
  388. * ##### *args
  389. * Mixed[]
  390. *
  391. * Any combination of values that could be passed to `console.log()`.
  392. *
  393. * #### Return Value
  394. *
  395. * `undefined`
  396. *
  397. * @namespace
  398. * @param {Mixed[]} args
  399. * One or more messages or objects that should be logged.
  400. */
  401. var log = function log() {
  402. var stringify = log.stringify || IE_VERSION && IE_VERSION < 11;
  403. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  404. args[_key] = arguments[_key];
  405. }
  406. logByType('log', level, args, stringify);
  407. };
  408. // This is the logByType helper that the logging methods below use
  409. logByType = LogByTypeFactory(name, log);
  410. /**
  411. * Create a new sublogger which chains the old name to the new name.
  412. *
  413. * For example, doing `videojs.log.createLogger('player')` and then using that logger will log the following:
  414. * ```js
  415. * mylogger('foo');
  416. * // > VIDEOJS: player: foo
  417. * ```
  418. *
  419. * @param {string} name
  420. * The name to add call the new logger
  421. * @return {Object}
  422. */
  423. log.createLogger = function (subname) {
  424. return createLogger$1(name + ': ' + subname);
  425. };
  426. /**
  427. * Enumeration of available logging levels, where the keys are the level names
  428. * and the values are `|`-separated strings containing logging methods allowed
  429. * in that logging level. These strings are used to create a regular expression
  430. * matching the function name being called.
  431. *
  432. * Levels provided by Video.js are:
  433. *
  434. * - `off`: Matches no calls. Any value that can be cast to `false` will have
  435. * this effect. The most restrictive.
  436. * - `all`: Matches only Video.js-provided functions (`debug`, `log`,
  437. * `log.warn`, and `log.error`).
  438. * - `debug`: Matches `log.debug`, `log`, `log.warn`, and `log.error` calls.
  439. * - `info` (default): Matches `log`, `log.warn`, and `log.error` calls.
  440. * - `warn`: Matches `log.warn` and `log.error` calls.
  441. * - `error`: Matches only `log.error` calls.
  442. *
  443. * @type {Object}
  444. */
  445. log.levels = {
  446. all: 'debug|log|warn|error',
  447. off: '',
  448. debug: 'debug|log|warn|error',
  449. info: 'log|warn|error',
  450. warn: 'warn|error',
  451. error: 'error',
  452. DEFAULT: level
  453. };
  454. /**
  455. * Get or set the current logging level.
  456. *
  457. * If a string matching a key from {@link module:log.levels} is provided, acts
  458. * as a setter.
  459. *
  460. * @param {string} [lvl]
  461. * Pass a valid level to set a new logging level.
  462. *
  463. * @return {string}
  464. * The current logging level.
  465. */
  466. log.level = function (lvl) {
  467. if (typeof lvl === 'string') {
  468. if (!log.levels.hasOwnProperty(lvl)) {
  469. throw new Error('"' + lvl + '" in not a valid log level');
  470. }
  471. level = lvl;
  472. }
  473. return level;
  474. };
  475. /**
  476. * Returns an array containing everything that has been logged to the history.
  477. *
  478. * This array is a shallow clone of the internal history record. However, its
  479. * contents are _not_ cloned; so, mutating objects inside this array will
  480. * mutate them in history.
  481. *
  482. * @return {Array}
  483. */
  484. log.history = function () {
  485. return history ? [].concat(history) : [];
  486. };
  487. /**
  488. * Allows you to filter the history by the given logger name
  489. *
  490. * @param {string} fname
  491. * The name to filter by
  492. *
  493. * @return {Array}
  494. * The filtered list to return
  495. */
  496. log.history.filter = function (fname) {
  497. return (history || []).filter(function (historyItem) {
  498. // if the first item in each historyItem includes `fname`, then it's a match
  499. return new RegExp('.*' + fname + '.*').test(historyItem[0]);
  500. });
  501. };
  502. /**
  503. * Clears the internal history tracking, but does not prevent further history
  504. * tracking.
  505. */
  506. log.history.clear = function () {
  507. if (history) {
  508. history.length = 0;
  509. }
  510. };
  511. /**
  512. * Disable history tracking if it is currently enabled.
  513. */
  514. log.history.disable = function () {
  515. if (history !== null) {
  516. history.length = 0;
  517. history = null;
  518. }
  519. };
  520. /**
  521. * Enable history tracking if it is currently disabled.
  522. */
  523. log.history.enable = function () {
  524. if (history === null) {
  525. history = [];
  526. }
  527. };
  528. /**
  529. * Logs error messages. Similar to `console.error`.
  530. *
  531. * @param {Mixed[]} args
  532. * One or more messages or objects that should be logged as an error
  533. */
  534. log.error = function () {
  535. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  536. args[_key2] = arguments[_key2];
  537. }
  538. return logByType('error', level, args);
  539. };
  540. /**
  541. * Logs warning messages. Similar to `console.warn`.
  542. *
  543. * @param {Mixed[]} args
  544. * One or more messages or objects that should be logged as a warning.
  545. */
  546. log.warn = function () {
  547. for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
  548. args[_key3] = arguments[_key3];
  549. }
  550. return logByType('warn', level, args);
  551. };
  552. /**
  553. * Logs debug messages. Similar to `console.debug`, but may also act as a comparable
  554. * log if `console.debug` is not available
  555. *
  556. * @param {Mixed[]} args
  557. * One or more messages or objects that should be logged as debug.
  558. */
  559. log.debug = function () {
  560. for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
  561. args[_key4] = arguments[_key4];
  562. }
  563. return logByType('debug', level, args);
  564. };
  565. return log;
  566. }
  567. /**
  568. * @file log.js
  569. * @module log
  570. */
  571. var log = createLogger$1('VIDEOJS');
  572. var createLogger = log.createLogger;
  573. function clean (s) {
  574. return s.replace(/\n\r?\s*/g, '')
  575. }
  576. var tsml = function tsml (sa) {
  577. var s = ''
  578. , i = 0;
  579. for (; i < arguments.length; i++)
  580. s += clean(sa[i]) + (arguments[i + 1] || '');
  581. return s
  582. };
  583. /**
  584. * @file computed-style.js
  585. * @module computed-style
  586. */
  587. /**
  588. * A safe getComputedStyle with an IE8 fallback.
  589. *
  590. * This is needed because in Firefox, if the player is loaded in an iframe with
  591. * `display:none`, then `getComputedStyle` returns `null`, so, we do a null-check to
  592. * make sure that the player doesn't break in these cases.
  593. *
  594. * @param {Element} el
  595. * The element you want the computed style of
  596. *
  597. * @param {string} prop
  598. * The property name you want
  599. *
  600. * @see https://bugzilla.mozilla.org/show_bug.cgi?id=548397
  601. *
  602. * @static
  603. * @const
  604. */
  605. function computedStyle(el, prop) {
  606. if (!el || !prop) {
  607. return '';
  608. }
  609. if (typeof window_1.getComputedStyle === 'function') {
  610. var cs = window_1.getComputedStyle(el);
  611. return cs ? cs[prop] : '';
  612. }
  613. return el.currentStyle[prop] || '';
  614. }
  615. 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 ', '.']);
  616. /**
  617. * @file dom.js
  618. * @module dom
  619. */
  620. /**
  621. * Detect if a value is a string with any non-whitespace characters.
  622. *
  623. * @param {string} str
  624. * The string to check
  625. *
  626. * @return {boolean}
  627. * - True if the string is non-blank
  628. * - False otherwise
  629. *
  630. */
  631. function isNonBlankString(str) {
  632. return typeof str === 'string' && /\S/.test(str);
  633. }
  634. /**
  635. * Throws an error if the passed string has whitespace. This is used by
  636. * class methods to be relatively consistent with the classList API.
  637. *
  638. * @param {string} str
  639. * The string to check for whitespace.
  640. *
  641. * @throws {Error}
  642. * Throws an error if there is whitespace in the string.
  643. *
  644. */
  645. function throwIfWhitespace(str) {
  646. if (/\s/.test(str)) {
  647. throw new Error('class has illegal whitespace characters');
  648. }
  649. }
  650. /**
  651. * Produce a regular expression for matching a className within an elements className.
  652. *
  653. * @param {string} className
  654. * The className to generate the RegExp for.
  655. *
  656. * @return {RegExp}
  657. * The RegExp that will check for a specific `className` in an elements
  658. * className.
  659. */
  660. function classRegExp(className) {
  661. return new RegExp('(^|\\s)' + className + '($|\\s)');
  662. }
  663. /**
  664. * Whether the current DOM interface appears to be real.
  665. *
  666. * @return {Boolean}
  667. */
  668. function isReal() {
  669. return (
  670. // Both document and window will never be undefined thanks to `global`.
  671. document_1 === window_1.document &&
  672. // In IE < 9, DOM methods return "object" as their type, so all we can
  673. // confidently check is that it exists.
  674. typeof document_1.createElement !== 'undefined'
  675. );
  676. }
  677. /**
  678. * Determines, via duck typing, whether or not a value is a DOM element.
  679. *
  680. * @param {Mixed} value
  681. * The thing to check
  682. *
  683. * @return {boolean}
  684. * - True if it is a DOM element
  685. * - False otherwise
  686. */
  687. function isEl(value) {
  688. return isObject(value) && value.nodeType === 1;
  689. }
  690. /**
  691. * Determines if the current DOM is embedded in an iframe.
  692. *
  693. * @return {boolean}
  694. *
  695. */
  696. function isInFrame() {
  697. // We need a try/catch here because Safari will throw errors when attempting
  698. // to get either `parent` or `self`
  699. try {
  700. return window_1.parent !== window_1.self;
  701. } catch (x) {
  702. return true;
  703. }
  704. }
  705. /**
  706. * Creates functions to query the DOM using a given method.
  707. *
  708. * @param {string} method
  709. * The method to create the query with.
  710. *
  711. * @return {Function}
  712. * The query method
  713. */
  714. function createQuerier(method) {
  715. return function (selector, context) {
  716. if (!isNonBlankString(selector)) {
  717. return document_1[method](null);
  718. }
  719. if (isNonBlankString(context)) {
  720. context = document_1.querySelector(context);
  721. }
  722. var ctx = isEl(context) ? context : document_1;
  723. return ctx[method] && ctx[method](selector);
  724. };
  725. }
  726. /**
  727. * Creates an element and applies properties.
  728. *
  729. * @param {string} [tagName='div']
  730. * Name of tag to be created.
  731. *
  732. * @param {Object} [properties={}]
  733. * Element properties to be applied.
  734. *
  735. * @param {Object} [attributes={}]
  736. * Element attributes to be applied.
  737. *
  738. * @param {String|Element|TextNode|Array|Function} [content]
  739. * Contents for the element (see: {@link dom:normalizeContent})
  740. *
  741. * @return {Element}
  742. * The element that was created.
  743. */
  744. function createEl() {
  745. var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
  746. var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  747. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  748. var content = arguments[3];
  749. var el = document_1.createElement(tagName);
  750. Object.getOwnPropertyNames(properties).forEach(function (propName) {
  751. var val = properties[propName];
  752. // See #2176
  753. // We originally were accepting both properties and attributes in the
  754. // same object, but that doesn't work so well.
  755. if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
  756. log.warn(tsml(_templateObject, propName, val));
  757. el.setAttribute(propName, val);
  758. // Handle textContent since it's not supported everywhere and we have a
  759. // method for it.
  760. } else if (propName === 'textContent') {
  761. textContent(el, val);
  762. } else {
  763. el[propName] = val;
  764. }
  765. });
  766. Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
  767. el.setAttribute(attrName, attributes[attrName]);
  768. });
  769. if (content) {
  770. appendContent(el, content);
  771. }
  772. return el;
  773. }
  774. /**
  775. * Injects text into an element, replacing any existing contents entirely.
  776. *
  777. * @param {Element} el
  778. * The element to add text content into
  779. *
  780. * @param {string} text
  781. * The text content to add.
  782. *
  783. * @return {Element}
  784. * The element with added text content.
  785. */
  786. function textContent(el, text) {
  787. if (typeof el.textContent === 'undefined') {
  788. el.innerText = text;
  789. } else {
  790. el.textContent = text;
  791. }
  792. return el;
  793. }
  794. /**
  795. * Insert an element as the first child node of another
  796. *
  797. * @param {Element} child
  798. * Element to insert
  799. *
  800. * @param {Element} parent
  801. * Element to insert child into
  802. */
  803. function prependTo(child, parent) {
  804. if (parent.firstChild) {
  805. parent.insertBefore(child, parent.firstChild);
  806. } else {
  807. parent.appendChild(child);
  808. }
  809. }
  810. /**
  811. * Check if an element has a CSS class
  812. *
  813. * @param {Element} element
  814. * Element to check
  815. *
  816. * @param {string} classToCheck
  817. * Class name to check for
  818. *
  819. * @return {boolean}
  820. * - True if the element had the class
  821. * - False otherwise.
  822. *
  823. * @throws {Error}
  824. * Throws an error if `classToCheck` has white space.
  825. */
  826. function hasClass(element, classToCheck) {
  827. throwIfWhitespace(classToCheck);
  828. if (element.classList) {
  829. return element.classList.contains(classToCheck);
  830. }
  831. return classRegExp(classToCheck).test(element.className);
  832. }
  833. /**
  834. * Add a CSS class name to an element
  835. *
  836. * @param {Element} element
  837. * Element to add class name to.
  838. *
  839. * @param {string} classToAdd
  840. * Class name to add.
  841. *
  842. * @return {Element}
  843. * The dom element with the added class name.
  844. */
  845. function addClass(element, classToAdd) {
  846. if (element.classList) {
  847. element.classList.add(classToAdd);
  848. // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
  849. // in the case of classList not being supported.
  850. } else if (!hasClass(element, classToAdd)) {
  851. element.className = (element.className + ' ' + classToAdd).trim();
  852. }
  853. return element;
  854. }
  855. /**
  856. * Remove a CSS class name from an element
  857. *
  858. * @param {Element} element
  859. * Element to remove a class name from.
  860. *
  861. * @param {string} classToRemove
  862. * Class name to remove
  863. *
  864. * @return {Element}
  865. * The dom element with class name removed.
  866. */
  867. function removeClass(element, classToRemove) {
  868. if (element.classList) {
  869. element.classList.remove(classToRemove);
  870. } else {
  871. throwIfWhitespace(classToRemove);
  872. element.className = element.className.split(/\s+/).filter(function (c) {
  873. return c !== classToRemove;
  874. }).join(' ');
  875. }
  876. return element;
  877. }
  878. /**
  879. * The callback definition for toggleElClass.
  880. *
  881. * @callback Dom~PredicateCallback
  882. * @param {Element} element
  883. * The DOM element of the Component.
  884. *
  885. * @param {string} classToToggle
  886. * The `className` that wants to be toggled
  887. *
  888. * @return {boolean|undefined}
  889. * - If true the `classToToggle` will get added to `element`.
  890. * - If false the `classToToggle` will get removed from `element`.
  891. * - If undefined this callback will be ignored
  892. */
  893. /**
  894. * Adds or removes a CSS class name on an element depending on an optional
  895. * condition or the presence/absence of the class name.
  896. *
  897. * @param {Element} element
  898. * The element to toggle a class name on.
  899. *
  900. * @param {string} classToToggle
  901. * The class that should be toggled
  902. *
  903. * @param {boolean|PredicateCallback} [predicate]
  904. * See the return value for {@link Dom~PredicateCallback}
  905. *
  906. * @return {Element}
  907. * The element with a class that has been toggled.
  908. */
  909. function toggleClass(element, classToToggle, predicate) {
  910. // This CANNOT use `classList` internally because IE does not support the
  911. // second parameter to the `classList.toggle()` method! Which is fine because
  912. // `classList` will be used by the add/remove functions.
  913. var has = hasClass(element, classToToggle);
  914. if (typeof predicate === 'function') {
  915. predicate = predicate(element, classToToggle);
  916. }
  917. if (typeof predicate !== 'boolean') {
  918. predicate = !has;
  919. }
  920. // If the necessary class operation matches the current state of the
  921. // element, no action is required.
  922. if (predicate === has) {
  923. return;
  924. }
  925. if (predicate) {
  926. addClass(element, classToToggle);
  927. } else {
  928. removeClass(element, classToToggle);
  929. }
  930. return element;
  931. }
  932. /**
  933. * Apply attributes to an HTML element.
  934. *
  935. * @param {Element} el
  936. * Element to add attributes to.
  937. *
  938. * @param {Object} [attributes]
  939. * Attributes to be applied.
  940. */
  941. function setAttributes(el, attributes) {
  942. Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
  943. var attrValue = attributes[attrName];
  944. if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
  945. el.removeAttribute(attrName);
  946. } else {
  947. el.setAttribute(attrName, attrValue === true ? '' : attrValue);
  948. }
  949. });
  950. }
  951. /**
  952. * Get an element's attribute values, as defined on the HTML tag
  953. * Attributes are not the same as properties. They're defined on the tag
  954. * or with setAttribute (which shouldn't be used with HTML)
  955. * This will return true or false for boolean attributes.
  956. *
  957. * @param {Element} tag
  958. * Element from which to get tag attributes.
  959. *
  960. * @return {Object}
  961. * All attributes of the element.
  962. */
  963. function getAttributes(tag) {
  964. var obj = {};
  965. // known boolean attributes
  966. // we can check for matching boolean properties, but older browsers
  967. // won't know about HTML5 boolean attributes that we still read from
  968. var knownBooleans = ',' + 'autoplay,controls,playsinline,loop,muted,default,defaultMuted' + ',';
  969. if (tag && tag.attributes && tag.attributes.length > 0) {
  970. var attrs = tag.attributes;
  971. for (var i = attrs.length - 1; i >= 0; i--) {
  972. var attrName = attrs[i].name;
  973. var attrVal = attrs[i].value;
  974. // check for known booleans
  975. // the matching element property will return a value for typeof
  976. if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
  977. // the value of an included boolean attribute is typically an empty
  978. // string ('') which would equal false if we just check for a false value.
  979. // we also don't want support bad code like autoplay='false'
  980. attrVal = attrVal !== null ? true : false;
  981. }
  982. obj[attrName] = attrVal;
  983. }
  984. }
  985. return obj;
  986. }
  987. /**
  988. * Get the value of an element's attribute
  989. *
  990. * @param {Element} el
  991. * A DOM element
  992. *
  993. * @param {string} attribute
  994. * Attribute to get the value of
  995. *
  996. * @return {string}
  997. * value of the attribute
  998. */
  999. function getAttribute(el, attribute) {
  1000. return el.getAttribute(attribute);
  1001. }
  1002. /**
  1003. * Set the value of an element's attribute
  1004. *
  1005. * @param {Element} el
  1006. * A DOM element
  1007. *
  1008. * @param {string} attribute
  1009. * Attribute to set
  1010. *
  1011. * @param {string} value
  1012. * Value to set the attribute to
  1013. */
  1014. function setAttribute(el, attribute, value) {
  1015. el.setAttribute(attribute, value);
  1016. }
  1017. /**
  1018. * Remove an element's attribute
  1019. *
  1020. * @param {Element} el
  1021. * A DOM element
  1022. *
  1023. * @param {string} attribute
  1024. * Attribute to remove
  1025. */
  1026. function removeAttribute(el, attribute) {
  1027. el.removeAttribute(attribute);
  1028. }
  1029. /**
  1030. * Attempt to block the ability to select text while dragging controls
  1031. */
  1032. function blockTextSelection() {
  1033. document_1.body.focus();
  1034. document_1.onselectstart = function () {
  1035. return false;
  1036. };
  1037. }
  1038. /**
  1039. * Turn off text selection blocking
  1040. */
  1041. function unblockTextSelection() {
  1042. document_1.onselectstart = function () {
  1043. return true;
  1044. };
  1045. }
  1046. /**
  1047. * Identical to the native `getBoundingClientRect` function, but ensures that
  1048. * the method is supported at all (it is in all browsers we claim to support)
  1049. * and that the element is in the DOM before continuing.
  1050. *
  1051. * This wrapper function also shims properties which are not provided by some
  1052. * older browsers (namely, IE8).
  1053. *
  1054. * Additionally, some browsers do not support adding properties to a
  1055. * `ClientRect`/`DOMRect` object; so, we shallow-copy it with the standard
  1056. * properties (except `x` and `y` which are not widely supported). This helps
  1057. * avoid implementations where keys are non-enumerable.
  1058. *
  1059. * @param {Element} el
  1060. * Element whose `ClientRect` we want to calculate.
  1061. *
  1062. * @return {Object|undefined}
  1063. * Always returns a plain
  1064. */
  1065. function getBoundingClientRect(el) {
  1066. if (el && el.getBoundingClientRect && el.parentNode) {
  1067. var rect = el.getBoundingClientRect();
  1068. var result = {};
  1069. ['bottom', 'height', 'left', 'right', 'top', 'width'].forEach(function (k) {
  1070. if (rect[k] !== undefined) {
  1071. result[k] = rect[k];
  1072. }
  1073. });
  1074. if (!result.height) {
  1075. result.height = parseFloat(computedStyle(el, 'height'));
  1076. }
  1077. if (!result.width) {
  1078. result.width = parseFloat(computedStyle(el, 'width'));
  1079. }
  1080. return result;
  1081. }
  1082. }
  1083. /**
  1084. * The postion of a DOM element on the page.
  1085. *
  1086. * @typedef {Object} module:dom~Position
  1087. *
  1088. * @property {number} left
  1089. * Pixels to the left
  1090. *
  1091. * @property {number} top
  1092. * Pixels on top
  1093. */
  1094. /**
  1095. * Offset Left.
  1096. * getBoundingClientRect technique from
  1097. * John Resig
  1098. *
  1099. * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
  1100. *
  1101. * @param {Element} el
  1102. * Element from which to get offset
  1103. *
  1104. * @return {module:dom~Position}
  1105. * The position of the element that was passed in.
  1106. */
  1107. function findPosition(el) {
  1108. var box = void 0;
  1109. if (el.getBoundingClientRect && el.parentNode) {
  1110. box = el.getBoundingClientRect();
  1111. }
  1112. if (!box) {
  1113. return {
  1114. left: 0,
  1115. top: 0
  1116. };
  1117. }
  1118. var docEl = document_1.documentElement;
  1119. var body = document_1.body;
  1120. var clientLeft = docEl.clientLeft || body.clientLeft || 0;
  1121. var scrollLeft = window_1.pageXOffset || body.scrollLeft;
  1122. var left = box.left + scrollLeft - clientLeft;
  1123. var clientTop = docEl.clientTop || body.clientTop || 0;
  1124. var scrollTop = window_1.pageYOffset || body.scrollTop;
  1125. var top = box.top + scrollTop - clientTop;
  1126. // Android sometimes returns slightly off decimal values, so need to round
  1127. return {
  1128. left: Math.round(left),
  1129. top: Math.round(top)
  1130. };
  1131. }
  1132. /**
  1133. * x and y coordinates for a dom element or mouse pointer
  1134. *
  1135. * @typedef {Object} Dom~Coordinates
  1136. *
  1137. * @property {number} x
  1138. * x coordinate in pixels
  1139. *
  1140. * @property {number} y
  1141. * y coordinate in pixels
  1142. */
  1143. /**
  1144. * Get pointer position in element
  1145. * Returns an object with x and y coordinates.
  1146. * The base on the coordinates are the bottom left of the element.
  1147. *
  1148. * @param {Element} el
  1149. * Element on which to get the pointer position on
  1150. *
  1151. * @param {EventTarget~Event} event
  1152. * Event object
  1153. *
  1154. * @return {Dom~Coordinates}
  1155. * A Coordinates object corresponding to the mouse position.
  1156. *
  1157. */
  1158. function getPointerPosition(el, event) {
  1159. var position = {};
  1160. var box = findPosition(el);
  1161. var boxW = el.offsetWidth;
  1162. var boxH = el.offsetHeight;
  1163. var boxY = box.top;
  1164. var boxX = box.left;
  1165. var pageY = event.pageY;
  1166. var pageX = event.pageX;
  1167. if (event.changedTouches) {
  1168. pageX = event.changedTouches[0].pageX;
  1169. pageY = event.changedTouches[0].pageY;
  1170. }
  1171. position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
  1172. position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
  1173. return position;
  1174. }
  1175. /**
  1176. * Determines, via duck typing, whether or not a value is a text node.
  1177. *
  1178. * @param {Mixed} value
  1179. * Check if this value is a text node.
  1180. *
  1181. * @return {boolean}
  1182. * - True if it is a text node
  1183. * - False otherwise
  1184. */
  1185. function isTextNode(value) {
  1186. return isObject(value) && value.nodeType === 3;
  1187. }
  1188. /**
  1189. * Empties the contents of an element.
  1190. *
  1191. * @param {Element} el
  1192. * The element to empty children from
  1193. *
  1194. * @return {Element}
  1195. * The element with no children
  1196. */
  1197. function emptyEl(el) {
  1198. while (el.firstChild) {
  1199. el.removeChild(el.firstChild);
  1200. }
  1201. return el;
  1202. }
  1203. /**
  1204. * Normalizes content for eventual insertion into the DOM.
  1205. *
  1206. * This allows a wide range of content definition methods, but protects
  1207. * from falling into the trap of simply writing to `innerHTML`, which is
  1208. * an XSS concern.
  1209. *
  1210. * The content for an element can be passed in multiple types and
  1211. * combinations, whose behavior is as follows:
  1212. *
  1213. * @param {String|Element|TextNode|Array|Function} content
  1214. * - String: Normalized into a text node.
  1215. * - Element/TextNode: Passed through.
  1216. * - Array: A one-dimensional array of strings, elements, nodes, or functions
  1217. * (which return single strings, elements, or nodes).
  1218. * - Function: If the sole argument, is expected to produce a string, element,
  1219. * node, or array as defined above.
  1220. *
  1221. * @return {Array}
  1222. * All of the content that was passed in normalized.
  1223. */
  1224. function normalizeContent(content) {
  1225. // First, invoke content if it is a function. If it produces an array,
  1226. // that needs to happen before normalization.
  1227. if (typeof content === 'function') {
  1228. content = content();
  1229. }
  1230. // Next up, normalize to an array, so one or many items can be normalized,
  1231. // filtered, and returned.
  1232. return (Array.isArray(content) ? content : [content]).map(function (value) {
  1233. // First, invoke value if it is a function to produce a new value,
  1234. // which will be subsequently normalized to a Node of some kind.
  1235. if (typeof value === 'function') {
  1236. value = value();
  1237. }
  1238. if (isEl(value) || isTextNode(value)) {
  1239. return value;
  1240. }
  1241. if (typeof value === 'string' && /\S/.test(value)) {
  1242. return document_1.createTextNode(value);
  1243. }
  1244. }).filter(function (value) {
  1245. return value;
  1246. });
  1247. }
  1248. /**
  1249. * Normalizes and appends content to an element.
  1250. *
  1251. * @param {Element} el
  1252. * Element to append normalized content to.
  1253. *
  1254. *
  1255. * @param {String|Element|TextNode|Array|Function} content
  1256. * See the `content` argument of {@link dom:normalizeContent}
  1257. *
  1258. * @return {Element}
  1259. * The element with appended normalized content.
  1260. */
  1261. function appendContent(el, content) {
  1262. normalizeContent(content).forEach(function (node) {
  1263. return el.appendChild(node);
  1264. });
  1265. return el;
  1266. }
  1267. /**
  1268. * Normalizes and inserts content into an element; this is identical to
  1269. * `appendContent()`, except it empties the element first.
  1270. *
  1271. * @param {Element} el
  1272. * Element to insert normalized content into.
  1273. *
  1274. * @param {String|Element|TextNode|Array|Function} content
  1275. * See the `content` argument of {@link dom:normalizeContent}
  1276. *
  1277. * @return {Element}
  1278. * The element with inserted normalized content.
  1279. *
  1280. */
  1281. function insertContent(el, content) {
  1282. return appendContent(emptyEl(el), content);
  1283. }
  1284. /**
  1285. * Check if event was a single left click
  1286. *
  1287. * @param {EventTarget~Event} event
  1288. * Event object
  1289. *
  1290. * @return {boolean}
  1291. * - True if a left click
  1292. * - False if not a left click
  1293. */
  1294. function isSingleLeftClick(event) {
  1295. // Note: if you create something draggable, be sure to
  1296. // call it on both `mousedown` and `mousemove` event,
  1297. // otherwise `mousedown` should be enough for a button
  1298. if (event.button === undefined && event.buttons === undefined) {
  1299. // Why do we need `buttons` ?
  1300. // Because, middle mouse sometimes have this:
  1301. // e.button === 0 and e.buttons === 4
  1302. // Furthermore, we want to prevent combination click, something like
  1303. // HOLD middlemouse then left click, that would be
  1304. // e.button === 0, e.buttons === 5
  1305. // just `button` is not gonna work
  1306. // Alright, then what this block does ?
  1307. // this is for chrome `simulate mobile devices`
  1308. // I want to support this as well
  1309. return true;
  1310. }
  1311. if (event.button === 0 && event.buttons === undefined) {
  1312. // Touch screen, sometimes on some specific device, `buttons`
  1313. // doesn't have anything (safari on ios, blackberry...)
  1314. return true;
  1315. }
  1316. if (IE_VERSION === 9) {
  1317. // Ignore IE9
  1318. return true;
  1319. }
  1320. if (event.button !== 0 || event.buttons !== 1) {
  1321. // This is the reason we have those if else block above
  1322. // if any special case we can catch and let it slide
  1323. // we do it above, when get to here, this definitely
  1324. // is-not-left-click
  1325. return false;
  1326. }
  1327. return true;
  1328. }
  1329. /**
  1330. * Finds a single DOM element matching `selector` within the optional
  1331. * `context` of another DOM element (defaulting to `document`).
  1332. *
  1333. * @param {string} selector
  1334. * A valid CSS selector, which will be passed to `querySelector`.
  1335. *
  1336. * @param {Element|String} [context=document]
  1337. * A DOM element within which to query. Can also be a selector
  1338. * string in which case the first matching element will be used
  1339. * as context. If missing (or no element matches selector), falls
  1340. * back to `document`.
  1341. *
  1342. * @return {Element|null}
  1343. * The element that was found or null.
  1344. */
  1345. var $ = createQuerier('querySelector');
  1346. /**
  1347. * Finds a all DOM elements matching `selector` within the optional
  1348. * `context` of another DOM element (defaulting to `document`).
  1349. *
  1350. * @param {string} selector
  1351. * A valid CSS selector, which will be passed to `querySelectorAll`.
  1352. *
  1353. * @param {Element|String} [context=document]
  1354. * A DOM element within which to query. Can also be a selector
  1355. * string in which case the first matching element will be used
  1356. * as context. If missing (or no element matches selector), falls
  1357. * back to `document`.
  1358. *
  1359. * @return {NodeList}
  1360. * A element list of elements that were found. Will be empty if none were found.
  1361. *
  1362. */
  1363. var $$ = createQuerier('querySelectorAll');
  1364. var Dom = (Object.freeze || Object)({
  1365. isReal: isReal,
  1366. isEl: isEl,
  1367. isInFrame: isInFrame,
  1368. createEl: createEl,
  1369. textContent: textContent,
  1370. prependTo: prependTo,
  1371. hasClass: hasClass,
  1372. addClass: addClass,
  1373. removeClass: removeClass,
  1374. toggleClass: toggleClass,
  1375. setAttributes: setAttributes,
  1376. getAttributes: getAttributes,
  1377. getAttribute: getAttribute,
  1378. setAttribute: setAttribute,
  1379. removeAttribute: removeAttribute,
  1380. blockTextSelection: blockTextSelection,
  1381. unblockTextSelection: unblockTextSelection,
  1382. getBoundingClientRect: getBoundingClientRect,
  1383. findPosition: findPosition,
  1384. getPointerPosition: getPointerPosition,
  1385. isTextNode: isTextNode,
  1386. emptyEl: emptyEl,
  1387. normalizeContent: normalizeContent,
  1388. appendContent: appendContent,
  1389. insertContent: insertContent,
  1390. isSingleLeftClick: isSingleLeftClick,
  1391. $: $,
  1392. $$: $$
  1393. });
  1394. /**
  1395. * @file guid.js
  1396. * @module guid
  1397. */
  1398. /**
  1399. * Unique ID for an element or function
  1400. * @type {Number}
  1401. */
  1402. var _guid = 1;
  1403. /**
  1404. * Get a unique auto-incrementing ID by number that has not been returned before.
  1405. *
  1406. * @return {number}
  1407. * A new unique ID.
  1408. */
  1409. function newGUID() {
  1410. return _guid++;
  1411. }
  1412. /**
  1413. * @file dom-data.js
  1414. * @module dom-data
  1415. */
  1416. /**
  1417. * Element Data Store.
  1418. *
  1419. * Allows for binding data to an element without putting it directly on the
  1420. * element. Ex. Event listeners are stored here.
  1421. * (also from jsninja.com, slightly modified and updated for closure compiler)
  1422. *
  1423. * @type {Object}
  1424. * @private
  1425. */
  1426. var elData = {};
  1427. /*
  1428. * Unique attribute name to store an element's guid in
  1429. *
  1430. * @type {String}
  1431. * @constant
  1432. * @private
  1433. */
  1434. var elIdAttr = 'vdata' + new Date().getTime();
  1435. /**
  1436. * Returns the cache object where data for an element is stored
  1437. *
  1438. * @param {Element} el
  1439. * Element to store data for.
  1440. *
  1441. * @return {Object}
  1442. * The cache object for that el that was passed in.
  1443. */
  1444. function getData(el) {
  1445. var id = el[elIdAttr];
  1446. if (!id) {
  1447. id = el[elIdAttr] = newGUID();
  1448. }
  1449. if (!elData[id]) {
  1450. elData[id] = {};
  1451. }
  1452. return elData[id];
  1453. }
  1454. /**
  1455. * Returns whether or not an element has cached data
  1456. *
  1457. * @param {Element} el
  1458. * Check if this element has cached data.
  1459. *
  1460. * @return {boolean}
  1461. * - True if the DOM element has cached data.
  1462. * - False otherwise.
  1463. */
  1464. function hasData(el) {
  1465. var id = el[elIdAttr];
  1466. if (!id) {
  1467. return false;
  1468. }
  1469. return !!Object.getOwnPropertyNames(elData[id]).length;
  1470. }
  1471. /**
  1472. * Delete data for the element from the cache and the guid attr from getElementById
  1473. *
  1474. * @param {Element} el
  1475. * Remove cached data for this element.
  1476. */
  1477. function removeData(el) {
  1478. var id = el[elIdAttr];
  1479. if (!id) {
  1480. return;
  1481. }
  1482. // Remove all stored data
  1483. delete elData[id];
  1484. // Remove the elIdAttr property from the DOM node
  1485. try {
  1486. delete el[elIdAttr];
  1487. } catch (e) {
  1488. if (el.removeAttribute) {
  1489. el.removeAttribute(elIdAttr);
  1490. } else {
  1491. // IE doesn't appear to support removeAttribute on the document element
  1492. el[elIdAttr] = null;
  1493. }
  1494. }
  1495. }
  1496. /**
  1497. * @file events.js. An Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
  1498. * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
  1499. * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
  1500. * robust as jquery's, so there's probably some differences.
  1501. *
  1502. * @module events
  1503. */
  1504. /**
  1505. * Clean up the listener cache and dispatchers
  1506. *
  1507. * @param {Element|Object} elem
  1508. * Element to clean up
  1509. *
  1510. * @param {string} type
  1511. * Type of event to clean up
  1512. */
  1513. function _cleanUpEvents(elem, type) {
  1514. var data = getData(elem);
  1515. // Remove the events of a particular type if there are none left
  1516. if (data.handlers[type].length === 0) {
  1517. delete data.handlers[type];
  1518. // data.handlers[type] = null;
  1519. // Setting to null was causing an error with data.handlers
  1520. // Remove the meta-handler from the element
  1521. if (elem.removeEventListener) {
  1522. elem.removeEventListener(type, data.dispatcher, false);
  1523. } else if (elem.detachEvent) {
  1524. elem.detachEvent('on' + type, data.dispatcher);
  1525. }
  1526. }
  1527. // Remove the events object if there are no types left
  1528. if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
  1529. delete data.handlers;
  1530. delete data.dispatcher;
  1531. delete data.disabled;
  1532. }
  1533. // Finally remove the element data if there is no data left
  1534. if (Object.getOwnPropertyNames(data).length === 0) {
  1535. removeData(elem);
  1536. }
  1537. }
  1538. /**
  1539. * Loops through an array of event types and calls the requested method for each type.
  1540. *
  1541. * @param {Function} fn
  1542. * The event method we want to use.
  1543. *
  1544. * @param {Element|Object} elem
  1545. * Element or object to bind listeners to
  1546. *
  1547. * @param {string} type
  1548. * Type of event to bind to.
  1549. *
  1550. * @param {EventTarget~EventListener} callback
  1551. * Event listener.
  1552. */
  1553. function _handleMultipleEvents(fn, elem, types, callback) {
  1554. types.forEach(function (type) {
  1555. // Call the event method for each one of the types
  1556. fn(elem, type, callback);
  1557. });
  1558. }
  1559. /**
  1560. * Fix a native event to have standard property values
  1561. *
  1562. * @param {Object} event
  1563. * Event object to fix.
  1564. *
  1565. * @return {Object}
  1566. * Fixed event object.
  1567. */
  1568. function fixEvent(event) {
  1569. function returnTrue() {
  1570. return true;
  1571. }
  1572. function returnFalse() {
  1573. return false;
  1574. }
  1575. // Test if fixing up is needed
  1576. // Used to check if !event.stopPropagation instead of isPropagationStopped
  1577. // But native events return true for stopPropagation, but don't have
  1578. // other expected methods like isPropagationStopped. Seems to be a problem
  1579. // with the Javascript Ninja code. So we're just overriding all events now.
  1580. if (!event || !event.isPropagationStopped) {
  1581. var old = event || window_1.event;
  1582. event = {};
  1583. // Clone the old object so that we can modify the values event = {};
  1584. // IE8 Doesn't like when you mess with native event properties
  1585. // Firefox returns false for event.hasOwnProperty('type') and other props
  1586. // which makes copying more difficult.
  1587. // TODO: Probably best to create a whitelist of event props
  1588. for (var key in old) {
  1589. // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
  1590. // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
  1591. // and webkitMovementX/Y
  1592. if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
  1593. // Chrome 32+ warns if you try to copy deprecated returnValue, but
  1594. // we still want to if preventDefault isn't supported (IE8).
  1595. if (!(key === 'returnValue' && old.preventDefault)) {
  1596. event[key] = old[key];
  1597. }
  1598. }
  1599. }
  1600. // The event occurred on this element
  1601. if (!event.target) {
  1602. event.target = event.srcElement || document_1;
  1603. }
  1604. // Handle which other element the event is related to
  1605. if (!event.relatedTarget) {
  1606. event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
  1607. }
  1608. // Stop the default browser action
  1609. event.preventDefault = function () {
  1610. if (old.preventDefault) {
  1611. old.preventDefault();
  1612. }
  1613. event.returnValue = false;
  1614. old.returnValue = false;
  1615. event.defaultPrevented = true;
  1616. };
  1617. event.defaultPrevented = false;
  1618. // Stop the event from bubbling
  1619. event.stopPropagation = function () {
  1620. if (old.stopPropagation) {
  1621. old.stopPropagation();
  1622. }
  1623. event.cancelBubble = true;
  1624. old.cancelBubble = true;
  1625. event.isPropagationStopped = returnTrue;
  1626. };
  1627. event.isPropagationStopped = returnFalse;
  1628. // Stop the event from bubbling and executing other handlers
  1629. event.stopImmediatePropagation = function () {
  1630. if (old.stopImmediatePropagation) {
  1631. old.stopImmediatePropagation();
  1632. }
  1633. event.isImmediatePropagationStopped = returnTrue;
  1634. event.stopPropagation();
  1635. };
  1636. event.isImmediatePropagationStopped = returnFalse;
  1637. // Handle mouse position
  1638. if (event.clientX !== null && event.clientX !== undefined) {
  1639. var doc = document_1.documentElement;
  1640. var body = document_1.body;
  1641. event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
  1642. event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
  1643. }
  1644. // Handle key presses
  1645. event.which = event.charCode || event.keyCode;
  1646. // Fix button for mouse clicks:
  1647. // 0 == left; 1 == middle; 2 == right
  1648. if (event.button !== null && event.button !== undefined) {
  1649. // The following is disabled because it does not pass videojs-standard
  1650. // and... yikes.
  1651. /* eslint-disable */
  1652. event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
  1653. /* eslint-enable */
  1654. }
  1655. }
  1656. // Returns fixed-up instance
  1657. return event;
  1658. }
  1659. /**
  1660. * Whether passive event listeners are supported
  1661. */
  1662. var _supportsPassive = false;
  1663. (function () {
  1664. try {
  1665. var opts = Object.defineProperty({}, 'passive', {
  1666. get: function get() {
  1667. _supportsPassive = true;
  1668. }
  1669. });
  1670. window_1.addEventListener('test', null, opts);
  1671. window_1.removeEventListener('test', null, opts);
  1672. } catch (e) {
  1673. // disregard
  1674. }
  1675. })();
  1676. /**
  1677. * Touch events Chrome expects to be passive
  1678. */
  1679. var passiveEvents = ['touchstart', 'touchmove'];
  1680. /**
  1681. * Add an event listener to element
  1682. * It stores the handler function in a separate cache object
  1683. * and adds a generic handler to the element's event,
  1684. * along with a unique id (guid) to the element.
  1685. *
  1686. * @param {Element|Object} elem
  1687. * Element or object to bind listeners to
  1688. *
  1689. * @param {string|string[]} type
  1690. * Type of event to bind to.
  1691. *
  1692. * @param {EventTarget~EventListener} fn
  1693. * Event listener.
  1694. */
  1695. function on(elem, type, fn) {
  1696. if (Array.isArray(type)) {
  1697. return _handleMultipleEvents(on, elem, type, fn);
  1698. }
  1699. var data = getData(elem);
  1700. // We need a place to store all our handler data
  1701. if (!data.handlers) {
  1702. data.handlers = {};
  1703. }
  1704. if (!data.handlers[type]) {
  1705. data.handlers[type] = [];
  1706. }
  1707. if (!fn.guid) {
  1708. fn.guid = newGUID();
  1709. }
  1710. data.handlers[type].push(fn);
  1711. if (!data.dispatcher) {
  1712. data.disabled = false;
  1713. data.dispatcher = function (event, hash) {
  1714. if (data.disabled) {
  1715. return;
  1716. }
  1717. event = fixEvent(event);
  1718. var handlers = data.handlers[event.type];
  1719. if (handlers) {
  1720. // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
  1721. var handlersCopy = handlers.slice(0);
  1722. for (var m = 0, n = handlersCopy.length; m < n; m++) {
  1723. if (event.isImmediatePropagationStopped()) {
  1724. break;
  1725. } else {
  1726. try {
  1727. handlersCopy[m].call(elem, event, hash);
  1728. } catch (e) {
  1729. log.error(e);
  1730. }
  1731. }
  1732. }
  1733. }
  1734. };
  1735. }
  1736. if (data.handlers[type].length === 1) {
  1737. if (elem.addEventListener) {
  1738. var options = false;
  1739. if (_supportsPassive && passiveEvents.indexOf(type) > -1) {
  1740. options = { passive: true };
  1741. }
  1742. elem.addEventListener(type, data.dispatcher, options);
  1743. } else if (elem.attachEvent) {
  1744. elem.attachEvent('on' + type, data.dispatcher);
  1745. }
  1746. }
  1747. }
  1748. /**
  1749. * Removes event listeners from an element
  1750. *
  1751. * @param {Element|Object} elem
  1752. * Object to remove listeners from.
  1753. *
  1754. * @param {string|string[]} [type]
  1755. * Type of listener to remove. Don't include to remove all events from element.
  1756. *
  1757. * @param {EventTarget~EventListener} [fn]
  1758. * Specific listener to remove. Don't include to remove listeners for an event
  1759. * type.
  1760. */
  1761. function off(elem, type, fn) {
  1762. // Don't want to add a cache object through getElData if not needed
  1763. if (!hasData(elem)) {
  1764. return;
  1765. }
  1766. var data = getData(elem);
  1767. // If no events exist, nothing to unbind
  1768. if (!data.handlers) {
  1769. return;
  1770. }
  1771. if (Array.isArray(type)) {
  1772. return _handleMultipleEvents(off, elem, type, fn);
  1773. }
  1774. // Utility function
  1775. var removeType = function removeType(el, t) {
  1776. data.handlers[t] = [];
  1777. _cleanUpEvents(el, t);
  1778. };
  1779. // Are we removing all bound events?
  1780. if (type === undefined) {
  1781. for (var t in data.handlers) {
  1782. if (Object.prototype.hasOwnProperty.call(data.handlers || {}, t)) {
  1783. removeType(elem, t);
  1784. }
  1785. }
  1786. return;
  1787. }
  1788. var handlers = data.handlers[type];
  1789. // If no handlers exist, nothing to unbind
  1790. if (!handlers) {
  1791. return;
  1792. }
  1793. // If no listener was provided, remove all listeners for type
  1794. if (!fn) {
  1795. removeType(elem, type);
  1796. return;
  1797. }
  1798. // We're only removing a single handler
  1799. if (fn.guid) {
  1800. for (var n = 0; n < handlers.length; n++) {
  1801. if (handlers[n].guid === fn.guid) {
  1802. handlers.splice(n--, 1);
  1803. }
  1804. }
  1805. }
  1806. _cleanUpEvents(elem, type);
  1807. }
  1808. /**
  1809. * Trigger an event for an element
  1810. *
  1811. * @param {Element|Object} elem
  1812. * Element to trigger an event on
  1813. *
  1814. * @param {EventTarget~Event|string} event
  1815. * A string (the type) or an event object with a type attribute
  1816. *
  1817. * @param {Object} [hash]
  1818. * data hash to pass along with the event
  1819. *
  1820. * @return {boolean|undefined}
  1821. * - Returns the opposite of `defaultPrevented` if default was prevented
  1822. * - Otherwise returns undefined
  1823. */
  1824. function trigger(elem, event, hash) {
  1825. // Fetches element data and a reference to the parent (for bubbling).
  1826. // Don't want to add a data object to cache for every parent,
  1827. // so checking hasElData first.
  1828. var elemData = hasData(elem) ? getData(elem) : {};
  1829. var parent = elem.parentNode || elem.ownerDocument;
  1830. // type = event.type || event,
  1831. // handler;
  1832. // If an event name was passed as a string, creates an event out of it
  1833. if (typeof event === 'string') {
  1834. event = { type: event, target: elem };
  1835. } else if (!event.target) {
  1836. event.target = elem;
  1837. }
  1838. // Normalizes the event properties.
  1839. event = fixEvent(event);
  1840. // If the passed element has a dispatcher, executes the established handlers.
  1841. if (elemData.dispatcher) {
  1842. elemData.dispatcher.call(elem, event, hash);
  1843. }
  1844. // Unless explicitly stopped or the event does not bubble (e.g. media events)
  1845. // recursively calls this function to bubble the event up the DOM.
  1846. if (parent && !event.isPropagationStopped() && event.bubbles === true) {
  1847. trigger.call(null, parent, event, hash);
  1848. // If at the top of the DOM, triggers the default action unless disabled.
  1849. } else if (!parent && !event.defaultPrevented) {
  1850. var targetData = getData(event.target);
  1851. // Checks if the target has a default action for this event.
  1852. if (event.target[event.type]) {
  1853. // Temporarily disables event dispatching on the target as we have already executed the handler.
  1854. targetData.disabled = true;
  1855. // Executes the default action.
  1856. if (typeof event.target[event.type] === 'function') {
  1857. event.target[event.type]();
  1858. }
  1859. // Re-enables event dispatching.
  1860. targetData.disabled = false;
  1861. }
  1862. }
  1863. // Inform the triggerer if the default was prevented by returning false
  1864. return !event.defaultPrevented;
  1865. }
  1866. /**
  1867. * Trigger a listener only once for an event
  1868. *
  1869. * @param {Element|Object} elem
  1870. * Element or object to bind to.
  1871. *
  1872. * @param {string|string[]} type
  1873. * Name/type of event
  1874. *
  1875. * @param {Event~EventListener} fn
  1876. * Event Listener function
  1877. */
  1878. function one(elem, type, fn) {
  1879. if (Array.isArray(type)) {
  1880. return _handleMultipleEvents(one, elem, type, fn);
  1881. }
  1882. var func = function func() {
  1883. off(elem, type, func);
  1884. fn.apply(this, arguments);
  1885. };
  1886. // copy the guid to the new function so it can removed using the original function's ID
  1887. func.guid = fn.guid = fn.guid || newGUID();
  1888. on(elem, type, func);
  1889. }
  1890. var Events = (Object.freeze || Object)({
  1891. fixEvent: fixEvent,
  1892. on: on,
  1893. off: off,
  1894. trigger: trigger,
  1895. one: one
  1896. });
  1897. /**
  1898. * @file setup.js - Functions for setting up a player without
  1899. * user interaction based on the data-setup `attribute` of the video tag.
  1900. *
  1901. * @module setup
  1902. */
  1903. var _windowLoaded = false;
  1904. var videojs$2 = void 0;
  1905. /**
  1906. * Set up any tags that have a data-setup `attribute` when the player is started.
  1907. */
  1908. var autoSetup = function autoSetup() {
  1909. // Protect against breakage in non-browser environments and check global autoSetup option.
  1910. if (!isReal() || videojs$2.options.autoSetup === false) {
  1911. return;
  1912. }
  1913. // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
  1914. // var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
  1915. // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
  1916. // var mediaEls = vids.concat(audios);
  1917. // Because IE8 doesn't support calling slice on a node list, we need to loop
  1918. // through each list of elements to build up a new, combined list of elements.
  1919. var vids = document_1.getElementsByTagName('video');
  1920. var audios = document_1.getElementsByTagName('audio');
  1921. var divs = document_1.getElementsByTagName('video-js');
  1922. var mediaEls = [];
  1923. if (vids && vids.length > 0) {
  1924. for (var i = 0, e = vids.length; i < e; i++) {
  1925. mediaEls.push(vids[i]);
  1926. }
  1927. }
  1928. if (audios && audios.length > 0) {
  1929. for (var _i = 0, _e = audios.length; _i < _e; _i++) {
  1930. mediaEls.push(audios[_i]);
  1931. }
  1932. }
  1933. if (divs && divs.length > 0) {
  1934. for (var _i2 = 0, _e2 = divs.length; _i2 < _e2; _i2++) {
  1935. mediaEls.push(divs[_i2]);
  1936. }
  1937. }
  1938. // Check if any media elements exist
  1939. if (mediaEls && mediaEls.length > 0) {
  1940. for (var _i3 = 0, _e3 = mediaEls.length; _i3 < _e3; _i3++) {
  1941. var mediaEl = mediaEls[_i3];
  1942. // Check if element exists, has getAttribute func.
  1943. // IE seems to consider typeof el.getAttribute == 'object' instead of
  1944. // 'function' like expected, at least when loading the player immediately.
  1945. if (mediaEl && mediaEl.getAttribute) {
  1946. // Make sure this player hasn't already been set up.
  1947. if (mediaEl.player === undefined) {
  1948. var options = mediaEl.getAttribute('data-setup');
  1949. // Check if data-setup attr exists.
  1950. // We only auto-setup if they've added the data-setup attr.
  1951. if (options !== null) {
  1952. // Create new video.js instance.
  1953. videojs$2(mediaEl);
  1954. }
  1955. }
  1956. // If getAttribute isn't defined, we need to wait for the DOM.
  1957. } else {
  1958. autoSetupTimeout(1);
  1959. break;
  1960. }
  1961. }
  1962. // No videos were found, so keep looping unless page is finished loading.
  1963. } else if (!_windowLoaded) {
  1964. autoSetupTimeout(1);
  1965. }
  1966. };
  1967. /**
  1968. * Wait until the page is loaded before running autoSetup. This will be called in
  1969. * autoSetup if `hasLoaded` returns false.
  1970. *
  1971. * @param {number} wait
  1972. * How long to wait in ms
  1973. *
  1974. * @param {module:videojs} [vjs]
  1975. * The videojs library function
  1976. */
  1977. function autoSetupTimeout(wait, vjs) {
  1978. if (vjs) {
  1979. videojs$2 = vjs;
  1980. }
  1981. window_1.setTimeout(autoSetup, wait);
  1982. }
  1983. if (isReal() && document_1.readyState === 'complete') {
  1984. _windowLoaded = true;
  1985. } else {
  1986. /**
  1987. * Listen for the load event on window, and set _windowLoaded to true.
  1988. *
  1989. * @listens load
  1990. */
  1991. one(window_1, 'load', function () {
  1992. _windowLoaded = true;
  1993. });
  1994. }
  1995. /**
  1996. * @file stylesheet.js
  1997. * @module stylesheet
  1998. */
  1999. /**
  2000. * Create a DOM syle element given a className for it.
  2001. *
  2002. * @param {string} className
  2003. * The className to add to the created style element.
  2004. *
  2005. * @return {Element}
  2006. * The element that was created.
  2007. */
  2008. var createStyleElement = function createStyleElement(className) {
  2009. var style = document_1.createElement('style');
  2010. style.className = className;
  2011. return style;
  2012. };
  2013. /**
  2014. * Add text to a DOM element.
  2015. *
  2016. * @param {Element} el
  2017. * The Element to add text content to.
  2018. *
  2019. * @param {string} content
  2020. * The text to add to the element.
  2021. */
  2022. var setTextContent = function setTextContent(el, content) {
  2023. if (el.styleSheet) {
  2024. el.styleSheet.cssText = content;
  2025. } else {
  2026. el.textContent = content;
  2027. }
  2028. };
  2029. /**
  2030. * @file fn.js
  2031. * @module fn
  2032. */
  2033. /**
  2034. * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
  2035. * It also stores a unique id on the function so it can be easily removed from events.
  2036. *
  2037. * @param {Mixed} context
  2038. * The object to bind as scope.
  2039. *
  2040. * @param {Function} fn
  2041. * The function to be bound to a scope.
  2042. *
  2043. * @param {number} [uid]
  2044. * An optional unique ID for the function to be set
  2045. *
  2046. * @return {Function}
  2047. * The new function that will be bound into the context given
  2048. */
  2049. var bind = function bind(context, fn, uid) {
  2050. // Make sure the function has a unique ID
  2051. if (!fn.guid) {
  2052. fn.guid = newGUID();
  2053. }
  2054. // Create the new function that changes the context
  2055. var bound = function bound() {
  2056. return fn.apply(context, arguments);
  2057. };
  2058. // Allow for the ability to individualize this function
  2059. // Needed in the case where multiple objects might share the same prototype
  2060. // IF both items add an event listener with the same function, then you try to remove just one
  2061. // it will remove both because they both have the same guid.
  2062. // when using this, you need to use the bind method when you remove the listener as well.
  2063. // currently used in text tracks
  2064. bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
  2065. return bound;
  2066. };
  2067. /**
  2068. * Wraps the given function, `fn`, with a new function that only invokes `fn`
  2069. * at most once per every `wait` milliseconds.
  2070. *
  2071. * @param {Function} fn
  2072. * The function to be throttled.
  2073. *
  2074. * @param {Number} wait
  2075. * The number of milliseconds by which to throttle.
  2076. *
  2077. * @return {Function}
  2078. */
  2079. var throttle = function throttle(fn, wait) {
  2080. var last = Date.now();
  2081. var throttled = function throttled() {
  2082. var now = Date.now();
  2083. if (now - last >= wait) {
  2084. fn.apply(undefined, arguments);
  2085. last = now;
  2086. }
  2087. };
  2088. return throttled;
  2089. };
  2090. /**
  2091. * Creates a debounced function that delays invoking `func` until after `wait`
  2092. * milliseconds have elapsed since the last time the debounced function was
  2093. * invoked.
  2094. *
  2095. * Inspired by lodash and underscore implementations.
  2096. *
  2097. * @param {Function} func
  2098. * The function to wrap with debounce behavior.
  2099. *
  2100. * @param {number} wait
  2101. * The number of milliseconds to wait after the last invocation.
  2102. *
  2103. * @param {boolean} [immediate]
  2104. * Whether or not to invoke the function immediately upon creation.
  2105. *
  2106. * @param {Object} [context=window]
  2107. * The "context" in which the debounced function should debounce. For
  2108. * example, if this function should be tied to a Video.js player,
  2109. * the player can be passed here. Alternatively, defaults to the
  2110. * global `window` object.
  2111. *
  2112. * @return {Function}
  2113. * A debounced function.
  2114. */
  2115. var debounce = function debounce(func, wait, immediate) {
  2116. var context = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : window_1;
  2117. var timeout = void 0;
  2118. var cancel = function cancel() {
  2119. context.clearTimeout(timeout);
  2120. timeout = null;
  2121. };
  2122. /* eslint-disable consistent-this */
  2123. var debounced = function debounced() {
  2124. var self = this;
  2125. var args = arguments;
  2126. var _later = function later() {
  2127. timeout = null;
  2128. _later = null;
  2129. if (!immediate) {
  2130. func.apply(self, args);
  2131. }
  2132. };
  2133. if (!timeout && immediate) {
  2134. func.apply(self, args);
  2135. }
  2136. context.clearTimeout(timeout);
  2137. timeout = context.setTimeout(_later, wait);
  2138. };
  2139. /* eslint-enable consistent-this */
  2140. debounced.cancel = cancel;
  2141. return debounced;
  2142. };
  2143. /**
  2144. * @file src/js/event-target.js
  2145. */
  2146. /**
  2147. * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
  2148. * adds shorthand functions that wrap around lengthy functions. For example:
  2149. * the `on` function is a wrapper around `addEventListener`.
  2150. *
  2151. * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
  2152. * @class EventTarget
  2153. */
  2154. var EventTarget = function EventTarget() {};
  2155. /**
  2156. * A Custom DOM event.
  2157. *
  2158. * @typedef {Object} EventTarget~Event
  2159. * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
  2160. */
  2161. /**
  2162. * All event listeners should follow the following format.
  2163. *
  2164. * @callback EventTarget~EventListener
  2165. * @this {EventTarget}
  2166. *
  2167. * @param {EventTarget~Event} event
  2168. * the event that triggered this function
  2169. *
  2170. * @param {Object} [hash]
  2171. * hash of data sent during the event
  2172. */
  2173. /**
  2174. * An object containing event names as keys and booleans as values.
  2175. *
  2176. * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
  2177. * will have extra functionality. See that function for more information.
  2178. *
  2179. * @property EventTarget.prototype.allowedEvents_
  2180. * @private
  2181. */
  2182. EventTarget.prototype.allowedEvents_ = {};
  2183. /**
  2184. * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
  2185. * function that will get called when an event with a certain name gets triggered.
  2186. *
  2187. * @param {string|string[]} type
  2188. * An event name or an array of event names.
  2189. *
  2190. * @param {EventTarget~EventListener} fn
  2191. * The function to call with `EventTarget`s
  2192. */
  2193. EventTarget.prototype.on = function (type, fn) {
  2194. // Remove the addEventListener alias before calling Events.on
  2195. // so we don't get into an infinite type loop
  2196. var ael = this.addEventListener;
  2197. this.addEventListener = function () {};
  2198. on(this, type, fn);
  2199. this.addEventListener = ael;
  2200. };
  2201. /**
  2202. * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
  2203. * the standard DOM API.
  2204. *
  2205. * @function
  2206. * @see {@link EventTarget#on}
  2207. */
  2208. EventTarget.prototype.addEventListener = EventTarget.prototype.on;
  2209. /**
  2210. * Removes an `event listener` for a specific event from an instance of `EventTarget`.
  2211. * This makes it so that the `event listener` will no longer get called when the
  2212. * named event happens.
  2213. *
  2214. * @param {string|string[]} type
  2215. * An event name or an array of event names.
  2216. *
  2217. * @param {EventTarget~EventListener} fn
  2218. * The function to remove.
  2219. */
  2220. EventTarget.prototype.off = function (type, fn) {
  2221. off(this, type, fn);
  2222. };
  2223. /**
  2224. * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
  2225. * the standard DOM API.
  2226. *
  2227. * @function
  2228. * @see {@link EventTarget#off}
  2229. */
  2230. EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
  2231. /**
  2232. * This function will add an `event listener` that gets triggered only once. After the
  2233. * first trigger it will get removed. This is like adding an `event listener`
  2234. * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
  2235. *
  2236. * @param {string|string[]} type
  2237. * An event name or an array of event names.
  2238. *
  2239. * @param {EventTarget~EventListener} fn
  2240. * The function to be called once for each event name.
  2241. */
  2242. EventTarget.prototype.one = function (type, fn) {
  2243. // Remove the addEventListener alialing Events.on
  2244. // so we don't get into an infinite type loop
  2245. var ael = this.addEventListener;
  2246. this.addEventListener = function () {};
  2247. one(this, type, fn);
  2248. this.addEventListener = ael;
  2249. };
  2250. /**
  2251. * This function causes an event to happen. This will then cause any `event listeners`
  2252. * that are waiting for that event, to get called. If there are no `event listeners`
  2253. * for an event then nothing will happen.
  2254. *
  2255. * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
  2256. * Trigger will also call the `on` + `uppercaseEventName` function.
  2257. *
  2258. * Example:
  2259. * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
  2260. * `onClick` if it exists.
  2261. *
  2262. * @param {string|EventTarget~Event|Object} event
  2263. * The name of the event, an `Event`, or an object with a key of type set to
  2264. * an event name.
  2265. */
  2266. EventTarget.prototype.trigger = function (event) {
  2267. var type = event.type || event;
  2268. if (typeof event === 'string') {
  2269. event = { type: type };
  2270. }
  2271. event = fixEvent(event);
  2272. if (this.allowedEvents_[type] && this['on' + type]) {
  2273. this['on' + type](event);
  2274. }
  2275. trigger(this, event);
  2276. };
  2277. /**
  2278. * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
  2279. * the standard DOM API.
  2280. *
  2281. * @function
  2282. * @see {@link EventTarget#trigger}
  2283. */
  2284. EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
  2285. /**
  2286. * @file mixins/evented.js
  2287. * @module evented
  2288. */
  2289. /**
  2290. * Returns whether or not an object has had the evented mixin applied.
  2291. *
  2292. * @param {Object} object
  2293. * An object to test.
  2294. *
  2295. * @return {boolean}
  2296. * Whether or not the object appears to be evented.
  2297. */
  2298. var isEvented = function isEvented(object) {
  2299. return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
  2300. return typeof object[k] === 'function';
  2301. });
  2302. };
  2303. /**
  2304. * Whether a value is a valid event type - non-empty string or array.
  2305. *
  2306. * @private
  2307. * @param {string|Array} type
  2308. * The type value to test.
  2309. *
  2310. * @return {boolean}
  2311. * Whether or not the type is a valid event type.
  2312. */
  2313. var isValidEventType = function isValidEventType(type) {
  2314. return (
  2315. // The regex here verifies that the `type` contains at least one non-
  2316. // whitespace character.
  2317. typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
  2318. );
  2319. };
  2320. /**
  2321. * Validates a value to determine if it is a valid event target. Throws if not.
  2322. *
  2323. * @private
  2324. * @throws {Error}
  2325. * If the target does not appear to be a valid event target.
  2326. *
  2327. * @param {Object} target
  2328. * The object to test.
  2329. */
  2330. var validateTarget = function validateTarget(target) {
  2331. if (!target.nodeName && !isEvented(target)) {
  2332. throw new Error('Invalid target; must be a DOM node or evented object.');
  2333. }
  2334. };
  2335. /**
  2336. * Validates a value to determine if it is a valid event target. Throws if not.
  2337. *
  2338. * @private
  2339. * @throws {Error}
  2340. * If the type does not appear to be a valid event type.
  2341. *
  2342. * @param {string|Array} type
  2343. * The type to test.
  2344. */
  2345. var validateEventType = function validateEventType(type) {
  2346. if (!isValidEventType(type)) {
  2347. throw new Error('Invalid event type; must be a non-empty string or array.');
  2348. }
  2349. };
  2350. /**
  2351. * Validates a value to determine if it is a valid listener. Throws if not.
  2352. *
  2353. * @private
  2354. * @throws {Error}
  2355. * If the listener is not a function.
  2356. *
  2357. * @param {Function} listener
  2358. * The listener to test.
  2359. */
  2360. var validateListener = function validateListener(listener) {
  2361. if (typeof listener !== 'function') {
  2362. throw new Error('Invalid listener; must be a function.');
  2363. }
  2364. };
  2365. /**
  2366. * Takes an array of arguments given to `on()` or `one()`, validates them, and
  2367. * normalizes them into an object.
  2368. *
  2369. * @private
  2370. * @param {Object} self
  2371. * The evented object on which `on()` or `one()` was called. This
  2372. * object will be bound as the `this` value for the listener.
  2373. *
  2374. * @param {Array} args
  2375. * An array of arguments passed to `on()` or `one()`.
  2376. *
  2377. * @return {Object}
  2378. * An object containing useful values for `on()` or `one()` calls.
  2379. */
  2380. var normalizeListenArgs = function normalizeListenArgs(self, args) {
  2381. // If the number of arguments is less than 3, the target is always the
  2382. // evented object itself.
  2383. var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
  2384. var target = void 0;
  2385. var type = void 0;
  2386. var listener = void 0;
  2387. if (isTargetingSelf) {
  2388. target = self.eventBusEl_;
  2389. // Deal with cases where we got 3 arguments, but we are still listening to
  2390. // the evented object itself.
  2391. if (args.length >= 3) {
  2392. args.shift();
  2393. }
  2394. type = args[0];
  2395. listener = args[1];
  2396. } else {
  2397. target = args[0];
  2398. type = args[1];
  2399. listener = args[2];
  2400. }
  2401. validateTarget(target);
  2402. validateEventType(type);
  2403. validateListener(listener);
  2404. listener = bind(self, listener);
  2405. return { isTargetingSelf: isTargetingSelf, target: target, type: type, listener: listener };
  2406. };
  2407. /**
  2408. * Adds the listener to the event type(s) on the target, normalizing for
  2409. * the type of target.
  2410. *
  2411. * @private
  2412. * @param {Element|Object} target
  2413. * A DOM node or evented object.
  2414. *
  2415. * @param {string} method
  2416. * The event binding method to use ("on" or "one").
  2417. *
  2418. * @param {string|Array} type
  2419. * One or more event type(s).
  2420. *
  2421. * @param {Function} listener
  2422. * A listener function.
  2423. */
  2424. var listen = function listen(target, method, type, listener) {
  2425. validateTarget(target);
  2426. if (target.nodeName) {
  2427. Events[method](target, type, listener);
  2428. } else {
  2429. target[method](type, listener);
  2430. }
  2431. };
  2432. /**
  2433. * Contains methods that provide event capabilites to an object which is passed
  2434. * to {@link module:evented|evented}.
  2435. *
  2436. * @mixin EventedMixin
  2437. */
  2438. var EventedMixin = {
  2439. /**
  2440. * Add a listener to an event (or events) on this object or another evented
  2441. * object.
  2442. *
  2443. * @param {string|Array|Element|Object} targetOrType
  2444. * If this is a string or array, it represents the event type(s)
  2445. * that will trigger the listener.
  2446. *
  2447. * Another evented object can be passed here instead, which will
  2448. * cause the listener to listen for events on _that_ object.
  2449. *
  2450. * In either case, the listener's `this` value will be bound to
  2451. * this object.
  2452. *
  2453. * @param {string|Array|Function} typeOrListener
  2454. * If the first argument was a string or array, this should be the
  2455. * listener function. Otherwise, this is a string or array of event
  2456. * type(s).
  2457. *
  2458. * @param {Function} [listener]
  2459. * If the first argument was another evented object, this will be
  2460. * the listener function.
  2461. */
  2462. on: function on$$1() {
  2463. var _this = this;
  2464. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  2465. args[_key] = arguments[_key];
  2466. }
  2467. var _normalizeListenArgs = normalizeListenArgs(this, args),
  2468. isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
  2469. target = _normalizeListenArgs.target,
  2470. type = _normalizeListenArgs.type,
  2471. listener = _normalizeListenArgs.listener;
  2472. listen(target, 'on', type, listener);
  2473. // If this object is listening to another evented object.
  2474. if (!isTargetingSelf) {
  2475. // If this object is disposed, remove the listener.
  2476. var removeListenerOnDispose = function removeListenerOnDispose() {
  2477. return _this.off(target, type, listener);
  2478. };
  2479. // Use the same function ID as the listener so we can remove it later it
  2480. // using the ID of the original listener.
  2481. removeListenerOnDispose.guid = listener.guid;
  2482. // Add a listener to the target's dispose event as well. This ensures
  2483. // that if the target is disposed BEFORE this object, we remove the
  2484. // removal listener that was just added. Otherwise, we create a memory leak.
  2485. var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
  2486. return _this.off('dispose', removeListenerOnDispose);
  2487. };
  2488. // Use the same function ID as the listener so we can remove it later
  2489. // it using the ID of the original listener.
  2490. removeRemoverOnTargetDispose.guid = listener.guid;
  2491. listen(this, 'on', 'dispose', removeListenerOnDispose);
  2492. listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
  2493. }
  2494. },
  2495. /**
  2496. * Add a listener to an event (or events) on this object or another evented
  2497. * object. The listener will only be called once and then removed.
  2498. *
  2499. * @param {string|Array|Element|Object} targetOrType
  2500. * If this is a string or array, it represents the event type(s)
  2501. * that will trigger the listener.
  2502. *
  2503. * Another evented object can be passed here instead, which will
  2504. * cause the listener to listen for events on _that_ object.
  2505. *
  2506. * In either case, the listener's `this` value will be bound to
  2507. * this object.
  2508. *
  2509. * @param {string|Array|Function} typeOrListener
  2510. * If the first argument was a string or array, this should be the
  2511. * listener function. Otherwise, this is a string or array of event
  2512. * type(s).
  2513. *
  2514. * @param {Function} [listener]
  2515. * If the first argument was another evented object, this will be
  2516. * the listener function.
  2517. */
  2518. one: function one$$1() {
  2519. var _this2 = this;
  2520. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  2521. args[_key2] = arguments[_key2];
  2522. }
  2523. var _normalizeListenArgs2 = normalizeListenArgs(this, args),
  2524. isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
  2525. target = _normalizeListenArgs2.target,
  2526. type = _normalizeListenArgs2.type,
  2527. listener = _normalizeListenArgs2.listener;
  2528. // Targeting this evented object.
  2529. if (isTargetingSelf) {
  2530. listen(target, 'one', type, listener);
  2531. // Targeting another evented object.
  2532. } else {
  2533. var wrapper = function wrapper() {
  2534. for (var _len3 = arguments.length, largs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
  2535. largs[_key3] = arguments[_key3];
  2536. }
  2537. _this2.off(target, type, wrapper);
  2538. listener.apply(null, largs);
  2539. };
  2540. // Use the same function ID as the listener so we can remove it later
  2541. // it using the ID of the original listener.
  2542. wrapper.guid = listener.guid;
  2543. listen(target, 'one', type, wrapper);
  2544. }
  2545. },
  2546. /**
  2547. * Removes listener(s) from event(s) on an evented object.
  2548. *
  2549. * @param {string|Array|Element|Object} [targetOrType]
  2550. * If this is a string or array, it represents the event type(s).
  2551. *
  2552. * Another evented object can be passed here instead, in which case
  2553. * ALL 3 arguments are _required_.
  2554. *
  2555. * @param {string|Array|Function} [typeOrListener]
  2556. * If the first argument was a string or array, this may be the
  2557. * listener function. Otherwise, this is a string or array of event
  2558. * type(s).
  2559. *
  2560. * @param {Function} [listener]
  2561. * If the first argument was another evented object, this will be
  2562. * the listener function; otherwise, _all_ listeners bound to the
  2563. * event type(s) will be removed.
  2564. */
  2565. off: function off$$1(targetOrType, typeOrListener, listener) {
  2566. // Targeting this evented object.
  2567. if (!targetOrType || isValidEventType(targetOrType)) {
  2568. off(this.eventBusEl_, targetOrType, typeOrListener);
  2569. // Targeting another evented object.
  2570. } else {
  2571. var target = targetOrType;
  2572. var type = typeOrListener;
  2573. // Fail fast and in a meaningful way!
  2574. validateTarget(target);
  2575. validateEventType(type);
  2576. validateListener(listener);
  2577. // Ensure there's at least a guid, even if the function hasn't been used
  2578. listener = bind(this, listener);
  2579. // Remove the dispose listener on this evented object, which was given
  2580. // the same guid as the event listener in on().
  2581. this.off('dispose', listener);
  2582. if (target.nodeName) {
  2583. off(target, type, listener);
  2584. off(target, 'dispose', listener);
  2585. } else if (isEvented(target)) {
  2586. target.off(type, listener);
  2587. target.off('dispose', listener);
  2588. }
  2589. }
  2590. },
  2591. /**
  2592. * Fire an event on this evented object, causing its listeners to be called.
  2593. *
  2594. * @param {string|Object} event
  2595. * An event type or an object with a type property.
  2596. *
  2597. * @param {Object} [hash]
  2598. * An additional object to pass along to listeners.
  2599. *
  2600. * @returns {boolean}
  2601. * Whether or not the default behavior was prevented.
  2602. */
  2603. trigger: function trigger$$1(event, hash) {
  2604. return trigger(this.eventBusEl_, event, hash);
  2605. }
  2606. };
  2607. /**
  2608. * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
  2609. *
  2610. * @param {Object} target
  2611. * The object to which to add event methods.
  2612. *
  2613. * @param {Object} [options={}]
  2614. * Options for customizing the mixin behavior.
  2615. *
  2616. * @param {String} [options.eventBusKey]
  2617. * By default, adds a `eventBusEl_` DOM element to the target object,
  2618. * which is used as an event bus. If the target object already has a
  2619. * DOM element that should be used, pass its key here.
  2620. *
  2621. * @return {Object}
  2622. * The target object.
  2623. */
  2624. function evented(target) {
  2625. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  2626. var eventBusKey = options.eventBusKey;
  2627. // Set or create the eventBusEl_.
  2628. if (eventBusKey) {
  2629. if (!target[eventBusKey].nodeName) {
  2630. throw new Error('The eventBusKey "' + eventBusKey + '" does not refer to an element.');
  2631. }
  2632. target.eventBusEl_ = target[eventBusKey];
  2633. } else {
  2634. target.eventBusEl_ = createEl('span', { className: 'vjs-event-bus' });
  2635. }
  2636. assign(target, EventedMixin);
  2637. // When any evented object is disposed, it removes all its listeners.
  2638. target.on('dispose', function () {
  2639. target.off();
  2640. window_1.setTimeout(function () {
  2641. target.eventBusEl_ = null;
  2642. }, 0);
  2643. });
  2644. return target;
  2645. }
  2646. /**
  2647. * @file mixins/stateful.js
  2648. * @module stateful
  2649. */
  2650. /**
  2651. * Contains methods that provide statefulness to an object which is passed
  2652. * to {@link module:stateful}.
  2653. *
  2654. * @mixin StatefulMixin
  2655. */
  2656. var StatefulMixin = {
  2657. /**
  2658. * A hash containing arbitrary keys and values representing the state of
  2659. * the object.
  2660. *
  2661. * @type {Object}
  2662. */
  2663. state: {},
  2664. /**
  2665. * Set the state of an object by mutating its
  2666. * {@link module:stateful~StatefulMixin.state|state} object in place.
  2667. *
  2668. * @fires module:stateful~StatefulMixin#statechanged
  2669. * @param {Object|Function} stateUpdates
  2670. * A new set of properties to shallow-merge into the plugin state.
  2671. * Can be a plain object or a function returning a plain object.
  2672. *
  2673. * @returns {Object|undefined}
  2674. * An object containing changes that occurred. If no changes
  2675. * occurred, returns `undefined`.
  2676. */
  2677. setState: function setState(stateUpdates) {
  2678. var _this = this;
  2679. // Support providing the `stateUpdates` state as a function.
  2680. if (typeof stateUpdates === 'function') {
  2681. stateUpdates = stateUpdates();
  2682. }
  2683. var changes = void 0;
  2684. each(stateUpdates, function (value, key) {
  2685. // Record the change if the value is different from what's in the
  2686. // current state.
  2687. if (_this.state[key] !== value) {
  2688. changes = changes || {};
  2689. changes[key] = {
  2690. from: _this.state[key],
  2691. to: value
  2692. };
  2693. }
  2694. _this.state[key] = value;
  2695. });
  2696. // Only trigger "statechange" if there were changes AND we have a trigger
  2697. // function. This allows us to not require that the target object be an
  2698. // evented object.
  2699. if (changes && isEvented(this)) {
  2700. /**
  2701. * An event triggered on an object that is both
  2702. * {@link module:stateful|stateful} and {@link module:evented|evented}
  2703. * indicating that its state has changed.
  2704. *
  2705. * @event module:stateful~StatefulMixin#statechanged
  2706. * @type {Object}
  2707. * @property {Object} changes
  2708. * A hash containing the properties that were changed and
  2709. * the values they were changed `from` and `to`.
  2710. */
  2711. this.trigger({
  2712. changes: changes,
  2713. type: 'statechanged'
  2714. });
  2715. }
  2716. return changes;
  2717. }
  2718. };
  2719. /**
  2720. * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
  2721. * object.
  2722. *
  2723. * If the target object is {@link module:evented|evented} and has a
  2724. * `handleStateChanged` method, that method will be automatically bound to the
  2725. * `statechanged` event on itself.
  2726. *
  2727. * @param {Object} target
  2728. * The object to be made stateful.
  2729. *
  2730. * @param {Object} [defaultState]
  2731. * A default set of properties to populate the newly-stateful object's
  2732. * `state` property.
  2733. *
  2734. * @returns {Object}
  2735. * Returns the `target`.
  2736. */
  2737. function stateful(target, defaultState) {
  2738. assign(target, StatefulMixin);
  2739. // This happens after the mixing-in because we need to replace the `state`
  2740. // added in that step.
  2741. target.state = assign({}, target.state, defaultState);
  2742. // Auto-bind the `handleStateChanged` method of the target object if it exists.
  2743. if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
  2744. target.on('statechanged', target.handleStateChanged);
  2745. }
  2746. return target;
  2747. }
  2748. /**
  2749. * @file to-title-case.js
  2750. * @module to-title-case
  2751. */
  2752. /**
  2753. * Uppercase the first letter of a string.
  2754. *
  2755. * @param {string} string
  2756. * String to be uppercased
  2757. *
  2758. * @return {string}
  2759. * The string with an uppercased first letter
  2760. */
  2761. function toTitleCase(string) {
  2762. if (typeof string !== 'string') {
  2763. return string;
  2764. }
  2765. return string.charAt(0).toUpperCase() + string.slice(1);
  2766. }
  2767. /**
  2768. * Compares the TitleCase versions of the two strings for equality.
  2769. *
  2770. * @param {string} str1
  2771. * The first string to compare
  2772. *
  2773. * @param {string} str2
  2774. * The second string to compare
  2775. *
  2776. * @return {boolean}
  2777. * Whether the TitleCase versions of the strings are equal
  2778. */
  2779. function titleCaseEquals(str1, str2) {
  2780. return toTitleCase(str1) === toTitleCase(str2);
  2781. }
  2782. /**
  2783. * @file merge-options.js
  2784. * @module merge-options
  2785. */
  2786. /**
  2787. * Deep-merge one or more options objects, recursively merging **only** plain
  2788. * object properties.
  2789. *
  2790. * @param {Object[]} sources
  2791. * One or more objects to merge into a new object.
  2792. *
  2793. * @returns {Object}
  2794. * A new object that is the merged result of all sources.
  2795. */
  2796. function mergeOptions() {
  2797. var result = {};
  2798. for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
  2799. sources[_key] = arguments[_key];
  2800. }
  2801. sources.forEach(function (source) {
  2802. if (!source) {
  2803. return;
  2804. }
  2805. each(source, function (value, key) {
  2806. if (!isPlain(value)) {
  2807. result[key] = value;
  2808. return;
  2809. }
  2810. if (!isPlain(result[key])) {
  2811. result[key] = {};
  2812. }
  2813. result[key] = mergeOptions(result[key], value);
  2814. });
  2815. });
  2816. return result;
  2817. }
  2818. /**
  2819. * Player Component - Base class for all UI objects
  2820. *
  2821. * @file component.js
  2822. */
  2823. /**
  2824. * Base class for all UI Components.
  2825. * Components are UI objects which represent both a javascript object and an element
  2826. * in the DOM. They can be children of other components, and can have
  2827. * children themselves.
  2828. *
  2829. * Components can also use methods from {@link EventTarget}
  2830. */
  2831. var Component = function () {
  2832. /**
  2833. * A callback that is called when a component is ready. Does not have any
  2834. * paramters and any callback value will be ignored.
  2835. *
  2836. * @callback Component~ReadyCallback
  2837. * @this Component
  2838. */
  2839. /**
  2840. * Creates an instance of this class.
  2841. *
  2842. * @param {Player} player
  2843. * The `Player` that this class should be attached to.
  2844. *
  2845. * @param {Object} [options]
  2846. * The key/value store of player options.
  2847. *
  2848. * @param {Object[]} [options.children]
  2849. * An array of children objects to intialize this component with. Children objects have
  2850. * a name property that will be used if more than one component of the same type needs to be
  2851. * added.
  2852. *
  2853. * @param {Component~ReadyCallback} [ready]
  2854. * Function that gets called when the `Component` is ready.
  2855. */
  2856. function Component(player, options, ready) {
  2857. classCallCheck(this, Component);
  2858. // The component might be the player itself and we can't pass `this` to super
  2859. if (!player && this.play) {
  2860. this.player_ = player = this; // eslint-disable-line
  2861. } else {
  2862. this.player_ = player;
  2863. }
  2864. // Make a copy of prototype.options_ to protect against overriding defaults
  2865. this.options_ = mergeOptions({}, this.options_);
  2866. // Updated options with supplied options
  2867. options = this.options_ = mergeOptions(this.options_, options);
  2868. // Get ID from options or options element if one is supplied
  2869. this.id_ = options.id || options.el && options.el.id;
  2870. // If there was no ID from the options, generate one
  2871. if (!this.id_) {
  2872. // Don't require the player ID function in the case of mock players
  2873. var id = player && player.id && player.id() || 'no_player';
  2874. this.id_ = id + '_component_' + newGUID();
  2875. }
  2876. this.name_ = options.name || null;
  2877. // Create element if one wasn't provided in options
  2878. if (options.el) {
  2879. this.el_ = options.el;
  2880. } else if (options.createEl !== false) {
  2881. this.el_ = this.createEl();
  2882. }
  2883. // if evented is anything except false, we want to mixin in evented
  2884. if (options.evented !== false) {
  2885. // Make this an evented object and use `el_`, if available, as its event bus
  2886. evented(this, { eventBusKey: this.el_ ? 'el_' : null });
  2887. }
  2888. stateful(this, this.constructor.defaultState);
  2889. this.children_ = [];
  2890. this.childIndex_ = {};
  2891. this.childNameIndex_ = {};
  2892. // Add any child components in options
  2893. if (options.initChildren !== false) {
  2894. this.initChildren();
  2895. }
  2896. this.ready(ready);
  2897. // Don't want to trigger ready here or it will before init is actually
  2898. // finished for all children that run this constructor
  2899. if (options.reportTouchActivity !== false) {
  2900. this.enableTouchActivity();
  2901. }
  2902. }
  2903. /**
  2904. * Dispose of the `Component` and all child components.
  2905. *
  2906. * @fires Component#dispose
  2907. */
  2908. Component.prototype.dispose = function dispose() {
  2909. /**
  2910. * Triggered when a `Component` is disposed.
  2911. *
  2912. * @event Component#dispose
  2913. * @type {EventTarget~Event}
  2914. *
  2915. * @property {boolean} [bubbles=false]
  2916. * set to false so that the close event does not
  2917. * bubble up
  2918. */
  2919. this.trigger({ type: 'dispose', bubbles: false });
  2920. // Dispose all children.
  2921. if (this.children_) {
  2922. for (var i = this.children_.length - 1; i >= 0; i--) {
  2923. if (this.children_[i].dispose) {
  2924. this.children_[i].dispose();
  2925. }
  2926. }
  2927. }
  2928. // Delete child references
  2929. this.children_ = null;
  2930. this.childIndex_ = null;
  2931. this.childNameIndex_ = null;
  2932. if (this.el_) {
  2933. // Remove element from DOM
  2934. if (this.el_.parentNode) {
  2935. this.el_.parentNode.removeChild(this.el_);
  2936. }
  2937. removeData(this.el_);
  2938. this.el_ = null;
  2939. }
  2940. // remove reference to the player after disposing of the element
  2941. this.player_ = null;
  2942. };
  2943. /**
  2944. * Return the {@link Player} that the `Component` has attached to.
  2945. *
  2946. * @return {Player}
  2947. * The player that this `Component` has attached to.
  2948. */
  2949. Component.prototype.player = function player() {
  2950. return this.player_;
  2951. };
  2952. /**
  2953. * Deep merge of options objects with new options.
  2954. * > Note: When both `obj` and `options` contain properties whose values are objects.
  2955. * The two properties get merged using {@link module:mergeOptions}
  2956. *
  2957. * @param {Object} obj
  2958. * The object that contains new options.
  2959. *
  2960. * @return {Object}
  2961. * A new object of `this.options_` and `obj` merged together.
  2962. *
  2963. * @deprecated since version 5
  2964. */
  2965. Component.prototype.options = function options(obj) {
  2966. log.warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
  2967. if (!obj) {
  2968. return this.options_;
  2969. }
  2970. this.options_ = mergeOptions(this.options_, obj);
  2971. return this.options_;
  2972. };
  2973. /**
  2974. * Get the `Component`s DOM element
  2975. *
  2976. * @return {Element}
  2977. * The DOM element for this `Component`.
  2978. */
  2979. Component.prototype.el = function el() {
  2980. return this.el_;
  2981. };
  2982. /**
  2983. * Create the `Component`s DOM element.
  2984. *
  2985. * @param {string} [tagName]
  2986. * Element's DOM node type. e.g. 'div'
  2987. *
  2988. * @param {Object} [properties]
  2989. * An object of properties that should be set.
  2990. *
  2991. * @param {Object} [attributes]
  2992. * An object of attributes that should be set.
  2993. *
  2994. * @return {Element}
  2995. * The element that gets created.
  2996. */
  2997. Component.prototype.createEl = function createEl$$1(tagName, properties, attributes) {
  2998. return createEl(tagName, properties, attributes);
  2999. };
  3000. /**
  3001. * Localize a string given the string in english.
  3002. *
  3003. * If tokens are provided, it'll try and run a simple token replacement on the provided string.
  3004. * The tokens it looks for look like `{1}` with the index being 1-indexed into the tokens array.
  3005. *
  3006. * If a `defaultValue` is provided, it'll use that over `string`,
  3007. * if a value isn't found in provided language files.
  3008. * This is useful if you want to have a descriptive key for token replacement
  3009. * but have a succinct localized string and not require `en.json` to be included.
  3010. *
  3011. * Currently, it is used for the progress bar timing.
  3012. * ```js
  3013. * {
  3014. * "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
  3015. * }
  3016. * ```
  3017. * It is then used like so:
  3018. * ```js
  3019. * this.localize('progress bar timing: currentTime={1} duration{2}',
  3020. * [this.player_.currentTime(), this.player_.duration()],
  3021. * '{1} of {2}');
  3022. * ```
  3023. *
  3024. * Which outputs something like: `01:23 of 24:56`.
  3025. *
  3026. *
  3027. * @param {string} string
  3028. * The string to localize and the key to lookup in the language files.
  3029. * @param {string[]} [tokens]
  3030. * If the current item has token replacements, provide the tokens here.
  3031. * @param {string} [defaultValue]
  3032. * Defaults to `string`. Can be a default value to use for token replacement
  3033. * if the lookup key is needed to be separate.
  3034. *
  3035. * @return {string}
  3036. * The localized string or if no localization exists the english string.
  3037. */
  3038. Component.prototype.localize = function localize(string, tokens) {
  3039. var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : string;
  3040. var code = this.player_.language && this.player_.language();
  3041. var languages = this.player_.languages && this.player_.languages();
  3042. var language = languages && languages[code];
  3043. var primaryCode = code && code.split('-')[0];
  3044. var primaryLang = languages && languages[primaryCode];
  3045. var localizedString = defaultValue;
  3046. if (language && language[string]) {
  3047. localizedString = language[string];
  3048. } else if (primaryLang && primaryLang[string]) {
  3049. localizedString = primaryLang[string];
  3050. }
  3051. if (tokens) {
  3052. localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
  3053. var value = tokens[index - 1];
  3054. var ret = value;
  3055. if (typeof value === 'undefined') {
  3056. ret = match;
  3057. }
  3058. return ret;
  3059. });
  3060. }
  3061. return localizedString;
  3062. };
  3063. /**
  3064. * Return the `Component`s DOM element. This is where children get inserted.
  3065. * This will usually be the the same as the element returned in {@link Component#el}.
  3066. *
  3067. * @return {Element}
  3068. * The content element for this `Component`.
  3069. */
  3070. Component.prototype.contentEl = function contentEl() {
  3071. return this.contentEl_ || this.el_;
  3072. };
  3073. /**
  3074. * Get this `Component`s ID
  3075. *
  3076. * @return {string}
  3077. * The id of this `Component`
  3078. */
  3079. Component.prototype.id = function id() {
  3080. return this.id_;
  3081. };
  3082. /**
  3083. * Get the `Component`s name. The name gets used to reference the `Component`
  3084. * and is set during registration.
  3085. *
  3086. * @return {string}
  3087. * The name of this `Component`.
  3088. */
  3089. Component.prototype.name = function name() {
  3090. return this.name_;
  3091. };
  3092. /**
  3093. * Get an array of all child components
  3094. *
  3095. * @return {Array}
  3096. * The children
  3097. */
  3098. Component.prototype.children = function children() {
  3099. return this.children_;
  3100. };
  3101. /**
  3102. * Returns the child `Component` with the given `id`.
  3103. *
  3104. * @param {string} id
  3105. * The id of the child `Component` to get.
  3106. *
  3107. * @return {Component|undefined}
  3108. * The child `Component` with the given `id` or undefined.
  3109. */
  3110. Component.prototype.getChildById = function getChildById(id) {
  3111. return this.childIndex_[id];
  3112. };
  3113. /**
  3114. * Returns the child `Component` with the given `name`.
  3115. *
  3116. * @param {string} name
  3117. * The name of the child `Component` to get.
  3118. *
  3119. * @return {Component|undefined}
  3120. * The child `Component` with the given `name` or undefined.
  3121. */
  3122. Component.prototype.getChild = function getChild(name) {
  3123. if (!name) {
  3124. return;
  3125. }
  3126. name = toTitleCase(name);
  3127. return this.childNameIndex_[name];
  3128. };
  3129. /**
  3130. * Add a child `Component` inside the current `Component`.
  3131. *
  3132. *
  3133. * @param {string|Component} child
  3134. * The name or instance of a child to add.
  3135. *
  3136. * @param {Object} [options={}]
  3137. * The key/value store of options that will get passed to children of
  3138. * the child.
  3139. *
  3140. * @param {number} [index=this.children_.length]
  3141. * The index to attempt to add a child into.
  3142. *
  3143. * @return {Component}
  3144. * The `Component` that gets added as a child. When using a string the
  3145. * `Component` will get created by this process.
  3146. */
  3147. Component.prototype.addChild = function addChild(child) {
  3148. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  3149. var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
  3150. var component = void 0;
  3151. var componentName = void 0;
  3152. // If child is a string, create component with options
  3153. if (typeof child === 'string') {
  3154. componentName = toTitleCase(child);
  3155. var componentClassName = options.componentClass || componentName;
  3156. // Set name through options
  3157. options.name = componentName;
  3158. // Create a new object & element for this controls set
  3159. // If there's no .player_, this is a player
  3160. var ComponentClass = Component.getComponent(componentClassName);
  3161. if (!ComponentClass) {
  3162. throw new Error('Component ' + componentClassName + ' does not exist');
  3163. }
  3164. // data stored directly on the videojs object may be
  3165. // misidentified as a component to retain
  3166. // backwards-compatibility with 4.x. check to make sure the
  3167. // component class can be instantiated.
  3168. if (typeof ComponentClass !== 'function') {
  3169. return null;
  3170. }
  3171. component = new ComponentClass(this.player_ || this, options);
  3172. // child is a component instance
  3173. } else {
  3174. component = child;
  3175. }
  3176. this.children_.splice(index, 0, component);
  3177. if (typeof component.id === 'function') {
  3178. this.childIndex_[component.id()] = component;
  3179. }
  3180. // If a name wasn't used to create the component, check if we can use the
  3181. // name function of the component
  3182. componentName = componentName || component.name && toTitleCase(component.name());
  3183. if (componentName) {
  3184. this.childNameIndex_[componentName] = component;
  3185. }
  3186. // Add the UI object's element to the container div (box)
  3187. // Having an element is not required
  3188. if (typeof component.el === 'function' && component.el()) {
  3189. var childNodes = this.contentEl().children;
  3190. var refNode = childNodes[index] || null;
  3191. this.contentEl().insertBefore(component.el(), refNode);
  3192. }
  3193. // Return so it can stored on parent object if desired.
  3194. return component;
  3195. };
  3196. /**
  3197. * Remove a child `Component` from this `Component`s list of children. Also removes
  3198. * the child `Component`s element from this `Component`s element.
  3199. *
  3200. * @param {Component} component
  3201. * The child `Component` to remove.
  3202. */
  3203. Component.prototype.removeChild = function removeChild(component) {
  3204. if (typeof component === 'string') {
  3205. component = this.getChild(component);
  3206. }
  3207. if (!component || !this.children_) {
  3208. return;
  3209. }
  3210. var childFound = false;
  3211. for (var i = this.children_.length - 1; i >= 0; i--) {
  3212. if (this.children_[i] === component) {
  3213. childFound = true;
  3214. this.children_.splice(i, 1);
  3215. break;
  3216. }
  3217. }
  3218. if (!childFound) {
  3219. return;
  3220. }
  3221. this.childIndex_[component.id()] = null;
  3222. this.childNameIndex_[component.name()] = null;
  3223. var compEl = component.el();
  3224. if (compEl && compEl.parentNode === this.contentEl()) {
  3225. this.contentEl().removeChild(component.el());
  3226. }
  3227. };
  3228. /**
  3229. * Add and initialize default child `Component`s based upon options.
  3230. */
  3231. Component.prototype.initChildren = function initChildren() {
  3232. var _this = this;
  3233. var children = this.options_.children;
  3234. if (children) {
  3235. // `this` is `parent`
  3236. var parentOptions = this.options_;
  3237. var handleAdd = function handleAdd(child) {
  3238. var name = child.name;
  3239. var opts = child.opts;
  3240. // Allow options for children to be set at the parent options
  3241. // e.g. videojs(id, { controlBar: false });
  3242. // instead of videojs(id, { children: { controlBar: false });
  3243. if (parentOptions[name] !== undefined) {
  3244. opts = parentOptions[name];
  3245. }
  3246. // Allow for disabling default components
  3247. // e.g. options['children']['posterImage'] = false
  3248. if (opts === false) {
  3249. return;
  3250. }
  3251. // Allow options to be passed as a simple boolean if no configuration
  3252. // is necessary.
  3253. if (opts === true) {
  3254. opts = {};
  3255. }
  3256. // We also want to pass the original player options
  3257. // to each component as well so they don't need to
  3258. // reach back into the player for options later.
  3259. opts.playerOptions = _this.options_.playerOptions;
  3260. // Create and add the child component.
  3261. // Add a direct reference to the child by name on the parent instance.
  3262. // If two of the same component are used, different names should be supplied
  3263. // for each
  3264. var newChild = _this.addChild(name, opts);
  3265. if (newChild) {
  3266. _this[name] = newChild;
  3267. }
  3268. };
  3269. // Allow for an array of children details to passed in the options
  3270. var workingChildren = void 0;
  3271. var Tech = Component.getComponent('Tech');
  3272. if (Array.isArray(children)) {
  3273. workingChildren = children;
  3274. } else {
  3275. workingChildren = Object.keys(children);
  3276. }
  3277. workingChildren
  3278. // children that are in this.options_ but also in workingChildren would
  3279. // give us extra children we do not want. So, we want to filter them out.
  3280. .concat(Object.keys(this.options_).filter(function (child) {
  3281. return !workingChildren.some(function (wchild) {
  3282. if (typeof wchild === 'string') {
  3283. return child === wchild;
  3284. }
  3285. return child === wchild.name;
  3286. });
  3287. })).map(function (child) {
  3288. var name = void 0;
  3289. var opts = void 0;
  3290. if (typeof child === 'string') {
  3291. name = child;
  3292. opts = children[name] || _this.options_[name] || {};
  3293. } else {
  3294. name = child.name;
  3295. opts = child;
  3296. }
  3297. return { name: name, opts: opts };
  3298. }).filter(function (child) {
  3299. // we have to make sure that child.name isn't in the techOrder since
  3300. // techs are registerd as Components but can't aren't compatible
  3301. // See https://github.com/videojs/video.js/issues/2772
  3302. var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
  3303. return c && !Tech.isTech(c);
  3304. }).forEach(handleAdd);
  3305. }
  3306. };
  3307. /**
  3308. * Builds the default DOM class name. Should be overriden by sub-components.
  3309. *
  3310. * @return {string}
  3311. * The DOM class name for this object.
  3312. *
  3313. * @abstract
  3314. */
  3315. Component.prototype.buildCSSClass = function buildCSSClass() {
  3316. // Child classes can include a function that does:
  3317. // return 'CLASS NAME' + this._super();
  3318. return '';
  3319. };
  3320. /**
  3321. * Bind a listener to the component's ready state.
  3322. * Different from event listeners in that if the ready event has already happened
  3323. * it will trigger the function immediately.
  3324. *
  3325. * @return {Component}
  3326. * Returns itself; method can be chained.
  3327. */
  3328. Component.prototype.ready = function ready(fn) {
  3329. var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
  3330. if (!fn) {
  3331. return;
  3332. }
  3333. if (!this.isReady_) {
  3334. this.readyQueue_ = this.readyQueue_ || [];
  3335. this.readyQueue_.push(fn);
  3336. return;
  3337. }
  3338. if (sync) {
  3339. fn.call(this);
  3340. } else {
  3341. // Call the function asynchronously by default for consistency
  3342. this.setTimeout(fn, 1);
  3343. }
  3344. };
  3345. /**
  3346. * Trigger all the ready listeners for this `Component`.
  3347. *
  3348. * @fires Component#ready
  3349. */
  3350. Component.prototype.triggerReady = function triggerReady() {
  3351. this.isReady_ = true;
  3352. // Ensure ready is triggered asynchronously
  3353. this.setTimeout(function () {
  3354. var readyQueue = this.readyQueue_;
  3355. // Reset Ready Queue
  3356. this.readyQueue_ = [];
  3357. if (readyQueue && readyQueue.length > 0) {
  3358. readyQueue.forEach(function (fn) {
  3359. fn.call(this);
  3360. }, this);
  3361. }
  3362. // Allow for using event listeners also
  3363. /**
  3364. * Triggered when a `Component` is ready.
  3365. *
  3366. * @event Component#ready
  3367. * @type {EventTarget~Event}
  3368. */
  3369. this.trigger('ready');
  3370. }, 1);
  3371. };
  3372. /**
  3373. * Find a single DOM element matching a `selector`. This can be within the `Component`s
  3374. * `contentEl()` or another custom context.
  3375. *
  3376. * @param {string} selector
  3377. * A valid CSS selector, which will be passed to `querySelector`.
  3378. *
  3379. * @param {Element|string} [context=this.contentEl()]
  3380. * A DOM element within which to query. Can also be a selector string in
  3381. * which case the first matching element will get used as context. If
  3382. * missing `this.contentEl()` gets used. If `this.contentEl()` returns
  3383. * nothing it falls back to `document`.
  3384. *
  3385. * @return {Element|null}
  3386. * the dom element that was found, or null
  3387. *
  3388. * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
  3389. */
  3390. Component.prototype.$ = function $$$1(selector, context) {
  3391. return $(selector, context || this.contentEl());
  3392. };
  3393. /**
  3394. * Finds all DOM element matching a `selector`. This can be within the `Component`s
  3395. * `contentEl()` or another custom context.
  3396. *
  3397. * @param {string} selector
  3398. * A valid CSS selector, which will be passed to `querySelectorAll`.
  3399. *
  3400. * @param {Element|string} [context=this.contentEl()]
  3401. * A DOM element within which to query. Can also be a selector string in
  3402. * which case the first matching element will get used as context. If
  3403. * missing `this.contentEl()` gets used. If `this.contentEl()` returns
  3404. * nothing it falls back to `document`.
  3405. *
  3406. * @return {NodeList}
  3407. * a list of dom elements that were found
  3408. *
  3409. * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
  3410. */
  3411. Component.prototype.$$ = function $$$$1(selector, context) {
  3412. return $$(selector, context || this.contentEl());
  3413. };
  3414. /**
  3415. * Check if a component's element has a CSS class name.
  3416. *
  3417. * @param {string} classToCheck
  3418. * CSS class name to check.
  3419. *
  3420. * @return {boolean}
  3421. * - True if the `Component` has the class.
  3422. * - False if the `Component` does not have the class`
  3423. */
  3424. Component.prototype.hasClass = function hasClass$$1(classToCheck) {
  3425. return hasClass(this.el_, classToCheck);
  3426. };
  3427. /**
  3428. * Add a CSS class name to the `Component`s element.
  3429. *
  3430. * @param {string} classToAdd
  3431. * CSS class name to add
  3432. */
  3433. Component.prototype.addClass = function addClass$$1(classToAdd) {
  3434. addClass(this.el_, classToAdd);
  3435. };
  3436. /**
  3437. * Remove a CSS class name from the `Component`s element.
  3438. *
  3439. * @param {string} classToRemove
  3440. * CSS class name to remove
  3441. */
  3442. Component.prototype.removeClass = function removeClass$$1(classToRemove) {
  3443. removeClass(this.el_, classToRemove);
  3444. };
  3445. /**
  3446. * Add or remove a CSS class name from the component's element.
  3447. * - `classToToggle` gets added when {@link Component#hasClass} would return false.
  3448. * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
  3449. *
  3450. * @param {string} classToToggle
  3451. * The class to add or remove based on (@link Component#hasClass}
  3452. *
  3453. * @param {boolean|Dom~predicate} [predicate]
  3454. * An {@link Dom~predicate} function or a boolean
  3455. */
  3456. Component.prototype.toggleClass = function toggleClass$$1(classToToggle, predicate) {
  3457. toggleClass(this.el_, classToToggle, predicate);
  3458. };
  3459. /**
  3460. * Show the `Component`s element if it is hidden by removing the
  3461. * 'vjs-hidden' class name from it.
  3462. */
  3463. Component.prototype.show = function show() {
  3464. this.removeClass('vjs-hidden');
  3465. };
  3466. /**
  3467. * Hide the `Component`s element if it is currently showing by adding the
  3468. * 'vjs-hidden` class name to it.
  3469. */
  3470. Component.prototype.hide = function hide() {
  3471. this.addClass('vjs-hidden');
  3472. };
  3473. /**
  3474. * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
  3475. * class name to it. Used during fadeIn/fadeOut.
  3476. *
  3477. * @private
  3478. */
  3479. Component.prototype.lockShowing = function lockShowing() {
  3480. this.addClass('vjs-lock-showing');
  3481. };
  3482. /**
  3483. * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
  3484. * class name from it. Used during fadeIn/fadeOut.
  3485. *
  3486. * @private
  3487. */
  3488. Component.prototype.unlockShowing = function unlockShowing() {
  3489. this.removeClass('vjs-lock-showing');
  3490. };
  3491. /**
  3492. * Get the value of an attribute on the `Component`s element.
  3493. *
  3494. * @param {string} attribute
  3495. * Name of the attribute to get the value from.
  3496. *
  3497. * @return {string|null}
  3498. * - The value of the attribute that was asked for.
  3499. * - Can be an empty string on some browsers if the attribute does not exist
  3500. * or has no value
  3501. * - Most browsers will return null if the attibute does not exist or has
  3502. * no value.
  3503. *
  3504. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
  3505. */
  3506. Component.prototype.getAttribute = function getAttribute$$1(attribute) {
  3507. return getAttribute(this.el_, attribute);
  3508. };
  3509. /**
  3510. * Set the value of an attribute on the `Component`'s element
  3511. *
  3512. * @param {string} attribute
  3513. * Name of the attribute to set.
  3514. *
  3515. * @param {string} value
  3516. * Value to set the attribute to.
  3517. *
  3518. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
  3519. */
  3520. Component.prototype.setAttribute = function setAttribute$$1(attribute, value) {
  3521. setAttribute(this.el_, attribute, value);
  3522. };
  3523. /**
  3524. * Remove an attribute from the `Component`s element.
  3525. *
  3526. * @param {string} attribute
  3527. * Name of the attribute to remove.
  3528. *
  3529. * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
  3530. */
  3531. Component.prototype.removeAttribute = function removeAttribute$$1(attribute) {
  3532. removeAttribute(this.el_, attribute);
  3533. };
  3534. /**
  3535. * Get or set the width of the component based upon the CSS styles.
  3536. * See {@link Component#dimension} for more detailed information.
  3537. *
  3538. * @param {number|string} [num]
  3539. * The width that you want to set postfixed with '%', 'px' or nothing.
  3540. *
  3541. * @param {boolean} [skipListeners]
  3542. * Skip the componentresize event trigger
  3543. *
  3544. * @return {number|string}
  3545. * The width when getting, zero if there is no width. Can be a string
  3546. * postpixed with '%' or 'px'.
  3547. */
  3548. Component.prototype.width = function width(num, skipListeners) {
  3549. return this.dimension('width', num, skipListeners);
  3550. };
  3551. /**
  3552. * Get or set the height of the component based upon the CSS styles.
  3553. * See {@link Component#dimension} for more detailed information.
  3554. *
  3555. * @param {number|string} [num]
  3556. * The height that you want to set postfixed with '%', 'px' or nothing.
  3557. *
  3558. * @param {boolean} [skipListeners]
  3559. * Skip the componentresize event trigger
  3560. *
  3561. * @return {number|string}
  3562. * The width when getting, zero if there is no width. Can be a string
  3563. * postpixed with '%' or 'px'.
  3564. */
  3565. Component.prototype.height = function height(num, skipListeners) {
  3566. return this.dimension('height', num, skipListeners);
  3567. };
  3568. /**
  3569. * Set both the width and height of the `Component` element at the same time.
  3570. *
  3571. * @param {number|string} width
  3572. * Width to set the `Component`s element to.
  3573. *
  3574. * @param {number|string} height
  3575. * Height to set the `Component`s element to.
  3576. */
  3577. Component.prototype.dimensions = function dimensions(width, height) {
  3578. // Skip componentresize listeners on width for optimization
  3579. this.width(width, true);
  3580. this.height(height);
  3581. };
  3582. /**
  3583. * Get or set width or height of the `Component` element. This is the shared code
  3584. * for the {@link Component#width} and {@link Component#height}.
  3585. *
  3586. * Things to know:
  3587. * - If the width or height in an number this will return the number postfixed with 'px'.
  3588. * - If the width/height is a percent this will return the percent postfixed with '%'
  3589. * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
  3590. * defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
  3591. * See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
  3592. * for more information
  3593. * - If you want the computed style of the component, use {@link Component#currentWidth}
  3594. * and {@link {Component#currentHeight}
  3595. *
  3596. * @fires Component#componentresize
  3597. *
  3598. * @param {string} widthOrHeight
  3599. 8 'width' or 'height'
  3600. *
  3601. * @param {number|string} [num]
  3602. 8 New dimension
  3603. *
  3604. * @param {boolean} [skipListeners]
  3605. * Skip componentresize event trigger
  3606. *
  3607. * @return {number}
  3608. * The dimension when getting or 0 if unset
  3609. */
  3610. Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
  3611. if (num !== undefined) {
  3612. // Set to zero if null or literally NaN (NaN !== NaN)
  3613. if (num === null || num !== num) {
  3614. num = 0;
  3615. }
  3616. // Check if using css width/height (% or px) and adjust
  3617. if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
  3618. this.el_.style[widthOrHeight] = num;
  3619. } else if (num === 'auto') {
  3620. this.el_.style[widthOrHeight] = '';
  3621. } else {
  3622. this.el_.style[widthOrHeight] = num + 'px';
  3623. }
  3624. // skipListeners allows us to avoid triggering the resize event when setting both width and height
  3625. if (!skipListeners) {
  3626. /**
  3627. * Triggered when a component is resized.
  3628. *
  3629. * @event Component#componentresize
  3630. * @type {EventTarget~Event}
  3631. */
  3632. this.trigger('componentresize');
  3633. }
  3634. return;
  3635. }
  3636. // Not setting a value, so getting it
  3637. // Make sure element exists
  3638. if (!this.el_) {
  3639. return 0;
  3640. }
  3641. // Get dimension value from style
  3642. var val = this.el_.style[widthOrHeight];
  3643. var pxIndex = val.indexOf('px');
  3644. if (pxIndex !== -1) {
  3645. // Return the pixel value with no 'px'
  3646. return parseInt(val.slice(0, pxIndex), 10);
  3647. }
  3648. // No px so using % or no style was set, so falling back to offsetWidth/height
  3649. // If component has display:none, offset will return 0
  3650. // TODO: handle display:none and no dimension style using px
  3651. return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
  3652. };
  3653. /**
  3654. * Get the computed width or the height of the component's element.
  3655. *
  3656. * Uses `window.getComputedStyle`.
  3657. *
  3658. * @param {string} widthOrHeight
  3659. * A string containing 'width' or 'height'. Whichever one you want to get.
  3660. *
  3661. * @return {number}
  3662. * The dimension that gets asked for or 0 if nothing was set
  3663. * for that dimension.
  3664. */
  3665. Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
  3666. var computedWidthOrHeight = 0;
  3667. if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
  3668. throw new Error('currentDimension only accepts width or height value');
  3669. }
  3670. if (typeof window_1.getComputedStyle === 'function') {
  3671. var computedStyle = window_1.getComputedStyle(this.el_);
  3672. computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
  3673. }
  3674. // remove 'px' from variable and parse as integer
  3675. computedWidthOrHeight = parseFloat(computedWidthOrHeight);
  3676. // if the computed value is still 0, it's possible that the browser is lying
  3677. // and we want to check the offset values.
  3678. // This code also runs on IE8 and wherever getComputedStyle doesn't exist.
  3679. if (computedWidthOrHeight === 0) {
  3680. var rule = 'offset' + toTitleCase(widthOrHeight);
  3681. computedWidthOrHeight = this.el_[rule];
  3682. }
  3683. return computedWidthOrHeight;
  3684. };
  3685. /**
  3686. * An object that contains width and height values of the `Component`s
  3687. * computed style. Uses `window.getComputedStyle`.
  3688. *
  3689. * @typedef {Object} Component~DimensionObject
  3690. *
  3691. * @property {number} width
  3692. * The width of the `Component`s computed style.
  3693. *
  3694. * @property {number} height
  3695. * The height of the `Component`s computed style.
  3696. */
  3697. /**
  3698. * Get an object that contains computed width and height values of the
  3699. * component's element.
  3700. *
  3701. * Uses `window.getComputedStyle`.
  3702. *
  3703. * @return {Component~DimensionObject}
  3704. * The computed dimensions of the component's element.
  3705. */
  3706. Component.prototype.currentDimensions = function currentDimensions() {
  3707. return {
  3708. width: this.currentDimension('width'),
  3709. height: this.currentDimension('height')
  3710. };
  3711. };
  3712. /**
  3713. * Get the computed width of the component's element.
  3714. *
  3715. * Uses `window.getComputedStyle`.
  3716. *
  3717. * @return {number}
  3718. * The computed width of the component's element.
  3719. */
  3720. Component.prototype.currentWidth = function currentWidth() {
  3721. return this.currentDimension('width');
  3722. };
  3723. /**
  3724. * Get the computed height of the component's element.
  3725. *
  3726. * Uses `window.getComputedStyle`.
  3727. *
  3728. * @return {number}
  3729. * The computed height of the component's element.
  3730. */
  3731. Component.prototype.currentHeight = function currentHeight() {
  3732. return this.currentDimension('height');
  3733. };
  3734. /**
  3735. * Set the focus to this component
  3736. */
  3737. Component.prototype.focus = function focus() {
  3738. this.el_.focus();
  3739. };
  3740. /**
  3741. * Remove the focus from this component
  3742. */
  3743. Component.prototype.blur = function blur() {
  3744. this.el_.blur();
  3745. };
  3746. /**
  3747. * Emit a 'tap' events when touch event support gets detected. This gets used to
  3748. * support toggling the controls through a tap on the video. They get enabled
  3749. * because every sub-component would have extra overhead otherwise.
  3750. *
  3751. * @private
  3752. * @fires Component#tap
  3753. * @listens Component#touchstart
  3754. * @listens Component#touchmove
  3755. * @listens Component#touchleave
  3756. * @listens Component#touchcancel
  3757. * @listens Component#touchend
  3758. */
  3759. Component.prototype.emitTapEvents = function emitTapEvents() {
  3760. // Track the start time so we can determine how long the touch lasted
  3761. var touchStart = 0;
  3762. var firstTouch = null;
  3763. // Maximum movement allowed during a touch event to still be considered a tap
  3764. // Other popular libs use anywhere from 2 (hammer.js) to 15,
  3765. // so 10 seems like a nice, round number.
  3766. var tapMovementThreshold = 10;
  3767. // The maximum length a touch can be while still being considered a tap
  3768. var touchTimeThreshold = 200;
  3769. var couldBeTap = void 0;
  3770. this.on('touchstart', function (event) {
  3771. // If more than one finger, don't consider treating this as a click
  3772. if (event.touches.length === 1) {
  3773. // Copy pageX/pageY from the object
  3774. firstTouch = {
  3775. pageX: event.touches[0].pageX,
  3776. pageY: event.touches[0].pageY
  3777. };
  3778. // Record start time so we can detect a tap vs. "touch and hold"
  3779. touchStart = new Date().getTime();
  3780. // Reset couldBeTap tracking
  3781. couldBeTap = true;
  3782. }
  3783. });
  3784. this.on('touchmove', function (event) {
  3785. // If more than one finger, don't consider treating this as a click
  3786. if (event.touches.length > 1) {
  3787. couldBeTap = false;
  3788. } else if (firstTouch) {
  3789. // Some devices will throw touchmoves for all but the slightest of taps.
  3790. // So, if we moved only a small distance, this could still be a tap
  3791. var xdiff = event.touches[0].pageX - firstTouch.pageX;
  3792. var ydiff = event.touches[0].pageY - firstTouch.pageY;
  3793. var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
  3794. if (touchDistance > tapMovementThreshold) {
  3795. couldBeTap = false;
  3796. }
  3797. }
  3798. });
  3799. var noTap = function noTap() {
  3800. couldBeTap = false;
  3801. };
  3802. // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
  3803. this.on('touchleave', noTap);
  3804. this.on('touchcancel', noTap);
  3805. // When the touch ends, measure how long it took and trigger the appropriate
  3806. // event
  3807. this.on('touchend', function (event) {
  3808. firstTouch = null;
  3809. // Proceed only if the touchmove/leave/cancel event didn't happen
  3810. if (couldBeTap === true) {
  3811. // Measure how long the touch lasted
  3812. var touchTime = new Date().getTime() - touchStart;
  3813. // Make sure the touch was less than the threshold to be considered a tap
  3814. if (touchTime < touchTimeThreshold) {
  3815. // Don't let browser turn this into a click
  3816. event.preventDefault();
  3817. /**
  3818. * Triggered when a `Component` is tapped.
  3819. *
  3820. * @event Component#tap
  3821. * @type {EventTarget~Event}
  3822. */
  3823. this.trigger('tap');
  3824. // It may be good to copy the touchend event object and change the
  3825. // type to tap, if the other event properties aren't exact after
  3826. // Events.fixEvent runs (e.g. event.target)
  3827. }
  3828. }
  3829. });
  3830. };
  3831. /**
  3832. * This function reports user activity whenever touch events happen. This can get
  3833. * turned off by any sub-components that wants touch events to act another way.
  3834. *
  3835. * Report user touch activity when touch events occur. User activity gets used to
  3836. * determine when controls should show/hide. It is simple when it comes to mouse
  3837. * events, because any mouse event should show the controls. So we capture mouse
  3838. * events that bubble up to the player and report activity when that happens.
  3839. * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
  3840. * controls. So touch events can't help us at the player level either.
  3841. *
  3842. * User activity gets checked asynchronously. So what could happen is a tap event
  3843. * on the video turns the controls off. Then the `touchend` event bubbles up to
  3844. * the player. Which, if it reported user activity, would turn the controls right
  3845. * back on. We also don't want to completely block touch events from bubbling up.
  3846. * Furthermore a `touchmove` event and anything other than a tap, should not turn
  3847. * controls back on.
  3848. *
  3849. * @listens Component#touchstart
  3850. * @listens Component#touchmove
  3851. * @listens Component#touchend
  3852. * @listens Component#touchcancel
  3853. */
  3854. Component.prototype.enableTouchActivity = function enableTouchActivity() {
  3855. // Don't continue if the root player doesn't support reporting user activity
  3856. if (!this.player() || !this.player().reportUserActivity) {
  3857. return;
  3858. }
  3859. // listener for reporting that the user is active
  3860. var report = bind(this.player(), this.player().reportUserActivity);
  3861. var touchHolding = void 0;
  3862. this.on('touchstart', function () {
  3863. report();
  3864. // For as long as the they are touching the device or have their mouse down,
  3865. // we consider them active even if they're not moving their finger or mouse.
  3866. // So we want to continue to update that they are active
  3867. this.clearInterval(touchHolding);
  3868. // report at the same interval as activityCheck
  3869. touchHolding = this.setInterval(report, 250);
  3870. });
  3871. var touchEnd = function touchEnd(event) {
  3872. report();
  3873. // stop the interval that maintains activity if the touch is holding
  3874. this.clearInterval(touchHolding);
  3875. };
  3876. this.on('touchmove', report);
  3877. this.on('touchend', touchEnd);
  3878. this.on('touchcancel', touchEnd);
  3879. };
  3880. /**
  3881. * A callback that has no parameters and is bound into `Component`s context.
  3882. *
  3883. * @callback Component~GenericCallback
  3884. * @this Component
  3885. */
  3886. /**
  3887. * Creates a function that runs after an `x` millisecond timeout. This function is a
  3888. * wrapper around `window.setTimeout`. There are a few reasons to use this one
  3889. * instead though:
  3890. * 1. It gets cleared via {@link Component#clearTimeout} when
  3891. * {@link Component#dispose} gets called.
  3892. * 2. The function callback will gets turned into a {@link Component~GenericCallback}
  3893. *
  3894. * > Note: You can't use `window.clearTimeout` on the id returned by this function. This
  3895. * will cause its dispose listener not to get cleaned up! Please use
  3896. * {@link Component#clearTimeout} or {@link Component#dispose} instead.
  3897. *
  3898. * @param {Component~GenericCallback} fn
  3899. * The function that will be run after `timeout`.
  3900. *
  3901. * @param {number} timeout
  3902. * Timeout in milliseconds to delay before executing the specified function.
  3903. *
  3904. * @return {number}
  3905. * Returns a timeout ID that gets used to identify the timeout. It can also
  3906. * get used in {@link Component#clearTimeout} to clear the timeout that
  3907. * was set.
  3908. *
  3909. * @listens Component#dispose
  3910. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
  3911. */
  3912. Component.prototype.setTimeout = function setTimeout(fn, timeout) {
  3913. var _this2 = this;
  3914. // declare as variables so they are properly available in timeout function
  3915. // eslint-disable-next-line
  3916. var timeoutId, disposeFn;
  3917. fn = bind(this, fn);
  3918. timeoutId = window_1.setTimeout(function () {
  3919. _this2.off('dispose', disposeFn);
  3920. fn();
  3921. }, timeout);
  3922. disposeFn = function disposeFn() {
  3923. return _this2.clearTimeout(timeoutId);
  3924. };
  3925. disposeFn.guid = 'vjs-timeout-' + timeoutId;
  3926. this.on('dispose', disposeFn);
  3927. return timeoutId;
  3928. };
  3929. /**
  3930. * Clears a timeout that gets created via `window.setTimeout` or
  3931. * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
  3932. * use this function instead of `window.clearTimout`. If you don't your dispose
  3933. * listener will not get cleaned up until {@link Component#dispose}!
  3934. *
  3935. * @param {number} timeoutId
  3936. * The id of the timeout to clear. The return value of
  3937. * {@link Component#setTimeout} or `window.setTimeout`.
  3938. *
  3939. * @return {number}
  3940. * Returns the timeout id that was cleared.
  3941. *
  3942. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
  3943. */
  3944. Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
  3945. window_1.clearTimeout(timeoutId);
  3946. var disposeFn = function disposeFn() {};
  3947. disposeFn.guid = 'vjs-timeout-' + timeoutId;
  3948. this.off('dispose', disposeFn);
  3949. return timeoutId;
  3950. };
  3951. /**
  3952. * Creates a function that gets run every `x` milliseconds. This function is a wrapper
  3953. * around `window.setInterval`. There are a few reasons to use this one instead though.
  3954. * 1. It gets cleared via {@link Component#clearInterval} when
  3955. * {@link Component#dispose} gets called.
  3956. * 2. The function callback will be a {@link Component~GenericCallback}
  3957. *
  3958. * @param {Component~GenericCallback} fn
  3959. * The function to run every `x` seconds.
  3960. *
  3961. * @param {number} interval
  3962. * Execute the specified function every `x` milliseconds.
  3963. *
  3964. * @return {number}
  3965. * Returns an id that can be used to identify the interval. It can also be be used in
  3966. * {@link Component#clearInterval} to clear the interval.
  3967. *
  3968. * @listens Component#dispose
  3969. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
  3970. */
  3971. Component.prototype.setInterval = function setInterval(fn, interval) {
  3972. var _this3 = this;
  3973. fn = bind(this, fn);
  3974. var intervalId = window_1.setInterval(fn, interval);
  3975. var disposeFn = function disposeFn() {
  3976. return _this3.clearInterval(intervalId);
  3977. };
  3978. disposeFn.guid = 'vjs-interval-' + intervalId;
  3979. this.on('dispose', disposeFn);
  3980. return intervalId;
  3981. };
  3982. /**
  3983. * Clears an interval that gets created via `window.setInterval` or
  3984. * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
  3985. * use this function instead of `window.clearInterval`. If you don't your dispose
  3986. * listener will not get cleaned up until {@link Component#dispose}!
  3987. *
  3988. * @param {number} intervalId
  3989. * The id of the interval to clear. The return value of
  3990. * {@link Component#setInterval} or `window.setInterval`.
  3991. *
  3992. * @return {number}
  3993. * Returns the interval id that was cleared.
  3994. *
  3995. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
  3996. */
  3997. Component.prototype.clearInterval = function clearInterval(intervalId) {
  3998. window_1.clearInterval(intervalId);
  3999. var disposeFn = function disposeFn() {};
  4000. disposeFn.guid = 'vjs-interval-' + intervalId;
  4001. this.off('dispose', disposeFn);
  4002. return intervalId;
  4003. };
  4004. /**
  4005. * Queues up a callback to be passed to requestAnimationFrame (rAF), but
  4006. * with a few extra bonuses:
  4007. *
  4008. * - Supports browsers that do not support rAF by falling back to
  4009. * {@link Component#setTimeout}.
  4010. *
  4011. * - The callback is turned into a {@link Component~GenericCallback} (i.e.
  4012. * bound to the component).
  4013. *
  4014. * - Automatic cancellation of the rAF callback is handled if the component
  4015. * is disposed before it is called.
  4016. *
  4017. * @param {Component~GenericCallback} fn
  4018. * A function that will be bound to this component and executed just
  4019. * before the browser's next repaint.
  4020. *
  4021. * @return {number}
  4022. * Returns an rAF ID that gets used to identify the timeout. It can
  4023. * also be used in {@link Component#cancelAnimationFrame} to cancel
  4024. * the animation frame callback.
  4025. *
  4026. * @listens Component#dispose
  4027. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
  4028. */
  4029. Component.prototype.requestAnimationFrame = function requestAnimationFrame(fn) {
  4030. var _this4 = this;
  4031. // declare as variables so they are properly available in rAF function
  4032. // eslint-disable-next-line
  4033. var id, disposeFn;
  4034. if (this.supportsRaf_) {
  4035. fn = bind(this, fn);
  4036. id = window_1.requestAnimationFrame(function () {
  4037. _this4.off('dispose', disposeFn);
  4038. fn();
  4039. });
  4040. disposeFn = function disposeFn() {
  4041. return _this4.cancelAnimationFrame(id);
  4042. };
  4043. disposeFn.guid = 'vjs-raf-' + id;
  4044. this.on('dispose', disposeFn);
  4045. return id;
  4046. }
  4047. // Fall back to using a timer.
  4048. return this.setTimeout(fn, 1000 / 60);
  4049. };
  4050. /**
  4051. * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
  4052. * (rAF).
  4053. *
  4054. * If you queue an rAF callback via {@link Component#requestAnimationFrame},
  4055. * use this function instead of `window.cancelAnimationFrame`. If you don't,
  4056. * your dispose listener will not get cleaned up until {@link Component#dispose}!
  4057. *
  4058. * @param {number} id
  4059. * The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
  4060. *
  4061. * @return {number}
  4062. * Returns the rAF ID that was cleared.
  4063. *
  4064. * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
  4065. */
  4066. Component.prototype.cancelAnimationFrame = function cancelAnimationFrame(id) {
  4067. if (this.supportsRaf_) {
  4068. window_1.cancelAnimationFrame(id);
  4069. var disposeFn = function disposeFn() {};
  4070. disposeFn.guid = 'vjs-raf-' + id;
  4071. this.off('dispose', disposeFn);
  4072. return id;
  4073. }
  4074. // Fall back to using a timer.
  4075. return this.clearTimeout(id);
  4076. };
  4077. /**
  4078. * Register a `Component` with `videojs` given the name and the component.
  4079. *
  4080. * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
  4081. * should be registered using {@link Tech.registerTech} or
  4082. * {@link videojs:videojs.registerTech}.
  4083. *
  4084. * > NOTE: This function can also be seen on videojs as
  4085. * {@link videojs:videojs.registerComponent}.
  4086. *
  4087. * @param {string} name
  4088. * The name of the `Component` to register.
  4089. *
  4090. * @param {Component} ComponentToRegister
  4091. * The `Component` class to register.
  4092. *
  4093. * @return {Component}
  4094. * The `Component` that was registered.
  4095. */
  4096. Component.registerComponent = function registerComponent(name, ComponentToRegister) {
  4097. if (typeof name !== 'string' || !name) {
  4098. throw new Error('Illegal component name, "' + name + '"; must be a non-empty string.');
  4099. }
  4100. var Tech = Component.getComponent('Tech');
  4101. // We need to make sure this check is only done if Tech has been registered.
  4102. var isTech = Tech && Tech.isTech(ComponentToRegister);
  4103. var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
  4104. if (isTech || !isComp) {
  4105. var reason = void 0;
  4106. if (isTech) {
  4107. reason = 'techs must be registered using Tech.registerTech()';
  4108. } else {
  4109. reason = 'must be a Component subclass';
  4110. }
  4111. throw new Error('Illegal component, "' + name + '"; ' + reason + '.');
  4112. }
  4113. name = toTitleCase(name);
  4114. if (!Component.components_) {
  4115. Component.components_ = {};
  4116. }
  4117. var Player = Component.getComponent('Player');
  4118. if (name === 'Player' && Player && Player.players) {
  4119. var players = Player.players;
  4120. var playerNames = Object.keys(players);
  4121. // If we have players that were disposed, then their name will still be
  4122. // in Players.players. So, we must loop through and verify that the value
  4123. // for each item is not null. This allows registration of the Player component
  4124. // after all players have been disposed or before any were created.
  4125. if (players && playerNames.length > 0 && playerNames.map(function (pname) {
  4126. return players[pname];
  4127. }).every(Boolean)) {
  4128. throw new Error('Can not register Player component after player has been created.');
  4129. }
  4130. }
  4131. Component.components_[name] = ComponentToRegister;
  4132. return ComponentToRegister;
  4133. };
  4134. /**
  4135. * Get a `Component` based on the name it was registered with.
  4136. *
  4137. * @param {string} name
  4138. * The Name of the component to get.
  4139. *
  4140. * @return {Component}
  4141. * The `Component` that got registered under the given name.
  4142. *
  4143. * @deprecated In `videojs` 6 this will not return `Component`s that were not
  4144. * registered using {@link Component.registerComponent}. Currently we
  4145. * check the global `videojs` object for a `Component` name and
  4146. * return that if it exists.
  4147. */
  4148. Component.getComponent = function getComponent(name) {
  4149. if (!name) {
  4150. return;
  4151. }
  4152. name = toTitleCase(name);
  4153. if (Component.components_ && Component.components_[name]) {
  4154. return Component.components_[name];
  4155. }
  4156. };
  4157. return Component;
  4158. }();
  4159. /**
  4160. * Whether or not this component supports `requestAnimationFrame`.
  4161. *
  4162. * This is exposed primarily for testing purposes.
  4163. *
  4164. * @private
  4165. * @type {Boolean}
  4166. */
  4167. Component.prototype.supportsRaf_ = typeof window_1.requestAnimationFrame === 'function' && typeof window_1.cancelAnimationFrame === 'function';
  4168. Component.registerComponent('Component', Component);
  4169. /**
  4170. * @file time-ranges.js
  4171. * @module time-ranges
  4172. */
  4173. /**
  4174. * Returns the time for the specified index at the start or end
  4175. * of a TimeRange object.
  4176. *
  4177. * @function time-ranges:indexFunction
  4178. *
  4179. * @param {number} [index=0]
  4180. * The range number to return the time for.
  4181. *
  4182. * @return {number}
  4183. * The time that offset at the specified index.
  4184. *
  4185. * @depricated index must be set to a value, in the future this will throw an error.
  4186. */
  4187. /**
  4188. * An object that contains ranges of time for various reasons.
  4189. *
  4190. * @typedef {Object} TimeRange
  4191. *
  4192. * @property {number} length
  4193. * The number of time ranges represented by this Object
  4194. *
  4195. * @property {time-ranges:indexFunction} start
  4196. * Returns the time offset at which a specified time range begins.
  4197. *
  4198. * @property {time-ranges:indexFunction} end
  4199. * Returns the time offset at which a specified time range ends.
  4200. *
  4201. * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
  4202. */
  4203. /**
  4204. * Check if any of the time ranges are over the maximum index.
  4205. *
  4206. * @param {string} fnName
  4207. * The function name to use for logging
  4208. *
  4209. * @param {number} index
  4210. * The index to check
  4211. *
  4212. * @param {number} maxIndex
  4213. * The maximum possible index
  4214. *
  4215. * @throws {Error} if the timeRanges provided are over the maxIndex
  4216. */
  4217. function rangeCheck(fnName, index, maxIndex) {
  4218. if (typeof index !== 'number' || index < 0 || index > maxIndex) {
  4219. throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is non-numeric or out of bounds (0-' + maxIndex + ').');
  4220. }
  4221. }
  4222. /**
  4223. * Get the time for the specified index at the start or end
  4224. * of a TimeRange object.
  4225. *
  4226. * @param {string} fnName
  4227. * The function name to use for logging
  4228. *
  4229. * @param {string} valueIndex
  4230. * The proprety that should be used to get the time. should be 'start' or 'end'
  4231. *
  4232. * @param {Array} ranges
  4233. * An array of time ranges
  4234. *
  4235. * @param {Array} [rangeIndex=0]
  4236. * The index to start the search at
  4237. *
  4238. * @return {number}
  4239. * The time that offset at the specified index.
  4240. *
  4241. *
  4242. * @depricated rangeIndex must be set to a value, in the future this will throw an error.
  4243. * @throws {Error} if rangeIndex is more than the length of ranges
  4244. */
  4245. function getRange(fnName, valueIndex, ranges, rangeIndex) {
  4246. rangeCheck(fnName, rangeIndex, ranges.length - 1);
  4247. return ranges[rangeIndex][valueIndex];
  4248. }
  4249. /**
  4250. * Create a time range object given ranges of time.
  4251. *
  4252. * @param {Array} [ranges]
  4253. * An array of time ranges.
  4254. */
  4255. function createTimeRangesObj(ranges) {
  4256. if (ranges === undefined || ranges.length === 0) {
  4257. return {
  4258. length: 0,
  4259. start: function start() {
  4260. throw new Error('This TimeRanges object is empty');
  4261. },
  4262. end: function end() {
  4263. throw new Error('This TimeRanges object is empty');
  4264. }
  4265. };
  4266. }
  4267. return {
  4268. length: ranges.length,
  4269. start: getRange.bind(null, 'start', 0, ranges),
  4270. end: getRange.bind(null, 'end', 1, ranges)
  4271. };
  4272. }
  4273. /**
  4274. * Should create a fake `TimeRange` object which mimics an HTML5 time range instance.
  4275. *
  4276. * @param {number|Array} start
  4277. * The start of a single range or an array of ranges
  4278. *
  4279. * @param {number} end
  4280. * The end of a single range.
  4281. *
  4282. * @private
  4283. */
  4284. function createTimeRanges(start, end) {
  4285. if (Array.isArray(start)) {
  4286. return createTimeRangesObj(start);
  4287. } else if (start === undefined || end === undefined) {
  4288. return createTimeRangesObj();
  4289. }
  4290. return createTimeRangesObj([[start, end]]);
  4291. }
  4292. /**
  4293. * @file buffer.js
  4294. * @module buffer
  4295. */
  4296. /**
  4297. * Compute the percentage of the media that has been buffered.
  4298. *
  4299. * @param {TimeRange} buffered
  4300. * The current `TimeRange` object representing buffered time ranges
  4301. *
  4302. * @param {number} duration
  4303. * Total duration of the media
  4304. *
  4305. * @return {number}
  4306. * Percent buffered of the total duration in decimal form.
  4307. */
  4308. function bufferedPercent(buffered, duration) {
  4309. var bufferedDuration = 0;
  4310. var start = void 0;
  4311. var end = void 0;
  4312. if (!duration) {
  4313. return 0;
  4314. }
  4315. if (!buffered || !buffered.length) {
  4316. buffered = createTimeRanges(0, 0);
  4317. }
  4318. for (var i = 0; i < buffered.length; i++) {
  4319. start = buffered.start(i);
  4320. end = buffered.end(i);
  4321. // buffered end can be bigger than duration by a very small fraction
  4322. if (end > duration) {
  4323. end = duration;
  4324. }
  4325. bufferedDuration += end - start;
  4326. }
  4327. return bufferedDuration / duration;
  4328. }
  4329. /**
  4330. * @file fullscreen-api.js
  4331. * @module fullscreen-api
  4332. * @private
  4333. */
  4334. /**
  4335. * Store the browser-specific methods for the fullscreen API.
  4336. *
  4337. * @type {Object}
  4338. * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
  4339. * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
  4340. */
  4341. var FullscreenApi = {};
  4342. // browser API methods
  4343. var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'],
  4344. // WebKit
  4345. ['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
  4346. // Old WebKit (Safari 5.1)
  4347. ['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
  4348. // Mozilla
  4349. ['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
  4350. // Microsoft
  4351. ['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];
  4352. var specApi = apiMap[0];
  4353. var browserApi = void 0;
  4354. // determine the supported set of functions
  4355. for (var i = 0; i < apiMap.length; i++) {
  4356. // check for exitFullscreen function
  4357. if (apiMap[i][1] in document_1) {
  4358. browserApi = apiMap[i];
  4359. break;
  4360. }
  4361. }
  4362. // map the browser API names to the spec API names
  4363. if (browserApi) {
  4364. for (var _i = 0; _i < browserApi.length; _i++) {
  4365. FullscreenApi[specApi[_i]] = browserApi[_i];
  4366. }
  4367. }
  4368. /**
  4369. * @file media-error.js
  4370. */
  4371. /**
  4372. * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
  4373. *
  4374. * @param {number|string|Object|MediaError} value
  4375. * This can be of multiple types:
  4376. * - number: should be a standard error code
  4377. * - string: an error message (the code will be 0)
  4378. * - Object: arbitrary properties
  4379. * - `MediaError` (native): used to populate a video.js `MediaError` object
  4380. * - `MediaError` (video.js): will return itself if it's already a
  4381. * video.js `MediaError` object.
  4382. *
  4383. * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
  4384. * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
  4385. *
  4386. * @class MediaError
  4387. */
  4388. function MediaError(value) {
  4389. // Allow redundant calls to this constructor to avoid having `instanceof`
  4390. // checks peppered around the code.
  4391. if (value instanceof MediaError) {
  4392. return value;
  4393. }
  4394. if (typeof value === 'number') {
  4395. this.code = value;
  4396. } else if (typeof value === 'string') {
  4397. // default code is zero, so this is a custom error
  4398. this.message = value;
  4399. } else if (isObject(value)) {
  4400. // We assign the `code` property manually because native `MediaError` objects
  4401. // do not expose it as an own/enumerable property of the object.
  4402. if (typeof value.code === 'number') {
  4403. this.code = value.code;
  4404. }
  4405. assign(this, value);
  4406. }
  4407. if (!this.message) {
  4408. this.message = MediaError.defaultMessages[this.code] || '';
  4409. }
  4410. }
  4411. /**
  4412. * The error code that refers two one of the defined `MediaError` types
  4413. *
  4414. * @type {Number}
  4415. */
  4416. MediaError.prototype.code = 0;
  4417. /**
  4418. * An optional message that to show with the error. Message is not part of the HTML5
  4419. * video spec but allows for more informative custom errors.
  4420. *
  4421. * @type {String}
  4422. */
  4423. MediaError.prototype.message = '';
  4424. /**
  4425. * An optional status code that can be set by plugins to allow even more detail about
  4426. * the error. For example a plugin might provide a specific HTTP status code and an
  4427. * error message for that code. Then when the plugin gets that error this class will
  4428. * know how to display an error message for it. This allows a custom message to show
  4429. * up on the `Player` error overlay.
  4430. *
  4431. * @type {Array}
  4432. */
  4433. MediaError.prototype.status = null;
  4434. /**
  4435. * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
  4436. * specification listed under {@link MediaError} for more information.
  4437. *
  4438. * @enum {array}
  4439. * @readonly
  4440. * @property {string} 0 - MEDIA_ERR_CUSTOM
  4441. * @property {string} 1 - MEDIA_ERR_CUSTOM
  4442. * @property {string} 2 - MEDIA_ERR_ABORTED
  4443. * @property {string} 3 - MEDIA_ERR_NETWORK
  4444. * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
  4445. * @property {string} 5 - MEDIA_ERR_ENCRYPTED
  4446. */
  4447. MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
  4448. /**
  4449. * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
  4450. *
  4451. * @type {Array}
  4452. * @constant
  4453. */
  4454. MediaError.defaultMessages = {
  4455. 1: 'You aborted the media playback',
  4456. 2: 'A network error caused the media download to fail part-way.',
  4457. 3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
  4458. 4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
  4459. 5: 'The media is encrypted and we do not have the keys to decrypt it.'
  4460. };
  4461. // Add types as properties on MediaError
  4462. // e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
  4463. for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
  4464. MediaError[MediaError.errorTypes[errNum]] = errNum;
  4465. // values should be accessible on both the class and instance
  4466. MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
  4467. }
  4468. var tuple = SafeParseTuple;
  4469. function SafeParseTuple(obj, reviver) {
  4470. var json;
  4471. var error = null;
  4472. try {
  4473. json = JSON.parse(obj, reviver);
  4474. } catch (err) {
  4475. error = err;
  4476. }
  4477. return [error, json]
  4478. }
  4479. /**
  4480. * Returns whether an object is `Promise`-like (i.e. has a `then` method).
  4481. *
  4482. * @param {Object} value
  4483. * An object that may or may not be `Promise`-like.
  4484. *
  4485. * @return {Boolean}
  4486. * Whether or not the object is `Promise`-like.
  4487. */
  4488. function isPromise(value) {
  4489. return value !== undefined && value !== null && typeof value.then === 'function';
  4490. }
  4491. /**
  4492. * Silence a Promise-like object.
  4493. *
  4494. * This is useful for avoiding non-harmful, but potentially confusing "uncaught
  4495. * play promise" rejection error messages.
  4496. *
  4497. * @param {Object} value
  4498. * An object that may or may not be `Promise`-like.
  4499. */
  4500. function silencePromise(value) {
  4501. if (isPromise(value)) {
  4502. value.then(null, function (e) {});
  4503. }
  4504. }
  4505. /**
  4506. * @file text-track-list-converter.js Utilities for capturing text track state and
  4507. * re-creating tracks based on a capture.
  4508. *
  4509. * @module text-track-list-converter
  4510. */
  4511. /**
  4512. * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
  4513. * represents the {@link TextTrack}'s state.
  4514. *
  4515. * @param {TextTrack} track
  4516. * The text track to query.
  4517. *
  4518. * @return {Object}
  4519. * A serializable javascript representation of the TextTrack.
  4520. * @private
  4521. */
  4522. var trackToJson_ = function trackToJson_(track) {
  4523. var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
  4524. if (track[prop]) {
  4525. acc[prop] = track[prop];
  4526. }
  4527. return acc;
  4528. }, {
  4529. cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
  4530. return {
  4531. startTime: cue.startTime,
  4532. endTime: cue.endTime,
  4533. text: cue.text,
  4534. id: cue.id
  4535. };
  4536. })
  4537. });
  4538. return ret;
  4539. };
  4540. /**
  4541. * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
  4542. * state of all {@link TextTrack}s currently configured. The return array is compatible with
  4543. * {@link text-track-list-converter:jsonToTextTracks}.
  4544. *
  4545. * @param {Tech} tech
  4546. * The tech object to query
  4547. *
  4548. * @return {Array}
  4549. * A serializable javascript representation of the {@link Tech}s
  4550. * {@link TextTrackList}.
  4551. */
  4552. var textTracksToJson = function textTracksToJson(tech) {
  4553. var trackEls = tech.$$('track');
  4554. var trackObjs = Array.prototype.map.call(trackEls, function (t) {
  4555. return t.track;
  4556. });
  4557. var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
  4558. var json = trackToJson_(trackEl.track);
  4559. if (trackEl.src) {
  4560. json.src = trackEl.src;
  4561. }
  4562. return json;
  4563. });
  4564. return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
  4565. return trackObjs.indexOf(track) === -1;
  4566. }).map(trackToJson_));
  4567. };
  4568. /**
  4569. * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
  4570. * object {@link TextTrack} representations.
  4571. *
  4572. * @param {Array} json
  4573. * An array of `TextTrack` representation objects, like those that would be
  4574. * produced by `textTracksToJson`.
  4575. *
  4576. * @param {Tech} tech
  4577. * The `Tech` to create the `TextTrack`s on.
  4578. */
  4579. var jsonToTextTracks = function jsonToTextTracks(json, tech) {
  4580. json.forEach(function (track) {
  4581. var addedTrack = tech.addRemoteTextTrack(track).track;
  4582. if (!track.src && track.cues) {
  4583. track.cues.forEach(function (cue) {
  4584. return addedTrack.addCue(cue);
  4585. });
  4586. }
  4587. });
  4588. return tech.textTracks();
  4589. };
  4590. var textTrackConverter = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ };
  4591. /**
  4592. * @file modal-dialog.js
  4593. */
  4594. var MODAL_CLASS_NAME = 'vjs-modal-dialog';
  4595. var ESC = 27;
  4596. /**
  4597. * The `ModalDialog` displays over the video and its controls, which blocks
  4598. * interaction with the player until it is closed.
  4599. *
  4600. * Modal dialogs include a "Close" button and will close when that button
  4601. * is activated - or when ESC is pressed anywhere.
  4602. *
  4603. * @extends Component
  4604. */
  4605. var ModalDialog = function (_Component) {
  4606. inherits(ModalDialog, _Component);
  4607. /**
  4608. * Create an instance of this class.
  4609. *
  4610. * @param {Player} player
  4611. * The `Player` that this class should be attached to.
  4612. *
  4613. * @param {Object} [options]
  4614. * The key/value store of player options.
  4615. *
  4616. * @param {Mixed} [options.content=undefined]
  4617. * Provide customized content for this modal.
  4618. *
  4619. * @param {string} [options.description]
  4620. * A text description for the modal, primarily for accessibility.
  4621. *
  4622. * @param {boolean} [options.fillAlways=false]
  4623. * Normally, modals are automatically filled only the first time
  4624. * they open. This tells the modal to refresh its content
  4625. * every time it opens.
  4626. *
  4627. * @param {string} [options.label]
  4628. * A text label for the modal, primarily for accessibility.
  4629. *
  4630. * @param {boolean} [options.temporary=true]
  4631. * If `true`, the modal can only be opened once; it will be
  4632. * disposed as soon as it's closed.
  4633. *
  4634. * @param {boolean} [options.uncloseable=false]
  4635. * If `true`, the user will not be able to close the modal
  4636. * through the UI in the normal ways. Programmatic closing is
  4637. * still possible.
  4638. */
  4639. function ModalDialog(player, options) {
  4640. classCallCheck(this, ModalDialog);
  4641. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  4642. _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
  4643. _this.closeable(!_this.options_.uncloseable);
  4644. _this.content(_this.options_.content);
  4645. // Make sure the contentEl is defined AFTER any children are initialized
  4646. // because we only want the contents of the modal in the contentEl
  4647. // (not the UI elements like the close button).
  4648. _this.contentEl_ = createEl('div', {
  4649. className: MODAL_CLASS_NAME + '-content'
  4650. }, {
  4651. role: 'document'
  4652. });
  4653. _this.descEl_ = createEl('p', {
  4654. className: MODAL_CLASS_NAME + '-description vjs-control-text',
  4655. id: _this.el().getAttribute('aria-describedby')
  4656. });
  4657. textContent(_this.descEl_, _this.description());
  4658. _this.el_.appendChild(_this.descEl_);
  4659. _this.el_.appendChild(_this.contentEl_);
  4660. return _this;
  4661. }
  4662. /**
  4663. * Create the `ModalDialog`'s DOM element
  4664. *
  4665. * @return {Element}
  4666. * The DOM element that gets created.
  4667. */
  4668. ModalDialog.prototype.createEl = function createEl$$1() {
  4669. return _Component.prototype.createEl.call(this, 'div', {
  4670. className: this.buildCSSClass(),
  4671. tabIndex: -1
  4672. }, {
  4673. 'aria-describedby': this.id() + '_description',
  4674. 'aria-hidden': 'true',
  4675. 'aria-label': this.label(),
  4676. 'role': 'dialog'
  4677. });
  4678. };
  4679. ModalDialog.prototype.dispose = function dispose() {
  4680. this.contentEl_ = null;
  4681. this.descEl_ = null;
  4682. this.previouslyActiveEl_ = null;
  4683. _Component.prototype.dispose.call(this);
  4684. };
  4685. /**
  4686. * Builds the default DOM `className`.
  4687. *
  4688. * @return {string}
  4689. * The DOM `className` for this object.
  4690. */
  4691. ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
  4692. return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
  4693. };
  4694. /**
  4695. * Handles `keydown` events on the document, looking for ESC, which closes
  4696. * the modal.
  4697. *
  4698. * @param {EventTarget~Event} e
  4699. * The keypress that triggered this event.
  4700. *
  4701. * @listens keydown
  4702. */
  4703. ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
  4704. if (e.which === ESC && this.closeable()) {
  4705. this.close();
  4706. }
  4707. };
  4708. /**
  4709. * Returns the label string for this modal. Primarily used for accessibility.
  4710. *
  4711. * @return {string}
  4712. * the localized or raw label of this modal.
  4713. */
  4714. ModalDialog.prototype.label = function label() {
  4715. return this.localize(this.options_.label || 'Modal Window');
  4716. };
  4717. /**
  4718. * Returns the description string for this modal. Primarily used for
  4719. * accessibility.
  4720. *
  4721. * @return {string}
  4722. * The localized or raw description of this modal.
  4723. */
  4724. ModalDialog.prototype.description = function description() {
  4725. var desc = this.options_.description || this.localize('This is a modal window.');
  4726. // Append a universal closeability message if the modal is closeable.
  4727. if (this.closeable()) {
  4728. desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
  4729. }
  4730. return desc;
  4731. };
  4732. /**
  4733. * Opens the modal.
  4734. *
  4735. * @fires ModalDialog#beforemodalopen
  4736. * @fires ModalDialog#modalopen
  4737. */
  4738. ModalDialog.prototype.open = function open() {
  4739. if (!this.opened_) {
  4740. var player = this.player();
  4741. /**
  4742. * Fired just before a `ModalDialog` is opened.
  4743. *
  4744. * @event ModalDialog#beforemodalopen
  4745. * @type {EventTarget~Event}
  4746. */
  4747. this.trigger('beforemodalopen');
  4748. this.opened_ = true;
  4749. // Fill content if the modal has never opened before and
  4750. // never been filled.
  4751. if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
  4752. this.fill();
  4753. }
  4754. // If the player was playing, pause it and take note of its previously
  4755. // playing state.
  4756. this.wasPlaying_ = !player.paused();
  4757. if (this.options_.pauseOnOpen && this.wasPlaying_) {
  4758. player.pause();
  4759. }
  4760. if (this.closeable()) {
  4761. this.on(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
  4762. }
  4763. // Hide controls and note if they were enabled.
  4764. this.hadControls_ = player.controls();
  4765. player.controls(false);
  4766. this.show();
  4767. this.conditionalFocus_();
  4768. this.el().setAttribute('aria-hidden', 'false');
  4769. /**
  4770. * Fired just after a `ModalDialog` is opened.
  4771. *
  4772. * @event ModalDialog#modalopen
  4773. * @type {EventTarget~Event}
  4774. */
  4775. this.trigger('modalopen');
  4776. this.hasBeenOpened_ = true;
  4777. }
  4778. };
  4779. /**
  4780. * If the `ModalDialog` is currently open or closed.
  4781. *
  4782. * @param {boolean} [value]
  4783. * If given, it will open (`true`) or close (`false`) the modal.
  4784. *
  4785. * @return {boolean}
  4786. * the current open state of the modaldialog
  4787. */
  4788. ModalDialog.prototype.opened = function opened(value) {
  4789. if (typeof value === 'boolean') {
  4790. this[value ? 'open' : 'close']();
  4791. }
  4792. return this.opened_;
  4793. };
  4794. /**
  4795. * Closes the modal, does nothing if the `ModalDialog` is
  4796. * not open.
  4797. *
  4798. * @fires ModalDialog#beforemodalclose
  4799. * @fires ModalDialog#modalclose
  4800. */
  4801. ModalDialog.prototype.close = function close() {
  4802. if (!this.opened_) {
  4803. return;
  4804. }
  4805. var player = this.player();
  4806. /**
  4807. * Fired just before a `ModalDialog` is closed.
  4808. *
  4809. * @event ModalDialog#beforemodalclose
  4810. * @type {EventTarget~Event}
  4811. */
  4812. this.trigger('beforemodalclose');
  4813. this.opened_ = false;
  4814. if (this.wasPlaying_ && this.options_.pauseOnOpen) {
  4815. player.play();
  4816. }
  4817. if (this.closeable()) {
  4818. this.off(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
  4819. }
  4820. if (this.hadControls_) {
  4821. player.controls(true);
  4822. }
  4823. this.hide();
  4824. this.el().setAttribute('aria-hidden', 'true');
  4825. /**
  4826. * Fired just after a `ModalDialog` is closed.
  4827. *
  4828. * @event ModalDialog#modalclose
  4829. * @type {EventTarget~Event}
  4830. */
  4831. this.trigger('modalclose');
  4832. this.conditionalBlur_();
  4833. if (this.options_.temporary) {
  4834. this.dispose();
  4835. }
  4836. };
  4837. /**
  4838. * Check to see if the `ModalDialog` is closeable via the UI.
  4839. *
  4840. * @param {boolean} [value]
  4841. * If given as a boolean, it will set the `closeable` option.
  4842. *
  4843. * @return {boolean}
  4844. * Returns the final value of the closable option.
  4845. */
  4846. ModalDialog.prototype.closeable = function closeable(value) {
  4847. if (typeof value === 'boolean') {
  4848. var closeable = this.closeable_ = !!value;
  4849. var close = this.getChild('closeButton');
  4850. // If this is being made closeable and has no close button, add one.
  4851. if (closeable && !close) {
  4852. // The close button should be a child of the modal - not its
  4853. // content element, so temporarily change the content element.
  4854. var temp = this.contentEl_;
  4855. this.contentEl_ = this.el_;
  4856. close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' });
  4857. this.contentEl_ = temp;
  4858. this.on(close, 'close', this.close);
  4859. }
  4860. // If this is being made uncloseable and has a close button, remove it.
  4861. if (!closeable && close) {
  4862. this.off(close, 'close', this.close);
  4863. this.removeChild(close);
  4864. close.dispose();
  4865. }
  4866. }
  4867. return this.closeable_;
  4868. };
  4869. /**
  4870. * Fill the modal's content element with the modal's "content" option.
  4871. * The content element will be emptied before this change takes place.
  4872. */
  4873. ModalDialog.prototype.fill = function fill() {
  4874. this.fillWith(this.content());
  4875. };
  4876. /**
  4877. * Fill the modal's content element with arbitrary content.
  4878. * The content element will be emptied before this change takes place.
  4879. *
  4880. * @fires ModalDialog#beforemodalfill
  4881. * @fires ModalDialog#modalfill
  4882. *
  4883. * @param {Mixed} [content]
  4884. * The same rules apply to this as apply to the `content` option.
  4885. */
  4886. ModalDialog.prototype.fillWith = function fillWith(content) {
  4887. var contentEl = this.contentEl();
  4888. var parentEl = contentEl.parentNode;
  4889. var nextSiblingEl = contentEl.nextSibling;
  4890. /**
  4891. * Fired just before a `ModalDialog` is filled with content.
  4892. *
  4893. * @event ModalDialog#beforemodalfill
  4894. * @type {EventTarget~Event}
  4895. */
  4896. this.trigger('beforemodalfill');
  4897. this.hasBeenFilled_ = true;
  4898. // Detach the content element from the DOM before performing
  4899. // manipulation to avoid modifying the live DOM multiple times.
  4900. parentEl.removeChild(contentEl);
  4901. this.empty();
  4902. insertContent(contentEl, content);
  4903. /**
  4904. * Fired just after a `ModalDialog` is filled with content.
  4905. *
  4906. * @event ModalDialog#modalfill
  4907. * @type {EventTarget~Event}
  4908. */
  4909. this.trigger('modalfill');
  4910. // Re-inject the re-filled content element.
  4911. if (nextSiblingEl) {
  4912. parentEl.insertBefore(contentEl, nextSiblingEl);
  4913. } else {
  4914. parentEl.appendChild(contentEl);
  4915. }
  4916. // make sure that the close button is last in the dialog DOM
  4917. var closeButton = this.getChild('closeButton');
  4918. if (closeButton) {
  4919. parentEl.appendChild(closeButton.el_);
  4920. }
  4921. };
  4922. /**
  4923. * Empties the content element. This happens anytime the modal is filled.
  4924. *
  4925. * @fires ModalDialog#beforemodalempty
  4926. * @fires ModalDialog#modalempty
  4927. */
  4928. ModalDialog.prototype.empty = function empty() {
  4929. /**
  4930. * Fired just before a `ModalDialog` is emptied.
  4931. *
  4932. * @event ModalDialog#beforemodalempty
  4933. * @type {EventTarget~Event}
  4934. */
  4935. this.trigger('beforemodalempty');
  4936. emptyEl(this.contentEl());
  4937. /**
  4938. * Fired just after a `ModalDialog` is emptied.
  4939. *
  4940. * @event ModalDialog#modalempty
  4941. * @type {EventTarget~Event}
  4942. */
  4943. this.trigger('modalempty');
  4944. };
  4945. /**
  4946. * Gets or sets the modal content, which gets normalized before being
  4947. * rendered into the DOM.
  4948. *
  4949. * This does not update the DOM or fill the modal, but it is called during
  4950. * that process.
  4951. *
  4952. * @param {Mixed} [value]
  4953. * If defined, sets the internal content value to be used on the
  4954. * next call(s) to `fill`. This value is normalized before being
  4955. * inserted. To "clear" the internal content value, pass `null`.
  4956. *
  4957. * @return {Mixed}
  4958. * The current content of the modal dialog
  4959. */
  4960. ModalDialog.prototype.content = function content(value) {
  4961. if (typeof value !== 'undefined') {
  4962. this.content_ = value;
  4963. }
  4964. return this.content_;
  4965. };
  4966. /**
  4967. * conditionally focus the modal dialog if focus was previously on the player.
  4968. *
  4969. * @private
  4970. */
  4971. ModalDialog.prototype.conditionalFocus_ = function conditionalFocus_() {
  4972. var activeEl = document_1.activeElement;
  4973. var playerEl = this.player_.el_;
  4974. this.previouslyActiveEl_ = null;
  4975. if (playerEl.contains(activeEl) || playerEl === activeEl) {
  4976. this.previouslyActiveEl_ = activeEl;
  4977. this.focus();
  4978. this.on(document_1, 'keydown', this.handleKeyDown);
  4979. }
  4980. };
  4981. /**
  4982. * conditionally blur the element and refocus the last focused element
  4983. *
  4984. * @private
  4985. */
  4986. ModalDialog.prototype.conditionalBlur_ = function conditionalBlur_() {
  4987. if (this.previouslyActiveEl_) {
  4988. this.previouslyActiveEl_.focus();
  4989. this.previouslyActiveEl_ = null;
  4990. }
  4991. this.off(document_1, 'keydown', this.handleKeyDown);
  4992. };
  4993. /**
  4994. * Keydown handler. Attached when modal is focused.
  4995. *
  4996. * @listens keydown
  4997. */
  4998. ModalDialog.prototype.handleKeyDown = function handleKeyDown(event) {
  4999. // exit early if it isn't a tab key
  5000. if (event.which !== 9) {
  5001. return;
  5002. }
  5003. var focusableEls = this.focusableEls_();
  5004. var activeEl = this.el_.querySelector(':focus');
  5005. var focusIndex = void 0;
  5006. for (var i = 0; i < focusableEls.length; i++) {
  5007. if (activeEl === focusableEls[i]) {
  5008. focusIndex = i;
  5009. break;
  5010. }
  5011. }
  5012. if (document_1.activeElement === this.el_) {
  5013. focusIndex = 0;
  5014. }
  5015. if (event.shiftKey && focusIndex === 0) {
  5016. focusableEls[focusableEls.length - 1].focus();
  5017. event.preventDefault();
  5018. } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
  5019. focusableEls[0].focus();
  5020. event.preventDefault();
  5021. }
  5022. };
  5023. /**
  5024. * get all focusable elements
  5025. *
  5026. * @private
  5027. */
  5028. ModalDialog.prototype.focusableEls_ = function focusableEls_() {
  5029. var allChildren = this.el_.querySelectorAll('*');
  5030. return Array.prototype.filter.call(allChildren, function (child) {
  5031. 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');
  5032. });
  5033. };
  5034. return ModalDialog;
  5035. }(Component);
  5036. /**
  5037. * Default options for `ModalDialog` default options.
  5038. *
  5039. * @type {Object}
  5040. * @private
  5041. */
  5042. ModalDialog.prototype.options_ = {
  5043. pauseOnOpen: true,
  5044. temporary: true
  5045. };
  5046. Component.registerComponent('ModalDialog', ModalDialog);
  5047. /**
  5048. * @file track-list.js
  5049. */
  5050. /**
  5051. * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
  5052. * {@link VideoTrackList}
  5053. *
  5054. * @extends EventTarget
  5055. */
  5056. var TrackList = function (_EventTarget) {
  5057. inherits(TrackList, _EventTarget);
  5058. /**
  5059. * Create an instance of this class
  5060. *
  5061. * @param {Track[]} tracks
  5062. * A list of tracks to initialize the list with.
  5063. *
  5064. * @param {Object} [list]
  5065. * The child object with inheritance done manually for ie8.
  5066. *
  5067. * @abstract
  5068. */
  5069. function TrackList() {
  5070. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5071. var _ret;
  5072. var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
  5073. classCallCheck(this, TrackList);
  5074. var _this = possibleConstructorReturn(this, _EventTarget.call(this));
  5075. if (!list) {
  5076. list = _this; // eslint-disable-line
  5077. if (IS_IE8) {
  5078. list = document_1.createElement('custom');
  5079. for (var prop in TrackList.prototype) {
  5080. if (prop !== 'constructor') {
  5081. list[prop] = TrackList.prototype[prop];
  5082. }
  5083. }
  5084. }
  5085. }
  5086. list.tracks_ = [];
  5087. /**
  5088. * @memberof TrackList
  5089. * @member {number} length
  5090. * The current number of `Track`s in the this Trackist.
  5091. * @instance
  5092. */
  5093. Object.defineProperty(list, 'length', {
  5094. get: function get$$1() {
  5095. return this.tracks_.length;
  5096. }
  5097. });
  5098. for (var i = 0; i < tracks.length; i++) {
  5099. list.addTrack(tracks[i]);
  5100. }
  5101. // must return the object, as for ie8 it will not be this
  5102. // but a reference to a document object
  5103. return _ret = list, possibleConstructorReturn(_this, _ret);
  5104. }
  5105. /**
  5106. * Add a {@link Track} to the `TrackList`
  5107. *
  5108. * @param {Track} track
  5109. * The audio, video, or text track to add to the list.
  5110. *
  5111. * @fires TrackList#addtrack
  5112. */
  5113. TrackList.prototype.addTrack = function addTrack(track) {
  5114. var index = this.tracks_.length;
  5115. if (!('' + index in this)) {
  5116. Object.defineProperty(this, index, {
  5117. get: function get$$1() {
  5118. return this.tracks_[index];
  5119. }
  5120. });
  5121. }
  5122. // Do not add duplicate tracks
  5123. if (this.tracks_.indexOf(track) === -1) {
  5124. this.tracks_.push(track);
  5125. /**
  5126. * Triggered when a track is added to a track list.
  5127. *
  5128. * @event TrackList#addtrack
  5129. * @type {EventTarget~Event}
  5130. * @property {Track} track
  5131. * A reference to track that was added.
  5132. */
  5133. this.trigger({
  5134. track: track,
  5135. type: 'addtrack'
  5136. });
  5137. }
  5138. };
  5139. /**
  5140. * Remove a {@link Track} from the `TrackList`
  5141. *
  5142. * @param {Track} rtrack
  5143. * The audio, video, or text track to remove from the list.
  5144. *
  5145. * @fires TrackList#removetrack
  5146. */
  5147. TrackList.prototype.removeTrack = function removeTrack(rtrack) {
  5148. var track = void 0;
  5149. for (var i = 0, l = this.length; i < l; i++) {
  5150. if (this[i] === rtrack) {
  5151. track = this[i];
  5152. if (track.off) {
  5153. track.off();
  5154. }
  5155. this.tracks_.splice(i, 1);
  5156. break;
  5157. }
  5158. }
  5159. if (!track) {
  5160. return;
  5161. }
  5162. /**
  5163. * Triggered when a track is removed from track list.
  5164. *
  5165. * @event TrackList#removetrack
  5166. * @type {EventTarget~Event}
  5167. * @property {Track} track
  5168. * A reference to track that was removed.
  5169. */
  5170. this.trigger({
  5171. track: track,
  5172. type: 'removetrack'
  5173. });
  5174. };
  5175. /**
  5176. * Get a Track from the TrackList by a tracks id
  5177. *
  5178. * @param {String} id - the id of the track to get
  5179. * @method getTrackById
  5180. * @return {Track}
  5181. * @private
  5182. */
  5183. TrackList.prototype.getTrackById = function getTrackById(id) {
  5184. var result = null;
  5185. for (var i = 0, l = this.length; i < l; i++) {
  5186. var track = this[i];
  5187. if (track.id === id) {
  5188. result = track;
  5189. break;
  5190. }
  5191. }
  5192. return result;
  5193. };
  5194. return TrackList;
  5195. }(EventTarget);
  5196. /**
  5197. * Triggered when a different track is selected/enabled.
  5198. *
  5199. * @event TrackList#change
  5200. * @type {EventTarget~Event}
  5201. */
  5202. /**
  5203. * Events that can be called with on + eventName. See {@link EventHandler}.
  5204. *
  5205. * @property {Object} TrackList#allowedEvents_
  5206. * @private
  5207. */
  5208. TrackList.prototype.allowedEvents_ = {
  5209. change: 'change',
  5210. addtrack: 'addtrack',
  5211. removetrack: 'removetrack'
  5212. };
  5213. // emulate attribute EventHandler support to allow for feature detection
  5214. for (var event in TrackList.prototype.allowedEvents_) {
  5215. TrackList.prototype['on' + event] = null;
  5216. }
  5217. /**
  5218. * @file audio-track-list.js
  5219. */
  5220. /**
  5221. * Anywhere we call this function we diverge from the spec
  5222. * as we only support one enabled audiotrack at a time
  5223. *
  5224. * @param {AudioTrackList} list
  5225. * list to work on
  5226. *
  5227. * @param {AudioTrack} track
  5228. * The track to skip
  5229. *
  5230. * @private
  5231. */
  5232. var disableOthers = function disableOthers(list, track) {
  5233. for (var i = 0; i < list.length; i++) {
  5234. if (!Object.keys(list[i]).length || track.id === list[i].id) {
  5235. continue;
  5236. }
  5237. // another audio track is enabled, disable it
  5238. list[i].enabled = false;
  5239. }
  5240. };
  5241. /**
  5242. * The current list of {@link AudioTrack} for a media file.
  5243. *
  5244. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
  5245. * @extends TrackList
  5246. */
  5247. var AudioTrackList = function (_TrackList) {
  5248. inherits(AudioTrackList, _TrackList);
  5249. /**
  5250. * Create an instance of this class.
  5251. *
  5252. * @param {AudioTrack[]} [tracks=[]]
  5253. * A list of `AudioTrack` to instantiate the list with.
  5254. */
  5255. function AudioTrackList() {
  5256. var _this, _ret;
  5257. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5258. classCallCheck(this, AudioTrackList);
  5259. var list = void 0;
  5260. // make sure only 1 track is enabled
  5261. // sorted from last index to first index
  5262. for (var i = tracks.length - 1; i >= 0; i--) {
  5263. if (tracks[i].enabled) {
  5264. disableOthers(tracks, tracks[i]);
  5265. break;
  5266. }
  5267. }
  5268. // IE8 forces us to implement inheritance ourselves
  5269. // as it does not support Object.defineProperty properly
  5270. if (IS_IE8) {
  5271. list = document_1.createElement('custom');
  5272. for (var prop in TrackList.prototype) {
  5273. if (prop !== 'constructor') {
  5274. list[prop] = TrackList.prototype[prop];
  5275. }
  5276. }
  5277. for (var _prop in AudioTrackList.prototype) {
  5278. if (_prop !== 'constructor') {
  5279. list[_prop] = AudioTrackList.prototype[_prop];
  5280. }
  5281. }
  5282. }
  5283. list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  5284. list.changing_ = false;
  5285. return _ret = list, possibleConstructorReturn(_this, _ret);
  5286. }
  5287. /**
  5288. * Add an {@link AudioTrack} to the `AudioTrackList`.
  5289. *
  5290. * @param {AudioTrack} track
  5291. * The AudioTrack to add to the list
  5292. *
  5293. * @fires TrackList#addtrack
  5294. */
  5295. AudioTrackList.prototype.addTrack = function addTrack(track) {
  5296. var _this2 = this;
  5297. if (track.enabled) {
  5298. disableOthers(this, track);
  5299. }
  5300. _TrackList.prototype.addTrack.call(this, track);
  5301. // native tracks don't have this
  5302. if (!track.addEventListener) {
  5303. return;
  5304. }
  5305. /**
  5306. * @listens AudioTrack#enabledchange
  5307. * @fires TrackList#change
  5308. */
  5309. track.addEventListener('enabledchange', function () {
  5310. // when we are disabling other tracks (since we don't support
  5311. // more than one track at a time) we will set changing_
  5312. // to true so that we don't trigger additional change events
  5313. if (_this2.changing_) {
  5314. return;
  5315. }
  5316. _this2.changing_ = true;
  5317. disableOthers(_this2, track);
  5318. _this2.changing_ = false;
  5319. _this2.trigger('change');
  5320. });
  5321. };
  5322. return AudioTrackList;
  5323. }(TrackList);
  5324. /**
  5325. * @file video-track-list.js
  5326. */
  5327. /**
  5328. * Un-select all other {@link VideoTrack}s that are selected.
  5329. *
  5330. * @param {VideoTrackList} list
  5331. * list to work on
  5332. *
  5333. * @param {VideoTrack} track
  5334. * The track to skip
  5335. *
  5336. * @private
  5337. */
  5338. var disableOthers$1 = function disableOthers(list, track) {
  5339. for (var i = 0; i < list.length; i++) {
  5340. if (!Object.keys(list[i]).length || track.id === list[i].id) {
  5341. continue;
  5342. }
  5343. // another video track is enabled, disable it
  5344. list[i].selected = false;
  5345. }
  5346. };
  5347. /**
  5348. * The current list of {@link VideoTrack} for a video.
  5349. *
  5350. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
  5351. * @extends TrackList
  5352. */
  5353. var VideoTrackList = function (_TrackList) {
  5354. inherits(VideoTrackList, _TrackList);
  5355. /**
  5356. * Create an instance of this class.
  5357. *
  5358. * @param {VideoTrack[]} [tracks=[]]
  5359. * A list of `VideoTrack` to instantiate the list with.
  5360. */
  5361. function VideoTrackList() {
  5362. var _this, _ret;
  5363. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5364. classCallCheck(this, VideoTrackList);
  5365. var list = void 0;
  5366. // make sure only 1 track is enabled
  5367. // sorted from last index to first index
  5368. for (var i = tracks.length - 1; i >= 0; i--) {
  5369. if (tracks[i].selected) {
  5370. disableOthers$1(tracks, tracks[i]);
  5371. break;
  5372. }
  5373. }
  5374. // IE8 forces us to implement inheritance ourselves
  5375. // as it does not support Object.defineProperty properly
  5376. if (IS_IE8) {
  5377. list = document_1.createElement('custom');
  5378. for (var prop in TrackList.prototype) {
  5379. if (prop !== 'constructor') {
  5380. list[prop] = TrackList.prototype[prop];
  5381. }
  5382. }
  5383. for (var _prop in VideoTrackList.prototype) {
  5384. if (_prop !== 'constructor') {
  5385. list[_prop] = VideoTrackList.prototype[_prop];
  5386. }
  5387. }
  5388. }
  5389. list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  5390. list.changing_ = false;
  5391. /**
  5392. * @member {number} VideoTrackList#selectedIndex
  5393. * The current index of the selected {@link VideoTrack`}.
  5394. */
  5395. Object.defineProperty(list, 'selectedIndex', {
  5396. get: function get$$1() {
  5397. for (var _i = 0; _i < this.length; _i++) {
  5398. if (this[_i].selected) {
  5399. return _i;
  5400. }
  5401. }
  5402. return -1;
  5403. },
  5404. set: function set$$1() {}
  5405. });
  5406. return _ret = list, possibleConstructorReturn(_this, _ret);
  5407. }
  5408. /**
  5409. * Add a {@link VideoTrack} to the `VideoTrackList`.
  5410. *
  5411. * @param {VideoTrack} track
  5412. * The VideoTrack to add to the list
  5413. *
  5414. * @fires TrackList#addtrack
  5415. */
  5416. VideoTrackList.prototype.addTrack = function addTrack(track) {
  5417. var _this2 = this;
  5418. if (track.selected) {
  5419. disableOthers$1(this, track);
  5420. }
  5421. _TrackList.prototype.addTrack.call(this, track);
  5422. // native tracks don't have this
  5423. if (!track.addEventListener) {
  5424. return;
  5425. }
  5426. /**
  5427. * @listens VideoTrack#selectedchange
  5428. * @fires TrackList#change
  5429. */
  5430. track.addEventListener('selectedchange', function () {
  5431. if (_this2.changing_) {
  5432. return;
  5433. }
  5434. _this2.changing_ = true;
  5435. disableOthers$1(_this2, track);
  5436. _this2.changing_ = false;
  5437. _this2.trigger('change');
  5438. });
  5439. };
  5440. return VideoTrackList;
  5441. }(TrackList);
  5442. /**
  5443. * @file text-track-list.js
  5444. */
  5445. /**
  5446. * The current list of {@link TextTrack} for a media file.
  5447. *
  5448. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
  5449. * @extends TrackList
  5450. */
  5451. var TextTrackList = function (_TrackList) {
  5452. inherits(TextTrackList, _TrackList);
  5453. /**
  5454. * Create an instance of this class.
  5455. *
  5456. * @param {TextTrack[]} [tracks=[]]
  5457. * A list of `TextTrack` to instantiate the list with.
  5458. */
  5459. function TextTrackList() {
  5460. var _this, _ret;
  5461. var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5462. classCallCheck(this, TextTrackList);
  5463. var list = void 0;
  5464. // IE8 forces us to implement inheritance ourselves
  5465. // as it does not support Object.defineProperty properly
  5466. if (IS_IE8) {
  5467. list = document_1.createElement('custom');
  5468. for (var prop in TrackList.prototype) {
  5469. if (prop !== 'constructor') {
  5470. list[prop] = TrackList.prototype[prop];
  5471. }
  5472. }
  5473. for (var _prop in TextTrackList.prototype) {
  5474. if (_prop !== 'constructor') {
  5475. list[_prop] = TextTrackList.prototype[_prop];
  5476. }
  5477. }
  5478. }
  5479. list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
  5480. return _ret = list, possibleConstructorReturn(_this, _ret);
  5481. }
  5482. /**
  5483. * Add a {@link TextTrack} to the `TextTrackList`
  5484. *
  5485. * @param {TextTrack} track
  5486. * The text track to add to the list.
  5487. *
  5488. * @fires TrackList#addtrack
  5489. */
  5490. TextTrackList.prototype.addTrack = function addTrack(track) {
  5491. _TrackList.prototype.addTrack.call(this, track);
  5492. /**
  5493. * @listens TextTrack#modechange
  5494. * @fires TrackList#change
  5495. */
  5496. track.addEventListener('modechange', bind(this, function () {
  5497. this.trigger('change');
  5498. }));
  5499. var nonLanguageTextTrackKind = ['metadata', 'chapters'];
  5500. if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
  5501. track.addEventListener('modechange', bind(this, function () {
  5502. this.trigger('selectedlanguagechange');
  5503. }));
  5504. }
  5505. };
  5506. return TextTrackList;
  5507. }(TrackList);
  5508. /**
  5509. * @file html-track-element-list.js
  5510. */
  5511. /**
  5512. * The current list of {@link HtmlTrackElement}s.
  5513. */
  5514. var HtmlTrackElementList = function () {
  5515. /**
  5516. * Create an instance of this class.
  5517. *
  5518. * @param {HtmlTrackElement[]} [tracks=[]]
  5519. * A list of `HtmlTrackElement` to instantiate the list with.
  5520. */
  5521. function HtmlTrackElementList() {
  5522. var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  5523. classCallCheck(this, HtmlTrackElementList);
  5524. var list = this; // eslint-disable-line
  5525. if (IS_IE8) {
  5526. list = document_1.createElement('custom');
  5527. for (var prop in HtmlTrackElementList.prototype) {
  5528. if (prop !== 'constructor') {
  5529. list[prop] = HtmlTrackElementList.prototype[prop];
  5530. }
  5531. }
  5532. }
  5533. list.trackElements_ = [];
  5534. /**
  5535. * @memberof HtmlTrackElementList
  5536. * @member {number} length
  5537. * The current number of `Track`s in the this Trackist.
  5538. * @instance
  5539. */
  5540. Object.defineProperty(list, 'length', {
  5541. get: function get$$1() {
  5542. return this.trackElements_.length;
  5543. }
  5544. });
  5545. for (var i = 0, length = trackElements.length; i < length; i++) {
  5546. list.addTrackElement_(trackElements[i]);
  5547. }
  5548. if (IS_IE8) {
  5549. return list;
  5550. }
  5551. }
  5552. /**
  5553. * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
  5554. *
  5555. * @param {HtmlTrackElement} trackElement
  5556. * The track element to add to the list.
  5557. *
  5558. * @private
  5559. */
  5560. HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
  5561. var index = this.trackElements_.length;
  5562. if (!('' + index in this)) {
  5563. Object.defineProperty(this, index, {
  5564. get: function get$$1() {
  5565. return this.trackElements_[index];
  5566. }
  5567. });
  5568. }
  5569. // Do not add duplicate elements
  5570. if (this.trackElements_.indexOf(trackElement) === -1) {
  5571. this.trackElements_.push(trackElement);
  5572. }
  5573. };
  5574. /**
  5575. * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
  5576. * {@link TextTrack}.
  5577. *
  5578. * @param {TextTrack} track
  5579. * The track associated with a track element.
  5580. *
  5581. * @return {HtmlTrackElement|undefined}
  5582. * The track element that was found or undefined.
  5583. *
  5584. * @private
  5585. */
  5586. HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
  5587. var trackElement_ = void 0;
  5588. for (var i = 0, length = this.trackElements_.length; i < length; i++) {
  5589. if (track === this.trackElements_[i].track) {
  5590. trackElement_ = this.trackElements_[i];
  5591. break;
  5592. }
  5593. }
  5594. return trackElement_;
  5595. };
  5596. /**
  5597. * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
  5598. *
  5599. * @param {HtmlTrackElement} trackElement
  5600. * The track element to remove from the list.
  5601. *
  5602. * @private
  5603. */
  5604. HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
  5605. for (var i = 0, length = this.trackElements_.length; i < length; i++) {
  5606. if (trackElement === this.trackElements_[i]) {
  5607. this.trackElements_.splice(i, 1);
  5608. break;
  5609. }
  5610. }
  5611. };
  5612. return HtmlTrackElementList;
  5613. }();
  5614. /**
  5615. * @file text-track-cue-list.js
  5616. */
  5617. /**
  5618. * @typedef {Object} TextTrackCueList~TextTrackCue
  5619. *
  5620. * @property {string} id
  5621. * The unique id for this text track cue
  5622. *
  5623. * @property {number} startTime
  5624. * The start time for this text track cue
  5625. *
  5626. * @property {number} endTime
  5627. * The end time for this text track cue
  5628. *
  5629. * @property {boolean} pauseOnExit
  5630. * Pause when the end time is reached if true.
  5631. *
  5632. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
  5633. */
  5634. /**
  5635. * A List of TextTrackCues.
  5636. *
  5637. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
  5638. */
  5639. var TextTrackCueList = function () {
  5640. /**
  5641. * Create an instance of this class..
  5642. *
  5643. * @param {Array} cues
  5644. * A list of cues to be initialized with
  5645. */
  5646. function TextTrackCueList(cues) {
  5647. classCallCheck(this, TextTrackCueList);
  5648. var list = this; // eslint-disable-line
  5649. if (IS_IE8) {
  5650. list = document_1.createElement('custom');
  5651. for (var prop in TextTrackCueList.prototype) {
  5652. if (prop !== 'constructor') {
  5653. list[prop] = TextTrackCueList.prototype[prop];
  5654. }
  5655. }
  5656. }
  5657. TextTrackCueList.prototype.setCues_.call(list, cues);
  5658. /**
  5659. * @memberof TextTrackCueList
  5660. * @member {number} length
  5661. * The current number of `TextTrackCue`s in the TextTrackCueList.
  5662. * @instance
  5663. */
  5664. Object.defineProperty(list, 'length', {
  5665. get: function get$$1() {
  5666. return this.length_;
  5667. }
  5668. });
  5669. if (IS_IE8) {
  5670. return list;
  5671. }
  5672. }
  5673. /**
  5674. * A setter for cues in this list. Creates getters
  5675. * an an index for the cues.
  5676. *
  5677. * @param {Array} cues
  5678. * An array of cues to set
  5679. *
  5680. * @private
  5681. */
  5682. TextTrackCueList.prototype.setCues_ = function setCues_(cues) {
  5683. var oldLength = this.length || 0;
  5684. var i = 0;
  5685. var l = cues.length;
  5686. this.cues_ = cues;
  5687. this.length_ = cues.length;
  5688. var defineProp = function defineProp(index) {
  5689. if (!('' + index in this)) {
  5690. Object.defineProperty(this, '' + index, {
  5691. get: function get$$1() {
  5692. return this.cues_[index];
  5693. }
  5694. });
  5695. }
  5696. };
  5697. if (oldLength < l) {
  5698. i = oldLength;
  5699. for (; i < l; i++) {
  5700. defineProp.call(this, i);
  5701. }
  5702. }
  5703. };
  5704. /**
  5705. * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
  5706. *
  5707. * @param {string} id
  5708. * The id of the cue that should be searched for.
  5709. *
  5710. * @return {TextTrackCueList~TextTrackCue|null}
  5711. * A single cue or null if none was found.
  5712. */
  5713. TextTrackCueList.prototype.getCueById = function getCueById(id) {
  5714. var result = null;
  5715. for (var i = 0, l = this.length; i < l; i++) {
  5716. var cue = this[i];
  5717. if (cue.id === id) {
  5718. result = cue;
  5719. break;
  5720. }
  5721. }
  5722. return result;
  5723. };
  5724. return TextTrackCueList;
  5725. }();
  5726. /**
  5727. * @file track-kinds.js
  5728. */
  5729. /**
  5730. * All possible `VideoTrackKind`s
  5731. *
  5732. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
  5733. * @typedef VideoTrack~Kind
  5734. * @enum
  5735. */
  5736. var VideoTrackKind = {
  5737. alternative: 'alternative',
  5738. captions: 'captions',
  5739. main: 'main',
  5740. sign: 'sign',
  5741. subtitles: 'subtitles',
  5742. commentary: 'commentary'
  5743. };
  5744. /**
  5745. * All possible `AudioTrackKind`s
  5746. *
  5747. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
  5748. * @typedef AudioTrack~Kind
  5749. * @enum
  5750. */
  5751. var AudioTrackKind = {
  5752. 'alternative': 'alternative',
  5753. 'descriptions': 'descriptions',
  5754. 'main': 'main',
  5755. 'main-desc': 'main-desc',
  5756. 'translation': 'translation',
  5757. 'commentary': 'commentary'
  5758. };
  5759. /**
  5760. * All possible `TextTrackKind`s
  5761. *
  5762. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
  5763. * @typedef TextTrack~Kind
  5764. * @enum
  5765. */
  5766. var TextTrackKind = {
  5767. subtitles: 'subtitles',
  5768. captions: 'captions',
  5769. descriptions: 'descriptions',
  5770. chapters: 'chapters',
  5771. metadata: 'metadata'
  5772. };
  5773. /**
  5774. * All possible `TextTrackMode`s
  5775. *
  5776. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
  5777. * @typedef TextTrack~Mode
  5778. * @enum
  5779. */
  5780. var TextTrackMode = {
  5781. disabled: 'disabled',
  5782. hidden: 'hidden',
  5783. showing: 'showing'
  5784. };
  5785. /**
  5786. * @file track.js
  5787. */
  5788. /**
  5789. * A Track class that contains all of the common functionality for {@link AudioTrack},
  5790. * {@link VideoTrack}, and {@link TextTrack}.
  5791. *
  5792. * > Note: This class should not be used directly
  5793. *
  5794. * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
  5795. * @extends EventTarget
  5796. * @abstract
  5797. */
  5798. var Track = function (_EventTarget) {
  5799. inherits(Track, _EventTarget);
  5800. /**
  5801. * Create an instance of this class.
  5802. *
  5803. * @param {Object} [options={}]
  5804. * Object of option names and values
  5805. *
  5806. * @param {string} [options.kind='']
  5807. * A valid kind for the track type you are creating.
  5808. *
  5809. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  5810. * A unique id for this AudioTrack.
  5811. *
  5812. * @param {string} [options.label='']
  5813. * The menu label for this track.
  5814. *
  5815. * @param {string} [options.language='']
  5816. * A valid two character language code.
  5817. *
  5818. * @abstract
  5819. */
  5820. function Track() {
  5821. var _ret;
  5822. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  5823. classCallCheck(this, Track);
  5824. var _this = possibleConstructorReturn(this, _EventTarget.call(this));
  5825. var track = _this; // eslint-disable-line
  5826. if (IS_IE8) {
  5827. track = document_1.createElement('custom');
  5828. for (var prop in Track.prototype) {
  5829. if (prop !== 'constructor') {
  5830. track[prop] = Track.prototype[prop];
  5831. }
  5832. }
  5833. }
  5834. var trackProps = {
  5835. id: options.id || 'vjs_track_' + newGUID(),
  5836. kind: options.kind || '',
  5837. label: options.label || '',
  5838. language: options.language || ''
  5839. };
  5840. /**
  5841. * @memberof Track
  5842. * @member {string} id
  5843. * The id of this track. Cannot be changed after creation.
  5844. * @instance
  5845. *
  5846. * @readonly
  5847. */
  5848. /**
  5849. * @memberof Track
  5850. * @member {string} kind
  5851. * The kind of track that this is. Cannot be changed after creation.
  5852. * @instance
  5853. *
  5854. * @readonly
  5855. */
  5856. /**
  5857. * @memberof Track
  5858. * @member {string} label
  5859. * The label of this track. Cannot be changed after creation.
  5860. * @instance
  5861. *
  5862. * @readonly
  5863. */
  5864. /**
  5865. * @memberof Track
  5866. * @member {string} language
  5867. * The two letter language code for this track. Cannot be changed after
  5868. * creation.
  5869. * @instance
  5870. *
  5871. * @readonly
  5872. */
  5873. var _loop = function _loop(key) {
  5874. Object.defineProperty(track, key, {
  5875. get: function get$$1() {
  5876. return trackProps[key];
  5877. },
  5878. set: function set$$1() {}
  5879. });
  5880. };
  5881. for (var key in trackProps) {
  5882. _loop(key);
  5883. }
  5884. return _ret = track, possibleConstructorReturn(_this, _ret);
  5885. }
  5886. return Track;
  5887. }(EventTarget);
  5888. /**
  5889. * @file url.js
  5890. * @module url
  5891. */
  5892. /**
  5893. * @typedef {Object} url:URLObject
  5894. *
  5895. * @property {string} protocol
  5896. * The protocol of the url that was parsed.
  5897. *
  5898. * @property {string} hostname
  5899. * The hostname of the url that was parsed.
  5900. *
  5901. * @property {string} port
  5902. * The port of the url that was parsed.
  5903. *
  5904. * @property {string} pathname
  5905. * The pathname of the url that was parsed.
  5906. *
  5907. * @property {string} search
  5908. * The search query of the url that was parsed.
  5909. *
  5910. * @property {string} hash
  5911. * The hash of the url that was parsed.
  5912. *
  5913. * @property {string} host
  5914. * The host of the url that was parsed.
  5915. */
  5916. /**
  5917. * Resolve and parse the elements of a URL.
  5918. *
  5919. * @param {String} url
  5920. * The url to parse
  5921. *
  5922. * @return {url:URLObject}
  5923. * An object of url details
  5924. */
  5925. var parseUrl = function parseUrl(url) {
  5926. var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host'];
  5927. // add the url to an anchor and let the browser parse the URL
  5928. var a = document_1.createElement('a');
  5929. a.href = url;
  5930. // IE8 (and 9?) Fix
  5931. // ie8 doesn't parse the URL correctly until the anchor is actually
  5932. // added to the body, and an innerHTML is needed to trigger the parsing
  5933. var addToBody = a.host === '' && a.protocol !== 'file:';
  5934. var div = void 0;
  5935. if (addToBody) {
  5936. div = document_1.createElement('div');
  5937. div.innerHTML = '<a href="' + url + '"></a>';
  5938. a = div.firstChild;
  5939. // prevent the div from affecting layout
  5940. div.setAttribute('style', 'display:none; position:absolute;');
  5941. document_1.body.appendChild(div);
  5942. }
  5943. // Copy the specific URL properties to a new object
  5944. // This is also needed for IE8 because the anchor loses its
  5945. // properties when it's removed from the dom
  5946. var details = {};
  5947. for (var i = 0; i < props.length; i++) {
  5948. details[props[i]] = a[props[i]];
  5949. }
  5950. // IE9 adds the port to the host property unlike everyone else. If
  5951. // a port identifier is added for standard ports, strip it.
  5952. if (details.protocol === 'http:') {
  5953. details.host = details.host.replace(/:80$/, '');
  5954. }
  5955. if (details.protocol === 'https:') {
  5956. details.host = details.host.replace(/:443$/, '');
  5957. }
  5958. if (!details.protocol) {
  5959. details.protocol = window_1.location.protocol;
  5960. }
  5961. if (addToBody) {
  5962. document_1.body.removeChild(div);
  5963. }
  5964. return details;
  5965. };
  5966. /**
  5967. * Get absolute version of relative URL. Used to tell flash correct URL.
  5968. *
  5969. *
  5970. * @param {string} url
  5971. * URL to make absolute
  5972. *
  5973. * @return {string}
  5974. * Absolute URL
  5975. *
  5976. * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
  5977. */
  5978. var getAbsoluteURL = function getAbsoluteURL(url) {
  5979. // Check if absolute URL
  5980. if (!url.match(/^https?:\/\//)) {
  5981. // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
  5982. var div = document_1.createElement('div');
  5983. div.innerHTML = '<a href="' + url + '">x</a>';
  5984. url = div.firstChild.href;
  5985. }
  5986. return url;
  5987. };
  5988. /**
  5989. * Returns the extension of the passed file name. It will return an empty string
  5990. * if passed an invalid path.
  5991. *
  5992. * @param {string} path
  5993. * The fileName path like '/path/to/file.mp4'
  5994. *
  5995. * @returns {string}
  5996. * The extension in lower case or an empty string if no
  5997. * extension could be found.
  5998. */
  5999. var getFileExtension = function getFileExtension(path) {
  6000. if (typeof path === 'string') {
  6001. var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
  6002. var pathParts = splitPathRe.exec(path);
  6003. if (pathParts) {
  6004. return pathParts.pop().toLowerCase();
  6005. }
  6006. }
  6007. return '';
  6008. };
  6009. /**
  6010. * Returns whether the url passed is a cross domain request or not.
  6011. *
  6012. * @param {string} url
  6013. * The url to check.
  6014. *
  6015. * @return {boolean}
  6016. * Whether it is a cross domain request or not.
  6017. */
  6018. var isCrossOrigin = function isCrossOrigin(url) {
  6019. var winLoc = window_1.location;
  6020. var urlInfo = parseUrl(url);
  6021. // IE8 protocol relative urls will return ':' for protocol
  6022. var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;
  6023. // Check if url is for another domain/origin
  6024. // IE8 doesn't know location.origin, so we won't rely on it here
  6025. var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
  6026. return crossOrigin;
  6027. };
  6028. var Url = (Object.freeze || Object)({
  6029. parseUrl: parseUrl,
  6030. getAbsoluteURL: getAbsoluteURL,
  6031. getFileExtension: getFileExtension,
  6032. isCrossOrigin: isCrossOrigin
  6033. });
  6034. var isFunction_1 = isFunction;
  6035. var toString$1 = Object.prototype.toString;
  6036. function isFunction (fn) {
  6037. var string = toString$1.call(fn);
  6038. return string === '[object Function]' ||
  6039. (typeof fn === 'function' && string !== '[object RegExp]') ||
  6040. (typeof window !== 'undefined' &&
  6041. // IE8 and below
  6042. (fn === window.setTimeout ||
  6043. fn === window.alert ||
  6044. fn === window.confirm ||
  6045. fn === window.prompt))
  6046. }
  6047. var trim_1 = createCommonjsModule(function (module, exports) {
  6048. exports = module.exports = trim;
  6049. function trim(str){
  6050. return str.replace(/^\s*|\s*$/g, '');
  6051. }
  6052. exports.left = function(str){
  6053. return str.replace(/^\s*/, '');
  6054. };
  6055. exports.right = function(str){
  6056. return str.replace(/\s*$/, '');
  6057. };
  6058. });
  6059. var fnToStr = Function.prototype.toString;
  6060. var constructorRegex = /^\s*class\b/;
  6061. var isES6ClassFn = function isES6ClassFunction(value) {
  6062. try {
  6063. var fnStr = fnToStr.call(value);
  6064. return constructorRegex.test(fnStr);
  6065. } catch (e) {
  6066. return false; // not a function
  6067. }
  6068. };
  6069. var tryFunctionObject = function tryFunctionToStr(value) {
  6070. try {
  6071. if (isES6ClassFn(value)) { return false; }
  6072. fnToStr.call(value);
  6073. return true;
  6074. } catch (e) {
  6075. return false;
  6076. }
  6077. };
  6078. var toStr$1 = Object.prototype.toString;
  6079. var fnClass = '[object Function]';
  6080. var genClass = '[object GeneratorFunction]';
  6081. var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
  6082. var isCallable = function isCallable(value) {
  6083. if (!value) { return false; }
  6084. if (typeof value !== 'function' && typeof value !== 'object') { return false; }
  6085. if (typeof value === 'function' && !value.prototype) { return true; }
  6086. if (hasToStringTag) { return tryFunctionObject(value); }
  6087. if (isES6ClassFn(value)) { return false; }
  6088. var strClass = toStr$1.call(value);
  6089. return strClass === fnClass || strClass === genClass;
  6090. };
  6091. var toStr = Object.prototype.toString;
  6092. var hasOwnProperty = Object.prototype.hasOwnProperty;
  6093. var forEachArray$1 = function forEachArray(array, iterator, receiver) {
  6094. for (var i = 0, len = array.length; i < len; i++) {
  6095. if (hasOwnProperty.call(array, i)) {
  6096. if (receiver == null) {
  6097. iterator(array[i], i, array);
  6098. } else {
  6099. iterator.call(receiver, array[i], i, array);
  6100. }
  6101. }
  6102. }
  6103. };
  6104. var forEachString = function forEachString(string, iterator, receiver) {
  6105. for (var i = 0, len = string.length; i < len; i++) {
  6106. // no such thing as a sparse string.
  6107. if (receiver == null) {
  6108. iterator(string.charAt(i), i, string);
  6109. } else {
  6110. iterator.call(receiver, string.charAt(i), i, string);
  6111. }
  6112. }
  6113. };
  6114. var forEachObject = function forEachObject(object, iterator, receiver) {
  6115. for (var k in object) {
  6116. if (hasOwnProperty.call(object, k)) {
  6117. if (receiver == null) {
  6118. iterator(object[k], k, object);
  6119. } else {
  6120. iterator.call(receiver, object[k], k, object);
  6121. }
  6122. }
  6123. }
  6124. };
  6125. var forEach = function forEach(list, iterator, thisArg) {
  6126. if (!isCallable(iterator)) {
  6127. throw new TypeError('iterator must be a function');
  6128. }
  6129. var receiver;
  6130. if (arguments.length >= 3) {
  6131. receiver = thisArg;
  6132. }
  6133. if (toStr.call(list) === '[object Array]') {
  6134. forEachArray$1(list, iterator, receiver);
  6135. } else if (typeof list === 'string') {
  6136. forEachString(list, iterator, receiver);
  6137. } else {
  6138. forEachObject(list, iterator, receiver);
  6139. }
  6140. };
  6141. var forEach_1 = forEach;
  6142. var isArray = function(arg) {
  6143. return Object.prototype.toString.call(arg) === '[object Array]';
  6144. };
  6145. var parseHeaders = function (headers) {
  6146. if (!headers)
  6147. return {}
  6148. var result = {};
  6149. forEach_1(
  6150. trim_1(headers).split('\n')
  6151. , function (row) {
  6152. var index = row.indexOf(':')
  6153. , key = trim_1(row.slice(0, index)).toLowerCase()
  6154. , value = trim_1(row.slice(index + 1));
  6155. if (typeof(result[key]) === 'undefined') {
  6156. result[key] = value;
  6157. } else if (isArray(result[key])) {
  6158. result[key].push(value);
  6159. } else {
  6160. result[key] = [ result[key], value ];
  6161. }
  6162. }
  6163. );
  6164. return result
  6165. };
  6166. var immutable = extend;
  6167. var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
  6168. function extend() {
  6169. var target = {};
  6170. for (var i = 0; i < arguments.length; i++) {
  6171. var source = arguments[i];
  6172. for (var key in source) {
  6173. if (hasOwnProperty$1.call(source, key)) {
  6174. target[key] = source[key];
  6175. }
  6176. }
  6177. }
  6178. return target
  6179. }
  6180. var xhr = createXHR;
  6181. createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop;
  6182. createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window_1.XDomainRequest;
  6183. forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
  6184. createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
  6185. options = initParams(uri, options, callback);
  6186. options.method = method.toUpperCase();
  6187. return _createXHR(options)
  6188. };
  6189. });
  6190. function forEachArray(array, iterator) {
  6191. for (var i = 0; i < array.length; i++) {
  6192. iterator(array[i]);
  6193. }
  6194. }
  6195. function isEmpty(obj){
  6196. for(var i in obj){
  6197. if(obj.hasOwnProperty(i)) return false
  6198. }
  6199. return true
  6200. }
  6201. function initParams(uri, options, callback) {
  6202. var params = uri;
  6203. if (isFunction_1(options)) {
  6204. callback = options;
  6205. if (typeof uri === "string") {
  6206. params = {uri:uri};
  6207. }
  6208. } else {
  6209. params = immutable(options, {uri: uri});
  6210. }
  6211. params.callback = callback;
  6212. return params
  6213. }
  6214. function createXHR(uri, options, callback) {
  6215. options = initParams(uri, options, callback);
  6216. return _createXHR(options)
  6217. }
  6218. function _createXHR(options) {
  6219. if(typeof options.callback === "undefined"){
  6220. throw new Error("callback argument missing")
  6221. }
  6222. var called = false;
  6223. var callback = function cbOnce(err, response, body){
  6224. if(!called){
  6225. called = true;
  6226. options.callback(err, response, body);
  6227. }
  6228. };
  6229. function readystatechange() {
  6230. if (xhr.readyState === 4) {
  6231. setTimeout(loadFunc, 0);
  6232. }
  6233. }
  6234. function getBody() {
  6235. // Chrome with requestType=blob throws errors arround when even testing access to responseText
  6236. var body = undefined;
  6237. if (xhr.response) {
  6238. body = xhr.response;
  6239. } else {
  6240. body = xhr.responseText || getXml(xhr);
  6241. }
  6242. if (isJson) {
  6243. try {
  6244. body = JSON.parse(body);
  6245. } catch (e) {}
  6246. }
  6247. return body
  6248. }
  6249. function errorFunc(evt) {
  6250. clearTimeout(timeoutTimer);
  6251. if(!(evt instanceof Error)){
  6252. evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") );
  6253. }
  6254. evt.statusCode = 0;
  6255. return callback(evt, failureResponse)
  6256. }
  6257. // will load the data & process the response in a special response object
  6258. function loadFunc() {
  6259. if (aborted) return
  6260. var status;
  6261. clearTimeout(timeoutTimer);
  6262. if(options.useXDR && xhr.status===undefined) {
  6263. //IE8 CORS GET successful response doesn't have a status field, but body is fine
  6264. status = 200;
  6265. } else {
  6266. status = (xhr.status === 1223 ? 204 : xhr.status);
  6267. }
  6268. var response = failureResponse;
  6269. var err = null;
  6270. if (status !== 0){
  6271. response = {
  6272. body: getBody(),
  6273. statusCode: status,
  6274. method: method,
  6275. headers: {},
  6276. url: uri,
  6277. rawRequest: xhr
  6278. };
  6279. if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
  6280. response.headers = parseHeaders(xhr.getAllResponseHeaders());
  6281. }
  6282. } else {
  6283. err = new Error("Internal XMLHttpRequest Error");
  6284. }
  6285. return callback(err, response, response.body)
  6286. }
  6287. var xhr = options.xhr || null;
  6288. if (!xhr) {
  6289. if (options.cors || options.useXDR) {
  6290. xhr = new createXHR.XDomainRequest();
  6291. }else{
  6292. xhr = new createXHR.XMLHttpRequest();
  6293. }
  6294. }
  6295. var key;
  6296. var aborted;
  6297. var uri = xhr.url = options.uri || options.url;
  6298. var method = xhr.method = options.method || "GET";
  6299. var body = options.body || options.data;
  6300. var headers = xhr.headers = options.headers || {};
  6301. var sync = !!options.sync;
  6302. var isJson = false;
  6303. var timeoutTimer;
  6304. var failureResponse = {
  6305. body: undefined,
  6306. headers: {},
  6307. statusCode: 0,
  6308. method: method,
  6309. url: uri,
  6310. rawRequest: xhr
  6311. };
  6312. if ("json" in options && options.json !== false) {
  6313. isJson = true;
  6314. headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
  6315. if (method !== "GET" && method !== "HEAD") {
  6316. headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
  6317. body = JSON.stringify(options.json === true ? body : options.json);
  6318. }
  6319. }
  6320. xhr.onreadystatechange = readystatechange;
  6321. xhr.onload = loadFunc;
  6322. xhr.onerror = errorFunc;
  6323. // IE9 must have onprogress be set to a unique function.
  6324. xhr.onprogress = function () {
  6325. // IE must die
  6326. };
  6327. xhr.onabort = function(){
  6328. aborted = true;
  6329. };
  6330. xhr.ontimeout = errorFunc;
  6331. xhr.open(method, uri, !sync, options.username, options.password);
  6332. //has to be after open
  6333. if(!sync) {
  6334. xhr.withCredentials = !!options.withCredentials;
  6335. }
  6336. // Cannot set timeout with sync request
  6337. // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
  6338. // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
  6339. if (!sync && options.timeout > 0 ) {
  6340. timeoutTimer = setTimeout(function(){
  6341. if (aborted) return
  6342. aborted = true;//IE9 may still call readystatechange
  6343. xhr.abort("timeout");
  6344. var e = new Error("XMLHttpRequest timeout");
  6345. e.code = "ETIMEDOUT";
  6346. errorFunc(e);
  6347. }, options.timeout );
  6348. }
  6349. if (xhr.setRequestHeader) {
  6350. for(key in headers){
  6351. if(headers.hasOwnProperty(key)){
  6352. xhr.setRequestHeader(key, headers[key]);
  6353. }
  6354. }
  6355. } else if (options.headers && !isEmpty(options.headers)) {
  6356. throw new Error("Headers cannot be set on an XDomainRequest object")
  6357. }
  6358. if ("responseType" in options) {
  6359. xhr.responseType = options.responseType;
  6360. }
  6361. if ("beforeSend" in options &&
  6362. typeof options.beforeSend === "function"
  6363. ) {
  6364. options.beforeSend(xhr);
  6365. }
  6366. // Microsoft Edge browser sends "undefined" when send is called with undefined value.
  6367. // XMLHttpRequest spec says to pass null as body to indicate no body
  6368. // See https://github.com/naugtur/xhr/issues/100.
  6369. xhr.send(body || null);
  6370. return xhr
  6371. }
  6372. function getXml(xhr) {
  6373. if (xhr.responseType === "document") {
  6374. return xhr.responseXML
  6375. }
  6376. var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
  6377. if (xhr.responseType === "" && !firefoxBugTakenEffect) {
  6378. return xhr.responseXML
  6379. }
  6380. return null
  6381. }
  6382. function noop() {}
  6383. /**
  6384. * @file text-track.js
  6385. */
  6386. /**
  6387. * Takes a webvtt file contents and parses it into cues
  6388. *
  6389. * @param {string} srcContent
  6390. * webVTT file contents
  6391. *
  6392. * @param {TextTrack} track
  6393. * TextTrack to add cues to. Cues come from the srcContent.
  6394. *
  6395. * @private
  6396. */
  6397. var parseCues = function parseCues(srcContent, track) {
  6398. var parser = new window_1.WebVTT.Parser(window_1, window_1.vttjs, window_1.WebVTT.StringDecoder());
  6399. var errors = [];
  6400. parser.oncue = function (cue) {
  6401. track.addCue(cue);
  6402. };
  6403. parser.onparsingerror = function (error) {
  6404. errors.push(error);
  6405. };
  6406. parser.onflush = function () {
  6407. track.trigger({
  6408. type: 'loadeddata',
  6409. target: track
  6410. });
  6411. };
  6412. parser.parse(srcContent);
  6413. if (errors.length > 0) {
  6414. if (window_1.console && window_1.console.groupCollapsed) {
  6415. window_1.console.groupCollapsed('Text Track parsing errors for ' + track.src);
  6416. }
  6417. errors.forEach(function (error) {
  6418. return log.error(error);
  6419. });
  6420. if (window_1.console && window_1.console.groupEnd) {
  6421. window_1.console.groupEnd();
  6422. }
  6423. }
  6424. parser.flush();
  6425. };
  6426. /**
  6427. * Load a `TextTrack` from a specifed url.
  6428. *
  6429. * @param {string} src
  6430. * Url to load track from.
  6431. *
  6432. * @param {TextTrack} track
  6433. * Track to add cues to. Comes from the content at the end of `url`.
  6434. *
  6435. * @private
  6436. */
  6437. var loadTrack = function loadTrack(src, track) {
  6438. var opts = {
  6439. uri: src
  6440. };
  6441. var crossOrigin = isCrossOrigin(src);
  6442. if (crossOrigin) {
  6443. opts.cors = crossOrigin;
  6444. }
  6445. xhr(opts, bind(this, function (err, response, responseBody) {
  6446. if (err) {
  6447. return log.error(err, response);
  6448. }
  6449. track.loaded_ = true;
  6450. // Make sure that vttjs has loaded, otherwise, wait till it finished loading
  6451. // NOTE: this is only used for the alt/video.novtt.js build
  6452. if (typeof window_1.WebVTT !== 'function') {
  6453. if (track.tech_) {
  6454. var loadHandler = function loadHandler() {
  6455. return parseCues(responseBody, track);
  6456. };
  6457. track.tech_.on('vttjsloaded', loadHandler);
  6458. track.tech_.on('vttjserror', function () {
  6459. log.error('vttjs failed to load, stopping trying to process ' + track.src);
  6460. track.tech_.off('vttjsloaded', loadHandler);
  6461. });
  6462. }
  6463. } else {
  6464. parseCues(responseBody, track);
  6465. }
  6466. }));
  6467. };
  6468. /**
  6469. * A representation of a single `TextTrack`.
  6470. *
  6471. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
  6472. * @extends Track
  6473. */
  6474. var TextTrack = function (_Track) {
  6475. inherits(TextTrack, _Track);
  6476. /**
  6477. * Create an instance of this class.
  6478. *
  6479. * @param {Object} options={}
  6480. * Object of option names and values
  6481. *
  6482. * @param {Tech} options.tech
  6483. * A reference to the tech that owns this TextTrack.
  6484. *
  6485. * @param {TextTrack~Kind} [options.kind='subtitles']
  6486. * A valid text track kind.
  6487. *
  6488. * @param {TextTrack~Mode} [options.mode='disabled']
  6489. * A valid text track mode.
  6490. *
  6491. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6492. * A unique id for this TextTrack.
  6493. *
  6494. * @param {string} [options.label='']
  6495. * The menu label for this track.
  6496. *
  6497. * @param {string} [options.language='']
  6498. * A valid two character language code.
  6499. *
  6500. * @param {string} [options.srclang='']
  6501. * A valid two character language code. An alternative, but deprioritized
  6502. * vesion of `options.language`
  6503. *
  6504. * @param {string} [options.src]
  6505. * A url to TextTrack cues.
  6506. *
  6507. * @param {boolean} [options.default]
  6508. * If this track should default to on or off.
  6509. */
  6510. function TextTrack() {
  6511. var _this, _ret;
  6512. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6513. classCallCheck(this, TextTrack);
  6514. if (!options.tech) {
  6515. throw new Error('A tech was not provided.');
  6516. }
  6517. var settings = mergeOptions(options, {
  6518. kind: TextTrackKind[options.kind] || 'subtitles',
  6519. language: options.language || options.srclang || ''
  6520. });
  6521. var mode = TextTrackMode[settings.mode] || 'disabled';
  6522. var default_ = settings['default'];
  6523. if (settings.kind === 'metadata' || settings.kind === 'chapters') {
  6524. mode = 'hidden';
  6525. }
  6526. // on IE8 this will be a document element
  6527. // for every other browser this will be a normal object
  6528. var tt = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  6529. tt.tech_ = settings.tech;
  6530. if (IS_IE8) {
  6531. for (var prop in TextTrack.prototype) {
  6532. if (prop !== 'constructor') {
  6533. tt[prop] = TextTrack.prototype[prop];
  6534. }
  6535. }
  6536. }
  6537. tt.cues_ = [];
  6538. tt.activeCues_ = [];
  6539. var cues = new TextTrackCueList(tt.cues_);
  6540. var activeCues = new TextTrackCueList(tt.activeCues_);
  6541. var changed = false;
  6542. var timeupdateHandler = bind(tt, function () {
  6543. // Accessing this.activeCues for the side-effects of updating itself
  6544. // due to it's nature as a getter function. Do not remove or cues will
  6545. // stop updating!
  6546. // Use the setter to prevent deletion from uglify (pure_getters rule)
  6547. this.activeCues = this.activeCues;
  6548. if (changed) {
  6549. this.trigger('cuechange');
  6550. changed = false;
  6551. }
  6552. });
  6553. if (mode !== 'disabled') {
  6554. tt.tech_.ready(function () {
  6555. tt.tech_.on('timeupdate', timeupdateHandler);
  6556. }, true);
  6557. }
  6558. /**
  6559. * @memberof TextTrack
  6560. * @member {boolean} default
  6561. * If this track was set to be on or off by default. Cannot be changed after
  6562. * creation.
  6563. * @instance
  6564. *
  6565. * @readonly
  6566. */
  6567. Object.defineProperty(tt, 'default', {
  6568. get: function get$$1() {
  6569. return default_;
  6570. },
  6571. set: function set$$1() {}
  6572. });
  6573. /**
  6574. * @memberof TextTrack
  6575. * @member {string} mode
  6576. * Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
  6577. * not be set if setting to an invalid mode.
  6578. * @instance
  6579. *
  6580. * @fires TextTrack#modechange
  6581. */
  6582. Object.defineProperty(tt, 'mode', {
  6583. get: function get$$1() {
  6584. return mode;
  6585. },
  6586. set: function set$$1(newMode) {
  6587. var _this2 = this;
  6588. if (!TextTrackMode[newMode]) {
  6589. return;
  6590. }
  6591. mode = newMode;
  6592. if (mode !== 'disabled') {
  6593. this.tech_.ready(function () {
  6594. _this2.tech_.on('timeupdate', timeupdateHandler);
  6595. }, true);
  6596. } else {
  6597. this.tech_.off('timeupdate', timeupdateHandler);
  6598. }
  6599. /**
  6600. * An event that fires when mode changes on this track. This allows
  6601. * the TextTrackList that holds this track to act accordingly.
  6602. *
  6603. * > Note: This is not part of the spec!
  6604. *
  6605. * @event TextTrack#modechange
  6606. * @type {EventTarget~Event}
  6607. */
  6608. this.trigger('modechange');
  6609. }
  6610. });
  6611. /**
  6612. * @memberof TextTrack
  6613. * @member {TextTrackCueList} cues
  6614. * The text track cue list for this TextTrack.
  6615. * @instance
  6616. */
  6617. Object.defineProperty(tt, 'cues', {
  6618. get: function get$$1() {
  6619. if (!this.loaded_) {
  6620. return null;
  6621. }
  6622. return cues;
  6623. },
  6624. set: function set$$1() {}
  6625. });
  6626. /**
  6627. * @memberof TextTrack
  6628. * @member {TextTrackCueList} activeCues
  6629. * The list text track cues that are currently active for this TextTrack.
  6630. * @instance
  6631. */
  6632. Object.defineProperty(tt, 'activeCues', {
  6633. get: function get$$1() {
  6634. if (!this.loaded_) {
  6635. return null;
  6636. }
  6637. // nothing to do
  6638. if (this.cues.length === 0) {
  6639. return activeCues;
  6640. }
  6641. var ct = this.tech_.currentTime();
  6642. var active = [];
  6643. for (var i = 0, l = this.cues.length; i < l; i++) {
  6644. var cue = this.cues[i];
  6645. if (cue.startTime <= ct && cue.endTime >= ct) {
  6646. active.push(cue);
  6647. } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
  6648. active.push(cue);
  6649. }
  6650. }
  6651. changed = false;
  6652. if (active.length !== this.activeCues_.length) {
  6653. changed = true;
  6654. } else {
  6655. for (var _i = 0; _i < active.length; _i++) {
  6656. if (this.activeCues_.indexOf(active[_i]) === -1) {
  6657. changed = true;
  6658. }
  6659. }
  6660. }
  6661. this.activeCues_ = active;
  6662. activeCues.setCues_(this.activeCues_);
  6663. return activeCues;
  6664. },
  6665. // /!\ Keep this setter empty (see the timeupdate handler above)
  6666. set: function set$$1() {}
  6667. });
  6668. if (settings.src) {
  6669. tt.src = settings.src;
  6670. loadTrack(settings.src, tt);
  6671. } else {
  6672. tt.loaded_ = true;
  6673. }
  6674. return _ret = tt, possibleConstructorReturn(_this, _ret);
  6675. }
  6676. /**
  6677. * Add a cue to the internal list of cues.
  6678. *
  6679. * @param {TextTrack~Cue} cue
  6680. * The cue to add to our internal list
  6681. */
  6682. TextTrack.prototype.addCue = function addCue(originalCue) {
  6683. var cue = originalCue;
  6684. if (window_1.vttjs && !(originalCue instanceof window_1.vttjs.VTTCue)) {
  6685. cue = new window_1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
  6686. for (var prop in originalCue) {
  6687. if (!(prop in cue)) {
  6688. cue[prop] = originalCue[prop];
  6689. }
  6690. }
  6691. // make sure that `id` is copied over
  6692. cue.id = originalCue.id;
  6693. cue.originalCue_ = originalCue;
  6694. }
  6695. var tracks = this.tech_.textTracks();
  6696. for (var i = 0; i < tracks.length; i++) {
  6697. if (tracks[i] !== this) {
  6698. tracks[i].removeCue(cue);
  6699. }
  6700. }
  6701. this.cues_.push(cue);
  6702. this.cues.setCues_(this.cues_);
  6703. };
  6704. /**
  6705. * Remove a cue from our internal list
  6706. *
  6707. * @param {TextTrack~Cue} removeCue
  6708. * The cue to remove from our internal list
  6709. */
  6710. TextTrack.prototype.removeCue = function removeCue(_removeCue) {
  6711. var i = this.cues_.length;
  6712. while (i--) {
  6713. var cue = this.cues_[i];
  6714. if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
  6715. this.cues_.splice(i, 1);
  6716. this.cues.setCues_(this.cues_);
  6717. break;
  6718. }
  6719. }
  6720. };
  6721. return TextTrack;
  6722. }(Track);
  6723. /**
  6724. * cuechange - One or more cues in the track have become active or stopped being active.
  6725. */
  6726. TextTrack.prototype.allowedEvents_ = {
  6727. cuechange: 'cuechange'
  6728. };
  6729. /**
  6730. * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
  6731. * only one `AudioTrack` in the list will be enabled at a time.
  6732. *
  6733. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
  6734. * @extends Track
  6735. */
  6736. var AudioTrack = function (_Track) {
  6737. inherits(AudioTrack, _Track);
  6738. /**
  6739. * Create an instance of this class.
  6740. *
  6741. * @param {Object} [options={}]
  6742. * Object of option names and values
  6743. *
  6744. * @param {AudioTrack~Kind} [options.kind='']
  6745. * A valid audio track kind
  6746. *
  6747. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6748. * A unique id for this AudioTrack.
  6749. *
  6750. * @param {string} [options.label='']
  6751. * The menu label for this track.
  6752. *
  6753. * @param {string} [options.language='']
  6754. * A valid two character language code.
  6755. *
  6756. * @param {boolean} [options.enabled]
  6757. * If this track is the one that is currently playing. If this track is part of
  6758. * an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
  6759. */
  6760. function AudioTrack() {
  6761. var _this, _ret;
  6762. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6763. classCallCheck(this, AudioTrack);
  6764. var settings = mergeOptions(options, {
  6765. kind: AudioTrackKind[options.kind] || ''
  6766. });
  6767. // on IE8 this will be a document element
  6768. // for every other browser this will be a normal object
  6769. var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  6770. var enabled = false;
  6771. if (IS_IE8) {
  6772. for (var prop in AudioTrack.prototype) {
  6773. if (prop !== 'constructor') {
  6774. track[prop] = AudioTrack.prototype[prop];
  6775. }
  6776. }
  6777. }
  6778. /**
  6779. * @memberof AudioTrack
  6780. * @member {boolean} enabled
  6781. * If this `AudioTrack` is enabled or not. When setting this will
  6782. * fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
  6783. * @instance
  6784. *
  6785. * @fires VideoTrack#selectedchange
  6786. */
  6787. Object.defineProperty(track, 'enabled', {
  6788. get: function get$$1() {
  6789. return enabled;
  6790. },
  6791. set: function set$$1(newEnabled) {
  6792. // an invalid or unchanged value
  6793. if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
  6794. return;
  6795. }
  6796. enabled = newEnabled;
  6797. /**
  6798. * An event that fires when enabled changes on this track. This allows
  6799. * the AudioTrackList that holds this track to act accordingly.
  6800. *
  6801. * > Note: This is not part of the spec! Native tracks will do
  6802. * this internally without an event.
  6803. *
  6804. * @event AudioTrack#enabledchange
  6805. * @type {EventTarget~Event}
  6806. */
  6807. this.trigger('enabledchange');
  6808. }
  6809. });
  6810. // if the user sets this track to selected then
  6811. // set selected to that true value otherwise
  6812. // we keep it false
  6813. if (settings.enabled) {
  6814. track.enabled = settings.enabled;
  6815. }
  6816. track.loaded_ = true;
  6817. return _ret = track, possibleConstructorReturn(_this, _ret);
  6818. }
  6819. return AudioTrack;
  6820. }(Track);
  6821. /**
  6822. * A representation of a single `VideoTrack`.
  6823. *
  6824. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
  6825. * @extends Track
  6826. */
  6827. var VideoTrack = function (_Track) {
  6828. inherits(VideoTrack, _Track);
  6829. /**
  6830. * Create an instance of this class.
  6831. *
  6832. * @param {Object} [options={}]
  6833. * Object of option names and values
  6834. *
  6835. * @param {string} [options.kind='']
  6836. * A valid {@link VideoTrack~Kind}
  6837. *
  6838. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6839. * A unique id for this AudioTrack.
  6840. *
  6841. * @param {string} [options.label='']
  6842. * The menu label for this track.
  6843. *
  6844. * @param {string} [options.language='']
  6845. * A valid two character language code.
  6846. *
  6847. * @param {boolean} [options.selected]
  6848. * If this track is the one that is currently playing.
  6849. */
  6850. function VideoTrack() {
  6851. var _this, _ret;
  6852. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6853. classCallCheck(this, VideoTrack);
  6854. var settings = mergeOptions(options, {
  6855. kind: VideoTrackKind[options.kind] || ''
  6856. });
  6857. // on IE8 this will be a document element
  6858. // for every other browser this will be a normal object
  6859. var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
  6860. var selected = false;
  6861. if (IS_IE8) {
  6862. for (var prop in VideoTrack.prototype) {
  6863. if (prop !== 'constructor') {
  6864. track[prop] = VideoTrack.prototype[prop];
  6865. }
  6866. }
  6867. }
  6868. /**
  6869. * @memberof VideoTrack
  6870. * @member {boolean} selected
  6871. * If this `VideoTrack` is selected or not. When setting this will
  6872. * fire {@link VideoTrack#selectedchange} if the state of selected changed.
  6873. * @instance
  6874. *
  6875. * @fires VideoTrack#selectedchange
  6876. */
  6877. Object.defineProperty(track, 'selected', {
  6878. get: function get$$1() {
  6879. return selected;
  6880. },
  6881. set: function set$$1(newSelected) {
  6882. // an invalid or unchanged value
  6883. if (typeof newSelected !== 'boolean' || newSelected === selected) {
  6884. return;
  6885. }
  6886. selected = newSelected;
  6887. /**
  6888. * An event that fires when selected changes on this track. This allows
  6889. * the VideoTrackList that holds this track to act accordingly.
  6890. *
  6891. * > Note: This is not part of the spec! Native tracks will do
  6892. * this internally without an event.
  6893. *
  6894. * @event VideoTrack#selectedchange
  6895. * @type {EventTarget~Event}
  6896. */
  6897. this.trigger('selectedchange');
  6898. }
  6899. });
  6900. // if the user sets this track to selected then
  6901. // set selected to that true value otherwise
  6902. // we keep it false
  6903. if (settings.selected) {
  6904. track.selected = settings.selected;
  6905. }
  6906. return _ret = track, possibleConstructorReturn(_this, _ret);
  6907. }
  6908. return VideoTrack;
  6909. }(Track);
  6910. /**
  6911. * @file html-track-element.js
  6912. */
  6913. /**
  6914. * @memberof HTMLTrackElement
  6915. * @typedef {HTMLTrackElement~ReadyState}
  6916. * @enum {number}
  6917. */
  6918. var NONE = 0;
  6919. var LOADING = 1;
  6920. var LOADED = 2;
  6921. var ERROR = 3;
  6922. /**
  6923. * A single track represented in the DOM.
  6924. *
  6925. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
  6926. * @extends EventTarget
  6927. */
  6928. var HTMLTrackElement = function (_EventTarget) {
  6929. inherits(HTMLTrackElement, _EventTarget);
  6930. /**
  6931. * Create an instance of this class.
  6932. *
  6933. * @param {Object} options={}
  6934. * Object of option names and values
  6935. *
  6936. * @param {Tech} options.tech
  6937. * A reference to the tech that owns this HTMLTrackElement.
  6938. *
  6939. * @param {TextTrack~Kind} [options.kind='subtitles']
  6940. * A valid text track kind.
  6941. *
  6942. * @param {TextTrack~Mode} [options.mode='disabled']
  6943. * A valid text track mode.
  6944. *
  6945. * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
  6946. * A unique id for this TextTrack.
  6947. *
  6948. * @param {string} [options.label='']
  6949. * The menu label for this track.
  6950. *
  6951. * @param {string} [options.language='']
  6952. * A valid two character language code.
  6953. *
  6954. * @param {string} [options.srclang='']
  6955. * A valid two character language code. An alternative, but deprioritized
  6956. * vesion of `options.language`
  6957. *
  6958. * @param {string} [options.src]
  6959. * A url to TextTrack cues.
  6960. *
  6961. * @param {boolean} [options.default]
  6962. * If this track should default to on or off.
  6963. */
  6964. function HTMLTrackElement() {
  6965. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  6966. classCallCheck(this, HTMLTrackElement);
  6967. var _this = possibleConstructorReturn(this, _EventTarget.call(this));
  6968. var readyState = void 0;
  6969. var trackElement = _this; // eslint-disable-line
  6970. if (IS_IE8) {
  6971. trackElement = document_1.createElement('custom');
  6972. for (var prop in HTMLTrackElement.prototype) {
  6973. if (prop !== 'constructor') {
  6974. trackElement[prop] = HTMLTrackElement.prototype[prop];
  6975. }
  6976. }
  6977. }
  6978. var track = new TextTrack(options);
  6979. trackElement.kind = track.kind;
  6980. trackElement.src = track.src;
  6981. trackElement.srclang = track.language;
  6982. trackElement.label = track.label;
  6983. trackElement['default'] = track['default'];
  6984. /**
  6985. * @memberof HTMLTrackElement
  6986. * @member {HTMLTrackElement~ReadyState} readyState
  6987. * The current ready state of the track element.
  6988. * @instance
  6989. */
  6990. Object.defineProperty(trackElement, 'readyState', {
  6991. get: function get$$1() {
  6992. return readyState;
  6993. }
  6994. });
  6995. /**
  6996. * @memberof HTMLTrackElement
  6997. * @member {TextTrack} track
  6998. * The underlying TextTrack object.
  6999. * @instance
  7000. *
  7001. */
  7002. Object.defineProperty(trackElement, 'track', {
  7003. get: function get$$1() {
  7004. return track;
  7005. }
  7006. });
  7007. readyState = NONE;
  7008. /**
  7009. * @listens TextTrack#loadeddata
  7010. * @fires HTMLTrackElement#load
  7011. */
  7012. track.addEventListener('loadeddata', function () {
  7013. readyState = LOADED;
  7014. trackElement.trigger({
  7015. type: 'load',
  7016. target: trackElement
  7017. });
  7018. });
  7019. if (IS_IE8) {
  7020. var _ret;
  7021. return _ret = trackElement, possibleConstructorReturn(_this, _ret);
  7022. }
  7023. return _this;
  7024. }
  7025. return HTMLTrackElement;
  7026. }(EventTarget);
  7027. HTMLTrackElement.prototype.allowedEvents_ = {
  7028. load: 'load'
  7029. };
  7030. HTMLTrackElement.NONE = NONE;
  7031. HTMLTrackElement.LOADING = LOADING;
  7032. HTMLTrackElement.LOADED = LOADED;
  7033. HTMLTrackElement.ERROR = ERROR;
  7034. /*
  7035. * This file contains all track properties that are used in
  7036. * player.js, tech.js, html5.js and possibly other techs in the future.
  7037. */
  7038. var NORMAL = {
  7039. audio: {
  7040. ListClass: AudioTrackList,
  7041. TrackClass: AudioTrack,
  7042. capitalName: 'Audio'
  7043. },
  7044. video: {
  7045. ListClass: VideoTrackList,
  7046. TrackClass: VideoTrack,
  7047. capitalName: 'Video'
  7048. },
  7049. text: {
  7050. ListClass: TextTrackList,
  7051. TrackClass: TextTrack,
  7052. capitalName: 'Text'
  7053. }
  7054. };
  7055. Object.keys(NORMAL).forEach(function (type) {
  7056. NORMAL[type].getterName = type + 'Tracks';
  7057. NORMAL[type].privateName = type + 'Tracks_';
  7058. });
  7059. var REMOTE = {
  7060. remoteText: {
  7061. ListClass: TextTrackList,
  7062. TrackClass: TextTrack,
  7063. capitalName: 'RemoteText',
  7064. getterName: 'remoteTextTracks',
  7065. privateName: 'remoteTextTracks_'
  7066. },
  7067. remoteTextEl: {
  7068. ListClass: HtmlTrackElementList,
  7069. TrackClass: HTMLTrackElement,
  7070. capitalName: 'RemoteTextTrackEls',
  7071. getterName: 'remoteTextTrackEls',
  7072. privateName: 'remoteTextTrackEls_'
  7073. }
  7074. };
  7075. var ALL = mergeOptions(NORMAL, REMOTE);
  7076. REMOTE.names = Object.keys(REMOTE);
  7077. NORMAL.names = Object.keys(NORMAL);
  7078. ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
  7079. var vtt = {};
  7080. /**
  7081. * @file tech.js
  7082. */
  7083. /**
  7084. * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
  7085. * that just contains the src url alone.
  7086. * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
  7087. * `var SourceString = 'http://example.com/some-video.mp4';`
  7088. *
  7089. * @typedef {Object|string} Tech~SourceObject
  7090. *
  7091. * @property {string} src
  7092. * The url to the source
  7093. *
  7094. * @property {string} type
  7095. * The mime type of the source
  7096. */
  7097. /**
  7098. * A function used by {@link Tech} to create a new {@link TextTrack}.
  7099. *
  7100. * @private
  7101. *
  7102. * @param {Tech} self
  7103. * An instance of the Tech class.
  7104. *
  7105. * @param {string} kind
  7106. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  7107. *
  7108. * @param {string} [label]
  7109. * Label to identify the text track
  7110. *
  7111. * @param {string} [language]
  7112. * Two letter language abbreviation
  7113. *
  7114. * @param {Object} [options={}]
  7115. * An object with additional text track options
  7116. *
  7117. * @return {TextTrack}
  7118. * The text track that was created.
  7119. */
  7120. function createTrackHelper(self, kind, label, language) {
  7121. var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
  7122. var tracks = self.textTracks();
  7123. options.kind = kind;
  7124. if (label) {
  7125. options.label = label;
  7126. }
  7127. if (language) {
  7128. options.language = language;
  7129. }
  7130. options.tech = self;
  7131. var track = new ALL.text.TrackClass(options);
  7132. tracks.addTrack(track);
  7133. return track;
  7134. }
  7135. /**
  7136. * This is the base class for media playback technology controllers, such as
  7137. * {@link Flash} and {@link HTML5}
  7138. *
  7139. * @extends Component
  7140. */
  7141. var Tech = function (_Component) {
  7142. inherits(Tech, _Component);
  7143. /**
  7144. * Create an instance of this Tech.
  7145. *
  7146. * @param {Object} [options]
  7147. * The key/value store of player options.
  7148. *
  7149. * @param {Component~ReadyCallback} ready
  7150. * Callback function to call when the `HTML5` Tech is ready.
  7151. */
  7152. function Tech() {
  7153. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  7154. var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
  7155. classCallCheck(this, Tech);
  7156. // we don't want the tech to report user activity automatically.
  7157. // This is done manually in addControlsListeners
  7158. options.reportTouchActivity = false;
  7159. // keep track of whether the current source has played at all to
  7160. // implement a very limited played()
  7161. var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
  7162. _this.hasStarted_ = false;
  7163. _this.on('playing', function () {
  7164. this.hasStarted_ = true;
  7165. });
  7166. _this.on('loadstart', function () {
  7167. this.hasStarted_ = false;
  7168. });
  7169. ALL.names.forEach(function (name) {
  7170. var props = ALL[name];
  7171. if (options && options[props.getterName]) {
  7172. _this[props.privateName] = options[props.getterName];
  7173. }
  7174. });
  7175. // Manually track progress in cases where the browser/flash player doesn't report it.
  7176. if (!_this.featuresProgressEvents) {
  7177. _this.manualProgressOn();
  7178. }
  7179. // Manually track timeupdates in cases where the browser/flash player doesn't report it.
  7180. if (!_this.featuresTimeupdateEvents) {
  7181. _this.manualTimeUpdatesOn();
  7182. }
  7183. ['Text', 'Audio', 'Video'].forEach(function (track) {
  7184. if (options['native' + track + 'Tracks'] === false) {
  7185. _this['featuresNative' + track + 'Tracks'] = false;
  7186. }
  7187. });
  7188. if (options.nativeCaptions === false || options.nativeTextTracks === false) {
  7189. _this.featuresNativeTextTracks = false;
  7190. } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
  7191. _this.featuresNativeTextTracks = true;
  7192. }
  7193. if (!_this.featuresNativeTextTracks) {
  7194. _this.emulateTextTracks();
  7195. }
  7196. _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
  7197. _this.initTrackListeners();
  7198. // Turn on component tap events only if not using native controls
  7199. if (!options.nativeControlsForTouch) {
  7200. _this.emitTapEvents();
  7201. }
  7202. if (_this.constructor) {
  7203. _this.name_ = _this.constructor.name || 'Unknown Tech';
  7204. }
  7205. return _this;
  7206. }
  7207. /**
  7208. * A special function to trigger source set in a way that will allow player
  7209. * to re-trigger if the player or tech are not ready yet.
  7210. *
  7211. * @fires Tech#sourceset
  7212. * @param {string} src The source string at the time of the source changing.
  7213. */
  7214. Tech.prototype.triggerSourceset = function triggerSourceset(src) {
  7215. var _this2 = this;
  7216. if (!this.isReady_) {
  7217. // on initial ready we have to trigger source set
  7218. // 1ms after ready so that player can watch for it.
  7219. this.one('ready', function () {
  7220. return _this2.setTimeout(function () {
  7221. return _this2.triggerSourceset(src);
  7222. }, 1);
  7223. });
  7224. }
  7225. /**
  7226. * Fired when the source is set on the tech causing the media element
  7227. * to reload.
  7228. *
  7229. * @see {@link Player#event:sourceset}
  7230. * @event Tech#sourceset
  7231. * @type {EventTarget~Event}
  7232. */
  7233. this.trigger({
  7234. src: src,
  7235. type: 'sourceset'
  7236. });
  7237. };
  7238. /* Fallbacks for unsupported event types
  7239. ================================================================================ */
  7240. /**
  7241. * Polyfill the `progress` event for browsers that don't support it natively.
  7242. *
  7243. * @see {@link Tech#trackProgress}
  7244. */
  7245. Tech.prototype.manualProgressOn = function manualProgressOn() {
  7246. this.on('durationchange', this.onDurationChange);
  7247. this.manualProgress = true;
  7248. // Trigger progress watching when a source begins loading
  7249. this.one('ready', this.trackProgress);
  7250. };
  7251. /**
  7252. * Turn off the polyfill for `progress` events that was created in
  7253. * {@link Tech#manualProgressOn}
  7254. */
  7255. Tech.prototype.manualProgressOff = function manualProgressOff() {
  7256. this.manualProgress = false;
  7257. this.stopTrackingProgress();
  7258. this.off('durationchange', this.onDurationChange);
  7259. };
  7260. /**
  7261. * This is used to trigger a `progress` event when the buffered percent changes. It
  7262. * sets an interval function that will be called every 500 milliseconds to check if the
  7263. * buffer end percent has changed.
  7264. *
  7265. * > This function is called by {@link Tech#manualProgressOn}
  7266. *
  7267. * @param {EventTarget~Event} event
  7268. * The `ready` event that caused this to run.
  7269. *
  7270. * @listens Tech#ready
  7271. * @fires Tech#progress
  7272. */
  7273. Tech.prototype.trackProgress = function trackProgress(event) {
  7274. this.stopTrackingProgress();
  7275. this.progressInterval = this.setInterval(bind(this, function () {
  7276. // Don't trigger unless buffered amount is greater than last time
  7277. var numBufferedPercent = this.bufferedPercent();
  7278. if (this.bufferedPercent_ !== numBufferedPercent) {
  7279. /**
  7280. * See {@link Player#progress}
  7281. *
  7282. * @event Tech#progress
  7283. * @type {EventTarget~Event}
  7284. */
  7285. this.trigger('progress');
  7286. }
  7287. this.bufferedPercent_ = numBufferedPercent;
  7288. if (numBufferedPercent === 1) {
  7289. this.stopTrackingProgress();
  7290. }
  7291. }), 500);
  7292. };
  7293. /**
  7294. * Update our internal duration on a `durationchange` event by calling
  7295. * {@link Tech#duration}.
  7296. *
  7297. * @param {EventTarget~Event} event
  7298. * The `durationchange` event that caused this to run.
  7299. *
  7300. * @listens Tech#durationchange
  7301. */
  7302. Tech.prototype.onDurationChange = function onDurationChange(event) {
  7303. this.duration_ = this.duration();
  7304. };
  7305. /**
  7306. * Get and create a `TimeRange` object for buffering.
  7307. *
  7308. * @return {TimeRange}
  7309. * The time range object that was created.
  7310. */
  7311. Tech.prototype.buffered = function buffered() {
  7312. return createTimeRanges(0, 0);
  7313. };
  7314. /**
  7315. * Get the percentage of the current video that is currently buffered.
  7316. *
  7317. * @return {number}
  7318. * A number from 0 to 1 that represents the decimal percentage of the
  7319. * video that is buffered.
  7320. *
  7321. */
  7322. Tech.prototype.bufferedPercent = function bufferedPercent$$1() {
  7323. return bufferedPercent(this.buffered(), this.duration_);
  7324. };
  7325. /**
  7326. * Turn off the polyfill for `progress` events that was created in
  7327. * {@link Tech#manualProgressOn}
  7328. * Stop manually tracking progress events by clearing the interval that was set in
  7329. * {@link Tech#trackProgress}.
  7330. */
  7331. Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
  7332. this.clearInterval(this.progressInterval);
  7333. };
  7334. /**
  7335. * Polyfill the `timeupdate` event for browsers that don't support it.
  7336. *
  7337. * @see {@link Tech#trackCurrentTime}
  7338. */
  7339. Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
  7340. this.manualTimeUpdates = true;
  7341. this.on('play', this.trackCurrentTime);
  7342. this.on('pause', this.stopTrackingCurrentTime);
  7343. };
  7344. /**
  7345. * Turn off the polyfill for `timeupdate` events that was created in
  7346. * {@link Tech#manualTimeUpdatesOn}
  7347. */
  7348. Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
  7349. this.manualTimeUpdates = false;
  7350. this.stopTrackingCurrentTime();
  7351. this.off('play', this.trackCurrentTime);
  7352. this.off('pause', this.stopTrackingCurrentTime);
  7353. };
  7354. /**
  7355. * Sets up an interval function to track current time and trigger `timeupdate` every
  7356. * 250 milliseconds.
  7357. *
  7358. * @listens Tech#play
  7359. * @triggers Tech#timeupdate
  7360. */
  7361. Tech.prototype.trackCurrentTime = function trackCurrentTime() {
  7362. if (this.currentTimeInterval) {
  7363. this.stopTrackingCurrentTime();
  7364. }
  7365. this.currentTimeInterval = this.setInterval(function () {
  7366. /**
  7367. * Triggered at an interval of 250ms to indicated that time is passing in the video.
  7368. *
  7369. * @event Tech#timeupdate
  7370. * @type {EventTarget~Event}
  7371. */
  7372. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  7373. // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
  7374. }, 250);
  7375. };
  7376. /**
  7377. * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
  7378. * `timeupdate` event is no longer triggered.
  7379. *
  7380. * @listens {Tech#pause}
  7381. */
  7382. Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
  7383. this.clearInterval(this.currentTimeInterval);
  7384. // #1002 - if the video ends right before the next timeupdate would happen,
  7385. // the progress bar won't make it all the way to the end
  7386. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  7387. };
  7388. /**
  7389. * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
  7390. * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
  7391. *
  7392. * @fires Component#dispose
  7393. */
  7394. Tech.prototype.dispose = function dispose() {
  7395. // clear out all tracks because we can't reuse them between techs
  7396. this.clearTracks(NORMAL.names);
  7397. // Turn off any manual progress or timeupdate tracking
  7398. if (this.manualProgress) {
  7399. this.manualProgressOff();
  7400. }
  7401. if (this.manualTimeUpdates) {
  7402. this.manualTimeUpdatesOff();
  7403. }
  7404. _Component.prototype.dispose.call(this);
  7405. };
  7406. /**
  7407. * Clear out a single `TrackList` or an array of `TrackLists` given their names.
  7408. *
  7409. * > Note: Techs without source handlers should call this between sources for `video`
  7410. * & `audio` tracks. You don't want to use them between tracks!
  7411. *
  7412. * @param {string[]|string} types
  7413. * TrackList names to clear, valid names are `video`, `audio`, and
  7414. * `text`.
  7415. */
  7416. Tech.prototype.clearTracks = function clearTracks(types) {
  7417. var _this3 = this;
  7418. types = [].concat(types);
  7419. // clear out all tracks because we can't reuse them between techs
  7420. types.forEach(function (type) {
  7421. var list = _this3[type + 'Tracks']() || [];
  7422. var i = list.length;
  7423. while (i--) {
  7424. var track = list[i];
  7425. if (type === 'text') {
  7426. _this3.removeRemoteTextTrack(track);
  7427. }
  7428. list.removeTrack(track);
  7429. }
  7430. });
  7431. };
  7432. /**
  7433. * Remove any TextTracks added via addRemoteTextTrack that are
  7434. * flagged for automatic garbage collection
  7435. */
  7436. Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
  7437. var list = this.autoRemoteTextTracks_ || [];
  7438. var i = list.length;
  7439. while (i--) {
  7440. var track = list[i];
  7441. this.removeRemoteTextTrack(track);
  7442. }
  7443. };
  7444. /**
  7445. * Reset the tech, which will removes all sources and reset the internal readyState.
  7446. *
  7447. * @abstract
  7448. */
  7449. Tech.prototype.reset = function reset() {};
  7450. /**
  7451. * Get or set an error on the Tech.
  7452. *
  7453. * @param {MediaError} [err]
  7454. * Error to set on the Tech
  7455. *
  7456. * @return {MediaError|null}
  7457. * The current error object on the tech, or null if there isn't one.
  7458. */
  7459. Tech.prototype.error = function error(err) {
  7460. if (err !== undefined) {
  7461. this.error_ = new MediaError(err);
  7462. this.trigger('error');
  7463. }
  7464. return this.error_;
  7465. };
  7466. /**
  7467. * Returns the `TimeRange`s that have been played through for the current source.
  7468. *
  7469. * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
  7470. * It only checks wether the source has played at all or not.
  7471. *
  7472. * @return {TimeRange}
  7473. * - A single time range if this video has played
  7474. * - An empty set of ranges if not.
  7475. */
  7476. Tech.prototype.played = function played() {
  7477. if (this.hasStarted_) {
  7478. return createTimeRanges(0, 0);
  7479. }
  7480. return createTimeRanges();
  7481. };
  7482. /**
  7483. * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
  7484. * previously called.
  7485. *
  7486. * @fires Tech#timeupdate
  7487. */
  7488. Tech.prototype.setCurrentTime = function setCurrentTime() {
  7489. // improve the accuracy of manual timeupdates
  7490. if (this.manualTimeUpdates) {
  7491. /**
  7492. * A manual `timeupdate` event.
  7493. *
  7494. * @event Tech#timeupdate
  7495. * @type {EventTarget~Event}
  7496. */
  7497. this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  7498. }
  7499. };
  7500. /**
  7501. * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
  7502. * {@link TextTrackList} events.
  7503. *
  7504. * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`.
  7505. *
  7506. * @fires Tech#audiotrackchange
  7507. * @fires Tech#videotrackchange
  7508. * @fires Tech#texttrackchange
  7509. */
  7510. Tech.prototype.initTrackListeners = function initTrackListeners() {
  7511. var _this4 = this;
  7512. /**
  7513. * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
  7514. *
  7515. * @event Tech#audiotrackchange
  7516. * @type {EventTarget~Event}
  7517. */
  7518. /**
  7519. * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
  7520. *
  7521. * @event Tech#videotrackchange
  7522. * @type {EventTarget~Event}
  7523. */
  7524. /**
  7525. * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
  7526. *
  7527. * @event Tech#texttrackchange
  7528. * @type {EventTarget~Event}
  7529. */
  7530. NORMAL.names.forEach(function (name) {
  7531. var props = NORMAL[name];
  7532. var trackListChanges = function trackListChanges() {
  7533. _this4.trigger(name + 'trackchange');
  7534. };
  7535. var tracks = _this4[props.getterName]();
  7536. tracks.addEventListener('removetrack', trackListChanges);
  7537. tracks.addEventListener('addtrack', trackListChanges);
  7538. _this4.on('dispose', function () {
  7539. tracks.removeEventListener('removetrack', trackListChanges);
  7540. tracks.removeEventListener('addtrack', trackListChanges);
  7541. });
  7542. });
  7543. };
  7544. /**
  7545. * Emulate TextTracks using vtt.js if necessary
  7546. *
  7547. * @fires Tech#vttjsloaded
  7548. * @fires Tech#vttjserror
  7549. */
  7550. Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
  7551. var _this5 = this;
  7552. if (window_1.WebVTT) {
  7553. return;
  7554. }
  7555. // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
  7556. // signals that the Tech is ready at which point Tech.el_ is part of the DOM
  7557. // before inserting the WebVTT script
  7558. if (document_1.body.contains(this.el())) {
  7559. // load via require if available and vtt.js script location was not passed in
  7560. // as an option. novtt builds will turn the above require call into an empty object
  7561. // which will cause this if check to always fail.
  7562. if (!this.options_['vtt.js'] && isPlain(vtt) && Object.keys(vtt).length > 0) {
  7563. this.trigger('vttjsloaded');
  7564. return;
  7565. }
  7566. // load vtt.js via the script location option or the cdn of no location was
  7567. // passed in
  7568. var script = document_1.createElement('script');
  7569. script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js';
  7570. script.onload = function () {
  7571. /**
  7572. * Fired when vtt.js is loaded.
  7573. *
  7574. * @event Tech#vttjsloaded
  7575. * @type {EventTarget~Event}
  7576. */
  7577. _this5.trigger('vttjsloaded');
  7578. };
  7579. script.onerror = function () {
  7580. /**
  7581. * Fired when vtt.js was not loaded due to an error
  7582. *
  7583. * @event Tech#vttjsloaded
  7584. * @type {EventTarget~Event}
  7585. */
  7586. _this5.trigger('vttjserror');
  7587. };
  7588. this.on('dispose', function () {
  7589. script.onload = null;
  7590. script.onerror = null;
  7591. });
  7592. // but have not loaded yet and we set it to true before the inject so that
  7593. // we don't overwrite the injected window.WebVTT if it loads right away
  7594. window_1.WebVTT = true;
  7595. this.el().parentNode.appendChild(script);
  7596. } else {
  7597. this.ready(this.addWebVttScript_);
  7598. }
  7599. };
  7600. /**
  7601. * Emulate texttracks
  7602. *
  7603. */
  7604. Tech.prototype.emulateTextTracks = function emulateTextTracks() {
  7605. var _this6 = this;
  7606. var tracks = this.textTracks();
  7607. var remoteTracks = this.remoteTextTracks();
  7608. var handleAddTrack = function handleAddTrack(e) {
  7609. return tracks.addTrack(e.track);
  7610. };
  7611. var handleRemoveTrack = function handleRemoveTrack(e) {
  7612. return tracks.removeTrack(e.track);
  7613. };
  7614. remoteTracks.on('addtrack', handleAddTrack);
  7615. remoteTracks.on('removetrack', handleRemoveTrack);
  7616. this.addWebVttScript_();
  7617. var updateDisplay = function updateDisplay() {
  7618. return _this6.trigger('texttrackchange');
  7619. };
  7620. var textTracksChanges = function textTracksChanges() {
  7621. updateDisplay();
  7622. for (var i = 0; i < tracks.length; i++) {
  7623. var track = tracks[i];
  7624. track.removeEventListener('cuechange', updateDisplay);
  7625. if (track.mode === 'showing') {
  7626. track.addEventListener('cuechange', updateDisplay);
  7627. }
  7628. }
  7629. };
  7630. textTracksChanges();
  7631. tracks.addEventListener('change', textTracksChanges);
  7632. tracks.addEventListener('addtrack', textTracksChanges);
  7633. tracks.addEventListener('removetrack', textTracksChanges);
  7634. this.on('dispose', function () {
  7635. remoteTracks.off('addtrack', handleAddTrack);
  7636. remoteTracks.off('removetrack', handleRemoveTrack);
  7637. tracks.removeEventListener('change', textTracksChanges);
  7638. tracks.removeEventListener('addtrack', textTracksChanges);
  7639. tracks.removeEventListener('removetrack', textTracksChanges);
  7640. for (var i = 0; i < tracks.length; i++) {
  7641. var track = tracks[i];
  7642. track.removeEventListener('cuechange', updateDisplay);
  7643. }
  7644. });
  7645. };
  7646. /**
  7647. * Create and returns a remote {@link TextTrack} object.
  7648. *
  7649. * @param {string} kind
  7650. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  7651. *
  7652. * @param {string} [label]
  7653. * Label to identify the text track
  7654. *
  7655. * @param {string} [language]
  7656. * Two letter language abbreviation
  7657. *
  7658. * @return {TextTrack}
  7659. * The TextTrack that gets created.
  7660. */
  7661. Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  7662. if (!kind) {
  7663. throw new Error('TextTrack kind is required but was not provided');
  7664. }
  7665. return createTrackHelper(this, kind, label, language);
  7666. };
  7667. /**
  7668. * Create an emulated TextTrack for use by addRemoteTextTrack
  7669. *
  7670. * This is intended to be overridden by classes that inherit from
  7671. * Tech in order to create native or custom TextTracks.
  7672. *
  7673. * @param {Object} options
  7674. * The object should contain the options to initialize the TextTrack with.
  7675. *
  7676. * @param {string} [options.kind]
  7677. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
  7678. *
  7679. * @param {string} [options.label].
  7680. * Label to identify the text track
  7681. *
  7682. * @param {string} [options.language]
  7683. * Two letter language abbreviation.
  7684. *
  7685. * @return {HTMLTrackElement}
  7686. * The track element that gets created.
  7687. */
  7688. Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
  7689. var track = mergeOptions(options, {
  7690. tech: this
  7691. });
  7692. return new REMOTE.remoteTextEl.TrackClass(track);
  7693. };
  7694. /**
  7695. * Creates a remote text track object and returns an html track element.
  7696. *
  7697. * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
  7698. *
  7699. * @param {Object} options
  7700. * See {@link Tech#createRemoteTextTrack} for more detailed properties.
  7701. *
  7702. * @param {boolean} [manualCleanup=true]
  7703. * - When false: the TextTrack will be automatically removed from the video
  7704. * element whenever the source changes
  7705. * - When True: The TextTrack will have to be cleaned up manually
  7706. *
  7707. * @return {HTMLTrackElement}
  7708. * An Html Track Element.
  7709. *
  7710. * @deprecated The default functionality for this function will be equivalent
  7711. * to "manualCleanup=false" in the future. The manualCleanup parameter will
  7712. * also be removed.
  7713. */
  7714. Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
  7715. var _this7 = this;
  7716. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  7717. var manualCleanup = arguments[1];
  7718. var htmlTrackElement = this.createRemoteTextTrack(options);
  7719. if (manualCleanup !== true && manualCleanup !== false) {
  7720. // deprecation warning
  7721. log.warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
  7722. manualCleanup = true;
  7723. }
  7724. // store HTMLTrackElement and TextTrack to remote list
  7725. this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
  7726. this.remoteTextTracks().addTrack(htmlTrackElement.track);
  7727. if (manualCleanup !== true) {
  7728. // create the TextTrackList if it doesn't exist
  7729. this.ready(function () {
  7730. return _this7.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
  7731. });
  7732. }
  7733. return htmlTrackElement;
  7734. };
  7735. /**
  7736. * Remove a remote text track from the remote `TextTrackList`.
  7737. *
  7738. * @param {TextTrack} track
  7739. * `TextTrack` to remove from the `TextTrackList`
  7740. */
  7741. Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
  7742. var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
  7743. // remove HTMLTrackElement and TextTrack from remote list
  7744. this.remoteTextTrackEls().removeTrackElement_(trackElement);
  7745. this.remoteTextTracks().removeTrack(track);
  7746. this.autoRemoteTextTracks_.removeTrack(track);
  7747. };
  7748. /**
  7749. * Gets available media playback quality metrics as specified by the W3C's Media
  7750. * Playback Quality API.
  7751. *
  7752. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  7753. *
  7754. * @return {Object}
  7755. * An object with supported media playback quality metrics
  7756. *
  7757. * @abstract
  7758. */
  7759. Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  7760. return {};
  7761. };
  7762. /**
  7763. * A method to set a poster from a `Tech`.
  7764. *
  7765. * @abstract
  7766. */
  7767. Tech.prototype.setPoster = function setPoster() {};
  7768. /**
  7769. * A method to check for the presence of the 'playsinine' <video> attribute.
  7770. *
  7771. * @abstract
  7772. */
  7773. Tech.prototype.playsinline = function playsinline() {};
  7774. /**
  7775. * A method to set or unset the 'playsinine' <video> attribute.
  7776. *
  7777. * @abstract
  7778. */
  7779. Tech.prototype.setPlaysinline = function setPlaysinline() {};
  7780. /*
  7781. * Check if the tech can support the given mime-type.
  7782. *
  7783. * The base tech does not support any type, but source handlers might
  7784. * overwrite this.
  7785. *
  7786. * @param {string} type
  7787. * The mimetype to check for support
  7788. *
  7789. * @return {string}
  7790. * 'probably', 'maybe', or empty string
  7791. *
  7792. * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
  7793. *
  7794. * @abstract
  7795. */
  7796. Tech.prototype.canPlayType = function canPlayType() {
  7797. return '';
  7798. };
  7799. /**
  7800. * Check if the type is supported by this tech.
  7801. *
  7802. * The base tech does not support any type, but source handlers might
  7803. * overwrite this.
  7804. *
  7805. * @param {string} type
  7806. * The media type to check
  7807. * @return {string} Returns the native video element's response
  7808. */
  7809. Tech.canPlayType = function canPlayType() {
  7810. return '';
  7811. };
  7812. /**
  7813. * Check if the tech can support the given source
  7814. * @param {Object} srcObj
  7815. * The source object
  7816. * @param {Object} options
  7817. * The options passed to the tech
  7818. * @return {string} 'probably', 'maybe', or '' (empty string)
  7819. */
  7820. Tech.canPlaySource = function canPlaySource(srcObj, options) {
  7821. return Tech.canPlayType(srcObj.type);
  7822. };
  7823. /*
  7824. * Return whether the argument is a Tech or not.
  7825. * Can be passed either a Class like `Html5` or a instance like `player.tech_`
  7826. *
  7827. * @param {Object} component
  7828. * The item to check
  7829. *
  7830. * @return {boolean}
  7831. * Whether it is a tech or not
  7832. * - True if it is a tech
  7833. * - False if it is not
  7834. */
  7835. Tech.isTech = function isTech(component) {
  7836. return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
  7837. };
  7838. /**
  7839. * Registers a `Tech` into a shared list for videojs.
  7840. *
  7841. * @param {string} name
  7842. * Name of the `Tech` to register.
  7843. *
  7844. * @param {Object} tech
  7845. * The `Tech` class to register.
  7846. */
  7847. Tech.registerTech = function registerTech(name, tech) {
  7848. if (!Tech.techs_) {
  7849. Tech.techs_ = {};
  7850. }
  7851. if (!Tech.isTech(tech)) {
  7852. throw new Error('Tech ' + name + ' must be a Tech');
  7853. }
  7854. if (!Tech.canPlayType) {
  7855. throw new Error('Techs must have a static canPlayType method on them');
  7856. }
  7857. if (!Tech.canPlaySource) {
  7858. throw new Error('Techs must have a static canPlaySource method on them');
  7859. }
  7860. name = toTitleCase(name);
  7861. Tech.techs_[name] = tech;
  7862. if (name !== 'Tech') {
  7863. // camel case the techName for use in techOrder
  7864. Tech.defaultTechOrder_.push(name);
  7865. }
  7866. return tech;
  7867. };
  7868. /**
  7869. * Get a `Tech` from the shared list by name.
  7870. *
  7871. * @param {string} name
  7872. * `camelCase` or `TitleCase` name of the Tech to get
  7873. *
  7874. * @return {Tech|undefined}
  7875. * The `Tech` or undefined if there was no tech with the name requsted.
  7876. */
  7877. Tech.getTech = function getTech(name) {
  7878. if (!name) {
  7879. return;
  7880. }
  7881. name = toTitleCase(name);
  7882. if (Tech.techs_ && Tech.techs_[name]) {
  7883. return Tech.techs_[name];
  7884. }
  7885. if (window_1 && window_1.videojs && window_1.videojs[name]) {
  7886. log.warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
  7887. return window_1.videojs[name];
  7888. }
  7889. };
  7890. return Tech;
  7891. }(Component);
  7892. /**
  7893. * Get the {@link VideoTrackList}
  7894. *
  7895. * @returns {VideoTrackList}
  7896. * @method Tech.prototype.videoTracks
  7897. */
  7898. /**
  7899. * Get the {@link AudioTrackList}
  7900. *
  7901. * @returns {AudioTrackList}
  7902. * @method Tech.prototype.audioTracks
  7903. */
  7904. /**
  7905. * Get the {@link TextTrackList}
  7906. *
  7907. * @returns {TextTrackList}
  7908. * @method Tech.prototype.textTracks
  7909. */
  7910. /**
  7911. * Get the remote element {@link TextTrackList}
  7912. *
  7913. * @returns {TextTrackList}
  7914. * @method Tech.prototype.remoteTextTracks
  7915. */
  7916. /**
  7917. * Get the remote element {@link HtmlTrackElementList}
  7918. *
  7919. * @returns {HtmlTrackElementList}
  7920. * @method Tech.prototype.remoteTextTrackEls
  7921. */
  7922. ALL.names.forEach(function (name) {
  7923. var props = ALL[name];
  7924. Tech.prototype[props.getterName] = function () {
  7925. this[props.privateName] = this[props.privateName] || new props.ListClass();
  7926. return this[props.privateName];
  7927. };
  7928. });
  7929. /**
  7930. * List of associated text tracks
  7931. *
  7932. * @type {TextTrackList}
  7933. * @private
  7934. * @property Tech#textTracks_
  7935. */
  7936. /**
  7937. * List of associated audio tracks.
  7938. *
  7939. * @type {AudioTrackList}
  7940. * @private
  7941. * @property Tech#audioTracks_
  7942. */
  7943. /**
  7944. * List of associated video tracks.
  7945. *
  7946. * @type {VideoTrackList}
  7947. * @private
  7948. * @property Tech#videoTracks_
  7949. */
  7950. /**
  7951. * Boolean indicating wether the `Tech` supports volume control.
  7952. *
  7953. * @type {boolean}
  7954. * @default
  7955. */
  7956. Tech.prototype.featuresVolumeControl = true;
  7957. /**
  7958. * Boolean indicating whether the `Tech` supports muting volume.
  7959. *
  7960. * @type {bolean}
  7961. * @default
  7962. */
  7963. Tech.prototype.featuresMuteControl = true;
  7964. /**
  7965. * Boolean indicating whether the `Tech` supports fullscreen resize control.
  7966. * Resizing plugins using request fullscreen reloads the plugin
  7967. *
  7968. * @type {boolean}
  7969. * @default
  7970. */
  7971. Tech.prototype.featuresFullscreenResize = false;
  7972. /**
  7973. * Boolean indicating wether the `Tech` supports changing the speed at which the video
  7974. * plays. Examples:
  7975. * - Set player to play 2x (twice) as fast
  7976. * - Set player to play 0.5x (half) as fast
  7977. *
  7978. * @type {boolean}
  7979. * @default
  7980. */
  7981. Tech.prototype.featuresPlaybackRate = false;
  7982. /**
  7983. * Boolean indicating wether the `Tech` supports the `progress` event. This is currently
  7984. * not triggered by video-js-swf. This will be used to determine if
  7985. * {@link Tech#manualProgressOn} should be called.
  7986. *
  7987. * @type {boolean}
  7988. * @default
  7989. */
  7990. Tech.prototype.featuresProgressEvents = false;
  7991. /**
  7992. * Boolean indicating wether the `Tech` supports the `sourceset` event.
  7993. *
  7994. * A tech should set this to `true` and then use {@link Tech#triggerSourceset}
  7995. * to trigger a {@link Tech#event:sourceset} at the earliest time after getting
  7996. * a new source.
  7997. *
  7998. * @type {boolean}
  7999. * @default
  8000. */
  8001. Tech.prototype.featuresSourceset = false;
  8002. /**
  8003. * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently
  8004. * not triggered by video-js-swf. This will be used to determine if
  8005. * {@link Tech#manualTimeUpdates} should be called.
  8006. *
  8007. * @type {boolean}
  8008. * @default
  8009. */
  8010. Tech.prototype.featuresTimeupdateEvents = false;
  8011. /**
  8012. * Boolean indicating wether the `Tech` supports the native `TextTrack`s.
  8013. * This will help us integrate with native `TextTrack`s if the browser supports them.
  8014. *
  8015. * @type {boolean}
  8016. * @default
  8017. */
  8018. Tech.prototype.featuresNativeTextTracks = false;
  8019. /**
  8020. * A functional mixin for techs that want to use the Source Handler pattern.
  8021. * Source handlers are scripts for handling specific formats.
  8022. * The source handler pattern is used for adaptive formats (HLS, DASH) that
  8023. * manually load video data and feed it into a Source Buffer (Media Source Extensions)
  8024. * Example: `Tech.withSourceHandlers.call(MyTech);`
  8025. *
  8026. * @param {Tech} _Tech
  8027. * The tech to add source handler functions to.
  8028. *
  8029. * @mixes Tech~SourceHandlerAdditions
  8030. */
  8031. Tech.withSourceHandlers = function (_Tech) {
  8032. /**
  8033. * Register a source handler
  8034. *
  8035. * @param {Function} handler
  8036. * The source handler class
  8037. *
  8038. * @param {number} [index]
  8039. * Register it at the following index
  8040. */
  8041. _Tech.registerSourceHandler = function (handler, index) {
  8042. var handlers = _Tech.sourceHandlers;
  8043. if (!handlers) {
  8044. handlers = _Tech.sourceHandlers = [];
  8045. }
  8046. if (index === undefined) {
  8047. // add to the end of the list
  8048. index = handlers.length;
  8049. }
  8050. handlers.splice(index, 0, handler);
  8051. };
  8052. /**
  8053. * Check if the tech can support the given type. Also checks the
  8054. * Techs sourceHandlers.
  8055. *
  8056. * @param {string} type
  8057. * The mimetype to check.
  8058. *
  8059. * @return {string}
  8060. * 'probably', 'maybe', or '' (empty string)
  8061. */
  8062. _Tech.canPlayType = function (type) {
  8063. var handlers = _Tech.sourceHandlers || [];
  8064. var can = void 0;
  8065. for (var i = 0; i < handlers.length; i++) {
  8066. can = handlers[i].canPlayType(type);
  8067. if (can) {
  8068. return can;
  8069. }
  8070. }
  8071. return '';
  8072. };
  8073. /**
  8074. * Returns the first source handler that supports the source.
  8075. *
  8076. * TODO: Answer question: should 'probably' be prioritized over 'maybe'
  8077. *
  8078. * @param {Tech~SourceObject} source
  8079. * The source object
  8080. *
  8081. * @param {Object} options
  8082. * The options passed to the tech
  8083. *
  8084. * @return {SourceHandler|null}
  8085. * The first source handler that supports the source or null if
  8086. * no SourceHandler supports the source
  8087. */
  8088. _Tech.selectSourceHandler = function (source, options) {
  8089. var handlers = _Tech.sourceHandlers || [];
  8090. var can = void 0;
  8091. for (var i = 0; i < handlers.length; i++) {
  8092. can = handlers[i].canHandleSource(source, options);
  8093. if (can) {
  8094. return handlers[i];
  8095. }
  8096. }
  8097. return null;
  8098. };
  8099. /**
  8100. * Check if the tech can support the given source.
  8101. *
  8102. * @param {Tech~SourceObject} srcObj
  8103. * The source object
  8104. *
  8105. * @param {Object} options
  8106. * The options passed to the tech
  8107. *
  8108. * @return {string}
  8109. * 'probably', 'maybe', or '' (empty string)
  8110. */
  8111. _Tech.canPlaySource = function (srcObj, options) {
  8112. var sh = _Tech.selectSourceHandler(srcObj, options);
  8113. if (sh) {
  8114. return sh.canHandleSource(srcObj, options);
  8115. }
  8116. return '';
  8117. };
  8118. /**
  8119. * When using a source handler, prefer its implementation of
  8120. * any function normally provided by the tech.
  8121. */
  8122. var deferrable = ['seekable', 'seeking', 'duration'];
  8123. /**
  8124. * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
  8125. * function if it exists, with a fallback to the Techs seekable function.
  8126. *
  8127. * @method _Tech.seekable
  8128. */
  8129. /**
  8130. * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
  8131. * function if it exists, otherwise it will fallback to the techs duration function.
  8132. *
  8133. * @method _Tech.duration
  8134. */
  8135. deferrable.forEach(function (fnName) {
  8136. var originalFn = this[fnName];
  8137. if (typeof originalFn !== 'function') {
  8138. return;
  8139. }
  8140. this[fnName] = function () {
  8141. if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
  8142. return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
  8143. }
  8144. return originalFn.apply(this, arguments);
  8145. };
  8146. }, _Tech.prototype);
  8147. /**
  8148. * Create a function for setting the source using a source object
  8149. * and source handlers.
  8150. * Should never be called unless a source handler was found.
  8151. *
  8152. * @param {Tech~SourceObject} source
  8153. * A source object with src and type keys
  8154. */
  8155. _Tech.prototype.setSource = function (source) {
  8156. var sh = _Tech.selectSourceHandler(source, this.options_);
  8157. if (!sh) {
  8158. // Fall back to a native source hander when unsupported sources are
  8159. // deliberately set
  8160. if (_Tech.nativeSourceHandler) {
  8161. sh = _Tech.nativeSourceHandler;
  8162. } else {
  8163. log.error('No source hander found for the current source.');
  8164. }
  8165. }
  8166. // Dispose any existing source handler
  8167. this.disposeSourceHandler();
  8168. this.off('dispose', this.disposeSourceHandler);
  8169. if (sh !== _Tech.nativeSourceHandler) {
  8170. this.currentSource_ = source;
  8171. }
  8172. this.sourceHandler_ = sh.handleSource(source, this, this.options_);
  8173. this.on('dispose', this.disposeSourceHandler);
  8174. };
  8175. /**
  8176. * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
  8177. *
  8178. * @listens Tech#dispose
  8179. */
  8180. _Tech.prototype.disposeSourceHandler = function () {
  8181. // if we have a source and get another one
  8182. // then we are loading something new
  8183. // than clear all of our current tracks
  8184. if (this.currentSource_) {
  8185. this.clearTracks(['audio', 'video']);
  8186. this.currentSource_ = null;
  8187. }
  8188. // always clean up auto-text tracks
  8189. this.cleanupAutoTextTracks();
  8190. if (this.sourceHandler_) {
  8191. if (this.sourceHandler_.dispose) {
  8192. this.sourceHandler_.dispose();
  8193. }
  8194. this.sourceHandler_ = null;
  8195. }
  8196. };
  8197. };
  8198. // The base Tech class needs to be registered as a Component. It is the only
  8199. // Tech that can be registered as a Component.
  8200. Component.registerComponent('Tech', Tech);
  8201. Tech.registerTech('Tech', Tech);
  8202. /**
  8203. * A list of techs that should be added to techOrder on Players
  8204. *
  8205. * @private
  8206. */
  8207. Tech.defaultTechOrder_ = [];
  8208. var middlewares = {};
  8209. var middlewareInstances = {};
  8210. var TERMINATOR = {};
  8211. function use(type, middleware) {
  8212. middlewares[type] = middlewares[type] || [];
  8213. middlewares[type].push(middleware);
  8214. }
  8215. function setSource(player, src, next) {
  8216. player.setTimeout(function () {
  8217. return setSourceHelper(src, middlewares[src.type], next, player);
  8218. }, 1);
  8219. }
  8220. function setTech(middleware, tech) {
  8221. middleware.forEach(function (mw) {
  8222. return mw.setTech && mw.setTech(tech);
  8223. });
  8224. }
  8225. /**
  8226. * Calls a getter on the tech first, through each middleware
  8227. * from right to left to the player.
  8228. */
  8229. function get$1(middleware, tech, method) {
  8230. return middleware.reduceRight(middlewareIterator(method), tech[method]());
  8231. }
  8232. /**
  8233. * Takes the argument given to the player and calls the setter method on each
  8234. * middlware from left to right to the tech.
  8235. */
  8236. function set$1(middleware, tech, method, arg) {
  8237. return tech[method](middleware.reduce(middlewareIterator(method), arg));
  8238. }
  8239. /**
  8240. * Takes the argument given to the player and calls the `call` version of the method
  8241. * on each middleware from left to right.
  8242. * Then, call the passed in method on the tech and return the result unchanged
  8243. * back to the player, through middleware, this time from right to left.
  8244. */
  8245. function mediate(middleware, tech, method) {
  8246. var arg = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
  8247. var callMethod = 'call' + toTitleCase(method);
  8248. var middlewareValue = middleware.reduce(middlewareIterator(callMethod), arg);
  8249. var terminated = middlewareValue === TERMINATOR;
  8250. var returnValue = terminated ? null : tech[method](middlewareValue);
  8251. executeRight(middleware, method, returnValue, terminated);
  8252. return returnValue;
  8253. }
  8254. var allowedGetters = {
  8255. buffered: 1,
  8256. currentTime: 1,
  8257. duration: 1,
  8258. seekable: 1,
  8259. played: 1,
  8260. paused: 1
  8261. };
  8262. var allowedSetters = {
  8263. setCurrentTime: 1
  8264. };
  8265. var allowedMediators = {
  8266. play: 1,
  8267. pause: 1
  8268. };
  8269. function middlewareIterator(method) {
  8270. return function (value, mw) {
  8271. // if the previous middleware terminated, pass along the termination
  8272. if (value === TERMINATOR) {
  8273. return TERMINATOR;
  8274. }
  8275. if (mw[method]) {
  8276. return mw[method](value);
  8277. }
  8278. return value;
  8279. };
  8280. }
  8281. function executeRight(mws, method, value, terminated) {
  8282. for (var i = mws.length - 1; i >= 0; i--) {
  8283. var mw = mws[i];
  8284. if (mw[method]) {
  8285. mw[method](terminated, value);
  8286. }
  8287. }
  8288. }
  8289. function clearCacheForPlayer(player) {
  8290. middlewareInstances[player.id()] = null;
  8291. }
  8292. /**
  8293. * {
  8294. * [playerId]: [[mwFactory, mwInstance], ...]
  8295. * }
  8296. */
  8297. function getOrCreateFactory(player, mwFactory) {
  8298. var mws = middlewareInstances[player.id()];
  8299. var mw = null;
  8300. if (mws === undefined || mws === null) {
  8301. mw = mwFactory(player);
  8302. middlewareInstances[player.id()] = [[mwFactory, mw]];
  8303. return mw;
  8304. }
  8305. for (var i = 0; i < mws.length; i++) {
  8306. var _mws$i = mws[i],
  8307. mwf = _mws$i[0],
  8308. mwi = _mws$i[1];
  8309. if (mwf !== mwFactory) {
  8310. continue;
  8311. }
  8312. mw = mwi;
  8313. }
  8314. if (mw === null) {
  8315. mw = mwFactory(player);
  8316. mws.push([mwFactory, mw]);
  8317. }
  8318. return mw;
  8319. }
  8320. function setSourceHelper() {
  8321. var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  8322. var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  8323. var next = arguments[2];
  8324. var player = arguments[3];
  8325. var acc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
  8326. var lastRun = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
  8327. var mwFactory = middleware[0],
  8328. mwrest = middleware.slice(1);
  8329. // if mwFactory is a string, then we're at a fork in the road
  8330. if (typeof mwFactory === 'string') {
  8331. setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun);
  8332. // if we have an mwFactory, call it with the player to get the mw,
  8333. // then call the mw's setSource method
  8334. } else if (mwFactory) {
  8335. var mw = getOrCreateFactory(player, mwFactory);
  8336. // if setSource isn't present, implicitly select this middleware
  8337. if (!mw.setSource) {
  8338. acc.push(mw);
  8339. return setSourceHelper(src, mwrest, next, player, acc, lastRun);
  8340. }
  8341. mw.setSource(assign({}, src), function (err, _src) {
  8342. // something happened, try the next middleware on the current level
  8343. // make sure to use the old src
  8344. if (err) {
  8345. return setSourceHelper(src, mwrest, next, player, acc, lastRun);
  8346. }
  8347. // we've succeeded, now we need to go deeper
  8348. acc.push(mw);
  8349. // if it's the same type, continue down the current chain
  8350. // otherwise, we want to go down the new chain
  8351. setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
  8352. });
  8353. } else if (mwrest.length) {
  8354. setSourceHelper(src, mwrest, next, player, acc, lastRun);
  8355. } else if (lastRun) {
  8356. next(src, acc);
  8357. } else {
  8358. setSourceHelper(src, middlewares['*'], next, player, acc, true);
  8359. }
  8360. }
  8361. /**
  8362. * Mimetypes
  8363. *
  8364. * @see http://hul.harvard.edu/ois/////systems/wax/wax-public-help/mimetypes.htm
  8365. * @typedef Mimetypes~Kind
  8366. * @enum
  8367. */
  8368. var MimetypesKind = {
  8369. opus: 'video/ogg',
  8370. ogv: 'video/ogg',
  8371. mp4: 'video/mp4',
  8372. mov: 'video/mp4',
  8373. m4v: 'video/mp4',
  8374. mkv: 'video/x-matroska',
  8375. mp3: 'audio/mpeg',
  8376. aac: 'audio/aac',
  8377. oga: 'audio/ogg',
  8378. m3u8: 'application/x-mpegURL'
  8379. };
  8380. /**
  8381. * Get the mimetype of a given src url if possible
  8382. *
  8383. * @param {string} src
  8384. * The url to the src
  8385. *
  8386. * @return {string}
  8387. * return the mimetype if it was known or empty string otherwise
  8388. */
  8389. var getMimetype = function getMimetype() {
  8390. var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  8391. var ext = getFileExtension(src);
  8392. var mimetype = MimetypesKind[ext.toLowerCase()];
  8393. return mimetype || '';
  8394. };
  8395. /**
  8396. * Find the mime type of a given source string if possible. Uses the player
  8397. * source cache.
  8398. *
  8399. * @param {Player} player
  8400. * The player object
  8401. *
  8402. * @param {string} src
  8403. * The source string
  8404. *
  8405. * @return {string}
  8406. * The type that was found
  8407. */
  8408. var findMimetype = function findMimetype(player, src) {
  8409. if (!src) {
  8410. return '';
  8411. }
  8412. // 1. check for the type in the `source` cache
  8413. if (player.cache_.source.src === src && player.cache_.source.type) {
  8414. return player.cache_.source.type;
  8415. }
  8416. // 2. see if we have this source in our `currentSources` cache
  8417. var matchingSources = player.cache_.sources.filter(function (s) {
  8418. return s.src === src;
  8419. });
  8420. if (matchingSources.length) {
  8421. return matchingSources[0].type;
  8422. }
  8423. // 3. look for the src url in source elements and use the type there
  8424. var sources = player.$$('source');
  8425. for (var i = 0; i < sources.length; i++) {
  8426. var s = sources[i];
  8427. if (s.type && s.src && s.src === src) {
  8428. return s.type;
  8429. }
  8430. }
  8431. // 4. finally fallback to our list of mime types based on src url extension
  8432. return getMimetype(src);
  8433. };
  8434. /**
  8435. * @module filter-source
  8436. */
  8437. /**
  8438. * Filter out single bad source objects or multiple source objects in an
  8439. * array. Also flattens nested source object arrays into a 1 dimensional
  8440. * array of source objects.
  8441. *
  8442. * @param {Tech~SourceObject|Tech~SourceObject[]} src
  8443. * The src object to filter
  8444. *
  8445. * @return {Tech~SourceObject[]}
  8446. * An array of sourceobjects containing only valid sources
  8447. *
  8448. * @private
  8449. */
  8450. var filterSource = function filterSource(src) {
  8451. // traverse array
  8452. if (Array.isArray(src)) {
  8453. var newsrc = [];
  8454. src.forEach(function (srcobj) {
  8455. srcobj = filterSource(srcobj);
  8456. if (Array.isArray(srcobj)) {
  8457. newsrc = newsrc.concat(srcobj);
  8458. } else if (isObject(srcobj)) {
  8459. newsrc.push(srcobj);
  8460. }
  8461. });
  8462. src = newsrc;
  8463. } else if (typeof src === 'string' && src.trim()) {
  8464. // convert string into object
  8465. src = [fixSource({ src: src })];
  8466. } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
  8467. // src is already valid
  8468. src = [fixSource(src)];
  8469. } else {
  8470. // invalid source, turn it into an empty array
  8471. src = [];
  8472. }
  8473. return src;
  8474. };
  8475. /**
  8476. * Checks src mimetype, adding it when possible
  8477. *
  8478. * @param {Tech~SourceObject} src
  8479. * The src object to check
  8480. * @return {Tech~SourceObject}
  8481. * src Object with known type
  8482. */
  8483. function fixSource(src) {
  8484. var mimetype = getMimetype(src.src);
  8485. if (!src.type && mimetype) {
  8486. src.type = mimetype;
  8487. }
  8488. return src;
  8489. }
  8490. /**
  8491. * @file loader.js
  8492. */
  8493. /**
  8494. * The `MediaLoader` is the `Component` that decides which playback technology to load
  8495. * when a player is initialized.
  8496. *
  8497. * @extends Component
  8498. */
  8499. var MediaLoader = function (_Component) {
  8500. inherits(MediaLoader, _Component);
  8501. /**
  8502. * Create an instance of this class.
  8503. *
  8504. * @param {Player} player
  8505. * The `Player` that this class should attach to.
  8506. *
  8507. * @param {Object} [options]
  8508. * The key/value stroe of player options.
  8509. *
  8510. * @param {Component~ReadyCallback} [ready]
  8511. * The function that is run when this component is ready.
  8512. */
  8513. function MediaLoader(player, options, ready) {
  8514. classCallCheck(this, MediaLoader);
  8515. // MediaLoader has no element
  8516. var options_ = mergeOptions({ createEl: false }, options);
  8517. // If there are no sources when the player is initialized,
  8518. // load the first supported playback technology.
  8519. var _this = possibleConstructorReturn(this, _Component.call(this, player, options_, ready));
  8520. if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
  8521. for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
  8522. var techName = toTitleCase(j[i]);
  8523. var tech = Tech.getTech(techName);
  8524. // Support old behavior of techs being registered as components.
  8525. // Remove once that deprecated behavior is removed.
  8526. if (!techName) {
  8527. tech = Component.getComponent(techName);
  8528. }
  8529. // Check if the browser supports this technology
  8530. if (tech && tech.isSupported()) {
  8531. player.loadTech_(techName);
  8532. break;
  8533. }
  8534. }
  8535. } else {
  8536. // Loop through playback technologies (HTML5, Flash) and check for support.
  8537. // Then load the best source.
  8538. // A few assumptions here:
  8539. // All playback technologies respect preload false.
  8540. player.src(options.playerOptions.sources);
  8541. }
  8542. return _this;
  8543. }
  8544. return MediaLoader;
  8545. }(Component);
  8546. Component.registerComponent('MediaLoader', MediaLoader);
  8547. /**
  8548. * @file button.js
  8549. */
  8550. /**
  8551. * Clickable Component which is clickable or keyboard actionable,
  8552. * but is not a native HTML button.
  8553. *
  8554. * @extends Component
  8555. */
  8556. var ClickableComponent = function (_Component) {
  8557. inherits(ClickableComponent, _Component);
  8558. /**
  8559. * Creates an instance of this class.
  8560. *
  8561. * @param {Player} player
  8562. * The `Player` that this class should be attached to.
  8563. *
  8564. * @param {Object} [options]
  8565. * The key/value store of player options.
  8566. */
  8567. function ClickableComponent(player, options) {
  8568. classCallCheck(this, ClickableComponent);
  8569. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  8570. _this.emitTapEvents();
  8571. _this.enable();
  8572. return _this;
  8573. }
  8574. /**
  8575. * Create the `Component`s DOM element.
  8576. *
  8577. * @param {string} [tag=div]
  8578. * The element's node type.
  8579. *
  8580. * @param {Object} [props={}]
  8581. * An object of properties that should be set on the element.
  8582. *
  8583. * @param {Object} [attributes={}]
  8584. * An object of attributes that should be set on the element.
  8585. *
  8586. * @return {Element}
  8587. * The element that gets created.
  8588. */
  8589. ClickableComponent.prototype.createEl = function createEl$$1() {
  8590. var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
  8591. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  8592. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  8593. props = assign({
  8594. innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
  8595. className: this.buildCSSClass(),
  8596. tabIndex: 0
  8597. }, props);
  8598. if (tag === 'button') {
  8599. log.error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
  8600. }
  8601. // Add ARIA attributes for clickable element which is not a native HTML button
  8602. attributes = assign({
  8603. role: 'button'
  8604. }, attributes);
  8605. this.tabIndex_ = props.tabIndex;
  8606. var el = _Component.prototype.createEl.call(this, tag, props, attributes);
  8607. this.createControlTextEl(el);
  8608. return el;
  8609. };
  8610. ClickableComponent.prototype.dispose = function dispose() {
  8611. // remove controlTextEl_ on dipose
  8612. this.controlTextEl_ = null;
  8613. _Component.prototype.dispose.call(this);
  8614. };
  8615. /**
  8616. * Create a control text element on this `Component`
  8617. *
  8618. * @param {Element} [el]
  8619. * Parent element for the control text.
  8620. *
  8621. * @return {Element}
  8622. * The control text element that gets created.
  8623. */
  8624. ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
  8625. this.controlTextEl_ = createEl('span', {
  8626. className: 'vjs-control-text'
  8627. }, {
  8628. // let the screen reader user know that the text of the element may change
  8629. 'aria-live': 'polite'
  8630. });
  8631. if (el) {
  8632. el.appendChild(this.controlTextEl_);
  8633. }
  8634. this.controlText(this.controlText_, el);
  8635. return this.controlTextEl_;
  8636. };
  8637. /**
  8638. * Get or set the localize text to use for the controls on the `Component`.
  8639. *
  8640. * @param {string} [text]
  8641. * Control text for element.
  8642. *
  8643. * @param {Element} [el=this.el()]
  8644. * Element to set the title on.
  8645. *
  8646. * @return {string}
  8647. * - The control text when getting
  8648. */
  8649. ClickableComponent.prototype.controlText = function controlText(text) {
  8650. var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
  8651. if (text === undefined) {
  8652. return this.controlText_ || 'Need Text';
  8653. }
  8654. var localizedText = this.localize(text);
  8655. this.controlText_ = text;
  8656. textContent(this.controlTextEl_, localizedText);
  8657. if (!this.nonIconControl) {
  8658. // Set title attribute if only an icon is shown
  8659. el.setAttribute('title', localizedText);
  8660. }
  8661. };
  8662. /**
  8663. * Builds the default DOM `className`.
  8664. *
  8665. * @return {string}
  8666. * The DOM `className` for this object.
  8667. */
  8668. ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
  8669. return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
  8670. };
  8671. /**
  8672. * Enable this `Component`s element.
  8673. */
  8674. ClickableComponent.prototype.enable = function enable() {
  8675. if (!this.enabled_) {
  8676. this.enabled_ = true;
  8677. this.removeClass('vjs-disabled');
  8678. this.el_.setAttribute('aria-disabled', 'false');
  8679. if (typeof this.tabIndex_ !== 'undefined') {
  8680. this.el_.setAttribute('tabIndex', this.tabIndex_);
  8681. }
  8682. this.on(['tap', 'click'], this.handleClick);
  8683. this.on('focus', this.handleFocus);
  8684. this.on('blur', this.handleBlur);
  8685. }
  8686. };
  8687. /**
  8688. * Disable this `Component`s element.
  8689. */
  8690. ClickableComponent.prototype.disable = function disable() {
  8691. this.enabled_ = false;
  8692. this.addClass('vjs-disabled');
  8693. this.el_.setAttribute('aria-disabled', 'true');
  8694. if (typeof this.tabIndex_ !== 'undefined') {
  8695. this.el_.removeAttribute('tabIndex');
  8696. }
  8697. this.off(['tap', 'click'], this.handleClick);
  8698. this.off('focus', this.handleFocus);
  8699. this.off('blur', this.handleBlur);
  8700. };
  8701. /**
  8702. * This gets called when a `ClickableComponent` gets:
  8703. * - Clicked (via the `click` event, listening starts in the constructor)
  8704. * - Tapped (via the `tap` event, listening starts in the constructor)
  8705. * - The following things happen in order:
  8706. * 1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
  8707. * `ClickableComponent`.
  8708. * 2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
  8709. * {@link ClickableComponent#handleKeyPress}.
  8710. * 3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
  8711. * the space or enter key.
  8712. * 4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
  8713. * event as a parameter.
  8714. *
  8715. * @param {EventTarget~Event} event
  8716. * The `keydown`, `tap`, or `click` event that caused this function to be
  8717. * called.
  8718. *
  8719. * @listens tap
  8720. * @listens click
  8721. * @abstract
  8722. */
  8723. ClickableComponent.prototype.handleClick = function handleClick(event) {};
  8724. /**
  8725. * This gets called when a `ClickableComponent` gains focus via a `focus` event.
  8726. * Turns on listening for `keydown` events. When they happen it
  8727. * calls `this.handleKeyPress`.
  8728. *
  8729. * @param {EventTarget~Event} event
  8730. * The `focus` event that caused this function to be called.
  8731. *
  8732. * @listens focus
  8733. */
  8734. ClickableComponent.prototype.handleFocus = function handleFocus(event) {
  8735. on(document_1, 'keydown', bind(this, this.handleKeyPress));
  8736. };
  8737. /**
  8738. * Called when this ClickableComponent has focus and a key gets pressed down. By
  8739. * default it will call `this.handleClick` when the key is space or enter.
  8740. *
  8741. * @param {EventTarget~Event} event
  8742. * The `keydown` event that caused this function to be called.
  8743. *
  8744. * @listens keydown
  8745. */
  8746. ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
  8747. // Support Space (32) or Enter (13) key operation to fire a click event
  8748. if (event.which === 32 || event.which === 13) {
  8749. event.preventDefault();
  8750. this.trigger('click');
  8751. } else if (_Component.prototype.handleKeyPress) {
  8752. // Pass keypress handling up for unsupported keys
  8753. _Component.prototype.handleKeyPress.call(this, event);
  8754. }
  8755. };
  8756. /**
  8757. * Called when a `ClickableComponent` loses focus. Turns off the listener for
  8758. * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
  8759. *
  8760. * @param {EventTarget~Event} event
  8761. * The `blur` event that caused this function to be called.
  8762. *
  8763. * @listens blur
  8764. */
  8765. ClickableComponent.prototype.handleBlur = function handleBlur(event) {
  8766. off(document_1, 'keydown', bind(this, this.handleKeyPress));
  8767. };
  8768. return ClickableComponent;
  8769. }(Component);
  8770. Component.registerComponent('ClickableComponent', ClickableComponent);
  8771. /**
  8772. * @file poster-image.js
  8773. */
  8774. /**
  8775. * A `ClickableComponent` that handles showing the poster image for the player.
  8776. *
  8777. * @extends ClickableComponent
  8778. */
  8779. var PosterImage = function (_ClickableComponent) {
  8780. inherits(PosterImage, _ClickableComponent);
  8781. /**
  8782. * Create an instance of this class.
  8783. *
  8784. * @param {Player} player
  8785. * The `Player` that this class should attach to.
  8786. *
  8787. * @param {Object} [options]
  8788. * The key/value store of player options.
  8789. */
  8790. function PosterImage(player, options) {
  8791. classCallCheck(this, PosterImage);
  8792. var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  8793. _this.update();
  8794. player.on('posterchange', bind(_this, _this.update));
  8795. return _this;
  8796. }
  8797. /**
  8798. * Clean up and dispose of the `PosterImage`.
  8799. */
  8800. PosterImage.prototype.dispose = function dispose() {
  8801. this.player().off('posterchange', this.update);
  8802. _ClickableComponent.prototype.dispose.call(this);
  8803. };
  8804. /**
  8805. * Create the `PosterImage`s DOM element.
  8806. *
  8807. * @return {Element}
  8808. * The element that gets created.
  8809. */
  8810. PosterImage.prototype.createEl = function createEl$$1() {
  8811. var el = createEl('div', {
  8812. className: 'vjs-poster',
  8813. // Don't want poster to be tabbable.
  8814. tabIndex: -1
  8815. });
  8816. // To ensure the poster image resizes while maintaining its original aspect
  8817. // ratio, use a div with `background-size` when available. For browsers that
  8818. // do not support `background-size` (e.g. IE8), fall back on using a regular
  8819. // img element.
  8820. if (!BACKGROUND_SIZE_SUPPORTED) {
  8821. this.fallbackImg_ = createEl('img');
  8822. el.appendChild(this.fallbackImg_);
  8823. }
  8824. return el;
  8825. };
  8826. /**
  8827. * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
  8828. *
  8829. * @listens Player#posterchange
  8830. *
  8831. * @param {EventTarget~Event} [event]
  8832. * The `Player#posterchange` event that triggered this function.
  8833. */
  8834. PosterImage.prototype.update = function update(event) {
  8835. var url = this.player().poster();
  8836. this.setSrc(url);
  8837. // If there's no poster source we should display:none on this component
  8838. // so it's not still clickable or right-clickable
  8839. if (url) {
  8840. this.show();
  8841. } else {
  8842. this.hide();
  8843. }
  8844. };
  8845. /**
  8846. * Set the source of the `PosterImage` depending on the display method.
  8847. *
  8848. * @param {string} url
  8849. * The URL to the source for the `PosterImage`.
  8850. */
  8851. PosterImage.prototype.setSrc = function setSrc(url) {
  8852. if (this.fallbackImg_) {
  8853. this.fallbackImg_.src = url;
  8854. } else {
  8855. var backgroundImage = '';
  8856. // Any falsey values should stay as an empty string, otherwise
  8857. // this will throw an extra error
  8858. if (url) {
  8859. backgroundImage = 'url("' + url + '")';
  8860. }
  8861. this.el_.style.backgroundImage = backgroundImage;
  8862. }
  8863. };
  8864. /**
  8865. * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
  8866. * {@link ClickableComponent#handleClick} for instances where this will be triggered.
  8867. *
  8868. * @listens tap
  8869. * @listens click
  8870. * @listens keydown
  8871. *
  8872. * @param {EventTarget~Event} event
  8873. + The `click`, `tap` or `keydown` event that caused this function to be called.
  8874. */
  8875. PosterImage.prototype.handleClick = function handleClick(event) {
  8876. // We don't want a click to trigger playback when controls are disabled
  8877. if (!this.player_.controls()) {
  8878. return;
  8879. }
  8880. if (this.player_.paused()) {
  8881. silencePromise(this.player_.play());
  8882. } else {
  8883. this.player_.pause();
  8884. }
  8885. };
  8886. return PosterImage;
  8887. }(ClickableComponent);
  8888. Component.registerComponent('PosterImage', PosterImage);
  8889. /**
  8890. * @file text-track-display.js
  8891. */
  8892. var darkGray = '#222';
  8893. var lightGray = '#ccc';
  8894. var fontMap = {
  8895. monospace: 'monospace',
  8896. sansSerif: 'sans-serif',
  8897. serif: 'serif',
  8898. monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
  8899. monospaceSerif: '"Courier New", monospace',
  8900. proportionalSansSerif: 'sans-serif',
  8901. proportionalSerif: 'serif',
  8902. casual: '"Comic Sans MS", Impact, fantasy',
  8903. script: '"Monotype Corsiva", cursive',
  8904. smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
  8905. };
  8906. /**
  8907. * Construct an rgba color from a given hex color code.
  8908. *
  8909. * @param {number} color
  8910. * Hex number for color, like #f0e or #f604e2.
  8911. *
  8912. * @param {number} opacity
  8913. * Value for opacity, 0.0 - 1.0.
  8914. *
  8915. * @return {string}
  8916. * The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
  8917. */
  8918. function constructColor(color, opacity) {
  8919. var hex = void 0;
  8920. if (color.length === 4) {
  8921. // color looks like "#f0e"
  8922. hex = color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
  8923. } else if (color.length === 7) {
  8924. // color looks like "#f604e2"
  8925. hex = color.slice(1);
  8926. } else {
  8927. throw new Error('Invalid color code provided, ' + color + '; must be formatted as e.g. #f0e or #f604e2.');
  8928. }
  8929. return 'rgba(' + parseInt(hex.slice(0, 2), 16) + ',' + parseInt(hex.slice(2, 4), 16) + ',' + parseInt(hex.slice(4, 6), 16) + ',' + opacity + ')';
  8930. }
  8931. /**
  8932. * Try to update the style of a DOM element. Some style changes will throw an error,
  8933. * particularly in IE8. Those should be noops.
  8934. *
  8935. * @param {Element} el
  8936. * The DOM element to be styled.
  8937. *
  8938. * @param {string} style
  8939. * The CSS property on the element that should be styled.
  8940. *
  8941. * @param {string} rule
  8942. * The style rule that should be applied to the property.
  8943. *
  8944. * @private
  8945. */
  8946. function tryUpdateStyle(el, style, rule) {
  8947. try {
  8948. el.style[style] = rule;
  8949. } catch (e) {
  8950. // Satisfies linter.
  8951. return;
  8952. }
  8953. }
  8954. /**
  8955. * The component for displaying text track cues.
  8956. *
  8957. * @extends Component
  8958. */
  8959. var TextTrackDisplay = function (_Component) {
  8960. inherits(TextTrackDisplay, _Component);
  8961. /**
  8962. * Creates an instance of this class.
  8963. *
  8964. * @param {Player} player
  8965. * The `Player` that this class should be attached to.
  8966. *
  8967. * @param {Object} [options]
  8968. * The key/value store of player options.
  8969. *
  8970. * @param {Component~ReadyCallback} [ready]
  8971. * The function to call when `TextTrackDisplay` is ready.
  8972. */
  8973. function TextTrackDisplay(player, options, ready) {
  8974. classCallCheck(this, TextTrackDisplay);
  8975. var _this = possibleConstructorReturn(this, _Component.call(this, player, options, ready));
  8976. var updateDisplayHandler = bind(_this, _this.updateDisplay);
  8977. player.on('loadstart', bind(_this, _this.toggleDisplay));
  8978. player.on('texttrackchange', updateDisplayHandler);
  8979. player.on('loadstart', bind(_this, _this.preselectTrack));
  8980. // This used to be called during player init, but was causing an error
  8981. // if a track should show by default and the display hadn't loaded yet.
  8982. // Should probably be moved to an external track loader when we support
  8983. // tracks that don't need a display.
  8984. player.ready(bind(_this, function () {
  8985. if (player.tech_ && player.tech_.featuresNativeTextTracks) {
  8986. this.hide();
  8987. return;
  8988. }
  8989. player.on('fullscreenchange', updateDisplayHandler);
  8990. player.on('playerresize', updateDisplayHandler);
  8991. if (window_1.addEventListener) {
  8992. window_1.addEventListener('orientationchange', updateDisplayHandler);
  8993. }
  8994. player.on('dispose', function () {
  8995. return window_1.removeEventListener('orientationchange', updateDisplayHandler);
  8996. });
  8997. var tracks = this.options_.playerOptions.tracks || [];
  8998. for (var i = 0; i < tracks.length; i++) {
  8999. this.player_.addRemoteTextTrack(tracks[i], true);
  9000. }
  9001. this.preselectTrack();
  9002. }));
  9003. return _this;
  9004. }
  9005. /**
  9006. * Preselect a track following this precedence:
  9007. * - matches the previously selected {@link TextTrack}'s language and kind
  9008. * - matches the previously selected {@link TextTrack}'s language only
  9009. * - is the first default captions track
  9010. * - is the first default descriptions track
  9011. *
  9012. * @listens Player#loadstart
  9013. */
  9014. TextTrackDisplay.prototype.preselectTrack = function preselectTrack() {
  9015. var modes = { captions: 1, subtitles: 1 };
  9016. var trackList = this.player_.textTracks();
  9017. var userPref = this.player_.cache_.selectedLanguage;
  9018. var firstDesc = void 0;
  9019. var firstCaptions = void 0;
  9020. var preferredTrack = void 0;
  9021. for (var i = 0; i < trackList.length; i++) {
  9022. var track = trackList[i];
  9023. if (userPref && userPref.enabled && userPref.language === track.language) {
  9024. // Always choose the track that matches both language and kind
  9025. if (track.kind === userPref.kind) {
  9026. preferredTrack = track;
  9027. // or choose the first track that matches language
  9028. } else if (!preferredTrack) {
  9029. preferredTrack = track;
  9030. }
  9031. // clear everything if offTextTrackMenuItem was clicked
  9032. } else if (userPref && !userPref.enabled) {
  9033. preferredTrack = null;
  9034. firstDesc = null;
  9035. firstCaptions = null;
  9036. } else if (track['default']) {
  9037. if (track.kind === 'descriptions' && !firstDesc) {
  9038. firstDesc = track;
  9039. } else if (track.kind in modes && !firstCaptions) {
  9040. firstCaptions = track;
  9041. }
  9042. }
  9043. }
  9044. // The preferredTrack matches the user preference and takes
  9045. // precendence over all the other tracks.
  9046. // So, display the preferredTrack before the first default track
  9047. // and the subtitles/captions track before the descriptions track
  9048. if (preferredTrack) {
  9049. preferredTrack.mode = 'showing';
  9050. } else if (firstCaptions) {
  9051. firstCaptions.mode = 'showing';
  9052. } else if (firstDesc) {
  9053. firstDesc.mode = 'showing';
  9054. }
  9055. };
  9056. /**
  9057. * Turn display of {@link TextTrack}'s from the current state into the other state.
  9058. * There are only two states:
  9059. * - 'shown'
  9060. * - 'hidden'
  9061. *
  9062. * @listens Player#loadstart
  9063. */
  9064. TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
  9065. if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
  9066. this.hide();
  9067. } else {
  9068. this.show();
  9069. }
  9070. };
  9071. /**
  9072. * Create the {@link Component}'s DOM element.
  9073. *
  9074. * @return {Element}
  9075. * The element that was created.
  9076. */
  9077. TextTrackDisplay.prototype.createEl = function createEl() {
  9078. return _Component.prototype.createEl.call(this, 'div', {
  9079. className: 'vjs-text-track-display'
  9080. }, {
  9081. 'aria-live': 'off',
  9082. 'aria-atomic': 'true'
  9083. });
  9084. };
  9085. /**
  9086. * Clear all displayed {@link TextTrack}s.
  9087. */
  9088. TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
  9089. if (typeof window_1.WebVTT === 'function') {
  9090. window_1.WebVTT.processCues(window_1, [], this.el_);
  9091. }
  9092. };
  9093. /**
  9094. * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
  9095. * a {@link Player#fullscreenchange} is fired.
  9096. *
  9097. * @listens Player#texttrackchange
  9098. * @listens Player#fullscreenchange
  9099. */
  9100. TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
  9101. var tracks = this.player_.textTracks();
  9102. this.clearDisplay();
  9103. // Track display prioritization model: if multiple tracks are 'showing',
  9104. // display the first 'subtitles' or 'captions' track which is 'showing',
  9105. // otherwise display the first 'descriptions' track which is 'showing'
  9106. var descriptionsTrack = null;
  9107. var captionsSubtitlesTrack = null;
  9108. var i = tracks.length;
  9109. while (i--) {
  9110. var track = tracks[i];
  9111. if (track.mode === 'showing') {
  9112. if (track.kind === 'descriptions') {
  9113. descriptionsTrack = track;
  9114. } else {
  9115. captionsSubtitlesTrack = track;
  9116. }
  9117. }
  9118. }
  9119. if (captionsSubtitlesTrack) {
  9120. if (this.getAttribute('aria-live') !== 'off') {
  9121. this.setAttribute('aria-live', 'off');
  9122. }
  9123. this.updateForTrack(captionsSubtitlesTrack);
  9124. } else if (descriptionsTrack) {
  9125. if (this.getAttribute('aria-live') !== 'assertive') {
  9126. this.setAttribute('aria-live', 'assertive');
  9127. }
  9128. this.updateForTrack(descriptionsTrack);
  9129. }
  9130. };
  9131. /**
  9132. * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
  9133. *
  9134. * @param {TextTrack} track
  9135. * Text track object to be added to the list.
  9136. */
  9137. TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
  9138. if (typeof window_1.WebVTT !== 'function' || !track.activeCues) {
  9139. return;
  9140. }
  9141. var cues = [];
  9142. for (var _i = 0; _i < track.activeCues.length; _i++) {
  9143. cues.push(track.activeCues[_i]);
  9144. }
  9145. window_1.WebVTT.processCues(window_1, cues, this.el_);
  9146. if (!this.player_.textTrackSettings) {
  9147. return;
  9148. }
  9149. var overrides = this.player_.textTrackSettings.getValues();
  9150. var i = cues.length;
  9151. while (i--) {
  9152. var cue = cues[i];
  9153. if (!cue) {
  9154. continue;
  9155. }
  9156. var cueDiv = cue.displayState;
  9157. if (overrides.color) {
  9158. cueDiv.firstChild.style.color = overrides.color;
  9159. }
  9160. if (overrides.textOpacity) {
  9161. tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
  9162. }
  9163. if (overrides.backgroundColor) {
  9164. cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
  9165. }
  9166. if (overrides.backgroundOpacity) {
  9167. tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
  9168. }
  9169. if (overrides.windowColor) {
  9170. if (overrides.windowOpacity) {
  9171. tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
  9172. } else {
  9173. cueDiv.style.backgroundColor = overrides.windowColor;
  9174. }
  9175. }
  9176. if (overrides.edgeStyle) {
  9177. if (overrides.edgeStyle === 'dropshadow') {
  9178. cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
  9179. } else if (overrides.edgeStyle === 'raised') {
  9180. cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
  9181. } else if (overrides.edgeStyle === 'depressed') {
  9182. cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
  9183. } else if (overrides.edgeStyle === 'uniform') {
  9184. cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
  9185. }
  9186. }
  9187. if (overrides.fontPercent && overrides.fontPercent !== 1) {
  9188. var fontSize = window_1.parseFloat(cueDiv.style.fontSize);
  9189. cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
  9190. cueDiv.style.height = 'auto';
  9191. cueDiv.style.top = 'auto';
  9192. cueDiv.style.bottom = '2px';
  9193. }
  9194. if (overrides.fontFamily && overrides.fontFamily !== 'default') {
  9195. if (overrides.fontFamily === 'small-caps') {
  9196. cueDiv.firstChild.style.fontVariant = 'small-caps';
  9197. } else {
  9198. cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
  9199. }
  9200. }
  9201. }
  9202. };
  9203. return TextTrackDisplay;
  9204. }(Component);
  9205. Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
  9206. /**
  9207. * @file loading-spinner.js
  9208. */
  9209. /**
  9210. * A loading spinner for use during waiting/loading events.
  9211. *
  9212. * @extends Component
  9213. */
  9214. var LoadingSpinner = function (_Component) {
  9215. inherits(LoadingSpinner, _Component);
  9216. function LoadingSpinner() {
  9217. classCallCheck(this, LoadingSpinner);
  9218. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  9219. }
  9220. /**
  9221. * Create the `LoadingSpinner`s DOM element.
  9222. *
  9223. * @return {Element}
  9224. * The dom element that gets created.
  9225. */
  9226. LoadingSpinner.prototype.createEl = function createEl$$1() {
  9227. var isAudio = this.player_.isAudio();
  9228. var playerType = this.localize(isAudio ? 'Audio Player' : 'Video Player');
  9229. var controlText = createEl('span', {
  9230. className: 'vjs-control-text',
  9231. innerHTML: this.localize('{1} is loading.', [playerType])
  9232. });
  9233. var el = _Component.prototype.createEl.call(this, 'div', {
  9234. className: 'vjs-loading-spinner',
  9235. dir: 'ltr'
  9236. });
  9237. el.appendChild(controlText);
  9238. return el;
  9239. };
  9240. return LoadingSpinner;
  9241. }(Component);
  9242. Component.registerComponent('LoadingSpinner', LoadingSpinner);
  9243. /**
  9244. * @file button.js
  9245. */
  9246. /**
  9247. * Base class for all buttons.
  9248. *
  9249. * @extends ClickableComponent
  9250. */
  9251. var Button = function (_ClickableComponent) {
  9252. inherits(Button, _ClickableComponent);
  9253. function Button() {
  9254. classCallCheck(this, Button);
  9255. return possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments));
  9256. }
  9257. /**
  9258. * Create the `Button`s DOM element.
  9259. *
  9260. * @param {string} [tag="button"]
  9261. * The element's node type. This argument is IGNORED: no matter what
  9262. * is passed, it will always create a `button` element.
  9263. *
  9264. * @param {Object} [props={}]
  9265. * An object of properties that should be set on the element.
  9266. *
  9267. * @param {Object} [attributes={}]
  9268. * An object of attributes that should be set on the element.
  9269. *
  9270. * @return {Element}
  9271. * The element that gets created.
  9272. */
  9273. Button.prototype.createEl = function createEl(tag) {
  9274. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  9275. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  9276. tag = 'button';
  9277. props = assign({
  9278. innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
  9279. className: this.buildCSSClass()
  9280. }, props);
  9281. // Add attributes for button element
  9282. attributes = assign({
  9283. // Necessary since the default button type is "submit"
  9284. type: 'button'
  9285. }, attributes);
  9286. var el = Component.prototype.createEl.call(this, tag, props, attributes);
  9287. this.createControlTextEl(el);
  9288. return el;
  9289. };
  9290. /**
  9291. * Add a child `Component` inside of this `Button`.
  9292. *
  9293. * @param {string|Component} child
  9294. * The name or instance of a child to add.
  9295. *
  9296. * @param {Object} [options={}]
  9297. * The key/value store of options that will get passed to children of
  9298. * the child.
  9299. *
  9300. * @return {Component}
  9301. * The `Component` that gets added as a child. When using a string the
  9302. * `Component` will get created by this process.
  9303. *
  9304. * @deprecated since version 5
  9305. */
  9306. Button.prototype.addChild = function addChild(child) {
  9307. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  9308. var className = this.constructor.name;
  9309. log.warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
  9310. // Avoid the error message generated by ClickableComponent's addChild method
  9311. return Component.prototype.addChild.call(this, child, options);
  9312. };
  9313. /**
  9314. * Enable the `Button` element so that it can be activated or clicked. Use this with
  9315. * {@link Button#disable}.
  9316. */
  9317. Button.prototype.enable = function enable() {
  9318. _ClickableComponent.prototype.enable.call(this);
  9319. this.el_.removeAttribute('disabled');
  9320. };
  9321. /**
  9322. * Disable the `Button` element so that it cannot be activated or clicked. Use this with
  9323. * {@link Button#enable}.
  9324. */
  9325. Button.prototype.disable = function disable() {
  9326. _ClickableComponent.prototype.disable.call(this);
  9327. this.el_.setAttribute('disabled', 'disabled');
  9328. };
  9329. /**
  9330. * This gets called when a `Button` has focus and `keydown` is triggered via a key
  9331. * press.
  9332. *
  9333. * @param {EventTarget~Event} event
  9334. * The event that caused this function to get called.
  9335. *
  9336. * @listens keydown
  9337. */
  9338. Button.prototype.handleKeyPress = function handleKeyPress(event) {
  9339. // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
  9340. if (event.which === 32 || event.which === 13) {
  9341. return;
  9342. }
  9343. // Pass keypress handling up for unsupported keys
  9344. _ClickableComponent.prototype.handleKeyPress.call(this, event);
  9345. };
  9346. return Button;
  9347. }(ClickableComponent);
  9348. Component.registerComponent('Button', Button);
  9349. /**
  9350. * @file big-play-button.js
  9351. */
  9352. /**
  9353. * The initial play button that shows before the video has played. The hiding of the
  9354. * `BigPlayButton` get done via CSS and `Player` states.
  9355. *
  9356. * @extends Button
  9357. */
  9358. var BigPlayButton = function (_Button) {
  9359. inherits(BigPlayButton, _Button);
  9360. function BigPlayButton(player, options) {
  9361. classCallCheck(this, BigPlayButton);
  9362. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  9363. _this.mouseused_ = false;
  9364. _this.on('mousedown', _this.handleMouseDown);
  9365. return _this;
  9366. }
  9367. /**
  9368. * Builds the default DOM `className`.
  9369. *
  9370. * @return {string}
  9371. * The DOM `className` for this object. Always returns 'vjs-big-play-button'.
  9372. */
  9373. BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
  9374. return 'vjs-big-play-button';
  9375. };
  9376. /**
  9377. * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
  9378. * for more detailed information on what a click can be.
  9379. *
  9380. * @param {EventTarget~Event} event
  9381. * The `keydown`, `tap`, or `click` event that caused this function to be
  9382. * called.
  9383. *
  9384. * @listens tap
  9385. * @listens click
  9386. */
  9387. BigPlayButton.prototype.handleClick = function handleClick(event) {
  9388. var playPromise = this.player_.play();
  9389. // exit early if clicked via the mouse
  9390. if (this.mouseused_ && event.clientX && event.clientY) {
  9391. silencePromise(playPromise);
  9392. return;
  9393. }
  9394. var cb = this.player_.getChild('controlBar');
  9395. var playToggle = cb && cb.getChild('playToggle');
  9396. if (!playToggle) {
  9397. this.player_.focus();
  9398. return;
  9399. }
  9400. var playFocus = function playFocus() {
  9401. return playToggle.focus();
  9402. };
  9403. if (isPromise(playPromise)) {
  9404. playPromise.then(playFocus, function () {});
  9405. } else {
  9406. this.setTimeout(playFocus, 1);
  9407. }
  9408. };
  9409. BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) {
  9410. this.mouseused_ = false;
  9411. _Button.prototype.handleKeyPress.call(this, event);
  9412. };
  9413. BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) {
  9414. this.mouseused_ = true;
  9415. };
  9416. return BigPlayButton;
  9417. }(Button);
  9418. /**
  9419. * The text that should display over the `BigPlayButton`s controls. Added to for localization.
  9420. *
  9421. * @type {string}
  9422. * @private
  9423. */
  9424. BigPlayButton.prototype.controlText_ = 'Play Video';
  9425. Component.registerComponent('BigPlayButton', BigPlayButton);
  9426. /**
  9427. * @file close-button.js
  9428. */
  9429. /**
  9430. * The `CloseButton` is a `{@link Button}` that fires a `close` event when
  9431. * it gets clicked.
  9432. *
  9433. * @extends Button
  9434. */
  9435. var CloseButton = function (_Button) {
  9436. inherits(CloseButton, _Button);
  9437. /**
  9438. * Creates an instance of the this class.
  9439. *
  9440. * @param {Player} player
  9441. * The `Player` that this class should be attached to.
  9442. *
  9443. * @param {Object} [options]
  9444. * The key/value store of player options.
  9445. */
  9446. function CloseButton(player, options) {
  9447. classCallCheck(this, CloseButton);
  9448. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  9449. _this.controlText(options && options.controlText || _this.localize('Close'));
  9450. return _this;
  9451. }
  9452. /**
  9453. * Builds the default DOM `className`.
  9454. *
  9455. * @return {string}
  9456. * The DOM `className` for this object.
  9457. */
  9458. CloseButton.prototype.buildCSSClass = function buildCSSClass() {
  9459. return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
  9460. };
  9461. /**
  9462. * This gets called when a `CloseButton` gets clicked. See
  9463. * {@link ClickableComponent#handleClick} for more information on when this will be
  9464. * triggered
  9465. *
  9466. * @param {EventTarget~Event} event
  9467. * The `keydown`, `tap`, or `click` event that caused this function to be
  9468. * called.
  9469. *
  9470. * @listens tap
  9471. * @listens click
  9472. * @fires CloseButton#close
  9473. */
  9474. CloseButton.prototype.handleClick = function handleClick(event) {
  9475. /**
  9476. * Triggered when the a `CloseButton` is clicked.
  9477. *
  9478. * @event CloseButton#close
  9479. * @type {EventTarget~Event}
  9480. *
  9481. * @property {boolean} [bubbles=false]
  9482. * set to false so that the close event does not
  9483. * bubble up to parents if there is no listener
  9484. */
  9485. this.trigger({ type: 'close', bubbles: false });
  9486. };
  9487. return CloseButton;
  9488. }(Button);
  9489. Component.registerComponent('CloseButton', CloseButton);
  9490. /**
  9491. * @file play-toggle.js
  9492. */
  9493. /**
  9494. * Button to toggle between play and pause.
  9495. *
  9496. * @extends Button
  9497. */
  9498. var PlayToggle = function (_Button) {
  9499. inherits(PlayToggle, _Button);
  9500. /**
  9501. * Creates an instance of this class.
  9502. *
  9503. * @param {Player} player
  9504. * The `Player` that this class should be attached to.
  9505. *
  9506. * @param {Object} [options]
  9507. * The key/value store of player options.
  9508. */
  9509. function PlayToggle(player, options) {
  9510. classCallCheck(this, PlayToggle);
  9511. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  9512. _this.on(player, 'play', _this.handlePlay);
  9513. _this.on(player, 'pause', _this.handlePause);
  9514. _this.on(player, 'ended', _this.handleEnded);
  9515. return _this;
  9516. }
  9517. /**
  9518. * Builds the default DOM `className`.
  9519. *
  9520. * @return {string}
  9521. * The DOM `className` for this object.
  9522. */
  9523. PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
  9524. return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this);
  9525. };
  9526. /**
  9527. * This gets called when an `PlayToggle` is "clicked". See
  9528. * {@link ClickableComponent} for more detailed information on what a click can be.
  9529. *
  9530. * @param {EventTarget~Event} [event]
  9531. * The `keydown`, `tap`, or `click` event that caused this function to be
  9532. * called.
  9533. *
  9534. * @listens tap
  9535. * @listens click
  9536. */
  9537. PlayToggle.prototype.handleClick = function handleClick(event) {
  9538. if (this.player_.paused()) {
  9539. this.player_.play();
  9540. } else {
  9541. this.player_.pause();
  9542. }
  9543. };
  9544. /**
  9545. * This gets called once after the video has ended and the user seeks so that
  9546. * we can change the replay button back to a play button.
  9547. *
  9548. * @param {EventTarget~Event} [event]
  9549. * The event that caused this function to run.
  9550. *
  9551. * @listens Player#seeked
  9552. */
  9553. PlayToggle.prototype.handleSeeked = function handleSeeked(event) {
  9554. this.removeClass('vjs-ended');
  9555. if (this.player_.paused()) {
  9556. this.handlePause(event);
  9557. } else {
  9558. this.handlePlay(event);
  9559. }
  9560. };
  9561. /**
  9562. * Add the vjs-playing class to the element so it can change appearance.
  9563. *
  9564. * @param {EventTarget~Event} [event]
  9565. * The event that caused this function to run.
  9566. *
  9567. * @listens Player#play
  9568. */
  9569. PlayToggle.prototype.handlePlay = function handlePlay(event) {
  9570. this.removeClass('vjs-ended');
  9571. this.removeClass('vjs-paused');
  9572. this.addClass('vjs-playing');
  9573. // change the button text to "Pause"
  9574. this.controlText('Pause');
  9575. };
  9576. /**
  9577. * Add the vjs-paused class to the element so it can change appearance.
  9578. *
  9579. * @param {EventTarget~Event} [event]
  9580. * The event that caused this function to run.
  9581. *
  9582. * @listens Player#pause
  9583. */
  9584. PlayToggle.prototype.handlePause = function handlePause(event) {
  9585. this.removeClass('vjs-playing');
  9586. this.addClass('vjs-paused');
  9587. // change the button text to "Play"
  9588. this.controlText('Play');
  9589. };
  9590. /**
  9591. * Add the vjs-ended class to the element so it can change appearance
  9592. *
  9593. * @param {EventTarget~Event} [event]
  9594. * The event that caused this function to run.
  9595. *
  9596. * @listens Player#ended
  9597. */
  9598. PlayToggle.prototype.handleEnded = function handleEnded(event) {
  9599. this.removeClass('vjs-playing');
  9600. this.addClass('vjs-ended');
  9601. // change the button text to "Replay"
  9602. this.controlText('Replay');
  9603. // on the next seek remove the replay button
  9604. this.one(this.player_, 'seeked', this.handleSeeked);
  9605. };
  9606. return PlayToggle;
  9607. }(Button);
  9608. /**
  9609. * The text that should display over the `PlayToggle`s controls. Added for localization.
  9610. *
  9611. * @type {string}
  9612. * @private
  9613. */
  9614. PlayToggle.prototype.controlText_ = 'Play';
  9615. Component.registerComponent('PlayToggle', PlayToggle);
  9616. /**
  9617. * @file format-time.js
  9618. * @module format-time
  9619. */
  9620. /**
  9621. * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds)
  9622. * will force a number of leading zeros to cover the length of the guide.
  9623. *
  9624. * @param {number} seconds
  9625. * Number of seconds to be turned into a string
  9626. *
  9627. * @param {number} guide
  9628. * Number (in seconds) to model the string after
  9629. *
  9630. * @return {string}
  9631. * Time formatted as H:MM:SS or M:SS
  9632. */
  9633. var defaultImplementation = function defaultImplementation(seconds, guide) {
  9634. seconds = seconds < 0 ? 0 : seconds;
  9635. var s = Math.floor(seconds % 60);
  9636. var m = Math.floor(seconds / 60 % 60);
  9637. var h = Math.floor(seconds / 3600);
  9638. var gm = Math.floor(guide / 60 % 60);
  9639. var gh = Math.floor(guide / 3600);
  9640. // handle invalid times
  9641. if (isNaN(seconds) || seconds === Infinity) {
  9642. // '-' is false for all relational operators (e.g. <, >=) so this setting
  9643. // will add the minimum number of fields specified by the guide
  9644. h = m = s = '-';
  9645. }
  9646. // Check if we need to show hours
  9647. h = h > 0 || gh > 0 ? h + ':' : '';
  9648. // If hours are showing, we may need to add a leading zero.
  9649. // Always show at least one digit of minutes.
  9650. m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
  9651. // Check if leading zero is need for seconds
  9652. s = s < 10 ? '0' + s : s;
  9653. return h + m + s;
  9654. };
  9655. var implementation = defaultImplementation;
  9656. /**
  9657. * Replaces the default formatTime implementation with a custom implementation.
  9658. *
  9659. * @param {Function} customImplementation
  9660. * A function which will be used in place of the default formatTime implementation.
  9661. * Will receive the current time in seconds and the guide (in seconds) as arguments.
  9662. */
  9663. function setFormatTime(customImplementation) {
  9664. implementation = customImplementation;
  9665. }
  9666. /**
  9667. * Resets formatTime to the default implementation.
  9668. */
  9669. function resetFormatTime() {
  9670. implementation = defaultImplementation;
  9671. }
  9672. var formatTime = function (seconds) {
  9673. var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds;
  9674. return implementation(seconds, guide);
  9675. };
  9676. /**
  9677. * @file time-display.js
  9678. */
  9679. /**
  9680. * Displays the time left in the video
  9681. *
  9682. * @extends Component
  9683. */
  9684. var TimeDisplay = function (_Component) {
  9685. inherits(TimeDisplay, _Component);
  9686. /**
  9687. * Creates an instance of this class.
  9688. *
  9689. * @param {Player} player
  9690. * The `Player` that this class should be attached to.
  9691. *
  9692. * @param {Object} [options]
  9693. * The key/value store of player options.
  9694. */
  9695. function TimeDisplay(player, options) {
  9696. classCallCheck(this, TimeDisplay);
  9697. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  9698. _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
  9699. _this.on(player, 'timeupdate', _this.throttledUpdateContent);
  9700. return _this;
  9701. }
  9702. /**
  9703. * Create the `Component`'s DOM element
  9704. *
  9705. * @return {Element}
  9706. * The element that was created.
  9707. */
  9708. TimeDisplay.prototype.createEl = function createEl$$1(plainName) {
  9709. var className = this.buildCSSClass();
  9710. var el = _Component.prototype.createEl.call(this, 'div', {
  9711. className: className + ' vjs-time-control vjs-control',
  9712. innerHTML: '<span class="vjs-control-text">' + this.localize(this.labelText_) + '\xA0</span>'
  9713. });
  9714. this.contentEl_ = createEl('span', {
  9715. className: className + '-display'
  9716. }, {
  9717. // tell screen readers not to automatically read the time as it changes
  9718. 'aria-live': 'off'
  9719. });
  9720. this.updateTextNode_();
  9721. el.appendChild(this.contentEl_);
  9722. return el;
  9723. };
  9724. TimeDisplay.prototype.dispose = function dispose() {
  9725. this.contentEl_ = null;
  9726. this.textNode_ = null;
  9727. _Component.prototype.dispose.call(this);
  9728. };
  9729. /**
  9730. * Updates the "remaining time" text node with new content using the
  9731. * contents of the `formattedTime_` property.
  9732. *
  9733. * @private
  9734. */
  9735. TimeDisplay.prototype.updateTextNode_ = function updateTextNode_() {
  9736. if (!this.contentEl_) {
  9737. return;
  9738. }
  9739. while (this.contentEl_.firstChild) {
  9740. this.contentEl_.removeChild(this.contentEl_.firstChild);
  9741. }
  9742. this.textNode_ = document_1.createTextNode(this.formattedTime_ || this.formatTime_(0));
  9743. this.contentEl_.appendChild(this.textNode_);
  9744. };
  9745. /**
  9746. * Generates a formatted time for this component to use in display.
  9747. *
  9748. * @param {number} time
  9749. * A numeric time, in seconds.
  9750. *
  9751. * @return {string}
  9752. * A formatted time
  9753. *
  9754. * @private
  9755. */
  9756. TimeDisplay.prototype.formatTime_ = function formatTime_(time) {
  9757. return formatTime(time);
  9758. };
  9759. /**
  9760. * Updates the time display text node if it has what was passed in changed
  9761. * the formatted time.
  9762. *
  9763. * @param {number} time
  9764. * The time to update to
  9765. *
  9766. * @private
  9767. */
  9768. TimeDisplay.prototype.updateFormattedTime_ = function updateFormattedTime_(time) {
  9769. var formattedTime = this.formatTime_(time);
  9770. if (formattedTime === this.formattedTime_) {
  9771. return;
  9772. }
  9773. this.formattedTime_ = formattedTime;
  9774. this.requestAnimationFrame(this.updateTextNode_);
  9775. };
  9776. /**
  9777. * To be filled out in the child class, should update the displayed time
  9778. * in accordance with the fact that the current time has changed.
  9779. *
  9780. * @param {EventTarget~Event} [event]
  9781. * The `timeupdate` event that caused this to run.
  9782. *
  9783. * @listens Player#timeupdate
  9784. */
  9785. TimeDisplay.prototype.updateContent = function updateContent(event) {};
  9786. return TimeDisplay;
  9787. }(Component);
  9788. /**
  9789. * The text that is added to the `TimeDisplay` for screen reader users.
  9790. *
  9791. * @type {string}
  9792. * @private
  9793. */
  9794. TimeDisplay.prototype.labelText_ = 'Time';
  9795. /**
  9796. * The text that should display over the `TimeDisplay`s controls. Added to for localization.
  9797. *
  9798. * @type {string}
  9799. * @private
  9800. *
  9801. * @deprecated in v7; controlText_ is not used in non-active display Components
  9802. */
  9803. TimeDisplay.prototype.controlText_ = 'Time';
  9804. Component.registerComponent('TimeDisplay', TimeDisplay);
  9805. /**
  9806. * @file current-time-display.js
  9807. */
  9808. /**
  9809. * Displays the current time
  9810. *
  9811. * @extends Component
  9812. */
  9813. var CurrentTimeDisplay = function (_TimeDisplay) {
  9814. inherits(CurrentTimeDisplay, _TimeDisplay);
  9815. /**
  9816. * Creates an instance of this class.
  9817. *
  9818. * @param {Player} player
  9819. * The `Player` that this class should be attached to.
  9820. *
  9821. * @param {Object} [options]
  9822. * The key/value store of player options.
  9823. */
  9824. function CurrentTimeDisplay(player, options) {
  9825. classCallCheck(this, CurrentTimeDisplay);
  9826. var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
  9827. _this.on(player, 'ended', _this.handleEnded);
  9828. return _this;
  9829. }
  9830. /**
  9831. * Builds the default DOM `className`.
  9832. *
  9833. * @return {string}
  9834. * The DOM `className` for this object.
  9835. */
  9836. CurrentTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
  9837. return 'vjs-current-time';
  9838. };
  9839. /**
  9840. * Update current time display
  9841. *
  9842. * @param {EventTarget~Event} [event]
  9843. * The `timeupdate` event that caused this function to run.
  9844. *
  9845. * @listens Player#timeupdate
  9846. */
  9847. CurrentTimeDisplay.prototype.updateContent = function updateContent(event) {
  9848. // Allows for smooth scrubbing, when player can't keep up.
  9849. var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  9850. this.updateFormattedTime_(time);
  9851. };
  9852. /**
  9853. * When the player fires ended there should be no time left. Sadly
  9854. * this is not always the case, lets make it seem like that is the case
  9855. * for users.
  9856. *
  9857. * @param {EventTarget~Event} [event]
  9858. * The `ended` event that caused this to run.
  9859. *
  9860. * @listens Player#ended
  9861. */
  9862. CurrentTimeDisplay.prototype.handleEnded = function handleEnded(event) {
  9863. if (!this.player_.duration()) {
  9864. return;
  9865. }
  9866. this.updateFormattedTime_(this.player_.duration());
  9867. };
  9868. return CurrentTimeDisplay;
  9869. }(TimeDisplay);
  9870. /**
  9871. * The text that is added to the `CurrentTimeDisplay` for screen reader users.
  9872. *
  9873. * @type {string}
  9874. * @private
  9875. */
  9876. CurrentTimeDisplay.prototype.labelText_ = 'Current Time';
  9877. /**
  9878. * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
  9879. *
  9880. * @type {string}
  9881. * @private
  9882. *
  9883. * @deprecated in v7; controlText_ is not used in non-active display Components
  9884. */
  9885. CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
  9886. Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
  9887. /**
  9888. * @file duration-display.js
  9889. */
  9890. /**
  9891. * Displays the duration
  9892. *
  9893. * @extends Component
  9894. */
  9895. var DurationDisplay = function (_TimeDisplay) {
  9896. inherits(DurationDisplay, _TimeDisplay);
  9897. /**
  9898. * Creates an instance of this class.
  9899. *
  9900. * @param {Player} player
  9901. * The `Player` that this class should be attached to.
  9902. *
  9903. * @param {Object} [options]
  9904. * The key/value store of player options.
  9905. */
  9906. function DurationDisplay(player, options) {
  9907. classCallCheck(this, DurationDisplay);
  9908. // we do not want to/need to throttle duration changes,
  9909. // as they should always display the changed duration as
  9910. // it has changed
  9911. var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
  9912. _this.on(player, 'durationchange', _this.updateContent);
  9913. // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
  9914. // listeners could have broken dependent applications/libraries. These
  9915. // can likely be removed for 7.0.
  9916. _this.on(player, 'loadedmetadata', _this.throttledUpdateContent);
  9917. return _this;
  9918. }
  9919. /**
  9920. * Builds the default DOM `className`.
  9921. *
  9922. * @return {string}
  9923. * The DOM `className` for this object.
  9924. */
  9925. DurationDisplay.prototype.buildCSSClass = function buildCSSClass() {
  9926. return 'vjs-duration';
  9927. };
  9928. /**
  9929. * Update duration time display.
  9930. *
  9931. * @param {EventTarget~Event} [event]
  9932. * The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
  9933. * this function to be called.
  9934. *
  9935. * @listens Player#durationchange
  9936. * @listens Player#timeupdate
  9937. * @listens Player#loadedmetadata
  9938. */
  9939. DurationDisplay.prototype.updateContent = function updateContent(event) {
  9940. var duration = this.player_.duration();
  9941. if (duration && this.duration_ !== duration) {
  9942. this.duration_ = duration;
  9943. this.updateFormattedTime_(duration);
  9944. }
  9945. };
  9946. return DurationDisplay;
  9947. }(TimeDisplay);
  9948. /**
  9949. * The text that is added to the `DurationDisplay` for screen reader users.
  9950. *
  9951. * @type {string}
  9952. * @private
  9953. */
  9954. DurationDisplay.prototype.labelText_ = 'Duration';
  9955. /**
  9956. * The text that should display over the `DurationDisplay`s controls. Added to for localization.
  9957. *
  9958. * @type {string}
  9959. * @private
  9960. *
  9961. * @deprecated in v7; controlText_ is not used in non-active display Components
  9962. */
  9963. DurationDisplay.prototype.controlText_ = 'Duration';
  9964. Component.registerComponent('DurationDisplay', DurationDisplay);
  9965. /**
  9966. * @file time-divider.js
  9967. */
  9968. /**
  9969. * The separator between the current time and duration.
  9970. * Can be hidden if it's not needed in the design.
  9971. *
  9972. * @extends Component
  9973. */
  9974. var TimeDivider = function (_Component) {
  9975. inherits(TimeDivider, _Component);
  9976. function TimeDivider() {
  9977. classCallCheck(this, TimeDivider);
  9978. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  9979. }
  9980. /**
  9981. * Create the component's DOM element
  9982. *
  9983. * @return {Element}
  9984. * The element that was created.
  9985. */
  9986. TimeDivider.prototype.createEl = function createEl() {
  9987. return _Component.prototype.createEl.call(this, 'div', {
  9988. className: 'vjs-time-control vjs-time-divider',
  9989. innerHTML: '<div><span>/</span></div>'
  9990. });
  9991. };
  9992. return TimeDivider;
  9993. }(Component);
  9994. Component.registerComponent('TimeDivider', TimeDivider);
  9995. /**
  9996. * @file remaining-time-display.js
  9997. */
  9998. /**
  9999. * Displays the time left in the video
  10000. *
  10001. * @extends Component
  10002. */
  10003. var RemainingTimeDisplay = function (_TimeDisplay) {
  10004. inherits(RemainingTimeDisplay, _TimeDisplay);
  10005. /**
  10006. * Creates an instance of this class.
  10007. *
  10008. * @param {Player} player
  10009. * The `Player` that this class should be attached to.
  10010. *
  10011. * @param {Object} [options]
  10012. * The key/value store of player options.
  10013. */
  10014. function RemainingTimeDisplay(player, options) {
  10015. classCallCheck(this, RemainingTimeDisplay);
  10016. var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
  10017. _this.on(player, 'durationchange', _this.throttledUpdateContent);
  10018. _this.on(player, 'ended', _this.handleEnded);
  10019. return _this;
  10020. }
  10021. /**
  10022. * Builds the default DOM `className`.
  10023. *
  10024. * @return {string}
  10025. * The DOM `className` for this object.
  10026. */
  10027. RemainingTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
  10028. return 'vjs-remaining-time';
  10029. };
  10030. /**
  10031. * The remaining time display prefixes numbers with a "minus" character.
  10032. *
  10033. * @param {number} time
  10034. * A numeric time, in seconds.
  10035. *
  10036. * @return {string}
  10037. * A formatted time
  10038. *
  10039. * @private
  10040. */
  10041. RemainingTimeDisplay.prototype.formatTime_ = function formatTime_(time) {
  10042. // TODO: The "-" should be decorative, and not announced by a screen reader
  10043. return '-' + _TimeDisplay.prototype.formatTime_.call(this, time);
  10044. };
  10045. /**
  10046. * Update remaining time display.
  10047. *
  10048. * @param {EventTarget~Event} [event]
  10049. * The `timeupdate` or `durationchange` event that caused this to run.
  10050. *
  10051. * @listens Player#timeupdate
  10052. * @listens Player#durationchange
  10053. */
  10054. RemainingTimeDisplay.prototype.updateContent = function updateContent(event) {
  10055. if (!this.player_.duration()) {
  10056. return;
  10057. }
  10058. // @deprecated We should only use remainingTimeDisplay
  10059. // as of video.js 7
  10060. if (this.player_.remainingTimeDisplay) {
  10061. this.updateFormattedTime_(this.player_.remainingTimeDisplay());
  10062. } else {
  10063. this.updateFormattedTime_(this.player_.remainingTime());
  10064. }
  10065. };
  10066. /**
  10067. * When the player fires ended there should be no time left. Sadly
  10068. * this is not always the case, lets make it seem like that is the case
  10069. * for users.
  10070. *
  10071. * @param {EventTarget~Event} [event]
  10072. * The `ended` event that caused this to run.
  10073. *
  10074. * @listens Player#ended
  10075. */
  10076. RemainingTimeDisplay.prototype.handleEnded = function handleEnded(event) {
  10077. if (!this.player_.duration()) {
  10078. return;
  10079. }
  10080. this.updateFormattedTime_(0);
  10081. };
  10082. return RemainingTimeDisplay;
  10083. }(TimeDisplay);
  10084. /**
  10085. * The text that is added to the `RemainingTimeDisplay` for screen reader users.
  10086. *
  10087. * @type {string}
  10088. * @private
  10089. */
  10090. RemainingTimeDisplay.prototype.labelText_ = 'Remaining Time';
  10091. /**
  10092. * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
  10093. *
  10094. * @type {string}
  10095. * @private
  10096. *
  10097. * @deprecated in v7; controlText_ is not used in non-active display Components
  10098. */
  10099. RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
  10100. Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
  10101. /**
  10102. * @file live-display.js
  10103. */
  10104. // TODO - Future make it click to snap to live
  10105. /**
  10106. * Displays the live indicator when duration is Infinity.
  10107. *
  10108. * @extends Component
  10109. */
  10110. var LiveDisplay = function (_Component) {
  10111. inherits(LiveDisplay, _Component);
  10112. /**
  10113. * Creates an instance of this class.
  10114. *
  10115. * @param {Player} player
  10116. * The `Player` that this class should be attached to.
  10117. *
  10118. * @param {Object} [options]
  10119. * The key/value store of player options.
  10120. */
  10121. function LiveDisplay(player, options) {
  10122. classCallCheck(this, LiveDisplay);
  10123. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  10124. _this.updateShowing();
  10125. _this.on(_this.player(), 'durationchange', _this.updateShowing);
  10126. return _this;
  10127. }
  10128. /**
  10129. * Create the `Component`'s DOM element
  10130. *
  10131. * @return {Element}
  10132. * The element that was created.
  10133. */
  10134. LiveDisplay.prototype.createEl = function createEl$$1() {
  10135. var el = _Component.prototype.createEl.call(this, 'div', {
  10136. className: 'vjs-live-control vjs-control'
  10137. });
  10138. this.contentEl_ = createEl('div', {
  10139. className: 'vjs-live-display',
  10140. innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '\xA0</span>' + this.localize('LIVE')
  10141. }, {
  10142. 'aria-live': 'off'
  10143. });
  10144. el.appendChild(this.contentEl_);
  10145. return el;
  10146. };
  10147. LiveDisplay.prototype.dispose = function dispose() {
  10148. this.contentEl_ = null;
  10149. _Component.prototype.dispose.call(this);
  10150. };
  10151. /**
  10152. * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
  10153. * it accordingly
  10154. *
  10155. * @param {EventTarget~Event} [event]
  10156. * The {@link Player#durationchange} event that caused this function to run.
  10157. *
  10158. * @listens Player#durationchange
  10159. */
  10160. LiveDisplay.prototype.updateShowing = function updateShowing(event) {
  10161. if (this.player().duration() === Infinity) {
  10162. this.show();
  10163. } else {
  10164. this.hide();
  10165. }
  10166. };
  10167. return LiveDisplay;
  10168. }(Component);
  10169. Component.registerComponent('LiveDisplay', LiveDisplay);
  10170. /**
  10171. * @file slider.js
  10172. */
  10173. /**
  10174. * The base functionality for a slider. Can be vertical or horizontal.
  10175. * For instance the volume bar or the seek bar on a video is a slider.
  10176. *
  10177. * @extends Component
  10178. */
  10179. var Slider = function (_Component) {
  10180. inherits(Slider, _Component);
  10181. /**
  10182. * Create an instance of this class
  10183. *
  10184. * @param {Player} player
  10185. * The `Player` that this class should be attached to.
  10186. *
  10187. * @param {Object} [options]
  10188. * The key/value store of player options.
  10189. */
  10190. function Slider(player, options) {
  10191. classCallCheck(this, Slider);
  10192. // Set property names to bar to match with the child Slider class is looking for
  10193. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  10194. _this.bar = _this.getChild(_this.options_.barName);
  10195. // Set a horizontal or vertical class on the slider depending on the slider type
  10196. _this.vertical(!!_this.options_.vertical);
  10197. _this.enable();
  10198. return _this;
  10199. }
  10200. /**
  10201. * Are controls are currently enabled for this slider or not.
  10202. *
  10203. * @return {boolean}
  10204. * true if controls are enabled, false otherwise
  10205. */
  10206. Slider.prototype.enabled = function enabled() {
  10207. return this.enabled_;
  10208. };
  10209. /**
  10210. * Enable controls for this slider if they are disabled
  10211. */
  10212. Slider.prototype.enable = function enable() {
  10213. if (this.enabled()) {
  10214. return;
  10215. }
  10216. this.on('mousedown', this.handleMouseDown);
  10217. this.on('touchstart', this.handleMouseDown);
  10218. this.on('focus', this.handleFocus);
  10219. this.on('blur', this.handleBlur);
  10220. this.on('click', this.handleClick);
  10221. this.on(this.player_, 'controlsvisible', this.update);
  10222. if (this.playerEvent) {
  10223. this.on(this.player_, this.playerEvent, this.update);
  10224. }
  10225. this.removeClass('disabled');
  10226. this.setAttribute('tabindex', 0);
  10227. this.enabled_ = true;
  10228. };
  10229. /**
  10230. * Disable controls for this slider if they are enabled
  10231. */
  10232. Slider.prototype.disable = function disable() {
  10233. if (!this.enabled()) {
  10234. return;
  10235. }
  10236. var doc = this.bar.el_.ownerDocument;
  10237. this.off('mousedown', this.handleMouseDown);
  10238. this.off('touchstart', this.handleMouseDown);
  10239. this.off('focus', this.handleFocus);
  10240. this.off('blur', this.handleBlur);
  10241. this.off('click', this.handleClick);
  10242. this.off(this.player_, 'controlsvisible', this.update);
  10243. this.off(doc, 'mousemove', this.handleMouseMove);
  10244. this.off(doc, 'mouseup', this.handleMouseUp);
  10245. this.off(doc, 'touchmove', this.handleMouseMove);
  10246. this.off(doc, 'touchend', this.handleMouseUp);
  10247. this.removeAttribute('tabindex');
  10248. this.addClass('disabled');
  10249. if (this.playerEvent) {
  10250. this.off(this.player_, this.playerEvent, this.update);
  10251. }
  10252. this.enabled_ = false;
  10253. };
  10254. /**
  10255. * Create the `Button`s DOM element.
  10256. *
  10257. * @param {string} type
  10258. * Type of element to create.
  10259. *
  10260. * @param {Object} [props={}]
  10261. * List of properties in Object form.
  10262. *
  10263. * @param {Object} [attributes={}]
  10264. * list of attributes in Object form.
  10265. *
  10266. * @return {Element}
  10267. * The element that gets created.
  10268. */
  10269. Slider.prototype.createEl = function createEl$$1(type) {
  10270. var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  10271. var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  10272. // Add the slider element class to all sub classes
  10273. props.className = props.className + ' vjs-slider';
  10274. props = assign({
  10275. tabIndex: 0
  10276. }, props);
  10277. attributes = assign({
  10278. 'role': 'slider',
  10279. 'aria-valuenow': 0,
  10280. 'aria-valuemin': 0,
  10281. 'aria-valuemax': 100,
  10282. 'tabIndex': 0
  10283. }, attributes);
  10284. return _Component.prototype.createEl.call(this, type, props, attributes);
  10285. };
  10286. /**
  10287. * Handle `mousedown` or `touchstart` events on the `Slider`.
  10288. *
  10289. * @param {EventTarget~Event} event
  10290. * `mousedown` or `touchstart` event that triggered this function
  10291. *
  10292. * @listens mousedown
  10293. * @listens touchstart
  10294. * @fires Slider#slideractive
  10295. */
  10296. Slider.prototype.handleMouseDown = function handleMouseDown(event) {
  10297. var doc = this.bar.el_.ownerDocument;
  10298. if (event.type === 'mousedown') {
  10299. event.preventDefault();
  10300. }
  10301. // Do not call preventDefault() on touchstart in Chrome
  10302. // to avoid console warnings. Use a 'touch-action: none' style
  10303. // instead to prevent unintented scrolling.
  10304. // https://developers.google.com/web/updates/2017/01/scrolling-intervention
  10305. if (event.type === 'touchstart' && !IS_CHROME) {
  10306. event.preventDefault();
  10307. }
  10308. blockTextSelection();
  10309. this.addClass('vjs-sliding');
  10310. /**
  10311. * Triggered when the slider is in an active state
  10312. *
  10313. * @event Slider#slideractive
  10314. * @type {EventTarget~Event}
  10315. */
  10316. this.trigger('slideractive');
  10317. this.on(doc, 'mousemove', this.handleMouseMove);
  10318. this.on(doc, 'mouseup', this.handleMouseUp);
  10319. this.on(doc, 'touchmove', this.handleMouseMove);
  10320. this.on(doc, 'touchend', this.handleMouseUp);
  10321. this.handleMouseMove(event);
  10322. };
  10323. /**
  10324. * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
  10325. * The `mousemove` and `touchmove` events will only only trigger this function during
  10326. * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
  10327. * {@link Slider#handleMouseUp}.
  10328. *
  10329. * @param {EventTarget~Event} event
  10330. * `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
  10331. * this function
  10332. *
  10333. * @listens mousemove
  10334. * @listens touchmove
  10335. */
  10336. Slider.prototype.handleMouseMove = function handleMouseMove(event) {};
  10337. /**
  10338. * Handle `mouseup` or `touchend` events on the `Slider`.
  10339. *
  10340. * @param {EventTarget~Event} event
  10341. * `mouseup` or `touchend` event that triggered this function.
  10342. *
  10343. * @listens touchend
  10344. * @listens mouseup
  10345. * @fires Slider#sliderinactive
  10346. */
  10347. Slider.prototype.handleMouseUp = function handleMouseUp() {
  10348. var doc = this.bar.el_.ownerDocument;
  10349. unblockTextSelection();
  10350. this.removeClass('vjs-sliding');
  10351. /**
  10352. * Triggered when the slider is no longer in an active state.
  10353. *
  10354. * @event Slider#sliderinactive
  10355. * @type {EventTarget~Event}
  10356. */
  10357. this.trigger('sliderinactive');
  10358. this.off(doc, 'mousemove', this.handleMouseMove);
  10359. this.off(doc, 'mouseup', this.handleMouseUp);
  10360. this.off(doc, 'touchmove', this.handleMouseMove);
  10361. this.off(doc, 'touchend', this.handleMouseUp);
  10362. this.update();
  10363. };
  10364. /**
  10365. * Update the progress bar of the `Slider`.
  10366. *
  10367. * @returns {number}
  10368. * The percentage of progress the progress bar represents as a
  10369. * number from 0 to 1.
  10370. */
  10371. Slider.prototype.update = function update() {
  10372. // In VolumeBar init we have a setTimeout for update that pops and update
  10373. // to the end of the execution stack. The player is destroyed before then
  10374. // update will cause an error
  10375. if (!this.el_) {
  10376. return;
  10377. }
  10378. // If scrubbing, we could use a cached value to make the handle keep up
  10379. // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but
  10380. // some flash players are slow, so we might want to utilize this later.
  10381. // var progress = (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
  10382. var progress = this.getPercent();
  10383. var bar = this.bar;
  10384. // If there's no bar...
  10385. if (!bar) {
  10386. return;
  10387. }
  10388. // Protect against no duration and other division issues
  10389. if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
  10390. progress = 0;
  10391. }
  10392. // Convert to a percentage for setting
  10393. var percentage = (progress * 100).toFixed(2) + '%';
  10394. var style = bar.el().style;
  10395. // Set the new bar width or height
  10396. if (this.vertical()) {
  10397. style.height = percentage;
  10398. } else {
  10399. style.width = percentage;
  10400. }
  10401. return progress;
  10402. };
  10403. /**
  10404. * Calculate distance for slider
  10405. *
  10406. * @param {EventTarget~Event} event
  10407. * The event that caused this function to run.
  10408. *
  10409. * @return {number}
  10410. * The current position of the Slider.
  10411. * - postition.x for vertical `Slider`s
  10412. * - postition.y for horizontal `Slider`s
  10413. */
  10414. Slider.prototype.calculateDistance = function calculateDistance(event) {
  10415. var position = getPointerPosition(this.el_, event);
  10416. if (this.vertical()) {
  10417. return position.y;
  10418. }
  10419. return position.x;
  10420. };
  10421. /**
  10422. * Handle a `focus` event on this `Slider`.
  10423. *
  10424. * @param {EventTarget~Event} event
  10425. * The `focus` event that caused this function to run.
  10426. *
  10427. * @listens focus
  10428. */
  10429. Slider.prototype.handleFocus = function handleFocus() {
  10430. this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  10431. };
  10432. /**
  10433. * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
  10434. * arrow keys. This function will only be called when the slider has focus. See
  10435. * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
  10436. *
  10437. * @param {EventTarget~Event} event
  10438. * the `keydown` event that caused this function to run.
  10439. *
  10440. * @listens keydown
  10441. */
  10442. Slider.prototype.handleKeyPress = function handleKeyPress(event) {
  10443. // Left and Down Arrows
  10444. if (event.which === 37 || event.which === 40) {
  10445. event.preventDefault();
  10446. this.stepBack();
  10447. // Up and Right Arrows
  10448. } else if (event.which === 38 || event.which === 39) {
  10449. event.preventDefault();
  10450. this.stepForward();
  10451. }
  10452. };
  10453. /**
  10454. * Handle a `blur` event on this `Slider`.
  10455. *
  10456. * @param {EventTarget~Event} event
  10457. * The `blur` event that caused this function to run.
  10458. *
  10459. * @listens blur
  10460. */
  10461. Slider.prototype.handleBlur = function handleBlur() {
  10462. this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  10463. };
  10464. /**
  10465. * Listener for click events on slider, used to prevent clicks
  10466. * from bubbling up to parent elements like button menus.
  10467. *
  10468. * @param {Object} event
  10469. * Event that caused this object to run
  10470. */
  10471. Slider.prototype.handleClick = function handleClick(event) {
  10472. event.stopImmediatePropagation();
  10473. event.preventDefault();
  10474. };
  10475. /**
  10476. * Get/set if slider is horizontal for vertical
  10477. *
  10478. * @param {boolean} [bool]
  10479. * - true if slider is vertical,
  10480. * - false is horizontal
  10481. *
  10482. * @return {boolean}
  10483. * - true if slider is vertical, and getting
  10484. * - false if the slider is horizontal, and getting
  10485. */
  10486. Slider.prototype.vertical = function vertical(bool) {
  10487. if (bool === undefined) {
  10488. return this.vertical_ || false;
  10489. }
  10490. this.vertical_ = !!bool;
  10491. if (this.vertical_) {
  10492. this.addClass('vjs-slider-vertical');
  10493. } else {
  10494. this.addClass('vjs-slider-horizontal');
  10495. }
  10496. };
  10497. return Slider;
  10498. }(Component);
  10499. Component.registerComponent('Slider', Slider);
  10500. /**
  10501. * @file load-progress-bar.js
  10502. */
  10503. /**
  10504. * Shows loading progress
  10505. *
  10506. * @extends Component
  10507. */
  10508. var LoadProgressBar = function (_Component) {
  10509. inherits(LoadProgressBar, _Component);
  10510. /**
  10511. * Creates an instance of this class.
  10512. *
  10513. * @param {Player} player
  10514. * The `Player` that this class should be attached to.
  10515. *
  10516. * @param {Object} [options]
  10517. * The key/value store of player options.
  10518. */
  10519. function LoadProgressBar(player, options) {
  10520. classCallCheck(this, LoadProgressBar);
  10521. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  10522. _this.partEls_ = [];
  10523. _this.on(player, 'progress', _this.update);
  10524. return _this;
  10525. }
  10526. /**
  10527. * Create the `Component`'s DOM element
  10528. *
  10529. * @return {Element}
  10530. * The element that was created.
  10531. */
  10532. LoadProgressBar.prototype.createEl = function createEl$$1() {
  10533. return _Component.prototype.createEl.call(this, 'div', {
  10534. className: 'vjs-load-progress',
  10535. innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
  10536. });
  10537. };
  10538. LoadProgressBar.prototype.dispose = function dispose() {
  10539. this.partEls_ = null;
  10540. _Component.prototype.dispose.call(this);
  10541. };
  10542. /**
  10543. * Update progress bar
  10544. *
  10545. * @param {EventTarget~Event} [event]
  10546. * The `progress` event that caused this function to run.
  10547. *
  10548. * @listens Player#progress
  10549. */
  10550. LoadProgressBar.prototype.update = function update(event) {
  10551. var buffered = this.player_.buffered();
  10552. var duration = this.player_.duration();
  10553. var bufferedEnd = this.player_.bufferedEnd();
  10554. var children = this.partEls_;
  10555. // get the percent width of a time compared to the total end
  10556. var percentify = function percentify(time, end) {
  10557. // no NaN
  10558. var percent = time / end || 0;
  10559. return (percent >= 1 ? 1 : percent) * 100 + '%';
  10560. };
  10561. // update the width of the progress bar
  10562. this.el_.style.width = percentify(bufferedEnd, duration);
  10563. // add child elements to represent the individual buffered time ranges
  10564. for (var i = 0; i < buffered.length; i++) {
  10565. var start = buffered.start(i);
  10566. var end = buffered.end(i);
  10567. var part = children[i];
  10568. if (!part) {
  10569. part = this.el_.appendChild(createEl());
  10570. children[i] = part;
  10571. }
  10572. // set the percent based on the width of the progress bar (bufferedEnd)
  10573. part.style.left = percentify(start, bufferedEnd);
  10574. part.style.width = percentify(end - start, bufferedEnd);
  10575. }
  10576. // remove unused buffered range elements
  10577. for (var _i = children.length; _i > buffered.length; _i--) {
  10578. this.el_.removeChild(children[_i - 1]);
  10579. }
  10580. children.length = buffered.length;
  10581. };
  10582. return LoadProgressBar;
  10583. }(Component);
  10584. Component.registerComponent('LoadProgressBar', LoadProgressBar);
  10585. /**
  10586. * @file time-tooltip.js
  10587. */
  10588. /**
  10589. * Time tooltips display a time above the progress bar.
  10590. *
  10591. * @extends Component
  10592. */
  10593. var TimeTooltip = function (_Component) {
  10594. inherits(TimeTooltip, _Component);
  10595. function TimeTooltip() {
  10596. classCallCheck(this, TimeTooltip);
  10597. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  10598. }
  10599. /**
  10600. * Create the time tooltip DOM element
  10601. *
  10602. * @return {Element}
  10603. * The element that was created.
  10604. */
  10605. TimeTooltip.prototype.createEl = function createEl$$1() {
  10606. return _Component.prototype.createEl.call(this, 'div', {
  10607. className: 'vjs-time-tooltip'
  10608. });
  10609. };
  10610. /**
  10611. * Updates the position of the time tooltip relative to the `SeekBar`.
  10612. *
  10613. * @param {Object} seekBarRect
  10614. * The `ClientRect` for the {@link SeekBar} element.
  10615. *
  10616. * @param {number} seekBarPoint
  10617. * A number from 0 to 1, representing a horizontal reference point
  10618. * from the left edge of the {@link SeekBar}
  10619. */
  10620. TimeTooltip.prototype.update = function update(seekBarRect, seekBarPoint, content) {
  10621. var tooltipRect = getBoundingClientRect(this.el_);
  10622. var playerRect = getBoundingClientRect(this.player_.el());
  10623. var seekBarPointPx = seekBarRect.width * seekBarPoint;
  10624. // do nothing if either rect isn't available
  10625. // for example, if the player isn't in the DOM for testing
  10626. if (!playerRect || !tooltipRect) {
  10627. return;
  10628. }
  10629. // This is the space left of the `seekBarPoint` available within the bounds
  10630. // of the player. We calculate any gap between the left edge of the player
  10631. // and the left edge of the `SeekBar` and add the number of pixels in the
  10632. // `SeekBar` before hitting the `seekBarPoint`
  10633. var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx;
  10634. // This is the space right of the `seekBarPoint` available within the bounds
  10635. // of the player. We calculate the number of pixels from the `seekBarPoint`
  10636. // to the right edge of the `SeekBar` and add to that any gap between the
  10637. // right edge of the `SeekBar` and the player.
  10638. var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right);
  10639. // This is the number of pixels by which the tooltip will need to be pulled
  10640. // further to the right to center it over the `seekBarPoint`.
  10641. var pullTooltipBy = tooltipRect.width / 2;
  10642. // Adjust the `pullTooltipBy` distance to the left or right depending on
  10643. // the results of the space calculations above.
  10644. if (spaceLeftOfPoint < pullTooltipBy) {
  10645. pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
  10646. } else if (spaceRightOfPoint < pullTooltipBy) {
  10647. pullTooltipBy = spaceRightOfPoint;
  10648. }
  10649. // Due to the imprecision of decimal/ratio based calculations and varying
  10650. // rounding behaviors, there are cases where the spacing adjustment is off
  10651. // by a pixel or two. This adds insurance to these calculations.
  10652. if (pullTooltipBy < 0) {
  10653. pullTooltipBy = 0;
  10654. } else if (pullTooltipBy > tooltipRect.width) {
  10655. pullTooltipBy = tooltipRect.width;
  10656. }
  10657. this.el_.style.right = '-' + pullTooltipBy + 'px';
  10658. textContent(this.el_, content);
  10659. };
  10660. return TimeTooltip;
  10661. }(Component);
  10662. Component.registerComponent('TimeTooltip', TimeTooltip);
  10663. /**
  10664. * @file play-progress-bar.js
  10665. */
  10666. /**
  10667. * Used by {@link SeekBar} to display media playback progress as part of the
  10668. * {@link ProgressControl}.
  10669. *
  10670. * @extends Component
  10671. */
  10672. var PlayProgressBar = function (_Component) {
  10673. inherits(PlayProgressBar, _Component);
  10674. function PlayProgressBar() {
  10675. classCallCheck(this, PlayProgressBar);
  10676. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  10677. }
  10678. /**
  10679. * Create the the DOM element for this class.
  10680. *
  10681. * @return {Element}
  10682. * The element that was created.
  10683. */
  10684. PlayProgressBar.prototype.createEl = function createEl() {
  10685. return _Component.prototype.createEl.call(this, 'div', {
  10686. className: 'vjs-play-progress vjs-slider-bar',
  10687. innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
  10688. });
  10689. };
  10690. /**
  10691. * Enqueues updates to its own DOM as well as the DOM of its
  10692. * {@link TimeTooltip} child.
  10693. *
  10694. * @param {Object} seekBarRect
  10695. * The `ClientRect` for the {@link SeekBar} element.
  10696. *
  10697. * @param {number} seekBarPoint
  10698. * A number from 0 to 1, representing a horizontal reference point
  10699. * from the left edge of the {@link SeekBar}
  10700. */
  10701. PlayProgressBar.prototype.update = function update(seekBarRect, seekBarPoint) {
  10702. var _this2 = this;
  10703. // If there is an existing rAF ID, cancel it so we don't over-queue.
  10704. if (this.rafId_) {
  10705. this.cancelAnimationFrame(this.rafId_);
  10706. }
  10707. this.rafId_ = this.requestAnimationFrame(function () {
  10708. var time = _this2.player_.scrubbing() ? _this2.player_.getCache().currentTime : _this2.player_.currentTime();
  10709. var content = formatTime(time, _this2.player_.duration());
  10710. var timeTooltip = _this2.getChild('timeTooltip');
  10711. if (timeTooltip) {
  10712. timeTooltip.update(seekBarRect, seekBarPoint, content);
  10713. }
  10714. });
  10715. };
  10716. return PlayProgressBar;
  10717. }(Component);
  10718. /**
  10719. * Default options for {@link PlayProgressBar}.
  10720. *
  10721. * @type {Object}
  10722. * @private
  10723. */
  10724. PlayProgressBar.prototype.options_ = {
  10725. children: []
  10726. };
  10727. // Time tooltips should not be added to a player on mobile devices or IE8
  10728. if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
  10729. PlayProgressBar.prototype.options_.children.push('timeTooltip');
  10730. }
  10731. Component.registerComponent('PlayProgressBar', PlayProgressBar);
  10732. /**
  10733. * @file mouse-time-display.js
  10734. */
  10735. /**
  10736. * The {@link MouseTimeDisplay} component tracks mouse movement over the
  10737. * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
  10738. * indicating the time which is represented by a given point in the
  10739. * {@link ProgressControl}.
  10740. *
  10741. * @extends Component
  10742. */
  10743. var MouseTimeDisplay = function (_Component) {
  10744. inherits(MouseTimeDisplay, _Component);
  10745. /**
  10746. * Creates an instance of this class.
  10747. *
  10748. * @param {Player} player
  10749. * The {@link Player} that this class should be attached to.
  10750. *
  10751. * @param {Object} [options]
  10752. * The key/value store of player options.
  10753. */
  10754. function MouseTimeDisplay(player, options) {
  10755. classCallCheck(this, MouseTimeDisplay);
  10756. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  10757. _this.update = throttle(bind(_this, _this.update), 25);
  10758. return _this;
  10759. }
  10760. /**
  10761. * Create the DOM element for this class.
  10762. *
  10763. * @return {Element}
  10764. * The element that was created.
  10765. */
  10766. MouseTimeDisplay.prototype.createEl = function createEl() {
  10767. return _Component.prototype.createEl.call(this, 'div', {
  10768. className: 'vjs-mouse-display'
  10769. });
  10770. };
  10771. /**
  10772. * Enqueues updates to its own DOM as well as the DOM of its
  10773. * {@link TimeTooltip} child.
  10774. *
  10775. * @param {Object} seekBarRect
  10776. * The `ClientRect` for the {@link SeekBar} element.
  10777. *
  10778. * @param {number} seekBarPoint
  10779. * A number from 0 to 1, representing a horizontal reference point
  10780. * from the left edge of the {@link SeekBar}
  10781. */
  10782. MouseTimeDisplay.prototype.update = function update(seekBarRect, seekBarPoint) {
  10783. var _this2 = this;
  10784. // If there is an existing rAF ID, cancel it so we don't over-queue.
  10785. if (this.rafId_) {
  10786. this.cancelAnimationFrame(this.rafId_);
  10787. }
  10788. this.rafId_ = this.requestAnimationFrame(function () {
  10789. var duration = _this2.player_.duration();
  10790. var content = formatTime(seekBarPoint * duration, duration);
  10791. _this2.el_.style.left = seekBarRect.width * seekBarPoint + 'px';
  10792. _this2.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
  10793. });
  10794. };
  10795. return MouseTimeDisplay;
  10796. }(Component);
  10797. /**
  10798. * Default options for `MouseTimeDisplay`
  10799. *
  10800. * @type {Object}
  10801. * @private
  10802. */
  10803. MouseTimeDisplay.prototype.options_ = {
  10804. children: ['timeTooltip']
  10805. };
  10806. Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
  10807. /**
  10808. * @file seek-bar.js
  10809. */
  10810. // The number of seconds the `step*` functions move the timeline.
  10811. var STEP_SECONDS = 5;
  10812. // The interval at which the bar should update as it progresses.
  10813. var UPDATE_REFRESH_INTERVAL = 30;
  10814. /**
  10815. * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
  10816. * as its `bar`.
  10817. *
  10818. * @extends Slider
  10819. */
  10820. var SeekBar = function (_Slider) {
  10821. inherits(SeekBar, _Slider);
  10822. /**
  10823. * Creates an instance of this class.
  10824. *
  10825. * @param {Player} player
  10826. * The `Player` that this class should be attached to.
  10827. *
  10828. * @param {Object} [options]
  10829. * The key/value store of player options.
  10830. */
  10831. function SeekBar(player, options) {
  10832. classCallCheck(this, SeekBar);
  10833. var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
  10834. _this.setEventHandlers_();
  10835. return _this;
  10836. }
  10837. /**
  10838. * Sets the event handlers
  10839. *
  10840. * @private
  10841. */
  10842. SeekBar.prototype.setEventHandlers_ = function setEventHandlers_() {
  10843. var _this2 = this;
  10844. this.update = throttle(bind(this, this.update), UPDATE_REFRESH_INTERVAL);
  10845. this.on(this.player_, 'timeupdate', this.update);
  10846. this.on(this.player_, 'ended', this.handleEnded);
  10847. // when playing, let's ensure we smoothly update the play progress bar
  10848. // via an interval
  10849. this.updateInterval = null;
  10850. this.on(this.player_, ['playing'], function () {
  10851. _this2.clearInterval(_this2.updateInterval);
  10852. _this2.updateInterval = _this2.setInterval(function () {
  10853. _this2.requestAnimationFrame(function () {
  10854. _this2.update();
  10855. });
  10856. }, UPDATE_REFRESH_INTERVAL);
  10857. });
  10858. this.on(this.player_, ['ended', 'pause', 'waiting'], function () {
  10859. _this2.clearInterval(_this2.updateInterval);
  10860. });
  10861. this.on(this.player_, ['timeupdate', 'ended'], this.update);
  10862. };
  10863. /**
  10864. * Create the `Component`'s DOM element
  10865. *
  10866. * @return {Element}
  10867. * The element that was created.
  10868. */
  10869. SeekBar.prototype.createEl = function createEl$$1() {
  10870. return _Slider.prototype.createEl.call(this, 'div', {
  10871. className: 'vjs-progress-holder'
  10872. }, {
  10873. 'aria-label': this.localize('Progress Bar')
  10874. });
  10875. };
  10876. /**
  10877. * This function updates the play progress bar and accessiblity
  10878. * attributes to whatever is passed in.
  10879. *
  10880. * @param {number} currentTime
  10881. * The currentTime value that should be used for accessiblity
  10882. *
  10883. * @param {number} percent
  10884. * The percentage as a decimal that the bar should be filled from 0-1.
  10885. *
  10886. * @private
  10887. */
  10888. SeekBar.prototype.update_ = function update_(currentTime, percent) {
  10889. var duration = this.player_.duration();
  10890. // machine readable value of progress bar (percentage complete)
  10891. this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
  10892. // human readable value of progress bar (time complete)
  10893. this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));
  10894. // Update the `PlayProgressBar`.
  10895. this.bar.update(getBoundingClientRect(this.el_), percent);
  10896. };
  10897. /**
  10898. * Update the seek bar's UI.
  10899. *
  10900. * @param {EventTarget~Event} [event]
  10901. * The `timeupdate` or `ended` event that caused this to run.
  10902. *
  10903. * @listens Player#timeupdate
  10904. *
  10905. * @returns {number}
  10906. * The current percent at a number from 0-1
  10907. */
  10908. SeekBar.prototype.update = function update(event) {
  10909. var percent = _Slider.prototype.update.call(this);
  10910. this.update_(this.getCurrentTime_(), percent);
  10911. return percent;
  10912. };
  10913. /**
  10914. * Get the value of current time but allows for smooth scrubbing,
  10915. * when player can't keep up.
  10916. *
  10917. * @return {number}
  10918. * The current time value to display
  10919. *
  10920. * @private
  10921. */
  10922. SeekBar.prototype.getCurrentTime_ = function getCurrentTime_() {
  10923. return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
  10924. };
  10925. /**
  10926. * We want the seek bar to be full on ended
  10927. * no matter what the actual internal values are. so we force it.
  10928. *
  10929. * @param {EventTarget~Event} [event]
  10930. * The `timeupdate` or `ended` event that caused this to run.
  10931. *
  10932. * @listens Player#ended
  10933. */
  10934. SeekBar.prototype.handleEnded = function handleEnded(event) {
  10935. this.update_(this.player_.duration(), 1);
  10936. };
  10937. /**
  10938. * Get the percentage of media played so far.
  10939. *
  10940. * @return {number}
  10941. * The percentage of media played so far (0 to 1).
  10942. */
  10943. SeekBar.prototype.getPercent = function getPercent() {
  10944. var percent = this.getCurrentTime_() / this.player_.duration();
  10945. return percent >= 1 ? 1 : percent;
  10946. };
  10947. /**
  10948. * Handle mouse down on seek bar
  10949. *
  10950. * @param {EventTarget~Event} event
  10951. * The `mousedown` event that caused this to run.
  10952. *
  10953. * @listens mousedown
  10954. */
  10955. SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
  10956. if (!isSingleLeftClick(event)) {
  10957. return;
  10958. }
  10959. // Stop event propagation to prevent double fire in progress-control.js
  10960. event.stopPropagation();
  10961. this.player_.scrubbing(true);
  10962. this.videoWasPlaying = !this.player_.paused();
  10963. this.player_.pause();
  10964. _Slider.prototype.handleMouseDown.call(this, event);
  10965. };
  10966. /**
  10967. * Handle mouse move on seek bar
  10968. *
  10969. * @param {EventTarget~Event} event
  10970. * The `mousemove` event that caused this to run.
  10971. *
  10972. * @listens mousemove
  10973. */
  10974. SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
  10975. if (!isSingleLeftClick(event)) {
  10976. return;
  10977. }
  10978. var newTime = this.calculateDistance(event) * this.player_.duration();
  10979. // Don't let video end while scrubbing.
  10980. if (newTime === this.player_.duration()) {
  10981. newTime = newTime - 0.1;
  10982. }
  10983. // Set new time (tell player to seek to new time)
  10984. this.player_.currentTime(newTime);
  10985. };
  10986. SeekBar.prototype.enable = function enable() {
  10987. _Slider.prototype.enable.call(this);
  10988. var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
  10989. if (!mouseTimeDisplay) {
  10990. return;
  10991. }
  10992. mouseTimeDisplay.show();
  10993. };
  10994. SeekBar.prototype.disable = function disable() {
  10995. _Slider.prototype.disable.call(this);
  10996. var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
  10997. if (!mouseTimeDisplay) {
  10998. return;
  10999. }
  11000. mouseTimeDisplay.hide();
  11001. };
  11002. /**
  11003. * Handle mouse up on seek bar
  11004. *
  11005. * @param {EventTarget~Event} event
  11006. * The `mouseup` event that caused this to run.
  11007. *
  11008. * @listens mouseup
  11009. */
  11010. SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
  11011. _Slider.prototype.handleMouseUp.call(this, event);
  11012. // Stop event propagation to prevent double fire in progress-control.js
  11013. if (event) {
  11014. event.stopPropagation();
  11015. }
  11016. this.player_.scrubbing(false);
  11017. /**
  11018. * Trigger timeupdate because we're done seeking and the time has changed.
  11019. * This is particularly useful for if the player is paused to time the time displays.
  11020. *
  11021. * @event Tech#timeupdate
  11022. * @type {EventTarget~Event}
  11023. */
  11024. this.player_.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  11025. if (this.videoWasPlaying) {
  11026. silencePromise(this.player_.play());
  11027. }
  11028. };
  11029. /**
  11030. * Move more quickly fast forward for keyboard-only users
  11031. */
  11032. SeekBar.prototype.stepForward = function stepForward() {
  11033. this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
  11034. };
  11035. /**
  11036. * Move more quickly rewind for keyboard-only users
  11037. */
  11038. SeekBar.prototype.stepBack = function stepBack() {
  11039. this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
  11040. };
  11041. /**
  11042. * Toggles the playback state of the player
  11043. * This gets called when enter or space is used on the seekbar
  11044. *
  11045. * @param {EventTarget~Event} event
  11046. * The `keydown` event that caused this function to be called
  11047. *
  11048. */
  11049. SeekBar.prototype.handleAction = function handleAction(event) {
  11050. if (this.player_.paused()) {
  11051. this.player_.play();
  11052. } else {
  11053. this.player_.pause();
  11054. }
  11055. };
  11056. /**
  11057. * Called when this SeekBar has focus and a key gets pressed down. By
  11058. * default it will call `this.handleAction` when the key is space or enter.
  11059. *
  11060. * @param {EventTarget~Event} event
  11061. * The `keydown` event that caused this function to be called.
  11062. *
  11063. * @listens keydown
  11064. */
  11065. SeekBar.prototype.handleKeyPress = function handleKeyPress(event) {
  11066. // Support Space (32) or Enter (13) key operation to fire a click event
  11067. if (event.which === 32 || event.which === 13) {
  11068. event.preventDefault();
  11069. this.handleAction(event);
  11070. } else if (_Slider.prototype.handleKeyPress) {
  11071. // Pass keypress handling up for unsupported keys
  11072. _Slider.prototype.handleKeyPress.call(this, event);
  11073. }
  11074. };
  11075. return SeekBar;
  11076. }(Slider);
  11077. /**
  11078. * Default options for the `SeekBar`
  11079. *
  11080. * @type {Object}
  11081. * @private
  11082. */
  11083. SeekBar.prototype.options_ = {
  11084. children: ['loadProgressBar', 'playProgressBar'],
  11085. barName: 'playProgressBar'
  11086. };
  11087. // MouseTimeDisplay tooltips should not be added to a player on mobile devices or IE8
  11088. if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
  11089. SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
  11090. }
  11091. /**
  11092. * Call the update event for this Slider when this event happens on the player.
  11093. *
  11094. * @type {string}
  11095. */
  11096. SeekBar.prototype.playerEvent = 'timeupdate';
  11097. Component.registerComponent('SeekBar', SeekBar);
  11098. /**
  11099. * @file progress-control.js
  11100. */
  11101. /**
  11102. * The Progress Control component contains the seek bar, load progress,
  11103. * and play progress.
  11104. *
  11105. * @extends Component
  11106. */
  11107. var ProgressControl = function (_Component) {
  11108. inherits(ProgressControl, _Component);
  11109. /**
  11110. * Creates an instance of this class.
  11111. *
  11112. * @param {Player} player
  11113. * The `Player` that this class should be attached to.
  11114. *
  11115. * @param {Object} [options]
  11116. * The key/value store of player options.
  11117. */
  11118. function ProgressControl(player, options) {
  11119. classCallCheck(this, ProgressControl);
  11120. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  11121. _this.handleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
  11122. _this.throttledHandleMouseSeek = throttle(bind(_this, _this.handleMouseSeek), 25);
  11123. _this.enable();
  11124. return _this;
  11125. }
  11126. /**
  11127. * Create the `Component`'s DOM element
  11128. *
  11129. * @return {Element}
  11130. * The element that was created.
  11131. */
  11132. ProgressControl.prototype.createEl = function createEl$$1() {
  11133. return _Component.prototype.createEl.call(this, 'div', {
  11134. className: 'vjs-progress-control vjs-control'
  11135. });
  11136. };
  11137. /**
  11138. * When the mouse moves over the `ProgressControl`, the pointer position
  11139. * gets passed down to the `MouseTimeDisplay` component.
  11140. *
  11141. * @param {EventTarget~Event} event
  11142. * The `mousemove` event that caused this function to run.
  11143. *
  11144. * @listen mousemove
  11145. */
  11146. ProgressControl.prototype.handleMouseMove = function handleMouseMove(event) {
  11147. var seekBar = this.getChild('seekBar');
  11148. if (seekBar) {
  11149. var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
  11150. var seekBarEl = seekBar.el();
  11151. var seekBarRect = getBoundingClientRect(seekBarEl);
  11152. var seekBarPoint = getPointerPosition(seekBarEl, event).x;
  11153. // The default skin has a gap on either side of the `SeekBar`. This means
  11154. // that it's possible to trigger this behavior outside the boundaries of
  11155. // the `SeekBar`. This ensures we stay within it at all times.
  11156. if (seekBarPoint > 1) {
  11157. seekBarPoint = 1;
  11158. } else if (seekBarPoint < 0) {
  11159. seekBarPoint = 0;
  11160. }
  11161. if (mouseTimeDisplay) {
  11162. mouseTimeDisplay.update(seekBarRect, seekBarPoint);
  11163. }
  11164. }
  11165. };
  11166. /**
  11167. * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
  11168. *
  11169. * @method ProgressControl#throttledHandleMouseSeek
  11170. * @param {EventTarget~Event} event
  11171. * The `mousemove` event that caused this function to run.
  11172. *
  11173. * @listen mousemove
  11174. * @listen touchmove
  11175. */
  11176. /**
  11177. * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
  11178. *
  11179. * @param {EventTarget~Event} event
  11180. * `mousedown` or `touchstart` event that triggered this function
  11181. *
  11182. * @listens mousemove
  11183. * @listens touchmove
  11184. */
  11185. ProgressControl.prototype.handleMouseSeek = function handleMouseSeek(event) {
  11186. var seekBar = this.getChild('seekBar');
  11187. if (seekBar) {
  11188. seekBar.handleMouseMove(event);
  11189. }
  11190. };
  11191. /**
  11192. * Are controls are currently enabled for this progress control.
  11193. *
  11194. * @return {boolean}
  11195. * true if controls are enabled, false otherwise
  11196. */
  11197. ProgressControl.prototype.enabled = function enabled() {
  11198. return this.enabled_;
  11199. };
  11200. /**
  11201. * Disable all controls on the progress control and its children
  11202. */
  11203. ProgressControl.prototype.disable = function disable() {
  11204. this.children().forEach(function (child) {
  11205. return child.disable && child.disable();
  11206. });
  11207. if (!this.enabled()) {
  11208. return;
  11209. }
  11210. this.off(['mousedown', 'touchstart'], this.handleMouseDown);
  11211. this.off(this.el_, 'mousemove', this.handleMouseMove);
  11212. this.handleMouseUp();
  11213. this.addClass('disabled');
  11214. this.enabled_ = false;
  11215. };
  11216. /**
  11217. * Enable all controls on the progress control and its children
  11218. */
  11219. ProgressControl.prototype.enable = function enable() {
  11220. this.children().forEach(function (child) {
  11221. return child.enable && child.enable();
  11222. });
  11223. if (this.enabled()) {
  11224. return;
  11225. }
  11226. this.on(['mousedown', 'touchstart'], this.handleMouseDown);
  11227. this.on(this.el_, 'mousemove', this.handleMouseMove);
  11228. this.removeClass('disabled');
  11229. this.enabled_ = true;
  11230. };
  11231. /**
  11232. * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
  11233. *
  11234. * @param {EventTarget~Event} event
  11235. * `mousedown` or `touchstart` event that triggered this function
  11236. *
  11237. * @listens mousedown
  11238. * @listens touchstart
  11239. */
  11240. ProgressControl.prototype.handleMouseDown = function handleMouseDown(event) {
  11241. var doc = this.el_.ownerDocument;
  11242. var seekBar = this.getChild('seekBar');
  11243. if (seekBar) {
  11244. seekBar.handleMouseDown(event);
  11245. }
  11246. this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
  11247. this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
  11248. this.on(doc, 'mouseup', this.handleMouseUp);
  11249. this.on(doc, 'touchend', this.handleMouseUp);
  11250. };
  11251. /**
  11252. * Handle `mouseup` or `touchend` events on the `ProgressControl`.
  11253. *
  11254. * @param {EventTarget~Event} event
  11255. * `mouseup` or `touchend` event that triggered this function.
  11256. *
  11257. * @listens touchend
  11258. * @listens mouseup
  11259. */
  11260. ProgressControl.prototype.handleMouseUp = function handleMouseUp(event) {
  11261. var doc = this.el_.ownerDocument;
  11262. var seekBar = this.getChild('seekBar');
  11263. if (seekBar) {
  11264. seekBar.handleMouseUp(event);
  11265. }
  11266. this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
  11267. this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
  11268. this.off(doc, 'mouseup', this.handleMouseUp);
  11269. this.off(doc, 'touchend', this.handleMouseUp);
  11270. };
  11271. return ProgressControl;
  11272. }(Component);
  11273. /**
  11274. * Default options for `ProgressControl`
  11275. *
  11276. * @type {Object}
  11277. * @private
  11278. */
  11279. ProgressControl.prototype.options_ = {
  11280. children: ['seekBar']
  11281. };
  11282. Component.registerComponent('ProgressControl', ProgressControl);
  11283. /**
  11284. * @file fullscreen-toggle.js
  11285. */
  11286. /**
  11287. * Toggle fullscreen video
  11288. *
  11289. * @extends Button
  11290. */
  11291. var FullscreenToggle = function (_Button) {
  11292. inherits(FullscreenToggle, _Button);
  11293. /**
  11294. * Creates an instance of this class.
  11295. *
  11296. * @param {Player} player
  11297. * The `Player` that this class should be attached to.
  11298. *
  11299. * @param {Object} [options]
  11300. * The key/value store of player options.
  11301. */
  11302. function FullscreenToggle(player, options) {
  11303. classCallCheck(this, FullscreenToggle);
  11304. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  11305. _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
  11306. if (document_1[FullscreenApi.fullscreenEnabled] === false) {
  11307. _this.disable();
  11308. }
  11309. return _this;
  11310. }
  11311. /**
  11312. * Builds the default DOM `className`.
  11313. *
  11314. * @return {string}
  11315. * The DOM `className` for this object.
  11316. */
  11317. FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
  11318. return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
  11319. };
  11320. /**
  11321. * Handles fullscreenchange on the player and change control text accordingly.
  11322. *
  11323. * @param {EventTarget~Event} [event]
  11324. * The {@link Player#fullscreenchange} event that caused this function to be
  11325. * called.
  11326. *
  11327. * @listens Player#fullscreenchange
  11328. */
  11329. FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) {
  11330. if (this.player_.isFullscreen()) {
  11331. this.controlText('Non-Fullscreen');
  11332. } else {
  11333. this.controlText('Fullscreen');
  11334. }
  11335. };
  11336. /**
  11337. * This gets called when an `FullscreenToggle` is "clicked". See
  11338. * {@link ClickableComponent} for more detailed information on what a click can be.
  11339. *
  11340. * @param {EventTarget~Event} [event]
  11341. * The `keydown`, `tap`, or `click` event that caused this function to be
  11342. * called.
  11343. *
  11344. * @listens tap
  11345. * @listens click
  11346. */
  11347. FullscreenToggle.prototype.handleClick = function handleClick(event) {
  11348. if (!this.player_.isFullscreen()) {
  11349. this.player_.requestFullscreen();
  11350. } else {
  11351. this.player_.exitFullscreen();
  11352. }
  11353. };
  11354. return FullscreenToggle;
  11355. }(Button);
  11356. /**
  11357. * The text that should display over the `FullscreenToggle`s controls. Added for localization.
  11358. *
  11359. * @type {string}
  11360. * @private
  11361. */
  11362. FullscreenToggle.prototype.controlText_ = 'Fullscreen';
  11363. Component.registerComponent('FullscreenToggle', FullscreenToggle);
  11364. /**
  11365. * Check if volume control is supported and if it isn't hide the
  11366. * `Component` that was passed using the `vjs-hidden` class.
  11367. *
  11368. * @param {Component} self
  11369. * The component that should be hidden if volume is unsupported
  11370. *
  11371. * @param {Player} player
  11372. * A reference to the player
  11373. *
  11374. * @private
  11375. */
  11376. var checkVolumeSupport = function checkVolumeSupport(self, player) {
  11377. // hide volume controls when they're not supported by the current tech
  11378. if (player.tech_ && !player.tech_.featuresVolumeControl) {
  11379. self.addClass('vjs-hidden');
  11380. }
  11381. self.on(player, 'loadstart', function () {
  11382. if (!player.tech_.featuresVolumeControl) {
  11383. self.addClass('vjs-hidden');
  11384. } else {
  11385. self.removeClass('vjs-hidden');
  11386. }
  11387. });
  11388. };
  11389. /**
  11390. * @file volume-level.js
  11391. */
  11392. /**
  11393. * Shows volume level
  11394. *
  11395. * @extends Component
  11396. */
  11397. var VolumeLevel = function (_Component) {
  11398. inherits(VolumeLevel, _Component);
  11399. function VolumeLevel() {
  11400. classCallCheck(this, VolumeLevel);
  11401. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  11402. }
  11403. /**
  11404. * Create the `Component`'s DOM element
  11405. *
  11406. * @return {Element}
  11407. * The element that was created.
  11408. */
  11409. VolumeLevel.prototype.createEl = function createEl() {
  11410. return _Component.prototype.createEl.call(this, 'div', {
  11411. className: 'vjs-volume-level',
  11412. innerHTML: '<span class="vjs-control-text"></span>'
  11413. });
  11414. };
  11415. return VolumeLevel;
  11416. }(Component);
  11417. Component.registerComponent('VolumeLevel', VolumeLevel);
  11418. /**
  11419. * @file volume-bar.js
  11420. */
  11421. // Required children
  11422. /**
  11423. * The bar that contains the volume level and can be clicked on to adjust the level
  11424. *
  11425. * @extends Slider
  11426. */
  11427. var VolumeBar = function (_Slider) {
  11428. inherits(VolumeBar, _Slider);
  11429. /**
  11430. * Creates an instance of this class.
  11431. *
  11432. * @param {Player} player
  11433. * The `Player` that this class should be attached to.
  11434. *
  11435. * @param {Object} [options]
  11436. * The key/value store of player options.
  11437. */
  11438. function VolumeBar(player, options) {
  11439. classCallCheck(this, VolumeBar);
  11440. var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
  11441. _this.on('slideractive', _this.updateLastVolume_);
  11442. _this.on(player, 'volumechange', _this.updateARIAAttributes);
  11443. player.ready(function () {
  11444. return _this.updateARIAAttributes();
  11445. });
  11446. return _this;
  11447. }
  11448. /**
  11449. * Create the `Component`'s DOM element
  11450. *
  11451. * @return {Element}
  11452. * The element that was created.
  11453. */
  11454. VolumeBar.prototype.createEl = function createEl$$1() {
  11455. return _Slider.prototype.createEl.call(this, 'div', {
  11456. className: 'vjs-volume-bar vjs-slider-bar'
  11457. }, {
  11458. 'aria-label': this.localize('Volume Level'),
  11459. 'aria-live': 'polite'
  11460. });
  11461. };
  11462. /**
  11463. * Handle mouse down on volume bar
  11464. *
  11465. * @param {EventTarget~Event} event
  11466. * The `mousedown` event that caused this to run.
  11467. *
  11468. * @listens mousedown
  11469. */
  11470. VolumeBar.prototype.handleMouseDown = function handleMouseDown(event) {
  11471. if (!isSingleLeftClick(event)) {
  11472. return;
  11473. }
  11474. _Slider.prototype.handleMouseDown.call(this, event);
  11475. };
  11476. /**
  11477. * Handle movement events on the {@link VolumeMenuButton}.
  11478. *
  11479. * @param {EventTarget~Event} event
  11480. * The event that caused this function to run.
  11481. *
  11482. * @listens mousemove
  11483. */
  11484. VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
  11485. if (!isSingleLeftClick(event)) {
  11486. return;
  11487. }
  11488. this.checkMuted();
  11489. this.player_.volume(this.calculateDistance(event));
  11490. };
  11491. /**
  11492. * If the player is muted unmute it.
  11493. */
  11494. VolumeBar.prototype.checkMuted = function checkMuted() {
  11495. if (this.player_.muted()) {
  11496. this.player_.muted(false);
  11497. }
  11498. };
  11499. /**
  11500. * Get percent of volume level
  11501. *
  11502. * @return {number}
  11503. * Volume level percent as a decimal number.
  11504. */
  11505. VolumeBar.prototype.getPercent = function getPercent() {
  11506. if (this.player_.muted()) {
  11507. return 0;
  11508. }
  11509. return this.player_.volume();
  11510. };
  11511. /**
  11512. * Increase volume level for keyboard users
  11513. */
  11514. VolumeBar.prototype.stepForward = function stepForward() {
  11515. this.checkMuted();
  11516. this.player_.volume(this.player_.volume() + 0.1);
  11517. };
  11518. /**
  11519. * Decrease volume level for keyboard users
  11520. */
  11521. VolumeBar.prototype.stepBack = function stepBack() {
  11522. this.checkMuted();
  11523. this.player_.volume(this.player_.volume() - 0.1);
  11524. };
  11525. /**
  11526. * Update ARIA accessibility attributes
  11527. *
  11528. * @param {EventTarget~Event} [event]
  11529. * The `volumechange` event that caused this function to run.
  11530. *
  11531. * @listens Player#volumechange
  11532. */
  11533. VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) {
  11534. var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
  11535. this.el_.setAttribute('aria-valuenow', ariaValue);
  11536. this.el_.setAttribute('aria-valuetext', ariaValue + '%');
  11537. };
  11538. /**
  11539. * Returns the current value of the player volume as a percentage
  11540. *
  11541. * @private
  11542. */
  11543. VolumeBar.prototype.volumeAsPercentage_ = function volumeAsPercentage_() {
  11544. return Math.round(this.player_.volume() * 100);
  11545. };
  11546. /**
  11547. * When user starts dragging the VolumeBar, store the volume and listen for
  11548. * the end of the drag. When the drag ends, if the volume was set to zero,
  11549. * set lastVolume to the stored volume.
  11550. *
  11551. * @listens slideractive
  11552. * @private
  11553. */
  11554. VolumeBar.prototype.updateLastVolume_ = function updateLastVolume_() {
  11555. var _this2 = this;
  11556. var volumeBeforeDrag = this.player_.volume();
  11557. this.one('sliderinactive', function () {
  11558. if (_this2.player_.volume() === 0) {
  11559. _this2.player_.lastVolume_(volumeBeforeDrag);
  11560. }
  11561. });
  11562. };
  11563. return VolumeBar;
  11564. }(Slider);
  11565. /**
  11566. * Default options for the `VolumeBar`
  11567. *
  11568. * @type {Object}
  11569. * @private
  11570. */
  11571. VolumeBar.prototype.options_ = {
  11572. children: ['volumeLevel'],
  11573. barName: 'volumeLevel'
  11574. };
  11575. /**
  11576. * Call the update event for this Slider when this event happens on the player.
  11577. *
  11578. * @type {string}
  11579. */
  11580. VolumeBar.prototype.playerEvent = 'volumechange';
  11581. Component.registerComponent('VolumeBar', VolumeBar);
  11582. /**
  11583. * @file volume-control.js
  11584. */
  11585. // Required children
  11586. /**
  11587. * The component for controlling the volume level
  11588. *
  11589. * @extends Component
  11590. */
  11591. var VolumeControl = function (_Component) {
  11592. inherits(VolumeControl, _Component);
  11593. /**
  11594. * Creates an instance of this class.
  11595. *
  11596. * @param {Player} player
  11597. * The `Player` that this class should be attached to.
  11598. *
  11599. * @param {Object} [options={}]
  11600. * The key/value store of player options.
  11601. */
  11602. function VolumeControl(player) {
  11603. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  11604. classCallCheck(this, VolumeControl);
  11605. options.vertical = options.vertical || false;
  11606. // Pass the vertical option down to the VolumeBar if
  11607. // the VolumeBar is turned on.
  11608. if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
  11609. options.volumeBar = options.volumeBar || {};
  11610. options.volumeBar.vertical = options.vertical;
  11611. }
  11612. // hide this control if volume support is missing
  11613. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  11614. checkVolumeSupport(_this, player);
  11615. _this.throttledHandleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
  11616. _this.on('mousedown', _this.handleMouseDown);
  11617. _this.on('touchstart', _this.handleMouseDown);
  11618. // while the slider is active (the mouse has been pressed down and
  11619. // is dragging) or in focus we do not want to hide the VolumeBar
  11620. _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
  11621. _this.volumeBar.addClass('vjs-slider-active');
  11622. _this.addClass('vjs-slider-active');
  11623. _this.trigger('slideractive');
  11624. });
  11625. _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
  11626. _this.volumeBar.removeClass('vjs-slider-active');
  11627. _this.removeClass('vjs-slider-active');
  11628. _this.trigger('sliderinactive');
  11629. });
  11630. return _this;
  11631. }
  11632. /**
  11633. * Create the `Component`'s DOM element
  11634. *
  11635. * @return {Element}
  11636. * The element that was created.
  11637. */
  11638. VolumeControl.prototype.createEl = function createEl() {
  11639. var orientationClass = 'vjs-volume-horizontal';
  11640. if (this.options_.vertical) {
  11641. orientationClass = 'vjs-volume-vertical';
  11642. }
  11643. return _Component.prototype.createEl.call(this, 'div', {
  11644. className: 'vjs-volume-control vjs-control ' + orientationClass
  11645. });
  11646. };
  11647. /**
  11648. * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
  11649. *
  11650. * @param {EventTarget~Event} event
  11651. * `mousedown` or `touchstart` event that triggered this function
  11652. *
  11653. * @listens mousedown
  11654. * @listens touchstart
  11655. */
  11656. VolumeControl.prototype.handleMouseDown = function handleMouseDown(event) {
  11657. var doc = this.el_.ownerDocument;
  11658. this.on(doc, 'mousemove', this.throttledHandleMouseMove);
  11659. this.on(doc, 'touchmove', this.throttledHandleMouseMove);
  11660. this.on(doc, 'mouseup', this.handleMouseUp);
  11661. this.on(doc, 'touchend', this.handleMouseUp);
  11662. };
  11663. /**
  11664. * Handle `mouseup` or `touchend` events on the `VolumeControl`.
  11665. *
  11666. * @param {EventTarget~Event} event
  11667. * `mouseup` or `touchend` event that triggered this function.
  11668. *
  11669. * @listens touchend
  11670. * @listens mouseup
  11671. */
  11672. VolumeControl.prototype.handleMouseUp = function handleMouseUp(event) {
  11673. var doc = this.el_.ownerDocument;
  11674. this.off(doc, 'mousemove', this.throttledHandleMouseMove);
  11675. this.off(doc, 'touchmove', this.throttledHandleMouseMove);
  11676. this.off(doc, 'mouseup', this.handleMouseUp);
  11677. this.off(doc, 'touchend', this.handleMouseUp);
  11678. };
  11679. /**
  11680. * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
  11681. *
  11682. * @param {EventTarget~Event} event
  11683. * `mousedown` or `touchstart` event that triggered this function
  11684. *
  11685. * @listens mousedown
  11686. * @listens touchstart
  11687. */
  11688. VolumeControl.prototype.handleMouseMove = function handleMouseMove(event) {
  11689. this.volumeBar.handleMouseMove(event);
  11690. };
  11691. return VolumeControl;
  11692. }(Component);
  11693. /**
  11694. * Default options for the `VolumeControl`
  11695. *
  11696. * @type {Object}
  11697. * @private
  11698. */
  11699. VolumeControl.prototype.options_ = {
  11700. children: ['volumeBar']
  11701. };
  11702. Component.registerComponent('VolumeControl', VolumeControl);
  11703. /**
  11704. * Check if muting volume is supported and if it isn't hide the mute toggle
  11705. * button.
  11706. *
  11707. * @param {Component} self
  11708. * A reference to the mute toggle button
  11709. *
  11710. * @param {Player} player
  11711. * A reference to the player
  11712. *
  11713. * @private
  11714. */
  11715. var checkMuteSupport = function checkMuteSupport(self, player) {
  11716. // hide mute toggle button if it's not supported by the current tech
  11717. if (player.tech_ && !player.tech_.featuresMuteControl) {
  11718. self.addClass('vjs-hidden');
  11719. }
  11720. self.on(player, 'loadstart', function () {
  11721. if (!player.tech_.featuresMuteControl) {
  11722. self.addClass('vjs-hidden');
  11723. } else {
  11724. self.removeClass('vjs-hidden');
  11725. }
  11726. });
  11727. };
  11728. /**
  11729. * @file mute-toggle.js
  11730. */
  11731. /**
  11732. * A button component for muting the audio.
  11733. *
  11734. * @extends Button
  11735. */
  11736. var MuteToggle = function (_Button) {
  11737. inherits(MuteToggle, _Button);
  11738. /**
  11739. * Creates an instance of this class.
  11740. *
  11741. * @param {Player} player
  11742. * The `Player` that this class should be attached to.
  11743. *
  11744. * @param {Object} [options]
  11745. * The key/value store of player options.
  11746. */
  11747. function MuteToggle(player, options) {
  11748. classCallCheck(this, MuteToggle);
  11749. // hide this control if volume support is missing
  11750. var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
  11751. checkMuteSupport(_this, player);
  11752. _this.on(player, ['loadstart', 'volumechange'], _this.update);
  11753. return _this;
  11754. }
  11755. /**
  11756. * Builds the default DOM `className`.
  11757. *
  11758. * @return {string}
  11759. * The DOM `className` for this object.
  11760. */
  11761. MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
  11762. return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
  11763. };
  11764. /**
  11765. * This gets called when an `MuteToggle` is "clicked". See
  11766. * {@link ClickableComponent} for more detailed information on what a click can be.
  11767. *
  11768. * @param {EventTarget~Event} [event]
  11769. * The `keydown`, `tap`, or `click` event that caused this function to be
  11770. * called.
  11771. *
  11772. * @listens tap
  11773. * @listens click
  11774. */
  11775. MuteToggle.prototype.handleClick = function handleClick(event) {
  11776. var vol = this.player_.volume();
  11777. var lastVolume = this.player_.lastVolume_();
  11778. if (vol === 0) {
  11779. var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
  11780. this.player_.volume(volumeToSet);
  11781. this.player_.muted(false);
  11782. } else {
  11783. this.player_.muted(this.player_.muted() ? false : true);
  11784. }
  11785. };
  11786. /**
  11787. * Update the `MuteToggle` button based on the state of `volume` and `muted`
  11788. * on the player.
  11789. *
  11790. * @param {EventTarget~Event} [event]
  11791. * The {@link Player#loadstart} event if this function was called
  11792. * through an event.
  11793. *
  11794. * @listens Player#loadstart
  11795. * @listens Player#volumechange
  11796. */
  11797. MuteToggle.prototype.update = function update(event) {
  11798. this.updateIcon_();
  11799. this.updateControlText_();
  11800. };
  11801. /**
  11802. * Update the appearance of the `MuteToggle` icon.
  11803. *
  11804. * Possible states (given `level` variable below):
  11805. * - 0: crossed out
  11806. * - 1: zero bars of volume
  11807. * - 2: one bar of volume
  11808. * - 3: two bars of volume
  11809. *
  11810. * @private
  11811. */
  11812. MuteToggle.prototype.updateIcon_ = function updateIcon_() {
  11813. var vol = this.player_.volume();
  11814. var level = 3;
  11815. // in iOS when a player is loaded with muted attribute
  11816. // and volume is changed with a native mute button
  11817. // we want to make sure muted state is updated
  11818. if (IS_IOS) {
  11819. this.player_.muted(this.player_.tech_.el_.muted);
  11820. }
  11821. if (vol === 0 || this.player_.muted()) {
  11822. level = 0;
  11823. } else if (vol < 0.33) {
  11824. level = 1;
  11825. } else if (vol < 0.67) {
  11826. level = 2;
  11827. }
  11828. // TODO improve muted icon classes
  11829. for (var i = 0; i < 4; i++) {
  11830. removeClass(this.el_, 'vjs-vol-' + i);
  11831. }
  11832. addClass(this.el_, 'vjs-vol-' + level);
  11833. };
  11834. /**
  11835. * If `muted` has changed on the player, update the control text
  11836. * (`title` attribute on `vjs-mute-control` element and content of
  11837. * `vjs-control-text` element).
  11838. *
  11839. * @private
  11840. */
  11841. MuteToggle.prototype.updateControlText_ = function updateControlText_() {
  11842. var soundOff = this.player_.muted() || this.player_.volume() === 0;
  11843. var text = soundOff ? 'Unmute' : 'Mute';
  11844. if (this.controlText() !== text) {
  11845. this.controlText(text);
  11846. }
  11847. };
  11848. return MuteToggle;
  11849. }(Button);
  11850. /**
  11851. * The text that should display over the `MuteToggle`s controls. Added for localization.
  11852. *
  11853. * @type {string}
  11854. * @private
  11855. */
  11856. MuteToggle.prototype.controlText_ = 'Mute';
  11857. Component.registerComponent('MuteToggle', MuteToggle);
  11858. /**
  11859. * @file volume-control.js
  11860. */
  11861. // Required children
  11862. /**
  11863. * A Component to contain the MuteToggle and VolumeControl so that
  11864. * they can work together.
  11865. *
  11866. * @extends Component
  11867. */
  11868. var VolumePanel = function (_Component) {
  11869. inherits(VolumePanel, _Component);
  11870. /**
  11871. * Creates an instance of this class.
  11872. *
  11873. * @param {Player} player
  11874. * The `Player` that this class should be attached to.
  11875. *
  11876. * @param {Object} [options={}]
  11877. * The key/value store of player options.
  11878. */
  11879. function VolumePanel(player) {
  11880. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  11881. classCallCheck(this, VolumePanel);
  11882. if (typeof options.inline !== 'undefined') {
  11883. options.inline = options.inline;
  11884. } else {
  11885. options.inline = true;
  11886. }
  11887. // pass the inline option down to the VolumeControl as vertical if
  11888. // the VolumeControl is on.
  11889. if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
  11890. options.volumeControl = options.volumeControl || {};
  11891. options.volumeControl.vertical = !options.inline;
  11892. }
  11893. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  11894. _this.on(player, ['loadstart'], _this.volumePanelState_);
  11895. // while the slider is active (the mouse has been pressed down and
  11896. // is dragging) we do not want to hide the VolumeBar
  11897. _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
  11898. _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
  11899. return _this;
  11900. }
  11901. /**
  11902. * Add vjs-slider-active class to the VolumePanel
  11903. *
  11904. * @listens VolumeControl#slideractive
  11905. * @private
  11906. */
  11907. VolumePanel.prototype.sliderActive_ = function sliderActive_() {
  11908. this.addClass('vjs-slider-active');
  11909. };
  11910. /**
  11911. * Removes vjs-slider-active class to the VolumePanel
  11912. *
  11913. * @listens VolumeControl#sliderinactive
  11914. * @private
  11915. */
  11916. VolumePanel.prototype.sliderInactive_ = function sliderInactive_() {
  11917. this.removeClass('vjs-slider-active');
  11918. };
  11919. /**
  11920. * Adds vjs-hidden or vjs-mute-toggle-only to the VolumePanel
  11921. * depending on MuteToggle and VolumeControl state
  11922. *
  11923. * @listens Player#loadstart
  11924. * @private
  11925. */
  11926. VolumePanel.prototype.volumePanelState_ = function volumePanelState_() {
  11927. // hide volume panel if neither volume control or mute toggle
  11928. // are displayed
  11929. if (this.volumeControl.hasClass('vjs-hidden') && this.muteToggle.hasClass('vjs-hidden')) {
  11930. this.addClass('vjs-hidden');
  11931. }
  11932. // if only mute toggle is visible we don't want
  11933. // volume panel expanding when hovered or active
  11934. if (this.volumeControl.hasClass('vjs-hidden') && !this.muteToggle.hasClass('vjs-hidden')) {
  11935. this.addClass('vjs-mute-toggle-only');
  11936. }
  11937. };
  11938. /**
  11939. * Create the `Component`'s DOM element
  11940. *
  11941. * @return {Element}
  11942. * The element that was created.
  11943. */
  11944. VolumePanel.prototype.createEl = function createEl() {
  11945. var orientationClass = 'vjs-volume-panel-horizontal';
  11946. if (!this.options_.inline) {
  11947. orientationClass = 'vjs-volume-panel-vertical';
  11948. }
  11949. return _Component.prototype.createEl.call(this, 'div', {
  11950. className: 'vjs-volume-panel vjs-control ' + orientationClass
  11951. });
  11952. };
  11953. return VolumePanel;
  11954. }(Component);
  11955. /**
  11956. * Default options for the `VolumeControl`
  11957. *
  11958. * @type {Object}
  11959. * @private
  11960. */
  11961. VolumePanel.prototype.options_ = {
  11962. children: ['muteToggle', 'volumeControl']
  11963. };
  11964. Component.registerComponent('VolumePanel', VolumePanel);
  11965. /**
  11966. * @file menu.js
  11967. */
  11968. /**
  11969. * The Menu component is used to build popup menus, including subtitle and
  11970. * captions selection menus.
  11971. *
  11972. * @extends Component
  11973. */
  11974. var Menu = function (_Component) {
  11975. inherits(Menu, _Component);
  11976. /**
  11977. * Create an instance of this class.
  11978. *
  11979. * @param {Player} player
  11980. * the player that this component should attach to
  11981. *
  11982. * @param {Object} [options]
  11983. * Object of option names and values
  11984. *
  11985. */
  11986. function Menu(player, options) {
  11987. classCallCheck(this, Menu);
  11988. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  11989. if (options) {
  11990. _this.menuButton_ = options.menuButton;
  11991. }
  11992. _this.focusedChild_ = -1;
  11993. _this.on('keydown', _this.handleKeyPress);
  11994. return _this;
  11995. }
  11996. /**
  11997. * Add a {@link MenuItem} to the menu.
  11998. *
  11999. * @param {Object|string} component
  12000. * The name or instance of the `MenuItem` to add.
  12001. *
  12002. */
  12003. Menu.prototype.addItem = function addItem(component) {
  12004. this.addChild(component);
  12005. component.on('click', bind(this, function (event) {
  12006. // Unpress the associated MenuButton, and move focus back to it
  12007. if (this.menuButton_) {
  12008. this.menuButton_.unpressButton();
  12009. // don't focus menu button if item is a caption settings item
  12010. // because focus will move elsewhere and it logs an error on IE8
  12011. if (component.name() !== 'CaptionSettingsMenuItem') {
  12012. this.menuButton_.focus();
  12013. }
  12014. }
  12015. }));
  12016. };
  12017. /**
  12018. * Create the `Menu`s DOM element.
  12019. *
  12020. * @return {Element}
  12021. * the element that was created
  12022. */
  12023. Menu.prototype.createEl = function createEl$$1() {
  12024. var contentElType = this.options_.contentElType || 'ul';
  12025. this.contentEl_ = createEl(contentElType, {
  12026. className: 'vjs-menu-content'
  12027. });
  12028. this.contentEl_.setAttribute('role', 'menu');
  12029. var el = _Component.prototype.createEl.call(this, 'div', {
  12030. append: this.contentEl_,
  12031. className: 'vjs-menu'
  12032. });
  12033. el.appendChild(this.contentEl_);
  12034. // Prevent clicks from bubbling up. Needed for Menu Buttons,
  12035. // where a click on the parent is significant
  12036. on(el, 'click', function (event) {
  12037. event.preventDefault();
  12038. event.stopImmediatePropagation();
  12039. });
  12040. return el;
  12041. };
  12042. Menu.prototype.dispose = function dispose() {
  12043. this.contentEl_ = null;
  12044. _Component.prototype.dispose.call(this);
  12045. };
  12046. /**
  12047. * Handle a `keydown` event on this menu. This listener is added in the constructor.
  12048. *
  12049. * @param {EventTarget~Event} event
  12050. * A `keydown` event that happened on the menu.
  12051. *
  12052. * @listens keydown
  12053. */
  12054. Menu.prototype.handleKeyPress = function handleKeyPress(event) {
  12055. // Left and Down Arrows
  12056. if (event.which === 37 || event.which === 40) {
  12057. event.preventDefault();
  12058. this.stepForward();
  12059. // Up and Right Arrows
  12060. } else if (event.which === 38 || event.which === 39) {
  12061. event.preventDefault();
  12062. this.stepBack();
  12063. }
  12064. };
  12065. /**
  12066. * Move to next (lower) menu item for keyboard users.
  12067. */
  12068. Menu.prototype.stepForward = function stepForward() {
  12069. var stepChild = 0;
  12070. if (this.focusedChild_ !== undefined) {
  12071. stepChild = this.focusedChild_ + 1;
  12072. }
  12073. this.focus(stepChild);
  12074. };
  12075. /**
  12076. * Move to previous (higher) menu item for keyboard users.
  12077. */
  12078. Menu.prototype.stepBack = function stepBack() {
  12079. var stepChild = 0;
  12080. if (this.focusedChild_ !== undefined) {
  12081. stepChild = this.focusedChild_ - 1;
  12082. }
  12083. this.focus(stepChild);
  12084. };
  12085. /**
  12086. * Set focus on a {@link MenuItem} in the `Menu`.
  12087. *
  12088. * @param {Object|string} [item=0]
  12089. * Index of child item set focus on.
  12090. */
  12091. Menu.prototype.focus = function focus() {
  12092. var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
  12093. var children = this.children().slice();
  12094. var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
  12095. if (haveTitle) {
  12096. children.shift();
  12097. }
  12098. if (children.length > 0) {
  12099. if (item < 0) {
  12100. item = 0;
  12101. } else if (item >= children.length) {
  12102. item = children.length - 1;
  12103. }
  12104. this.focusedChild_ = item;
  12105. children[item].el_.focus();
  12106. }
  12107. };
  12108. return Menu;
  12109. }(Component);
  12110. Component.registerComponent('Menu', Menu);
  12111. /**
  12112. * @file menu-button.js
  12113. */
  12114. /**
  12115. * A `MenuButton` class for any popup {@link Menu}.
  12116. *
  12117. * @extends Component
  12118. */
  12119. var MenuButton = function (_Component) {
  12120. inherits(MenuButton, _Component);
  12121. /**
  12122. * Creates an instance of this class.
  12123. *
  12124. * @param {Player} player
  12125. * The `Player` that this class should be attached to.
  12126. *
  12127. * @param {Object} [options={}]
  12128. * The key/value store of player options.
  12129. */
  12130. function MenuButton(player) {
  12131. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  12132. classCallCheck(this, MenuButton);
  12133. var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
  12134. _this.menuButton_ = new Button(player, options);
  12135. _this.menuButton_.controlText(_this.controlText_);
  12136. _this.menuButton_.el_.setAttribute('aria-haspopup', 'true');
  12137. // Add buildCSSClass values to the button, not the wrapper
  12138. var buttonClass = Button.prototype.buildCSSClass();
  12139. _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
  12140. _this.menuButton_.removeClass('vjs-control');
  12141. _this.addChild(_this.menuButton_);
  12142. _this.update();
  12143. _this.enabled_ = true;
  12144. _this.on(_this.menuButton_, 'tap', _this.handleClick);
  12145. _this.on(_this.menuButton_, 'click', _this.handleClick);
  12146. _this.on(_this.menuButton_, 'focus', _this.handleFocus);
  12147. _this.on(_this.menuButton_, 'blur', _this.handleBlur);
  12148. _this.on('keydown', _this.handleSubmenuKeyPress);
  12149. return _this;
  12150. }
  12151. /**
  12152. * Update the menu based on the current state of its items.
  12153. */
  12154. MenuButton.prototype.update = function update() {
  12155. var menu = this.createMenu();
  12156. if (this.menu) {
  12157. this.menu.dispose();
  12158. this.removeChild(this.menu);
  12159. }
  12160. this.menu = menu;
  12161. this.addChild(menu);
  12162. /**
  12163. * Track the state of the menu button
  12164. *
  12165. * @type {Boolean}
  12166. * @private
  12167. */
  12168. this.buttonPressed_ = false;
  12169. this.menuButton_.el_.setAttribute('aria-expanded', 'false');
  12170. if (this.items && this.items.length <= this.hideThreshold_) {
  12171. this.hide();
  12172. } else {
  12173. this.show();
  12174. }
  12175. };
  12176. /**
  12177. * Create the menu and add all items to it.
  12178. *
  12179. * @return {Menu}
  12180. * The constructed menu
  12181. */
  12182. MenuButton.prototype.createMenu = function createMenu() {
  12183. var menu = new Menu(this.player_, { menuButton: this });
  12184. /**
  12185. * Hide the menu if the number of items is less than or equal to this threshold. This defaults
  12186. * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
  12187. * it here because every time we run `createMenu` we need to reset the value.
  12188. *
  12189. * @protected
  12190. * @type {Number}
  12191. */
  12192. this.hideThreshold_ = 0;
  12193. // Add a title list item to the top
  12194. if (this.options_.title) {
  12195. var title = createEl('li', {
  12196. className: 'vjs-menu-title',
  12197. innerHTML: toTitleCase(this.options_.title),
  12198. tabIndex: -1
  12199. });
  12200. this.hideThreshold_ += 1;
  12201. menu.children_.unshift(title);
  12202. prependTo(title, menu.contentEl());
  12203. }
  12204. this.items = this.createItems();
  12205. if (this.items) {
  12206. // Add menu items to the menu
  12207. for (var i = 0; i < this.items.length; i++) {
  12208. menu.addItem(this.items[i]);
  12209. }
  12210. }
  12211. return menu;
  12212. };
  12213. /**
  12214. * Create the list of menu items. Specific to each subclass.
  12215. *
  12216. * @abstract
  12217. */
  12218. MenuButton.prototype.createItems = function createItems() {};
  12219. /**
  12220. * Create the `MenuButtons`s DOM element.
  12221. *
  12222. * @return {Element}
  12223. * The element that gets created.
  12224. */
  12225. MenuButton.prototype.createEl = function createEl$$1() {
  12226. return _Component.prototype.createEl.call(this, 'div', {
  12227. className: this.buildWrapperCSSClass()
  12228. }, {});
  12229. };
  12230. /**
  12231. * Allow sub components to stack CSS class names for the wrapper element
  12232. *
  12233. * @return {string}
  12234. * The constructed wrapper DOM `className`
  12235. */
  12236. MenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  12237. var menuButtonClass = 'vjs-menu-button';
  12238. // If the inline option is passed, we want to use different styles altogether.
  12239. if (this.options_.inline === true) {
  12240. menuButtonClass += '-inline';
  12241. } else {
  12242. menuButtonClass += '-popup';
  12243. }
  12244. // TODO: Fix the CSS so that this isn't necessary
  12245. var buttonClass = Button.prototype.buildCSSClass();
  12246. return 'vjs-menu-button ' + menuButtonClass + ' ' + buttonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
  12247. };
  12248. /**
  12249. * Builds the default DOM `className`.
  12250. *
  12251. * @return {string}
  12252. * The DOM `className` for this object.
  12253. */
  12254. MenuButton.prototype.buildCSSClass = function buildCSSClass() {
  12255. var menuButtonClass = 'vjs-menu-button';
  12256. // If the inline option is passed, we want to use different styles altogether.
  12257. if (this.options_.inline === true) {
  12258. menuButtonClass += '-inline';
  12259. } else {
  12260. menuButtonClass += '-popup';
  12261. }
  12262. return 'vjs-menu-button ' + menuButtonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
  12263. };
  12264. /**
  12265. * Get or set the localized control text that will be used for accessibility.
  12266. *
  12267. * > NOTE: This will come from the internal `menuButton_` element.
  12268. *
  12269. * @param {string} [text]
  12270. * Control text for element.
  12271. *
  12272. * @param {Element} [el=this.menuButton_.el()]
  12273. * Element to set the title on.
  12274. *
  12275. * @return {string}
  12276. * - The control text when getting
  12277. */
  12278. MenuButton.prototype.controlText = function controlText(text) {
  12279. var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.menuButton_.el();
  12280. return this.menuButton_.controlText(text, el);
  12281. };
  12282. /**
  12283. * Handle a click on a `MenuButton`.
  12284. * See {@link ClickableComponent#handleClick} for instances where this is called.
  12285. *
  12286. * @param {EventTarget~Event} event
  12287. * The `keydown`, `tap`, or `click` event that caused this function to be
  12288. * called.
  12289. *
  12290. * @listens tap
  12291. * @listens click
  12292. */
  12293. MenuButton.prototype.handleClick = function handleClick(event) {
  12294. // When you click the button it adds focus, which will show the menu.
  12295. // So we'll remove focus when the mouse leaves the button. Focus is needed
  12296. // for tab navigation.
  12297. this.one(this.menu.contentEl(), 'mouseleave', bind(this, function (e) {
  12298. this.unpressButton();
  12299. this.el_.blur();
  12300. }));
  12301. if (this.buttonPressed_) {
  12302. this.unpressButton();
  12303. } else {
  12304. this.pressButton();
  12305. }
  12306. };
  12307. /**
  12308. * Set the focus to the actual button, not to this element
  12309. */
  12310. MenuButton.prototype.focus = function focus() {
  12311. this.menuButton_.focus();
  12312. };
  12313. /**
  12314. * Remove the focus from the actual button, not this element
  12315. */
  12316. MenuButton.prototype.blur = function blur() {
  12317. this.menuButton_.blur();
  12318. };
  12319. /**
  12320. * This gets called when a `MenuButton` gains focus via a `focus` event.
  12321. * Turns on listening for `keydown` events. When they happen it
  12322. * calls `this.handleKeyPress`.
  12323. *
  12324. * @param {EventTarget~Event} event
  12325. * The `focus` event that caused this function to be called.
  12326. *
  12327. * @listens focus
  12328. */
  12329. MenuButton.prototype.handleFocus = function handleFocus() {
  12330. on(document_1, 'keydown', bind(this, this.handleKeyPress));
  12331. };
  12332. /**
  12333. * Called when a `MenuButton` loses focus. Turns off the listener for
  12334. * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
  12335. *
  12336. * @param {EventTarget~Event} event
  12337. * The `blur` event that caused this function to be called.
  12338. *
  12339. * @listens blur
  12340. */
  12341. MenuButton.prototype.handleBlur = function handleBlur() {
  12342. off(document_1, 'keydown', bind(this, this.handleKeyPress));
  12343. };
  12344. /**
  12345. * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
  12346. * {@link ClickableComponent#handleKeyPress} for instances where this is called.
  12347. *
  12348. * @param {EventTarget~Event} event
  12349. * The `keydown` event that caused this function to be called.
  12350. *
  12351. * @listens keydown
  12352. */
  12353. MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {
  12354. // Escape (27) key or Tab (9) key unpress the 'button'
  12355. if (event.which === 27 || event.which === 9) {
  12356. if (this.buttonPressed_) {
  12357. this.unpressButton();
  12358. }
  12359. // Don't preventDefault for Tab key - we still want to lose focus
  12360. if (event.which !== 9) {
  12361. event.preventDefault();
  12362. // Set focus back to the menu button's button
  12363. this.menuButton_.el_.focus();
  12364. }
  12365. // Up (38) key or Down (40) key press the 'button'
  12366. } else if (event.which === 38 || event.which === 40) {
  12367. if (!this.buttonPressed_) {
  12368. this.pressButton();
  12369. event.preventDefault();
  12370. }
  12371. }
  12372. };
  12373. /**
  12374. * Handle a `keydown` event on a sub-menu. The listener for this is added in
  12375. * the constructor.
  12376. *
  12377. * @param {EventTarget~Event} event
  12378. * Key press event
  12379. *
  12380. * @listens keydown
  12381. */
  12382. MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
  12383. // Escape (27) key or Tab (9) key unpress the 'button'
  12384. if (event.which === 27 || event.which === 9) {
  12385. if (this.buttonPressed_) {
  12386. this.unpressButton();
  12387. }
  12388. // Don't preventDefault for Tab key - we still want to lose focus
  12389. if (event.which !== 9) {
  12390. event.preventDefault();
  12391. // Set focus back to the menu button's button
  12392. this.menuButton_.el_.focus();
  12393. }
  12394. }
  12395. };
  12396. /**
  12397. * Put the current `MenuButton` into a pressed state.
  12398. */
  12399. MenuButton.prototype.pressButton = function pressButton() {
  12400. if (this.enabled_) {
  12401. this.buttonPressed_ = true;
  12402. this.menu.lockShowing();
  12403. this.menuButton_.el_.setAttribute('aria-expanded', 'true');
  12404. // set the focus into the submenu, except on iOS where it is resulting in
  12405. // undesired scrolling behavior when the player is in an iframe
  12406. if (IS_IOS && isInFrame()) {
  12407. // Return early so that the menu isn't focused
  12408. return;
  12409. }
  12410. this.menu.focus();
  12411. }
  12412. };
  12413. /**
  12414. * Take the current `MenuButton` out of a pressed state.
  12415. */
  12416. MenuButton.prototype.unpressButton = function unpressButton() {
  12417. if (this.enabled_) {
  12418. this.buttonPressed_ = false;
  12419. this.menu.unlockShowing();
  12420. this.menuButton_.el_.setAttribute('aria-expanded', 'false');
  12421. }
  12422. };
  12423. /**
  12424. * Disable the `MenuButton`. Don't allow it to be clicked.
  12425. */
  12426. MenuButton.prototype.disable = function disable() {
  12427. this.unpressButton();
  12428. this.enabled_ = false;
  12429. this.addClass('vjs-disabled');
  12430. this.menuButton_.disable();
  12431. };
  12432. /**
  12433. * Enable the `MenuButton`. Allow it to be clicked.
  12434. */
  12435. MenuButton.prototype.enable = function enable() {
  12436. this.enabled_ = true;
  12437. this.removeClass('vjs-disabled');
  12438. this.menuButton_.enable();
  12439. };
  12440. return MenuButton;
  12441. }(Component);
  12442. Component.registerComponent('MenuButton', MenuButton);
  12443. /**
  12444. * @file track-button.js
  12445. */
  12446. /**
  12447. * The base class for buttons that toggle specific track types (e.g. subtitles).
  12448. *
  12449. * @extends MenuButton
  12450. */
  12451. var TrackButton = function (_MenuButton) {
  12452. inherits(TrackButton, _MenuButton);
  12453. /**
  12454. * Creates an instance of this class.
  12455. *
  12456. * @param {Player} player
  12457. * The `Player` that this class should be attached to.
  12458. *
  12459. * @param {Object} [options]
  12460. * The key/value store of player options.
  12461. */
  12462. function TrackButton(player, options) {
  12463. classCallCheck(this, TrackButton);
  12464. var tracks = options.tracks;
  12465. var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
  12466. if (_this.items.length <= 1) {
  12467. _this.hide();
  12468. }
  12469. if (!tracks) {
  12470. return possibleConstructorReturn(_this);
  12471. }
  12472. var updateHandler = bind(_this, _this.update);
  12473. tracks.addEventListener('removetrack', updateHandler);
  12474. tracks.addEventListener('addtrack', updateHandler);
  12475. _this.player_.on('ready', updateHandler);
  12476. _this.player_.on('dispose', function () {
  12477. tracks.removeEventListener('removetrack', updateHandler);
  12478. tracks.removeEventListener('addtrack', updateHandler);
  12479. });
  12480. return _this;
  12481. }
  12482. return TrackButton;
  12483. }(MenuButton);
  12484. Component.registerComponent('TrackButton', TrackButton);
  12485. /**
  12486. * @file menu-item.js
  12487. */
  12488. /**
  12489. * The component for a menu item. `<li>`
  12490. *
  12491. * @extends ClickableComponent
  12492. */
  12493. var MenuItem = function (_ClickableComponent) {
  12494. inherits(MenuItem, _ClickableComponent);
  12495. /**
  12496. * Creates an instance of the this class.
  12497. *
  12498. * @param {Player} player
  12499. * The `Player` that this class should be attached to.
  12500. *
  12501. * @param {Object} [options={}]
  12502. * The key/value store of player options.
  12503. *
  12504. */
  12505. function MenuItem(player, options) {
  12506. classCallCheck(this, MenuItem);
  12507. var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
  12508. _this.selectable = options.selectable;
  12509. _this.isSelected_ = options.selected || false;
  12510. _this.multiSelectable = options.multiSelectable;
  12511. _this.selected(_this.isSelected_);
  12512. if (_this.selectable) {
  12513. if (_this.multiSelectable) {
  12514. _this.el_.setAttribute('role', 'menuitemcheckbox');
  12515. } else {
  12516. _this.el_.setAttribute('role', 'menuitemradio');
  12517. }
  12518. } else {
  12519. _this.el_.setAttribute('role', 'menuitem');
  12520. }
  12521. return _this;
  12522. }
  12523. /**
  12524. * Create the `MenuItem's DOM element
  12525. *
  12526. * @param {string} [type=li]
  12527. * Element's node type, not actually used, always set to `li`.
  12528. *
  12529. * @param {Object} [props={}]
  12530. * An object of properties that should be set on the element
  12531. *
  12532. * @param {Object} [attrs={}]
  12533. * An object of attributes that should be set on the element
  12534. *
  12535. * @return {Element}
  12536. * The element that gets created.
  12537. */
  12538. MenuItem.prototype.createEl = function createEl(type, props, attrs) {
  12539. // The control is textual, not just an icon
  12540. this.nonIconControl = true;
  12541. return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
  12542. className: 'vjs-menu-item',
  12543. innerHTML: '<span class="vjs-menu-item-text">' + this.localize(this.options_.label) + '</span>',
  12544. tabIndex: -1
  12545. }, props), attrs);
  12546. };
  12547. /**
  12548. * Any click on a `MenuItem` puts it into the selected state.
  12549. * See {@link ClickableComponent#handleClick} for instances where this is called.
  12550. *
  12551. * @param {EventTarget~Event} event
  12552. * The `keydown`, `tap`, or `click` event that caused this function to be
  12553. * called.
  12554. *
  12555. * @listens tap
  12556. * @listens click
  12557. */
  12558. MenuItem.prototype.handleClick = function handleClick(event) {
  12559. this.selected(true);
  12560. };
  12561. /**
  12562. * Set the state for this menu item as selected or not.
  12563. *
  12564. * @param {boolean} selected
  12565. * if the menu item is selected or not
  12566. */
  12567. MenuItem.prototype.selected = function selected(_selected) {
  12568. if (this.selectable) {
  12569. if (_selected) {
  12570. this.addClass('vjs-selected');
  12571. this.el_.setAttribute('aria-checked', 'true');
  12572. // aria-checked isn't fully supported by browsers/screen readers,
  12573. // so indicate selected state to screen reader in the control text.
  12574. this.controlText(', selected');
  12575. this.isSelected_ = true;
  12576. } else {
  12577. this.removeClass('vjs-selected');
  12578. this.el_.setAttribute('aria-checked', 'false');
  12579. // Indicate un-selected state to screen reader
  12580. this.controlText('');
  12581. this.isSelected_ = false;
  12582. }
  12583. }
  12584. };
  12585. return MenuItem;
  12586. }(ClickableComponent);
  12587. Component.registerComponent('MenuItem', MenuItem);
  12588. /**
  12589. * @file text-track-menu-item.js
  12590. */
  12591. /**
  12592. * The specific menu item type for selecting a language within a text track kind
  12593. *
  12594. * @extends MenuItem
  12595. */
  12596. var TextTrackMenuItem = function (_MenuItem) {
  12597. inherits(TextTrackMenuItem, _MenuItem);
  12598. /**
  12599. * Creates an instance of this class.
  12600. *
  12601. * @param {Player} player
  12602. * The `Player` that this class should be attached to.
  12603. *
  12604. * @param {Object} [options]
  12605. * The key/value store of player options.
  12606. */
  12607. function TextTrackMenuItem(player, options) {
  12608. classCallCheck(this, TextTrackMenuItem);
  12609. var track = options.track;
  12610. var tracks = player.textTracks();
  12611. // Modify options for parent MenuItem class's init.
  12612. options.label = track.label || track.language || 'Unknown';
  12613. options.selected = track.mode === 'showing';
  12614. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  12615. _this.track = track;
  12616. var changeHandler = function changeHandler() {
  12617. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  12618. args[_key] = arguments[_key];
  12619. }
  12620. _this.handleTracksChange.apply(_this, args);
  12621. };
  12622. var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
  12623. for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
  12624. args[_key2] = arguments[_key2];
  12625. }
  12626. _this.handleSelectedLanguageChange.apply(_this, args);
  12627. };
  12628. player.on(['loadstart', 'texttrackchange'], changeHandler);
  12629. tracks.addEventListener('change', changeHandler);
  12630. tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
  12631. _this.on('dispose', function () {
  12632. player.off(['loadstart', 'texttrackchange'], changeHandler);
  12633. tracks.removeEventListener('change', changeHandler);
  12634. tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
  12635. });
  12636. // iOS7 doesn't dispatch change events to TextTrackLists when an
  12637. // associated track's mode changes. Without something like
  12638. // Object.observe() (also not present on iOS7), it's not
  12639. // possible to detect changes to the mode attribute and polyfill
  12640. // the change event. As a poor substitute, we manually dispatch
  12641. // change events whenever the controls modify the mode.
  12642. if (tracks.onchange === undefined) {
  12643. var event = void 0;
  12644. _this.on(['tap', 'click'], function () {
  12645. if (_typeof(window_1.Event) !== 'object') {
  12646. // Android 2.3 throws an Illegal Constructor error for window.Event
  12647. try {
  12648. event = new window_1.Event('change');
  12649. } catch (err) {
  12650. // continue regardless of error
  12651. }
  12652. }
  12653. if (!event) {
  12654. event = document_1.createEvent('Event');
  12655. event.initEvent('change', true, true);
  12656. }
  12657. tracks.dispatchEvent(event);
  12658. });
  12659. }
  12660. // set the default state based on current tracks
  12661. _this.handleTracksChange();
  12662. return _this;
  12663. }
  12664. /**
  12665. * This gets called when an `TextTrackMenuItem` is "clicked". See
  12666. * {@link ClickableComponent} for more detailed information on what a click can be.
  12667. *
  12668. * @param {EventTarget~Event} event
  12669. * The `keydown`, `tap`, or `click` event that caused this function to be
  12670. * called.
  12671. *
  12672. * @listens tap
  12673. * @listens click
  12674. */
  12675. TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
  12676. var kind = this.track.kind;
  12677. var kinds = this.track.kinds;
  12678. var tracks = this.player_.textTracks();
  12679. if (!kinds) {
  12680. kinds = [kind];
  12681. }
  12682. _MenuItem.prototype.handleClick.call(this, event);
  12683. if (!tracks) {
  12684. return;
  12685. }
  12686. for (var i = 0; i < tracks.length; i++) {
  12687. var track = tracks[i];
  12688. if (track === this.track && kinds.indexOf(track.kind) > -1) {
  12689. if (track.mode !== 'showing') {
  12690. track.mode = 'showing';
  12691. }
  12692. } else if (track.mode !== 'disabled') {
  12693. track.mode = 'disabled';
  12694. }
  12695. }
  12696. };
  12697. /**
  12698. * Handle text track list change
  12699. *
  12700. * @param {EventTarget~Event} event
  12701. * The `change` event that caused this function to be called.
  12702. *
  12703. * @listens TextTrackList#change
  12704. */
  12705. TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  12706. var shouldBeSelected = this.track.mode === 'showing';
  12707. // Prevent redundant selected() calls because they may cause
  12708. // screen readers to read the appended control text unnecessarily
  12709. if (shouldBeSelected !== this.isSelected_) {
  12710. this.selected(shouldBeSelected);
  12711. }
  12712. };
  12713. TextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
  12714. if (this.track.mode === 'showing') {
  12715. var selectedLanguage = this.player_.cache_.selectedLanguage;
  12716. // Don't replace the kind of track across the same language
  12717. if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
  12718. return;
  12719. }
  12720. this.player_.cache_.selectedLanguage = {
  12721. enabled: true,
  12722. language: this.track.language,
  12723. kind: this.track.kind
  12724. };
  12725. }
  12726. };
  12727. TextTrackMenuItem.prototype.dispose = function dispose() {
  12728. // remove reference to track object on dispose
  12729. this.track = null;
  12730. _MenuItem.prototype.dispose.call(this);
  12731. };
  12732. return TextTrackMenuItem;
  12733. }(MenuItem);
  12734. Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
  12735. /**
  12736. * @file off-text-track-menu-item.js
  12737. */
  12738. /**
  12739. * A special menu item for turning of a specific type of text track
  12740. *
  12741. * @extends TextTrackMenuItem
  12742. */
  12743. var OffTextTrackMenuItem = function (_TextTrackMenuItem) {
  12744. inherits(OffTextTrackMenuItem, _TextTrackMenuItem);
  12745. /**
  12746. * Creates an instance of this class.
  12747. *
  12748. * @param {Player} player
  12749. * The `Player` that this class should be attached to.
  12750. *
  12751. * @param {Object} [options]
  12752. * The key/value store of player options.
  12753. */
  12754. function OffTextTrackMenuItem(player, options) {
  12755. classCallCheck(this, OffTextTrackMenuItem);
  12756. // Create pseudo track info
  12757. // Requires options['kind']
  12758. options.track = {
  12759. player: player,
  12760. kind: options.kind,
  12761. kinds: options.kinds,
  12762. 'default': false,
  12763. mode: 'disabled'
  12764. };
  12765. if (!options.kinds) {
  12766. options.kinds = [options.kind];
  12767. }
  12768. if (options.label) {
  12769. options.track.label = options.label;
  12770. } else {
  12771. options.track.label = options.kinds.join(' and ') + ' off';
  12772. }
  12773. // MenuItem is selectable
  12774. options.selectable = true;
  12775. // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
  12776. options.multiSelectable = false;
  12777. return possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
  12778. }
  12779. /**
  12780. * Handle text track change
  12781. *
  12782. * @param {EventTarget~Event} event
  12783. * The event that caused this function to run
  12784. */
  12785. OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  12786. var tracks = this.player().textTracks();
  12787. var shouldBeSelected = true;
  12788. for (var i = 0, l = tracks.length; i < l; i++) {
  12789. var track = tracks[i];
  12790. if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
  12791. shouldBeSelected = false;
  12792. break;
  12793. }
  12794. }
  12795. // Prevent redundant selected() calls because they may cause
  12796. // screen readers to read the appended control text unnecessarily
  12797. if (shouldBeSelected !== this.isSelected_) {
  12798. this.selected(shouldBeSelected);
  12799. }
  12800. };
  12801. OffTextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
  12802. var tracks = this.player().textTracks();
  12803. var allHidden = true;
  12804. for (var i = 0, l = tracks.length; i < l; i++) {
  12805. var track = tracks[i];
  12806. if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
  12807. allHidden = false;
  12808. break;
  12809. }
  12810. }
  12811. if (allHidden) {
  12812. this.player_.cache_.selectedLanguage = {
  12813. enabled: false
  12814. };
  12815. }
  12816. };
  12817. return OffTextTrackMenuItem;
  12818. }(TextTrackMenuItem);
  12819. Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
  12820. /**
  12821. * @file text-track-button.js
  12822. */
  12823. /**
  12824. * The base class for buttons that toggle specific text track types (e.g. subtitles)
  12825. *
  12826. * @extends MenuButton
  12827. */
  12828. var TextTrackButton = function (_TrackButton) {
  12829. inherits(TextTrackButton, _TrackButton);
  12830. /**
  12831. * Creates an instance of this class.
  12832. *
  12833. * @param {Player} player
  12834. * The `Player` that this class should be attached to.
  12835. *
  12836. * @param {Object} [options={}]
  12837. * The key/value store of player options.
  12838. */
  12839. function TextTrackButton(player) {
  12840. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  12841. classCallCheck(this, TextTrackButton);
  12842. options.tracks = player.textTracks();
  12843. return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
  12844. }
  12845. /**
  12846. * Create a menu item for each text track
  12847. *
  12848. * @param {TextTrackMenuItem[]} [items=[]]
  12849. * Existing array of items to use during creation
  12850. *
  12851. * @return {TextTrackMenuItem[]}
  12852. * Array of menu items that were created
  12853. */
  12854. TextTrackButton.prototype.createItems = function createItems() {
  12855. var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  12856. var TrackMenuItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TextTrackMenuItem;
  12857. // Label is an overide for the [track] off label
  12858. // USed to localise captions/subtitles
  12859. var label = void 0;
  12860. if (this.label_) {
  12861. label = this.label_ + ' off';
  12862. }
  12863. // Add an OFF menu item to turn all tracks off
  12864. items.push(new OffTextTrackMenuItem(this.player_, {
  12865. kinds: this.kinds_,
  12866. kind: this.kind_,
  12867. label: label
  12868. }));
  12869. this.hideThreshold_ += 1;
  12870. var tracks = this.player_.textTracks();
  12871. if (!Array.isArray(this.kinds_)) {
  12872. this.kinds_ = [this.kind_];
  12873. }
  12874. for (var i = 0; i < tracks.length; i++) {
  12875. var track = tracks[i];
  12876. // only add tracks that are of an appropriate kind and have a label
  12877. if (this.kinds_.indexOf(track.kind) > -1) {
  12878. var item = new TrackMenuItem(this.player_, {
  12879. track: track,
  12880. // MenuItem is selectable
  12881. selectable: true,
  12882. // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
  12883. multiSelectable: false
  12884. });
  12885. item.addClass('vjs-' + track.kind + '-menu-item');
  12886. items.push(item);
  12887. }
  12888. }
  12889. return items;
  12890. };
  12891. return TextTrackButton;
  12892. }(TrackButton);
  12893. Component.registerComponent('TextTrackButton', TextTrackButton);
  12894. /**
  12895. * @file chapters-track-menu-item.js
  12896. */
  12897. /**
  12898. * The chapter track menu item
  12899. *
  12900. * @extends MenuItem
  12901. */
  12902. var ChaptersTrackMenuItem = function (_MenuItem) {
  12903. inherits(ChaptersTrackMenuItem, _MenuItem);
  12904. /**
  12905. * Creates an instance of this class.
  12906. *
  12907. * @param {Player} player
  12908. * The `Player` that this class should be attached to.
  12909. *
  12910. * @param {Object} [options]
  12911. * The key/value store of player options.
  12912. */
  12913. function ChaptersTrackMenuItem(player, options) {
  12914. classCallCheck(this, ChaptersTrackMenuItem);
  12915. var track = options.track;
  12916. var cue = options.cue;
  12917. var currentTime = player.currentTime();
  12918. // Modify options for parent MenuItem class's init.
  12919. options.selectable = true;
  12920. options.multiSelectable = false;
  12921. options.label = cue.text;
  12922. options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
  12923. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  12924. _this.track = track;
  12925. _this.cue = cue;
  12926. track.addEventListener('cuechange', bind(_this, _this.update));
  12927. return _this;
  12928. }
  12929. /**
  12930. * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
  12931. * {@link ClickableComponent} for more detailed information on what a click can be.
  12932. *
  12933. * @param {EventTarget~Event} [event]
  12934. * The `keydown`, `tap`, or `click` event that caused this function to be
  12935. * called.
  12936. *
  12937. * @listens tap
  12938. * @listens click
  12939. */
  12940. ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) {
  12941. _MenuItem.prototype.handleClick.call(this);
  12942. this.player_.currentTime(this.cue.startTime);
  12943. this.update(this.cue.startTime);
  12944. };
  12945. /**
  12946. * Update chapter menu item
  12947. *
  12948. * @param {EventTarget~Event} [event]
  12949. * The `cuechange` event that caused this function to run.
  12950. *
  12951. * @listens TextTrack#cuechange
  12952. */
  12953. ChaptersTrackMenuItem.prototype.update = function update(event) {
  12954. var cue = this.cue;
  12955. var currentTime = this.player_.currentTime();
  12956. // vjs.log(currentTime, cue.startTime);
  12957. this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
  12958. };
  12959. return ChaptersTrackMenuItem;
  12960. }(MenuItem);
  12961. Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
  12962. /**
  12963. * @file chapters-button.js
  12964. */
  12965. /**
  12966. * The button component for toggling and selecting chapters
  12967. * Chapters act much differently than other text tracks
  12968. * Cues are navigation vs. other tracks of alternative languages
  12969. *
  12970. * @extends TextTrackButton
  12971. */
  12972. var ChaptersButton = function (_TextTrackButton) {
  12973. inherits(ChaptersButton, _TextTrackButton);
  12974. /**
  12975. * Creates an instance of this class.
  12976. *
  12977. * @param {Player} player
  12978. * The `Player` that this class should be attached to.
  12979. *
  12980. * @param {Object} [options]
  12981. * The key/value store of player options.
  12982. *
  12983. * @param {Component~ReadyCallback} [ready]
  12984. * The function to call when this function is ready.
  12985. */
  12986. function ChaptersButton(player, options, ready) {
  12987. classCallCheck(this, ChaptersButton);
  12988. return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  12989. }
  12990. /**
  12991. * Builds the default DOM `className`.
  12992. *
  12993. * @return {string}
  12994. * The DOM `className` for this object.
  12995. */
  12996. ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
  12997. return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  12998. };
  12999. ChaptersButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13000. return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  13001. };
  13002. /**
  13003. * Update the menu based on the current state of its items.
  13004. *
  13005. * @param {EventTarget~Event} [event]
  13006. * An event that triggered this function to run.
  13007. *
  13008. * @listens TextTrackList#addtrack
  13009. * @listens TextTrackList#removetrack
  13010. * @listens TextTrackList#change
  13011. */
  13012. ChaptersButton.prototype.update = function update(event) {
  13013. if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
  13014. this.setTrack(this.findChaptersTrack());
  13015. }
  13016. _TextTrackButton.prototype.update.call(this);
  13017. };
  13018. /**
  13019. * Set the currently selected track for the chapters button.
  13020. *
  13021. * @param {TextTrack} track
  13022. * The new track to select. Nothing will change if this is the currently selected
  13023. * track.
  13024. */
  13025. ChaptersButton.prototype.setTrack = function setTrack(track) {
  13026. if (this.track_ === track) {
  13027. return;
  13028. }
  13029. if (!this.updateHandler_) {
  13030. this.updateHandler_ = this.update.bind(this);
  13031. }
  13032. // here this.track_ refers to the old track instance
  13033. if (this.track_) {
  13034. var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
  13035. if (remoteTextTrackEl) {
  13036. remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
  13037. }
  13038. this.track_ = null;
  13039. }
  13040. this.track_ = track;
  13041. // here this.track_ refers to the new track instance
  13042. if (this.track_) {
  13043. this.track_.mode = 'hidden';
  13044. var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
  13045. if (_remoteTextTrackEl) {
  13046. _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
  13047. }
  13048. }
  13049. };
  13050. /**
  13051. * Find the track object that is currently in use by this ChaptersButton
  13052. *
  13053. * @return {TextTrack|undefined}
  13054. * The current track or undefined if none was found.
  13055. */
  13056. ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() {
  13057. var tracks = this.player_.textTracks() || [];
  13058. for (var i = tracks.length - 1; i >= 0; i--) {
  13059. // We will always choose the last track as our chaptersTrack
  13060. var track = tracks[i];
  13061. if (track.kind === this.kind_) {
  13062. return track;
  13063. }
  13064. }
  13065. };
  13066. /**
  13067. * Get the caption for the ChaptersButton based on the track label. This will also
  13068. * use the current tracks localized kind as a fallback if a label does not exist.
  13069. *
  13070. * @return {string}
  13071. * The tracks current label or the localized track kind.
  13072. */
  13073. ChaptersButton.prototype.getMenuCaption = function getMenuCaption() {
  13074. if (this.track_ && this.track_.label) {
  13075. return this.track_.label;
  13076. }
  13077. return this.localize(toTitleCase(this.kind_));
  13078. };
  13079. /**
  13080. * Create menu from chapter track
  13081. *
  13082. * @return {Menu}
  13083. * New menu for the chapter buttons
  13084. */
  13085. ChaptersButton.prototype.createMenu = function createMenu() {
  13086. this.options_.title = this.getMenuCaption();
  13087. return _TextTrackButton.prototype.createMenu.call(this);
  13088. };
  13089. /**
  13090. * Create a menu item for each text track
  13091. *
  13092. * @return {TextTrackMenuItem[]}
  13093. * Array of menu items
  13094. */
  13095. ChaptersButton.prototype.createItems = function createItems() {
  13096. var items = [];
  13097. if (!this.track_) {
  13098. return items;
  13099. }
  13100. var cues = this.track_.cues;
  13101. if (!cues) {
  13102. return items;
  13103. }
  13104. for (var i = 0, l = cues.length; i < l; i++) {
  13105. var cue = cues[i];
  13106. var mi = new ChaptersTrackMenuItem(this.player_, { track: this.track_, cue: cue });
  13107. items.push(mi);
  13108. }
  13109. return items;
  13110. };
  13111. return ChaptersButton;
  13112. }(TextTrackButton);
  13113. /**
  13114. * `kind` of TextTrack to look for to associate it with this menu.
  13115. *
  13116. * @type {string}
  13117. * @private
  13118. */
  13119. ChaptersButton.prototype.kind_ = 'chapters';
  13120. /**
  13121. * The text that should display over the `ChaptersButton`s controls. Added for localization.
  13122. *
  13123. * @type {string}
  13124. * @private
  13125. */
  13126. ChaptersButton.prototype.controlText_ = 'Chapters';
  13127. Component.registerComponent('ChaptersButton', ChaptersButton);
  13128. /**
  13129. * @file descriptions-button.js
  13130. */
  13131. /**
  13132. * The button component for toggling and selecting descriptions
  13133. *
  13134. * @extends TextTrackButton
  13135. */
  13136. var DescriptionsButton = function (_TextTrackButton) {
  13137. inherits(DescriptionsButton, _TextTrackButton);
  13138. /**
  13139. * Creates an instance of this class.
  13140. *
  13141. * @param {Player} player
  13142. * The `Player` that this class should be attached to.
  13143. *
  13144. * @param {Object} [options]
  13145. * The key/value store of player options.
  13146. *
  13147. * @param {Component~ReadyCallback} [ready]
  13148. * The function to call when this component is ready.
  13149. */
  13150. function DescriptionsButton(player, options, ready) {
  13151. classCallCheck(this, DescriptionsButton);
  13152. var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  13153. var tracks = player.textTracks();
  13154. var changeHandler = bind(_this, _this.handleTracksChange);
  13155. tracks.addEventListener('change', changeHandler);
  13156. _this.on('dispose', function () {
  13157. tracks.removeEventListener('change', changeHandler);
  13158. });
  13159. return _this;
  13160. }
  13161. /**
  13162. * Handle text track change
  13163. *
  13164. * @param {EventTarget~Event} event
  13165. * The event that caused this function to run
  13166. *
  13167. * @listens TextTrackList#change
  13168. */
  13169. DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
  13170. var tracks = this.player().textTracks();
  13171. var disabled = false;
  13172. // Check whether a track of a different kind is showing
  13173. for (var i = 0, l = tracks.length; i < l; i++) {
  13174. var track = tracks[i];
  13175. if (track.kind !== this.kind_ && track.mode === 'showing') {
  13176. disabled = true;
  13177. break;
  13178. }
  13179. }
  13180. // If another track is showing, disable this menu button
  13181. if (disabled) {
  13182. this.disable();
  13183. } else {
  13184. this.enable();
  13185. }
  13186. };
  13187. /**
  13188. * Builds the default DOM `className`.
  13189. *
  13190. * @return {string}
  13191. * The DOM `className` for this object.
  13192. */
  13193. DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
  13194. return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  13195. };
  13196. DescriptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13197. return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  13198. };
  13199. return DescriptionsButton;
  13200. }(TextTrackButton);
  13201. /**
  13202. * `kind` of TextTrack to look for to associate it with this menu.
  13203. *
  13204. * @type {string}
  13205. * @private
  13206. */
  13207. DescriptionsButton.prototype.kind_ = 'descriptions';
  13208. /**
  13209. * The text that should display over the `DescriptionsButton`s controls. Added for localization.
  13210. *
  13211. * @type {string}
  13212. * @private
  13213. */
  13214. DescriptionsButton.prototype.controlText_ = 'Descriptions';
  13215. Component.registerComponent('DescriptionsButton', DescriptionsButton);
  13216. /**
  13217. * @file subtitles-button.js
  13218. */
  13219. /**
  13220. * The button component for toggling and selecting subtitles
  13221. *
  13222. * @extends TextTrackButton
  13223. */
  13224. var SubtitlesButton = function (_TextTrackButton) {
  13225. inherits(SubtitlesButton, _TextTrackButton);
  13226. /**
  13227. * Creates an instance of this class.
  13228. *
  13229. * @param {Player} player
  13230. * The `Player` that this class should be attached to.
  13231. *
  13232. * @param {Object} [options]
  13233. * The key/value store of player options.
  13234. *
  13235. * @param {Component~ReadyCallback} [ready]
  13236. * The function to call when this component is ready.
  13237. */
  13238. function SubtitlesButton(player, options, ready) {
  13239. classCallCheck(this, SubtitlesButton);
  13240. return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  13241. }
  13242. /**
  13243. * Builds the default DOM `className`.
  13244. *
  13245. * @return {string}
  13246. * The DOM `className` for this object.
  13247. */
  13248. SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
  13249. return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  13250. };
  13251. SubtitlesButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13252. return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  13253. };
  13254. return SubtitlesButton;
  13255. }(TextTrackButton);
  13256. /**
  13257. * `kind` of TextTrack to look for to associate it with this menu.
  13258. *
  13259. * @type {string}
  13260. * @private
  13261. */
  13262. SubtitlesButton.prototype.kind_ = 'subtitles';
  13263. /**
  13264. * The text that should display over the `SubtitlesButton`s controls. Added for localization.
  13265. *
  13266. * @type {string}
  13267. * @private
  13268. */
  13269. SubtitlesButton.prototype.controlText_ = 'Subtitles';
  13270. Component.registerComponent('SubtitlesButton', SubtitlesButton);
  13271. /**
  13272. * @file caption-settings-menu-item.js
  13273. */
  13274. /**
  13275. * The menu item for caption track settings menu
  13276. *
  13277. * @extends TextTrackMenuItem
  13278. */
  13279. var CaptionSettingsMenuItem = function (_TextTrackMenuItem) {
  13280. inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);
  13281. /**
  13282. * Creates an instance of this class.
  13283. *
  13284. * @param {Player} player
  13285. * The `Player` that this class should be attached to.
  13286. *
  13287. * @param {Object} [options]
  13288. * The key/value store of player options.
  13289. */
  13290. function CaptionSettingsMenuItem(player, options) {
  13291. classCallCheck(this, CaptionSettingsMenuItem);
  13292. options.track = {
  13293. player: player,
  13294. kind: options.kind,
  13295. label: options.kind + ' settings',
  13296. selectable: false,
  13297. 'default': false,
  13298. mode: 'disabled'
  13299. };
  13300. // CaptionSettingsMenuItem has no concept of 'selected'
  13301. options.selectable = false;
  13302. options.name = 'CaptionSettingsMenuItem';
  13303. var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
  13304. _this.addClass('vjs-texttrack-settings');
  13305. _this.controlText(', opens ' + options.kind + ' settings dialog');
  13306. return _this;
  13307. }
  13308. /**
  13309. * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
  13310. * {@link ClickableComponent} for more detailed information on what a click can be.
  13311. *
  13312. * @param {EventTarget~Event} [event]
  13313. * The `keydown`, `tap`, or `click` event that caused this function to be
  13314. * called.
  13315. *
  13316. * @listens tap
  13317. * @listens click
  13318. */
  13319. CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) {
  13320. this.player().getChild('textTrackSettings').open();
  13321. };
  13322. return CaptionSettingsMenuItem;
  13323. }(TextTrackMenuItem);
  13324. Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
  13325. /**
  13326. * @file captions-button.js
  13327. */
  13328. /**
  13329. * The button component for toggling and selecting captions
  13330. *
  13331. * @extends TextTrackButton
  13332. */
  13333. var CaptionsButton = function (_TextTrackButton) {
  13334. inherits(CaptionsButton, _TextTrackButton);
  13335. /**
  13336. * Creates an instance of this class.
  13337. *
  13338. * @param {Player} player
  13339. * The `Player` that this class should be attached to.
  13340. *
  13341. * @param {Object} [options]
  13342. * The key/value store of player options.
  13343. *
  13344. * @param {Component~ReadyCallback} [ready]
  13345. * The function to call when this component is ready.
  13346. */
  13347. function CaptionsButton(player, options, ready) {
  13348. classCallCheck(this, CaptionsButton);
  13349. return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
  13350. }
  13351. /**
  13352. * Builds the default DOM `className`.
  13353. *
  13354. * @return {string}
  13355. * The DOM `className` for this object.
  13356. */
  13357. CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
  13358. return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  13359. };
  13360. CaptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13361. return 'vjs-captions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  13362. };
  13363. /**
  13364. * Create caption menu items
  13365. *
  13366. * @return {CaptionSettingsMenuItem[]}
  13367. * The array of current menu items.
  13368. */
  13369. CaptionsButton.prototype.createItems = function createItems() {
  13370. var items = [];
  13371. if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
  13372. items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.kind_ }));
  13373. this.hideThreshold_ += 1;
  13374. }
  13375. return _TextTrackButton.prototype.createItems.call(this, items);
  13376. };
  13377. return CaptionsButton;
  13378. }(TextTrackButton);
  13379. /**
  13380. * `kind` of TextTrack to look for to associate it with this menu.
  13381. *
  13382. * @type {string}
  13383. * @private
  13384. */
  13385. CaptionsButton.prototype.kind_ = 'captions';
  13386. /**
  13387. * The text that should display over the `CaptionsButton`s controls. Added for localization.
  13388. *
  13389. * @type {string}
  13390. * @private
  13391. */
  13392. CaptionsButton.prototype.controlText_ = 'Captions';
  13393. Component.registerComponent('CaptionsButton', CaptionsButton);
  13394. /**
  13395. * @file subs-caps-menu-item.js
  13396. */
  13397. /**
  13398. * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
  13399. * in the SubsCapsMenu.
  13400. *
  13401. * @extends TextTrackMenuItem
  13402. */
  13403. var SubsCapsMenuItem = function (_TextTrackMenuItem) {
  13404. inherits(SubsCapsMenuItem, _TextTrackMenuItem);
  13405. function SubsCapsMenuItem() {
  13406. classCallCheck(this, SubsCapsMenuItem);
  13407. return possibleConstructorReturn(this, _TextTrackMenuItem.apply(this, arguments));
  13408. }
  13409. SubsCapsMenuItem.prototype.createEl = function createEl(type, props, attrs) {
  13410. var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
  13411. if (this.options_.track.kind === 'captions') {
  13412. innerHTML += '\n <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n <span class="vjs-control-text"> ' + this.localize('Captions') + '</span>\n ';
  13413. }
  13414. innerHTML += '</span>';
  13415. var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
  13416. innerHTML: innerHTML
  13417. }, props), attrs);
  13418. return el;
  13419. };
  13420. return SubsCapsMenuItem;
  13421. }(TextTrackMenuItem);
  13422. Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
  13423. /**
  13424. * @file sub-caps-button.js
  13425. */
  13426. /**
  13427. * The button component for toggling and selecting captions and/or subtitles
  13428. *
  13429. * @extends TextTrackButton
  13430. */
  13431. var SubsCapsButton = function (_TextTrackButton) {
  13432. inherits(SubsCapsButton, _TextTrackButton);
  13433. function SubsCapsButton(player) {
  13434. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13435. classCallCheck(this, SubsCapsButton);
  13436. // Although North America uses "captions" in most cases for
  13437. // "captions and subtitles" other locales use "subtitles"
  13438. var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options));
  13439. _this.label_ = 'subtitles';
  13440. if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
  13441. _this.label_ = 'captions';
  13442. }
  13443. _this.menuButton_.controlText(toTitleCase(_this.label_));
  13444. return _this;
  13445. }
  13446. /**
  13447. * Builds the default DOM `className`.
  13448. *
  13449. * @return {string}
  13450. * The DOM `className` for this object.
  13451. */
  13452. SubsCapsButton.prototype.buildCSSClass = function buildCSSClass() {
  13453. return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  13454. };
  13455. SubsCapsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13456. return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
  13457. };
  13458. /**
  13459. * Create caption/subtitles menu items
  13460. *
  13461. * @return {CaptionSettingsMenuItem[]}
  13462. * The array of current menu items.
  13463. */
  13464. SubsCapsButton.prototype.createItems = function createItems() {
  13465. var items = [];
  13466. if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks) && this.player().getChild('textTrackSettings')) {
  13467. items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.label_ }));
  13468. this.hideThreshold_ += 1;
  13469. }
  13470. items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
  13471. return items;
  13472. };
  13473. return SubsCapsButton;
  13474. }(TextTrackButton);
  13475. /**
  13476. * `kind`s of TextTrack to look for to associate it with this menu.
  13477. *
  13478. * @type {array}
  13479. * @private
  13480. */
  13481. SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
  13482. /**
  13483. * The text that should display over the `SubsCapsButton`s controls.
  13484. *
  13485. *
  13486. * @type {string}
  13487. * @private
  13488. */
  13489. SubsCapsButton.prototype.controlText_ = 'Subtitles';
  13490. Component.registerComponent('SubsCapsButton', SubsCapsButton);
  13491. /**
  13492. * @file audio-track-menu-item.js
  13493. */
  13494. /**
  13495. * An {@link AudioTrack} {@link MenuItem}
  13496. *
  13497. * @extends MenuItem
  13498. */
  13499. var AudioTrackMenuItem = function (_MenuItem) {
  13500. inherits(AudioTrackMenuItem, _MenuItem);
  13501. /**
  13502. * Creates an instance of this class.
  13503. *
  13504. * @param {Player} player
  13505. * The `Player` that this class should be attached to.
  13506. *
  13507. * @param {Object} [options]
  13508. * The key/value store of player options.
  13509. */
  13510. function AudioTrackMenuItem(player, options) {
  13511. classCallCheck(this, AudioTrackMenuItem);
  13512. var track = options.track;
  13513. var tracks = player.audioTracks();
  13514. // Modify options for parent MenuItem class's init.
  13515. options.label = track.label || track.language || 'Unknown';
  13516. options.selected = track.enabled;
  13517. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  13518. _this.track = track;
  13519. _this.addClass('vjs-' + track.kind + '-menu-item');
  13520. var changeHandler = function changeHandler() {
  13521. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  13522. args[_key] = arguments[_key];
  13523. }
  13524. _this.handleTracksChange.apply(_this, args);
  13525. };
  13526. tracks.addEventListener('change', changeHandler);
  13527. _this.on('dispose', function () {
  13528. tracks.removeEventListener('change', changeHandler);
  13529. });
  13530. return _this;
  13531. }
  13532. AudioTrackMenuItem.prototype.createEl = function createEl(type, props, attrs) {
  13533. var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
  13534. if (this.options_.track.kind === 'main-desc') {
  13535. innerHTML += '\n <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n <span class="vjs-control-text"> ' + this.localize('Descriptions') + '</span>\n ';
  13536. }
  13537. innerHTML += '</span>';
  13538. var el = _MenuItem.prototype.createEl.call(this, type, assign({
  13539. innerHTML: innerHTML
  13540. }, props), attrs);
  13541. return el;
  13542. };
  13543. /**
  13544. * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
  13545. * for more detailed information on what a click can be.
  13546. *
  13547. * @param {EventTarget~Event} [event]
  13548. * The `keydown`, `tap`, or `click` event that caused this function to be
  13549. * called.
  13550. *
  13551. * @listens tap
  13552. * @listens click
  13553. */
  13554. AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
  13555. var tracks = this.player_.audioTracks();
  13556. _MenuItem.prototype.handleClick.call(this, event);
  13557. for (var i = 0; i < tracks.length; i++) {
  13558. var track = tracks[i];
  13559. track.enabled = track === this.track;
  13560. }
  13561. };
  13562. /**
  13563. * Handle any {@link AudioTrack} change.
  13564. *
  13565. * @param {EventTarget~Event} [event]
  13566. * The {@link AudioTrackList#change} event that caused this to run.
  13567. *
  13568. * @listens AudioTrackList#change
  13569. */
  13570. AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
  13571. this.selected(this.track.enabled);
  13572. };
  13573. return AudioTrackMenuItem;
  13574. }(MenuItem);
  13575. Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
  13576. /**
  13577. * @file audio-track-button.js
  13578. */
  13579. /**
  13580. * The base class for buttons that toggle specific {@link AudioTrack} types.
  13581. *
  13582. * @extends TrackButton
  13583. */
  13584. var AudioTrackButton = function (_TrackButton) {
  13585. inherits(AudioTrackButton, _TrackButton);
  13586. /**
  13587. * Creates an instance of this class.
  13588. *
  13589. * @param {Player} player
  13590. * The `Player` that this class should be attached to.
  13591. *
  13592. * @param {Object} [options={}]
  13593. * The key/value store of player options.
  13594. */
  13595. function AudioTrackButton(player) {
  13596. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  13597. classCallCheck(this, AudioTrackButton);
  13598. options.tracks = player.audioTracks();
  13599. return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
  13600. }
  13601. /**
  13602. * Builds the default DOM `className`.
  13603. *
  13604. * @return {string}
  13605. * The DOM `className` for this object.
  13606. */
  13607. AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
  13608. return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
  13609. };
  13610. AudioTrackButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13611. return 'vjs-audio-button ' + _TrackButton.prototype.buildWrapperCSSClass.call(this);
  13612. };
  13613. /**
  13614. * Create a menu item for each audio track
  13615. *
  13616. * @param {AudioTrackMenuItem[]} [items=[]]
  13617. * An array of existing menu items to use.
  13618. *
  13619. * @return {AudioTrackMenuItem[]}
  13620. * An array of menu items
  13621. */
  13622. AudioTrackButton.prototype.createItems = function createItems() {
  13623. var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  13624. // if there's only one audio track, there no point in showing it
  13625. this.hideThreshold_ = 1;
  13626. var tracks = this.player_.audioTracks();
  13627. for (var i = 0; i < tracks.length; i++) {
  13628. var track = tracks[i];
  13629. items.push(new AudioTrackMenuItem(this.player_, {
  13630. track: track,
  13631. // MenuItem is selectable
  13632. selectable: true,
  13633. // MenuItem is NOT multiSelectable (i.e. only one can be marked "selected" at a time)
  13634. multiSelectable: false
  13635. }));
  13636. }
  13637. return items;
  13638. };
  13639. return AudioTrackButton;
  13640. }(TrackButton);
  13641. /**
  13642. * The text that should display over the `AudioTrackButton`s controls. Added for localization.
  13643. *
  13644. * @type {string}
  13645. * @private
  13646. */
  13647. AudioTrackButton.prototype.controlText_ = 'Audio Track';
  13648. Component.registerComponent('AudioTrackButton', AudioTrackButton);
  13649. /**
  13650. * @file playback-rate-menu-item.js
  13651. */
  13652. /**
  13653. * The specific menu item type for selecting a playback rate.
  13654. *
  13655. * @extends MenuItem
  13656. */
  13657. var PlaybackRateMenuItem = function (_MenuItem) {
  13658. inherits(PlaybackRateMenuItem, _MenuItem);
  13659. /**
  13660. * Creates an instance of this class.
  13661. *
  13662. * @param {Player} player
  13663. * The `Player` that this class should be attached to.
  13664. *
  13665. * @param {Object} [options]
  13666. * The key/value store of player options.
  13667. */
  13668. function PlaybackRateMenuItem(player, options) {
  13669. classCallCheck(this, PlaybackRateMenuItem);
  13670. var label = options.rate;
  13671. var rate = parseFloat(label, 10);
  13672. // Modify options for parent MenuItem class's init.
  13673. options.label = label;
  13674. options.selected = rate === 1;
  13675. options.selectable = true;
  13676. options.multiSelectable = false;
  13677. var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
  13678. _this.label = label;
  13679. _this.rate = rate;
  13680. _this.on(player, 'ratechange', _this.update);
  13681. return _this;
  13682. }
  13683. /**
  13684. * This gets called when an `PlaybackRateMenuItem` is "clicked". See
  13685. * {@link ClickableComponent} for more detailed information on what a click can be.
  13686. *
  13687. * @param {EventTarget~Event} [event]
  13688. * The `keydown`, `tap`, or `click` event that caused this function to be
  13689. * called.
  13690. *
  13691. * @listens tap
  13692. * @listens click
  13693. */
  13694. PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) {
  13695. _MenuItem.prototype.handleClick.call(this);
  13696. this.player().playbackRate(this.rate);
  13697. };
  13698. /**
  13699. * Update the PlaybackRateMenuItem when the playbackrate changes.
  13700. *
  13701. * @param {EventTarget~Event} [event]
  13702. * The `ratechange` event that caused this function to run.
  13703. *
  13704. * @listens Player#ratechange
  13705. */
  13706. PlaybackRateMenuItem.prototype.update = function update(event) {
  13707. this.selected(this.player().playbackRate() === this.rate);
  13708. };
  13709. return PlaybackRateMenuItem;
  13710. }(MenuItem);
  13711. /**
  13712. * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
  13713. *
  13714. * @type {string}
  13715. * @private
  13716. */
  13717. PlaybackRateMenuItem.prototype.contentElType = 'button';
  13718. Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
  13719. /**
  13720. * @file playback-rate-menu-button.js
  13721. */
  13722. /**
  13723. * The component for controlling the playback rate.
  13724. *
  13725. * @extends MenuButton
  13726. */
  13727. var PlaybackRateMenuButton = function (_MenuButton) {
  13728. inherits(PlaybackRateMenuButton, _MenuButton);
  13729. /**
  13730. * Creates an instance of this class.
  13731. *
  13732. * @param {Player} player
  13733. * The `Player` that this class should be attached to.
  13734. *
  13735. * @param {Object} [options]
  13736. * The key/value store of player options.
  13737. */
  13738. function PlaybackRateMenuButton(player, options) {
  13739. classCallCheck(this, PlaybackRateMenuButton);
  13740. var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
  13741. _this.updateVisibility();
  13742. _this.updateLabel();
  13743. _this.on(player, 'loadstart', _this.updateVisibility);
  13744. _this.on(player, 'ratechange', _this.updateLabel);
  13745. return _this;
  13746. }
  13747. /**
  13748. * Create the `Component`'s DOM element
  13749. *
  13750. * @return {Element}
  13751. * The element that was created.
  13752. */
  13753. PlaybackRateMenuButton.prototype.createEl = function createEl$$1() {
  13754. var el = _MenuButton.prototype.createEl.call(this);
  13755. this.labelEl_ = createEl('div', {
  13756. className: 'vjs-playback-rate-value',
  13757. innerHTML: '1x'
  13758. });
  13759. el.appendChild(this.labelEl_);
  13760. return el;
  13761. };
  13762. PlaybackRateMenuButton.prototype.dispose = function dispose() {
  13763. this.labelEl_ = null;
  13764. _MenuButton.prototype.dispose.call(this);
  13765. };
  13766. /**
  13767. * Builds the default DOM `className`.
  13768. *
  13769. * @return {string}
  13770. * The DOM `className` for this object.
  13771. */
  13772. PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
  13773. return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
  13774. };
  13775. PlaybackRateMenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
  13776. return 'vjs-playback-rate ' + _MenuButton.prototype.buildWrapperCSSClass.call(this);
  13777. };
  13778. /**
  13779. * Create the playback rate menu
  13780. *
  13781. * @return {Menu}
  13782. * Menu object populated with {@link PlaybackRateMenuItem}s
  13783. */
  13784. PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
  13785. var menu = new Menu(this.player());
  13786. var rates = this.playbackRates();
  13787. if (rates) {
  13788. for (var i = rates.length - 1; i >= 0; i--) {
  13789. menu.addChild(new PlaybackRateMenuItem(this.player(), { rate: rates[i] + 'x' }));
  13790. }
  13791. }
  13792. return menu;
  13793. };
  13794. /**
  13795. * Updates ARIA accessibility attributes
  13796. */
  13797. PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
  13798. // Current playback rate
  13799. this.el().setAttribute('aria-valuenow', this.player().playbackRate());
  13800. };
  13801. /**
  13802. * This gets called when an `PlaybackRateMenuButton` is "clicked". See
  13803. * {@link ClickableComponent} for more detailed information on what a click can be.
  13804. *
  13805. * @param {EventTarget~Event} [event]
  13806. * The `keydown`, `tap`, or `click` event that caused this function to be
  13807. * called.
  13808. *
  13809. * @listens tap
  13810. * @listens click
  13811. */
  13812. PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) {
  13813. // select next rate option
  13814. var currentRate = this.player().playbackRate();
  13815. var rates = this.playbackRates();
  13816. // this will select first one if the last one currently selected
  13817. var newRate = rates[0];
  13818. for (var i = 0; i < rates.length; i++) {
  13819. if (rates[i] > currentRate) {
  13820. newRate = rates[i];
  13821. break;
  13822. }
  13823. }
  13824. this.player().playbackRate(newRate);
  13825. };
  13826. /**
  13827. * Get possible playback rates
  13828. *
  13829. * @return {Array}
  13830. * All possible playback rates
  13831. */
  13832. PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
  13833. return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
  13834. };
  13835. /**
  13836. * Get whether playback rates is supported by the tech
  13837. * and an array of playback rates exists
  13838. *
  13839. * @return {boolean}
  13840. * Whether changing playback rate is supported
  13841. */
  13842. PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
  13843. return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
  13844. };
  13845. /**
  13846. * Hide playback rate controls when they're no playback rate options to select
  13847. *
  13848. * @param {EventTarget~Event} [event]
  13849. * The event that caused this function to run.
  13850. *
  13851. * @listens Player#loadstart
  13852. */
  13853. PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) {
  13854. if (this.playbackRateSupported()) {
  13855. this.removeClass('vjs-hidden');
  13856. } else {
  13857. this.addClass('vjs-hidden');
  13858. }
  13859. };
  13860. /**
  13861. * Update button label when rate changed
  13862. *
  13863. * @param {EventTarget~Event} [event]
  13864. * The event that caused this function to run.
  13865. *
  13866. * @listens Player#ratechange
  13867. */
  13868. PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) {
  13869. if (this.playbackRateSupported()) {
  13870. this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
  13871. }
  13872. };
  13873. return PlaybackRateMenuButton;
  13874. }(MenuButton);
  13875. /**
  13876. * The text that should display over the `FullscreenToggle`s controls. Added for localization.
  13877. *
  13878. * @type {string}
  13879. * @private
  13880. */
  13881. PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
  13882. Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
  13883. /**
  13884. * @file spacer.js
  13885. */
  13886. /**
  13887. * Just an empty spacer element that can be used as an append point for plugins, etc.
  13888. * Also can be used to create space between elements when necessary.
  13889. *
  13890. * @extends Component
  13891. */
  13892. var Spacer = function (_Component) {
  13893. inherits(Spacer, _Component);
  13894. function Spacer() {
  13895. classCallCheck(this, Spacer);
  13896. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  13897. }
  13898. /**
  13899. * Builds the default DOM `className`.
  13900. *
  13901. * @return {string}
  13902. * The DOM `className` for this object.
  13903. */
  13904. Spacer.prototype.buildCSSClass = function buildCSSClass() {
  13905. return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
  13906. };
  13907. /**
  13908. * Create the `Component`'s DOM element
  13909. *
  13910. * @return {Element}
  13911. * The element that was created.
  13912. */
  13913. Spacer.prototype.createEl = function createEl() {
  13914. return _Component.prototype.createEl.call(this, 'div', {
  13915. className: this.buildCSSClass()
  13916. });
  13917. };
  13918. return Spacer;
  13919. }(Component);
  13920. Component.registerComponent('Spacer', Spacer);
  13921. /**
  13922. * @file custom-control-spacer.js
  13923. */
  13924. /**
  13925. * Spacer specifically meant to be used as an insertion point for new plugins, etc.
  13926. *
  13927. * @extends Spacer
  13928. */
  13929. var CustomControlSpacer = function (_Spacer) {
  13930. inherits(CustomControlSpacer, _Spacer);
  13931. function CustomControlSpacer() {
  13932. classCallCheck(this, CustomControlSpacer);
  13933. return possibleConstructorReturn(this, _Spacer.apply(this, arguments));
  13934. }
  13935. /**
  13936. * Builds the default DOM `className`.
  13937. *
  13938. * @return {string}
  13939. * The DOM `className` for this object.
  13940. */
  13941. CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
  13942. return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
  13943. };
  13944. /**
  13945. * Create the `Component`'s DOM element
  13946. *
  13947. * @return {Element}
  13948. * The element that was created.
  13949. */
  13950. CustomControlSpacer.prototype.createEl = function createEl() {
  13951. var el = _Spacer.prototype.createEl.call(this, {
  13952. className: this.buildCSSClass()
  13953. });
  13954. // No-flex/table-cell mode requires there be some content
  13955. // in the cell to fill the remaining space of the table.
  13956. el.innerHTML = '\xA0';
  13957. return el;
  13958. };
  13959. return CustomControlSpacer;
  13960. }(Spacer);
  13961. Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
  13962. /**
  13963. * @file control-bar.js
  13964. */
  13965. // Required children
  13966. /**
  13967. * Container of main controls.
  13968. *
  13969. * @extends Component
  13970. */
  13971. var ControlBar = function (_Component) {
  13972. inherits(ControlBar, _Component);
  13973. function ControlBar() {
  13974. classCallCheck(this, ControlBar);
  13975. return possibleConstructorReturn(this, _Component.apply(this, arguments));
  13976. }
  13977. /**
  13978. * Create the `Component`'s DOM element
  13979. *
  13980. * @return {Element}
  13981. * The element that was created.
  13982. */
  13983. ControlBar.prototype.createEl = function createEl() {
  13984. return _Component.prototype.createEl.call(this, 'div', {
  13985. className: 'vjs-control-bar',
  13986. dir: 'ltr'
  13987. });
  13988. };
  13989. return ControlBar;
  13990. }(Component);
  13991. /**
  13992. * Default options for `ControlBar`
  13993. *
  13994. * @type {Object}
  13995. * @private
  13996. */
  13997. ControlBar.prototype.options_ = {
  13998. children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
  13999. };
  14000. Component.registerComponent('ControlBar', ControlBar);
  14001. /**
  14002. * @file error-display.js
  14003. */
  14004. /**
  14005. * A display that indicates an error has occurred. This means that the video
  14006. * is unplayable.
  14007. *
  14008. * @extends ModalDialog
  14009. */
  14010. var ErrorDisplay = function (_ModalDialog) {
  14011. inherits(ErrorDisplay, _ModalDialog);
  14012. /**
  14013. * Creates an instance of this class.
  14014. *
  14015. * @param {Player} player
  14016. * The `Player` that this class should be attached to.
  14017. *
  14018. * @param {Object} [options]
  14019. * The key/value store of player options.
  14020. */
  14021. function ErrorDisplay(player, options) {
  14022. classCallCheck(this, ErrorDisplay);
  14023. var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
  14024. _this.on(player, 'error', _this.open);
  14025. return _this;
  14026. }
  14027. /**
  14028. * Builds the default DOM `className`.
  14029. *
  14030. * @return {string}
  14031. * The DOM `className` for this object.
  14032. *
  14033. * @deprecated Since version 5.
  14034. */
  14035. ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
  14036. return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
  14037. };
  14038. /**
  14039. * Gets the localized error message based on the `Player`s error.
  14040. *
  14041. * @return {string}
  14042. * The `Player`s error message localized or an empty string.
  14043. */
  14044. ErrorDisplay.prototype.content = function content() {
  14045. var error = this.player().error();
  14046. return error ? this.localize(error.message) : '';
  14047. };
  14048. return ErrorDisplay;
  14049. }(ModalDialog);
  14050. /**
  14051. * The default options for an `ErrorDisplay`.
  14052. *
  14053. * @private
  14054. */
  14055. ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
  14056. pauseOnOpen: false,
  14057. fillAlways: true,
  14058. temporary: false,
  14059. uncloseable: true
  14060. });
  14061. Component.registerComponent('ErrorDisplay', ErrorDisplay);
  14062. /**
  14063. * @file text-track-settings.js
  14064. */
  14065. var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
  14066. var COLOR_BLACK = ['#000', 'Black'];
  14067. var COLOR_BLUE = ['#00F', 'Blue'];
  14068. var COLOR_CYAN = ['#0FF', 'Cyan'];
  14069. var COLOR_GREEN = ['#0F0', 'Green'];
  14070. var COLOR_MAGENTA = ['#F0F', 'Magenta'];
  14071. var COLOR_RED = ['#F00', 'Red'];
  14072. var COLOR_WHITE = ['#FFF', 'White'];
  14073. var COLOR_YELLOW = ['#FF0', 'Yellow'];
  14074. var OPACITY_OPAQUE = ['1', 'Opaque'];
  14075. var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
  14076. var OPACITY_TRANS = ['0', 'Transparent'];
  14077. // Configuration for the various <select> elements in the DOM of this component.
  14078. //
  14079. // Possible keys include:
  14080. //
  14081. // `default`:
  14082. // The default option index. Only needs to be provided if not zero.
  14083. // `parser`:
  14084. // A function which is used to parse the value from the selected option in
  14085. // a customized way.
  14086. // `selector`:
  14087. // The selector used to find the associated <select> element.
  14088. var selectConfigs = {
  14089. backgroundColor: {
  14090. selector: '.vjs-bg-color > select',
  14091. id: 'captions-background-color-%s',
  14092. label: 'Color',
  14093. options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
  14094. },
  14095. backgroundOpacity: {
  14096. selector: '.vjs-bg-opacity > select',
  14097. id: 'captions-background-opacity-%s',
  14098. label: 'Transparency',
  14099. options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
  14100. },
  14101. color: {
  14102. selector: '.vjs-fg-color > select',
  14103. id: 'captions-foreground-color-%s',
  14104. label: 'Color',
  14105. options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
  14106. },
  14107. edgeStyle: {
  14108. selector: '.vjs-edge-style > select',
  14109. id: '%s',
  14110. label: 'Text Edge Style',
  14111. options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
  14112. },
  14113. fontFamily: {
  14114. selector: '.vjs-font-family > select',
  14115. id: 'captions-font-family-%s',
  14116. label: 'Font Family',
  14117. options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
  14118. },
  14119. fontPercent: {
  14120. selector: '.vjs-font-percent > select',
  14121. id: 'captions-font-size-%s',
  14122. label: 'Font Size',
  14123. 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%']],
  14124. 'default': 2,
  14125. parser: function parser(v) {
  14126. return v === '1.00' ? null : Number(v);
  14127. }
  14128. },
  14129. textOpacity: {
  14130. selector: '.vjs-text-opacity > select',
  14131. id: 'captions-foreground-opacity-%s',
  14132. label: 'Transparency',
  14133. options: [OPACITY_OPAQUE, OPACITY_SEMI]
  14134. },
  14135. // Options for this object are defined below.
  14136. windowColor: {
  14137. selector: '.vjs-window-color > select',
  14138. id: 'captions-window-color-%s',
  14139. label: 'Color'
  14140. },
  14141. // Options for this object are defined below.
  14142. windowOpacity: {
  14143. selector: '.vjs-window-opacity > select',
  14144. id: 'captions-window-opacity-%s',
  14145. label: 'Transparency',
  14146. options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
  14147. }
  14148. };
  14149. selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
  14150. /**
  14151. * Get the actual value of an option.
  14152. *
  14153. * @param {string} value
  14154. * The value to get
  14155. *
  14156. * @param {Function} [parser]
  14157. * Optional function to adjust the value.
  14158. *
  14159. * @return {Mixed}
  14160. * - Will be `undefined` if no value exists
  14161. * - Will be `undefined` if the given value is "none".
  14162. * - Will be the actual value otherwise.
  14163. *
  14164. * @private
  14165. */
  14166. function parseOptionValue(value, parser) {
  14167. if (parser) {
  14168. value = parser(value);
  14169. }
  14170. if (value && value !== 'none') {
  14171. return value;
  14172. }
  14173. }
  14174. /**
  14175. * Gets the value of the selected <option> element within a <select> element.
  14176. *
  14177. * @param {Element} el
  14178. * the element to look in
  14179. *
  14180. * @param {Function} [parser]
  14181. * Optional function to adjust the value.
  14182. *
  14183. * @return {Mixed}
  14184. * - Will be `undefined` if no value exists
  14185. * - Will be `undefined` if the given value is "none".
  14186. * - Will be the actual value otherwise.
  14187. *
  14188. * @private
  14189. */
  14190. function getSelectedOptionValue(el, parser) {
  14191. var value = el.options[el.options.selectedIndex].value;
  14192. return parseOptionValue(value, parser);
  14193. }
  14194. /**
  14195. * Sets the selected <option> element within a <select> element based on a
  14196. * given value.
  14197. *
  14198. * @param {Element} el
  14199. * The element to look in.
  14200. *
  14201. * @param {string} value
  14202. * the property to look on.
  14203. *
  14204. * @param {Function} [parser]
  14205. * Optional function to adjust the value before comparing.
  14206. *
  14207. * @private
  14208. */
  14209. function setSelectedOption(el, value, parser) {
  14210. if (!value) {
  14211. return;
  14212. }
  14213. for (var i = 0; i < el.options.length; i++) {
  14214. if (parseOptionValue(el.options[i].value, parser) === value) {
  14215. el.selectedIndex = i;
  14216. break;
  14217. }
  14218. }
  14219. }
  14220. /**
  14221. * Manipulate Text Tracks settings.
  14222. *
  14223. * @extends ModalDialog
  14224. */
  14225. var TextTrackSettings = function (_ModalDialog) {
  14226. inherits(TextTrackSettings, _ModalDialog);
  14227. /**
  14228. * Creates an instance of this class.
  14229. *
  14230. * @param {Player} player
  14231. * The `Player` that this class should be attached to.
  14232. *
  14233. * @param {Object} [options]
  14234. * The key/value store of player options.
  14235. */
  14236. function TextTrackSettings(player, options) {
  14237. classCallCheck(this, TextTrackSettings);
  14238. options.temporary = false;
  14239. var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
  14240. _this.updateDisplay = bind(_this, _this.updateDisplay);
  14241. // fill the modal and pretend we have opened it
  14242. _this.fill();
  14243. _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
  14244. _this.endDialog = createEl('p', {
  14245. className: 'vjs-control-text',
  14246. textContent: _this.localize('End of dialog window.')
  14247. });
  14248. _this.el().appendChild(_this.endDialog);
  14249. _this.setDefaults();
  14250. // Grab `persistTextTrackSettings` from the player options if not passed in child options
  14251. if (options.persistTextTrackSettings === undefined) {
  14252. _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
  14253. }
  14254. _this.on(_this.$('.vjs-done-button'), 'click', function () {
  14255. _this.saveSettings();
  14256. _this.close();
  14257. });
  14258. _this.on(_this.$('.vjs-default-button'), 'click', function () {
  14259. _this.setDefaults();
  14260. _this.updateDisplay();
  14261. });
  14262. each(selectConfigs, function (config) {
  14263. _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
  14264. });
  14265. if (_this.options_.persistTextTrackSettings) {
  14266. _this.restoreSettings();
  14267. }
  14268. return _this;
  14269. }
  14270. TextTrackSettings.prototype.dispose = function dispose() {
  14271. this.endDialog = null;
  14272. _ModalDialog.prototype.dispose.call(this);
  14273. };
  14274. /**
  14275. * Create a <select> element with configured options.
  14276. *
  14277. * @param {string} key
  14278. * Configuration key to use during creation.
  14279. *
  14280. * @return {string}
  14281. * An HTML string.
  14282. *
  14283. * @private
  14284. */
  14285. TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) {
  14286. var _this2 = this;
  14287. var legendId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
  14288. var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'label';
  14289. var config = selectConfigs[key];
  14290. var id = config.id.replace('%s', this.id_);
  14291. var selectLabelledbyIds = [legendId, id].join(' ').trim();
  14292. return ['<' + type + ' id="' + id + '" class="' + (type === 'label' ? 'vjs-label' : '') + '">', this.localize(config.label), '</' + type + '>', '<select aria-labelledby="' + selectLabelledbyIds + '">'].concat(config.options.map(function (o) {
  14293. var optionId = id + '-' + o[1].replace(/\W+/g, '');
  14294. return ['<option id="' + optionId + '" value="' + o[0] + '" ', 'aria-labelledby="' + selectLabelledbyIds + ' ' + optionId + '">', _this2.localize(o[1]), '</option>'].join('');
  14295. })).concat('</select>').join('');
  14296. };
  14297. /**
  14298. * Create foreground color element for the component
  14299. *
  14300. * @return {string}
  14301. * An HTML string.
  14302. *
  14303. * @private
  14304. */
  14305. TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() {
  14306. var legendId = 'captions-text-legend-' + this.id_;
  14307. 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('');
  14308. };
  14309. /**
  14310. * Create background color element for the component
  14311. *
  14312. * @return {string}
  14313. * An HTML string.
  14314. *
  14315. * @private
  14316. */
  14317. TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() {
  14318. var legendId = 'captions-background-' + this.id_;
  14319. 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('');
  14320. };
  14321. /**
  14322. * Create window color element for the component
  14323. *
  14324. * @return {string}
  14325. * An HTML string.
  14326. *
  14327. * @private
  14328. */
  14329. TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() {
  14330. var legendId = 'captions-window-' + this.id_;
  14331. 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('');
  14332. };
  14333. /**
  14334. * Create color elements for the component
  14335. *
  14336. * @return {Element}
  14337. * The element that was created
  14338. *
  14339. * @private
  14340. */
  14341. TextTrackSettings.prototype.createElColors_ = function createElColors_() {
  14342. return createEl('div', {
  14343. className: 'vjs-track-settings-colors',
  14344. innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
  14345. });
  14346. };
  14347. /**
  14348. * Create font elements for the component
  14349. *
  14350. * @return {Element}
  14351. * The element that was created.
  14352. *
  14353. * @private
  14354. */
  14355. TextTrackSettings.prototype.createElFont_ = function createElFont_() {
  14356. return createEl('div', {
  14357. className: 'vjs-track-settings-font',
  14358. 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('')
  14359. });
  14360. };
  14361. /**
  14362. * Create controls for the component
  14363. *
  14364. * @return {Element}
  14365. * The element that was created.
  14366. *
  14367. * @private
  14368. */
  14369. TextTrackSettings.prototype.createElControls_ = function createElControls_() {
  14370. var defaultsDescription = this.localize('restore all settings to the default values');
  14371. return createEl('div', {
  14372. className: 'vjs-track-settings-controls',
  14373. 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('')
  14374. });
  14375. };
  14376. TextTrackSettings.prototype.content = function content() {
  14377. return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
  14378. };
  14379. TextTrackSettings.prototype.label = function label() {
  14380. return this.localize('Caption Settings Dialog');
  14381. };
  14382. TextTrackSettings.prototype.description = function description() {
  14383. return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
  14384. };
  14385. TextTrackSettings.prototype.buildCSSClass = function buildCSSClass() {
  14386. return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
  14387. };
  14388. /**
  14389. * Gets an object of text track settings (or null).
  14390. *
  14391. * @return {Object}
  14392. * An object with config values parsed from the DOM or localStorage.
  14393. */
  14394. TextTrackSettings.prototype.getValues = function getValues() {
  14395. var _this3 = this;
  14396. return reduce(selectConfigs, function (accum, config, key) {
  14397. var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
  14398. if (value !== undefined) {
  14399. accum[key] = value;
  14400. }
  14401. return accum;
  14402. }, {});
  14403. };
  14404. /**
  14405. * Sets text track settings from an object of values.
  14406. *
  14407. * @param {Object} values
  14408. * An object with config values parsed from the DOM or localStorage.
  14409. */
  14410. TextTrackSettings.prototype.setValues = function setValues(values) {
  14411. var _this4 = this;
  14412. each(selectConfigs, function (config, key) {
  14413. setSelectedOption(_this4.$(config.selector), values[key], config.parser);
  14414. });
  14415. };
  14416. /**
  14417. * Sets all `<select>` elements to their default values.
  14418. */
  14419. TextTrackSettings.prototype.setDefaults = function setDefaults() {
  14420. var _this5 = this;
  14421. each(selectConfigs, function (config) {
  14422. var index = config.hasOwnProperty('default') ? config['default'] : 0;
  14423. _this5.$(config.selector).selectedIndex = index;
  14424. });
  14425. };
  14426. /**
  14427. * Restore texttrack settings from localStorage
  14428. */
  14429. TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
  14430. var values = void 0;
  14431. try {
  14432. values = JSON.parse(window_1.localStorage.getItem(LOCAL_STORAGE_KEY));
  14433. } catch (err) {
  14434. log.warn(err);
  14435. }
  14436. if (values) {
  14437. this.setValues(values);
  14438. }
  14439. };
  14440. /**
  14441. * Save text track settings to localStorage
  14442. */
  14443. TextTrackSettings.prototype.saveSettings = function saveSettings() {
  14444. if (!this.options_.persistTextTrackSettings) {
  14445. return;
  14446. }
  14447. var values = this.getValues();
  14448. try {
  14449. if (Object.keys(values).length) {
  14450. window_1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
  14451. } else {
  14452. window_1.localStorage.removeItem(LOCAL_STORAGE_KEY);
  14453. }
  14454. } catch (err) {
  14455. log.warn(err);
  14456. }
  14457. };
  14458. /**
  14459. * Update display of text track settings
  14460. */
  14461. TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
  14462. var ttDisplay = this.player_.getChild('textTrackDisplay');
  14463. if (ttDisplay) {
  14464. ttDisplay.updateDisplay();
  14465. }
  14466. };
  14467. /**
  14468. * conditionally blur the element and refocus the captions button
  14469. *
  14470. * @private
  14471. */
  14472. TextTrackSettings.prototype.conditionalBlur_ = function conditionalBlur_() {
  14473. this.previouslyActiveEl_ = null;
  14474. this.off(document_1, 'keydown', this.handleKeyDown);
  14475. var cb = this.player_.controlBar;
  14476. var subsCapsBtn = cb && cb.subsCapsButton;
  14477. var ccBtn = cb && cb.captionsButton;
  14478. if (subsCapsBtn) {
  14479. subsCapsBtn.focus();
  14480. } else if (ccBtn) {
  14481. ccBtn.focus();
  14482. }
  14483. };
  14484. return TextTrackSettings;
  14485. }(ModalDialog);
  14486. Component.registerComponent('TextTrackSettings', TextTrackSettings);
  14487. /**
  14488. * @file resize-manager.js
  14489. */
  14490. /**
  14491. * A Resize Manager. It is in charge of triggering `playerresize` on the player in the right conditions.
  14492. *
  14493. * 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}.
  14494. *
  14495. * If the ResizeObserver is available natively, it will be used. A polyfill can be passed in as an option.
  14496. * If a `playerresize` event is not needed, the ResizeManager component can be removed from the player, see the example below.
  14497. * @example <caption>How to disable the resize manager</caption>
  14498. * const player = videojs('#vid', {
  14499. * resizeManager: false
  14500. * });
  14501. *
  14502. * @see {@link https://wicg.github.io/ResizeObserver/|ResizeObserver specification}
  14503. *
  14504. * @extends Component
  14505. */
  14506. var ResizeManager = function (_Component) {
  14507. inherits(ResizeManager, _Component);
  14508. /**
  14509. * Create the ResizeManager.
  14510. *
  14511. * @param {Object} player
  14512. * The `Player` that this class should be attached to.
  14513. *
  14514. * @param {Object} [options]
  14515. * The key/value store of ResizeManager options.
  14516. *
  14517. * @param {Object} [options.ResizeObserver]
  14518. * A polyfill for ResizeObserver can be passed in here.
  14519. * If this is set to null it will ignore the native ResizeObserver and fall back to the iframe fallback.
  14520. */
  14521. function ResizeManager(player, options) {
  14522. classCallCheck(this, ResizeManager);
  14523. var RESIZE_OBSERVER_AVAILABLE = options.ResizeObserver || window_1.ResizeObserver;
  14524. // if `null` was passed, we want to disable the ResizeObserver
  14525. if (options.ResizeObserver === null) {
  14526. RESIZE_OBSERVER_AVAILABLE = false;
  14527. }
  14528. // Only create an element when ResizeObserver isn't available
  14529. var options_ = mergeOptions({ createEl: !RESIZE_OBSERVER_AVAILABLE }, options);
  14530. var _this = possibleConstructorReturn(this, _Component.call(this, player, options_));
  14531. _this.ResizeObserver = options.ResizeObserver || window_1.ResizeObserver;
  14532. _this.loadListener_ = null;
  14533. _this.resizeObserver_ = null;
  14534. _this.debouncedHandler_ = debounce(function () {
  14535. _this.resizeHandler();
  14536. }, 100, false, _this);
  14537. if (RESIZE_OBSERVER_AVAILABLE) {
  14538. _this.resizeObserver_ = new _this.ResizeObserver(_this.debouncedHandler_);
  14539. _this.resizeObserver_.observe(player.el());
  14540. } else {
  14541. _this.loadListener_ = function () {
  14542. if (!_this.el_ || !_this.el_.contentWindow) {
  14543. return;
  14544. }
  14545. on(_this.el_.contentWindow, 'resize', _this.debouncedHandler_);
  14546. };
  14547. _this.one('load', _this.loadListener_);
  14548. }
  14549. return _this;
  14550. }
  14551. ResizeManager.prototype.createEl = function createEl() {
  14552. return _Component.prototype.createEl.call(this, 'iframe', {
  14553. className: 'vjs-resize-manager'
  14554. });
  14555. };
  14556. /**
  14557. * Called when a resize is triggered on the iframe or a resize is observed via the ResizeObserver
  14558. *
  14559. * @fires Player#playerresize
  14560. */
  14561. ResizeManager.prototype.resizeHandler = function resizeHandler() {
  14562. /**
  14563. * Called when the player size has changed
  14564. *
  14565. * @event Player#playerresize
  14566. * @type {EventTarget~Event}
  14567. */
  14568. // make sure player is still around to trigger
  14569. // prevents this from causing an error after dispose
  14570. if (!this.player_ || !this.player_.trigger) {
  14571. return;
  14572. }
  14573. this.player_.trigger('playerresize');
  14574. };
  14575. ResizeManager.prototype.dispose = function dispose() {
  14576. if (this.debouncedHandler_) {
  14577. this.debouncedHandler_.cancel();
  14578. }
  14579. if (this.resizeObserver_) {
  14580. if (this.player_.el()) {
  14581. this.resizeObserver_.unobserve(this.player_.el());
  14582. }
  14583. this.resizeObserver_.disconnect();
  14584. }
  14585. if (this.el_ && this.el_.contentWindow) {
  14586. off(this.el_.contentWindow, 'resize', this.debouncedHandler_);
  14587. }
  14588. if (this.loadListener_) {
  14589. this.off('load', this.loadListener_);
  14590. }
  14591. this.ResizeObserver = null;
  14592. this.resizeObserver = null;
  14593. this.debouncedHandler_ = null;
  14594. this.loadListener_ = null;
  14595. };
  14596. return ResizeManager;
  14597. }(Component);
  14598. Component.registerComponent('ResizeManager', ResizeManager);
  14599. /**
  14600. * This function is used to fire a sourceset when there is something
  14601. * similar to `mediaEl.load()` being called. It will try to find the source via
  14602. * the `src` attribute and then the `<source>` elements. It will then fire `sourceset`
  14603. * with the source that was found or empty string if we cannot know. If it cannot
  14604. * find a source then `sourceset` will not be fired.
  14605. *
  14606. * @param {Html5} tech
  14607. * The tech object that sourceset was setup on
  14608. *
  14609. * @return {boolean}
  14610. * returns false if the sourceset was not fired and true otherwise.
  14611. */
  14612. var sourcesetLoad = function sourcesetLoad(tech) {
  14613. var el = tech.el();
  14614. // if `el.src` is set, that source will be loaded.
  14615. if (el.hasAttribute('src')) {
  14616. tech.triggerSourceset(el.src);
  14617. return true;
  14618. }
  14619. /**
  14620. * Since there isn't a src property on the media element, source elements will be used for
  14621. * implementing the source selection algorithm. This happens asynchronously and
  14622. * for most cases were there is more than one source we cannot tell what source will
  14623. * be loaded, without re-implementing the source selection algorithm. At this time we are not
  14624. * going to do that. There are three special cases that we do handle here though:
  14625. *
  14626. * 1. If there are no sources, do not fire `sourceset`.
  14627. * 2. If there is only one `<source>` with a `src` property/attribute that is our `src`
  14628. * 3. If there is more than one `<source>` but all of them have the same `src` url.
  14629. * That will be our src.
  14630. */
  14631. var sources = tech.$$('source');
  14632. var srcUrls = [];
  14633. var src = '';
  14634. // if there are no sources, do not fire sourceset
  14635. if (!sources.length) {
  14636. return false;
  14637. }
  14638. // only count valid/non-duplicate source elements
  14639. for (var i = 0; i < sources.length; i++) {
  14640. var url = sources[i].src;
  14641. if (url && srcUrls.indexOf(url) === -1) {
  14642. srcUrls.push(url);
  14643. }
  14644. }
  14645. // there were no valid sources
  14646. if (!srcUrls.length) {
  14647. return false;
  14648. }
  14649. // there is only one valid source element url
  14650. // use that
  14651. if (srcUrls.length === 1) {
  14652. src = srcUrls[0];
  14653. }
  14654. tech.triggerSourceset(src);
  14655. return true;
  14656. };
  14657. /**
  14658. * our implementation of an `innerHTML` descriptor for browsers
  14659. * that do not have one.
  14660. */
  14661. var innerHTMLDescriptorPolyfill = {};
  14662. if (!IS_IE8) {
  14663. innerHTMLDescriptorPolyfill = Object.defineProperty({}, 'innerHTML', {
  14664. get: function get() {
  14665. return this.cloneNode(true).innerHTML;
  14666. },
  14667. set: function set(v) {
  14668. // make a dummy node to use innerHTML on
  14669. var dummy = document_1.createElement(this.nodeName.toLowerCase());
  14670. // set innerHTML to the value provided
  14671. dummy.innerHTML = v;
  14672. // make a document fragment to hold the nodes from dummy
  14673. var docFrag = document_1.createDocumentFragment();
  14674. // copy all of the nodes created by the innerHTML on dummy
  14675. // to the document fragment
  14676. while (dummy.childNodes.length) {
  14677. docFrag.appendChild(dummy.childNodes[0]);
  14678. }
  14679. // remove content
  14680. this.innerText = '';
  14681. // now we add all of that html in one by appending the
  14682. // document fragment. This is how innerHTML does it.
  14683. window_1.Element.prototype.appendChild.call(this, docFrag);
  14684. // then return the result that innerHTML's setter would
  14685. return this.innerHTML;
  14686. }
  14687. });
  14688. }
  14689. /**
  14690. * Get a property descriptor given a list of priorities and the
  14691. * property to get.
  14692. */
  14693. var getDescriptor = function getDescriptor(priority, prop) {
  14694. var descriptor = {};
  14695. for (var i = 0; i < priority.length; i++) {
  14696. descriptor = Object.getOwnPropertyDescriptor(priority[i], prop);
  14697. if (descriptor && descriptor.set && descriptor.get) {
  14698. break;
  14699. }
  14700. }
  14701. descriptor.enumerable = true;
  14702. descriptor.configurable = true;
  14703. return descriptor;
  14704. };
  14705. var getInnerHTMLDescriptor = function getInnerHTMLDescriptor(tech) {
  14706. return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, window_1.Element.prototype, innerHTMLDescriptorPolyfill], 'innerHTML');
  14707. };
  14708. /**
  14709. * Patches browser internal functions so that we can tell syncronously
  14710. * if a `<source>` was appended to the media element. For some reason this
  14711. * causes a `sourceset` if the the media element is ready and has no source.
  14712. * This happens when:
  14713. * - The page has just loaded and the media element does not have a source.
  14714. * - The media element was emptied of all sources, then `load()` was called.
  14715. *
  14716. * It does this by patching the following functions/properties when they are supported:
  14717. *
  14718. * - `append()` - can be used to add a `<source>` element to the media element
  14719. * - `appendChild()` - can be used to add a `<source>` element to the media element
  14720. * - `insertAdjacentHTML()` - can be used to add a `<source>` element to the media element
  14721. * - `innerHTML` - can be used to add a `<source>` element to the media element
  14722. *
  14723. * @param {Html5} tech
  14724. * The tech object that sourceset is being setup on.
  14725. */
  14726. var firstSourceWatch = function firstSourceWatch(tech) {
  14727. var el = tech.el();
  14728. // make sure firstSourceWatch isn't setup twice.
  14729. if (el.resetSourceWatch_) {
  14730. return;
  14731. }
  14732. var old = {};
  14733. var innerDescriptor = getInnerHTMLDescriptor(tech);
  14734. var appendWrapper = function appendWrapper(appendFn) {
  14735. return function () {
  14736. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  14737. args[_key] = arguments[_key];
  14738. }
  14739. var retval = appendFn.apply(el, args);
  14740. sourcesetLoad(tech);
  14741. return retval;
  14742. };
  14743. };
  14744. ['append', 'appendChild', 'insertAdjacentHTML'].forEach(function (k) {
  14745. if (!el[k]) {
  14746. return;
  14747. }
  14748. // store the old function
  14749. old[k] = el[k];
  14750. // call the old function with a sourceset if a source
  14751. // was loaded
  14752. el[k] = appendWrapper(old[k]);
  14753. });
  14754. Object.defineProperty(el, 'innerHTML', mergeOptions(innerDescriptor, {
  14755. set: appendWrapper(innerDescriptor.set)
  14756. }));
  14757. el.resetSourceWatch_ = function () {
  14758. el.resetSourceWatch_ = null;
  14759. Object.keys(old).forEach(function (k) {
  14760. el[k] = old[k];
  14761. });
  14762. Object.defineProperty(el, 'innerHTML', innerDescriptor);
  14763. };
  14764. // on the first sourceset, we need to revert our changes
  14765. tech.one('sourceset', el.resetSourceWatch_);
  14766. };
  14767. /**
  14768. * our implementation of a `src` descriptor for browsers
  14769. * that do not have one.
  14770. */
  14771. var srcDescriptorPolyfill = {};
  14772. if (!IS_IE8) {
  14773. srcDescriptorPolyfill = Object.defineProperty({}, 'src', {
  14774. get: function get() {
  14775. if (this.hasAttribute('src')) {
  14776. return getAbsoluteURL(window_1.Element.prototype.getAttribute.call(this, 'src'));
  14777. }
  14778. return '';
  14779. },
  14780. set: function set(v) {
  14781. window_1.Element.prototype.setAttribute.call(this, 'src', v);
  14782. return v;
  14783. }
  14784. });
  14785. }
  14786. var getSrcDescriptor = function getSrcDescriptor(tech) {
  14787. return getDescriptor([tech.el(), window_1.HTMLMediaElement.prototype, srcDescriptorPolyfill], 'src');
  14788. };
  14789. /**
  14790. * setup `sourceset` handling on the `Html5` tech. This function
  14791. * patches the following element properties/functions:
  14792. *
  14793. * - `src` - to determine when `src` is set
  14794. * - `setAttribute()` - to determine when `src` is set
  14795. * - `load()` - this re-triggers the source selection algorithm, and can
  14796. * cause a sourceset.
  14797. *
  14798. * If there is no source when we are adding `sourceset` support or during a `load()`
  14799. * we also patch the functions listed in `firstSourceWatch`.
  14800. *
  14801. * @param {Html5} tech
  14802. * The tech to patch
  14803. */
  14804. var setupSourceset = function setupSourceset(tech) {
  14805. if (!tech.featuresSourceset) {
  14806. return;
  14807. }
  14808. var el = tech.el();
  14809. // make sure sourceset isn't setup twice.
  14810. if (el.resetSourceset_) {
  14811. return;
  14812. }
  14813. var srcDescriptor = getSrcDescriptor(tech);
  14814. var oldSetAttribute = el.setAttribute;
  14815. var oldLoad = el.load;
  14816. Object.defineProperty(el, 'src', mergeOptions(srcDescriptor, {
  14817. set: function set(v) {
  14818. var retval = srcDescriptor.set.call(el, v);
  14819. // we use the getter here to get the actual value set on src
  14820. tech.triggerSourceset(el.src);
  14821. return retval;
  14822. }
  14823. }));
  14824. el.setAttribute = function (n, v) {
  14825. var retval = oldSetAttribute.call(el, n, v);
  14826. if (/src/i.test(n)) {
  14827. tech.triggerSourceset(el.src);
  14828. }
  14829. return retval;
  14830. };
  14831. el.load = function () {
  14832. var retval = oldLoad.call(el);
  14833. // if load was called, but there was no source to fire
  14834. // sourceset on. We have to watch for a source append
  14835. // as that can trigger a `sourceset` when the media element
  14836. // has no source
  14837. if (!sourcesetLoad(tech)) {
  14838. tech.triggerSourceset('');
  14839. firstSourceWatch(tech);
  14840. }
  14841. return retval;
  14842. };
  14843. if (el.currentSrc) {
  14844. tech.triggerSourceset(el.currentSrc);
  14845. } else if (!sourcesetLoad(tech)) {
  14846. firstSourceWatch(tech);
  14847. }
  14848. el.resetSourceset_ = function () {
  14849. el.resetSourceset_ = null;
  14850. el.load = oldLoad;
  14851. el.setAttribute = oldSetAttribute;
  14852. Object.defineProperty(el, 'src', srcDescriptor);
  14853. if (el.resetSourceWatch_) {
  14854. el.resetSourceWatch_();
  14855. }
  14856. };
  14857. };
  14858. 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.']);
  14859. /**
  14860. * @file html5.js
  14861. */
  14862. /**
  14863. * HTML5 Media Controller - Wrapper for HTML5 Media API
  14864. *
  14865. * @mixes Tech~SouceHandlerAdditions
  14866. * @extends Tech
  14867. */
  14868. var Html5 = function (_Tech) {
  14869. inherits(Html5, _Tech);
  14870. /**
  14871. * Create an instance of this Tech.
  14872. *
  14873. * @param {Object} [options]
  14874. * The key/value store of player options.
  14875. *
  14876. * @param {Component~ReadyCallback} ready
  14877. * Callback function to call when the `HTML5` Tech is ready.
  14878. */
  14879. function Html5(options, ready) {
  14880. classCallCheck(this, Html5);
  14881. var _this = possibleConstructorReturn(this, _Tech.call(this, options, ready));
  14882. var source = options.source;
  14883. var crossoriginTracks = false;
  14884. // Set the source if one is provided
  14885. // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
  14886. // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
  14887. // anyway so the error gets fired.
  14888. if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
  14889. _this.setSource(source);
  14890. } else {
  14891. _this.handleLateInit_(_this.el_);
  14892. }
  14893. // setup sourceset after late sourceset/init
  14894. if (options.enableSourceset) {
  14895. _this.setupSourcesetHandling_();
  14896. }
  14897. if (_this.el_.hasChildNodes()) {
  14898. var nodes = _this.el_.childNodes;
  14899. var nodesLength = nodes.length;
  14900. var removeNodes = [];
  14901. while (nodesLength--) {
  14902. var node = nodes[nodesLength];
  14903. var nodeName = node.nodeName.toLowerCase();
  14904. if (nodeName === 'track') {
  14905. if (!_this.featuresNativeTextTracks) {
  14906. // Empty video tag tracks so the built-in player doesn't use them also.
  14907. // This may not be fast enough to stop HTML5 browsers from reading the tags
  14908. // so we'll need to turn off any default tracks if we're manually doing
  14909. // captions and subtitles. videoElement.textTracks
  14910. removeNodes.push(node);
  14911. } else {
  14912. // store HTMLTrackElement and TextTrack to remote list
  14913. _this.remoteTextTrackEls().addTrackElement_(node);
  14914. _this.remoteTextTracks().addTrack(node.track);
  14915. _this.textTracks().addTrack(node.track);
  14916. if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
  14917. crossoriginTracks = true;
  14918. }
  14919. }
  14920. }
  14921. }
  14922. for (var i = 0; i < removeNodes.length; i++) {
  14923. _this.el_.removeChild(removeNodes[i]);
  14924. }
  14925. }
  14926. _this.proxyNativeTracks_();
  14927. if (_this.featuresNativeTextTracks && crossoriginTracks) {
  14928. log.warn(tsml(_templateObject$2));
  14929. }
  14930. // prevent iOS Safari from disabling metadata text tracks during native playback
  14931. _this.restoreMetadataTracksInIOSNativePlayer_();
  14932. // Determine if native controls should be used
  14933. // Our goal should be to get the custom controls on mobile solid everywhere
  14934. // so we can remove this all together. Right now this will block custom
  14935. // controls on touch enabled laptops like the Chrome Pixel
  14936. if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
  14937. _this.setControls(true);
  14938. }
  14939. // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
  14940. // into a `fullscreenchange` event
  14941. _this.proxyWebkitFullscreen_();
  14942. _this.triggerReady();
  14943. return _this;
  14944. }
  14945. /**
  14946. * Dispose of `HTML5` media element and remove all tracks.
  14947. */
  14948. Html5.prototype.dispose = function dispose() {
  14949. if (this.el_ && this.el_.resetSourceset_) {
  14950. this.el_.resetSourceset_();
  14951. }
  14952. Html5.disposeMediaElement(this.el_);
  14953. this.options_ = null;
  14954. // tech will handle clearing of the emulated track list
  14955. _Tech.prototype.dispose.call(this);
  14956. };
  14957. /**
  14958. * Modify the media element so that we can detect when
  14959. * the source is changed. Fires `sourceset` just after the source has changed
  14960. */
  14961. Html5.prototype.setupSourcesetHandling_ = function setupSourcesetHandling_() {
  14962. setupSourceset(this);
  14963. };
  14964. /**
  14965. * When a captions track is enabled in the iOS Safari native player, all other
  14966. * tracks are disabled (including metadata tracks), which nulls all of their
  14967. * associated cue points. This will restore metadata tracks to their pre-fullscreen
  14968. * state in those cases so that cue points are not needlessly lost.
  14969. *
  14970. * @private
  14971. */
  14972. Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
  14973. var textTracks = this.textTracks();
  14974. var metadataTracksPreFullscreenState = void 0;
  14975. // captures a snapshot of every metadata track's current state
  14976. var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
  14977. metadataTracksPreFullscreenState = [];
  14978. for (var i = 0; i < textTracks.length; i++) {
  14979. var track = textTracks[i];
  14980. if (track.kind === 'metadata') {
  14981. metadataTracksPreFullscreenState.push({
  14982. track: track,
  14983. storedMode: track.mode
  14984. });
  14985. }
  14986. }
  14987. };
  14988. // snapshot each metadata track's initial state, and update the snapshot
  14989. // each time there is a track 'change' event
  14990. takeMetadataTrackSnapshot();
  14991. textTracks.addEventListener('change', takeMetadataTrackSnapshot);
  14992. this.on('dispose', function () {
  14993. return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  14994. });
  14995. var restoreTrackMode = function restoreTrackMode() {
  14996. for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
  14997. var storedTrack = metadataTracksPreFullscreenState[i];
  14998. if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
  14999. storedTrack.track.mode = storedTrack.storedMode;
  15000. }
  15001. }
  15002. // we only want this handler to be executed on the first 'change' event
  15003. textTracks.removeEventListener('change', restoreTrackMode);
  15004. };
  15005. // when we enter fullscreen playback, stop updating the snapshot and
  15006. // restore all track modes to their pre-fullscreen state
  15007. this.on('webkitbeginfullscreen', function () {
  15008. textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  15009. // remove the listener before adding it just in case it wasn't previously removed
  15010. textTracks.removeEventListener('change', restoreTrackMode);
  15011. textTracks.addEventListener('change', restoreTrackMode);
  15012. });
  15013. // start updating the snapshot again after leaving fullscreen
  15014. this.on('webkitendfullscreen', function () {
  15015. // remove the listener before adding it just in case it wasn't previously removed
  15016. textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
  15017. textTracks.addEventListener('change', takeMetadataTrackSnapshot);
  15018. // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
  15019. textTracks.removeEventListener('change', restoreTrackMode);
  15020. });
  15021. };
  15022. /**
  15023. * Proxy all native track list events to our track lists if the browser we are playing
  15024. * in supports that type of track list.
  15025. *
  15026. * @private
  15027. */
  15028. Html5.prototype.proxyNativeTracks_ = function proxyNativeTracks_() {
  15029. var _this2 = this;
  15030. NORMAL.names.forEach(function (name) {
  15031. var props = NORMAL[name];
  15032. var elTracks = _this2.el()[props.getterName];
  15033. var techTracks = _this2[props.getterName]();
  15034. if (!_this2['featuresNative' + props.capitalName + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
  15035. return;
  15036. }
  15037. var listeners = {
  15038. change: function change(e) {
  15039. techTracks.trigger({
  15040. type: 'change',
  15041. target: techTracks,
  15042. currentTarget: techTracks,
  15043. srcElement: techTracks
  15044. });
  15045. },
  15046. addtrack: function addtrack(e) {
  15047. techTracks.addTrack(e.track);
  15048. },
  15049. removetrack: function removetrack(e) {
  15050. techTracks.removeTrack(e.track);
  15051. }
  15052. };
  15053. var removeOldTracks = function removeOldTracks() {
  15054. var removeTracks = [];
  15055. for (var i = 0; i < techTracks.length; i++) {
  15056. var found = false;
  15057. for (var j = 0; j < elTracks.length; j++) {
  15058. if (elTracks[j] === techTracks[i]) {
  15059. found = true;
  15060. break;
  15061. }
  15062. }
  15063. if (!found) {
  15064. removeTracks.push(techTracks[i]);
  15065. }
  15066. }
  15067. while (removeTracks.length) {
  15068. techTracks.removeTrack(removeTracks.shift());
  15069. }
  15070. };
  15071. Object.keys(listeners).forEach(function (eventName) {
  15072. var listener = listeners[eventName];
  15073. elTracks.addEventListener(eventName, listener);
  15074. _this2.on('dispose', function (e) {
  15075. return elTracks.removeEventListener(eventName, listener);
  15076. });
  15077. });
  15078. // Remove (native) tracks that are not used anymore
  15079. _this2.on('loadstart', removeOldTracks);
  15080. _this2.on('dispose', function (e) {
  15081. return _this2.off('loadstart', removeOldTracks);
  15082. });
  15083. });
  15084. };
  15085. /**
  15086. * Create the `Html5` Tech's DOM element.
  15087. *
  15088. * @return {Element}
  15089. * The element that gets created.
  15090. */
  15091. Html5.prototype.createEl = function createEl$$1() {
  15092. var el = this.options_.tag;
  15093. // Check if this browser supports moving the element into the box.
  15094. // On the iPhone video will break if you move the element,
  15095. // So we have to create a brand new element.
  15096. // If we ingested the player div, we do not need to move the media element.
  15097. if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
  15098. // If the original tag is still there, clone and remove it.
  15099. if (el) {
  15100. var clone = el.cloneNode(true);
  15101. if (el.parentNode) {
  15102. el.parentNode.insertBefore(clone, el);
  15103. }
  15104. Html5.disposeMediaElement(el);
  15105. el = clone;
  15106. } else {
  15107. el = document_1.createElement('video');
  15108. // determine if native controls should be used
  15109. var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
  15110. var attributes = mergeOptions({}, tagAttributes);
  15111. if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
  15112. delete attributes.controls;
  15113. }
  15114. setAttributes(el, assign(attributes, {
  15115. id: this.options_.techId,
  15116. 'class': 'vjs-tech'
  15117. }));
  15118. }
  15119. el.playerId = this.options_.playerId;
  15120. }
  15121. if (typeof this.options_.preload !== 'undefined') {
  15122. setAttribute(el, 'preload', this.options_.preload);
  15123. }
  15124. // Update specific tag settings, in case they were overridden
  15125. // `autoplay` has to be *last* so that `muted` and `playsinline` are present
  15126. // when iOS/Safari or other browsers attempt to autoplay.
  15127. var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
  15128. for (var i = 0; i < settingsAttrs.length; i++) {
  15129. var attr = settingsAttrs[i];
  15130. var value = this.options_[attr];
  15131. if (typeof value !== 'undefined') {
  15132. if (value) {
  15133. setAttribute(el, attr, attr);
  15134. } else {
  15135. removeAttribute(el, attr);
  15136. }
  15137. el[attr] = value;
  15138. }
  15139. }
  15140. return el;
  15141. };
  15142. /**
  15143. * This will be triggered if the loadstart event has already fired, before videojs was
  15144. * ready. Two known examples of when this can happen are:
  15145. * 1. If we're loading the playback object after it has started loading
  15146. * 2. The media is already playing the (often with autoplay on) then
  15147. *
  15148. * This function will fire another loadstart so that videojs can catchup.
  15149. *
  15150. * @fires Tech#loadstart
  15151. *
  15152. * @return {undefined}
  15153. * returns nothing.
  15154. */
  15155. Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
  15156. if (el.networkState === 0 || el.networkState === 3) {
  15157. // The video element hasn't started loading the source yet
  15158. // or didn't find a source
  15159. return;
  15160. }
  15161. if (el.readyState === 0) {
  15162. // NetworkState is set synchronously BUT loadstart is fired at the
  15163. // end of the current stack, usually before setInterval(fn, 0).
  15164. // So at this point we know loadstart may have already fired or is
  15165. // about to fire, and either way the player hasn't seen it yet.
  15166. // We don't want to fire loadstart prematurely here and cause a
  15167. // double loadstart so we'll wait and see if it happens between now
  15168. // and the next loop, and fire it if not.
  15169. // HOWEVER, we also want to make sure it fires before loadedmetadata
  15170. // which could also happen between now and the next loop, so we'll
  15171. // watch for that also.
  15172. var loadstartFired = false;
  15173. var setLoadstartFired = function setLoadstartFired() {
  15174. loadstartFired = true;
  15175. };
  15176. this.on('loadstart', setLoadstartFired);
  15177. var triggerLoadstart = function triggerLoadstart() {
  15178. // We did miss the original loadstart. Make sure the player
  15179. // sees loadstart before loadedmetadata
  15180. if (!loadstartFired) {
  15181. this.trigger('loadstart');
  15182. }
  15183. };
  15184. this.on('loadedmetadata', triggerLoadstart);
  15185. this.ready(function () {
  15186. this.off('loadstart', setLoadstartFired);
  15187. this.off('loadedmetadata', triggerLoadstart);
  15188. if (!loadstartFired) {
  15189. // We did miss the original native loadstart. Fire it now.
  15190. this.trigger('loadstart');
  15191. }
  15192. });
  15193. return;
  15194. }
  15195. // From here on we know that loadstart already fired and we missed it.
  15196. // The other readyState events aren't as much of a problem if we double
  15197. // them, so not going to go to as much trouble as loadstart to prevent
  15198. // that unless we find reason to.
  15199. var eventsToTrigger = ['loadstart'];
  15200. // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
  15201. eventsToTrigger.push('loadedmetadata');
  15202. // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
  15203. if (el.readyState >= 2) {
  15204. eventsToTrigger.push('loadeddata');
  15205. }
  15206. // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
  15207. if (el.readyState >= 3) {
  15208. eventsToTrigger.push('canplay');
  15209. }
  15210. // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
  15211. if (el.readyState >= 4) {
  15212. eventsToTrigger.push('canplaythrough');
  15213. }
  15214. // We still need to give the player time to add event listeners
  15215. this.ready(function () {
  15216. eventsToTrigger.forEach(function (type) {
  15217. this.trigger(type);
  15218. }, this);
  15219. });
  15220. };
  15221. /**
  15222. * Set current time for the `HTML5` tech.
  15223. *
  15224. * @param {number} seconds
  15225. * Set the current time of the media to this.
  15226. */
  15227. Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
  15228. try {
  15229. this.el_.currentTime = seconds;
  15230. } catch (e) {
  15231. log(e, 'Video is not ready. (Video.js)');
  15232. // this.warning(VideoJS.warnings.videoNotReady);
  15233. }
  15234. };
  15235. /**
  15236. * Get the current duration of the HTML5 media element.
  15237. *
  15238. * @return {number}
  15239. * The duration of the media or 0 if there is no duration.
  15240. */
  15241. Html5.prototype.duration = function duration() {
  15242. var _this3 = this;
  15243. // Android Chrome will report duration as Infinity for VOD HLS until after
  15244. // playback has started, which triggers the live display erroneously.
  15245. // Return NaN if playback has not started and trigger a durationupdate once
  15246. // the duration can be reliably known.
  15247. if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
  15248. // Wait for the first `timeupdate` with currentTime > 0 - there may be
  15249. // several with 0
  15250. var checkProgress = function checkProgress() {
  15251. if (_this3.el_.currentTime > 0) {
  15252. // Trigger durationchange for genuinely live video
  15253. if (_this3.el_.duration === Infinity) {
  15254. _this3.trigger('durationchange');
  15255. }
  15256. _this3.off('timeupdate', checkProgress);
  15257. }
  15258. };
  15259. this.on('timeupdate', checkProgress);
  15260. return NaN;
  15261. }
  15262. return this.el_.duration || NaN;
  15263. };
  15264. /**
  15265. * Get the current width of the HTML5 media element.
  15266. *
  15267. * @return {number}
  15268. * The width of the HTML5 media element.
  15269. */
  15270. Html5.prototype.width = function width() {
  15271. return this.el_.offsetWidth;
  15272. };
  15273. /**
  15274. * Get the current height of the HTML5 media element.
  15275. *
  15276. * @return {number}
  15277. * The heigth of the HTML5 media element.
  15278. */
  15279. Html5.prototype.height = function height() {
  15280. return this.el_.offsetHeight;
  15281. };
  15282. /**
  15283. * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
  15284. * `fullscreenchange` event.
  15285. *
  15286. * @private
  15287. * @fires fullscreenchange
  15288. * @listens webkitendfullscreen
  15289. * @listens webkitbeginfullscreen
  15290. * @listens webkitbeginfullscreen
  15291. */
  15292. Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
  15293. var _this4 = this;
  15294. if (!('webkitDisplayingFullscreen' in this.el_)) {
  15295. return;
  15296. }
  15297. var endFn = function endFn() {
  15298. this.trigger('fullscreenchange', { isFullscreen: false });
  15299. };
  15300. var beginFn = function beginFn() {
  15301. if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
  15302. this.one('webkitendfullscreen', endFn);
  15303. this.trigger('fullscreenchange', { isFullscreen: true });
  15304. }
  15305. };
  15306. this.on('webkitbeginfullscreen', beginFn);
  15307. this.on('dispose', function () {
  15308. _this4.off('webkitbeginfullscreen', beginFn);
  15309. _this4.off('webkitendfullscreen', endFn);
  15310. });
  15311. };
  15312. /**
  15313. * Check if fullscreen is supported on the current playback device.
  15314. *
  15315. * @return {boolean}
  15316. * - True if fullscreen is supported.
  15317. * - False if fullscreen is not supported.
  15318. */
  15319. Html5.prototype.supportsFullScreen = function supportsFullScreen() {
  15320. if (typeof this.el_.webkitEnterFullScreen === 'function') {
  15321. var userAgent = window_1.navigator && window_1.navigator.userAgent || '';
  15322. // Seems to be broken in Chromium/Chrome && Safari in Leopard
  15323. if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
  15324. return true;
  15325. }
  15326. }
  15327. return false;
  15328. };
  15329. /**
  15330. * Request that the `HTML5` Tech enter fullscreen.
  15331. */
  15332. Html5.prototype.enterFullScreen = function enterFullScreen() {
  15333. var video = this.el_;
  15334. if (video.paused && video.networkState <= video.HAVE_METADATA) {
  15335. // attempt to prime the video element for programmatic access
  15336. // this isn't necessary on the desktop but shouldn't hurt
  15337. this.el_.play();
  15338. // playing and pausing synchronously during the transition to fullscreen
  15339. // can get iOS ~6.1 devices into a play/pause loop
  15340. this.setTimeout(function () {
  15341. video.pause();
  15342. video.webkitEnterFullScreen();
  15343. }, 0);
  15344. } else {
  15345. video.webkitEnterFullScreen();
  15346. }
  15347. };
  15348. /**
  15349. * Request that the `HTML5` Tech exit fullscreen.
  15350. */
  15351. Html5.prototype.exitFullScreen = function exitFullScreen() {
  15352. this.el_.webkitExitFullScreen();
  15353. };
  15354. /**
  15355. * A getter/setter for the `Html5` Tech's source object.
  15356. * > Note: Please use {@link Html5#setSource}
  15357. *
  15358. * @param {Tech~SourceObject} [src]
  15359. * The source object you want to set on the `HTML5` techs element.
  15360. *
  15361. * @return {Tech~SourceObject|undefined}
  15362. * - The current source object when a source is not passed in.
  15363. * - undefined when setting
  15364. *
  15365. * @deprecated Since version 5.
  15366. */
  15367. Html5.prototype.src = function src(_src) {
  15368. if (_src === undefined) {
  15369. return this.el_.src;
  15370. }
  15371. // Setting src through `src` instead of `setSrc` will be deprecated
  15372. this.setSrc(_src);
  15373. };
  15374. /**
  15375. * Reset the tech by removing all sources and then calling
  15376. * {@link Html5.resetMediaElement}.
  15377. */
  15378. Html5.prototype.reset = function reset() {
  15379. Html5.resetMediaElement(this.el_);
  15380. };
  15381. /**
  15382. * Get the current source on the `HTML5` Tech. Falls back to returning the source from
  15383. * the HTML5 media element.
  15384. *
  15385. * @return {Tech~SourceObject}
  15386. * The current source object from the HTML5 tech. With a fallback to the
  15387. * elements source.
  15388. */
  15389. Html5.prototype.currentSrc = function currentSrc() {
  15390. if (this.currentSource_) {
  15391. return this.currentSource_.src;
  15392. }
  15393. return this.el_.currentSrc;
  15394. };
  15395. /**
  15396. * Set controls attribute for the HTML5 media Element.
  15397. *
  15398. * @param {string} val
  15399. * Value to set the controls attribute to
  15400. */
  15401. Html5.prototype.setControls = function setControls(val) {
  15402. this.el_.controls = !!val;
  15403. };
  15404. /**
  15405. * Create and returns a remote {@link TextTrack} object.
  15406. *
  15407. * @param {string} kind
  15408. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
  15409. *
  15410. * @param {string} [label]
  15411. * Label to identify the text track
  15412. *
  15413. * @param {string} [language]
  15414. * Two letter language abbreviation
  15415. *
  15416. * @return {TextTrack}
  15417. * The TextTrack that gets created.
  15418. */
  15419. Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  15420. if (!this.featuresNativeTextTracks) {
  15421. return _Tech.prototype.addTextTrack.call(this, kind, label, language);
  15422. }
  15423. return this.el_.addTextTrack(kind, label, language);
  15424. };
  15425. /**
  15426. * Creates either native TextTrack or an emulated TextTrack depending
  15427. * on the value of `featuresNativeTextTracks`
  15428. *
  15429. * @param {Object} options
  15430. * The object should contain the options to intialize the TextTrack with.
  15431. *
  15432. * @param {string} [options.kind]
  15433. * `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
  15434. *
  15435. * @param {string} [options.label].
  15436. * Label to identify the text track
  15437. *
  15438. * @param {string} [options.language]
  15439. * Two letter language abbreviation.
  15440. *
  15441. * @param {boolean} [options.default]
  15442. * Default this track to on.
  15443. *
  15444. * @param {string} [options.id]
  15445. * The internal id to assign this track.
  15446. *
  15447. * @param {string} [options.src]
  15448. * A source url for the track.
  15449. *
  15450. * @return {HTMLTrackElement}
  15451. * The track element that gets created.
  15452. */
  15453. Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
  15454. if (!this.featuresNativeTextTracks) {
  15455. return _Tech.prototype.createRemoteTextTrack.call(this, options);
  15456. }
  15457. var htmlTrackElement = document_1.createElement('track');
  15458. if (options.kind) {
  15459. htmlTrackElement.kind = options.kind;
  15460. }
  15461. if (options.label) {
  15462. htmlTrackElement.label = options.label;
  15463. }
  15464. if (options.language || options.srclang) {
  15465. htmlTrackElement.srclang = options.language || options.srclang;
  15466. }
  15467. if (options['default']) {
  15468. htmlTrackElement['default'] = options['default'];
  15469. }
  15470. if (options.id) {
  15471. htmlTrackElement.id = options.id;
  15472. }
  15473. if (options.src) {
  15474. htmlTrackElement.src = options.src;
  15475. }
  15476. return htmlTrackElement;
  15477. };
  15478. /**
  15479. * Creates a remote text track object and returns an html track element.
  15480. *
  15481. * @param {Object} options The object should contain values for
  15482. * kind, language, label, and src (location of the WebVTT file)
  15483. * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
  15484. * automatically removed from the video element whenever the source changes
  15485. * @return {HTMLTrackElement} An Html Track Element.
  15486. * This can be an emulated {@link HTMLTrackElement} or a native one.
  15487. * @deprecated The default value of the "manualCleanup" parameter will default
  15488. * to "false" in upcoming versions of Video.js
  15489. */
  15490. Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
  15491. var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
  15492. if (this.featuresNativeTextTracks) {
  15493. this.el().appendChild(htmlTrackElement);
  15494. }
  15495. return htmlTrackElement;
  15496. };
  15497. /**
  15498. * Remove remote `TextTrack` from `TextTrackList` object
  15499. *
  15500. * @param {TextTrack} track
  15501. * `TextTrack` object to remove
  15502. */
  15503. Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
  15504. _Tech.prototype.removeRemoteTextTrack.call(this, track);
  15505. if (this.featuresNativeTextTracks) {
  15506. var tracks = this.$$('track');
  15507. var i = tracks.length;
  15508. while (i--) {
  15509. if (track === tracks[i] || track === tracks[i].track) {
  15510. this.el().removeChild(tracks[i]);
  15511. }
  15512. }
  15513. }
  15514. };
  15515. /**
  15516. * Gets available media playback quality metrics as specified by the W3C's Media
  15517. * Playback Quality API.
  15518. *
  15519. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  15520. *
  15521. * @return {Object}
  15522. * An object with supported media playback quality metrics
  15523. */
  15524. Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  15525. if (typeof this.el().getVideoPlaybackQuality === 'function') {
  15526. return this.el().getVideoPlaybackQuality();
  15527. }
  15528. var videoPlaybackQuality = {};
  15529. if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
  15530. videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
  15531. videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
  15532. }
  15533. if (window_1.performance && typeof window_1.performance.now === 'function') {
  15534. videoPlaybackQuality.creationTime = window_1.performance.now();
  15535. } else if (window_1.performance && window_1.performance.timing && typeof window_1.performance.timing.navigationStart === 'number') {
  15536. videoPlaybackQuality.creationTime = window_1.Date.now() - window_1.performance.timing.navigationStart;
  15537. }
  15538. return videoPlaybackQuality;
  15539. };
  15540. return Html5;
  15541. }(Tech);
  15542. /* HTML5 Support Testing ---------------------------------------------------- */
  15543. if (isReal()) {
  15544. /**
  15545. * Element for testing browser HTML5 media capabilities
  15546. *
  15547. * @type {Element}
  15548. * @constant
  15549. * @private
  15550. */
  15551. Html5.TEST_VID = document_1.createElement('video');
  15552. var track = document_1.createElement('track');
  15553. track.kind = 'captions';
  15554. track.srclang = 'en';
  15555. track.label = 'English';
  15556. Html5.TEST_VID.appendChild(track);
  15557. }
  15558. /**
  15559. * Check if HTML5 media is supported by this browser/device.
  15560. *
  15561. * @return {boolean}
  15562. * - True if HTML5 media is supported.
  15563. * - False if HTML5 media is not supported.
  15564. */
  15565. Html5.isSupported = function () {
  15566. // IE9 with no Media Player is a LIAR! (#984)
  15567. try {
  15568. Html5.TEST_VID.volume = 0.5;
  15569. } catch (e) {
  15570. return false;
  15571. }
  15572. return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
  15573. };
  15574. /**
  15575. * Check if the tech can support the given type
  15576. *
  15577. * @param {string} type
  15578. * The mimetype to check
  15579. * @return {string} 'probably', 'maybe', or '' (empty string)
  15580. */
  15581. Html5.canPlayType = function (type) {
  15582. return Html5.TEST_VID.canPlayType(type);
  15583. };
  15584. /**
  15585. * Check if the tech can support the given source
  15586. * @param {Object} srcObj
  15587. * The source object
  15588. * @param {Object} options
  15589. * The options passed to the tech
  15590. * @return {string} 'probably', 'maybe', or '' (empty string)
  15591. */
  15592. Html5.canPlaySource = function (srcObj, options) {
  15593. return Html5.canPlayType(srcObj.type);
  15594. };
  15595. /**
  15596. * Check if the volume can be changed in this browser/device.
  15597. * Volume cannot be changed in a lot of mobile devices.
  15598. * Specifically, it can't be changed from 1 on iOS.
  15599. *
  15600. * @return {boolean}
  15601. * - True if volume can be controlled
  15602. * - False otherwise
  15603. */
  15604. Html5.canControlVolume = function () {
  15605. // IE will error if Windows Media Player not installed #3315
  15606. try {
  15607. var volume = Html5.TEST_VID.volume;
  15608. Html5.TEST_VID.volume = volume / 2 + 0.1;
  15609. return volume !== Html5.TEST_VID.volume;
  15610. } catch (e) {
  15611. return false;
  15612. }
  15613. };
  15614. /**
  15615. * Check if the volume can be muted in this browser/device.
  15616. * Some devices, e.g. iOS, don't allow changing volume
  15617. * but permits muting/unmuting.
  15618. *
  15619. * @return {bolean}
  15620. * - True if volume can be muted
  15621. * - False otherwise
  15622. */
  15623. Html5.canMuteVolume = function () {
  15624. try {
  15625. var muted = Html5.TEST_VID.muted;
  15626. // in some versions of iOS muted property doesn't always
  15627. // work, so we want to set both property and attribute
  15628. Html5.TEST_VID.muted = !muted;
  15629. if (Html5.TEST_VID.muted) {
  15630. setAttribute(Html5.TEST_VID, 'muted', 'muted');
  15631. } else {
  15632. removeAttribute(Html5.TEST_VID, 'muted', 'muted');
  15633. }
  15634. return muted !== Html5.TEST_VID.muted;
  15635. } catch (e) {
  15636. return false;
  15637. }
  15638. };
  15639. /**
  15640. * Check if the playback rate can be changed in this browser/device.
  15641. *
  15642. * @return {boolean}
  15643. * - True if playback rate can be controlled
  15644. * - False otherwise
  15645. */
  15646. Html5.canControlPlaybackRate = function () {
  15647. // Playback rate API is implemented in Android Chrome, but doesn't do anything
  15648. // https://github.com/videojs/video.js/issues/3180
  15649. if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
  15650. return false;
  15651. }
  15652. // IE will error if Windows Media Player not installed #3315
  15653. try {
  15654. var playbackRate = Html5.TEST_VID.playbackRate;
  15655. Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
  15656. return playbackRate !== Html5.TEST_VID.playbackRate;
  15657. } catch (e) {
  15658. return false;
  15659. }
  15660. };
  15661. /**
  15662. * Check if we can override a video/audio elements attributes, with
  15663. * Object.defineProperty.
  15664. *
  15665. * @return {boolean}
  15666. * - True if builtin attributes can be overriden
  15667. * - False otherwise
  15668. */
  15669. Html5.canOverrideAttributes = function () {
  15670. if (IS_IE8) {
  15671. return false;
  15672. }
  15673. // if we cannot overwrite the src/innerHTML property, there is no support
  15674. // iOS 7 safari for instance cannot do this.
  15675. try {
  15676. var noop = function noop() {};
  15677. Object.defineProperty(document_1.createElement('video'), 'src', { get: noop, set: noop });
  15678. Object.defineProperty(document_1.createElement('audio'), 'src', { get: noop, set: noop });
  15679. Object.defineProperty(document_1.createElement('video'), 'innerHTML', { get: noop, set: noop });
  15680. Object.defineProperty(document_1.createElement('audio'), 'innerHTML', { get: noop, set: noop });
  15681. } catch (e) {
  15682. return false;
  15683. }
  15684. return true;
  15685. };
  15686. /**
  15687. * Check to see if native `TextTrack`s are supported by this browser/device.
  15688. *
  15689. * @return {boolean}
  15690. * - True if native `TextTrack`s are supported.
  15691. * - False otherwise
  15692. */
  15693. Html5.supportsNativeTextTracks = function () {
  15694. return IS_ANY_SAFARI || IS_IOS && IS_CHROME;
  15695. };
  15696. /**
  15697. * Check to see if native `VideoTrack`s are supported by this browser/device
  15698. *
  15699. * @return {boolean}
  15700. * - True if native `VideoTrack`s are supported.
  15701. * - False otherwise
  15702. */
  15703. Html5.supportsNativeVideoTracks = function () {
  15704. return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
  15705. };
  15706. /**
  15707. * Check to see if native `AudioTrack`s are supported by this browser/device
  15708. *
  15709. * @return {boolean}
  15710. * - True if native `AudioTrack`s are supported.
  15711. * - False otherwise
  15712. */
  15713. Html5.supportsNativeAudioTracks = function () {
  15714. return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
  15715. };
  15716. /**
  15717. * An array of events available on the Html5 tech.
  15718. *
  15719. * @private
  15720. * @type {Array}
  15721. */
  15722. 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'];
  15723. /**
  15724. * Boolean indicating whether the `Tech` supports volume control.
  15725. *
  15726. * @type {boolean}
  15727. * @default {@link Html5.canControlVolume}
  15728. */
  15729. Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
  15730. /**
  15731. * Boolean indicating whether the `Tech` supports muting volume.
  15732. *
  15733. * @type {bolean}
  15734. * @default {@link Html5.canMuteVolume}
  15735. */
  15736. Html5.prototype.featuresMuteControl = Html5.canMuteVolume();
  15737. /**
  15738. * Boolean indicating whether the `Tech` supports changing the speed at which the media
  15739. * plays. Examples:
  15740. * - Set player to play 2x (twice) as fast
  15741. * - Set player to play 0.5x (half) as fast
  15742. *
  15743. * @type {boolean}
  15744. * @default {@link Html5.canControlPlaybackRate}
  15745. */
  15746. Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
  15747. /**
  15748. * Boolean indicating wether the `Tech` supports the `sourceset` event.
  15749. *
  15750. * @type {boolean}
  15751. * @default
  15752. */
  15753. Html5.prototype.featuresSourceset = Html5.canOverrideAttributes();
  15754. /**
  15755. * Boolean indicating whether the `HTML5` tech currently supports the media element
  15756. * moving in the DOM. iOS breaks if you move the media element, so this is set this to
  15757. * false there. Everywhere else this should be true.
  15758. *
  15759. * @type {boolean}
  15760. * @default
  15761. */
  15762. Html5.prototype.movingMediaElementInDOM = !IS_IOS;
  15763. // TODO: Previous comment: No longer appears to be used. Can probably be removed.
  15764. // Is this true?
  15765. /**
  15766. * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
  15767. * when going into fullscreen.
  15768. *
  15769. * @type {boolean}
  15770. * @default
  15771. */
  15772. Html5.prototype.featuresFullscreenResize = true;
  15773. /**
  15774. * Boolean indicating whether the `HTML5` tech currently supports the progress event.
  15775. * If this is false, manual `progress` events will be triggred instead.
  15776. *
  15777. * @type {boolean}
  15778. * @default
  15779. */
  15780. Html5.prototype.featuresProgressEvents = true;
  15781. /**
  15782. * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
  15783. * If this is false, manual `timeupdate` events will be triggred instead.
  15784. *
  15785. * @default
  15786. */
  15787. Html5.prototype.featuresTimeupdateEvents = true;
  15788. /**
  15789. * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
  15790. *
  15791. * @type {boolean}
  15792. * @default {@link Html5.supportsNativeTextTracks}
  15793. */
  15794. Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
  15795. /**
  15796. * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
  15797. *
  15798. * @type {boolean}
  15799. * @default {@link Html5.supportsNativeVideoTracks}
  15800. */
  15801. Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
  15802. /**
  15803. * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
  15804. *
  15805. * @type {boolean}
  15806. * @default {@link Html5.supportsNativeAudioTracks}
  15807. */
  15808. Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
  15809. // HTML5 Feature detection and Device Fixes --------------------------------- //
  15810. var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
  15811. var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
  15812. var mp4RE = /^video\/mp4/i;
  15813. Html5.patchCanPlayType = function () {
  15814. // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
  15815. // Firefox and Chrome report correctly
  15816. if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX && !IS_CHROME) {
  15817. Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
  15818. if (type && mpegurlRE.test(type)) {
  15819. return 'maybe';
  15820. }
  15821. return canPlayType.call(this, type);
  15822. };
  15823. // Override Android 2.2 and less canPlayType method which is broken
  15824. } else if (IS_OLD_ANDROID) {
  15825. Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
  15826. if (type && mp4RE.test(type)) {
  15827. return 'maybe';
  15828. }
  15829. return canPlayType.call(this, type);
  15830. };
  15831. }
  15832. };
  15833. Html5.unpatchCanPlayType = function () {
  15834. var r = Html5.TEST_VID.constructor.prototype.canPlayType;
  15835. Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
  15836. return r;
  15837. };
  15838. // by default, patch the media element
  15839. Html5.patchCanPlayType();
  15840. Html5.disposeMediaElement = function (el) {
  15841. if (!el) {
  15842. return;
  15843. }
  15844. if (el.parentNode) {
  15845. el.parentNode.removeChild(el);
  15846. }
  15847. // remove any child track or source nodes to prevent their loading
  15848. while (el.hasChildNodes()) {
  15849. el.removeChild(el.firstChild);
  15850. }
  15851. // remove any src reference. not setting `src=''` because that causes a warning
  15852. // in firefox
  15853. el.removeAttribute('src');
  15854. // force the media element to update its loading state by calling load()
  15855. // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
  15856. if (typeof el.load === 'function') {
  15857. // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
  15858. (function () {
  15859. try {
  15860. el.load();
  15861. } catch (e) {
  15862. // not supported
  15863. }
  15864. })();
  15865. }
  15866. };
  15867. Html5.resetMediaElement = function (el) {
  15868. if (!el) {
  15869. return;
  15870. }
  15871. var sources = el.querySelectorAll('source');
  15872. var i = sources.length;
  15873. while (i--) {
  15874. el.removeChild(sources[i]);
  15875. }
  15876. // remove any src reference.
  15877. // not setting `src=''` because that throws an error
  15878. el.removeAttribute('src');
  15879. if (typeof el.load === 'function') {
  15880. // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
  15881. (function () {
  15882. try {
  15883. el.load();
  15884. } catch (e) {
  15885. // satisfy linter
  15886. }
  15887. })();
  15888. }
  15889. };
  15890. /* Native HTML5 element property wrapping ----------------------------------- */
  15891. // Wrap native boolean attributes with getters that check both property and attribute
  15892. // The list is as followed:
  15893. // muted, defaultMuted, autoplay, controls, loop, playsinline
  15894. [
  15895. /**
  15896. * Get the value of `muted` from the media element. `muted` indicates
  15897. * that the volume for the media should be set to silent. This does not actually change
  15898. * the `volume` attribute.
  15899. *
  15900. * @method Html5#muted
  15901. * @return {boolean}
  15902. * - True if the value of `volume` should be ignored and the audio set to silent.
  15903. * - False if the value of `volume` should be used.
  15904. *
  15905. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
  15906. */
  15907. 'muted',
  15908. /**
  15909. * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
  15910. * whether the media should start muted or not. Only changes the default state of the
  15911. * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
  15912. * current state.
  15913. *
  15914. * @method Html5#defaultMuted
  15915. * @return {boolean}
  15916. * - The value of `defaultMuted` from the media element.
  15917. * - True indicates that the media should start muted.
  15918. * - False indicates that the media should not start muted
  15919. *
  15920. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
  15921. */
  15922. 'defaultMuted',
  15923. /**
  15924. * Get the value of `autoplay` from the media element. `autoplay` indicates
  15925. * that the media should start to play as soon as the page is ready.
  15926. *
  15927. * @method Html5#autoplay
  15928. * @return {boolean}
  15929. * - The value of `autoplay` from the media element.
  15930. * - True indicates that the media should start as soon as the page loads.
  15931. * - False indicates that the media should not start as soon as the page loads.
  15932. *
  15933. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
  15934. */
  15935. 'autoplay',
  15936. /**
  15937. * Get the value of `controls` from the media element. `controls` indicates
  15938. * whether the native media controls should be shown or hidden.
  15939. *
  15940. * @method Html5#controls
  15941. * @return {boolean}
  15942. * - The value of `controls` from the media element.
  15943. * - True indicates that native controls should be showing.
  15944. * - False indicates that native controls should be hidden.
  15945. *
  15946. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
  15947. */
  15948. 'controls',
  15949. /**
  15950. * Get the value of `loop` from the media element. `loop` indicates
  15951. * that the media should return to the start of the media and continue playing once
  15952. * it reaches the end.
  15953. *
  15954. * @method Html5#loop
  15955. * @return {boolean}
  15956. * - The value of `loop` from the media element.
  15957. * - True indicates that playback should seek back to start once
  15958. * the end of a media is reached.
  15959. * - False indicates that playback should not loop back to the start when the
  15960. * end of the media is reached.
  15961. *
  15962. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
  15963. */
  15964. 'loop',
  15965. /**
  15966. * Get the value of `playsinline` from the media element. `playsinline` indicates
  15967. * to the browser that non-fullscreen playback is preferred when fullscreen
  15968. * playback is the native default, such as in iOS Safari.
  15969. *
  15970. * @method Html5#playsinline
  15971. * @return {boolean}
  15972. * - The value of `playsinline` from the media element.
  15973. * - True indicates that the media should play inline.
  15974. * - False indicates that the media should not play inline.
  15975. *
  15976. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  15977. */
  15978. 'playsinline'].forEach(function (prop) {
  15979. Html5.prototype[prop] = function () {
  15980. return this.el_[prop] || this.el_.hasAttribute(prop);
  15981. };
  15982. });
  15983. // Wrap native boolean attributes with setters that set both property and attribute
  15984. // The list is as followed:
  15985. // setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
  15986. // setControls is special-cased above
  15987. [
  15988. /**
  15989. * Set the value of `muted` on the media element. `muted` indicates that the current
  15990. * audio level should be silent.
  15991. *
  15992. * @method Html5#setMuted
  15993. * @param {boolean} muted
  15994. * - True if the audio should be set to silent
  15995. * - False otherwise
  15996. *
  15997. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
  15998. */
  15999. 'muted',
  16000. /**
  16001. * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
  16002. * audio level should be silent, but will only effect the muted level on intial playback..
  16003. *
  16004. * @method Html5.prototype.setDefaultMuted
  16005. * @param {boolean} defaultMuted
  16006. * - True if the audio should be set to silent
  16007. * - False otherwise
  16008. *
  16009. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
  16010. */
  16011. 'defaultMuted',
  16012. /**
  16013. * Set the value of `autoplay` on the media element. `autoplay` indicates
  16014. * that the media should start to play as soon as the page is ready.
  16015. *
  16016. * @method Html5#setAutoplay
  16017. * @param {boolean} autoplay
  16018. * - True indicates that the media should start as soon as the page loads.
  16019. * - False indicates that the media should not start as soon as the page loads.
  16020. *
  16021. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
  16022. */
  16023. 'autoplay',
  16024. /**
  16025. * Set the value of `loop` on the media element. `loop` indicates
  16026. * that the media should return to the start of the media and continue playing once
  16027. * it reaches the end.
  16028. *
  16029. * @method Html5#setLoop
  16030. * @param {boolean} loop
  16031. * - True indicates that playback should seek back to start once
  16032. * the end of a media is reached.
  16033. * - False indicates that playback should not loop back to the start when the
  16034. * end of the media is reached.
  16035. *
  16036. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
  16037. */
  16038. 'loop',
  16039. /**
  16040. * Set the value of `playsinline` from the media element. `playsinline` indicates
  16041. * to the browser that non-fullscreen playback is preferred when fullscreen
  16042. * playback is the native default, such as in iOS Safari.
  16043. *
  16044. * @method Html5#setPlaysinline
  16045. * @param {boolean} playsinline
  16046. * - True indicates that the media should play inline.
  16047. * - False indicates that the media should not play inline.
  16048. *
  16049. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  16050. */
  16051. 'playsinline'].forEach(function (prop) {
  16052. Html5.prototype['set' + toTitleCase(prop)] = function (v) {
  16053. this.el_[prop] = v;
  16054. if (v) {
  16055. this.el_.setAttribute(prop, prop);
  16056. } else {
  16057. this.el_.removeAttribute(prop);
  16058. }
  16059. };
  16060. });
  16061. // Wrap native properties with a getter
  16062. // The list is as followed
  16063. // paused, currentTime, buffered, volume, poster, preload, error, seeking
  16064. // seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
  16065. // readyState, videoWidth, videoHeight
  16066. [
  16067. /**
  16068. * Get the value of `paused` from the media element. `paused` indicates whether the media element
  16069. * is currently paused or not.
  16070. *
  16071. * @method Html5#paused
  16072. * @return {boolean}
  16073. * The value of `paused` from the media element.
  16074. *
  16075. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
  16076. */
  16077. 'paused',
  16078. /**
  16079. * Get the value of `currentTime` from the media element. `currentTime` indicates
  16080. * the current second that the media is at in playback.
  16081. *
  16082. * @method Html5#currentTime
  16083. * @return {number}
  16084. * The value of `currentTime` from the media element.
  16085. *
  16086. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
  16087. */
  16088. 'currentTime',
  16089. /**
  16090. * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
  16091. * object that represents the parts of the media that are already downloaded and
  16092. * available for playback.
  16093. *
  16094. * @method Html5#buffered
  16095. * @return {TimeRange}
  16096. * The value of `buffered` from the media element.
  16097. *
  16098. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
  16099. */
  16100. 'buffered',
  16101. /**
  16102. * Get the value of `volume` from the media element. `volume` indicates
  16103. * the current playback volume of audio for a media. `volume` will be a value from 0
  16104. * (silent) to 1 (loudest and default).
  16105. *
  16106. * @method Html5#volume
  16107. * @return {number}
  16108. * The value of `volume` from the media element. Value will be between 0-1.
  16109. *
  16110. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
  16111. */
  16112. 'volume',
  16113. /**
  16114. * Get the value of `poster` from the media element. `poster` indicates
  16115. * that the url of an image file that can/will be shown when no media data is available.
  16116. *
  16117. * @method Html5#poster
  16118. * @return {string}
  16119. * The value of `poster` from the media element. Value will be a url to an
  16120. * image.
  16121. *
  16122. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
  16123. */
  16124. 'poster',
  16125. /**
  16126. * Get the value of `preload` from the media element. `preload` indicates
  16127. * what should download before the media is interacted with. It can have the following
  16128. * values:
  16129. * - none: nothing should be downloaded
  16130. * - metadata: poster and the first few frames of the media may be downloaded to get
  16131. * media dimensions and other metadata
  16132. * - auto: allow the media and metadata for the media to be downloaded before
  16133. * interaction
  16134. *
  16135. * @method Html5#preload
  16136. * @return {string}
  16137. * The value of `preload` from the media element. Will be 'none', 'metadata',
  16138. * or 'auto'.
  16139. *
  16140. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
  16141. */
  16142. 'preload',
  16143. /**
  16144. * Get the value of the `error` from the media element. `error` indicates any
  16145. * MediaError that may have occured during playback. If error returns null there is no
  16146. * current error.
  16147. *
  16148. * @method Html5#error
  16149. * @return {MediaError|null}
  16150. * The value of `error` from the media element. Will be `MediaError` if there
  16151. * is a current error and null otherwise.
  16152. *
  16153. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
  16154. */
  16155. 'error',
  16156. /**
  16157. * Get the value of `seeking` from the media element. `seeking` indicates whether the
  16158. * media is currently seeking to a new position or not.
  16159. *
  16160. * @method Html5#seeking
  16161. * @return {boolean}
  16162. * - The value of `seeking` from the media element.
  16163. * - True indicates that the media is currently seeking to a new position.
  16164. * - Flase indicates that the media is not seeking to a new position at this time.
  16165. *
  16166. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
  16167. */
  16168. 'seeking',
  16169. /**
  16170. * Get the value of `seekable` from the media element. `seekable` returns a
  16171. * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
  16172. *
  16173. * @method Html5#seekable
  16174. * @return {TimeRange}
  16175. * The value of `seekable` from the media element. A `TimeRange` object
  16176. * indicating the current ranges of time that can be seeked to.
  16177. *
  16178. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
  16179. */
  16180. 'seekable',
  16181. /**
  16182. * Get the value of `ended` from the media element. `ended` indicates whether
  16183. * the media has reached the end or not.
  16184. *
  16185. * @method Html5#ended
  16186. * @return {boolean}
  16187. * - The value of `ended` from the media element.
  16188. * - True indicates that the media has ended.
  16189. * - False indicates that the media has not ended.
  16190. *
  16191. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
  16192. */
  16193. 'ended',
  16194. /**
  16195. * Get the value of `playbackRate` from the media element. `playbackRate` indicates
  16196. * the rate at which the media is currently playing back. Examples:
  16197. * - if playbackRate is set to 2, media will play twice as fast.
  16198. * - if playbackRate is set to 0.5, media will play half as fast.
  16199. *
  16200. * @method Html5#playbackRate
  16201. * @return {number}
  16202. * The value of `playbackRate` from the media element. A number indicating
  16203. * the current playback speed of the media, where 1 is normal speed.
  16204. *
  16205. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  16206. */
  16207. 'playbackRate',
  16208. /**
  16209. * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
  16210. * the rate at which the media is currently playing back. This value will not indicate the current
  16211. * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
  16212. *
  16213. * Examples:
  16214. * - if defaultPlaybackRate is set to 2, media will play twice as fast.
  16215. * - if defaultPlaybackRate is set to 0.5, media will play half as fast.
  16216. *
  16217. * @method Html5.prototype.defaultPlaybackRate
  16218. * @return {number}
  16219. * The value of `defaultPlaybackRate` from the media element. A number indicating
  16220. * the current playback speed of the media, where 1 is normal speed.
  16221. *
  16222. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  16223. */
  16224. 'defaultPlaybackRate',
  16225. /**
  16226. * Get the value of `played` from the media element. `played` returns a `TimeRange`
  16227. * object representing points in the media timeline that have been played.
  16228. *
  16229. * @method Html5#played
  16230. * @return {TimeRange}
  16231. * The value of `played` from the media element. A `TimeRange` object indicating
  16232. * the ranges of time that have been played.
  16233. *
  16234. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
  16235. */
  16236. 'played',
  16237. /**
  16238. * Get the value of `networkState` from the media element. `networkState` indicates
  16239. * the current network state. It returns an enumeration from the following list:
  16240. * - 0: NETWORK_EMPTY
  16241. * - 1: NEWORK_IDLE
  16242. * - 2: NETWORK_LOADING
  16243. * - 3: NETWORK_NO_SOURCE
  16244. *
  16245. * @method Html5#networkState
  16246. * @return {number}
  16247. * The value of `networkState` from the media element. This will be a number
  16248. * from the list in the description.
  16249. *
  16250. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
  16251. */
  16252. 'networkState',
  16253. /**
  16254. * Get the value of `readyState` from the media element. `readyState` indicates
  16255. * the current state of the media element. It returns an enumeration from the
  16256. * following list:
  16257. * - 0: HAVE_NOTHING
  16258. * - 1: HAVE_METADATA
  16259. * - 2: HAVE_CURRENT_DATA
  16260. * - 3: HAVE_FUTURE_DATA
  16261. * - 4: HAVE_ENOUGH_DATA
  16262. *
  16263. * @method Html5#readyState
  16264. * @return {number}
  16265. * The value of `readyState` from the media element. This will be a number
  16266. * from the list in the description.
  16267. *
  16268. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
  16269. */
  16270. 'readyState',
  16271. /**
  16272. * Get the value of `videoWidth` from the video element. `videoWidth` indicates
  16273. * the current width of the video in css pixels.
  16274. *
  16275. * @method Html5#videoWidth
  16276. * @return {number}
  16277. * The value of `videoWidth` from the video element. This will be a number
  16278. * in css pixels.
  16279. *
  16280. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
  16281. */
  16282. 'videoWidth',
  16283. /**
  16284. * Get the value of `videoHeight` from the video element. `videoHeigth` indicates
  16285. * the current height of the video in css pixels.
  16286. *
  16287. * @method Html5#videoHeight
  16288. * @return {number}
  16289. * The value of `videoHeight` from the video element. This will be a number
  16290. * in css pixels.
  16291. *
  16292. * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
  16293. */
  16294. 'videoHeight'].forEach(function (prop) {
  16295. Html5.prototype[prop] = function () {
  16296. return this.el_[prop];
  16297. };
  16298. });
  16299. // Wrap native properties with a setter in this format:
  16300. // set + toTitleCase(name)
  16301. // The list is as follows:
  16302. // setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
  16303. [
  16304. /**
  16305. * Set the value of `volume` on the media element. `volume` indicates the current
  16306. * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
  16307. * so on.
  16308. *
  16309. * @method Html5#setVolume
  16310. * @param {number} percentAsDecimal
  16311. * The volume percent as a decimal. Valid range is from 0-1.
  16312. *
  16313. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
  16314. */
  16315. 'volume',
  16316. /**
  16317. * Set the value of `src` on the media element. `src` indicates the current
  16318. * {@link Tech~SourceObject} for the media.
  16319. *
  16320. * @method Html5#setSrc
  16321. * @param {Tech~SourceObject} src
  16322. * The source object to set as the current source.
  16323. *
  16324. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
  16325. */
  16326. 'src',
  16327. /**
  16328. * Set the value of `poster` on the media element. `poster` is the url to
  16329. * an image file that can/will be shown when no media data is available.
  16330. *
  16331. * @method Html5#setPoster
  16332. * @param {string} poster
  16333. * The url to an image that should be used as the `poster` for the media
  16334. * element.
  16335. *
  16336. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
  16337. */
  16338. 'poster',
  16339. /**
  16340. * Set the value of `preload` on the media element. `preload` indicates
  16341. * what should download before the media is interacted with. It can have the following
  16342. * values:
  16343. * - none: nothing should be downloaded
  16344. * - metadata: poster and the first few frames of the media may be downloaded to get
  16345. * media dimensions and other metadata
  16346. * - auto: allow the media and metadata for the media to be downloaded before
  16347. * interaction
  16348. *
  16349. * @method Html5#setPreload
  16350. * @param {string} preload
  16351. * The value of `preload` to set on the media element. Must be 'none', 'metadata',
  16352. * or 'auto'.
  16353. *
  16354. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
  16355. */
  16356. 'preload',
  16357. /**
  16358. * Set the value of `playbackRate` on the media element. `playbackRate` indicates
  16359. * the rate at which the media should play back. Examples:
  16360. * - if playbackRate is set to 2, media will play twice as fast.
  16361. * - if playbackRate is set to 0.5, media will play half as fast.
  16362. *
  16363. * @method Html5#setPlaybackRate
  16364. * @return {number}
  16365. * The value of `playbackRate` from the media element. A number indicating
  16366. * the current playback speed of the media, where 1 is normal speed.
  16367. *
  16368. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
  16369. */
  16370. 'playbackRate',
  16371. /**
  16372. * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
  16373. * the rate at which the media should play back upon initial startup. Changing this value
  16374. * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
  16375. *
  16376. * Example Values:
  16377. * - if playbackRate is set to 2, media will play twice as fast.
  16378. * - if playbackRate is set to 0.5, media will play half as fast.
  16379. *
  16380. * @method Html5.prototype.setDefaultPlaybackRate
  16381. * @return {number}
  16382. * The value of `defaultPlaybackRate` from the media element. A number indicating
  16383. * the current playback speed of the media, where 1 is normal speed.
  16384. *
  16385. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
  16386. */
  16387. 'defaultPlaybackRate'].forEach(function (prop) {
  16388. Html5.prototype['set' + toTitleCase(prop)] = function (v) {
  16389. this.el_[prop] = v;
  16390. };
  16391. });
  16392. // wrap native functions with a function
  16393. // The list is as follows:
  16394. // pause, load play
  16395. [
  16396. /**
  16397. * A wrapper around the media elements `pause` function. This will call the `HTML5`
  16398. * media elements `pause` function.
  16399. *
  16400. * @method Html5#pause
  16401. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
  16402. */
  16403. 'pause',
  16404. /**
  16405. * A wrapper around the media elements `load` function. This will call the `HTML5`s
  16406. * media element `load` function.
  16407. *
  16408. * @method Html5#load
  16409. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
  16410. */
  16411. 'load',
  16412. /**
  16413. * A wrapper around the media elements `play` function. This will call the `HTML5`s
  16414. * media element `play` function.
  16415. *
  16416. * @method Html5#play
  16417. * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
  16418. */
  16419. 'play'].forEach(function (prop) {
  16420. Html5.prototype[prop] = function () {
  16421. return this.el_[prop]();
  16422. };
  16423. });
  16424. Tech.withSourceHandlers(Html5);
  16425. /**
  16426. * Native source handler for Html5, simply passes the source to the media element.
  16427. *
  16428. * @proprety {Tech~SourceObject} source
  16429. * The source object
  16430. *
  16431. * @proprety {Html5} tech
  16432. * The instance of the HTML5 tech.
  16433. */
  16434. Html5.nativeSourceHandler = {};
  16435. /**
  16436. * Check if the media element can play the given mime type.
  16437. *
  16438. * @param {string} type
  16439. * The mimetype to check
  16440. *
  16441. * @return {string}
  16442. * 'probably', 'maybe', or '' (empty string)
  16443. */
  16444. Html5.nativeSourceHandler.canPlayType = function (type) {
  16445. // IE9 on Windows 7 without MediaPlayer throws an error here
  16446. // https://github.com/videojs/video.js/issues/519
  16447. try {
  16448. return Html5.TEST_VID.canPlayType(type);
  16449. } catch (e) {
  16450. return '';
  16451. }
  16452. };
  16453. /**
  16454. * Check if the media element can handle a source natively.
  16455. *
  16456. * @param {Tech~SourceObject} source
  16457. * The source object
  16458. *
  16459. * @param {Object} [options]
  16460. * Options to be passed to the tech.
  16461. *
  16462. * @return {string}
  16463. * 'probably', 'maybe', or '' (empty string).
  16464. */
  16465. Html5.nativeSourceHandler.canHandleSource = function (source, options) {
  16466. // If a type was provided we should rely on that
  16467. if (source.type) {
  16468. return Html5.nativeSourceHandler.canPlayType(source.type);
  16469. // If no type, fall back to checking 'video/[EXTENSION]'
  16470. } else if (source.src) {
  16471. var ext = getFileExtension(source.src);
  16472. return Html5.nativeSourceHandler.canPlayType('video/' + ext);
  16473. }
  16474. return '';
  16475. };
  16476. /**
  16477. * Pass the source to the native media element.
  16478. *
  16479. * @param {Tech~SourceObject} source
  16480. * The source object
  16481. *
  16482. * @param {Html5} tech
  16483. * The instance of the Html5 tech
  16484. *
  16485. * @param {Object} [options]
  16486. * The options to pass to the source
  16487. */
  16488. Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
  16489. tech.setSrc(source.src);
  16490. };
  16491. /**
  16492. * A noop for the native dispose function, as cleanup is not needed.
  16493. */
  16494. Html5.nativeSourceHandler.dispose = function () {};
  16495. // Register the native source handler
  16496. Html5.registerSourceHandler(Html5.nativeSourceHandler);
  16497. Tech.registerTech('Html5', Html5);
  16498. 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 ']);
  16499. /**
  16500. * @file player.js
  16501. */
  16502. // Subclasses Component
  16503. // The following imports are used only to ensure that the corresponding modules
  16504. // are always included in the video.js package. Importing the modules will
  16505. // execute them and they will register themselves with video.js.
  16506. // Import Html5 tech, at least for disposing the original video tag.
  16507. // The following tech events are simply re-triggered
  16508. // on the player when they happen
  16509. var TECH_EVENTS_RETRIGGER = [
  16510. /**
  16511. * Fired while the user agent is downloading media data.
  16512. *
  16513. * @event Player#progress
  16514. * @type {EventTarget~Event}
  16515. */
  16516. /**
  16517. * Retrigger the `progress` event that was triggered by the {@link Tech}.
  16518. *
  16519. * @private
  16520. * @method Player#handleTechProgress_
  16521. * @fires Player#progress
  16522. * @listens Tech#progress
  16523. */
  16524. 'progress',
  16525. /**
  16526. * Fires when the loading of an audio/video is aborted.
  16527. *
  16528. * @event Player#abort
  16529. * @type {EventTarget~Event}
  16530. */
  16531. /**
  16532. * Retrigger the `abort` event that was triggered by the {@link Tech}.
  16533. *
  16534. * @private
  16535. * @method Player#handleTechAbort_
  16536. * @fires Player#abort
  16537. * @listens Tech#abort
  16538. */
  16539. 'abort',
  16540. /**
  16541. * Fires when the browser is intentionally not getting media data.
  16542. *
  16543. * @event Player#suspend
  16544. * @type {EventTarget~Event}
  16545. */
  16546. /**
  16547. * Retrigger the `suspend` event that was triggered by the {@link Tech}.
  16548. *
  16549. * @private
  16550. * @method Player#handleTechSuspend_
  16551. * @fires Player#suspend
  16552. * @listens Tech#suspend
  16553. */
  16554. 'suspend',
  16555. /**
  16556. * Fires when the current playlist is empty.
  16557. *
  16558. * @event Player#emptied
  16559. * @type {EventTarget~Event}
  16560. */
  16561. /**
  16562. * Retrigger the `emptied` event that was triggered by the {@link Tech}.
  16563. *
  16564. * @private
  16565. * @method Player#handleTechEmptied_
  16566. * @fires Player#emptied
  16567. * @listens Tech#emptied
  16568. */
  16569. 'emptied',
  16570. /**
  16571. * Fires when the browser is trying to get media data, but data is not available.
  16572. *
  16573. * @event Player#stalled
  16574. * @type {EventTarget~Event}
  16575. */
  16576. /**
  16577. * Retrigger the `stalled` event that was triggered by the {@link Tech}.
  16578. *
  16579. * @private
  16580. * @method Player#handleTechStalled_
  16581. * @fires Player#stalled
  16582. * @listens Tech#stalled
  16583. */
  16584. 'stalled',
  16585. /**
  16586. * Fires when the browser has loaded meta data for the audio/video.
  16587. *
  16588. * @event Player#loadedmetadata
  16589. * @type {EventTarget~Event}
  16590. */
  16591. /**
  16592. * Retrigger the `stalled` event that was triggered by the {@link Tech}.
  16593. *
  16594. * @private
  16595. * @method Player#handleTechLoadedmetadata_
  16596. * @fires Player#loadedmetadata
  16597. * @listens Tech#loadedmetadata
  16598. */
  16599. 'loadedmetadata',
  16600. /**
  16601. * Fires when the browser has loaded the current frame of the audio/video.
  16602. *
  16603. * @event Player#loadeddata
  16604. * @type {event}
  16605. */
  16606. /**
  16607. * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
  16608. *
  16609. * @private
  16610. * @method Player#handleTechLoaddeddata_
  16611. * @fires Player#loadeddata
  16612. * @listens Tech#loadeddata
  16613. */
  16614. 'loadeddata',
  16615. /**
  16616. * Fires when the current playback position has changed.
  16617. *
  16618. * @event Player#timeupdate
  16619. * @type {event}
  16620. */
  16621. /**
  16622. * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
  16623. *
  16624. * @private
  16625. * @method Player#handleTechTimeUpdate_
  16626. * @fires Player#timeupdate
  16627. * @listens Tech#timeupdate
  16628. */
  16629. 'timeupdate',
  16630. /**
  16631. * Fires when the video's intrinsic dimensions change
  16632. *
  16633. * @event Player#resize
  16634. * @type {event}
  16635. */
  16636. /**
  16637. * Retrigger the `resize` event that was triggered by the {@link Tech}.
  16638. *
  16639. * @private
  16640. * @method Player#handleTechResize_
  16641. * @fires Player#resize
  16642. * @listens Tech#resize
  16643. */
  16644. 'resize',
  16645. /**
  16646. * Fires when the volume has been changed
  16647. *
  16648. * @event Player#volumechange
  16649. * @type {event}
  16650. */
  16651. /**
  16652. * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
  16653. *
  16654. * @private
  16655. * @method Player#handleTechVolumechange_
  16656. * @fires Player#volumechange
  16657. * @listens Tech#volumechange
  16658. */
  16659. 'volumechange',
  16660. /**
  16661. * Fires when the text track has been changed
  16662. *
  16663. * @event Player#texttrackchange
  16664. * @type {event}
  16665. */
  16666. /**
  16667. * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
  16668. *
  16669. * @private
  16670. * @method Player#handleTechTexttrackchange_
  16671. * @fires Player#texttrackchange
  16672. * @listens Tech#texttrackchange
  16673. */
  16674. 'texttrackchange'];
  16675. // events to queue when playback rate is zero
  16676. // this is a hash for the sole purpose of mapping non-camel-cased event names
  16677. // to camel-cased function names
  16678. var TECH_EVENTS_QUEUE = {
  16679. canplay: 'CanPlay',
  16680. canplaythrough: 'CanPlayThrough',
  16681. playing: 'Playing',
  16682. seeked: 'Seeked'
  16683. };
  16684. var BREAKPOINT_ORDER = ['tiny', 'xsmall', 'small', 'medium', 'large', 'xlarge', 'huge'];
  16685. var BREAKPOINT_CLASSES = {};
  16686. // grep: vjs-layout-tiny
  16687. // grep: vjs-layout-x-small
  16688. // grep: vjs-layout-small
  16689. // grep: vjs-layout-medium
  16690. // grep: vjs-layout-large
  16691. // grep: vjs-layout-x-large
  16692. // grep: vjs-layout-huge
  16693. BREAKPOINT_ORDER.forEach(function (k) {
  16694. var v = k.charAt(0) === 'x' ? 'x-' + k.substring(1) : k;
  16695. BREAKPOINT_CLASSES[k] = 'vjs-layout-' + v;
  16696. });
  16697. var DEFAULT_BREAKPOINTS = {
  16698. tiny: 210,
  16699. xsmall: 320,
  16700. small: 425,
  16701. medium: 768,
  16702. large: 1440,
  16703. xlarge: 2560,
  16704. huge: Infinity
  16705. };
  16706. /**
  16707. * An instance of the `Player` class is created when any of the Video.js setup methods
  16708. * are used to initialize a video.
  16709. *
  16710. * After an instance has been created it can be accessed globally in two ways:
  16711. * 1. By calling `videojs('example_video_1');`
  16712. * 2. By using it directly via `videojs.players.example_video_1;`
  16713. *
  16714. * @extends Component
  16715. */
  16716. var Player = function (_Component) {
  16717. inherits(Player, _Component);
  16718. /**
  16719. * Create an instance of this class.
  16720. *
  16721. * @param {Element} tag
  16722. * The original video DOM element used for configuring options.
  16723. *
  16724. * @param {Object} [options]
  16725. * Object of option names and values.
  16726. *
  16727. * @param {Component~ReadyCallback} [ready]
  16728. * Ready callback function.
  16729. */
  16730. function Player(tag, options, ready) {
  16731. classCallCheck(this, Player);
  16732. // Make sure tag ID exists
  16733. tag.id = tag.id || options.id || 'vjs_video_' + newGUID();
  16734. // Set Options
  16735. // The options argument overrides options set in the video tag
  16736. // which overrides globally set options.
  16737. // This latter part coincides with the load order
  16738. // (tag must exist before Player)
  16739. options = assign(Player.getTagSettings(tag), options);
  16740. // Delay the initialization of children because we need to set up
  16741. // player properties first, and can't use `this` before `super()`
  16742. options.initChildren = false;
  16743. // Same with creating the element
  16744. options.createEl = false;
  16745. // don't auto mixin the evented mixin
  16746. options.evented = false;
  16747. // we don't want the player to report touch activity on itself
  16748. // see enableTouchActivity in Component
  16749. options.reportTouchActivity = false;
  16750. // If language is not set, get the closest lang attribute
  16751. if (!options.language) {
  16752. if (typeof tag.closest === 'function') {
  16753. var closest = tag.closest('[lang]');
  16754. if (closest && closest.getAttribute) {
  16755. options.language = closest.getAttribute('lang');
  16756. }
  16757. } else {
  16758. var element = tag;
  16759. while (element && element.nodeType === 1) {
  16760. if (getAttributes(element).hasOwnProperty('lang')) {
  16761. options.language = element.getAttribute('lang');
  16762. break;
  16763. }
  16764. element = element.parentNode;
  16765. }
  16766. }
  16767. }
  16768. // Run base component initializing with new options
  16769. // create logger
  16770. var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
  16771. _this.log = createLogger(_this.id_);
  16772. // Tracks when a tech changes the poster
  16773. _this.isPosterFromTech_ = false;
  16774. // Holds callback info that gets queued when playback rate is zero
  16775. // and a seek is happening
  16776. _this.queuedCallbacks_ = [];
  16777. // Turn off API access because we're loading a new tech that might load asynchronously
  16778. _this.isReady_ = false;
  16779. // Init state hasStarted_
  16780. _this.hasStarted_ = false;
  16781. // Init state userActive_
  16782. _this.userActive_ = false;
  16783. // if the global option object was accidentally blown away by
  16784. // someone, bail early with an informative error
  16785. if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
  16786. throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
  16787. }
  16788. // Store the original tag used to set options
  16789. _this.tag = tag;
  16790. // Store the tag attributes used to restore html5 element
  16791. _this.tagAttributes = tag && getAttributes(tag);
  16792. // Update current language
  16793. _this.language(_this.options_.language);
  16794. // Update Supported Languages
  16795. if (options.languages) {
  16796. // Normalise player option languages to lowercase
  16797. var languagesToLower = {};
  16798. Object.getOwnPropertyNames(options.languages).forEach(function (name$$1) {
  16799. languagesToLower[name$$1.toLowerCase()] = options.languages[name$$1];
  16800. });
  16801. _this.languages_ = languagesToLower;
  16802. } else {
  16803. _this.languages_ = Player.prototype.options_.languages;
  16804. }
  16805. // Cache for video property values.
  16806. _this.cache_ = {};
  16807. // Set poster
  16808. _this.poster_ = options.poster || '';
  16809. // Set controls
  16810. _this.controls_ = !!options.controls;
  16811. // Set default values for lastVolume
  16812. _this.cache_.lastVolume = 1;
  16813. // Original tag settings stored in options
  16814. // now remove immediately so native controls don't flash.
  16815. // May be turned back on by HTML5 tech if nativeControlsForTouch is true
  16816. tag.controls = false;
  16817. tag.removeAttribute('controls');
  16818. // the attribute overrides the option
  16819. if (tag.hasAttribute('autoplay')) {
  16820. _this.options_.autoplay = true;
  16821. } else {
  16822. // otherwise use the setter to validate and
  16823. // set the correct value.
  16824. _this.autoplay(_this.options_.autoplay);
  16825. }
  16826. /*
  16827. * Store the internal state of scrubbing
  16828. *
  16829. * @private
  16830. * @return {Boolean} True if the user is scrubbing
  16831. */
  16832. _this.scrubbing_ = false;
  16833. _this.el_ = _this.createEl();
  16834. // Set default value for lastPlaybackRate
  16835. _this.cache_.lastPlaybackRate = _this.defaultPlaybackRate();
  16836. // Make this an evented object and use `el_` as its event bus.
  16837. evented(_this, { eventBusKey: 'el_' });
  16838. // We also want to pass the original player options to each component and plugin
  16839. // as well so they don't need to reach back into the player for options later.
  16840. // We also need to do another copy of this.options_ so we don't end up with
  16841. // an infinite loop.
  16842. var playerOptionsCopy = mergeOptions(_this.options_);
  16843. // Load plugins
  16844. if (options.plugins) {
  16845. var plugins = options.plugins;
  16846. Object.keys(plugins).forEach(function (name$$1) {
  16847. if (typeof this[name$$1] === 'function') {
  16848. this[name$$1](plugins[name$$1]);
  16849. } else {
  16850. throw new Error('plugin "' + name$$1 + '" does not exist');
  16851. }
  16852. }, _this);
  16853. }
  16854. _this.options_.playerOptions = playerOptionsCopy;
  16855. _this.middleware_ = [];
  16856. _this.initChildren();
  16857. // Set isAudio based on whether or not an audio tag was used
  16858. _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
  16859. // Update controls className. Can't do this when the controls are initially
  16860. // set because the element doesn't exist yet.
  16861. if (_this.controls()) {
  16862. _this.addClass('vjs-controls-enabled');
  16863. } else {
  16864. _this.addClass('vjs-controls-disabled');
  16865. }
  16866. // Set ARIA label and region role depending on player type
  16867. _this.el_.setAttribute('role', 'region');
  16868. if (_this.isAudio()) {
  16869. _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
  16870. } else {
  16871. _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
  16872. }
  16873. if (_this.isAudio()) {
  16874. _this.addClass('vjs-audio');
  16875. }
  16876. if (_this.flexNotSupported_()) {
  16877. _this.addClass('vjs-no-flex');
  16878. }
  16879. // TODO: Make this smarter. Toggle user state between touching/mousing
  16880. // using events, since devices can have both touch and mouse events.
  16881. // if (browser.TOUCH_ENABLED) {
  16882. // this.addClass('vjs-touch-enabled');
  16883. // }
  16884. // iOS Safari has broken hover handling
  16885. if (!IS_IOS) {
  16886. _this.addClass('vjs-workinghover');
  16887. }
  16888. // Make player easily findable by ID
  16889. Player.players[_this.id_] = _this;
  16890. // Add a major version class to aid css in plugins
  16891. var majorVersion = version.split('.')[0];
  16892. _this.addClass('vjs-v' + majorVersion);
  16893. // When the player is first initialized, trigger activity so components
  16894. // like the control bar show themselves if needed
  16895. _this.userActive(true);
  16896. _this.reportUserActivity();
  16897. _this.one('play', _this.listenForUserActivity_);
  16898. _this.on('fullscreenchange', _this.handleFullscreenChange_);
  16899. _this.on('stageclick', _this.handleStageClick_);
  16900. _this.breakpoints(_this.options_.breakpoints);
  16901. _this.responsive(_this.options_.responsive);
  16902. _this.changingSrc_ = false;
  16903. _this.playWaitingForReady_ = false;
  16904. _this.playOnLoadstart_ = null;
  16905. return _this;
  16906. }
  16907. /**
  16908. * Destroys the video player and does any necessary cleanup.
  16909. *
  16910. * This is especially helpful if you are dynamically adding and removing videos
  16911. * to/from the DOM.
  16912. *
  16913. * @fires Player#dispose
  16914. */
  16915. Player.prototype.dispose = function dispose() {
  16916. /**
  16917. * Called when the player is being disposed of.
  16918. *
  16919. * @event Player#dispose
  16920. * @type {EventTarget~Event}
  16921. */
  16922. this.trigger('dispose');
  16923. // prevent dispose from being called twice
  16924. this.off('dispose');
  16925. if (this.styleEl_ && this.styleEl_.parentNode) {
  16926. this.styleEl_.parentNode.removeChild(this.styleEl_);
  16927. this.styleEl_ = null;
  16928. }
  16929. // Kill reference to this player
  16930. Player.players[this.id_] = null;
  16931. if (this.tag && this.tag.player) {
  16932. this.tag.player = null;
  16933. }
  16934. if (this.el_ && this.el_.player) {
  16935. this.el_.player = null;
  16936. }
  16937. if (this.tech_) {
  16938. this.tech_.dispose();
  16939. this.isPosterFromTech_ = false;
  16940. this.poster_ = '';
  16941. }
  16942. if (this.playerElIngest_) {
  16943. this.playerElIngest_ = null;
  16944. }
  16945. if (this.tag) {
  16946. this.tag = null;
  16947. }
  16948. clearCacheForPlayer(this);
  16949. // the actual .el_ is removed here
  16950. _Component.prototype.dispose.call(this);
  16951. };
  16952. /**
  16953. * Create the `Player`'s DOM element.
  16954. *
  16955. * @return {Element}
  16956. * The DOM element that gets created.
  16957. */
  16958. Player.prototype.createEl = function createEl$$1() {
  16959. var tag = this.tag;
  16960. var el = void 0;
  16961. var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
  16962. var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
  16963. if (playerElIngest) {
  16964. el = this.el_ = tag.parentNode;
  16965. } else if (!divEmbed) {
  16966. el = this.el_ = _Component.prototype.createEl.call(this, 'div');
  16967. }
  16968. // Copy over all the attributes from the tag, including ID and class
  16969. // ID will now reference player box, not the video tag
  16970. var attrs = getAttributes(tag);
  16971. if (divEmbed) {
  16972. el = this.el_ = tag;
  16973. tag = this.tag = document_1.createElement('video');
  16974. while (el.children.length) {
  16975. tag.appendChild(el.firstChild);
  16976. }
  16977. if (!hasClass(el, 'video-js')) {
  16978. addClass(el, 'video-js');
  16979. }
  16980. el.appendChild(tag);
  16981. playerElIngest = this.playerElIngest_ = el;
  16982. // copy over properties from the video-js element
  16983. // ie8 doesn't support Object.keys nor hasOwnProperty
  16984. // on dom elements so we have to specify properties individually
  16985. ['autoplay', 'controls', 'crossOrigin', 'defaultMuted', 'defaultPlaybackRate', 'loop', 'muted', 'playbackRate', 'src', 'volume'].forEach(function (prop) {
  16986. if (typeof el[prop] !== 'undefined') {
  16987. tag[prop] = el[prop];
  16988. }
  16989. });
  16990. }
  16991. // set tabindex to -1 to remove the video element from the focus order
  16992. tag.setAttribute('tabindex', '-1');
  16993. attrs.tabindex = '-1';
  16994. // Workaround for #4583 (JAWS+IE doesn't announce BPB or play button)
  16995. // See https://github.com/FreedomScientific/VFO-standards-support/issues/78
  16996. // Note that we can't detect if JAWS is being used, but this ARIA attribute
  16997. // doesn't change behavior of IE11 if JAWS is not being used
  16998. if (IE_VERSION) {
  16999. tag.setAttribute('role', 'application');
  17000. attrs.role = 'application';
  17001. }
  17002. // Remove width/height attrs from tag so CSS can make it 100% width/height
  17003. tag.removeAttribute('width');
  17004. tag.removeAttribute('height');
  17005. if ('width' in attrs) {
  17006. delete attrs.width;
  17007. }
  17008. if ('height' in attrs) {
  17009. delete attrs.height;
  17010. }
  17011. Object.getOwnPropertyNames(attrs).forEach(function (attr) {
  17012. // workaround so we don't totally break IE7
  17013. // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
  17014. if (attr === 'class') {
  17015. el.className += ' ' + attrs[attr];
  17016. if (divEmbed) {
  17017. tag.className += ' ' + attrs[attr];
  17018. }
  17019. } else {
  17020. el.setAttribute(attr, attrs[attr]);
  17021. if (divEmbed) {
  17022. tag.setAttribute(attr, attrs[attr]);
  17023. }
  17024. }
  17025. });
  17026. // Update tag id/class for use as HTML5 playback tech
  17027. // Might think we should do this after embedding in container so .vjs-tech class
  17028. // doesn't flash 100% width/height, but class only applies with .video-js parent
  17029. tag.playerId = tag.id;
  17030. tag.id += '_html5_api';
  17031. tag.className = 'vjs-tech';
  17032. // Make player findable on elements
  17033. tag.player = el.player = this;
  17034. // Default state of video is paused
  17035. this.addClass('vjs-paused');
  17036. // Add a style element in the player that we'll use to set the width/height
  17037. // of the player in a way that's still overrideable by CSS, just like the
  17038. // video element
  17039. if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
  17040. this.styleEl_ = createStyleElement('vjs-styles-dimensions');
  17041. var defaultsStyleEl = $('.vjs-styles-defaults');
  17042. var head = $('head');
  17043. head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
  17044. }
  17045. this.fill_ = false;
  17046. this.fluid_ = false;
  17047. // Pass in the width/height/aspectRatio options which will update the style el
  17048. this.width(this.options_.width);
  17049. this.height(this.options_.height);
  17050. this.fill(this.options_.fill);
  17051. this.fluid(this.options_.fluid);
  17052. this.aspectRatio(this.options_.aspectRatio);
  17053. // Hide any links within the video/audio tag, because IE doesn't hide them completely.
  17054. var links = tag.getElementsByTagName('a');
  17055. for (var i = 0; i < links.length; i++) {
  17056. var linkEl = links.item(i);
  17057. addClass(linkEl, 'vjs-hidden');
  17058. linkEl.setAttribute('hidden', 'hidden');
  17059. }
  17060. // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
  17061. // keep track of the original for later so we can know if the source originally failed
  17062. tag.initNetworkState_ = tag.networkState;
  17063. // Wrap video tag in div (el/box) container
  17064. if (tag.parentNode && !playerElIngest) {
  17065. tag.parentNode.insertBefore(el, tag);
  17066. }
  17067. // insert the tag as the first child of the player element
  17068. // then manually add it to the children array so that this.addChild
  17069. // will work properly for other components
  17070. //
  17071. // Breaks iPhone, fixed in HTML5 setup.
  17072. prependTo(tag, el);
  17073. this.children_.unshift(tag);
  17074. // Set lang attr on player to ensure CSS :lang() in consistent with player
  17075. // if it's been set to something different to the doc
  17076. this.el_.setAttribute('lang', this.language_);
  17077. this.el_ = el;
  17078. return el;
  17079. };
  17080. /**
  17081. * A getter/setter for the `Player`'s width. Returns the player's configured value.
  17082. * To get the current width use `currentWidth()`.
  17083. *
  17084. * @param {number} [value]
  17085. * The value to set the `Player`'s width to.
  17086. *
  17087. * @return {number}
  17088. * The current width of the `Player` when getting.
  17089. */
  17090. Player.prototype.width = function width(value) {
  17091. return this.dimension('width', value);
  17092. };
  17093. /**
  17094. * A getter/setter for the `Player`'s height. Returns the player's configured value.
  17095. * To get the current height use `currentheight()`.
  17096. *
  17097. * @param {number} [value]
  17098. * The value to set the `Player`'s heigth to.
  17099. *
  17100. * @return {number}
  17101. * The current height of the `Player` when getting.
  17102. */
  17103. Player.prototype.height = function height(value) {
  17104. return this.dimension('height', value);
  17105. };
  17106. /**
  17107. * A getter/setter for the `Player`'s width & height.
  17108. *
  17109. * @param {string} dimension
  17110. * This string can be:
  17111. * - 'width'
  17112. * - 'height'
  17113. *
  17114. * @param {number} [value]
  17115. * Value for dimension specified in the first argument.
  17116. *
  17117. * @return {number}
  17118. * The dimension arguments value when getting (width/height).
  17119. */
  17120. Player.prototype.dimension = function dimension(_dimension, value) {
  17121. var privDimension = _dimension + '_';
  17122. if (value === undefined) {
  17123. return this[privDimension] || 0;
  17124. }
  17125. if (value === '') {
  17126. // If an empty string is given, reset the dimension to be automatic
  17127. this[privDimension] = undefined;
  17128. this.updateStyleEl_();
  17129. return;
  17130. }
  17131. var parsedVal = parseFloat(value);
  17132. if (isNaN(parsedVal)) {
  17133. log.error('Improper value "' + value + '" supplied for for ' + _dimension);
  17134. return;
  17135. }
  17136. this[privDimension] = parsedVal;
  17137. this.updateStyleEl_();
  17138. };
  17139. /**
  17140. * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
  17141. *
  17142. * Turning this on will turn off fill mode.
  17143. *
  17144. * @param {boolean} [bool]
  17145. * - A value of true adds the class.
  17146. * - A value of false removes the class.
  17147. * - No value will be a getter.
  17148. *
  17149. * @return {boolean|undefined}
  17150. * - The value of fluid when getting.
  17151. * - `undefined` when setting.
  17152. */
  17153. Player.prototype.fluid = function fluid(bool) {
  17154. if (bool === undefined) {
  17155. return !!this.fluid_;
  17156. }
  17157. this.fluid_ = !!bool;
  17158. if (bool) {
  17159. this.addClass('vjs-fluid');
  17160. this.fill(false);
  17161. } else {
  17162. this.removeClass('vjs-fluid');
  17163. }
  17164. this.updateStyleEl_();
  17165. };
  17166. /**
  17167. * A getter/setter/toggler for the vjs-fill `className` on the `Player`.
  17168. *
  17169. * Turning this on will turn off fluid mode.
  17170. *
  17171. * @param {boolean} [bool]
  17172. * - A value of true adds the class.
  17173. * - A value of false removes the class.
  17174. * - No value will be a getter.
  17175. *
  17176. * @return {boolean|undefined}
  17177. * - The value of fluid when getting.
  17178. * - `undefined` when setting.
  17179. */
  17180. Player.prototype.fill = function fill(bool) {
  17181. if (bool === undefined) {
  17182. return !!this.fill_;
  17183. }
  17184. this.fill_ = !!bool;
  17185. if (bool) {
  17186. this.addClass('vjs-fill');
  17187. this.fluid(false);
  17188. } else {
  17189. this.removeClass('vjs-fill');
  17190. }
  17191. };
  17192. /**
  17193. * Get/Set the aspect ratio
  17194. *
  17195. * @param {string} [ratio]
  17196. * Aspect ratio for player
  17197. *
  17198. * @return {string|undefined}
  17199. * returns the current aspect ratio when getting
  17200. */
  17201. /**
  17202. * A getter/setter for the `Player`'s aspect ratio.
  17203. *
  17204. * @param {string} [ratio]
  17205. * The value to set the `Player's aspect ratio to.
  17206. *
  17207. * @return {string|undefined}
  17208. * - The current aspect ratio of the `Player` when getting.
  17209. * - undefined when setting
  17210. */
  17211. Player.prototype.aspectRatio = function aspectRatio(ratio) {
  17212. if (ratio === undefined) {
  17213. return this.aspectRatio_;
  17214. }
  17215. // Check for width:height format
  17216. if (!/^\d+\:\d+$/.test(ratio)) {
  17217. throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
  17218. }
  17219. this.aspectRatio_ = ratio;
  17220. // We're assuming if you set an aspect ratio you want fluid mode,
  17221. // because in fixed mode you could calculate width and height yourself.
  17222. this.fluid(true);
  17223. this.updateStyleEl_();
  17224. };
  17225. /**
  17226. * Update styles of the `Player` element (height, width and aspect ratio).
  17227. *
  17228. * @private
  17229. * @listens Tech#loadedmetadata
  17230. */
  17231. Player.prototype.updateStyleEl_ = function updateStyleEl_() {
  17232. if (window_1.VIDEOJS_NO_DYNAMIC_STYLE === true) {
  17233. var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
  17234. var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
  17235. var techEl = this.tech_ && this.tech_.el();
  17236. if (techEl) {
  17237. if (_width >= 0) {
  17238. techEl.width = _width;
  17239. }
  17240. if (_height >= 0) {
  17241. techEl.height = _height;
  17242. }
  17243. }
  17244. return;
  17245. }
  17246. var width = void 0;
  17247. var height = void 0;
  17248. var aspectRatio = void 0;
  17249. var idClass = void 0;
  17250. // The aspect ratio is either used directly or to calculate width and height.
  17251. if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
  17252. // Use any aspectRatio that's been specifically set
  17253. aspectRatio = this.aspectRatio_;
  17254. } else if (this.videoWidth() > 0) {
  17255. // Otherwise try to get the aspect ratio from the video metadata
  17256. aspectRatio = this.videoWidth() + ':' + this.videoHeight();
  17257. } else {
  17258. // Or use a default. The video element's is 2:1, but 16:9 is more common.
  17259. aspectRatio = '16:9';
  17260. }
  17261. // Get the ratio as a decimal we can use to calculate dimensions
  17262. var ratioParts = aspectRatio.split(':');
  17263. var ratioMultiplier = ratioParts[1] / ratioParts[0];
  17264. if (this.width_ !== undefined) {
  17265. // Use any width that's been specifically set
  17266. width = this.width_;
  17267. } else if (this.height_ !== undefined) {
  17268. // Or calulate the width from the aspect ratio if a height has been set
  17269. width = this.height_ / ratioMultiplier;
  17270. } else {
  17271. // Or use the video's metadata, or use the video el's default of 300
  17272. width = this.videoWidth() || 300;
  17273. }
  17274. if (this.height_ !== undefined) {
  17275. // Use any height that's been specifically set
  17276. height = this.height_;
  17277. } else {
  17278. // Otherwise calculate the height from the ratio and the width
  17279. height = width * ratioMultiplier;
  17280. }
  17281. // Ensure the CSS class is valid by starting with an alpha character
  17282. if (/^[^a-zA-Z]/.test(this.id())) {
  17283. idClass = 'dimensions-' + this.id();
  17284. } else {
  17285. idClass = this.id() + '-dimensions';
  17286. }
  17287. // Ensure the right class is still on the player for the style element
  17288. this.addClass(idClass);
  17289. 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 ');
  17290. };
  17291. /**
  17292. * Load/Create an instance of playback {@link Tech} including element
  17293. * and API methods. Then append the `Tech` element in `Player` as a child.
  17294. *
  17295. * @param {string} techName
  17296. * name of the playback technology
  17297. *
  17298. * @param {string} source
  17299. * video source
  17300. *
  17301. * @private
  17302. */
  17303. Player.prototype.loadTech_ = function loadTech_(techName, source) {
  17304. var _this2 = this;
  17305. // Pause and remove current playback technology
  17306. if (this.tech_) {
  17307. this.unloadTech_();
  17308. }
  17309. var titleTechName = toTitleCase(techName);
  17310. var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1);
  17311. // get rid of the HTML5 video tag as soon as we are using another tech
  17312. if (titleTechName !== 'Html5' && this.tag) {
  17313. Tech.getTech('Html5').disposeMediaElement(this.tag);
  17314. this.tag.player = null;
  17315. this.tag = null;
  17316. }
  17317. this.techName_ = titleTechName;
  17318. // Turn off API access because we're loading a new tech that might load asynchronously
  17319. this.isReady_ = false;
  17320. // if autoplay is a string we pass false to the tech
  17321. // because the player is going to handle autoplay on `loadstart`
  17322. var autoplay = typeof this.autoplay() === 'string' ? false : this.autoplay();
  17323. // Grab tech-specific options from player options and add source and parent element to use.
  17324. var techOptions = {
  17325. source: source,
  17326. autoplay: autoplay,
  17327. 'nativeControlsForTouch': this.options_.nativeControlsForTouch,
  17328. 'playerId': this.id(),
  17329. 'techId': this.id() + '_' + camelTechName + '_api',
  17330. 'playsinline': this.options_.playsinline,
  17331. 'preload': this.options_.preload,
  17332. 'loop': this.options_.loop,
  17333. 'muted': this.options_.muted,
  17334. 'poster': this.poster(),
  17335. 'language': this.language(),
  17336. 'playerElIngest': this.playerElIngest_ || false,
  17337. 'vtt.js': this.options_['vtt.js'],
  17338. 'canOverridePoster': !!this.options_.techCanOverridePoster,
  17339. 'enableSourceset': this.options_.enableSourceset
  17340. };
  17341. ALL.names.forEach(function (name$$1) {
  17342. var props = ALL[name$$1];
  17343. techOptions[props.getterName] = _this2[props.privateName];
  17344. });
  17345. assign(techOptions, this.options_[titleTechName]);
  17346. assign(techOptions, this.options_[camelTechName]);
  17347. assign(techOptions, this.options_[techName.toLowerCase()]);
  17348. if (this.tag) {
  17349. techOptions.tag = this.tag;
  17350. }
  17351. if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
  17352. techOptions.startTime = this.cache_.currentTime;
  17353. }
  17354. // Initialize tech instance
  17355. var TechClass = Tech.getTech(techName);
  17356. if (!TechClass) {
  17357. throw new Error('No Tech named \'' + titleTechName + '\' exists! \'' + titleTechName + '\' should be registered using videojs.registerTech()\'');
  17358. }
  17359. this.tech_ = new TechClass(techOptions);
  17360. // player.triggerReady is always async, so don't need this to be async
  17361. this.tech_.ready(bind(this, this.handleTechReady_), true);
  17362. textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
  17363. // Listen to all HTML5-defined events and trigger them on the player
  17364. TECH_EVENTS_RETRIGGER.forEach(function (event) {
  17365. _this2.on(_this2.tech_, event, _this2['handleTech' + toTitleCase(event) + '_']);
  17366. });
  17367. Object.keys(TECH_EVENTS_QUEUE).forEach(function (event) {
  17368. _this2.on(_this2.tech_, event, function (eventObj) {
  17369. if (_this2.tech_.playbackRate() === 0 && _this2.tech_.seeking()) {
  17370. _this2.queuedCallbacks_.push({
  17371. callback: _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'].bind(_this2),
  17372. event: eventObj
  17373. });
  17374. return;
  17375. }
  17376. _this2['handleTech' + TECH_EVENTS_QUEUE[event] + '_'](eventObj);
  17377. });
  17378. });
  17379. this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
  17380. this.on(this.tech_, 'sourceset', this.handleTechSourceset_);
  17381. this.on(this.tech_, 'waiting', this.handleTechWaiting_);
  17382. this.on(this.tech_, 'ended', this.handleTechEnded_);
  17383. this.on(this.tech_, 'seeking', this.handleTechSeeking_);
  17384. this.on(this.tech_, 'play', this.handleTechPlay_);
  17385. this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
  17386. this.on(this.tech_, 'pause', this.handleTechPause_);
  17387. this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
  17388. this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
  17389. this.on(this.tech_, 'error', this.handleTechError_);
  17390. this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
  17391. this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
  17392. this.on(this.tech_, 'textdata', this.handleTechTextData_);
  17393. this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
  17394. this.usingNativeControls(this.techGet_('controls'));
  17395. if (this.controls() && !this.usingNativeControls()) {
  17396. this.addTechControlsListeners_();
  17397. }
  17398. // Add the tech element in the DOM if it was not already there
  17399. // Make sure to not insert the original video element if using Html5
  17400. if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
  17401. prependTo(this.tech_.el(), this.el());
  17402. }
  17403. // Get rid of the original video tag reference after the first tech is loaded
  17404. if (this.tag) {
  17405. this.tag.player = null;
  17406. this.tag = null;
  17407. }
  17408. };
  17409. /**
  17410. * Unload and dispose of the current playback {@link Tech}.
  17411. *
  17412. * @private
  17413. */
  17414. Player.prototype.unloadTech_ = function unloadTech_() {
  17415. var _this3 = this;
  17416. // Save the current text tracks so that we can reuse the same text tracks with the next tech
  17417. ALL.names.forEach(function (name$$1) {
  17418. var props = ALL[name$$1];
  17419. _this3[props.privateName] = _this3[props.getterName]();
  17420. });
  17421. this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
  17422. this.isReady_ = false;
  17423. this.tech_.dispose();
  17424. this.tech_ = false;
  17425. if (this.isPosterFromTech_) {
  17426. this.poster_ = '';
  17427. this.trigger('posterchange');
  17428. }
  17429. this.isPosterFromTech_ = false;
  17430. };
  17431. /**
  17432. * Return a reference to the current {@link Tech}.
  17433. * It will print a warning by default about the danger of using the tech directly
  17434. * but any argument that is passed in will silence the warning.
  17435. *
  17436. * @param {*} [safety]
  17437. * Anything passed in to silence the warning
  17438. *
  17439. * @return {Tech}
  17440. * The Tech
  17441. */
  17442. Player.prototype.tech = function tech(safety) {
  17443. if (safety === undefined) {
  17444. log.warn(tsml(_templateObject$1));
  17445. }
  17446. return this.tech_;
  17447. };
  17448. /**
  17449. * Set up click and touch listeners for the playback element
  17450. *
  17451. * - On desktops: a click on the video itself will toggle playback
  17452. * - On mobile devices: a click on the video toggles controls
  17453. * which is done by toggling the user state between active and
  17454. * inactive
  17455. * - A tap can signal that a user has become active or has become inactive
  17456. * e.g. a quick tap on an iPhone movie should reveal the controls. Another
  17457. * quick tap should hide them again (signaling the user is in an inactive
  17458. * viewing state)
  17459. * - In addition to this, we still want the user to be considered inactive after
  17460. * a few seconds of inactivity.
  17461. *
  17462. * > Note: the only part of iOS interaction we can't mimic with this setup
  17463. * is a touch and hold on the video element counting as activity in order to
  17464. * keep the controls showing, but that shouldn't be an issue. A touch and hold
  17465. * on any controls will still keep the user active
  17466. *
  17467. * @private
  17468. */
  17469. Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
  17470. // Make sure to remove all the previous listeners in case we are called multiple times.
  17471. this.removeTechControlsListeners_();
  17472. // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
  17473. // trigger mousedown/up.
  17474. // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
  17475. // Any touch events are set to block the mousedown event from happening
  17476. this.on(this.tech_, 'mousedown', this.handleTechClick_);
  17477. // If the controls were hidden we don't want that to change without a tap event
  17478. // so we'll check if the controls were already showing before reporting user
  17479. // activity
  17480. this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
  17481. this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
  17482. this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
  17483. // The tap listener needs to come after the touchend listener because the tap
  17484. // listener cancels out any reportedUserActivity when setting userActive(false)
  17485. this.on(this.tech_, 'tap', this.handleTechTap_);
  17486. };
  17487. /**
  17488. * Remove the listeners used for click and tap controls. This is needed for
  17489. * toggling to controls disabled, where a tap/touch should do nothing.
  17490. *
  17491. * @private
  17492. */
  17493. Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
  17494. // We don't want to just use `this.off()` because there might be other needed
  17495. // listeners added by techs that extend this.
  17496. this.off(this.tech_, 'tap', this.handleTechTap_);
  17497. this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
  17498. this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
  17499. this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
  17500. this.off(this.tech_, 'mousedown', this.handleTechClick_);
  17501. };
  17502. /**
  17503. * Player waits for the tech to be ready
  17504. *
  17505. * @private
  17506. */
  17507. Player.prototype.handleTechReady_ = function handleTechReady_() {
  17508. this.triggerReady();
  17509. // Keep the same volume as before
  17510. if (this.cache_.volume) {
  17511. this.techCall_('setVolume', this.cache_.volume);
  17512. }
  17513. // Look if the tech found a higher resolution poster while loading
  17514. this.handleTechPosterChange_();
  17515. // Update the duration if available
  17516. this.handleTechDurationChange_();
  17517. // Chrome and Safari both have issues with autoplay.
  17518. // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
  17519. // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
  17520. // This fixes both issues. Need to wait for API, so it updates displays correctly
  17521. if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
  17522. try {
  17523. // Chrome Fix. Fixed in Chrome v16.
  17524. delete this.tag.poster;
  17525. } catch (e) {
  17526. log('deleting tag.poster throws in some browsers', e);
  17527. }
  17528. }
  17529. };
  17530. /**
  17531. * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
  17532. * function will also trigger {@link Player#firstplay} if it is the first loadstart
  17533. * for a video.
  17534. *
  17535. * @fires Player#loadstart
  17536. * @fires Player#firstplay
  17537. * @listens Tech#loadstart
  17538. * @private
  17539. */
  17540. Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
  17541. // TODO: Update to use `emptied` event instead. See #1277.
  17542. this.removeClass('vjs-ended');
  17543. this.removeClass('vjs-seeking');
  17544. // reset the error state
  17545. this.error(null);
  17546. // If it's already playing we want to trigger a firstplay event now.
  17547. // The firstplay event relies on both the play and loadstart events
  17548. // which can happen in any order for a new source
  17549. if (!this.paused()) {
  17550. /**
  17551. * Fired when the user agent begins looking for media data
  17552. *
  17553. * @event Player#loadstart
  17554. * @type {EventTarget~Event}
  17555. */
  17556. this.trigger('loadstart');
  17557. this.trigger('firstplay');
  17558. } else {
  17559. // reset the hasStarted state
  17560. this.hasStarted(false);
  17561. this.trigger('loadstart');
  17562. }
  17563. // autoplay happens after loadstart for the browser,
  17564. // so we mimic that behavior
  17565. this.manualAutoplay_(this.autoplay());
  17566. };
  17567. /**
  17568. * Handle autoplay string values, rather than the typical boolean
  17569. * values that should be handled by the tech. Note that this is not
  17570. * part of any specification. Valid values and what they do can be
  17571. * found on the autoplay getter at Player#autoplay()
  17572. */
  17573. Player.prototype.manualAutoplay_ = function manualAutoplay_(type) {
  17574. var _this4 = this;
  17575. if (!this.tech_ || typeof type !== 'string') {
  17576. return;
  17577. }
  17578. var muted = function muted() {
  17579. var previouslyMuted = _this4.muted();
  17580. _this4.muted(true);
  17581. var playPromise = _this4.play();
  17582. if (!playPromise || !playPromise.then || !playPromise['catch']) {
  17583. return;
  17584. }
  17585. return playPromise['catch'](function (e) {
  17586. // restore old value of muted on failure
  17587. _this4.muted(previouslyMuted);
  17588. });
  17589. };
  17590. var promise = void 0;
  17591. if (type === 'any') {
  17592. promise = this.play();
  17593. if (promise && promise.then && promise['catch']) {
  17594. promise['catch'](function () {
  17595. return muted();
  17596. });
  17597. }
  17598. } else if (type === 'muted') {
  17599. promise = muted();
  17600. } else {
  17601. promise = this.play();
  17602. }
  17603. if (!promise || !promise.then || !promise['catch']) {
  17604. return;
  17605. }
  17606. return promise.then(function () {
  17607. _this4.trigger({ type: 'autoplay-success', autoplay: type });
  17608. })['catch'](function (e) {
  17609. _this4.trigger({ type: 'autoplay-failure', autoplay: type });
  17610. });
  17611. };
  17612. /**
  17613. * Update the internal source caches so that we return the correct source from
  17614. * `src()`, `currentSource()`, and `currentSources()`.
  17615. *
  17616. * > Note: `currentSources` will not be updated if the source that is passed in exists
  17617. * in the current `currentSources` cache.
  17618. *
  17619. *
  17620. * @param {Tech~SourceObject} srcObj
  17621. * A string or object source to update our caches to.
  17622. */
  17623. Player.prototype.updateSourceCaches_ = function updateSourceCaches_() {
  17624. var srcObj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
  17625. var src = srcObj;
  17626. var type = '';
  17627. if (typeof src !== 'string') {
  17628. src = srcObj.src;
  17629. type = srcObj.type;
  17630. }
  17631. // make sure all the caches are set to default values
  17632. // to prevent null checking
  17633. this.cache_.source = this.cache_.source || {};
  17634. this.cache_.sources = this.cache_.sources || [];
  17635. // try to get the type of the src that was passed in
  17636. if (src && !type) {
  17637. type = findMimetype(this, src);
  17638. }
  17639. // update `currentSource` cache always
  17640. this.cache_.source = mergeOptions({}, srcObj, { src: src, type: type });
  17641. var matchingSources = this.cache_.sources.filter(function (s) {
  17642. return s.src && s.src === src;
  17643. });
  17644. var sourceElSources = [];
  17645. var sourceEls = this.$$('source');
  17646. var matchingSourceEls = [];
  17647. for (var i = 0; i < sourceEls.length; i++) {
  17648. var sourceObj = getAttributes(sourceEls[i]);
  17649. sourceElSources.push(sourceObj);
  17650. if (sourceObj.src && sourceObj.src === src) {
  17651. matchingSourceEls.push(sourceObj.src);
  17652. }
  17653. }
  17654. // if we have matching source els but not matching sources
  17655. // the current source cache is not up to date
  17656. if (matchingSourceEls.length && !matchingSources.length) {
  17657. this.cache_.sources = sourceElSources;
  17658. // if we don't have matching source or source els set the
  17659. // sources cache to the `currentSource` cache
  17660. } else if (!matchingSources.length) {
  17661. this.cache_.sources = [this.cache_.source];
  17662. }
  17663. // update the tech `src` cache
  17664. this.cache_.src = src;
  17665. };
  17666. /**
  17667. * *EXPERIMENTAL* Fired when the source is set or changed on the {@link Tech}
  17668. * causing the media element to reload.
  17669. *
  17670. * It will fire for the initial source and each subsequent source.
  17671. * This event is a custom event from Video.js and is triggered by the {@link Tech}.
  17672. *
  17673. * The event object for this event contains a `src` property that will contain the source
  17674. * that was available when the event was triggered. This is generally only necessary if Video.js
  17675. * is switching techs while the source was being changed.
  17676. *
  17677. * It is also fired when `load` is called on the player (or media element)
  17678. * because the {@link https://html.spec.whatwg.org/multipage/media.html#dom-media-load|specification for `load`}
  17679. * says that the resource selection algorithm needs to be aborted and restarted.
  17680. * In this case, it is very likely that the `src` property will be set to the
  17681. * empty string `""` to indicate we do not know what the source will be but
  17682. * that it is changing.
  17683. *
  17684. * *This event is currently still experimental and may change in minor releases.*
  17685. * __To use this, pass `enableSourceset` option to the player.__
  17686. *
  17687. * @event Player#sourceset
  17688. * @type {EventTarget~Event}
  17689. * @prop {string} src
  17690. * The source url available when the `sourceset` was triggered.
  17691. * It will be an empty string if we cannot know what the source is
  17692. * but know that the source will change.
  17693. */
  17694. /**
  17695. * Retrigger the `sourceset` event that was triggered by the {@link Tech}.
  17696. *
  17697. * @fires Player#sourceset
  17698. * @listens Tech#sourceset
  17699. * @private
  17700. */
  17701. Player.prototype.handleTechSourceset_ = function handleTechSourceset_(event) {
  17702. var _this5 = this;
  17703. // only update the source cache when the source
  17704. // was not updated using the player api
  17705. if (!this.changingSrc_) {
  17706. var updateSourceCaches = function updateSourceCaches(src) {
  17707. return _this5.updateSourceCaches_(src);
  17708. };
  17709. var playerSrc = this.currentSource().src;
  17710. var eventSrc = event.src;
  17711. // if we have a playerSrc that is not a blob, and a tech src that is a blob
  17712. if (playerSrc && !/^blob:/.test(playerSrc) && /^blob:/.test(eventSrc)) {
  17713. // if both the tech source and the player source were updated we assume
  17714. // something like @videojs/http-streaming did the sourceset and skip updating the source cache.
  17715. if (!this.lastSource_ || this.lastSource_.tech !== eventSrc && this.lastSource_.player !== playerSrc) {
  17716. updateSourceCaches = function updateSourceCaches() {};
  17717. }
  17718. }
  17719. // update the source to the intial source right away
  17720. // in some cases this will be empty string
  17721. updateSourceCaches(eventSrc);
  17722. // if the `sourceset` `src` was an empty string
  17723. // wait for a `loadstart` to update the cache to `currentSrc`.
  17724. // If a sourceset happens before a `loadstart`, we reset the state
  17725. // as this function will be called again.
  17726. if (!event.src) {
  17727. var updateCache = function updateCache(e) {
  17728. if (e.type !== 'sourceset') {
  17729. var techSrc = _this5.techGet('currentSrc');
  17730. _this5.lastSource_.tech = techSrc;
  17731. _this5.updateSourceCaches_(techSrc);
  17732. }
  17733. _this5.tech_.off(['sourceset', 'loadstart'], updateCache);
  17734. };
  17735. this.tech_.one(['sourceset', 'loadstart'], updateCache);
  17736. }
  17737. }
  17738. this.lastSource_ = { player: this.currentSource().src, tech: event.src };
  17739. this.trigger({
  17740. src: event.src,
  17741. type: 'sourceset'
  17742. });
  17743. };
  17744. /**
  17745. * Add/remove the vjs-has-started class
  17746. *
  17747. * @fires Player#firstplay
  17748. *
  17749. * @param {boolean} request
  17750. * - true: adds the class
  17751. * - false: remove the class
  17752. *
  17753. * @return {boolean}
  17754. * the boolean value of hasStarted_
  17755. */
  17756. Player.prototype.hasStarted = function hasStarted(request) {
  17757. if (request === undefined) {
  17758. // act as getter, if we have no request to change
  17759. return this.hasStarted_;
  17760. }
  17761. if (request === this.hasStarted_) {
  17762. return;
  17763. }
  17764. this.hasStarted_ = request;
  17765. if (this.hasStarted_) {
  17766. this.addClass('vjs-has-started');
  17767. this.trigger('firstplay');
  17768. } else {
  17769. this.removeClass('vjs-has-started');
  17770. }
  17771. };
  17772. /**
  17773. * Fired whenever the media begins or resumes playback
  17774. *
  17775. * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
  17776. * @fires Player#play
  17777. * @listens Tech#play
  17778. * @private
  17779. */
  17780. Player.prototype.handleTechPlay_ = function handleTechPlay_() {
  17781. this.removeClass('vjs-ended');
  17782. this.removeClass('vjs-paused');
  17783. this.addClass('vjs-playing');
  17784. // hide the poster when the user hits play
  17785. this.hasStarted(true);
  17786. /**
  17787. * Triggered whenever an {@link Tech#play} event happens. Indicates that
  17788. * playback has started or resumed.
  17789. *
  17790. * @event Player#play
  17791. * @type {EventTarget~Event}
  17792. */
  17793. this.trigger('play');
  17794. };
  17795. /**
  17796. * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
  17797. *
  17798. * If there were any events queued while the playback rate was zero, fire
  17799. * those events now.
  17800. *
  17801. * @private
  17802. * @method Player#handleTechRateChange_
  17803. * @fires Player#ratechange
  17804. * @listens Tech#ratechange
  17805. */
  17806. Player.prototype.handleTechRateChange_ = function handleTechRateChange_() {
  17807. if (this.tech_.playbackRate() > 0 && this.cache_.lastPlaybackRate === 0) {
  17808. this.queuedCallbacks_.forEach(function (queued) {
  17809. return queued.callback(queued.event);
  17810. });
  17811. this.queuedCallbacks_ = [];
  17812. }
  17813. this.cache_.lastPlaybackRate = this.tech_.playbackRate();
  17814. /**
  17815. * Fires when the playing speed of the audio/video is changed
  17816. *
  17817. * @event Player#ratechange
  17818. * @type {event}
  17819. */
  17820. this.trigger('ratechange');
  17821. };
  17822. /**
  17823. * Retrigger the `waiting` event that was triggered by the {@link Tech}.
  17824. *
  17825. * @fires Player#waiting
  17826. * @listens Tech#waiting
  17827. * @private
  17828. */
  17829. Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
  17830. var _this6 = this;
  17831. this.addClass('vjs-waiting');
  17832. /**
  17833. * A readyState change on the DOM element has caused playback to stop.
  17834. *
  17835. * @event Player#waiting
  17836. * @type {EventTarget~Event}
  17837. */
  17838. this.trigger('waiting');
  17839. this.one('timeupdate', function () {
  17840. return _this6.removeClass('vjs-waiting');
  17841. });
  17842. };
  17843. /**
  17844. * Retrigger the `canplay` event that was triggered by the {@link Tech}.
  17845. * > Note: This is not consistent between browsers. See #1351
  17846. *
  17847. * @fires Player#canplay
  17848. * @listens Tech#canplay
  17849. * @private
  17850. */
  17851. Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
  17852. this.removeClass('vjs-waiting');
  17853. /**
  17854. * The media has a readyState of HAVE_FUTURE_DATA or greater.
  17855. *
  17856. * @event Player#canplay
  17857. * @type {EventTarget~Event}
  17858. */
  17859. this.trigger('canplay');
  17860. };
  17861. /**
  17862. * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
  17863. *
  17864. * @fires Player#canplaythrough
  17865. * @listens Tech#canplaythrough
  17866. * @private
  17867. */
  17868. Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
  17869. this.removeClass('vjs-waiting');
  17870. /**
  17871. * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
  17872. * entire media file can be played without buffering.
  17873. *
  17874. * @event Player#canplaythrough
  17875. * @type {EventTarget~Event}
  17876. */
  17877. this.trigger('canplaythrough');
  17878. };
  17879. /**
  17880. * Retrigger the `playing` event that was triggered by the {@link Tech}.
  17881. *
  17882. * @fires Player#playing
  17883. * @listens Tech#playing
  17884. * @private
  17885. */
  17886. Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
  17887. this.removeClass('vjs-waiting');
  17888. /**
  17889. * The media is no longer blocked from playback, and has started playing.
  17890. *
  17891. * @event Player#playing
  17892. * @type {EventTarget~Event}
  17893. */
  17894. this.trigger('playing');
  17895. };
  17896. /**
  17897. * Retrigger the `seeking` event that was triggered by the {@link Tech}.
  17898. *
  17899. * @fires Player#seeking
  17900. * @listens Tech#seeking
  17901. * @private
  17902. */
  17903. Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
  17904. this.addClass('vjs-seeking');
  17905. /**
  17906. * Fired whenever the player is jumping to a new time
  17907. *
  17908. * @event Player#seeking
  17909. * @type {EventTarget~Event}
  17910. */
  17911. this.trigger('seeking');
  17912. };
  17913. /**
  17914. * Retrigger the `seeked` event that was triggered by the {@link Tech}.
  17915. *
  17916. * @fires Player#seeked
  17917. * @listens Tech#seeked
  17918. * @private
  17919. */
  17920. Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
  17921. this.removeClass('vjs-seeking');
  17922. /**
  17923. * Fired when the player has finished jumping to a new time
  17924. *
  17925. * @event Player#seeked
  17926. * @type {EventTarget~Event}
  17927. */
  17928. this.trigger('seeked');
  17929. };
  17930. /**
  17931. * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
  17932. *
  17933. * @fires Player#firstplay
  17934. * @listens Tech#firstplay
  17935. * @deprecated As of 6.0 firstplay event is deprecated.
  17936. * As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
  17937. * @private
  17938. */
  17939. Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
  17940. // If the first starttime attribute is specified
  17941. // then we will start at the given offset in seconds
  17942. if (this.options_.starttime) {
  17943. log.warn('Passing the `starttime` option to the player will be deprecated in 6.0');
  17944. this.currentTime(this.options_.starttime);
  17945. }
  17946. this.addClass('vjs-has-started');
  17947. /**
  17948. * Fired the first time a video is played. Not part of the HLS spec, and this is
  17949. * probably not the best implementation yet, so use sparingly. If you don't have a
  17950. * reason to prevent playback, use `myPlayer.one('play');` instead.
  17951. *
  17952. * @event Player#firstplay
  17953. * @deprecated As of 6.0 firstplay event is deprecated.
  17954. * @type {EventTarget~Event}
  17955. */
  17956. this.trigger('firstplay');
  17957. };
  17958. /**
  17959. * Retrigger the `pause` event that was triggered by the {@link Tech}.
  17960. *
  17961. * @fires Player#pause
  17962. * @listens Tech#pause
  17963. * @private
  17964. */
  17965. Player.prototype.handleTechPause_ = function handleTechPause_() {
  17966. this.removeClass('vjs-playing');
  17967. this.addClass('vjs-paused');
  17968. /**
  17969. * Fired whenever the media has been paused
  17970. *
  17971. * @event Player#pause
  17972. * @type {EventTarget~Event}
  17973. */
  17974. this.trigger('pause');
  17975. };
  17976. /**
  17977. * Retrigger the `ended` event that was triggered by the {@link Tech}.
  17978. *
  17979. * @fires Player#ended
  17980. * @listens Tech#ended
  17981. * @private
  17982. */
  17983. Player.prototype.handleTechEnded_ = function handleTechEnded_() {
  17984. this.addClass('vjs-ended');
  17985. if (this.options_.loop) {
  17986. this.currentTime(0);
  17987. this.play();
  17988. } else if (!this.paused()) {
  17989. this.pause();
  17990. }
  17991. /**
  17992. * Fired when the end of the media resource is reached (currentTime == duration)
  17993. *
  17994. * @event Player#ended
  17995. * @type {EventTarget~Event}
  17996. */
  17997. this.trigger('ended');
  17998. };
  17999. /**
  18000. * Fired when the duration of the media resource is first known or changed
  18001. *
  18002. * @listens Tech#durationchange
  18003. * @private
  18004. */
  18005. Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
  18006. this.duration(this.techGet_('duration'));
  18007. };
  18008. /**
  18009. * Handle a click on the media element to play/pause
  18010. *
  18011. * @param {EventTarget~Event} event
  18012. * the event that caused this function to trigger
  18013. *
  18014. * @listens Tech#mousedown
  18015. * @private
  18016. */
  18017. Player.prototype.handleTechClick_ = function handleTechClick_(event) {
  18018. if (!isSingleLeftClick(event)) {
  18019. return;
  18020. }
  18021. // When controls are disabled a click should not toggle playback because
  18022. // the click is considered a control
  18023. if (!this.controls_) {
  18024. return;
  18025. }
  18026. if (this.paused()) {
  18027. silencePromise(this.play());
  18028. } else {
  18029. this.pause();
  18030. }
  18031. };
  18032. /**
  18033. * Handle a tap on the media element. It will toggle the user
  18034. * activity state, which hides and shows the controls.
  18035. *
  18036. * @listens Tech#tap
  18037. * @private
  18038. */
  18039. Player.prototype.handleTechTap_ = function handleTechTap_() {
  18040. this.userActive(!this.userActive());
  18041. };
  18042. /**
  18043. * Handle touch to start
  18044. *
  18045. * @listens Tech#touchstart
  18046. * @private
  18047. */
  18048. Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
  18049. this.userWasActive = this.userActive();
  18050. };
  18051. /**
  18052. * Handle touch to move
  18053. *
  18054. * @listens Tech#touchmove
  18055. * @private
  18056. */
  18057. Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
  18058. if (this.userWasActive) {
  18059. this.reportUserActivity();
  18060. }
  18061. };
  18062. /**
  18063. * Handle touch to end
  18064. *
  18065. * @param {EventTarget~Event} event
  18066. * the touchend event that triggered
  18067. * this function
  18068. *
  18069. * @listens Tech#touchend
  18070. * @private
  18071. */
  18072. Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
  18073. // Stop the mouse events from also happening
  18074. event.preventDefault();
  18075. };
  18076. /**
  18077. * Fired when the player switches in or out of fullscreen mode
  18078. *
  18079. * @private
  18080. * @listens Player#fullscreenchange
  18081. */
  18082. Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
  18083. if (this.isFullscreen()) {
  18084. this.addClass('vjs-fullscreen');
  18085. } else {
  18086. this.removeClass('vjs-fullscreen');
  18087. }
  18088. };
  18089. /**
  18090. * native click events on the SWF aren't triggered on IE11, Win8.1RT
  18091. * use stageclick events triggered from inside the SWF instead
  18092. *
  18093. * @private
  18094. * @listens stageclick
  18095. */
  18096. Player.prototype.handleStageClick_ = function handleStageClick_() {
  18097. this.reportUserActivity();
  18098. };
  18099. /**
  18100. * Handle Tech Fullscreen Change
  18101. *
  18102. * @param {EventTarget~Event} event
  18103. * the fullscreenchange event that triggered this function
  18104. *
  18105. * @param {Object} data
  18106. * the data that was sent with the event
  18107. *
  18108. * @private
  18109. * @listens Tech#fullscreenchange
  18110. * @fires Player#fullscreenchange
  18111. */
  18112. Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
  18113. if (data) {
  18114. this.isFullscreen(data.isFullscreen);
  18115. }
  18116. /**
  18117. * Fired when going in and out of fullscreen.
  18118. *
  18119. * @event Player#fullscreenchange
  18120. * @type {EventTarget~Event}
  18121. */
  18122. this.trigger('fullscreenchange');
  18123. };
  18124. /**
  18125. * Fires when an error occurred during the loading of an audio/video.
  18126. *
  18127. * @private
  18128. * @listens Tech#error
  18129. */
  18130. Player.prototype.handleTechError_ = function handleTechError_() {
  18131. var error = this.tech_.error();
  18132. this.error(error);
  18133. };
  18134. /**
  18135. * Retrigger the `textdata` event that was triggered by the {@link Tech}.
  18136. *
  18137. * @fires Player#textdata
  18138. * @listens Tech#textdata
  18139. * @private
  18140. */
  18141. Player.prototype.handleTechTextData_ = function handleTechTextData_() {
  18142. var data = null;
  18143. if (arguments.length > 1) {
  18144. data = arguments[1];
  18145. }
  18146. /**
  18147. * Fires when we get a textdata event from tech
  18148. *
  18149. * @event Player#textdata
  18150. * @type {EventTarget~Event}
  18151. */
  18152. this.trigger('textdata', data);
  18153. };
  18154. /**
  18155. * Get object for cached values.
  18156. *
  18157. * @return {Object}
  18158. * get the current object cache
  18159. */
  18160. Player.prototype.getCache = function getCache() {
  18161. return this.cache_;
  18162. };
  18163. /**
  18164. * Pass values to the playback tech
  18165. *
  18166. * @param {string} [method]
  18167. * the method to call
  18168. *
  18169. * @param {Object} arg
  18170. * the argument to pass
  18171. *
  18172. * @private
  18173. */
  18174. Player.prototype.techCall_ = function techCall_(method, arg) {
  18175. // If it's not ready yet, call method when it is
  18176. this.ready(function () {
  18177. if (method in allowedSetters) {
  18178. return set$1(this.middleware_, this.tech_, method, arg);
  18179. } else if (method in allowedMediators) {
  18180. return mediate(this.middleware_, this.tech_, method, arg);
  18181. }
  18182. try {
  18183. if (this.tech_) {
  18184. this.tech_[method](arg);
  18185. }
  18186. } catch (e) {
  18187. log(e);
  18188. throw e;
  18189. }
  18190. }, true);
  18191. };
  18192. /**
  18193. * Get calls can't wait for the tech, and sometimes don't need to.
  18194. *
  18195. * @param {string} method
  18196. * Tech method
  18197. *
  18198. * @return {Function|undefined}
  18199. * the method or undefined
  18200. *
  18201. * @private
  18202. */
  18203. Player.prototype.techGet_ = function techGet_(method) {
  18204. if (!this.tech_ || !this.tech_.isReady_) {
  18205. return;
  18206. }
  18207. if (method in allowedGetters) {
  18208. return get$1(this.middleware_, this.tech_, method);
  18209. } else if (method in allowedMediators) {
  18210. return mediate(this.middleware_, this.tech_, method);
  18211. }
  18212. // Flash likes to die and reload when you hide or reposition it.
  18213. // In these cases the object methods go away and we get errors.
  18214. // When that happens we'll catch the errors and inform tech that it's not ready any more.
  18215. try {
  18216. return this.tech_[method]();
  18217. } catch (e) {
  18218. // When building additional tech libs, an expected method may not be defined yet
  18219. if (this.tech_[method] === undefined) {
  18220. log('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
  18221. throw e;
  18222. }
  18223. // When a method isn't available on the object it throws a TypeError
  18224. if (e.name === 'TypeError') {
  18225. log('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
  18226. this.tech_.isReady_ = false;
  18227. throw e;
  18228. }
  18229. // If error unknown, just log and throw
  18230. log(e);
  18231. throw e;
  18232. }
  18233. };
  18234. /**
  18235. * Attempt to begin playback at the first opportunity.
  18236. *
  18237. * @return {Promise|undefined}
  18238. * Returns a promise if the browser supports Promises (or one
  18239. * was passed in as an option). This promise will be resolved on
  18240. * the return value of play. If this is undefined it will fulfill the
  18241. * promise chain otherwise the promise chain will be fulfilled when
  18242. * the promise from play is fulfilled.
  18243. */
  18244. Player.prototype.play = function play() {
  18245. var _this7 = this;
  18246. var PromiseClass = this.options_.Promise || window_1.Promise;
  18247. if (PromiseClass) {
  18248. return new PromiseClass(function (resolve) {
  18249. _this7.play_(resolve);
  18250. });
  18251. }
  18252. return this.play_();
  18253. };
  18254. /**
  18255. * The actual logic for play, takes a callback that will be resolved on the
  18256. * return value of play. This allows us to resolve to the play promise if there
  18257. * is one on modern browsers.
  18258. *
  18259. * @private
  18260. * @param {Function} [callback]
  18261. * The callback that should be called when the techs play is actually called
  18262. */
  18263. Player.prototype.play_ = function play_() {
  18264. var _this8 = this;
  18265. var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : silencePromise;
  18266. // If this is called while we have a play queued up on a loadstart, remove
  18267. // that listener to avoid getting in a potentially bad state.
  18268. if (this.playOnLoadstart_) {
  18269. this.off('loadstart', this.playOnLoadstart_);
  18270. }
  18271. // If the player/tech is not ready, queue up another call to `play()` for
  18272. // when it is. This will loop back into this method for another attempt at
  18273. // playback when the tech is ready.
  18274. if (!this.isReady_) {
  18275. // Bail out if we're already waiting for `ready`!
  18276. if (this.playWaitingForReady_) {
  18277. return;
  18278. }
  18279. this.playWaitingForReady_ = true;
  18280. this.ready(function () {
  18281. _this8.playWaitingForReady_ = false;
  18282. callback(_this8.play());
  18283. });
  18284. // If the player/tech is ready and we have a source, we can attempt playback.
  18285. } else if (!this.changingSrc_ && (this.src() || this.currentSrc())) {
  18286. callback(this.techGet_('play'));
  18287. return;
  18288. // If the tech is ready, but we do not have a source, we'll need to wait
  18289. // for both the `ready` and a `loadstart` when the source is finally
  18290. // resolved by middleware and set on the player.
  18291. //
  18292. // This can happen if `play()` is called while changing sources or before
  18293. // one has been set on the player.
  18294. } else {
  18295. this.playOnLoadstart_ = function () {
  18296. _this8.playOnLoadstart_ = null;
  18297. callback(_this8.play());
  18298. };
  18299. this.one('loadstart', this.playOnLoadstart_);
  18300. }
  18301. };
  18302. /**
  18303. * Pause the video playback
  18304. *
  18305. * @return {Player}
  18306. * A reference to the player object this function was called on
  18307. */
  18308. Player.prototype.pause = function pause() {
  18309. this.techCall_('pause');
  18310. };
  18311. /**
  18312. * Check if the player is paused or has yet to play
  18313. *
  18314. * @return {boolean}
  18315. * - false: if the media is currently playing
  18316. * - true: if media is not currently playing
  18317. */
  18318. Player.prototype.paused = function paused() {
  18319. // The initial state of paused should be true (in Safari it's actually false)
  18320. return this.techGet_('paused') === false ? false : true;
  18321. };
  18322. /**
  18323. * Get a TimeRange object representing the current ranges of time that the user
  18324. * has played.
  18325. *
  18326. * @return {TimeRange}
  18327. * A time range object that represents all the increments of time that have
  18328. * been played.
  18329. */
  18330. Player.prototype.played = function played() {
  18331. return this.techGet_('played') || createTimeRanges(0, 0);
  18332. };
  18333. /**
  18334. * Returns whether or not the user is "scrubbing". Scrubbing is
  18335. * when the user has clicked the progress bar handle and is
  18336. * dragging it along the progress bar.
  18337. *
  18338. * @param {boolean} [isScrubbing]
  18339. * wether the user is or is not scrubbing
  18340. *
  18341. * @return {boolean}
  18342. * The value of scrubbing when getting
  18343. */
  18344. Player.prototype.scrubbing = function scrubbing(isScrubbing) {
  18345. if (typeof isScrubbing === 'undefined') {
  18346. return this.scrubbing_;
  18347. }
  18348. this.scrubbing_ = !!isScrubbing;
  18349. if (isScrubbing) {
  18350. this.addClass('vjs-scrubbing');
  18351. } else {
  18352. this.removeClass('vjs-scrubbing');
  18353. }
  18354. };
  18355. /**
  18356. * Get or set the current time (in seconds)
  18357. *
  18358. * @param {number|string} [seconds]
  18359. * The time to seek to in seconds
  18360. *
  18361. * @return {number}
  18362. * - the current time in seconds when getting
  18363. */
  18364. Player.prototype.currentTime = function currentTime(seconds) {
  18365. if (typeof seconds !== 'undefined') {
  18366. if (seconds < 0) {
  18367. seconds = 0;
  18368. }
  18369. this.techCall_('setCurrentTime', seconds);
  18370. return;
  18371. }
  18372. // cache last currentTime and return. default to 0 seconds
  18373. //
  18374. // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
  18375. // currentTime when scrubbing, but may not provide much performance benefit afterall.
  18376. // Should be tested. Also something has to read the actual current time or the cache will
  18377. // never get updated.
  18378. this.cache_.currentTime = this.techGet_('currentTime') || 0;
  18379. return this.cache_.currentTime;
  18380. };
  18381. /**
  18382. * Normally gets the length in time of the video in seconds;
  18383. * in all but the rarest use cases an argument will NOT be passed to the method
  18384. *
  18385. * > **NOTE**: The video must have started loading before the duration can be
  18386. * known, and in the case of Flash, may not be known until the video starts
  18387. * playing.
  18388. *
  18389. * @fires Player#durationchange
  18390. *
  18391. * @param {number} [seconds]
  18392. * The duration of the video to set in seconds
  18393. *
  18394. * @return {number}
  18395. * - The duration of the video in seconds when getting
  18396. */
  18397. Player.prototype.duration = function duration(seconds) {
  18398. if (seconds === undefined) {
  18399. // return NaN if the duration is not known
  18400. return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
  18401. }
  18402. seconds = parseFloat(seconds);
  18403. // Standardize on Inifity for signaling video is live
  18404. if (seconds < 0) {
  18405. seconds = Infinity;
  18406. }
  18407. if (seconds !== this.cache_.duration) {
  18408. // Cache the last set value for optimized scrubbing (esp. Flash)
  18409. this.cache_.duration = seconds;
  18410. if (seconds === Infinity) {
  18411. this.addClass('vjs-live');
  18412. } else {
  18413. this.removeClass('vjs-live');
  18414. }
  18415. /**
  18416. * @event Player#durationchange
  18417. * @type {EventTarget~Event}
  18418. */
  18419. this.trigger('durationchange');
  18420. }
  18421. };
  18422. /**
  18423. * Calculates how much time is left in the video. Not part
  18424. * of the native video API.
  18425. *
  18426. * @return {number}
  18427. * The time remaining in seconds
  18428. */
  18429. Player.prototype.remainingTime = function remainingTime() {
  18430. return this.duration() - this.currentTime();
  18431. };
  18432. /**
  18433. * A remaining time function that is intented to be used when
  18434. * the time is to be displayed directly to the user.
  18435. *
  18436. * @return {number}
  18437. * The rounded time remaining in seconds
  18438. */
  18439. Player.prototype.remainingTimeDisplay = function remainingTimeDisplay() {
  18440. return Math.floor(this.duration()) - Math.floor(this.currentTime());
  18441. };
  18442. //
  18443. // Kind of like an array of portions of the video that have been downloaded.
  18444. /**
  18445. * Get a TimeRange object with an array of the times of the video
  18446. * that have been downloaded. If you just want the percent of the
  18447. * video that's been downloaded, use bufferedPercent.
  18448. *
  18449. * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
  18450. *
  18451. * @return {TimeRange}
  18452. * A mock TimeRange object (following HTML spec)
  18453. */
  18454. Player.prototype.buffered = function buffered() {
  18455. var buffered = this.techGet_('buffered');
  18456. if (!buffered || !buffered.length) {
  18457. buffered = createTimeRanges(0, 0);
  18458. }
  18459. return buffered;
  18460. };
  18461. /**
  18462. * Get the percent (as a decimal) of the video that's been downloaded.
  18463. * This method is not a part of the native HTML video API.
  18464. *
  18465. * @return {number}
  18466. * A decimal between 0 and 1 representing the percent
  18467. * that is bufferred 0 being 0% and 1 being 100%
  18468. */
  18469. Player.prototype.bufferedPercent = function bufferedPercent$$1() {
  18470. return bufferedPercent(this.buffered(), this.duration());
  18471. };
  18472. /**
  18473. * Get the ending time of the last buffered time range
  18474. * This is used in the progress bar to encapsulate all time ranges.
  18475. *
  18476. * @return {number}
  18477. * The end of the last buffered time range
  18478. */
  18479. Player.prototype.bufferedEnd = function bufferedEnd() {
  18480. var buffered = this.buffered();
  18481. var duration = this.duration();
  18482. var end = buffered.end(buffered.length - 1);
  18483. if (end > duration) {
  18484. end = duration;
  18485. }
  18486. return end;
  18487. };
  18488. /**
  18489. * Get or set the current volume of the media
  18490. *
  18491. * @param {number} [percentAsDecimal]
  18492. * The new volume as a decimal percent:
  18493. * - 0 is muted/0%/off
  18494. * - 1.0 is 100%/full
  18495. * - 0.5 is half volume or 50%
  18496. *
  18497. * @return {number}
  18498. * The current volume as a percent when getting
  18499. */
  18500. Player.prototype.volume = function volume(percentAsDecimal) {
  18501. var vol = void 0;
  18502. if (percentAsDecimal !== undefined) {
  18503. // Force value to between 0 and 1
  18504. vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
  18505. this.cache_.volume = vol;
  18506. this.techCall_('setVolume', vol);
  18507. if (vol > 0) {
  18508. this.lastVolume_(vol);
  18509. }
  18510. return;
  18511. }
  18512. // Default to 1 when returning current volume.
  18513. vol = parseFloat(this.techGet_('volume'));
  18514. return isNaN(vol) ? 1 : vol;
  18515. };
  18516. /**
  18517. * Get the current muted state, or turn mute on or off
  18518. *
  18519. * @param {boolean} [muted]
  18520. * - true to mute
  18521. * - false to unmute
  18522. *
  18523. * @return {boolean}
  18524. * - true if mute is on and getting
  18525. * - false if mute is off and getting
  18526. */
  18527. Player.prototype.muted = function muted(_muted) {
  18528. if (_muted !== undefined) {
  18529. this.techCall_('setMuted', _muted);
  18530. return;
  18531. }
  18532. return this.techGet_('muted') || false;
  18533. };
  18534. /**
  18535. * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
  18536. * indicates the state of muted on intial playback.
  18537. *
  18538. * ```js
  18539. * var myPlayer = videojs('some-player-id');
  18540. *
  18541. * myPlayer.src("http://www.example.com/path/to/video.mp4");
  18542. *
  18543. * // get, should be false
  18544. * console.log(myPlayer.defaultMuted());
  18545. * // set to true
  18546. * myPlayer.defaultMuted(true);
  18547. * // get should be true
  18548. * console.log(myPlayer.defaultMuted());
  18549. * ```
  18550. *
  18551. * @param {boolean} [defaultMuted]
  18552. * - true to mute
  18553. * - false to unmute
  18554. *
  18555. * @return {boolean|Player}
  18556. * - true if defaultMuted is on and getting
  18557. * - false if defaultMuted is off and getting
  18558. * - A reference to the current player when setting
  18559. */
  18560. Player.prototype.defaultMuted = function defaultMuted(_defaultMuted) {
  18561. if (_defaultMuted !== undefined) {
  18562. return this.techCall_('setDefaultMuted', _defaultMuted);
  18563. }
  18564. return this.techGet_('defaultMuted') || false;
  18565. };
  18566. /**
  18567. * Get the last volume, or set it
  18568. *
  18569. * @param {number} [percentAsDecimal]
  18570. * The new last volume as a decimal percent:
  18571. * - 0 is muted/0%/off
  18572. * - 1.0 is 100%/full
  18573. * - 0.5 is half volume or 50%
  18574. *
  18575. * @return {number}
  18576. * the current value of lastVolume as a percent when getting
  18577. *
  18578. * @private
  18579. */
  18580. Player.prototype.lastVolume_ = function lastVolume_(percentAsDecimal) {
  18581. if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
  18582. this.cache_.lastVolume = percentAsDecimal;
  18583. return;
  18584. }
  18585. return this.cache_.lastVolume;
  18586. };
  18587. /**
  18588. * Check if current tech can support native fullscreen
  18589. * (e.g. with built in controls like iOS, so not our flash swf)
  18590. *
  18591. * @return {boolean}
  18592. * if native fullscreen is supported
  18593. */
  18594. Player.prototype.supportsFullScreen = function supportsFullScreen() {
  18595. return this.techGet_('supportsFullScreen') || false;
  18596. };
  18597. /**
  18598. * Check if the player is in fullscreen mode or tell the player that it
  18599. * is or is not in fullscreen mode.
  18600. *
  18601. * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
  18602. * property and instead document.fullscreenElement is used. But isFullscreen is
  18603. * still a valuable property for internal player workings.
  18604. *
  18605. * @param {boolean} [isFS]
  18606. * Set the players current fullscreen state
  18607. *
  18608. * @return {boolean}
  18609. * - true if fullscreen is on and getting
  18610. * - false if fullscreen is off and getting
  18611. */
  18612. Player.prototype.isFullscreen = function isFullscreen(isFS) {
  18613. if (isFS !== undefined) {
  18614. this.isFullscreen_ = !!isFS;
  18615. return;
  18616. }
  18617. return !!this.isFullscreen_;
  18618. };
  18619. /**
  18620. * Increase the size of the video to full screen
  18621. * In some browsers, full screen is not supported natively, so it enters
  18622. * "full window mode", where the video fills the browser window.
  18623. * In browsers and devices that support native full screen, sometimes the
  18624. * browser's default controls will be shown, and not the Video.js custom skin.
  18625. * This includes most mobile devices (iOS, Android) and older versions of
  18626. * Safari.
  18627. *
  18628. * @fires Player#fullscreenchange
  18629. */
  18630. Player.prototype.requestFullscreen = function requestFullscreen() {
  18631. var fsApi = FullscreenApi;
  18632. this.isFullscreen(true);
  18633. if (fsApi.requestFullscreen) {
  18634. // the browser supports going fullscreen at the element level so we can
  18635. // take the controls fullscreen as well as the video
  18636. // Trigger fullscreenchange event after change
  18637. // We have to specifically add this each time, and remove
  18638. // when canceling fullscreen. Otherwise if there's multiple
  18639. // players on a page, they would all be reacting to the same fullscreen
  18640. // events
  18641. on(document_1, fsApi.fullscreenchange, bind(this, function documentFullscreenChange(e) {
  18642. this.isFullscreen(document_1[fsApi.fullscreenElement]);
  18643. // If cancelling fullscreen, remove event listener.
  18644. if (this.isFullscreen() === false) {
  18645. off(document_1, fsApi.fullscreenchange, documentFullscreenChange);
  18646. }
  18647. /**
  18648. * @event Player#fullscreenchange
  18649. * @type {EventTarget~Event}
  18650. */
  18651. this.trigger('fullscreenchange');
  18652. }));
  18653. this.el_[fsApi.requestFullscreen]();
  18654. } else if (this.tech_.supportsFullScreen()) {
  18655. // we can't take the video.js controls fullscreen but we can go fullscreen
  18656. // with native controls
  18657. this.techCall_('enterFullScreen');
  18658. } else {
  18659. // fullscreen isn't supported so we'll just stretch the video element to
  18660. // fill the viewport
  18661. this.enterFullWindow();
  18662. /**
  18663. * @event Player#fullscreenchange
  18664. * @type {EventTarget~Event}
  18665. */
  18666. this.trigger('fullscreenchange');
  18667. }
  18668. };
  18669. /**
  18670. * Return the video to its normal size after having been in full screen mode
  18671. *
  18672. * @fires Player#fullscreenchange
  18673. */
  18674. Player.prototype.exitFullscreen = function exitFullscreen() {
  18675. var fsApi = FullscreenApi;
  18676. this.isFullscreen(false);
  18677. // Check for browser element fullscreen support
  18678. if (fsApi.requestFullscreen) {
  18679. document_1[fsApi.exitFullscreen]();
  18680. } else if (this.tech_.supportsFullScreen()) {
  18681. this.techCall_('exitFullScreen');
  18682. } else {
  18683. this.exitFullWindow();
  18684. /**
  18685. * @event Player#fullscreenchange
  18686. * @type {EventTarget~Event}
  18687. */
  18688. this.trigger('fullscreenchange');
  18689. }
  18690. };
  18691. /**
  18692. * When fullscreen isn't supported we can stretch the
  18693. * video container to as wide as the browser will let us.
  18694. *
  18695. * @fires Player#enterFullWindow
  18696. */
  18697. Player.prototype.enterFullWindow = function enterFullWindow() {
  18698. this.isFullWindow = true;
  18699. // Storing original doc overflow value to return to when fullscreen is off
  18700. this.docOrigOverflow = document_1.documentElement.style.overflow;
  18701. // Add listener for esc key to exit fullscreen
  18702. on(document_1, 'keydown', bind(this, this.fullWindowOnEscKey));
  18703. // Hide any scroll bars
  18704. document_1.documentElement.style.overflow = 'hidden';
  18705. // Apply fullscreen styles
  18706. addClass(document_1.body, 'vjs-full-window');
  18707. /**
  18708. * @event Player#enterFullWindow
  18709. * @type {EventTarget~Event}
  18710. */
  18711. this.trigger('enterFullWindow');
  18712. };
  18713. /**
  18714. * Check for call to either exit full window or
  18715. * full screen on ESC key
  18716. *
  18717. * @param {string} event
  18718. * Event to check for key press
  18719. */
  18720. Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
  18721. if (event.keyCode === 27) {
  18722. if (this.isFullscreen() === true) {
  18723. this.exitFullscreen();
  18724. } else {
  18725. this.exitFullWindow();
  18726. }
  18727. }
  18728. };
  18729. /**
  18730. * Exit full window
  18731. *
  18732. * @fires Player#exitFullWindow
  18733. */
  18734. Player.prototype.exitFullWindow = function exitFullWindow() {
  18735. this.isFullWindow = false;
  18736. off(document_1, 'keydown', this.fullWindowOnEscKey);
  18737. // Unhide scroll bars.
  18738. document_1.documentElement.style.overflow = this.docOrigOverflow;
  18739. // Remove fullscreen styles
  18740. removeClass(document_1.body, 'vjs-full-window');
  18741. // Resize the box, controller, and poster to original sizes
  18742. // this.positionAll();
  18743. /**
  18744. * @event Player#exitFullWindow
  18745. * @type {EventTarget~Event}
  18746. */
  18747. this.trigger('exitFullWindow');
  18748. };
  18749. /**
  18750. * Check whether the player can play a given mimetype
  18751. *
  18752. * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
  18753. *
  18754. * @param {string} type
  18755. * The mimetype to check
  18756. *
  18757. * @return {string}
  18758. * 'probably', 'maybe', or '' (empty string)
  18759. */
  18760. Player.prototype.canPlayType = function canPlayType(type) {
  18761. var can = void 0;
  18762. // Loop through each playback technology in the options order
  18763. for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
  18764. var techName = j[i];
  18765. var tech = Tech.getTech(techName);
  18766. // Support old behavior of techs being registered as components.
  18767. // Remove once that deprecated behavior is removed.
  18768. if (!tech) {
  18769. tech = Component.getComponent(techName);
  18770. }
  18771. // Check if the current tech is defined before continuing
  18772. if (!tech) {
  18773. log.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
  18774. continue;
  18775. }
  18776. // Check if the browser supports this technology
  18777. if (tech.isSupported()) {
  18778. can = tech.canPlayType(type);
  18779. if (can) {
  18780. return can;
  18781. }
  18782. }
  18783. }
  18784. return '';
  18785. };
  18786. /**
  18787. * Select source based on tech-order or source-order
  18788. * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
  18789. * defaults to tech-order selection
  18790. *
  18791. * @param {Array} sources
  18792. * The sources for a media asset
  18793. *
  18794. * @return {Object|boolean}
  18795. * Object of source and tech order or false
  18796. */
  18797. Player.prototype.selectSource = function selectSource(sources) {
  18798. var _this9 = this;
  18799. // Get only the techs specified in `techOrder` that exist and are supported by the
  18800. // current platform
  18801. var techs = this.options_.techOrder.map(function (techName) {
  18802. return [techName, Tech.getTech(techName)];
  18803. }).filter(function (_ref) {
  18804. var techName = _ref[0],
  18805. tech = _ref[1];
  18806. // Check if the current tech is defined before continuing
  18807. if (tech) {
  18808. // Check if the browser supports this technology
  18809. return tech.isSupported();
  18810. }
  18811. log.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
  18812. return false;
  18813. });
  18814. // Iterate over each `innerArray` element once per `outerArray` element and execute
  18815. // `tester` with both. If `tester` returns a non-falsy value, exit early and return
  18816. // that value.
  18817. var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
  18818. var found = void 0;
  18819. outerArray.some(function (outerChoice) {
  18820. return innerArray.some(function (innerChoice) {
  18821. found = tester(outerChoice, innerChoice);
  18822. if (found) {
  18823. return true;
  18824. }
  18825. });
  18826. });
  18827. return found;
  18828. };
  18829. var foundSourceAndTech = void 0;
  18830. var flip = function flip(fn) {
  18831. return function (a, b) {
  18832. return fn(b, a);
  18833. };
  18834. };
  18835. var finder = function finder(_ref2, source) {
  18836. var techName = _ref2[0],
  18837. tech = _ref2[1];
  18838. if (tech.canPlaySource(source, _this9.options_[techName.toLowerCase()])) {
  18839. return { source: source, tech: techName };
  18840. }
  18841. };
  18842. // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
  18843. // to select from them based on their priority.
  18844. if (this.options_.sourceOrder) {
  18845. // Source-first ordering
  18846. foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
  18847. } else {
  18848. // Tech-first ordering
  18849. foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
  18850. }
  18851. return foundSourceAndTech || false;
  18852. };
  18853. /**
  18854. * Get or set the video source.
  18855. *
  18856. * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
  18857. * A SourceObject, an array of SourceObjects, or a string referencing
  18858. * a URL to a media source. It is _highly recommended_ that an object
  18859. * or array of objects is used here, so that source selection
  18860. * algorithms can take the `type` into account.
  18861. *
  18862. * If not provided, this method acts as a getter.
  18863. *
  18864. * @return {string|undefined}
  18865. * If the `source` argument is missing, returns the current source
  18866. * URL. Otherwise, returns nothing/undefined.
  18867. */
  18868. Player.prototype.src = function src(source) {
  18869. var _this10 = this;
  18870. // getter usage
  18871. if (typeof source === 'undefined') {
  18872. return this.cache_.src || '';
  18873. }
  18874. // filter out invalid sources and turn our source into
  18875. // an array of source objects
  18876. var sources = filterSource(source);
  18877. // if a source was passed in then it is invalid because
  18878. // it was filtered to a zero length Array. So we have to
  18879. // show an error
  18880. if (!sources.length) {
  18881. this.setTimeout(function () {
  18882. this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
  18883. }, 0);
  18884. return;
  18885. }
  18886. // intial sources
  18887. this.changingSrc_ = true;
  18888. this.cache_.sources = sources;
  18889. this.updateSourceCaches_(sources[0]);
  18890. // middlewareSource is the source after it has been changed by middleware
  18891. setSource(this, sources[0], function (middlewareSource, mws) {
  18892. _this10.middleware_ = mws;
  18893. // since sourceSet is async we have to update the cache again after we select a source since
  18894. // the source that is selected could be out of order from the cache update above this callback.
  18895. _this10.cache_.sources = sources;
  18896. _this10.updateSourceCaches_(middlewareSource);
  18897. var err = _this10.src_(middlewareSource);
  18898. if (err) {
  18899. if (sources.length > 1) {
  18900. return _this10.src(sources.slice(1));
  18901. }
  18902. _this10.changingSrc_ = false;
  18903. // We need to wrap this in a timeout to give folks a chance to add error event handlers
  18904. _this10.setTimeout(function () {
  18905. this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
  18906. }, 0);
  18907. // we could not find an appropriate tech, but let's still notify the delegate that this is it
  18908. // this needs a better comment about why this is needed
  18909. _this10.triggerReady();
  18910. return;
  18911. }
  18912. setTech(mws, _this10.tech_);
  18913. });
  18914. };
  18915. /**
  18916. * Set the source object on the tech, returns a boolean that indicates wether
  18917. * there is a tech that can play the source or not
  18918. *
  18919. * @param {Tech~SourceObject} source
  18920. * The source object to set on the Tech
  18921. *
  18922. * @return {Boolean}
  18923. * - True if there is no Tech to playback this source
  18924. * - False otherwise
  18925. *
  18926. * @private
  18927. */
  18928. Player.prototype.src_ = function src_(source) {
  18929. var _this11 = this;
  18930. var sourceTech = this.selectSource([source]);
  18931. if (!sourceTech) {
  18932. return true;
  18933. }
  18934. if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
  18935. this.changingSrc_ = true;
  18936. // load this technology with the chosen source
  18937. this.loadTech_(sourceTech.tech, sourceTech.source);
  18938. this.tech_.ready(function () {
  18939. _this11.changingSrc_ = false;
  18940. });
  18941. return false;
  18942. }
  18943. // wait until the tech is ready to set the source
  18944. // and set it synchronously if possible (#2326)
  18945. this.ready(function () {
  18946. // The setSource tech method was added with source handlers
  18947. // so older techs won't support it
  18948. // We need to check the direct prototype for the case where subclasses
  18949. // of the tech do not support source handlers
  18950. if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
  18951. this.techCall_('setSource', source);
  18952. } else {
  18953. this.techCall_('src', source.src);
  18954. }
  18955. this.changingSrc_ = false;
  18956. }, true);
  18957. return false;
  18958. };
  18959. /**
  18960. * Begin loading the src data.
  18961. */
  18962. Player.prototype.load = function load() {
  18963. this.techCall_('load');
  18964. };
  18965. /**
  18966. * Reset the player. Loads the first tech in the techOrder,
  18967. * removes all the text tracks in the existing `tech`,
  18968. * and calls `reset` on the `tech`.
  18969. */
  18970. Player.prototype.reset = function reset() {
  18971. if (this.tech_) {
  18972. this.tech_.clearTracks('text');
  18973. }
  18974. this.loadTech_(this.options_.techOrder[0], null);
  18975. this.techCall_('reset');
  18976. };
  18977. /**
  18978. * Returns all of the current source objects.
  18979. *
  18980. * @return {Tech~SourceObject[]}
  18981. * The current source objects
  18982. */
  18983. Player.prototype.currentSources = function currentSources() {
  18984. var source = this.currentSource();
  18985. var sources = [];
  18986. // assume `{}` or `{ src }`
  18987. if (Object.keys(source).length !== 0) {
  18988. sources.push(source);
  18989. }
  18990. return this.cache_.sources || sources;
  18991. };
  18992. /**
  18993. * Returns the current source object.
  18994. *
  18995. * @return {Tech~SourceObject}
  18996. * The current source object
  18997. */
  18998. Player.prototype.currentSource = function currentSource() {
  18999. return this.cache_.source || {};
  19000. };
  19001. /**
  19002. * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
  19003. * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
  19004. *
  19005. * @return {string}
  19006. * The current source
  19007. */
  19008. Player.prototype.currentSrc = function currentSrc() {
  19009. return this.currentSource() && this.currentSource().src || '';
  19010. };
  19011. /**
  19012. * Get the current source type e.g. video/mp4
  19013. * This can allow you rebuild the current source object so that you could load the same
  19014. * source and tech later
  19015. *
  19016. * @return {string}
  19017. * The source MIME type
  19018. */
  19019. Player.prototype.currentType = function currentType() {
  19020. return this.currentSource() && this.currentSource().type || '';
  19021. };
  19022. /**
  19023. * Get or set the preload attribute
  19024. *
  19025. * @param {boolean} [value]
  19026. * - true means that we should preload
  19027. * - false maens that we should not preload
  19028. *
  19029. * @return {string}
  19030. * The preload attribute value when getting
  19031. */
  19032. Player.prototype.preload = function preload(value) {
  19033. if (value !== undefined) {
  19034. this.techCall_('setPreload', value);
  19035. this.options_.preload = value;
  19036. return;
  19037. }
  19038. return this.techGet_('preload');
  19039. };
  19040. /**
  19041. * Get or set the autoplay option. When this is a boolean it will
  19042. * modify the attribute on the tech. When this is a string the attribute on
  19043. * the tech will be removed and `Player` will handle autoplay on loadstarts.
  19044. *
  19045. * @param {boolean|string} [value]
  19046. * - true: autoplay using the browser behavior
  19047. * - false: do not autoplay
  19048. * - 'play': call play() on every loadstart
  19049. * - 'muted': call muted() then play() on every loadstart
  19050. * - 'any': call play() on every loadstart. if that fails call muted() then play().
  19051. * - *: values other than those listed here will be set `autoplay` to true
  19052. *
  19053. * @return {boolean|string}
  19054. * The current value of autoplay when getting
  19055. */
  19056. Player.prototype.autoplay = function autoplay(value) {
  19057. // getter usage
  19058. if (value === undefined) {
  19059. return this.options_.autoplay || false;
  19060. }
  19061. var techAutoplay = void 0;
  19062. // if the value is a valid string set it to that
  19063. if (typeof value === 'string' && /(any|play|muted)/.test(value)) {
  19064. this.options_.autoplay = value;
  19065. this.manualAutoplay_(value);
  19066. techAutoplay = false;
  19067. // any falsy value sets autoplay to false in the browser,
  19068. // lets do the same
  19069. } else if (!value) {
  19070. this.options_.autoplay = false;
  19071. // any other value (ie truthy) sets autoplay to true
  19072. } else {
  19073. this.options_.autoplay = true;
  19074. }
  19075. techAutoplay = techAutoplay || this.options_.autoplay;
  19076. // if we don't have a tech then we do not queue up
  19077. // a setAutoplay call on tech ready. We do this because the
  19078. // autoplay option will be passed in the constructor and we
  19079. // do not need to set it twice
  19080. if (this.tech_) {
  19081. this.techCall_('setAutoplay', techAutoplay);
  19082. }
  19083. };
  19084. /**
  19085. * Set or unset the playsinline attribute.
  19086. * Playsinline tells the browser that non-fullscreen playback is preferred.
  19087. *
  19088. * @param {boolean} [value]
  19089. * - true means that we should try to play inline by default
  19090. * - false means that we should use the browser's default playback mode,
  19091. * which in most cases is inline. iOS Safari is a notable exception
  19092. * and plays fullscreen by default.
  19093. *
  19094. * @return {string|Player}
  19095. * - the current value of playsinline
  19096. * - the player when setting
  19097. *
  19098. * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
  19099. */
  19100. Player.prototype.playsinline = function playsinline(value) {
  19101. if (value !== undefined) {
  19102. this.techCall_('setPlaysinline', value);
  19103. this.options_.playsinline = value;
  19104. return this;
  19105. }
  19106. return this.techGet_('playsinline');
  19107. };
  19108. /**
  19109. * Get or set the loop attribute on the video element.
  19110. *
  19111. * @param {boolean} [value]
  19112. * - true means that we should loop the video
  19113. * - false means that we should not loop the video
  19114. *
  19115. * @return {string}
  19116. * The current value of loop when getting
  19117. */
  19118. Player.prototype.loop = function loop(value) {
  19119. if (value !== undefined) {
  19120. this.techCall_('setLoop', value);
  19121. this.options_.loop = value;
  19122. return;
  19123. }
  19124. return this.techGet_('loop');
  19125. };
  19126. /**
  19127. * Get or set the poster image source url
  19128. *
  19129. * @fires Player#posterchange
  19130. *
  19131. * @param {string} [src]
  19132. * Poster image source URL
  19133. *
  19134. * @return {string}
  19135. * The current value of poster when getting
  19136. */
  19137. Player.prototype.poster = function poster(src) {
  19138. if (src === undefined) {
  19139. return this.poster_;
  19140. }
  19141. // The correct way to remove a poster is to set as an empty string
  19142. // other falsey values will throw errors
  19143. if (!src) {
  19144. src = '';
  19145. }
  19146. if (src === this.poster_) {
  19147. return;
  19148. }
  19149. // update the internal poster variable
  19150. this.poster_ = src;
  19151. // update the tech's poster
  19152. this.techCall_('setPoster', src);
  19153. this.isPosterFromTech_ = false;
  19154. // alert components that the poster has been set
  19155. /**
  19156. * This event fires when the poster image is changed on the player.
  19157. *
  19158. * @event Player#posterchange
  19159. * @type {EventTarget~Event}
  19160. */
  19161. this.trigger('posterchange');
  19162. };
  19163. /**
  19164. * Some techs (e.g. YouTube) can provide a poster source in an
  19165. * asynchronous way. We want the poster component to use this
  19166. * poster source so that it covers up the tech's controls.
  19167. * (YouTube's play button). However we only want to use this
  19168. * source if the player user hasn't set a poster through
  19169. * the normal APIs.
  19170. *
  19171. * @fires Player#posterchange
  19172. * @listens Tech#posterchange
  19173. * @private
  19174. */
  19175. Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
  19176. if ((!this.poster_ || this.options_.techCanOverridePoster) && this.tech_ && this.tech_.poster) {
  19177. var newPoster = this.tech_.poster() || '';
  19178. if (newPoster !== this.poster_) {
  19179. this.poster_ = newPoster;
  19180. this.isPosterFromTech_ = true;
  19181. // Let components know the poster has changed
  19182. this.trigger('posterchange');
  19183. }
  19184. }
  19185. };
  19186. /**
  19187. * Get or set whether or not the controls are showing.
  19188. *
  19189. * @fires Player#controlsenabled
  19190. *
  19191. * @param {boolean} [bool]
  19192. * - true to turn controls on
  19193. * - false to turn controls off
  19194. *
  19195. * @return {boolean}
  19196. * The current value of controls when getting
  19197. */
  19198. Player.prototype.controls = function controls(bool) {
  19199. if (bool === undefined) {
  19200. return !!this.controls_;
  19201. }
  19202. bool = !!bool;
  19203. // Don't trigger a change event unless it actually changed
  19204. if (this.controls_ === bool) {
  19205. return;
  19206. }
  19207. this.controls_ = bool;
  19208. if (this.usingNativeControls()) {
  19209. this.techCall_('setControls', bool);
  19210. }
  19211. if (this.controls_) {
  19212. this.removeClass('vjs-controls-disabled');
  19213. this.addClass('vjs-controls-enabled');
  19214. /**
  19215. * @event Player#controlsenabled
  19216. * @type {EventTarget~Event}
  19217. */
  19218. this.trigger('controlsenabled');
  19219. if (!this.usingNativeControls()) {
  19220. this.addTechControlsListeners_();
  19221. }
  19222. } else {
  19223. this.removeClass('vjs-controls-enabled');
  19224. this.addClass('vjs-controls-disabled');
  19225. /**
  19226. * @event Player#controlsdisabled
  19227. * @type {EventTarget~Event}
  19228. */
  19229. this.trigger('controlsdisabled');
  19230. if (!this.usingNativeControls()) {
  19231. this.removeTechControlsListeners_();
  19232. }
  19233. }
  19234. };
  19235. /**
  19236. * Toggle native controls on/off. Native controls are the controls built into
  19237. * devices (e.g. default iPhone controls), Flash, or other techs
  19238. * (e.g. Vimeo Controls)
  19239. * **This should only be set by the current tech, because only the tech knows
  19240. * if it can support native controls**
  19241. *
  19242. * @fires Player#usingnativecontrols
  19243. * @fires Player#usingcustomcontrols
  19244. *
  19245. * @param {boolean} [bool]
  19246. * - true to turn native controls on
  19247. * - false to turn native controls off
  19248. *
  19249. * @return {boolean}
  19250. * The current value of native controls when getting
  19251. */
  19252. Player.prototype.usingNativeControls = function usingNativeControls(bool) {
  19253. if (bool === undefined) {
  19254. return !!this.usingNativeControls_;
  19255. }
  19256. bool = !!bool;
  19257. // Don't trigger a change event unless it actually changed
  19258. if (this.usingNativeControls_ === bool) {
  19259. return;
  19260. }
  19261. this.usingNativeControls_ = bool;
  19262. if (this.usingNativeControls_) {
  19263. this.addClass('vjs-using-native-controls');
  19264. /**
  19265. * player is using the native device controls
  19266. *
  19267. * @event Player#usingnativecontrols
  19268. * @type {EventTarget~Event}
  19269. */
  19270. this.trigger('usingnativecontrols');
  19271. } else {
  19272. this.removeClass('vjs-using-native-controls');
  19273. /**
  19274. * player is using the custom HTML controls
  19275. *
  19276. * @event Player#usingcustomcontrols
  19277. * @type {EventTarget~Event}
  19278. */
  19279. this.trigger('usingcustomcontrols');
  19280. }
  19281. };
  19282. /**
  19283. * Set or get the current MediaError
  19284. *
  19285. * @fires Player#error
  19286. *
  19287. * @param {MediaError|string|number} [err]
  19288. * A MediaError or a string/number to be turned
  19289. * into a MediaError
  19290. *
  19291. * @return {MediaError|null}
  19292. * The current MediaError when getting (or null)
  19293. */
  19294. Player.prototype.error = function error(err) {
  19295. if (err === undefined) {
  19296. return this.error_ || null;
  19297. }
  19298. // restoring to default
  19299. if (err === null) {
  19300. this.error_ = err;
  19301. this.removeClass('vjs-error');
  19302. if (this.errorDisplay) {
  19303. this.errorDisplay.close();
  19304. }
  19305. return;
  19306. }
  19307. this.error_ = new MediaError(err);
  19308. // add the vjs-error classname to the player
  19309. this.addClass('vjs-error');
  19310. // log the name of the error type and any message
  19311. // ie8 just logs "[object object]" if you just log the error object
  19312. log.error('(CODE:' + this.error_.code + ' ' + MediaError.errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
  19313. /**
  19314. * @event Player#error
  19315. * @type {EventTarget~Event}
  19316. */
  19317. this.trigger('error');
  19318. return;
  19319. };
  19320. /**
  19321. * Report user activity
  19322. *
  19323. * @param {Object} event
  19324. * Event object
  19325. */
  19326. Player.prototype.reportUserActivity = function reportUserActivity(event) {
  19327. this.userActivity_ = true;
  19328. };
  19329. /**
  19330. * Get/set if user is active
  19331. *
  19332. * @fires Player#useractive
  19333. * @fires Player#userinactive
  19334. *
  19335. * @param {boolean} [bool]
  19336. * - true if the user is active
  19337. * - false if the user is inactive
  19338. *
  19339. * @return {boolean}
  19340. * The current value of userActive when getting
  19341. */
  19342. Player.prototype.userActive = function userActive(bool) {
  19343. if (bool === undefined) {
  19344. return this.userActive_;
  19345. }
  19346. bool = !!bool;
  19347. if (bool === this.userActive_) {
  19348. return;
  19349. }
  19350. this.userActive_ = bool;
  19351. if (this.userActive_) {
  19352. this.userActivity_ = true;
  19353. this.removeClass('vjs-user-inactive');
  19354. this.addClass('vjs-user-active');
  19355. /**
  19356. * @event Player#useractive
  19357. * @type {EventTarget~Event}
  19358. */
  19359. this.trigger('useractive');
  19360. return;
  19361. }
  19362. // Chrome/Safari/IE have bugs where when you change the cursor it can
  19363. // trigger a mousemove event. This causes an issue when you're hiding
  19364. // the cursor when the user is inactive, and a mousemove signals user
  19365. // activity. Making it impossible to go into inactive mode. Specifically
  19366. // this happens in fullscreen when we really need to hide the cursor.
  19367. //
  19368. // When this gets resolved in ALL browsers it can be removed
  19369. // https://code.google.com/p/chromium/issues/detail?id=103041
  19370. if (this.tech_) {
  19371. this.tech_.one('mousemove', function (e) {
  19372. e.stopPropagation();
  19373. e.preventDefault();
  19374. });
  19375. }
  19376. this.userActivity_ = false;
  19377. this.removeClass('vjs-user-active');
  19378. this.addClass('vjs-user-inactive');
  19379. /**
  19380. * @event Player#userinactive
  19381. * @type {EventTarget~Event}
  19382. */
  19383. this.trigger('userinactive');
  19384. };
  19385. /**
  19386. * Listen for user activity based on timeout value
  19387. *
  19388. * @private
  19389. */
  19390. Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
  19391. var mouseInProgress = void 0;
  19392. var lastMoveX = void 0;
  19393. var lastMoveY = void 0;
  19394. var handleActivity = bind(this, this.reportUserActivity);
  19395. var handleMouseMove = function handleMouseMove(e) {
  19396. // #1068 - Prevent mousemove spamming
  19397. // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
  19398. if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
  19399. lastMoveX = e.screenX;
  19400. lastMoveY = e.screenY;
  19401. handleActivity();
  19402. }
  19403. };
  19404. var handleMouseDown = function handleMouseDown() {
  19405. handleActivity();
  19406. // For as long as the they are touching the device or have their mouse down,
  19407. // we consider them active even if they're not moving their finger or mouse.
  19408. // So we want to continue to update that they are active
  19409. this.clearInterval(mouseInProgress);
  19410. // Setting userActivity=true now and setting the interval to the same time
  19411. // as the activityCheck interval (250) should ensure we never miss the
  19412. // next activityCheck
  19413. mouseInProgress = this.setInterval(handleActivity, 250);
  19414. };
  19415. var handleMouseUp = function handleMouseUp(event) {
  19416. handleActivity();
  19417. // Stop the interval that maintains activity if the mouse/touch is down
  19418. this.clearInterval(mouseInProgress);
  19419. };
  19420. // Any mouse movement will be considered user activity
  19421. this.on('mousedown', handleMouseDown);
  19422. this.on('mousemove', handleMouseMove);
  19423. this.on('mouseup', handleMouseUp);
  19424. // Listen for keyboard navigation
  19425. // Shouldn't need to use inProgress interval because of key repeat
  19426. this.on('keydown', handleActivity);
  19427. this.on('keyup', handleActivity);
  19428. // Run an interval every 250 milliseconds instead of stuffing everything into
  19429. // the mousemove/touchmove function itself, to prevent performance degradation.
  19430. // `this.reportUserActivity` simply sets this.userActivity_ to true, which
  19431. // then gets picked up by this loop
  19432. // http://ejohn.org/blog/learning-from-twitter/
  19433. var inactivityTimeout = void 0;
  19434. this.setInterval(function () {
  19435. // Check to see if mouse/touch activity has happened
  19436. if (!this.userActivity_) {
  19437. return;
  19438. }
  19439. // Reset the activity tracker
  19440. this.userActivity_ = false;
  19441. // If the user state was inactive, set the state to active
  19442. this.userActive(true);
  19443. // Clear any existing inactivity timeout to start the timer over
  19444. this.clearTimeout(inactivityTimeout);
  19445. var timeout = this.options_.inactivityTimeout;
  19446. if (timeout <= 0) {
  19447. return;
  19448. }
  19449. // In <timeout> milliseconds, if no more activity has occurred the
  19450. // user will be considered inactive
  19451. inactivityTimeout = this.setTimeout(function () {
  19452. // Protect against the case where the inactivityTimeout can trigger just
  19453. // before the next user activity is picked up by the activity check loop
  19454. // causing a flicker
  19455. if (!this.userActivity_) {
  19456. this.userActive(false);
  19457. }
  19458. }, timeout);
  19459. }, 250);
  19460. };
  19461. /**
  19462. * Gets or sets the current playback rate. A playback rate of
  19463. * 1.0 represents normal speed and 0.5 would indicate half-speed
  19464. * playback, for instance.
  19465. *
  19466. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
  19467. *
  19468. * @param {number} [rate]
  19469. * New playback rate to set.
  19470. *
  19471. * @return {number}
  19472. * The current playback rate when getting or 1.0
  19473. */
  19474. Player.prototype.playbackRate = function playbackRate(rate) {
  19475. if (rate !== undefined) {
  19476. // NOTE: this.cache_.lastPlaybackRate is set from the tech handler
  19477. // that is registered above
  19478. this.techCall_('setPlaybackRate', rate);
  19479. return;
  19480. }
  19481. if (this.tech_ && this.tech_.featuresPlaybackRate) {
  19482. return this.cache_.lastPlaybackRate || this.techGet_('playbackRate');
  19483. }
  19484. return 1.0;
  19485. };
  19486. /**
  19487. * Gets or sets the current default playback rate. A default playback rate of
  19488. * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
  19489. * defaultPlaybackRate will only represent what the intial playbackRate of a video was, not
  19490. * not the current playbackRate.
  19491. *
  19492. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
  19493. *
  19494. * @param {number} [rate]
  19495. * New default playback rate to set.
  19496. *
  19497. * @return {number|Player}
  19498. * - The default playback rate when getting or 1.0
  19499. * - the player when setting
  19500. */
  19501. Player.prototype.defaultPlaybackRate = function defaultPlaybackRate(rate) {
  19502. if (rate !== undefined) {
  19503. return this.techCall_('setDefaultPlaybackRate', rate);
  19504. }
  19505. if (this.tech_ && this.tech_.featuresPlaybackRate) {
  19506. return this.techGet_('defaultPlaybackRate');
  19507. }
  19508. return 1.0;
  19509. };
  19510. /**
  19511. * Gets or sets the audio flag
  19512. *
  19513. * @param {boolean} bool
  19514. * - true signals that this is an audio player
  19515. * - false signals that this is not an audio player
  19516. *
  19517. * @return {boolean}
  19518. * The current value of isAudio when getting
  19519. */
  19520. Player.prototype.isAudio = function isAudio(bool) {
  19521. if (bool !== undefined) {
  19522. this.isAudio_ = !!bool;
  19523. return;
  19524. }
  19525. return !!this.isAudio_;
  19526. };
  19527. /**
  19528. * A helper method for adding a {@link TextTrack} to our
  19529. * {@link TextTrackList}.
  19530. *
  19531. * In addition to the W3C settings we allow adding additional info through options.
  19532. *
  19533. * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
  19534. *
  19535. * @param {string} [kind]
  19536. * the kind of TextTrack you are adding
  19537. *
  19538. * @param {string} [label]
  19539. * the label to give the TextTrack label
  19540. *
  19541. * @param {string} [language]
  19542. * the language to set on the TextTrack
  19543. *
  19544. * @return {TextTrack|undefined}
  19545. * the TextTrack that was added or undefined
  19546. * if there is no tech
  19547. */
  19548. Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
  19549. if (this.tech_) {
  19550. return this.tech_.addTextTrack(kind, label, language);
  19551. }
  19552. };
  19553. /**
  19554. * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
  19555. * automatically removed from the video element whenever the source changes, unless
  19556. * manualCleanup is set to false.
  19557. *
  19558. * @param {Object} options
  19559. * Options to pass to {@link HTMLTrackElement} during creation. See
  19560. * {@link HTMLTrackElement} for object properties that you should use.
  19561. *
  19562. * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
  19563. *
  19564. * @return {HtmlTrackElement}
  19565. * the HTMLTrackElement that was created and added
  19566. * to the HtmlTrackElementList and the remote
  19567. * TextTrackList
  19568. *
  19569. * @deprecated The default value of the "manualCleanup" parameter will default
  19570. * to "false" in upcoming versions of Video.js
  19571. */
  19572. Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
  19573. if (this.tech_) {
  19574. return this.tech_.addRemoteTextTrack(options, manualCleanup);
  19575. }
  19576. };
  19577. /**
  19578. * Remove a remote {@link TextTrack} from the respective
  19579. * {@link TextTrackList} and {@link HtmlTrackElementList}.
  19580. *
  19581. * @param {Object} track
  19582. * Remote {@link TextTrack} to remove
  19583. *
  19584. * @return {undefined}
  19585. * does not return anything
  19586. */
  19587. Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
  19588. var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
  19589. _ref3$track = _ref3.track,
  19590. track = _ref3$track === undefined ? arguments[0] : _ref3$track;
  19591. // destructure the input into an object with a track argument, defaulting to arguments[0]
  19592. // default the whole argument to an empty object if nothing was passed in
  19593. if (this.tech_) {
  19594. return this.tech_.removeRemoteTextTrack(track);
  19595. }
  19596. };
  19597. /**
  19598. * Gets available media playback quality metrics as specified by the W3C's Media
  19599. * Playback Quality API.
  19600. *
  19601. * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
  19602. *
  19603. * @return {Object|undefined}
  19604. * An object with supported media playback quality metrics or undefined if there
  19605. * is no tech or the tech does not support it.
  19606. */
  19607. Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
  19608. return this.techGet_('getVideoPlaybackQuality');
  19609. };
  19610. /**
  19611. * Get video width
  19612. *
  19613. * @return {number}
  19614. * current video width
  19615. */
  19616. Player.prototype.videoWidth = function videoWidth() {
  19617. return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
  19618. };
  19619. /**
  19620. * Get video height
  19621. *
  19622. * @return {number}
  19623. * current video height
  19624. */
  19625. Player.prototype.videoHeight = function videoHeight() {
  19626. return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
  19627. };
  19628. /**
  19629. * The player's language code
  19630. * NOTE: The language should be set in the player options if you want the
  19631. * the controls to be built with a specific language. Changing the lanugage
  19632. * later will not update controls text.
  19633. *
  19634. * @param {string} [code]
  19635. * the language code to set the player to
  19636. *
  19637. * @return {string}
  19638. * The current language code when getting
  19639. */
  19640. Player.prototype.language = function language(code) {
  19641. if (code === undefined) {
  19642. return this.language_;
  19643. }
  19644. this.language_ = String(code).toLowerCase();
  19645. };
  19646. /**
  19647. * Get the player's language dictionary
  19648. * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
  19649. * Languages specified directly in the player options have precedence
  19650. *
  19651. * @return {Array}
  19652. * An array of of supported languages
  19653. */
  19654. Player.prototype.languages = function languages() {
  19655. return mergeOptions(Player.prototype.options_.languages, this.languages_);
  19656. };
  19657. /**
  19658. * returns a JavaScript object reperesenting the current track
  19659. * information. **DOES not return it as JSON**
  19660. *
  19661. * @return {Object}
  19662. * Object representing the current of track info
  19663. */
  19664. Player.prototype.toJSON = function toJSON() {
  19665. var options = mergeOptions(this.options_);
  19666. var tracks = options.tracks;
  19667. options.tracks = [];
  19668. for (var i = 0; i < tracks.length; i++) {
  19669. var track = tracks[i];
  19670. // deep merge tracks and null out player so no circular references
  19671. track = mergeOptions(track);
  19672. track.player = undefined;
  19673. options.tracks[i] = track;
  19674. }
  19675. return options;
  19676. };
  19677. /**
  19678. * Creates a simple modal dialog (an instance of the {@link ModalDialog}
  19679. * component) that immediately overlays the player with arbitrary
  19680. * content and removes itself when closed.
  19681. *
  19682. * @param {string|Function|Element|Array|null} content
  19683. * Same as {@link ModalDialog#content}'s param of the same name.
  19684. * The most straight-forward usage is to provide a string or DOM
  19685. * element.
  19686. *
  19687. * @param {Object} [options]
  19688. * Extra options which will be passed on to the {@link ModalDialog}.
  19689. *
  19690. * @return {ModalDialog}
  19691. * the {@link ModalDialog} that was created
  19692. */
  19693. Player.prototype.createModal = function createModal(content, options) {
  19694. var _this12 = this;
  19695. options = options || {};
  19696. options.content = content || '';
  19697. var modal = new ModalDialog(this, options);
  19698. this.addChild(modal);
  19699. modal.on('dispose', function () {
  19700. _this12.removeChild(modal);
  19701. });
  19702. modal.open();
  19703. return modal;
  19704. };
  19705. /**
  19706. * Change breakpoint classes when the player resizes.
  19707. *
  19708. * @private
  19709. */
  19710. Player.prototype.updateCurrentBreakpoint_ = function updateCurrentBreakpoint_() {
  19711. if (!this.responsive()) {
  19712. return;
  19713. }
  19714. var currentBreakpoint = this.currentBreakpoint();
  19715. var currentWidth = this.currentWidth();
  19716. for (var i = 0; i < BREAKPOINT_ORDER.length; i++) {
  19717. var candidateBreakpoint = BREAKPOINT_ORDER[i];
  19718. var maxWidth = this.breakpoints_[candidateBreakpoint];
  19719. if (currentWidth <= maxWidth) {
  19720. // The current breakpoint did not change, nothing to do.
  19721. if (currentBreakpoint === candidateBreakpoint) {
  19722. return;
  19723. }
  19724. // Only remove a class if there is a current breakpoint.
  19725. if (currentBreakpoint) {
  19726. this.removeClass(BREAKPOINT_CLASSES[currentBreakpoint]);
  19727. }
  19728. this.addClass(BREAKPOINT_CLASSES[candidateBreakpoint]);
  19729. this.breakpoint_ = candidateBreakpoint;
  19730. break;
  19731. }
  19732. }
  19733. };
  19734. /**
  19735. * Removes the current breakpoint.
  19736. *
  19737. * @private
  19738. */
  19739. Player.prototype.removeCurrentBreakpoint_ = function removeCurrentBreakpoint_() {
  19740. var className = this.currentBreakpointClass();
  19741. this.breakpoint_ = '';
  19742. if (className) {
  19743. this.removeClass(className);
  19744. }
  19745. };
  19746. /**
  19747. * Get or set breakpoints on the player.
  19748. *
  19749. * Calling this method with an object or `true` will remove any previous
  19750. * custom breakpoints and start from the defaults again.
  19751. *
  19752. * @param {Object|boolean} [breakpoints]
  19753. * If an object is given, it can be used to provide custom
  19754. * breakpoints. If `true` is given, will set default breakpoints.
  19755. * If this argument is not given, will simply return the current
  19756. * breakpoints.
  19757. *
  19758. * @param {number} [breakpoints.tiny]
  19759. * The maximum width for the "vjs-layout-tiny" class.
  19760. *
  19761. * @param {number} [breakpoints.xsmall]
  19762. * The maximum width for the "vjs-layout-x-small" class.
  19763. *
  19764. * @param {number} [breakpoints.small]
  19765. * The maximum width for the "vjs-layout-small" class.
  19766. *
  19767. * @param {number} [breakpoints.medium]
  19768. * The maximum width for the "vjs-layout-medium" class.
  19769. *
  19770. * @param {number} [breakpoints.large]
  19771. * The maximum width for the "vjs-layout-large" class.
  19772. *
  19773. * @param {number} [breakpoints.xlarge]
  19774. * The maximum width for the "vjs-layout-x-large" class.
  19775. *
  19776. * @param {number} [breakpoints.huge]
  19777. * The maximum width for the "vjs-layout-huge" class.
  19778. *
  19779. * @return {Object}
  19780. * An object mapping breakpoint names to maximum width values.
  19781. */
  19782. Player.prototype.breakpoints = function breakpoints(_breakpoints) {
  19783. // Used as a getter.
  19784. if (_breakpoints === undefined) {
  19785. return assign(this.breakpoints_);
  19786. }
  19787. this.breakpoint_ = '';
  19788. this.breakpoints_ = assign({}, DEFAULT_BREAKPOINTS, _breakpoints);
  19789. // When breakpoint definitions change, we need to update the currently
  19790. // selected breakpoint.
  19791. this.updateCurrentBreakpoint_();
  19792. // Clone the breakpoints before returning.
  19793. return assign(this.breakpoints_);
  19794. };
  19795. /**
  19796. * Get or set a flag indicating whether or not this player should adjust
  19797. * its UI based on its dimensions.
  19798. *
  19799. * @param {boolean} value
  19800. * Should be `true` if the player should adjust its UI based on its
  19801. * dimensions; otherwise, should be `false`.
  19802. *
  19803. * @return {boolean}
  19804. * Will be `true` if this player should adjust its UI based on its
  19805. * dimensions; otherwise, will be `false`.
  19806. */
  19807. Player.prototype.responsive = function responsive(value) {
  19808. // Used as a getter.
  19809. if (value === undefined) {
  19810. return this.responsive_;
  19811. }
  19812. value = Boolean(value);
  19813. var current = this.responsive_;
  19814. // Nothing changed.
  19815. if (value === current) {
  19816. return;
  19817. }
  19818. // The value actually changed, set it.
  19819. this.responsive_ = value;
  19820. // Start listening for breakpoints and set the initial breakpoint if the
  19821. // player is now responsive.
  19822. if (value) {
  19823. this.on('playerresize', this.updateCurrentBreakpoint_);
  19824. this.updateCurrentBreakpoint_();
  19825. // Stop listening for breakpoints if the player is no longer responsive.
  19826. } else {
  19827. this.off('playerresize', this.updateCurrentBreakpoint_);
  19828. this.removeCurrentBreakpoint_();
  19829. }
  19830. return value;
  19831. };
  19832. /**
  19833. * Get current breakpoint name, if any.
  19834. *
  19835. * @return {string}
  19836. * If there is currently a breakpoint set, returns a the key from the
  19837. * breakpoints object matching it. Otherwise, returns an empty string.
  19838. */
  19839. Player.prototype.currentBreakpoint = function currentBreakpoint() {
  19840. return this.breakpoint_;
  19841. };
  19842. /**
  19843. * Get the current breakpoint class name.
  19844. *
  19845. * @return {string}
  19846. * The matching class name (e.g. `"vjs-layout-tiny"` or
  19847. * `"vjs-layout-large"`) for the current breakpoint. Empty string if
  19848. * there is no current breakpoint.
  19849. */
  19850. Player.prototype.currentBreakpointClass = function currentBreakpointClass() {
  19851. return BREAKPOINT_CLASSES[this.breakpoint_] || '';
  19852. };
  19853. /**
  19854. * Gets tag settings
  19855. *
  19856. * @param {Element} tag
  19857. * The player tag
  19858. *
  19859. * @return {Object}
  19860. * An object containing all of the settings
  19861. * for a player tag
  19862. */
  19863. Player.getTagSettings = function getTagSettings(tag) {
  19864. var baseOptions = {
  19865. sources: [],
  19866. tracks: []
  19867. };
  19868. var tagOptions = getAttributes(tag);
  19869. var dataSetup = tagOptions['data-setup'];
  19870. if (hasClass(tag, 'vjs-fill')) {
  19871. tagOptions.fill = true;
  19872. }
  19873. if (hasClass(tag, 'vjs-fluid')) {
  19874. tagOptions.fluid = true;
  19875. }
  19876. // Check if data-setup attr exists.
  19877. if (dataSetup !== null) {
  19878. // Parse options JSON
  19879. // If empty string, make it a parsable json object.
  19880. var _safeParseTuple = tuple(dataSetup || '{}'),
  19881. err = _safeParseTuple[0],
  19882. data = _safeParseTuple[1];
  19883. if (err) {
  19884. log.error(err);
  19885. }
  19886. assign(tagOptions, data);
  19887. }
  19888. assign(baseOptions, tagOptions);
  19889. // Get tag children settings
  19890. if (tag.hasChildNodes()) {
  19891. var children = tag.childNodes;
  19892. for (var i = 0, j = children.length; i < j; i++) {
  19893. var child = children[i];
  19894. // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
  19895. var childName = child.nodeName.toLowerCase();
  19896. if (childName === 'source') {
  19897. baseOptions.sources.push(getAttributes(child));
  19898. } else if (childName === 'track') {
  19899. baseOptions.tracks.push(getAttributes(child));
  19900. }
  19901. }
  19902. }
  19903. return baseOptions;
  19904. };
  19905. /**
  19906. * Determine wether or not flexbox is supported
  19907. *
  19908. * @return {boolean}
  19909. * - true if flexbox is supported
  19910. * - false if flexbox is not supported
  19911. */
  19912. Player.prototype.flexNotSupported_ = function flexNotSupported_() {
  19913. var elem = document_1.createElement('i');
  19914. // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
  19915. // common flex features that we can rely on when checking for flex support.
  19916. return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
  19917. // IE10-specific (2012 flex spec)
  19918. 'msFlexOrder' in elem.style);
  19919. };
  19920. return Player;
  19921. }(Component);
  19922. /**
  19923. * Get the {@link VideoTrackList}
  19924. * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
  19925. *
  19926. * @return {VideoTrackList}
  19927. * the current video track list
  19928. *
  19929. * @method Player.prototype.videoTracks
  19930. */
  19931. /**
  19932. * Get the {@link AudioTrackList}
  19933. * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
  19934. *
  19935. * @return {AudioTrackList}
  19936. * the current audio track list
  19937. *
  19938. * @method Player.prototype.audioTracks
  19939. */
  19940. /**
  19941. * Get the {@link TextTrackList}
  19942. *
  19943. * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
  19944. *
  19945. * @return {TextTrackList}
  19946. * the current text track list
  19947. *
  19948. * @method Player.prototype.textTracks
  19949. */
  19950. /**
  19951. * Get the remote {@link TextTrackList}
  19952. *
  19953. * @return {TextTrackList}
  19954. * The current remote text track list
  19955. *
  19956. * @method Player.prototype.remoteTextTracks
  19957. */
  19958. /**
  19959. * Get the remote {@link HtmlTrackElementList} tracks.
  19960. *
  19961. * @return {HtmlTrackElementList}
  19962. * The current remote text track element list
  19963. *
  19964. * @method Player.prototype.remoteTextTrackEls
  19965. */
  19966. ALL.names.forEach(function (name$$1) {
  19967. var props = ALL[name$$1];
  19968. Player.prototype[props.getterName] = function () {
  19969. if (this.tech_) {
  19970. return this.tech_[props.getterName]();
  19971. }
  19972. // if we have not yet loadTech_, we create {video,audio,text}Tracks_
  19973. // these will be passed to the tech during loading
  19974. this[props.privateName] = this[props.privateName] || new props.ListClass();
  19975. return this[props.privateName];
  19976. };
  19977. });
  19978. /**
  19979. * Global player list
  19980. *
  19981. * @type {Object}
  19982. */
  19983. Player.players = {};
  19984. var navigator = window_1.navigator;
  19985. /*
  19986. * Player instance options, surfaced using options
  19987. * options = Player.prototype.options_
  19988. * Make changes in options, not here.
  19989. *
  19990. * @type {Object}
  19991. * @private
  19992. */
  19993. Player.prototype.options_ = {
  19994. // Default order of fallback technology
  19995. techOrder: Tech.defaultTechOrder_,
  19996. html5: {},
  19997. flash: {},
  19998. // default inactivity timeout
  19999. inactivityTimeout: 2000,
  20000. // default playback rates
  20001. playbackRates: [],
  20002. // Add playback rate selection by adding rates
  20003. // 'playbackRates': [0.5, 1, 1.5, 2],
  20004. // Included control sets
  20005. children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
  20006. language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
  20007. // locales and their language translations
  20008. languages: {},
  20009. // Default message to show when a video cannot be played.
  20010. notSupportedMessage: 'No compatible source was found for this media.',
  20011. breakpoints: {},
  20012. responsive: false
  20013. };
  20014. if (!IS_IE8) {
  20015. Player.prototype.options_.children.push('resizeManager');
  20016. }
  20017. [
  20018. /**
  20019. * Returns whether or not the player is in the "ended" state.
  20020. *
  20021. * @return {Boolean} True if the player is in the ended state, false if not.
  20022. * @method Player#ended
  20023. */
  20024. 'ended',
  20025. /**
  20026. * Returns whether or not the player is in the "seeking" state.
  20027. *
  20028. * @return {Boolean} True if the player is in the seeking state, false if not.
  20029. * @method Player#seeking
  20030. */
  20031. 'seeking',
  20032. /**
  20033. * Returns the TimeRanges of the media that are currently available
  20034. * for seeking to.
  20035. *
  20036. * @return {TimeRanges} the seekable intervals of the media timeline
  20037. * @method Player#seekable
  20038. */
  20039. 'seekable',
  20040. /**
  20041. * Returns the current state of network activity for the element, from
  20042. * the codes in the list below.
  20043. * - NETWORK_EMPTY (numeric value 0)
  20044. * The element has not yet been initialised. All attributes are in
  20045. * their initial states.
  20046. * - NETWORK_IDLE (numeric value 1)
  20047. * The element's resource selection algorithm is active and has
  20048. * selected a resource, but it is not actually using the network at
  20049. * this time.
  20050. * - NETWORK_LOADING (numeric value 2)
  20051. * The user agent is actively trying to download data.
  20052. * - NETWORK_NO_SOURCE (numeric value 3)
  20053. * The element's resource selection algorithm is active, but it has
  20054. * not yet found a resource to use.
  20055. *
  20056. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
  20057. * @return {number} the current network activity state
  20058. * @method Player#networkState
  20059. */
  20060. 'networkState',
  20061. /**
  20062. * Returns a value that expresses the current state of the element
  20063. * with respect to rendering the current playback position, from the
  20064. * codes in the list below.
  20065. * - HAVE_NOTHING (numeric value 0)
  20066. * No information regarding the media resource is available.
  20067. * - HAVE_METADATA (numeric value 1)
  20068. * Enough of the resource has been obtained that the duration of the
  20069. * resource is available.
  20070. * - HAVE_CURRENT_DATA (numeric value 2)
  20071. * Data for the immediate current playback position is available.
  20072. * - HAVE_FUTURE_DATA (numeric value 3)
  20073. * Data for the immediate current playback position is available, as
  20074. * well as enough data for the user agent to advance the current
  20075. * playback position in the direction of playback.
  20076. * - HAVE_ENOUGH_DATA (numeric value 4)
  20077. * The user agent estimates that enough data is available for
  20078. * playback to proceed uninterrupted.
  20079. *
  20080. * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
  20081. * @return {number} the current playback rendering state
  20082. * @method Player#readyState
  20083. */
  20084. 'readyState'].forEach(function (fn) {
  20085. Player.prototype[fn] = function () {
  20086. return this.techGet_(fn);
  20087. };
  20088. });
  20089. TECH_EVENTS_RETRIGGER.forEach(function (event) {
  20090. Player.prototype['handleTech' + toTitleCase(event) + '_'] = function () {
  20091. return this.trigger(event);
  20092. };
  20093. });
  20094. /**
  20095. * Fired when the player has initial duration and dimension information
  20096. *
  20097. * @event Player#loadedmetadata
  20098. * @type {EventTarget~Event}
  20099. */
  20100. /**
  20101. * Fired when the player has downloaded data at the current playback position
  20102. *
  20103. * @event Player#loadeddata
  20104. * @type {EventTarget~Event}
  20105. */
  20106. /**
  20107. * Fired when the current playback position has changed *
  20108. * During playback this is fired every 15-250 milliseconds, depending on the
  20109. * playback technology in use.
  20110. *
  20111. * @event Player#timeupdate
  20112. * @type {EventTarget~Event}
  20113. */
  20114. /**
  20115. * Fired when the volume changes
  20116. *
  20117. * @event Player#volumechange
  20118. * @type {EventTarget~Event}
  20119. */
  20120. /**
  20121. * Reports whether or not a player has a plugin available.
  20122. *
  20123. * This does not report whether or not the plugin has ever been initialized
  20124. * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
  20125. *
  20126. * @method Player#hasPlugin
  20127. * @param {string} name
  20128. * The name of a plugin.
  20129. *
  20130. * @return {boolean}
  20131. * Whether or not this player has the requested plugin available.
  20132. */
  20133. /**
  20134. * Reports whether or not a player is using a plugin by name.
  20135. *
  20136. * For basic plugins, this only reports whether the plugin has _ever_ been
  20137. * initialized on this player.
  20138. *
  20139. * @method Player#usingPlugin
  20140. * @param {string} name
  20141. * The name of a plugin.
  20142. *
  20143. * @return {boolean}
  20144. * Whether or not this player is using the requested plugin.
  20145. */
  20146. Component.registerComponent('Player', Player);
  20147. /**
  20148. * @file plugin.js
  20149. */
  20150. /**
  20151. * The base plugin name.
  20152. *
  20153. * @private
  20154. * @constant
  20155. * @type {string}
  20156. */
  20157. var BASE_PLUGIN_NAME = 'plugin';
  20158. /**
  20159. * The key on which a player's active plugins cache is stored.
  20160. *
  20161. * @private
  20162. * @constant
  20163. * @type {string}
  20164. */
  20165. var PLUGIN_CACHE_KEY = 'activePlugins_';
  20166. /**
  20167. * Stores registered plugins in a private space.
  20168. *
  20169. * @private
  20170. * @type {Object}
  20171. */
  20172. var pluginStorage = {};
  20173. /**
  20174. * Reports whether or not a plugin has been registered.
  20175. *
  20176. * @private
  20177. * @param {string} name
  20178. * The name of a plugin.
  20179. *
  20180. * @returns {boolean}
  20181. * Whether or not the plugin has been registered.
  20182. */
  20183. var pluginExists = function pluginExists(name) {
  20184. return pluginStorage.hasOwnProperty(name);
  20185. };
  20186. /**
  20187. * Get a single registered plugin by name.
  20188. *
  20189. * @private
  20190. * @param {string} name
  20191. * The name of a plugin.
  20192. *
  20193. * @returns {Function|undefined}
  20194. * The plugin (or undefined).
  20195. */
  20196. var getPlugin = function getPlugin(name) {
  20197. return pluginExists(name) ? pluginStorage[name] : undefined;
  20198. };
  20199. /**
  20200. * Marks a plugin as "active" on a player.
  20201. *
  20202. * Also, ensures that the player has an object for tracking active plugins.
  20203. *
  20204. * @private
  20205. * @param {Player} player
  20206. * A Video.js player instance.
  20207. *
  20208. * @param {string} name
  20209. * The name of a plugin.
  20210. */
  20211. var markPluginAsActive = function markPluginAsActive(player, name) {
  20212. player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
  20213. player[PLUGIN_CACHE_KEY][name] = true;
  20214. };
  20215. /**
  20216. * Triggers a pair of plugin setup events.
  20217. *
  20218. * @private
  20219. * @param {Player} player
  20220. * A Video.js player instance.
  20221. *
  20222. * @param {Plugin~PluginEventHash} hash
  20223. * A plugin event hash.
  20224. *
  20225. * @param {Boolean} [before]
  20226. * If true, prefixes the event name with "before". In other words,
  20227. * use this to trigger "beforepluginsetup" instead of "pluginsetup".
  20228. */
  20229. var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
  20230. var eventName = (before ? 'before' : '') + 'pluginsetup';
  20231. player.trigger(eventName, hash);
  20232. player.trigger(eventName + ':' + hash.name, hash);
  20233. };
  20234. /**
  20235. * Takes a basic plugin function and returns a wrapper function which marks
  20236. * on the player that the plugin has been activated.
  20237. *
  20238. * @private
  20239. * @param {string} name
  20240. * The name of the plugin.
  20241. *
  20242. * @param {Function} plugin
  20243. * The basic plugin.
  20244. *
  20245. * @returns {Function}
  20246. * A wrapper function for the given plugin.
  20247. */
  20248. var createBasicPlugin = function createBasicPlugin(name, plugin) {
  20249. var basicPluginWrapper = function basicPluginWrapper() {
  20250. // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
  20251. // regardless, but we want the hash to be consistent with the hash provided
  20252. // for advanced plugins.
  20253. //
  20254. // The only potentially counter-intuitive thing here is the `instance` in
  20255. // the "pluginsetup" event is the value returned by the `plugin` function.
  20256. triggerSetupEvent(this, { name: name, plugin: plugin, instance: null }, true);
  20257. var instance = plugin.apply(this, arguments);
  20258. markPluginAsActive(this, name);
  20259. triggerSetupEvent(this, { name: name, plugin: plugin, instance: instance });
  20260. return instance;
  20261. };
  20262. Object.keys(plugin).forEach(function (prop) {
  20263. basicPluginWrapper[prop] = plugin[prop];
  20264. });
  20265. return basicPluginWrapper;
  20266. };
  20267. /**
  20268. * Takes a plugin sub-class and returns a factory function for generating
  20269. * instances of it.
  20270. *
  20271. * This factory function will replace itself with an instance of the requested
  20272. * sub-class of Plugin.
  20273. *
  20274. * @private
  20275. * @param {string} name
  20276. * The name of the plugin.
  20277. *
  20278. * @param {Plugin} PluginSubClass
  20279. * The advanced plugin.
  20280. *
  20281. * @returns {Function}
  20282. */
  20283. var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
  20284. // Add a `name` property to the plugin prototype so that each plugin can
  20285. // refer to itself by name.
  20286. PluginSubClass.prototype.name = name;
  20287. return function () {
  20288. triggerSetupEvent(this, { name: name, plugin: PluginSubClass, instance: null }, true);
  20289. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  20290. args[_key] = arguments[_key];
  20291. }
  20292. var instance = new (Function.prototype.bind.apply(PluginSubClass, [null].concat([this].concat(args))))();
  20293. // The plugin is replaced by a function that returns the current instance.
  20294. this[name] = function () {
  20295. return instance;
  20296. };
  20297. triggerSetupEvent(this, instance.getEventHash());
  20298. return instance;
  20299. };
  20300. };
  20301. /**
  20302. * Parent class for all advanced plugins.
  20303. *
  20304. * @mixes module:evented~EventedMixin
  20305. * @mixes module:stateful~StatefulMixin
  20306. * @fires Player#beforepluginsetup
  20307. * @fires Player#beforepluginsetup:$name
  20308. * @fires Player#pluginsetup
  20309. * @fires Player#pluginsetup:$name
  20310. * @listens Player#dispose
  20311. * @throws {Error}
  20312. * If attempting to instantiate the base {@link Plugin} class
  20313. * directly instead of via a sub-class.
  20314. */
  20315. var Plugin = function () {
  20316. /**
  20317. * Creates an instance of this class.
  20318. *
  20319. * Sub-classes should call `super` to ensure plugins are properly initialized.
  20320. *
  20321. * @param {Player} player
  20322. * A Video.js player instance.
  20323. */
  20324. function Plugin(player) {
  20325. classCallCheck(this, Plugin);
  20326. if (this.constructor === Plugin) {
  20327. throw new Error('Plugin must be sub-classed; not directly instantiated.');
  20328. }
  20329. this.player = player;
  20330. // Make this object evented, but remove the added `trigger` method so we
  20331. // use the prototype version instead.
  20332. evented(this);
  20333. delete this.trigger;
  20334. stateful(this, this.constructor.defaultState);
  20335. markPluginAsActive(player, this.name);
  20336. // Auto-bind the dispose method so we can use it as a listener and unbind
  20337. // it later easily.
  20338. this.dispose = bind(this, this.dispose);
  20339. // If the player is disposed, dispose the plugin.
  20340. player.on('dispose', this.dispose);
  20341. }
  20342. /**
  20343. * Get the version of the plugin that was set on <pluginName>.VERSION
  20344. */
  20345. Plugin.prototype.version = function version() {
  20346. return this.constructor.VERSION;
  20347. };
  20348. /**
  20349. * Each event triggered by plugins includes a hash of additional data with
  20350. * conventional properties.
  20351. *
  20352. * This returns that object or mutates an existing hash.
  20353. *
  20354. * @param {Object} [hash={}]
  20355. * An object to be used as event an event hash.
  20356. *
  20357. * @returns {Plugin~PluginEventHash}
  20358. * An event hash object with provided properties mixed-in.
  20359. */
  20360. Plugin.prototype.getEventHash = function getEventHash() {
  20361. var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  20362. hash.name = this.name;
  20363. hash.plugin = this.constructor;
  20364. hash.instance = this;
  20365. return hash;
  20366. };
  20367. /**
  20368. * Triggers an event on the plugin object and overrides
  20369. * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
  20370. *
  20371. * @param {string|Object} event
  20372. * An event type or an object with a type property.
  20373. *
  20374. * @param {Object} [hash={}]
  20375. * Additional data hash to merge with a
  20376. * {@link Plugin~PluginEventHash|PluginEventHash}.
  20377. *
  20378. * @returns {boolean}
  20379. * Whether or not default was prevented.
  20380. */
  20381. Plugin.prototype.trigger = function trigger$$1(event) {
  20382. var hash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  20383. return trigger(this.eventBusEl_, event, this.getEventHash(hash));
  20384. };
  20385. /**
  20386. * Handles "statechanged" events on the plugin. No-op by default, override by
  20387. * subclassing.
  20388. *
  20389. * @abstract
  20390. * @param {Event} e
  20391. * An event object provided by a "statechanged" event.
  20392. *
  20393. * @param {Object} e.changes
  20394. * An object describing changes that occurred with the "statechanged"
  20395. * event.
  20396. */
  20397. Plugin.prototype.handleStateChanged = function handleStateChanged(e) {};
  20398. /**
  20399. * Disposes a plugin.
  20400. *
  20401. * Subclasses can override this if they want, but for the sake of safety,
  20402. * it's probably best to subscribe the "dispose" event.
  20403. *
  20404. * @fires Plugin#dispose
  20405. */
  20406. Plugin.prototype.dispose = function dispose() {
  20407. var name = this.name,
  20408. player = this.player;
  20409. /**
  20410. * Signals that a advanced plugin is about to be disposed.
  20411. *
  20412. * @event Plugin#dispose
  20413. * @type {EventTarget~Event}
  20414. */
  20415. this.trigger('dispose');
  20416. this.off();
  20417. player.off('dispose', this.dispose);
  20418. // Eliminate any possible sources of leaking memory by clearing up
  20419. // references between the player and the plugin instance and nulling out
  20420. // the plugin's state and replacing methods with a function that throws.
  20421. player[PLUGIN_CACHE_KEY][name] = false;
  20422. this.player = this.state = null;
  20423. // Finally, replace the plugin name on the player with a new factory
  20424. // function, so that the plugin is ready to be set up again.
  20425. player[name] = createPluginFactory(name, pluginStorage[name]);
  20426. };
  20427. /**
  20428. * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
  20429. *
  20430. * @param {string|Function} plugin
  20431. * If a string, matches the name of a plugin. If a function, will be
  20432. * tested directly.
  20433. *
  20434. * @returns {boolean}
  20435. * Whether or not a plugin is a basic plugin.
  20436. */
  20437. Plugin.isBasic = function isBasic(plugin) {
  20438. var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
  20439. return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
  20440. };
  20441. /**
  20442. * Register a Video.js plugin.
  20443. *
  20444. * @param {string} name
  20445. * The name of the plugin to be registered. Must be a string and
  20446. * must not match an existing plugin or a method on the `Player`
  20447. * prototype.
  20448. *
  20449. * @param {Function} plugin
  20450. * A sub-class of `Plugin` or a function for basic plugins.
  20451. *
  20452. * @returns {Function}
  20453. * For advanced plugins, a factory function for that plugin. For
  20454. * basic plugins, a wrapper function that initializes the plugin.
  20455. */
  20456. Plugin.registerPlugin = function registerPlugin(name, plugin) {
  20457. if (typeof name !== 'string') {
  20458. throw new Error('Illegal plugin name, "' + name + '", must be a string, was ' + (typeof name === 'undefined' ? 'undefined' : _typeof(name)) + '.');
  20459. }
  20460. if (pluginExists(name)) {
  20461. log.warn('A plugin named "' + name + '" already exists. You may want to avoid re-registering plugins!');
  20462. } else if (Player.prototype.hasOwnProperty(name)) {
  20463. throw new Error('Illegal plugin name, "' + name + '", cannot share a name with an existing player method!');
  20464. }
  20465. if (typeof plugin !== 'function') {
  20466. throw new Error('Illegal plugin for "' + name + '", must be a function, was ' + (typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin)) + '.');
  20467. }
  20468. pluginStorage[name] = plugin;
  20469. // Add a player prototype method for all sub-classed plugins (but not for
  20470. // the base Plugin class).
  20471. if (name !== BASE_PLUGIN_NAME) {
  20472. if (Plugin.isBasic(plugin)) {
  20473. Player.prototype[name] = createBasicPlugin(name, plugin);
  20474. } else {
  20475. Player.prototype[name] = createPluginFactory(name, plugin);
  20476. }
  20477. }
  20478. return plugin;
  20479. };
  20480. /**
  20481. * De-register a Video.js plugin.
  20482. *
  20483. * @param {string} name
  20484. * The name of the plugin to be deregistered.
  20485. */
  20486. Plugin.deregisterPlugin = function deregisterPlugin(name) {
  20487. if (name === BASE_PLUGIN_NAME) {
  20488. throw new Error('Cannot de-register base plugin.');
  20489. }
  20490. if (pluginExists(name)) {
  20491. delete pluginStorage[name];
  20492. delete Player.prototype[name];
  20493. }
  20494. };
  20495. /**
  20496. * Gets an object containing multiple Video.js plugins.
  20497. *
  20498. * @param {Array} [names]
  20499. * If provided, should be an array of plugin names. Defaults to _all_
  20500. * plugin names.
  20501. *
  20502. * @returns {Object|undefined}
  20503. * An object containing plugin(s) associated with their name(s) or
  20504. * `undefined` if no matching plugins exist).
  20505. */
  20506. Plugin.getPlugins = function getPlugins() {
  20507. var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(pluginStorage);
  20508. var result = void 0;
  20509. names.forEach(function (name) {
  20510. var plugin = getPlugin(name);
  20511. if (plugin) {
  20512. result = result || {};
  20513. result[name] = plugin;
  20514. }
  20515. });
  20516. return result;
  20517. };
  20518. /**
  20519. * Gets a plugin's version, if available
  20520. *
  20521. * @param {string} name
  20522. * The name of a plugin.
  20523. *
  20524. * @returns {string}
  20525. * The plugin's version or an empty string.
  20526. */
  20527. Plugin.getPluginVersion = function getPluginVersion(name) {
  20528. var plugin = getPlugin(name);
  20529. return plugin && plugin.VERSION || '';
  20530. };
  20531. return Plugin;
  20532. }();
  20533. /**
  20534. * Gets a plugin by name if it exists.
  20535. *
  20536. * @static
  20537. * @method getPlugin
  20538. * @memberOf Plugin
  20539. * @param {string} name
  20540. * The name of a plugin.
  20541. *
  20542. * @returns {Function|undefined}
  20543. * The plugin (or `undefined`).
  20544. */
  20545. Plugin.getPlugin = getPlugin;
  20546. /**
  20547. * The name of the base plugin class as it is registered.
  20548. *
  20549. * @type {string}
  20550. */
  20551. Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
  20552. Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
  20553. /**
  20554. * Documented in player.js
  20555. *
  20556. * @ignore
  20557. */
  20558. Player.prototype.usingPlugin = function (name) {
  20559. return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
  20560. };
  20561. /**
  20562. * Documented in player.js
  20563. *
  20564. * @ignore
  20565. */
  20566. Player.prototype.hasPlugin = function (name) {
  20567. return !!pluginExists(name);
  20568. };
  20569. /**
  20570. * Signals that a plugin is about to be set up on a player.
  20571. *
  20572. * @event Player#beforepluginsetup
  20573. * @type {Plugin~PluginEventHash}
  20574. */
  20575. /**
  20576. * Signals that a plugin is about to be set up on a player - by name. The name
  20577. * is the name of the plugin.
  20578. *
  20579. * @event Player#beforepluginsetup:$name
  20580. * @type {Plugin~PluginEventHash}
  20581. */
  20582. /**
  20583. * Signals that a plugin has just been set up on a player.
  20584. *
  20585. * @event Player#pluginsetup
  20586. * @type {Plugin~PluginEventHash}
  20587. */
  20588. /**
  20589. * Signals that a plugin has just been set up on a player - by name. The name
  20590. * is the name of the plugin.
  20591. *
  20592. * @event Player#pluginsetup:$name
  20593. * @type {Plugin~PluginEventHash}
  20594. */
  20595. /**
  20596. * @typedef {Object} Plugin~PluginEventHash
  20597. *
  20598. * @property {string} instance
  20599. * For basic plugins, the return value of the plugin function. For
  20600. * advanced plugins, the plugin instance on which the event is fired.
  20601. *
  20602. * @property {string} name
  20603. * The name of the plugin.
  20604. *
  20605. * @property {string} plugin
  20606. * For basic plugins, the plugin function. For advanced plugins, the
  20607. * plugin class/constructor.
  20608. */
  20609. /**
  20610. * @file extend.js
  20611. * @module extend
  20612. */
  20613. /**
  20614. * A combination of node inherits and babel's inherits (after transpile).
  20615. * Both work the same but node adds `super_` to the subClass
  20616. * and Bable adds the superClass as __proto__. Both seem useful.
  20617. *
  20618. * @param {Object} subClass
  20619. * The class to inherit to
  20620. *
  20621. * @param {Object} superClass
  20622. * The class to inherit from
  20623. *
  20624. * @private
  20625. */
  20626. var _inherits = function _inherits(subClass, superClass) {
  20627. if (typeof superClass !== 'function' && superClass !== null) {
  20628. throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
  20629. }
  20630. subClass.prototype = Object.create(superClass && superClass.prototype, {
  20631. constructor: {
  20632. value: subClass,
  20633. enumerable: false,
  20634. writable: true,
  20635. configurable: true
  20636. }
  20637. });
  20638. if (superClass) {
  20639. // node
  20640. subClass.super_ = superClass;
  20641. }
  20642. };
  20643. /**
  20644. * Function for subclassing using the same inheritance that
  20645. * videojs uses internally
  20646. *
  20647. * @static
  20648. * @const
  20649. *
  20650. * @param {Object} superClass
  20651. * The class to inherit from
  20652. *
  20653. * @param {Object} [subClassMethods={}]
  20654. * The class to inherit to
  20655. *
  20656. * @return {Object}
  20657. * The new object with subClassMethods that inherited superClass.
  20658. */
  20659. var extendFn = function extendFn(superClass) {
  20660. var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  20661. var subClass = function subClass() {
  20662. superClass.apply(this, arguments);
  20663. };
  20664. var methods = {};
  20665. if ((typeof subClassMethods === 'undefined' ? 'undefined' : _typeof(subClassMethods)) === 'object') {
  20666. if (subClassMethods.constructor !== Object.prototype.constructor) {
  20667. subClass = subClassMethods.constructor;
  20668. }
  20669. methods = subClassMethods;
  20670. } else if (typeof subClassMethods === 'function') {
  20671. subClass = subClassMethods;
  20672. }
  20673. _inherits(subClass, superClass);
  20674. // Extend subObj's prototype with functions and other properties from props
  20675. for (var name in methods) {
  20676. if (methods.hasOwnProperty(name)) {
  20677. subClass.prototype[name] = methods[name];
  20678. }
  20679. }
  20680. return subClass;
  20681. };
  20682. /**
  20683. * @file video.js
  20684. * @module videojs
  20685. */
  20686. // Include the built-in techs
  20687. // HTML5 Element Shim for IE8
  20688. if (typeof HTMLVideoElement === 'undefined' && isReal()) {
  20689. document_1.createElement('video');
  20690. document_1.createElement('audio');
  20691. document_1.createElement('track');
  20692. document_1.createElement('video-js');
  20693. }
  20694. /**
  20695. * Normalize an `id` value by trimming off a leading `#`
  20696. *
  20697. * @param {string} id
  20698. * A string, maybe with a leading `#`.
  20699. *
  20700. * @returns {string}
  20701. * The string, without any leading `#`.
  20702. */
  20703. var normalizeId = function normalizeId(id) {
  20704. return id.indexOf('#') === 0 ? id.slice(1) : id;
  20705. };
  20706. /**
  20707. * Doubles as the main function for users to create a player instance and also
  20708. * the main library object.
  20709. * The `videojs` function can be used to initialize or retrieve a player.
  20710. *
  20711. * @param {string|Element} id
  20712. * Video element or video element ID
  20713. *
  20714. * @param {Object} [options]
  20715. * Optional options object for config/settings
  20716. *
  20717. * @param {Component~ReadyCallback} [ready]
  20718. * Optional ready callback
  20719. *
  20720. * @return {Player}
  20721. * A player instance
  20722. */
  20723. function videojs(id, options, ready) {
  20724. var player = videojs.getPlayer(id);
  20725. if (player) {
  20726. if (options) {
  20727. log.warn('Player "' + id + '" is already initialised. Options will not be applied.');
  20728. }
  20729. if (ready) {
  20730. player.ready(ready);
  20731. }
  20732. return player;
  20733. }
  20734. var el = typeof id === 'string' ? $('#' + normalizeId(id)) : id;
  20735. if (!isEl(el)) {
  20736. throw new TypeError('The element or ID supplied is not valid. (videojs)');
  20737. }
  20738. if (!document_1.body.contains(el)) {
  20739. log.warn('The element supplied is not included in the DOM');
  20740. }
  20741. options = options || {};
  20742. videojs.hooks('beforesetup').forEach(function (hookFunction) {
  20743. var opts = hookFunction(el, mergeOptions(options));
  20744. if (!isObject(opts) || Array.isArray(opts)) {
  20745. log.error('please return an object in beforesetup hooks');
  20746. return;
  20747. }
  20748. options = mergeOptions(options, opts);
  20749. });
  20750. // We get the current "Player" component here in case an integration has
  20751. // replaced it with a custom player.
  20752. var PlayerComponent = Component.getComponent('Player');
  20753. player = new PlayerComponent(el, options, ready);
  20754. videojs.hooks('setup').forEach(function (hookFunction) {
  20755. return hookFunction(player);
  20756. });
  20757. return player;
  20758. }
  20759. /**
  20760. * An Object that contains lifecycle hooks as keys which point to an array
  20761. * of functions that are run when a lifecycle is triggered
  20762. */
  20763. videojs.hooks_ = {};
  20764. /**
  20765. * Get a list of hooks for a specific lifecycle
  20766. * @function videojs.hooks
  20767. *
  20768. * @param {string} type
  20769. * the lifecyle to get hooks from
  20770. *
  20771. * @param {Function|Function[]} [fn]
  20772. * Optionally add a hook (or hooks) to the lifecycle that your are getting.
  20773. *
  20774. * @return {Array}
  20775. * an array of hooks, or an empty array if there are none.
  20776. */
  20777. videojs.hooks = function (type, fn) {
  20778. videojs.hooks_[type] = videojs.hooks_[type] || [];
  20779. if (fn) {
  20780. videojs.hooks_[type] = videojs.hooks_[type].concat(fn);
  20781. }
  20782. return videojs.hooks_[type];
  20783. };
  20784. /**
  20785. * Add a function hook to a specific videojs lifecycle.
  20786. *
  20787. * @param {string} type
  20788. * the lifecycle to hook the function to.
  20789. *
  20790. * @param {Function|Function[]}
  20791. * The function or array of functions to attach.
  20792. */
  20793. videojs.hook = function (type, fn) {
  20794. videojs.hooks(type, fn);
  20795. };
  20796. /**
  20797. * Add a function hook that will only run once to a specific videojs lifecycle.
  20798. *
  20799. * @param {string} type
  20800. * the lifecycle to hook the function to.
  20801. *
  20802. * @param {Function|Function[]}
  20803. * The function or array of functions to attach.
  20804. */
  20805. videojs.hookOnce = function (type, fn) {
  20806. videojs.hooks(type, [].concat(fn).map(function (original) {
  20807. var wrapper = function wrapper() {
  20808. videojs.removeHook(type, wrapper);
  20809. return original.apply(undefined, arguments);
  20810. };
  20811. return wrapper;
  20812. }));
  20813. };
  20814. /**
  20815. * Remove a hook from a specific videojs lifecycle.
  20816. *
  20817. * @param {string} type
  20818. * the lifecycle that the function hooked to
  20819. *
  20820. * @param {Function} fn
  20821. * The hooked function to remove
  20822. *
  20823. * @return {boolean}
  20824. * The function that was removed or undef
  20825. */
  20826. videojs.removeHook = function (type, fn) {
  20827. var index = videojs.hooks(type).indexOf(fn);
  20828. if (index <= -1) {
  20829. return false;
  20830. }
  20831. videojs.hooks_[type] = videojs.hooks_[type].slice();
  20832. videojs.hooks_[type].splice(index, 1);
  20833. return true;
  20834. };
  20835. // Add default styles
  20836. if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
  20837. var style = $('.vjs-styles-defaults');
  20838. if (!style) {
  20839. style = createStyleElement('vjs-styles-defaults');
  20840. var head = $('head');
  20841. if (head) {
  20842. head.insertBefore(style, head.firstChild);
  20843. }
  20844. setTextContent(style, '\n .video-js {\n width: 300px;\n height: 150px;\n }\n\n .vjs-fluid {\n padding-top: 56.25%\n }\n ');
  20845. }
  20846. }
  20847. // Run Auto-load players
  20848. // You have to wait at least once in case this script is loaded after your
  20849. // video in the DOM (weird behavior only with minified version)
  20850. autoSetupTimeout(1, videojs);
  20851. /**
  20852. * Current software version. Follows semver.
  20853. *
  20854. * @type {string}
  20855. */
  20856. videojs.VERSION = version;
  20857. /**
  20858. * The global options object. These are the settings that take effect
  20859. * if no overrides are specified when the player is created.
  20860. *
  20861. * @type {Object}
  20862. */
  20863. videojs.options = Player.prototype.options_;
  20864. /**
  20865. * Get an object with the currently created players, keyed by player ID
  20866. *
  20867. * @return {Object}
  20868. * The created players
  20869. */
  20870. videojs.getPlayers = function () {
  20871. return Player.players;
  20872. };
  20873. /**
  20874. * Get a single player based on an ID or DOM element.
  20875. *
  20876. * This is useful if you want to check if an element or ID has an associated
  20877. * Video.js player, but not create one if it doesn't.
  20878. *
  20879. * @param {string|Element} id
  20880. * An HTML element - `<video>`, `<audio>`, or `<video-js>` -
  20881. * or a string matching the `id` of such an element.
  20882. *
  20883. * @returns {Player|undefined}
  20884. * A player instance or `undefined` if there is no player instance
  20885. * matching the argument.
  20886. */
  20887. videojs.getPlayer = function (id) {
  20888. var players = Player.players;
  20889. var tag = void 0;
  20890. if (typeof id === 'string') {
  20891. var nId = normalizeId(id);
  20892. var player = players[nId];
  20893. if (player) {
  20894. return player;
  20895. }
  20896. tag = $('#' + nId);
  20897. } else {
  20898. tag = id;
  20899. }
  20900. if (isEl(tag)) {
  20901. var _tag = tag,
  20902. _player = _tag.player,
  20903. playerId = _tag.playerId;
  20904. // Element may have a `player` property referring to an already created
  20905. // player instance. If so, return that.
  20906. if (_player || players[playerId]) {
  20907. return _player || players[playerId];
  20908. }
  20909. }
  20910. };
  20911. /**
  20912. * Returns an array of all current players.
  20913. *
  20914. * @return {Array}
  20915. * An array of all players. The array will be in the order that
  20916. * `Object.keys` provides, which could potentially vary between
  20917. * JavaScript engines.
  20918. *
  20919. */
  20920. videojs.getAllPlayers = function () {
  20921. return (
  20922. // Disposed players leave a key with a `null` value, so we need to make sure
  20923. // we filter those out.
  20924. Object.keys(Player.players).map(function (k) {
  20925. return Player.players[k];
  20926. }).filter(Boolean)
  20927. );
  20928. };
  20929. /**
  20930. * Expose players object.
  20931. *
  20932. * @memberOf videojs
  20933. * @property {Object} players
  20934. */
  20935. videojs.players = Player.players;
  20936. /**
  20937. * Get a component class object by name
  20938. *
  20939. * @borrows Component.getComponent as videojs.getComponent
  20940. */
  20941. videojs.getComponent = Component.getComponent;
  20942. /**
  20943. * Register a component so it can referred to by name. Used when adding to other
  20944. * components, either through addChild `component.addChild('myComponent')` or through
  20945. * default children options `{ children: ['myComponent'] }`.
  20946. *
  20947. * > NOTE: You could also just initialize the component before adding.
  20948. * `component.addChild(new MyComponent());`
  20949. *
  20950. * @param {string} name
  20951. * The class name of the component
  20952. *
  20953. * @param {Component} comp
  20954. * The component class
  20955. *
  20956. * @return {Component}
  20957. * The newly registered component
  20958. */
  20959. videojs.registerComponent = function (name$$1, comp) {
  20960. if (Tech.isTech(comp)) {
  20961. log.warn('The ' + name$$1 + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
  20962. }
  20963. Component.registerComponent.call(Component, name$$1, comp);
  20964. };
  20965. /**
  20966. * Get a Tech class object by name
  20967. *
  20968. * @borrows Tech.getTech as videojs.getTech
  20969. */
  20970. videojs.getTech = Tech.getTech;
  20971. /**
  20972. * Register a Tech so it can referred to by name.
  20973. * This is used in the tech order for the player.
  20974. *
  20975. * @borrows Tech.registerTech as videojs.registerTech
  20976. */
  20977. videojs.registerTech = Tech.registerTech;
  20978. /**
  20979. * Register a middleware to a source type.
  20980. *
  20981. * @param {String} type A string representing a MIME type.
  20982. * @param {function(player):object} middleware A middleware factory that takes a player.
  20983. */
  20984. videojs.use = use;
  20985. /**
  20986. * An object that can be returned by a middleware to signify
  20987. * that the middleware is being terminated.
  20988. *
  20989. * @type {object}
  20990. * @memberOf {videojs}
  20991. * @property {object} middleware.TERMINATOR
  20992. */
  20993. // Object.defineProperty is not available in IE8
  20994. if (!IS_IE8 && Object.defineProperty) {
  20995. Object.defineProperty(videojs, 'middleware', {
  20996. value: {},
  20997. writeable: false,
  20998. enumerable: true
  20999. });
  21000. Object.defineProperty(videojs.middleware, 'TERMINATOR', {
  21001. value: TERMINATOR,
  21002. writeable: false,
  21003. enumerable: true
  21004. });
  21005. } else {
  21006. videojs.middleware = { TERMINATOR: TERMINATOR };
  21007. }
  21008. /**
  21009. * A suite of browser and device tests from {@link browser}.
  21010. *
  21011. * @type {Object}
  21012. * @private
  21013. */
  21014. videojs.browser = browser;
  21015. /**
  21016. * Whether or not the browser supports touch events. Included for backward
  21017. * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
  21018. * instead going forward.
  21019. *
  21020. * @deprecated since version 5.0
  21021. * @type {boolean}
  21022. */
  21023. videojs.TOUCH_ENABLED = TOUCH_ENABLED;
  21024. /**
  21025. * Subclass an existing class
  21026. * Mimics ES6 subclassing with the `extend` keyword
  21027. *
  21028. * @borrows extend:extendFn as videojs.extend
  21029. */
  21030. videojs.extend = extendFn;
  21031. /**
  21032. * Merge two options objects recursively
  21033. * Performs a deep merge like lodash.merge but **only merges plain objects**
  21034. * (not arrays, elements, anything else)
  21035. * Other values will be copied directly from the second object.
  21036. *
  21037. * @borrows merge-options:mergeOptions as videojs.mergeOptions
  21038. */
  21039. videojs.mergeOptions = mergeOptions;
  21040. /**
  21041. * Change the context (this) of a function
  21042. *
  21043. * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native
  21044. * `function() {}.bind(newContext);` instead of this.
  21045. *
  21046. * @borrows fn:bind as videojs.bind
  21047. */
  21048. videojs.bind = bind;
  21049. /**
  21050. * Register a Video.js plugin.
  21051. *
  21052. * @borrows plugin:registerPlugin as videojs.registerPlugin
  21053. * @method registerPlugin
  21054. *
  21055. * @param {string} name
  21056. * The name of the plugin to be registered. Must be a string and
  21057. * must not match an existing plugin or a method on the `Player`
  21058. * prototype.
  21059. *
  21060. * @param {Function} plugin
  21061. * A sub-class of `Plugin` or a function for basic plugins.
  21062. *
  21063. * @return {Function}
  21064. * For advanced plugins, a factory function for that plugin. For
  21065. * basic plugins, a wrapper function that initializes the plugin.
  21066. */
  21067. videojs.registerPlugin = Plugin.registerPlugin;
  21068. /**
  21069. * Deregister a Video.js plugin.
  21070. *
  21071. * @borrows plugin:deregisterPlugin as videojs.deregisterPlugin
  21072. * @method deregisterPlugin
  21073. *
  21074. * @param {string} name
  21075. * The name of the plugin to be deregistered. Must be a string and
  21076. * must match an existing plugin or a method on the `Player`
  21077. * prototype.
  21078. *
  21079. */
  21080. videojs.deregisterPlugin = Plugin.deregisterPlugin;
  21081. /**
  21082. * Deprecated method to register a plugin with Video.js
  21083. *
  21084. * @deprecated
  21085. * videojs.plugin() is deprecated; use videojs.registerPlugin() instead
  21086. *
  21087. * @param {string} name
  21088. * The plugin name
  21089. *
  21090. * @param {Plugin|Function} plugin
  21091. * The plugin sub-class or function
  21092. */
  21093. videojs.plugin = function (name$$1, plugin) {
  21094. log.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
  21095. return Plugin.registerPlugin(name$$1, plugin);
  21096. };
  21097. /**
  21098. * Gets an object containing multiple Video.js plugins.
  21099. *
  21100. * @param {Array} [names]
  21101. * If provided, should be an array of plugin names. Defaults to _all_
  21102. * plugin names.
  21103. *
  21104. * @return {Object|undefined}
  21105. * An object containing plugin(s) associated with their name(s) or
  21106. * `undefined` if no matching plugins exist).
  21107. */
  21108. videojs.getPlugins = Plugin.getPlugins;
  21109. /**
  21110. * Gets a plugin by name if it exists.
  21111. *
  21112. * @param {string} name
  21113. * The name of a plugin.
  21114. *
  21115. * @return {Function|undefined}
  21116. * The plugin (or `undefined`).
  21117. */
  21118. videojs.getPlugin = Plugin.getPlugin;
  21119. /**
  21120. * Gets a plugin's version, if available
  21121. *
  21122. * @param {string} name
  21123. * The name of a plugin.
  21124. *
  21125. * @return {string}
  21126. * The plugin's version or an empty string.
  21127. */
  21128. videojs.getPluginVersion = Plugin.getPluginVersion;
  21129. /**
  21130. * Adding languages so that they're available to all players.
  21131. * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
  21132. *
  21133. * @param {string} code
  21134. * The language code or dictionary property
  21135. *
  21136. * @param {Object} data
  21137. * The data values to be translated
  21138. *
  21139. * @return {Object}
  21140. * The resulting language dictionary object
  21141. */
  21142. videojs.addLanguage = function (code, data) {
  21143. var _mergeOptions;
  21144. code = ('' + code).toLowerCase();
  21145. videojs.options.languages = mergeOptions(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
  21146. return videojs.options.languages[code];
  21147. };
  21148. /**
  21149. * Log messages
  21150. *
  21151. * @borrows log:log as videojs.log
  21152. */
  21153. videojs.log = log;
  21154. videojs.createLogger = createLogger;
  21155. /**
  21156. * Creates an emulated TimeRange object.
  21157. *
  21158. * @borrows time-ranges:createTimeRanges as videojs.createTimeRange
  21159. */
  21160. /**
  21161. * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges
  21162. */
  21163. videojs.createTimeRange = videojs.createTimeRanges = createTimeRanges;
  21164. /**
  21165. * Format seconds as a time string, H:MM:SS or M:SS
  21166. * Supplying a guide (in seconds) will force a number of leading zeros
  21167. * to cover the length of the guide
  21168. *
  21169. * @borrows format-time:formatTime as videojs.formatTime
  21170. */
  21171. videojs.formatTime = formatTime;
  21172. /**
  21173. * Replaces format-time with a custom implementation, to be used in place of the default.
  21174. *
  21175. * @borrows format-time:setFormatTime as videojs.setFormatTime
  21176. *
  21177. * @method setFormatTime
  21178. *
  21179. * @param {Function} customFn
  21180. * A custom format-time function which will be called with the current time and guide (in seconds) as arguments.
  21181. * Passed fn should return a string.
  21182. */
  21183. videojs.setFormatTime = setFormatTime;
  21184. /**
  21185. * Resets format-time to the default implementation.
  21186. *
  21187. * @borrows format-time:resetFormatTime as videojs.resetFormatTime
  21188. *
  21189. * @method resetFormatTime
  21190. */
  21191. videojs.resetFormatTime = resetFormatTime;
  21192. /**
  21193. * Resolve and parse the elements of a URL
  21194. *
  21195. * @borrows url:parseUrl as videojs.parseUrl
  21196. *
  21197. */
  21198. videojs.parseUrl = parseUrl;
  21199. /**
  21200. * Returns whether the url passed is a cross domain request or not.
  21201. *
  21202. * @borrows url:isCrossOrigin as videojs.isCrossOrigin
  21203. */
  21204. videojs.isCrossOrigin = isCrossOrigin;
  21205. /**
  21206. * Event target class.
  21207. *
  21208. * @borrows EventTarget as videojs.EventTarget
  21209. */
  21210. videojs.EventTarget = EventTarget;
  21211. /**
  21212. * Add an event listener to element
  21213. * It stores the handler function in a separate cache object
  21214. * and adds a generic handler to the element's event,
  21215. * along with a unique id (guid) to the element.
  21216. *
  21217. * @borrows events:on as videojs.on
  21218. */
  21219. videojs.on = on;
  21220. /**
  21221. * Trigger a listener only once for an event
  21222. *
  21223. * @borrows events:one as videojs.one
  21224. */
  21225. videojs.one = one;
  21226. /**
  21227. * Removes event listeners from an element
  21228. *
  21229. * @borrows events:off as videojs.off
  21230. */
  21231. videojs.off = off;
  21232. /**
  21233. * Trigger an event for an element
  21234. *
  21235. * @borrows events:trigger as videojs.trigger
  21236. */
  21237. videojs.trigger = trigger;
  21238. /**
  21239. * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
  21240. *
  21241. * @param {Object} options
  21242. * settings for the request.
  21243. *
  21244. * @return {XMLHttpRequest|XDomainRequest}
  21245. * The request object.
  21246. *
  21247. * @see https://github.com/Raynos/xhr
  21248. */
  21249. videojs.xhr = xhr;
  21250. /**
  21251. * TextTrack class
  21252. *
  21253. * @borrows TextTrack as videojs.TextTrack
  21254. */
  21255. videojs.TextTrack = TextTrack;
  21256. /**
  21257. * export the AudioTrack class so that source handlers can create
  21258. * AudioTracks and then add them to the players AudioTrackList
  21259. *
  21260. * @borrows AudioTrack as videojs.AudioTrack
  21261. */
  21262. videojs.AudioTrack = AudioTrack;
  21263. /**
  21264. * export the VideoTrack class so that source handlers can create
  21265. * VideoTracks and then add them to the players VideoTrackList
  21266. *
  21267. * @borrows VideoTrack as videojs.VideoTrack
  21268. */
  21269. videojs.VideoTrack = VideoTrack;
  21270. /**
  21271. * Determines, via duck typing, whether or not a value is a DOM element.
  21272. *
  21273. * @borrows dom:isEl as videojs.isEl
  21274. * @deprecated Use videojs.dom.isEl() instead
  21275. */
  21276. /**
  21277. * Determines, via duck typing, whether or not a value is a text node.
  21278. *
  21279. * @borrows dom:isTextNode as videojs.isTextNode
  21280. * @deprecated Use videojs.dom.isTextNode() instead
  21281. */
  21282. /**
  21283. * Creates an element and applies properties.
  21284. *
  21285. * @borrows dom:createEl as videojs.createEl
  21286. * @deprecated Use videojs.dom.createEl() instead
  21287. */
  21288. /**
  21289. * Check if an element has a CSS class
  21290. *
  21291. * @borrows dom:hasElClass as videojs.hasClass
  21292. * @deprecated Use videojs.dom.hasClass() instead
  21293. */
  21294. /**
  21295. * Add a CSS class name to an element
  21296. *
  21297. * @borrows dom:addElClass as videojs.addClass
  21298. * @deprecated Use videojs.dom.addClass() instead
  21299. */
  21300. /**
  21301. * Remove a CSS class name from an element
  21302. *
  21303. * @borrows dom:removeElClass as videojs.removeClass
  21304. * @deprecated Use videojs.dom.removeClass() instead
  21305. */
  21306. /**
  21307. * Adds or removes a CSS class name on an element depending on an optional
  21308. * condition or the presence/absence of the class name.
  21309. *
  21310. * @borrows dom:toggleElClass as videojs.toggleClass
  21311. * @deprecated Use videojs.dom.toggleClass() instead
  21312. */
  21313. /**
  21314. * Apply attributes to an HTML element.
  21315. *
  21316. * @borrows dom:setElAttributes as videojs.setAttribute
  21317. * @deprecated Use videojs.dom.setAttributes() instead
  21318. */
  21319. /**
  21320. * Get an element's attribute values, as defined on the HTML tag
  21321. * Attributes are not the same as properties. They're defined on the tag
  21322. * or with setAttribute (which shouldn't be used with HTML)
  21323. * This will return true or false for boolean attributes.
  21324. *
  21325. * @borrows dom:getElAttributes as videojs.getAttributes
  21326. * @deprecated Use videojs.dom.getAttributes() instead
  21327. */
  21328. /**
  21329. * Empties the contents of an element.
  21330. *
  21331. * @borrows dom:emptyEl as videojs.emptyEl
  21332. * @deprecated Use videojs.dom.emptyEl() instead
  21333. */
  21334. /**
  21335. * Normalizes and appends content to an element.
  21336. *
  21337. * The content for an element can be passed in multiple types and
  21338. * combinations, whose behavior is as follows:
  21339. *
  21340. * - String
  21341. * Normalized into a text node.
  21342. *
  21343. * - Element, TextNode
  21344. * Passed through.
  21345. *
  21346. * - Array
  21347. * A one-dimensional array of strings, elements, nodes, or functions (which
  21348. * return single strings, elements, or nodes).
  21349. *
  21350. * - Function
  21351. * If the sole argument, is expected to produce a string, element,
  21352. * node, or array.
  21353. *
  21354. * @borrows dom:appendContents as videojs.appendContet
  21355. * @deprecated Use videojs.dom.appendContent() instead
  21356. */
  21357. /**
  21358. * Normalizes and inserts content into an element; this is identical to
  21359. * `appendContent()`, except it empties the element first.
  21360. *
  21361. * The content for an element can be passed in multiple types and
  21362. * combinations, whose behavior is as follows:
  21363. *
  21364. * - String
  21365. * Normalized into a text node.
  21366. *
  21367. * - Element, TextNode
  21368. * Passed through.
  21369. *
  21370. * - Array
  21371. * A one-dimensional array of strings, elements, nodes, or functions (which
  21372. * return single strings, elements, or nodes).
  21373. *
  21374. * - Function
  21375. * If the sole argument, is expected to produce a string, element,
  21376. * node, or array.
  21377. *
  21378. * @borrows dom:insertContent as videojs.insertContent
  21379. * @deprecated Use videojs.dom.insertContent() instead
  21380. */
  21381. ['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
  21382. videojs[k] = function () {
  21383. log.warn('videojs.' + k + '() is deprecated; use videojs.dom.' + k + '() instead');
  21384. return Dom[k].apply(null, arguments);
  21385. };
  21386. });
  21387. /**
  21388. * A safe getComputedStyle with an IE8 fallback.
  21389. *
  21390. * This is because in Firefox, if the player is loaded in an iframe with `display:none`,
  21391. * then `getComputedStyle` returns `null`, so, we do a null-check to make sure
  21392. * that the player doesn't break in these cases.
  21393. * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details.
  21394. *
  21395. * @borrows computed-style:computedStyle as videojs.computedStyle
  21396. */
  21397. videojs.computedStyle = computedStyle;
  21398. /**
  21399. * Export the Dom utilities for use in external plugins
  21400. * and Tech's
  21401. */
  21402. videojs.dom = Dom;
  21403. /**
  21404. * Export the Url utilities for use in external plugins
  21405. * and Tech's
  21406. */
  21407. videojs.url = Url;
  21408. return videojs;
  21409. })));