testing.mjs 1.1 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728
  1. /**
  2. * @license Angular v16.2.9
  3. * (c) 2010-2022 Google LLC. https://angular.io/
  4. * License: MIT
  5. */
  6. import { getDebugNode, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, provideZoneChangeDetection, Compiler, COMPILER_OPTIONS, Injector as Injector$1, ɵisEnvironmentProviders, ɵNgModuleFactory, ModuleWithComponentFactories, ɵconvertToBitFlags, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, EnvironmentInjector as EnvironmentInjector$1, NgZone as NgZone$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
  7. import { ResourceLoader } from '@angular/compiler';
  8. import { Subject, Subscription, Observable, merge as merge$1 } from 'rxjs';
  9. import { share } from 'rxjs/operators';
  10. /**
  11. * Wraps a test function in an asynchronous test zone. The test will automatically
  12. * complete when all asynchronous calls within this zone are done. Can be used
  13. * to wrap an {@link inject} call.
  14. *
  15. * Example:
  16. *
  17. * ```
  18. * it('...', waitForAsync(inject([AClass], (object) => {
  19. * object.doSomething.then(() => {
  20. * expect(...);
  21. * })
  22. * });
  23. * ```
  24. *
  25. * @publicApi
  26. */
  27. function waitForAsync(fn) {
  28. const _Zone = typeof Zone !== 'undefined' ? Zone : null;
  29. if (!_Zone) {
  30. return function () {
  31. return Promise.reject('Zone is needed for the waitForAsync() test helper but could not be found. ' +
  32. 'Please make sure that your environment includes zone.js');
  33. };
  34. }
  35. const asyncTest = _Zone && _Zone[_Zone.__symbol__('asyncTest')];
  36. if (typeof asyncTest === 'function') {
  37. return asyncTest(fn);
  38. }
  39. return function () {
  40. return Promise.reject('zone-testing.js is needed for the async() test helper but could not be found. ' +
  41. 'Please make sure that your environment includes zone.js/testing');
  42. };
  43. }
  44. /**
  45. * @deprecated use `waitForAsync()`, (expected removal in v12)
  46. * @see {@link waitForAsync}
  47. * @publicApi
  48. * */
  49. function async(fn) {
  50. return waitForAsync(fn);
  51. }
  52. /**
  53. * Fixture for debugging and testing a component.
  54. *
  55. * @publicApi
  56. */
  57. class ComponentFixture {
  58. constructor(componentRef, ngZone, _autoDetect) {
  59. this.componentRef = componentRef;
  60. this.ngZone = ngZone;
  61. this._autoDetect = _autoDetect;
  62. this._isStable = true;
  63. this._isDestroyed = false;
  64. this._resolve = null;
  65. this._promise = null;
  66. this._onUnstableSubscription = null;
  67. this._onStableSubscription = null;
  68. this._onMicrotaskEmptySubscription = null;
  69. this._onErrorSubscription = null;
  70. this.changeDetectorRef = componentRef.changeDetectorRef;
  71. this.elementRef = componentRef.location;
  72. this.debugElement = getDebugNode(this.elementRef.nativeElement);
  73. this.componentInstance = componentRef.instance;
  74. this.nativeElement = this.elementRef.nativeElement;
  75. this.componentRef = componentRef;
  76. this.ngZone = ngZone;
  77. if (ngZone) {
  78. // Create subscriptions outside the NgZone so that the callbacks run oustide
  79. // of NgZone.
  80. ngZone.runOutsideAngular(() => {
  81. this._onUnstableSubscription = ngZone.onUnstable.subscribe({
  82. next: () => {
  83. this._isStable = false;
  84. }
  85. });
  86. this._onMicrotaskEmptySubscription = ngZone.onMicrotaskEmpty.subscribe({
  87. next: () => {
  88. if (this._autoDetect) {
  89. // Do a change detection run with checkNoChanges set to true to check
  90. // there are no changes on the second run.
  91. this.detectChanges(true);
  92. }
  93. }
  94. });
  95. this._onStableSubscription = ngZone.onStable.subscribe({
  96. next: () => {
  97. this._isStable = true;
  98. // Check whether there is a pending whenStable() completer to resolve.
  99. if (this._promise !== null) {
  100. // If so check whether there are no pending macrotasks before resolving.
  101. // Do this check in the next tick so that ngZone gets a chance to update the state of
  102. // pending macrotasks.
  103. queueMicrotask(() => {
  104. if (!ngZone.hasPendingMacrotasks) {
  105. if (this._promise !== null) {
  106. this._resolve(true);
  107. this._resolve = null;
  108. this._promise = null;
  109. }
  110. }
  111. });
  112. }
  113. }
  114. });
  115. this._onErrorSubscription = ngZone.onError.subscribe({
  116. next: (error) => {
  117. throw error;
  118. }
  119. });
  120. });
  121. }
  122. }
  123. _tick(checkNoChanges) {
  124. this.changeDetectorRef.detectChanges();
  125. if (checkNoChanges) {
  126. this.checkNoChanges();
  127. }
  128. }
  129. /**
  130. * Trigger a change detection cycle for the component.
  131. */
  132. detectChanges(checkNoChanges = true) {
  133. if (this.ngZone != null) {
  134. // Run the change detection inside the NgZone so that any async tasks as part of the change
  135. // detection are captured by the zone and can be waited for in isStable.
  136. this.ngZone.run(() => {
  137. this._tick(checkNoChanges);
  138. });
  139. }
  140. else {
  141. // Running without zone. Just do the change detection.
  142. this._tick(checkNoChanges);
  143. }
  144. }
  145. /**
  146. * Do a change detection run to make sure there were no changes.
  147. */
  148. checkNoChanges() {
  149. this.changeDetectorRef.checkNoChanges();
  150. }
  151. /**
  152. * Set whether the fixture should autodetect changes.
  153. *
  154. * Also runs detectChanges once so that any existing change is detected.
  155. */
  156. autoDetectChanges(autoDetect = true) {
  157. if (this.ngZone == null) {
  158. throw new Error('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set');
  159. }
  160. this._autoDetect = autoDetect;
  161. this.detectChanges();
  162. }
  163. /**
  164. * Return whether the fixture is currently stable or has async tasks that have not been completed
  165. * yet.
  166. */
  167. isStable() {
  168. return this._isStable && !this.ngZone.hasPendingMacrotasks;
  169. }
  170. /**
  171. * Get a promise that resolves when the fixture is stable.
  172. *
  173. * This can be used to resume testing after events have triggered asynchronous activity or
  174. * asynchronous change detection.
  175. */
  176. whenStable() {
  177. if (this.isStable()) {
  178. return Promise.resolve(false);
  179. }
  180. else if (this._promise !== null) {
  181. return this._promise;
  182. }
  183. else {
  184. this._promise = new Promise(res => {
  185. this._resolve = res;
  186. });
  187. return this._promise;
  188. }
  189. }
  190. _getRenderer() {
  191. if (this._renderer === undefined) {
  192. this._renderer = this.componentRef.injector.get(RendererFactory2$1, null);
  193. }
  194. return this._renderer;
  195. }
  196. /**
  197. * Get a promise that resolves when the ui state is stable following animations.
  198. */
  199. whenRenderingDone() {
  200. const renderer = this._getRenderer();
  201. if (renderer && renderer.whenRenderingDone) {
  202. return renderer.whenRenderingDone();
  203. }
  204. return this.whenStable();
  205. }
  206. /**
  207. * Trigger component destruction.
  208. */
  209. destroy() {
  210. if (!this._isDestroyed) {
  211. this.componentRef.destroy();
  212. if (this._onUnstableSubscription != null) {
  213. this._onUnstableSubscription.unsubscribe();
  214. this._onUnstableSubscription = null;
  215. }
  216. if (this._onStableSubscription != null) {
  217. this._onStableSubscription.unsubscribe();
  218. this._onStableSubscription = null;
  219. }
  220. if (this._onMicrotaskEmptySubscription != null) {
  221. this._onMicrotaskEmptySubscription.unsubscribe();
  222. this._onMicrotaskEmptySubscription = null;
  223. }
  224. if (this._onErrorSubscription != null) {
  225. this._onErrorSubscription.unsubscribe();
  226. this._onErrorSubscription = null;
  227. }
  228. this._isDestroyed = true;
  229. }
  230. }
  231. }
  232. const _Zone = typeof Zone !== 'undefined' ? Zone : null;
  233. const fakeAsyncTestModule = _Zone && _Zone[_Zone.__symbol__('fakeAsyncTest')];
  234. const fakeAsyncTestModuleNotLoadedErrorMessage = `zone-testing.js is needed for the fakeAsync() test helper but could not be found.
  235. Please make sure that your environment includes zone.js/testing`;
  236. /**
  237. * Clears out the shared fake async zone for a test.
  238. * To be called in a global `beforeEach`.
  239. *
  240. * @publicApi
  241. */
  242. function resetFakeAsyncZone() {
  243. if (fakeAsyncTestModule) {
  244. return fakeAsyncTestModule.resetFakeAsyncZone();
  245. }
  246. throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
  247. }
  248. /**
  249. * Wraps a function to be executed in the `fakeAsync` zone:
  250. * - Microtasks are manually executed by calling `flushMicrotasks()`.
  251. * - Timers are synchronous; `tick()` simulates the asynchronous passage of time.
  252. *
  253. * If there are any pending timers at the end of the function, an exception is thrown.
  254. *
  255. * Can be used to wrap `inject()` calls.
  256. *
  257. * @param fn The function that you want to wrap in the `fakeAsync` zone.
  258. *
  259. * @usageNotes
  260. * ### Example
  261. *
  262. * {@example core/testing/ts/fake_async.ts region='basic'}
  263. *
  264. *
  265. * @returns The function wrapped to be executed in the `fakeAsync` zone.
  266. * Any arguments passed when calling this returned function will be passed through to the `fn`
  267. * function in the parameters when it is called.
  268. *
  269. * @publicApi
  270. */
  271. function fakeAsync(fn) {
  272. if (fakeAsyncTestModule) {
  273. return fakeAsyncTestModule.fakeAsync(fn);
  274. }
  275. throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
  276. }
  277. /**
  278. * Simulates the asynchronous passage of time for the timers in the `fakeAsync` zone.
  279. *
  280. * The microtasks queue is drained at the very start of this function and after any timer callback
  281. * has been executed.
  282. *
  283. * @param millis The number of milliseconds to advance the virtual timer.
  284. * @param tickOptions The options to pass to the `tick()` function.
  285. *
  286. * @usageNotes
  287. *
  288. * The `tick()` option is a flag called `processNewMacroTasksSynchronously`,
  289. * which determines whether or not to invoke new macroTasks.
  290. *
  291. * If you provide a `tickOptions` object, but do not specify a
  292. * `processNewMacroTasksSynchronously` property (`tick(100, {})`),
  293. * then `processNewMacroTasksSynchronously` defaults to true.
  294. *
  295. * If you omit the `tickOptions` parameter (`tick(100))`), then
  296. * `tickOptions` defaults to `{processNewMacroTasksSynchronously: true}`.
  297. *
  298. * ### Example
  299. *
  300. * {@example core/testing/ts/fake_async.ts region='basic'}
  301. *
  302. * The following example includes a nested timeout (new macroTask), and
  303. * the `tickOptions` parameter is allowed to default. In this case,
  304. * `processNewMacroTasksSynchronously` defaults to true, and the nested
  305. * function is executed on each tick.
  306. *
  307. * ```
  308. * it ('test with nested setTimeout', fakeAsync(() => {
  309. * let nestedTimeoutInvoked = false;
  310. * function funcWithNestedTimeout() {
  311. * setTimeout(() => {
  312. * nestedTimeoutInvoked = true;
  313. * });
  314. * };
  315. * setTimeout(funcWithNestedTimeout);
  316. * tick();
  317. * expect(nestedTimeoutInvoked).toBe(true);
  318. * }));
  319. * ```
  320. *
  321. * In the following case, `processNewMacroTasksSynchronously` is explicitly
  322. * set to false, so the nested timeout function is not invoked.
  323. *
  324. * ```
  325. * it ('test with nested setTimeout', fakeAsync(() => {
  326. * let nestedTimeoutInvoked = false;
  327. * function funcWithNestedTimeout() {
  328. * setTimeout(() => {
  329. * nestedTimeoutInvoked = true;
  330. * });
  331. * };
  332. * setTimeout(funcWithNestedTimeout);
  333. * tick(0, {processNewMacroTasksSynchronously: false});
  334. * expect(nestedTimeoutInvoked).toBe(false);
  335. * }));
  336. * ```
  337. *
  338. *
  339. * @publicApi
  340. */
  341. function tick(millis = 0, tickOptions = {
  342. processNewMacroTasksSynchronously: true
  343. }) {
  344. if (fakeAsyncTestModule) {
  345. return fakeAsyncTestModule.tick(millis, tickOptions);
  346. }
  347. throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
  348. }
  349. /**
  350. * Flushes any pending microtasks and simulates the asynchronous passage of time for the timers in
  351. * the `fakeAsync` zone by
  352. * draining the macrotask queue until it is empty.
  353. *
  354. * @param maxTurns The maximum number of times the scheduler attempts to clear its queue before
  355. * throwing an error.
  356. * @returns The simulated time elapsed, in milliseconds.
  357. *
  358. * @publicApi
  359. */
  360. function flush(maxTurns) {
  361. if (fakeAsyncTestModule) {
  362. return fakeAsyncTestModule.flush(maxTurns);
  363. }
  364. throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
  365. }
  366. /**
  367. * Discard all remaining periodic tasks.
  368. *
  369. * @publicApi
  370. */
  371. function discardPeriodicTasks() {
  372. if (fakeAsyncTestModule) {
  373. return fakeAsyncTestModule.discardPeriodicTasks();
  374. }
  375. throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
  376. }
  377. /**
  378. * Flush any pending microtasks.
  379. *
  380. * @publicApi
  381. */
  382. function flushMicrotasks() {
  383. if (fakeAsyncTestModule) {
  384. return fakeAsyncTestModule.flushMicrotasks();
  385. }
  386. throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
  387. }
  388. /** Whether test modules should be torn down by default. */
  389. const TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT = true;
  390. /** Whether unknown elements in templates should throw by default. */
  391. const THROW_ON_UNKNOWN_ELEMENTS_DEFAULT = false;
  392. /** Whether unknown properties in templates should throw by default. */
  393. const THROW_ON_UNKNOWN_PROPERTIES_DEFAULT = false;
  394. /**
  395. * An abstract class for inserting the root test component element in a platform independent way.
  396. *
  397. * @publicApi
  398. */
  399. class TestComponentRenderer {
  400. insertRootElement(rootElementId) { }
  401. removeAllRootElements() { }
  402. }
  403. /**
  404. * @publicApi
  405. */
  406. const ComponentFixtureAutoDetect = new InjectionToken$1('ComponentFixtureAutoDetect');
  407. /**
  408. * @publicApi
  409. */
  410. const ComponentFixtureNoNgZone = new InjectionToken$1('ComponentFixtureNoNgZone');
  411. /**
  412. * Used to resolve resource URLs on `@Component` when used with JIT compilation.
  413. *
  414. * Example:
  415. * ```
  416. * @Component({
  417. * selector: 'my-comp',
  418. * templateUrl: 'my-comp.html', // This requires asynchronous resolution
  419. * })
  420. * class MyComponent{
  421. * }
  422. *
  423. * // Calling `renderComponent` will fail because `renderComponent` is a synchronous process
  424. * // and `MyComponent`'s `@Component.templateUrl` needs to be resolved asynchronously.
  425. *
  426. * // Calling `resolveComponentResources()` will resolve `@Component.templateUrl` into
  427. * // `@Component.template`, which allows `renderComponent` to proceed in a synchronous manner.
  428. *
  429. * // Use browser's `fetch()` function as the default resource resolution strategy.
  430. * resolveComponentResources(fetch).then(() => {
  431. * // After resolution all URLs have been converted into `template` strings.
  432. * renderComponent(MyComponent);
  433. * });
  434. *
  435. * ```
  436. *
  437. * NOTE: In AOT the resolution happens during compilation, and so there should be no need
  438. * to call this method outside JIT mode.
  439. *
  440. * @param resourceResolver a function which is responsible for returning a `Promise` to the
  441. * contents of the resolved URL. Browser's `fetch()` method is a good default implementation.
  442. */
  443. function resolveComponentResources(resourceResolver) {
  444. // Store all promises which are fetching the resources.
  445. const componentResolved = [];
  446. // Cache so that we don't fetch the same resource more than once.
  447. const urlMap = new Map();
  448. function cachedResourceResolve(url) {
  449. let promise = urlMap.get(url);
  450. if (!promise) {
  451. const resp = resourceResolver(url);
  452. urlMap.set(url, promise = resp.then(unwrapResponse));
  453. }
  454. return promise;
  455. }
  456. componentResourceResolutionQueue.forEach((component, type) => {
  457. const promises = [];
  458. if (component.templateUrl) {
  459. promises.push(cachedResourceResolve(component.templateUrl).then((template) => {
  460. component.template = template;
  461. }));
  462. }
  463. const styleUrls = component.styleUrls;
  464. const styles = component.styles || (component.styles = []);
  465. const styleOffset = component.styles.length;
  466. styleUrls && styleUrls.forEach((styleUrl, index) => {
  467. styles.push(''); // pre-allocate array.
  468. promises.push(cachedResourceResolve(styleUrl).then((style) => {
  469. styles[styleOffset + index] = style;
  470. styleUrls.splice(styleUrls.indexOf(styleUrl), 1);
  471. if (styleUrls.length == 0) {
  472. component.styleUrls = undefined;
  473. }
  474. }));
  475. });
  476. const fullyResolved = Promise.all(promises).then(() => componentDefResolved(type));
  477. componentResolved.push(fullyResolved);
  478. });
  479. clearResolutionOfComponentResourcesQueue();
  480. return Promise.all(componentResolved).then(() => undefined);
  481. }
  482. let componentResourceResolutionQueue = new Map();
  483. // Track when existing ɵcmp for a Type is waiting on resources.
  484. const componentDefPendingResolution = new Set();
  485. function maybeQueueResolutionOfComponentResources(type, metadata) {
  486. if (componentNeedsResolution(metadata)) {
  487. componentResourceResolutionQueue.set(type, metadata);
  488. componentDefPendingResolution.add(type);
  489. }
  490. }
  491. function isComponentDefPendingResolution(type) {
  492. return componentDefPendingResolution.has(type);
  493. }
  494. function componentNeedsResolution(component) {
  495. return !!((component.templateUrl && !component.hasOwnProperty('template')) ||
  496. component.styleUrls && component.styleUrls.length);
  497. }
  498. function clearResolutionOfComponentResourcesQueue() {
  499. const old = componentResourceResolutionQueue;
  500. componentResourceResolutionQueue = new Map();
  501. return old;
  502. }
  503. function restoreComponentResolutionQueue(queue) {
  504. componentDefPendingResolution.clear();
  505. queue.forEach((_, type) => componentDefPendingResolution.add(type));
  506. componentResourceResolutionQueue = queue;
  507. }
  508. function isComponentResourceResolutionQueueEmpty() {
  509. return componentResourceResolutionQueue.size === 0;
  510. }
  511. function unwrapResponse(response) {
  512. return typeof response == 'string' ? response : response.text();
  513. }
  514. function componentDefResolved(type) {
  515. componentDefPendingResolution.delete(type);
  516. }
  517. const _global = globalThis;
  518. var FactoryTarget;
  519. (function (FactoryTarget) {
  520. FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive";
  521. FactoryTarget[FactoryTarget["Component"] = 1] = "Component";
  522. FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable";
  523. FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe";
  524. FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule";
  525. })(FactoryTarget || (FactoryTarget = {}));
  526. var R3TemplateDependencyKind;
  527. (function (R3TemplateDependencyKind) {
  528. R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive";
  529. R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe";
  530. R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule";
  531. })(R3TemplateDependencyKind || (R3TemplateDependencyKind = {}));
  532. var ViewEncapsulation$1;
  533. (function (ViewEncapsulation) {
  534. ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
  535. // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
  536. ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
  537. ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
  538. })(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
  539. function getCompilerFacade(request) {
  540. const globalNg = _global['ng'];
  541. if (globalNg && globalNg.ɵcompilerFacade) {
  542. return globalNg.ɵcompilerFacade;
  543. }
  544. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  545. // Log the type as an error so that a developer can easily navigate to the type from the
  546. // console.
  547. console.error(`JIT compilation failed for ${request.kind}`, request.type);
  548. let message = `The ${request.kind} '${request
  549. .type.name}' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.\n\n`;
  550. if (request.usage === 1 /* JitCompilerUsage.PartialDeclaration */) {
  551. message += `The ${request.kind} is part of a library that has been partially compiled.\n`;
  552. message +=
  553. `However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.\n`;
  554. message += '\n';
  555. message +=
  556. `Ideally, the library is processed using the Angular Linker to become fully AOT compiled.\n`;
  557. }
  558. else {
  559. message +=
  560. `JIT compilation is discouraged for production use-cases! Consider using AOT mode instead.\n`;
  561. }
  562. message +=
  563. `Alternatively, the JIT compiler should be loaded by bootstrapping using '@angular/platform-browser-dynamic' or '@angular/platform-server',\n`;
  564. message +=
  565. `or manually provide the compiler with 'import "@angular/compiler";' before bootstrapping.`;
  566. throw new Error(message);
  567. }
  568. else {
  569. throw new Error('JIT compiler unavailable');
  570. }
  571. }
  572. function getClosureSafeProperty(objWithPropertyToExtract) {
  573. for (let key in objWithPropertyToExtract) {
  574. if (objWithPropertyToExtract[key] === getClosureSafeProperty) {
  575. return key;
  576. }
  577. }
  578. throw Error('Could not find renamed property on target object.');
  579. }
  580. /**
  581. * Sets properties on a target object from a source object, but only if
  582. * the property doesn't already exist on the target object.
  583. * @param target The target to set properties on
  584. * @param source The source of the property keys and values to set
  585. */
  586. function fillProperties(target, source) {
  587. for (const key in source) {
  588. if (source.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
  589. target[key] = source[key];
  590. }
  591. }
  592. }
  593. function stringify(token) {
  594. if (typeof token === 'string') {
  595. return token;
  596. }
  597. if (Array.isArray(token)) {
  598. return '[' + token.map(stringify).join(', ') + ']';
  599. }
  600. if (token == null) {
  601. return '' + token;
  602. }
  603. if (token.overriddenName) {
  604. return `${token.overriddenName}`;
  605. }
  606. if (token.name) {
  607. return `${token.name}`;
  608. }
  609. const res = token.toString();
  610. if (res == null) {
  611. return '' + res;
  612. }
  613. const newLineIndex = res.indexOf('\n');
  614. return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
  615. }
  616. /**
  617. * Concatenates two strings with separator, allocating new strings only when necessary.
  618. *
  619. * @param before before string.
  620. * @param separator separator string.
  621. * @param after after string.
  622. * @returns concatenated string.
  623. */
  624. function concatStringsWithSpace(before, after) {
  625. return (before == null || before === '') ?
  626. (after === null ? '' : after) :
  627. ((after == null || after === '') ? before : before + ' ' + after);
  628. }
  629. const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty });
  630. /**
  631. * Allows to refer to references which are not yet defined.
  632. *
  633. * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
  634. * DI is declared, but not yet defined. It is also used when the `token` which we use when creating
  635. * a query is not yet defined.
  636. *
  637. * `forwardRef` is also used to break circularities in standalone components imports.
  638. *
  639. * @usageNotes
  640. * ### Circular dependency example
  641. * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
  642. *
  643. * ### Circular standalone reference import example
  644. * ```ts
  645. * @Component({
  646. * standalone: true,
  647. * imports: [ChildComponent],
  648. * selector: 'app-parent',
  649. * template: `<app-child [hideParent]="hideParent"></app-child>`,
  650. * })
  651. * export class ParentComponent {
  652. * @Input() hideParent: boolean;
  653. * }
  654. *
  655. *
  656. * @Component({
  657. * standalone: true,
  658. * imports: [CommonModule, forwardRef(() => ParentComponent)],
  659. * selector: 'app-child',
  660. * template: `<app-parent *ngIf="!hideParent"></app-parent>`,
  661. * })
  662. * export class ChildComponent {
  663. * @Input() hideParent: boolean;
  664. * }
  665. * ```
  666. *
  667. * @publicApi
  668. */
  669. function forwardRef(forwardRefFn) {
  670. forwardRefFn.__forward_ref__ = forwardRef;
  671. forwardRefFn.toString = function () {
  672. return stringify(this());
  673. };
  674. return forwardRefFn;
  675. }
  676. /**
  677. * Lazily retrieves the reference value from a forwardRef.
  678. *
  679. * Acts as the identity function when given a non-forward-ref value.
  680. *
  681. * @usageNotes
  682. * ### Example
  683. *
  684. * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
  685. *
  686. * @see {@link forwardRef}
  687. * @publicApi
  688. */
  689. function resolveForwardRef(type) {
  690. return isForwardRef(type) ? type() : type;
  691. }
  692. /** Checks whether a function is wrapped by a `forwardRef`. */
  693. function isForwardRef(fn) {
  694. return typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
  695. fn.__forward_ref__ === forwardRef;
  696. }
  697. /**
  698. * Construct an injectable definition which defines how a token will be constructed by the DI
  699. * system, and in which injectors (if any) it will be available.
  700. *
  701. * This should be assigned to a static `ɵprov` field on a type, which will then be an
  702. * `InjectableType`.
  703. *
  704. * Options:
  705. * * `providedIn` determines which injectors will include the injectable, by either associating it
  706. * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
  707. * provided in the `'root'` injector, which will be the application-level injector in most apps.
  708. * * `factory` gives the zero argument function which will create an instance of the injectable.
  709. * The factory can call [`inject`](api/core/inject) to access the `Injector` and request injection
  710. * of dependencies.
  711. *
  712. * @codeGenApi
  713. * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm.
  714. */
  715. function ɵɵdefineInjectable(opts) {
  716. return {
  717. token: opts.token,
  718. providedIn: opts.providedIn || null,
  719. factory: opts.factory,
  720. value: undefined,
  721. };
  722. }
  723. /**
  724. * @deprecated in v8, delete after v10. This API should be used only by generated code, and that
  725. * code should now use ɵɵdefineInjectable instead.
  726. * @publicApi
  727. */
  728. const defineInjectable = ɵɵdefineInjectable;
  729. /**
  730. * Construct an `InjectorDef` which configures an injector.
  731. *
  732. * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
  733. * `InjectorType`.
  734. *
  735. * Options:
  736. *
  737. * * `providers`: an optional array of providers to add to the injector. Each provider must
  738. * either have a factory or point to a type which has a `ɵprov` static property (the
  739. * type must be an `InjectableType`).
  740. * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
  741. * whose providers will also be added to the injector. Locally provided types will override
  742. * providers from imports.
  743. *
  744. * @codeGenApi
  745. */
  746. function ɵɵdefineInjector(options) {
  747. return { providers: options.providers || [], imports: options.imports || [] };
  748. }
  749. /**
  750. * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
  751. * inherited value.
  752. *
  753. * @param type A type which may have its own (non-inherited) `ɵprov`.
  754. */
  755. function getInjectableDef(type) {
  756. return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF);
  757. }
  758. function isInjectable(type) {
  759. return getInjectableDef(type) !== null;
  760. }
  761. /**
  762. * Return definition only if it is defined directly on `type` and is not inherited from a base
  763. * class of `type`.
  764. */
  765. function getOwnDefinition(type, field) {
  766. return type.hasOwnProperty(field) ? type[field] : null;
  767. }
  768. /**
  769. * Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors.
  770. *
  771. * @param type A type which may have `ɵprov`, via inheritance.
  772. *
  773. * @deprecated Will be removed in a future version of Angular, where an error will occur in the
  774. * scenario if we find the `ɵprov` on an ancestor only.
  775. */
  776. function getInheritedInjectableDef(type) {
  777. const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF]);
  778. if (def) {
  779. ngDevMode &&
  780. console.warn(`DEPRECATED: DI is instantiating a token "${type.name}" that inherits its @Injectable decorator but does not provide one itself.\n` +
  781. `This will become an error in a future version of Angular. Please add @Injectable() to the "${type.name}" class.`);
  782. return def;
  783. }
  784. else {
  785. return null;
  786. }
  787. }
  788. /**
  789. * Read the injector def type in a way which is immune to accidentally reading inherited value.
  790. *
  791. * @param type type which may have an injector def (`ɵinj`)
  792. */
  793. function getInjectorDef(type) {
  794. return type && (type.hasOwnProperty(NG_INJ_DEF) || type.hasOwnProperty(NG_INJECTOR_DEF)) ?
  795. type[NG_INJ_DEF] :
  796. null;
  797. }
  798. const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty });
  799. const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty });
  800. // We need to keep these around so we can read off old defs if new defs are unavailable
  801. const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty });
  802. const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty });
  803. /**
  804. * Base URL for the error details page.
  805. *
  806. * Keep this constant in sync across:
  807. * - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts
  808. * - packages/core/src/error_details_base_url.ts
  809. */
  810. const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
  811. /**
  812. * URL for the XSS security documentation.
  813. */
  814. const XSS_SECURITY_URL = 'https://g.co/ng/security#xss';
  815. /**
  816. * Class that represents a runtime error.
  817. * Formats and outputs the error message in a consistent way.
  818. *
  819. * Example:
  820. * ```
  821. * throw new RuntimeError(
  822. * RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED,
  823. * ngDevMode && 'Injector has already been destroyed.');
  824. * ```
  825. *
  826. * Note: the `message` argument contains a descriptive error message as a string in development
  827. * mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the
  828. * `message` argument becomes `false`, thus we account for it in the typings and the runtime
  829. * logic.
  830. */
  831. class RuntimeError extends Error {
  832. constructor(code, message) {
  833. super(formatRuntimeError(code, message));
  834. this.code = code;
  835. }
  836. }
  837. /**
  838. * Called to format a runtime error.
  839. * See additional info on the `message` argument type in the `RuntimeError` class description.
  840. */
  841. function formatRuntimeError(code, message) {
  842. // Error code might be a negative number, which is a special marker that instructs the logic to
  843. // generate a link to the error details page on angular.io.
  844. // We also prepend `0` to non-compile-time errors.
  845. const fullCode = `NG0${Math.abs(code)}`;
  846. let errorMessage = `${fullCode}${message ? ': ' + message : ''}`;
  847. if (ngDevMode && code < 0) {
  848. const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/);
  849. const separator = addPeriodSeparator ? '.' : '';
  850. errorMessage =
  851. `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
  852. }
  853. return errorMessage;
  854. }
  855. /**
  856. * @description
  857. *
  858. * Represents a type that a Component or other object is instances of.
  859. *
  860. * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by
  861. * the `MyCustomComponent` constructor function.
  862. *
  863. * @publicApi
  864. */
  865. const Type = Function;
  866. function isType(v) {
  867. return typeof v === 'function';
  868. }
  869. // The functions in this file verify that the assumptions we are making
  870. function assertNumber(actual, msg) {
  871. if (!(typeof actual === 'number')) {
  872. throwError(msg, typeof actual, 'number', '===');
  873. }
  874. }
  875. function assertNumberInRange(actual, minInclusive, maxInclusive) {
  876. assertNumber(actual, 'Expected a number');
  877. assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to');
  878. assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to');
  879. }
  880. function assertString(actual, msg) {
  881. if (!(typeof actual === 'string')) {
  882. throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
  883. }
  884. }
  885. function assertFunction(actual, msg) {
  886. if (!(typeof actual === 'function')) {
  887. throwError(msg, actual === null ? 'null' : typeof actual, 'function', '===');
  888. }
  889. }
  890. function assertEqual(actual, expected, msg) {
  891. if (!(actual == expected)) {
  892. throwError(msg, actual, expected, '==');
  893. }
  894. }
  895. function assertNotEqual(actual, expected, msg) {
  896. if (!(actual != expected)) {
  897. throwError(msg, actual, expected, '!=');
  898. }
  899. }
  900. function assertSame(actual, expected, msg) {
  901. if (!(actual === expected)) {
  902. throwError(msg, actual, expected, '===');
  903. }
  904. }
  905. function assertNotSame(actual, expected, msg) {
  906. if (!(actual !== expected)) {
  907. throwError(msg, actual, expected, '!==');
  908. }
  909. }
  910. function assertLessThan(actual, expected, msg) {
  911. if (!(actual < expected)) {
  912. throwError(msg, actual, expected, '<');
  913. }
  914. }
  915. function assertLessThanOrEqual(actual, expected, msg) {
  916. if (!(actual <= expected)) {
  917. throwError(msg, actual, expected, '<=');
  918. }
  919. }
  920. function assertGreaterThan(actual, expected, msg) {
  921. if (!(actual > expected)) {
  922. throwError(msg, actual, expected, '>');
  923. }
  924. }
  925. function assertGreaterThanOrEqual(actual, expected, msg) {
  926. if (!(actual >= expected)) {
  927. throwError(msg, actual, expected, '>=');
  928. }
  929. }
  930. function assertNotDefined(actual, msg) {
  931. if (actual != null) {
  932. throwError(msg, actual, null, '==');
  933. }
  934. }
  935. function assertDefined(actual, msg) {
  936. if (actual == null) {
  937. throwError(msg, actual, null, '!=');
  938. }
  939. }
  940. function throwError(msg, actual, expected, comparison) {
  941. throw new Error(`ASSERTION ERROR: ${msg}` +
  942. (comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
  943. }
  944. function assertDomNode(node) {
  945. if (!(node instanceof Node)) {
  946. throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`);
  947. }
  948. }
  949. function assertIndexInRange(arr, index) {
  950. assertDefined(arr, 'Array must be defined.');
  951. const maxLen = arr.length;
  952. if (index < 0 || index >= maxLen) {
  953. throwError(`Index expected to be less than ${maxLen} but got ${index}`);
  954. }
  955. }
  956. function assertOneOf(value, ...validValues) {
  957. if (validValues.indexOf(value) !== -1)
  958. return true;
  959. throwError(`Expected value to be one of ${JSON.stringify(validValues)} but was ${JSON.stringify(value)}.`);
  960. }
  961. /**
  962. * Determines if the contents of two arrays is identical
  963. *
  964. * @param a first array
  965. * @param b second array
  966. * @param identityAccessor Optional function for extracting stable object identity from a value in
  967. * the array.
  968. */
  969. function arrayEquals(a, b, identityAccessor) {
  970. if (a.length !== b.length)
  971. return false;
  972. for (let i = 0; i < a.length; i++) {
  973. let valueA = a[i];
  974. let valueB = b[i];
  975. if (identityAccessor) {
  976. valueA = identityAccessor(valueA);
  977. valueB = identityAccessor(valueB);
  978. }
  979. if (valueB !== valueA) {
  980. return false;
  981. }
  982. }
  983. return true;
  984. }
  985. /**
  986. * Flattens an array.
  987. */
  988. function flatten$1(list) {
  989. return list.flat(Number.POSITIVE_INFINITY);
  990. }
  991. function deepForEach(input, fn) {
  992. input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value));
  993. }
  994. function addToArray(arr, index, value) {
  995. // perf: array.push is faster than array.splice!
  996. if (index >= arr.length) {
  997. arr.push(value);
  998. }
  999. else {
  1000. arr.splice(index, 0, value);
  1001. }
  1002. }
  1003. function removeFromArray(arr, index) {
  1004. // perf: array.pop is faster than array.splice!
  1005. if (index >= arr.length - 1) {
  1006. return arr.pop();
  1007. }
  1008. else {
  1009. return arr.splice(index, 1)[0];
  1010. }
  1011. }
  1012. function newArray(size, value) {
  1013. const list = [];
  1014. for (let i = 0; i < size; i++) {
  1015. list.push(value);
  1016. }
  1017. return list;
  1018. }
  1019. /**
  1020. * Remove item from array (Same as `Array.splice()` but faster.)
  1021. *
  1022. * `Array.splice()` is not as fast because it has to allocate an array for the elements which were
  1023. * removed. This causes memory pressure and slows down code when most of the time we don't
  1024. * care about the deleted items array.
  1025. *
  1026. * https://jsperf.com/fast-array-splice (About 20x faster)
  1027. *
  1028. * @param array Array to splice
  1029. * @param index Index of element in array to remove.
  1030. * @param count Number of items to remove.
  1031. */
  1032. function arraySplice(array, index, count) {
  1033. const length = array.length - count;
  1034. while (index < length) {
  1035. array[index] = array[index + count];
  1036. index++;
  1037. }
  1038. while (count--) {
  1039. array.pop(); // shrink the array
  1040. }
  1041. }
  1042. /**
  1043. * Same as `Array.splice(index, 0, value)` but faster.
  1044. *
  1045. * `Array.splice()` is not fast because it has to allocate an array for the elements which were
  1046. * removed. This causes memory pressure and slows down code when most of the time we don't
  1047. * care about the deleted items array.
  1048. *
  1049. * @param array Array to splice.
  1050. * @param index Index in array where the `value` should be added.
  1051. * @param value Value to add to array.
  1052. */
  1053. function arrayInsert(array, index, value) {
  1054. ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.');
  1055. let end = array.length;
  1056. while (end > index) {
  1057. const previousEnd = end - 1;
  1058. array[end] = array[previousEnd];
  1059. end = previousEnd;
  1060. }
  1061. array[index] = value;
  1062. }
  1063. /**
  1064. * Same as `Array.splice2(index, 0, value1, value2)` but faster.
  1065. *
  1066. * `Array.splice()` is not fast because it has to allocate an array for the elements which were
  1067. * removed. This causes memory pressure and slows down code when most of the time we don't
  1068. * care about the deleted items array.
  1069. *
  1070. * @param array Array to splice.
  1071. * @param index Index in array where the `value` should be added.
  1072. * @param value1 Value to add to array.
  1073. * @param value2 Value to add to array.
  1074. */
  1075. function arrayInsert2(array, index, value1, value2) {
  1076. ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.');
  1077. let end = array.length;
  1078. if (end == index) {
  1079. // inserting at the end.
  1080. array.push(value1, value2);
  1081. }
  1082. else if (end === 1) {
  1083. // corner case when we have less items in array than we have items to insert.
  1084. array.push(value2, array[0]);
  1085. array[0] = value1;
  1086. }
  1087. else {
  1088. end--;
  1089. array.push(array[end - 1], array[end]);
  1090. while (end > index) {
  1091. const previousEnd = end - 2;
  1092. array[end] = array[previousEnd];
  1093. end--;
  1094. }
  1095. array[index] = value1;
  1096. array[index + 1] = value2;
  1097. }
  1098. }
  1099. /**
  1100. * Get an index of an `value` in a sorted `array`.
  1101. *
  1102. * NOTE:
  1103. * - This uses binary search algorithm for fast removals.
  1104. *
  1105. * @param array A sorted array to binary search.
  1106. * @param value The value to look for.
  1107. * @returns index of the value.
  1108. * - positive index if value found.
  1109. * - negative index if value not found. (`~index` to get the value where it should have been
  1110. * located)
  1111. */
  1112. function arrayIndexOfSorted(array, value) {
  1113. return _arrayIndexOfSorted(array, value, 0);
  1114. }
  1115. /**
  1116. * Set a `value` for a `key`.
  1117. *
  1118. * @param keyValueArray to modify.
  1119. * @param key The key to locate or create.
  1120. * @param value The value to set for a `key`.
  1121. * @returns index (always even) of where the value vas set.
  1122. */
  1123. function keyValueArraySet(keyValueArray, key, value) {
  1124. let index = keyValueArrayIndexOf(keyValueArray, key);
  1125. if (index >= 0) {
  1126. // if we found it set it.
  1127. keyValueArray[index | 1] = value;
  1128. }
  1129. else {
  1130. index = ~index;
  1131. arrayInsert2(keyValueArray, index, key, value);
  1132. }
  1133. return index;
  1134. }
  1135. /**
  1136. * Retrieve a `value` for a `key` (on `undefined` if not found.)
  1137. *
  1138. * @param keyValueArray to search.
  1139. * @param key The key to locate.
  1140. * @return The `value` stored at the `key` location or `undefined if not found.
  1141. */
  1142. function keyValueArrayGet(keyValueArray, key) {
  1143. const index = keyValueArrayIndexOf(keyValueArray, key);
  1144. if (index >= 0) {
  1145. // if we found it retrieve it.
  1146. return keyValueArray[index | 1];
  1147. }
  1148. return undefined;
  1149. }
  1150. /**
  1151. * Retrieve a `key` index value in the array or `-1` if not found.
  1152. *
  1153. * @param keyValueArray to search.
  1154. * @param key The key to locate.
  1155. * @returns index of where the key is (or should have been.)
  1156. * - positive (even) index if key found.
  1157. * - negative index if key not found. (`~index` (even) to get the index where it should have
  1158. * been inserted.)
  1159. */
  1160. function keyValueArrayIndexOf(keyValueArray, key) {
  1161. return _arrayIndexOfSorted(keyValueArray, key, 1);
  1162. }
  1163. /**
  1164. * Delete a `key` (and `value`) from the `KeyValueArray`.
  1165. *
  1166. * @param keyValueArray to modify.
  1167. * @param key The key to locate or delete (if exist).
  1168. * @returns index of where the key was (or should have been.)
  1169. * - positive (even) index if key found and deleted.
  1170. * - negative index if key not found. (`~index` (even) to get the index where it should have
  1171. * been.)
  1172. */
  1173. function keyValueArrayDelete(keyValueArray, key) {
  1174. const index = keyValueArrayIndexOf(keyValueArray, key);
  1175. if (index >= 0) {
  1176. // if we found it remove it.
  1177. arraySplice(keyValueArray, index, 2);
  1178. }
  1179. return index;
  1180. }
  1181. /**
  1182. * INTERNAL: Get an index of an `value` in a sorted `array` by grouping search by `shift`.
  1183. *
  1184. * NOTE:
  1185. * - This uses binary search algorithm for fast removals.
  1186. *
  1187. * @param array A sorted array to binary search.
  1188. * @param value The value to look for.
  1189. * @param shift grouping shift.
  1190. * - `0` means look at every location
  1191. * - `1` means only look at every other (even) location (the odd locations are to be ignored as
  1192. * they are values.)
  1193. * @returns index of the value.
  1194. * - positive index if value found.
  1195. * - negative index if value not found. (`~index` to get the value where it should have been
  1196. * inserted)
  1197. */
  1198. function _arrayIndexOfSorted(array, value, shift) {
  1199. ngDevMode && assertEqual(Array.isArray(array), true, 'Expecting an array');
  1200. let start = 0;
  1201. let end = array.length >> shift;
  1202. while (end !== start) {
  1203. const middle = start + ((end - start) >> 1); // find the middle.
  1204. const current = array[middle << shift];
  1205. if (value === current) {
  1206. return (middle << shift);
  1207. }
  1208. else if (current > value) {
  1209. end = middle;
  1210. }
  1211. else {
  1212. start = middle + 1; // We already searched middle so make it non-inclusive by adding 1
  1213. }
  1214. }
  1215. return ~(end << shift);
  1216. }
  1217. /**
  1218. * Convince closure compiler that the wrapped function has no side-effects.
  1219. *
  1220. * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to
  1221. * allow us to execute a function but have closure compiler mark the call as no-side-effects.
  1222. * It is important that the return value for the `noSideEffects` function be assigned
  1223. * to something which is retained otherwise the call to `noSideEffects` will be removed by closure
  1224. * compiler.
  1225. */
  1226. function noSideEffects(fn) {
  1227. return { toString: fn }.toString();
  1228. }
  1229. const ANNOTATIONS = '__annotations__';
  1230. const PARAMETERS = '__parameters__';
  1231. const PROP_METADATA = '__prop__metadata__';
  1232. /**
  1233. * @suppress {globalThis}
  1234. */
  1235. function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) {
  1236. return noSideEffects(() => {
  1237. const metaCtor = makeMetadataCtor(props);
  1238. function DecoratorFactory(...args) {
  1239. if (this instanceof DecoratorFactory) {
  1240. metaCtor.call(this, ...args);
  1241. return this;
  1242. }
  1243. const annotationInstance = new DecoratorFactory(...args);
  1244. return function TypeDecorator(cls) {
  1245. if (typeFn)
  1246. typeFn(cls, ...args);
  1247. // Use of Object.defineProperty is important since it creates non-enumerable property which
  1248. // prevents the property is copied during subclassing.
  1249. const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
  1250. cls[ANNOTATIONS] :
  1251. Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS];
  1252. annotations.push(annotationInstance);
  1253. if (additionalProcessing)
  1254. additionalProcessing(cls);
  1255. return cls;
  1256. };
  1257. }
  1258. if (parentClass) {
  1259. DecoratorFactory.prototype = Object.create(parentClass.prototype);
  1260. }
  1261. DecoratorFactory.prototype.ngMetadataName = name;
  1262. DecoratorFactory.annotationCls = DecoratorFactory;
  1263. return DecoratorFactory;
  1264. });
  1265. }
  1266. function makeMetadataCtor(props) {
  1267. return function ctor(...args) {
  1268. if (props) {
  1269. const values = props(...args);
  1270. for (const propName in values) {
  1271. this[propName] = values[propName];
  1272. }
  1273. }
  1274. };
  1275. }
  1276. function makeParamDecorator(name, props, parentClass) {
  1277. return noSideEffects(() => {
  1278. const metaCtor = makeMetadataCtor(props);
  1279. function ParamDecoratorFactory(...args) {
  1280. if (this instanceof ParamDecoratorFactory) {
  1281. metaCtor.apply(this, args);
  1282. return this;
  1283. }
  1284. const annotationInstance = new ParamDecoratorFactory(...args);
  1285. ParamDecorator.annotation = annotationInstance;
  1286. return ParamDecorator;
  1287. function ParamDecorator(cls, unusedKey, index) {
  1288. // Use of Object.defineProperty is important since it creates non-enumerable property which
  1289. // prevents the property is copied during subclassing.
  1290. const parameters = cls.hasOwnProperty(PARAMETERS) ?
  1291. cls[PARAMETERS] :
  1292. Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS];
  1293. // there might be gaps if some in between parameters do not have annotations.
  1294. // we pad with nulls.
  1295. while (parameters.length <= index) {
  1296. parameters.push(null);
  1297. }
  1298. (parameters[index] = parameters[index] || []).push(annotationInstance);
  1299. return cls;
  1300. }
  1301. }
  1302. if (parentClass) {
  1303. ParamDecoratorFactory.prototype = Object.create(parentClass.prototype);
  1304. }
  1305. ParamDecoratorFactory.prototype.ngMetadataName = name;
  1306. ParamDecoratorFactory.annotationCls = ParamDecoratorFactory;
  1307. return ParamDecoratorFactory;
  1308. });
  1309. }
  1310. function makePropDecorator(name, props, parentClass, additionalProcessing) {
  1311. return noSideEffects(() => {
  1312. const metaCtor = makeMetadataCtor(props);
  1313. function PropDecoratorFactory(...args) {
  1314. if (this instanceof PropDecoratorFactory) {
  1315. metaCtor.apply(this, args);
  1316. return this;
  1317. }
  1318. const decoratorInstance = new PropDecoratorFactory(...args);
  1319. function PropDecorator(target, name) {
  1320. // target is undefined with standard decorators. This case is not supported and will throw
  1321. // if this decorator is used in JIT mode with standard decorators.
  1322. if (target === undefined) {
  1323. throw new Error('Standard Angular field decorators are not supported in JIT mode.');
  1324. }
  1325. const constructor = target.constructor;
  1326. // Use of Object.defineProperty is important because it creates a non-enumerable property
  1327. // which prevents the property from being copied during subclassing.
  1328. const meta = constructor.hasOwnProperty(PROP_METADATA) ?
  1329. constructor[PROP_METADATA] :
  1330. Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA];
  1331. meta[name] = meta.hasOwnProperty(name) && meta[name] || [];
  1332. meta[name].unshift(decoratorInstance);
  1333. if (additionalProcessing)
  1334. additionalProcessing(target, name, ...args);
  1335. }
  1336. return PropDecorator;
  1337. }
  1338. if (parentClass) {
  1339. PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
  1340. }
  1341. PropDecoratorFactory.prototype.ngMetadataName = name;
  1342. PropDecoratorFactory.annotationCls = PropDecoratorFactory;
  1343. return PropDecoratorFactory;
  1344. });
  1345. }
  1346. /*
  1347. * #########################
  1348. * Attention: These Regular expressions have to hold even if the code is minified!
  1349. * ##########################
  1350. */
  1351. /**
  1352. * Regular expression that detects pass-through constructors for ES5 output. This Regex
  1353. * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also
  1354. * it intends to capture the pattern where existing constructors have been downleveled from
  1355. * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g.
  1356. *
  1357. * ```
  1358. * function MyClass() {
  1359. * var _this = _super.apply(this, arguments) || this;
  1360. * ```
  1361. *
  1362. * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2:
  1363. * ```
  1364. * function MyClass() {
  1365. * var _this = _super.apply(this, __spread(arguments)) || this;
  1366. * ```
  1367. *
  1368. * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2:
  1369. * ```
  1370. * function MyClass() {
  1371. * var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this;
  1372. * ```
  1373. *
  1374. * More details can be found in: https://github.com/angular/angular/issues/38453.
  1375. */
  1376. const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\).*)\)/;
  1377. /** Regular expression that detects ES2015 classes which extend from other classes. */
  1378. const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/;
  1379. /**
  1380. * Regular expression that detects ES2015 classes which extend from other classes and
  1381. * have an explicit constructor defined.
  1382. */
  1383. const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/;
  1384. /**
  1385. * Regular expression that detects ES2015 classes which extend from other classes
  1386. * and inherit a constructor.
  1387. */
  1388. const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/;
  1389. /**
  1390. * Determine whether a stringified type is a class which delegates its constructor
  1391. * to its parent.
  1392. *
  1393. * This is not trivial since compiled code can actually contain a constructor function
  1394. * even if the original source code did not. For instance, when the child class contains
  1395. * an initialized instance property.
  1396. */
  1397. function isDelegateCtor(typeStr) {
  1398. return ES5_DELEGATE_CTOR.test(typeStr) ||
  1399. ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) ||
  1400. (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr));
  1401. }
  1402. class ReflectionCapabilities {
  1403. constructor(reflect) {
  1404. this._reflect = reflect || _global['Reflect'];
  1405. }
  1406. factory(t) {
  1407. return (...args) => new t(...args);
  1408. }
  1409. /** @internal */
  1410. _zipTypesAndAnnotations(paramTypes, paramAnnotations) {
  1411. let result;
  1412. if (typeof paramTypes === 'undefined') {
  1413. result = newArray(paramAnnotations.length);
  1414. }
  1415. else {
  1416. result = newArray(paramTypes.length);
  1417. }
  1418. for (let i = 0; i < result.length; i++) {
  1419. // TS outputs Object for parameters without types, while Traceur omits
  1420. // the annotations. For now we preserve the Traceur behavior to aid
  1421. // migration, but this can be revisited.
  1422. if (typeof paramTypes === 'undefined') {
  1423. result[i] = [];
  1424. }
  1425. else if (paramTypes[i] && paramTypes[i] != Object) {
  1426. result[i] = [paramTypes[i]];
  1427. }
  1428. else {
  1429. result[i] = [];
  1430. }
  1431. if (paramAnnotations && paramAnnotations[i] != null) {
  1432. result[i] = result[i].concat(paramAnnotations[i]);
  1433. }
  1434. }
  1435. return result;
  1436. }
  1437. _ownParameters(type, parentCtor) {
  1438. const typeStr = type.toString();
  1439. // If we have no decorators, we only have function.length as metadata.
  1440. // In that case, to detect whether a child class declared an own constructor or not,
  1441. // we need to look inside of that constructor to check whether it is
  1442. // just calling the parent.
  1443. // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
  1444. // that sets 'design:paramtypes' to []
  1445. // if a class inherits from another class but has no ctor declared itself.
  1446. if (isDelegateCtor(typeStr)) {
  1447. return null;
  1448. }
  1449. // Prefer the direct API.
  1450. if (type.parameters && type.parameters !== parentCtor.parameters) {
  1451. return type.parameters;
  1452. }
  1453. // API of tsickle for lowering decorators to properties on the class.
  1454. const tsickleCtorParams = type.ctorParameters;
  1455. if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) {
  1456. // Newer tsickle uses a function closure
  1457. // Retain the non-function case for compatibility with older tsickle
  1458. const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams;
  1459. const paramTypes = ctorParameters.map((ctorParam) => ctorParam && ctorParam.type);
  1460. const paramAnnotations = ctorParameters.map((ctorParam) => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
  1461. return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
  1462. }
  1463. // API for metadata created by invoking the decorators.
  1464. const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS];
  1465. const paramTypes = this._reflect && this._reflect.getOwnMetadata &&
  1466. this._reflect.getOwnMetadata('design:paramtypes', type);
  1467. if (paramTypes || paramAnnotations) {
  1468. return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
  1469. }
  1470. // If a class has no decorators, at least create metadata
  1471. // based on function.length.
  1472. // Note: We know that this is a real constructor as we checked
  1473. // the content of the constructor above.
  1474. return newArray(type.length);
  1475. }
  1476. parameters(type) {
  1477. // Note: only report metadata if we have at least one class decorator
  1478. // to stay in sync with the static reflector.
  1479. if (!isType(type)) {
  1480. return [];
  1481. }
  1482. const parentCtor = getParentCtor(type);
  1483. let parameters = this._ownParameters(type, parentCtor);
  1484. if (!parameters && parentCtor !== Object) {
  1485. parameters = this.parameters(parentCtor);
  1486. }
  1487. return parameters || [];
  1488. }
  1489. _ownAnnotations(typeOrFunc, parentCtor) {
  1490. // Prefer the direct API.
  1491. if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) {
  1492. let annotations = typeOrFunc.annotations;
  1493. if (typeof annotations === 'function' && annotations.annotations) {
  1494. annotations = annotations.annotations;
  1495. }
  1496. return annotations;
  1497. }
  1498. // API of tsickle for lowering decorators to properties on the class.
  1499. if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) {
  1500. return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators);
  1501. }
  1502. // API for metadata created by invoking the decorators.
  1503. if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
  1504. return typeOrFunc[ANNOTATIONS];
  1505. }
  1506. return null;
  1507. }
  1508. annotations(typeOrFunc) {
  1509. if (!isType(typeOrFunc)) {
  1510. return [];
  1511. }
  1512. const parentCtor = getParentCtor(typeOrFunc);
  1513. const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || [];
  1514. const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : [];
  1515. return parentAnnotations.concat(ownAnnotations);
  1516. }
  1517. _ownPropMetadata(typeOrFunc, parentCtor) {
  1518. // Prefer the direct API.
  1519. if (typeOrFunc.propMetadata &&
  1520. typeOrFunc.propMetadata !== parentCtor.propMetadata) {
  1521. let propMetadata = typeOrFunc.propMetadata;
  1522. if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
  1523. propMetadata = propMetadata.propMetadata;
  1524. }
  1525. return propMetadata;
  1526. }
  1527. // API of tsickle for lowering decorators to properties on the class.
  1528. if (typeOrFunc.propDecorators &&
  1529. typeOrFunc.propDecorators !== parentCtor.propDecorators) {
  1530. const propDecorators = typeOrFunc.propDecorators;
  1531. const propMetadata = {};
  1532. Object.keys(propDecorators).forEach(prop => {
  1533. propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
  1534. });
  1535. return propMetadata;
  1536. }
  1537. // API for metadata created by invoking the decorators.
  1538. if (typeOrFunc.hasOwnProperty(PROP_METADATA)) {
  1539. return typeOrFunc[PROP_METADATA];
  1540. }
  1541. return null;
  1542. }
  1543. propMetadata(typeOrFunc) {
  1544. if (!isType(typeOrFunc)) {
  1545. return {};
  1546. }
  1547. const parentCtor = getParentCtor(typeOrFunc);
  1548. const propMetadata = {};
  1549. if (parentCtor !== Object) {
  1550. const parentPropMetadata = this.propMetadata(parentCtor);
  1551. Object.keys(parentPropMetadata).forEach((propName) => {
  1552. propMetadata[propName] = parentPropMetadata[propName];
  1553. });
  1554. }
  1555. const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor);
  1556. if (ownPropMetadata) {
  1557. Object.keys(ownPropMetadata).forEach((propName) => {
  1558. const decorators = [];
  1559. if (propMetadata.hasOwnProperty(propName)) {
  1560. decorators.push(...propMetadata[propName]);
  1561. }
  1562. decorators.push(...ownPropMetadata[propName]);
  1563. propMetadata[propName] = decorators;
  1564. });
  1565. }
  1566. return propMetadata;
  1567. }
  1568. ownPropMetadata(typeOrFunc) {
  1569. if (!isType(typeOrFunc)) {
  1570. return {};
  1571. }
  1572. return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {};
  1573. }
  1574. hasLifecycleHook(type, lcProperty) {
  1575. return type instanceof Type && lcProperty in type.prototype;
  1576. }
  1577. }
  1578. function convertTsickleDecoratorIntoMetadata(decoratorInvocations) {
  1579. if (!decoratorInvocations) {
  1580. return [];
  1581. }
  1582. return decoratorInvocations.map(decoratorInvocation => {
  1583. const decoratorType = decoratorInvocation.type;
  1584. const annotationCls = decoratorType.annotationCls;
  1585. const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
  1586. return new annotationCls(...annotationArgs);
  1587. });
  1588. }
  1589. function getParentCtor(ctor) {
  1590. const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null;
  1591. const parentCtor = parentProto ? parentProto.constructor : null;
  1592. // Note: We always use `Object` as the null value
  1593. // to simplify checking later on.
  1594. return parentCtor || Object;
  1595. }
  1596. function ngDevModeResetPerfCounters() {
  1597. const locationString = typeof location !== 'undefined' ? location.toString() : '';
  1598. const newCounters = {
  1599. namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1,
  1600. firstCreatePass: 0,
  1601. tNode: 0,
  1602. tView: 0,
  1603. rendererCreateTextNode: 0,
  1604. rendererSetText: 0,
  1605. rendererCreateElement: 0,
  1606. rendererAddEventListener: 0,
  1607. rendererSetAttribute: 0,
  1608. rendererRemoveAttribute: 0,
  1609. rendererSetProperty: 0,
  1610. rendererSetClassName: 0,
  1611. rendererAddClass: 0,
  1612. rendererRemoveClass: 0,
  1613. rendererSetStyle: 0,
  1614. rendererRemoveStyle: 0,
  1615. rendererDestroy: 0,
  1616. rendererDestroyNode: 0,
  1617. rendererMoveNode: 0,
  1618. rendererRemoveNode: 0,
  1619. rendererAppendChild: 0,
  1620. rendererInsertBefore: 0,
  1621. rendererCreateComment: 0,
  1622. hydratedNodes: 0,
  1623. hydratedComponents: 0,
  1624. dehydratedViewsRemoved: 0,
  1625. dehydratedViewsCleanupRuns: 0,
  1626. componentsSkippedHydration: 0,
  1627. };
  1628. // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
  1629. const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
  1630. _global['ngDevMode'] = allowNgDevModeTrue && newCounters;
  1631. return newCounters;
  1632. }
  1633. /**
  1634. * This function checks to see if the `ngDevMode` has been set. If yes,
  1635. * then we honor it, otherwise we default to dev mode with additional checks.
  1636. *
  1637. * The idea is that unless we are doing production build where we explicitly
  1638. * set `ngDevMode == false` we should be helping the developer by providing
  1639. * as much early warning and errors as possible.
  1640. *
  1641. * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
  1642. * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
  1643. * is defined for the entire instruction set.
  1644. *
  1645. * When checking `ngDevMode` on toplevel, always init it before referencing it
  1646. * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
  1647. * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
  1648. *
  1649. * Details on possible values for `ngDevMode` can be found on its docstring.
  1650. *
  1651. * NOTE:
  1652. * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`.
  1653. */
  1654. function initNgDevMode() {
  1655. // The below checks are to ensure that calling `initNgDevMode` multiple times does not
  1656. // reset the counters.
  1657. // If the `ngDevMode` is not an object, then it means we have not created the perf counters
  1658. // yet.
  1659. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  1660. if (typeof ngDevMode !== 'object') {
  1661. ngDevModeResetPerfCounters();
  1662. }
  1663. return typeof ngDevMode !== 'undefined' && !!ngDevMode;
  1664. }
  1665. return false;
  1666. }
  1667. let _injectorProfilerContext;
  1668. function getInjectorProfilerContext() {
  1669. !ngDevMode && throwError('getInjectorProfilerContext should never be called in production mode');
  1670. return _injectorProfilerContext;
  1671. }
  1672. function setInjectorProfilerContext(context) {
  1673. !ngDevMode && throwError('setInjectorProfilerContext should never be called in production mode');
  1674. const previous = _injectorProfilerContext;
  1675. _injectorProfilerContext = context;
  1676. return previous;
  1677. }
  1678. let injectorProfilerCallback = null;
  1679. /**
  1680. * Sets the callback function which will be invoked during certain DI events within the
  1681. * runtime (for example: injecting services, creating injectable instances, configuring providers)
  1682. *
  1683. * Warning: this function is *INTERNAL* and should not be relied upon in application's code.
  1684. * The contract of the function might be changed in any release and/or the function can be removed
  1685. * completely.
  1686. *
  1687. * @param profiler function provided by the caller or null value to disable profiling.
  1688. */
  1689. const setInjectorProfiler = (injectorProfiler) => {
  1690. !ngDevMode && throwError('setInjectorProfiler should never be called in production mode');
  1691. injectorProfilerCallback = injectorProfiler;
  1692. };
  1693. /**
  1694. * Injector profiler function which emits on DI events executed by the runtime.
  1695. *
  1696. * @param event InjectorProfilerEvent corresponding to the DI event being emitted
  1697. */
  1698. function injectorProfiler(event) {
  1699. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  1700. if (injectorProfilerCallback != null /* both `null` and `undefined` */) {
  1701. injectorProfilerCallback(event);
  1702. }
  1703. }
  1704. /**
  1705. * Emits an InjectorProfilerEventType.ProviderConfigured to the injector profiler. The data in the
  1706. * emitted event includes the raw provider, as well as the token that provider is providing.
  1707. *
  1708. * @param provider A provider object
  1709. */
  1710. function emitProviderConfiguredEvent(provider, isViewProvider = false) {
  1711. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  1712. injectorProfiler({
  1713. type: 2 /* InjectorProfilerEventType.ProviderConfigured */,
  1714. context: getInjectorProfilerContext(),
  1715. providerRecord: {
  1716. token: typeof provider === 'function' ? provider : resolveForwardRef(provider.provide),
  1717. provider,
  1718. isViewProvider
  1719. }
  1720. });
  1721. }
  1722. /**
  1723. * Emits an event to the injector profiler with the instance that was created. Note that
  1724. * the injector associated with this emission can be accessed by using getDebugInjectContext()
  1725. *
  1726. * @param instance an object created by an injector
  1727. */
  1728. function emitInstanceCreatedByInjectorEvent(instance) {
  1729. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  1730. injectorProfiler({
  1731. type: 1 /* InjectorProfilerEventType.InstanceCreatedByInjector */,
  1732. context: getInjectorProfilerContext(),
  1733. instance: { value: instance }
  1734. });
  1735. }
  1736. /**
  1737. * @param token DI token associated with injected service
  1738. * @param value the instance of the injected service (i.e the result of `inject(token)`)
  1739. * @param flags the flags that the token was injected with
  1740. */
  1741. function emitInjectEvent(token, value, flags) {
  1742. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  1743. injectorProfiler({
  1744. type: 0 /* InjectorProfilerEventType.Inject */,
  1745. context: getInjectorProfilerContext(),
  1746. service: { token, value, flags }
  1747. });
  1748. }
  1749. function runInInjectorProfilerContext(injector, token, callback) {
  1750. !ngDevMode &&
  1751. throwError('runInInjectorProfilerContext should never be called in production mode');
  1752. const prevInjectContext = setInjectorProfilerContext({ injector, token });
  1753. try {
  1754. callback();
  1755. }
  1756. finally {
  1757. setInjectorProfilerContext(prevInjectContext);
  1758. }
  1759. }
  1760. function isEnvironmentProviders(value) {
  1761. return value && !!value.ɵproviders;
  1762. }
  1763. /**
  1764. * Used for stringify render output in Ivy.
  1765. * Important! This function is very performance-sensitive and we should
  1766. * be extra careful not to introduce megamorphic reads in it.
  1767. * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations.
  1768. */
  1769. function renderStringify(value) {
  1770. if (typeof value === 'string')
  1771. return value;
  1772. if (value == null)
  1773. return '';
  1774. // Use `String` so that it invokes the `toString` method of the value. Note that this
  1775. // appears to be faster than calling `value.toString` (see `render_stringify` benchmark).
  1776. return String(value);
  1777. }
  1778. /**
  1779. * Used to stringify a value so that it can be displayed in an error message.
  1780. * Important! This function contains a megamorphic read and should only be
  1781. * used for error messages.
  1782. */
  1783. function stringifyForError(value) {
  1784. if (typeof value === 'function')
  1785. return value.name || value.toString();
  1786. if (typeof value === 'object' && value != null && typeof value.type === 'function') {
  1787. return value.type.name || value.type.toString();
  1788. }
  1789. return renderStringify(value);
  1790. }
  1791. /** Called when directives inject each other (creating a circular dependency) */
  1792. function throwCyclicDependencyError(token, path) {
  1793. const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
  1794. throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, `Circular dependency in DI detected for ${token}${depPath}`);
  1795. }
  1796. function throwMixedMultiProviderError() {
  1797. throw new Error(`Cannot mix multi providers and regular providers`);
  1798. }
  1799. function throwInvalidProviderError(ngModuleType, providers, provider) {
  1800. if (ngModuleType && providers) {
  1801. const providerDetail = providers.map(v => v == provider ? '?' + provider + '?' : '...');
  1802. throw new Error(`Invalid provider for the NgModule '${stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`);
  1803. }
  1804. else if (isEnvironmentProviders(provider)) {
  1805. if (provider.ɵfromNgModule) {
  1806. throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers from 'importProvidersFrom' present in a non-environment injector. 'importProvidersFrom' can't be used for component providers.`);
  1807. }
  1808. else {
  1809. throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, `Invalid providers present in a non-environment injector. 'EnvironmentProviders' can't be used for component providers.`);
  1810. }
  1811. }
  1812. else {
  1813. throw new Error('Invalid provider');
  1814. }
  1815. }
  1816. /** Throws an error when a token is not found in DI. */
  1817. function throwProviderNotFoundError(token, injectorName) {
  1818. const injectorDetails = injectorName ? ` in ${injectorName}` : '';
  1819. throw new RuntimeError(-201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */, ngDevMode && `No provider for ${stringifyForError(token)} found${injectorDetails}`);
  1820. }
  1821. /**
  1822. * Injection flags for DI.
  1823. *
  1824. * @publicApi
  1825. * @deprecated use an options object for [`inject`](api/core/inject) instead.
  1826. */
  1827. var InjectFlags;
  1828. (function (InjectFlags) {
  1829. // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer
  1830. // writes exports of it into ngfactory files.
  1831. /** Check self and check parent injector if needed */
  1832. InjectFlags[InjectFlags["Default"] = 0] = "Default";
  1833. /**
  1834. * Specifies that an injector should retrieve a dependency from any injector until reaching the
  1835. * host element of the current component. (Only used with Element Injector)
  1836. */
  1837. InjectFlags[InjectFlags["Host"] = 1] = "Host";
  1838. /** Don't ascend to ancestors of the node requesting injection. */
  1839. InjectFlags[InjectFlags["Self"] = 2] = "Self";
  1840. /** Skip the node that is requesting injection. */
  1841. InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf";
  1842. /** Inject `defaultValue` instead if token not found. */
  1843. InjectFlags[InjectFlags["Optional"] = 8] = "Optional";
  1844. })(InjectFlags || (InjectFlags = {}));
  1845. /**
  1846. * Current implementation of inject.
  1847. *
  1848. * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
  1849. * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
  1850. * way for two reasons:
  1851. * 1. `Injector` should not depend on ivy logic.
  1852. * 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
  1853. */
  1854. let _injectImplementation;
  1855. function getInjectImplementation() {
  1856. return _injectImplementation;
  1857. }
  1858. /**
  1859. * Sets the current inject implementation.
  1860. */
  1861. function setInjectImplementation(impl) {
  1862. const previous = _injectImplementation;
  1863. _injectImplementation = impl;
  1864. return previous;
  1865. }
  1866. /**
  1867. * Injects `root` tokens in limp mode.
  1868. *
  1869. * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
  1870. * `"root"`. This is known as the limp mode injection. In such case the value is stored in the
  1871. * injectable definition.
  1872. */
  1873. function injectRootLimpMode(token, notFoundValue, flags) {
  1874. const injectableDef = getInjectableDef(token);
  1875. if (injectableDef && injectableDef.providedIn == 'root') {
  1876. return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
  1877. injectableDef.value;
  1878. }
  1879. if (flags & InjectFlags.Optional)
  1880. return null;
  1881. if (notFoundValue !== undefined)
  1882. return notFoundValue;
  1883. throwProviderNotFoundError(stringify(token), 'Injector');
  1884. }
  1885. /**
  1886. * Assert that `_injectImplementation` is not `fn`.
  1887. *
  1888. * This is useful, to prevent infinite recursion.
  1889. *
  1890. * @param fn Function which it should not equal to
  1891. */
  1892. function assertInjectImplementationNotEqual(fn) {
  1893. ngDevMode &&
  1894. assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion');
  1895. }
  1896. const _THROW_IF_NOT_FOUND = {};
  1897. const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
  1898. /*
  1899. * Name of a property (that we patch onto DI decorator), which is used as an annotation of which
  1900. * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators
  1901. * in the code, thus making them tree-shakable.
  1902. */
  1903. const DI_DECORATOR_FLAG = '__NG_DI_FLAG__';
  1904. const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
  1905. const NG_TOKEN_PATH = 'ngTokenPath';
  1906. const NEW_LINE = /\n/gm;
  1907. const NO_NEW_LINE = 'ɵ';
  1908. const SOURCE = '__source';
  1909. /**
  1910. * Current injector value used by `inject`.
  1911. * - `undefined`: it is an error to call `inject`
  1912. * - `null`: `inject` can be called but there is no injector (limp-mode).
  1913. * - Injector instance: Use the injector for resolution.
  1914. */
  1915. let _currentInjector = undefined;
  1916. function getCurrentInjector() {
  1917. return _currentInjector;
  1918. }
  1919. function setCurrentInjector(injector) {
  1920. const former = _currentInjector;
  1921. _currentInjector = injector;
  1922. return former;
  1923. }
  1924. function injectInjectorOnly(token, flags = InjectFlags.Default) {
  1925. if (_currentInjector === undefined) {
  1926. throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
  1927. `inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`runInInjectionContext\`.`);
  1928. }
  1929. else if (_currentInjector === null) {
  1930. return injectRootLimpMode(token, undefined, flags);
  1931. }
  1932. else {
  1933. const value = _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
  1934. ngDevMode && emitInjectEvent(token, value, flags);
  1935. return value;
  1936. }
  1937. }
  1938. function ɵɵinject(token, flags = InjectFlags.Default) {
  1939. return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
  1940. }
  1941. /**
  1942. * Throws an error indicating that a factory function could not be generated by the compiler for a
  1943. * particular class.
  1944. *
  1945. * The name of the class is not mentioned here, but will be in the generated factory function name
  1946. * and thus in the stack trace.
  1947. *
  1948. * @codeGenApi
  1949. */
  1950. function ɵɵinvalidFactoryDep(index) {
  1951. throw new RuntimeError(202 /* RuntimeErrorCode.INVALID_FACTORY_DEPENDENCY */, ngDevMode &&
  1952. `This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid.
  1953. This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator.
  1954. Please check that 1) the type for the parameter at index ${index} is correct and 2) the correct Angular decorators are defined for this class and its ancestors.`);
  1955. }
  1956. /**
  1957. * Injects a token from the currently active injector.
  1958. * `inject` is only supported in an [injection context](/guide/dependency-injection-context). It can
  1959. * be used during:
  1960. * - Construction (via the `constructor`) of a class being instantiated by the DI system, such
  1961. * as an `@Injectable` or `@Component`.
  1962. * - In the initializer for fields of such classes.
  1963. * - In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
  1964. * - In the `factory` function specified for an `InjectionToken`.
  1965. * - In a stackframe of a function call in a DI context
  1966. *
  1967. * @param token A token that represents a dependency that should be injected.
  1968. * @param flags Optional flags that control how injection is executed.
  1969. * The flags correspond to injection strategies that can be specified with
  1970. * parameter decorators `@Host`, `@Self`, `@SkipSelf`, and `@Optional`.
  1971. * @returns the injected value if operation is successful, `null` otherwise.
  1972. * @throws if called outside of a supported context.
  1973. *
  1974. * @usageNotes
  1975. * In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a
  1976. * field initializer:
  1977. *
  1978. * ```typescript
  1979. * @Injectable({providedIn: 'root'})
  1980. * export class Car {
  1981. * radio: Radio|undefined;
  1982. * // OK: field initializer
  1983. * spareTyre = inject(Tyre);
  1984. *
  1985. * constructor() {
  1986. * // OK: constructor body
  1987. * this.radio = inject(Radio);
  1988. * }
  1989. * }
  1990. * ```
  1991. *
  1992. * It is also legal to call `inject` from a provider's factory:
  1993. *
  1994. * ```typescript
  1995. * providers: [
  1996. * {provide: Car, useFactory: () => {
  1997. * // OK: a class factory
  1998. * const engine = inject(Engine);
  1999. * return new Car(engine);
  2000. * }}
  2001. * ]
  2002. * ```
  2003. *
  2004. * Calls to the `inject()` function outside of the class creation context will result in error. Most
  2005. * notably, calls to `inject()` are disallowed after a class instance was created, in methods
  2006. * (including lifecycle hooks):
  2007. *
  2008. * ```typescript
  2009. * @Component({ ... })
  2010. * export class CarComponent {
  2011. * ngOnInit() {
  2012. * // ERROR: too late, the component instance was already created
  2013. * const engine = inject(Engine);
  2014. * engine.start();
  2015. * }
  2016. * }
  2017. * ```
  2018. *
  2019. * @publicApi
  2020. */
  2021. function inject$1(token, flags = InjectFlags.Default) {
  2022. return ɵɵinject(token, convertToBitFlags(flags));
  2023. }
  2024. // Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
  2025. function convertToBitFlags(flags) {
  2026. if (typeof flags === 'undefined' || typeof flags === 'number') {
  2027. return flags;
  2028. }
  2029. // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
  2030. // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
  2031. // `InjectOptions` to `InjectFlags`.
  2032. return (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
  2033. (flags.optional && 8 /* InternalInjectFlags.Optional */) |
  2034. (flags.host && 1 /* InternalInjectFlags.Host */) |
  2035. (flags.self && 2 /* InternalInjectFlags.Self */) |
  2036. (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
  2037. }
  2038. function injectArgs(types) {
  2039. const args = [];
  2040. for (let i = 0; i < types.length; i++) {
  2041. const arg = resolveForwardRef(types[i]);
  2042. if (Array.isArray(arg)) {
  2043. if (arg.length === 0) {
  2044. throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && 'Arguments array must have arguments.');
  2045. }
  2046. let type = undefined;
  2047. let flags = InjectFlags.Default;
  2048. for (let j = 0; j < arg.length; j++) {
  2049. const meta = arg[j];
  2050. const flag = getInjectFlag(meta);
  2051. if (typeof flag === 'number') {
  2052. // Special case when we handle @Inject decorator.
  2053. if (flag === -1 /* DecoratorFlags.Inject */) {
  2054. type = meta.token;
  2055. }
  2056. else {
  2057. flags |= flag;
  2058. }
  2059. }
  2060. else {
  2061. type = meta;
  2062. }
  2063. }
  2064. args.push(ɵɵinject(type, flags));
  2065. }
  2066. else {
  2067. args.push(ɵɵinject(arg));
  2068. }
  2069. }
  2070. return args;
  2071. }
  2072. /**
  2073. * Attaches a given InjectFlag to a given decorator using monkey-patching.
  2074. * Since DI decorators can be used in providers `deps` array (when provider is configured using
  2075. * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we
  2076. * attach the flag to make it available both as a static property and as a field on decorator
  2077. * instance.
  2078. *
  2079. * @param decorator Provided DI decorator.
  2080. * @param flag InjectFlag that should be applied.
  2081. */
  2082. function attachInjectFlag(decorator, flag) {
  2083. decorator[DI_DECORATOR_FLAG] = flag;
  2084. decorator.prototype[DI_DECORATOR_FLAG] = flag;
  2085. return decorator;
  2086. }
  2087. /**
  2088. * Reads monkey-patched property that contains InjectFlag attached to a decorator.
  2089. *
  2090. * @param token Token that may contain monkey-patched DI flags property.
  2091. */
  2092. function getInjectFlag(token) {
  2093. return token[DI_DECORATOR_FLAG];
  2094. }
  2095. function catchInjectorError(e, token, injectorErrorName, source) {
  2096. const tokenPath = e[NG_TEMP_TOKEN_PATH];
  2097. if (token[SOURCE]) {
  2098. tokenPath.unshift(token[SOURCE]);
  2099. }
  2100. e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
  2101. e[NG_TOKEN_PATH] = tokenPath;
  2102. e[NG_TEMP_TOKEN_PATH] = null;
  2103. throw e;
  2104. }
  2105. function formatError(text, obj, injectorErrorName, source = null) {
  2106. text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.slice(2) : text;
  2107. let context = stringify(obj);
  2108. if (Array.isArray(obj)) {
  2109. context = obj.map(stringify).join(' -> ');
  2110. }
  2111. else if (typeof obj === 'object') {
  2112. let parts = [];
  2113. for (let key in obj) {
  2114. if (obj.hasOwnProperty(key)) {
  2115. let value = obj[key];
  2116. parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value)));
  2117. }
  2118. }
  2119. context = `{${parts.join(', ')}}`;
  2120. }
  2121. return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
  2122. }
  2123. /**
  2124. * Inject decorator and metadata.
  2125. *
  2126. * @Annotation
  2127. * @publicApi
  2128. */
  2129. const Inject = attachInjectFlag(
  2130. // Disable tslint because `DecoratorFlags` is a const enum which gets inlined.
  2131. // tslint:disable-next-line: no-toplevel-property-access
  2132. makeParamDecorator('Inject', (token) => ({ token })), -1 /* DecoratorFlags.Inject */);
  2133. /**
  2134. * Optional decorator and metadata.
  2135. *
  2136. * @Annotation
  2137. * @publicApi
  2138. */
  2139. const Optional =
  2140. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  2141. // tslint:disable-next-line: no-toplevel-property-access
  2142. attachInjectFlag(makeParamDecorator('Optional'), 8 /* InternalInjectFlags.Optional */);
  2143. /**
  2144. * Self decorator and metadata.
  2145. *
  2146. * @Annotation
  2147. * @publicApi
  2148. */
  2149. const Self =
  2150. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  2151. // tslint:disable-next-line: no-toplevel-property-access
  2152. attachInjectFlag(makeParamDecorator('Self'), 2 /* InternalInjectFlags.Self */);
  2153. /**
  2154. * `SkipSelf` decorator and metadata.
  2155. *
  2156. * @Annotation
  2157. * @publicApi
  2158. */
  2159. const SkipSelf =
  2160. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  2161. // tslint:disable-next-line: no-toplevel-property-access
  2162. attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* InternalInjectFlags.SkipSelf */);
  2163. /**
  2164. * Host decorator and metadata.
  2165. *
  2166. * @Annotation
  2167. * @publicApi
  2168. */
  2169. const Host =
  2170. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  2171. // tslint:disable-next-line: no-toplevel-property-access
  2172. attachInjectFlag(makeParamDecorator('Host'), 1 /* InternalInjectFlags.Host */);
  2173. /**
  2174. * The strategy that the default change detector uses to detect changes.
  2175. * When set, takes effect the next time change detection is triggered.
  2176. *
  2177. * @see {@link ChangeDetectorRef#usage-notes Change detection usage}
  2178. *
  2179. * @publicApi
  2180. */
  2181. var ChangeDetectionStrategy;
  2182. (function (ChangeDetectionStrategy) {
  2183. /**
  2184. * Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
  2185. * until reactivated by setting the strategy to `Default` (`CheckAlways`).
  2186. * Change detection can still be explicitly invoked.
  2187. * This strategy applies to all child directives and cannot be overridden.
  2188. */
  2189. ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
  2190. /**
  2191. * Use the default `CheckAlways` strategy, in which change detection is automatic until
  2192. * explicitly deactivated.
  2193. */
  2194. ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
  2195. })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
  2196. /**
  2197. * Defines the CSS styles encapsulation policies for the {@link Component} decorator's
  2198. * `encapsulation` option.
  2199. *
  2200. * See {@link Component#encapsulation encapsulation}.
  2201. *
  2202. * @usageNotes
  2203. * ### Example
  2204. *
  2205. * {@example core/ts/metadata/encapsulation.ts region='longform'}
  2206. *
  2207. * @publicApi
  2208. */
  2209. var ViewEncapsulation;
  2210. (function (ViewEncapsulation) {
  2211. // TODO: consider making `ViewEncapsulation` a `const enum` instead. See
  2212. // https://github.com/angular/angular/issues/44119 for additional information.
  2213. /**
  2214. * Emulates a native Shadow DOM encapsulation behavior by adding a specific attribute to the
  2215. * component's host element and applying the same attribute to all the CSS selectors provided
  2216. * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls}.
  2217. *
  2218. * This is the default option.
  2219. */
  2220. ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
  2221. // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
  2222. /**
  2223. * Doesn't provide any sort of CSS style encapsulation, meaning that all the styles provided
  2224. * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls} are applicable
  2225. * to any HTML element of the application regardless of their host Component.
  2226. */
  2227. ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
  2228. /**
  2229. * Uses the browser's native Shadow DOM API to encapsulate CSS styles, meaning that it creates
  2230. * a ShadowRoot for the component's host element which is then used to encapsulate
  2231. * all the Component's styling.
  2232. */
  2233. ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
  2234. })(ViewEncapsulation || (ViewEncapsulation = {}));
  2235. /**
  2236. * This file contains reuseable "empty" symbols that can be used as default return values
  2237. * in different parts of the rendering code. Because the same symbols are returned, this
  2238. * allows for identity checks against these values to be consistently used by the framework
  2239. * code.
  2240. */
  2241. const EMPTY_OBJ = {};
  2242. const EMPTY_ARRAY = [];
  2243. // freezing the values prevents any code from accidentally inserting new values in
  2244. if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
  2245. // These property accesses can be ignored because ngDevMode will be set to false
  2246. // when optimizing code and the whole if statement will be dropped.
  2247. // tslint:disable-next-line:no-toplevel-property-access
  2248. Object.freeze(EMPTY_OBJ);
  2249. // tslint:disable-next-line:no-toplevel-property-access
  2250. Object.freeze(EMPTY_ARRAY);
  2251. }
  2252. const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty });
  2253. const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty });
  2254. const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty });
  2255. const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty });
  2256. const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty });
  2257. /**
  2258. * If a directive is diPublic, bloomAdd sets a property on the type with this constant as
  2259. * the key and the directive's unique ID as the value. This allows us to map directives to their
  2260. * bloom filter bit for DI.
  2261. */
  2262. // TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
  2263. const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty });
  2264. /**
  2265. * The `NG_ENV_ID` field on a DI token indicates special processing in the `EnvironmentInjector`:
  2266. * getting such tokens from the `EnvironmentInjector` will bypass the standard DI resolution
  2267. * strategy and instead will return implementation produced by the `NG_ENV_ID` factory function.
  2268. *
  2269. * This particular retrieval of DI tokens is mostly done to eliminate circular dependencies and
  2270. * improve tree-shaking.
  2271. */
  2272. const NG_ENV_ID = getClosureSafeProperty({ __NG_ENV_ID__: getClosureSafeProperty });
  2273. /**
  2274. * Returns an index of `classToSearch` in `className` taking token boundaries into account.
  2275. *
  2276. * `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`)
  2277. *
  2278. * @param className A string containing classes (whitespace separated)
  2279. * @param classToSearch A class name to locate
  2280. * @param startingIndex Starting location of search
  2281. * @returns an index of the located class (or -1 if not found)
  2282. */
  2283. function classIndexOf(className, classToSearch, startingIndex) {
  2284. ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.');
  2285. let end = className.length;
  2286. while (true) {
  2287. const foundIndex = className.indexOf(classToSearch, startingIndex);
  2288. if (foundIndex === -1)
  2289. return foundIndex;
  2290. if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) {
  2291. // Ensure that it has leading whitespace
  2292. const length = classToSearch.length;
  2293. if (foundIndex + length === end ||
  2294. className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) {
  2295. // Ensure that it has trailing whitespace
  2296. return foundIndex;
  2297. }
  2298. }
  2299. // False positive, keep searching from where we left off.
  2300. startingIndex = foundIndex + 1;
  2301. }
  2302. }
  2303. /**
  2304. * Assigns all attribute values to the provided element via the inferred renderer.
  2305. *
  2306. * This function accepts two forms of attribute entries:
  2307. *
  2308. * default: (key, value):
  2309. * attrs = [key1, value1, key2, value2]
  2310. *
  2311. * namespaced: (NAMESPACE_MARKER, uri, name, value)
  2312. * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
  2313. *
  2314. * The `attrs` array can contain a mix of both the default and namespaced entries.
  2315. * The "default" values are set without a marker, but if the function comes across
  2316. * a marker value then it will attempt to set a namespaced value. If the marker is
  2317. * not of a namespaced value then the function will quit and return the index value
  2318. * where it stopped during the iteration of the attrs array.
  2319. *
  2320. * See [AttributeMarker] to understand what the namespace marker value is.
  2321. *
  2322. * Note that this instruction does not support assigning style and class values to
  2323. * an element. See `elementStart` and `elementHostAttrs` to learn how styling values
  2324. * are applied to an element.
  2325. * @param renderer The renderer to be used
  2326. * @param native The element that the attributes will be assigned to
  2327. * @param attrs The attribute array of values that will be assigned to the element
  2328. * @returns the index value that was last accessed in the attributes array
  2329. */
  2330. function setUpAttributes(renderer, native, attrs) {
  2331. let i = 0;
  2332. while (i < attrs.length) {
  2333. const value = attrs[i];
  2334. if (typeof value === 'number') {
  2335. // only namespaces are supported. Other value types (such as style/class
  2336. // entries) are not supported in this function.
  2337. if (value !== 0 /* AttributeMarker.NamespaceURI */) {
  2338. break;
  2339. }
  2340. // we just landed on the marker value ... therefore
  2341. // we should skip to the next entry
  2342. i++;
  2343. const namespaceURI = attrs[i++];
  2344. const attrName = attrs[i++];
  2345. const attrVal = attrs[i++];
  2346. ngDevMode && ngDevMode.rendererSetAttribute++;
  2347. renderer.setAttribute(native, attrName, attrVal, namespaceURI);
  2348. }
  2349. else {
  2350. // attrName is string;
  2351. const attrName = value;
  2352. const attrVal = attrs[++i];
  2353. // Standard attributes
  2354. ngDevMode && ngDevMode.rendererSetAttribute++;
  2355. if (isAnimationProp(attrName)) {
  2356. renderer.setProperty(native, attrName, attrVal);
  2357. }
  2358. else {
  2359. renderer.setAttribute(native, attrName, attrVal);
  2360. }
  2361. i++;
  2362. }
  2363. }
  2364. // another piece of code may iterate over the same attributes array. Therefore
  2365. // it may be helpful to return the exact spot where the attributes array exited
  2366. // whether by running into an unsupported marker or if all the static values were
  2367. // iterated over.
  2368. return i;
  2369. }
  2370. /**
  2371. * Test whether the given value is a marker that indicates that the following
  2372. * attribute values in a `TAttributes` array are only the names of attributes,
  2373. * and not name-value pairs.
  2374. * @param marker The attribute marker to test.
  2375. * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`).
  2376. */
  2377. function isNameOnlyAttributeMarker(marker) {
  2378. return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ ||
  2379. marker === 6 /* AttributeMarker.I18n */;
  2380. }
  2381. function isAnimationProp(name) {
  2382. // Perf note: accessing charCodeAt to check for the first character of a string is faster as
  2383. // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
  2384. // charCodeAt doesn't allocate memory to return a substring.
  2385. return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */;
  2386. }
  2387. /**
  2388. * Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process.
  2389. *
  2390. * This merge function keeps the order of attrs same.
  2391. *
  2392. * @param dst Location of where the merged `TAttributes` should end up.
  2393. * @param src `TAttributes` which should be appended to `dst`
  2394. */
  2395. function mergeHostAttrs(dst, src) {
  2396. if (src === null || src.length === 0) {
  2397. // do nothing
  2398. }
  2399. else if (dst === null || dst.length === 0) {
  2400. // We have source, but dst is empty, just make a copy.
  2401. dst = src.slice();
  2402. }
  2403. else {
  2404. let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */;
  2405. for (let i = 0; i < src.length; i++) {
  2406. const item = src[i];
  2407. if (typeof item === 'number') {
  2408. srcMarker = item;
  2409. }
  2410. else {
  2411. if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) {
  2412. // Case where we need to consume `key1`, `key2`, `value` items.
  2413. }
  2414. else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ ||
  2415. srcMarker === 2 /* AttributeMarker.Styles */) {
  2416. // Case where we have to consume `key1` and `value` only.
  2417. mergeHostAttribute(dst, srcMarker, item, null, src[++i]);
  2418. }
  2419. else {
  2420. // Case where we have to consume `key1` only.
  2421. mergeHostAttribute(dst, srcMarker, item, null, null);
  2422. }
  2423. }
  2424. }
  2425. }
  2426. return dst;
  2427. }
  2428. /**
  2429. * Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account.
  2430. *
  2431. * @param dst `TAttributes` to append to.
  2432. * @param marker Region where the `key`/`value` should be added.
  2433. * @param key1 Key to add to `TAttributes`
  2434. * @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`)
  2435. * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class.
  2436. */
  2437. function mergeHostAttribute(dst, marker, key1, key2, value) {
  2438. let i = 0;
  2439. // Assume that new markers will be inserted at the end.
  2440. let markerInsertPosition = dst.length;
  2441. // scan until correct type.
  2442. if (marker === -1 /* AttributeMarker.ImplicitAttributes */) {
  2443. markerInsertPosition = -1;
  2444. }
  2445. else {
  2446. while (i < dst.length) {
  2447. const dstValue = dst[i++];
  2448. if (typeof dstValue === 'number') {
  2449. if (dstValue === marker) {
  2450. markerInsertPosition = -1;
  2451. break;
  2452. }
  2453. else if (dstValue > marker) {
  2454. // We need to save this as we want the markers to be inserted in specific order.
  2455. markerInsertPosition = i - 1;
  2456. break;
  2457. }
  2458. }
  2459. }
  2460. }
  2461. // search until you find place of insertion
  2462. while (i < dst.length) {
  2463. const item = dst[i];
  2464. if (typeof item === 'number') {
  2465. // since `i` started as the index after the marker, we did not find it if we are at the next
  2466. // marker
  2467. break;
  2468. }
  2469. else if (item === key1) {
  2470. // We already have same token
  2471. if (key2 === null) {
  2472. if (value !== null) {
  2473. dst[i + 1] = value;
  2474. }
  2475. return;
  2476. }
  2477. else if (key2 === dst[i + 1]) {
  2478. dst[i + 2] = value;
  2479. return;
  2480. }
  2481. }
  2482. // Increment counter.
  2483. i++;
  2484. if (key2 !== null)
  2485. i++;
  2486. if (value !== null)
  2487. i++;
  2488. }
  2489. // insert at location.
  2490. if (markerInsertPosition !== -1) {
  2491. dst.splice(markerInsertPosition, 0, marker);
  2492. i = markerInsertPosition + 1;
  2493. }
  2494. dst.splice(i++, 0, key1);
  2495. if (key2 !== null) {
  2496. dst.splice(i++, 0, key2);
  2497. }
  2498. if (value !== null) {
  2499. dst.splice(i++, 0, value);
  2500. }
  2501. }
  2502. const NG_TEMPLATE_SELECTOR = 'ng-template';
  2503. /**
  2504. * Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive)
  2505. *
  2506. * @param attrs `TAttributes` to search through.
  2507. * @param cssClassToMatch class to match (lowercase)
  2508. * @param isProjectionMode Whether or not class matching should look into the attribute `class` in
  2509. * addition to the `AttributeMarker.Classes`.
  2510. */
  2511. function isCssClassMatching(attrs, cssClassToMatch, isProjectionMode) {
  2512. // TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
  2513. // It is strange to me that sometimes the class information comes in form of `class` attribute
  2514. // and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
  2515. // if that is the right behavior.
  2516. ngDevMode &&
  2517. assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
  2518. let i = 0;
  2519. // Indicates whether we are processing value from the implicit
  2520. // attribute section (i.e. before the first marker in the array).
  2521. let isImplicitAttrsSection = true;
  2522. while (i < attrs.length) {
  2523. let item = attrs[i++];
  2524. if (typeof item === 'string' && isImplicitAttrsSection) {
  2525. const value = attrs[i++];
  2526. if (isProjectionMode && item === 'class') {
  2527. // We found a `class` attribute in the implicit attribute section,
  2528. // check if it matches the value of the `cssClassToMatch` argument.
  2529. if (classIndexOf(value.toLowerCase(), cssClassToMatch, 0) !== -1) {
  2530. return true;
  2531. }
  2532. }
  2533. }
  2534. else if (item === 1 /* AttributeMarker.Classes */) {
  2535. // We found the classes section. Start searching for the class.
  2536. while (i < attrs.length && typeof (item = attrs[i++]) == 'string') {
  2537. // while we have strings
  2538. if (item.toLowerCase() === cssClassToMatch)
  2539. return true;
  2540. }
  2541. return false;
  2542. }
  2543. else if (typeof item === 'number') {
  2544. // We've came across a first marker, which indicates
  2545. // that the implicit attribute section is over.
  2546. isImplicitAttrsSection = false;
  2547. }
  2548. }
  2549. return false;
  2550. }
  2551. /**
  2552. * Checks whether the `tNode` represents an inline template (e.g. `*ngFor`).
  2553. *
  2554. * @param tNode current TNode
  2555. */
  2556. function isInlineTemplate(tNode) {
  2557. return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR;
  2558. }
  2559. /**
  2560. * Function that checks whether a given tNode matches tag-based selector and has a valid type.
  2561. *
  2562. * Matching can be performed in 2 modes: projection mode (when we project nodes) and regular
  2563. * directive matching mode:
  2564. * - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is
  2565. * different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a
  2566. * tag name was extracted from * syntax so we would match the same directive twice);
  2567. * - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing
  2568. * (applicable to TNodeType.Container only).
  2569. */
  2570. function hasTagAndTypeMatch(tNode, currentSelector, isProjectionMode) {
  2571. const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value;
  2572. return currentSelector === tagNameToCompare;
  2573. }
  2574. /**
  2575. * A utility function to match an Ivy node static data against a simple CSS selector
  2576. *
  2577. * @param node static data of the node to match
  2578. * @param selector The selector to try matching against the node.
  2579. * @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing
  2580. * directive matching.
  2581. * @returns true if node matches the selector.
  2582. */
  2583. function isNodeMatchingSelector(tNode, selector, isProjectionMode) {
  2584. ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
  2585. let mode = 4 /* SelectorFlags.ELEMENT */;
  2586. const nodeAttrs = tNode.attrs || [];
  2587. // Find the index of first attribute that has no value, only a name.
  2588. const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs);
  2589. // When processing ":not" selectors, we skip to the next ":not" if the
  2590. // current one doesn't match
  2591. let skipToNextSelector = false;
  2592. for (let i = 0; i < selector.length; i++) {
  2593. const current = selector[i];
  2594. if (typeof current === 'number') {
  2595. // If we finish processing a :not selector and it hasn't failed, return false
  2596. if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) {
  2597. return false;
  2598. }
  2599. // If we are skipping to the next :not() and this mode flag is positive,
  2600. // it's a part of the current :not() selector, and we should keep skipping
  2601. if (skipToNextSelector && isPositive(current))
  2602. continue;
  2603. skipToNextSelector = false;
  2604. mode = current | (mode & 1 /* SelectorFlags.NOT */);
  2605. continue;
  2606. }
  2607. if (skipToNextSelector)
  2608. continue;
  2609. if (mode & 4 /* SelectorFlags.ELEMENT */) {
  2610. mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */;
  2611. if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) ||
  2612. current === '' && selector.length === 1) {
  2613. if (isPositive(mode))
  2614. return false;
  2615. skipToNextSelector = true;
  2616. }
  2617. }
  2618. else {
  2619. const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i];
  2620. // special case for matching against classes when a tNode has been instantiated with
  2621. // class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
  2622. if ((mode & 8 /* SelectorFlags.CLASS */) && tNode.attrs !== null) {
  2623. if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) {
  2624. if (isPositive(mode))
  2625. return false;
  2626. skipToNextSelector = true;
  2627. }
  2628. continue;
  2629. }
  2630. const attrName = (mode & 8 /* SelectorFlags.CLASS */) ? 'class' : current;
  2631. const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
  2632. if (attrIndexInNode === -1) {
  2633. if (isPositive(mode))
  2634. return false;
  2635. skipToNextSelector = true;
  2636. continue;
  2637. }
  2638. if (selectorAttrValue !== '') {
  2639. let nodeAttrValue;
  2640. if (attrIndexInNode > nameOnlyMarkerIdx) {
  2641. nodeAttrValue = '';
  2642. }
  2643. else {
  2644. ngDevMode &&
  2645. assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes');
  2646. // we lowercase the attribute value to be able to match
  2647. // selectors without case-sensitivity
  2648. // (selectors are already in lowercase when generated)
  2649. nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase();
  2650. }
  2651. const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null;
  2652. if (compareAgainstClassName &&
  2653. classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 ||
  2654. mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) {
  2655. if (isPositive(mode))
  2656. return false;
  2657. skipToNextSelector = true;
  2658. }
  2659. }
  2660. }
  2661. }
  2662. return isPositive(mode) || skipToNextSelector;
  2663. }
  2664. function isPositive(mode) {
  2665. return (mode & 1 /* SelectorFlags.NOT */) === 0;
  2666. }
  2667. /**
  2668. * Examines the attribute's definition array for a node to find the index of the
  2669. * attribute that matches the given `name`.
  2670. *
  2671. * NOTE: This will not match namespaced attributes.
  2672. *
  2673. * Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`.
  2674. * The following table summarizes which types of attributes we attempt to match:
  2675. *
  2676. * ===========================================================================================================
  2677. * Modes | Normal Attributes | Bindings Attributes | Template Attributes | I18n
  2678. * Attributes
  2679. * ===========================================================================================================
  2680. * Inline + Projection | YES | YES | NO | YES
  2681. * -----------------------------------------------------------------------------------------------------------
  2682. * Inline + Directive | NO | NO | YES | NO
  2683. * -----------------------------------------------------------------------------------------------------------
  2684. * Non-inline + Projection | YES | YES | NO | YES
  2685. * -----------------------------------------------------------------------------------------------------------
  2686. * Non-inline + Directive | YES | YES | NO | YES
  2687. * ===========================================================================================================
  2688. *
  2689. * @param name the name of the attribute to find
  2690. * @param attrs the attribute array to examine
  2691. * @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`)
  2692. * rather than a manually expanded template node (e.g `<ng-template>`).
  2693. * @param isProjectionMode true if we are matching against content projection otherwise we are
  2694. * matching against directives.
  2695. */
  2696. function findAttrIndexInNode(name, attrs, isInlineTemplate, isProjectionMode) {
  2697. if (attrs === null)
  2698. return -1;
  2699. let i = 0;
  2700. if (isProjectionMode || !isInlineTemplate) {
  2701. let bindingsMode = false;
  2702. while (i < attrs.length) {
  2703. const maybeAttrName = attrs[i];
  2704. if (maybeAttrName === name) {
  2705. return i;
  2706. }
  2707. else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) {
  2708. bindingsMode = true;
  2709. }
  2710. else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) {
  2711. let value = attrs[++i];
  2712. // We should skip classes here because we have a separate mechanism for
  2713. // matching classes in projection mode.
  2714. while (typeof value === 'string') {
  2715. value = attrs[++i];
  2716. }
  2717. continue;
  2718. }
  2719. else if (maybeAttrName === 4 /* AttributeMarker.Template */) {
  2720. // We do not care about Template attributes in this scenario.
  2721. break;
  2722. }
  2723. else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) {
  2724. // Skip the whole namespaced attribute and value. This is by design.
  2725. i += 4;
  2726. continue;
  2727. }
  2728. // In binding mode there are only names, rather than name-value pairs.
  2729. i += bindingsMode ? 1 : 2;
  2730. }
  2731. // We did not match the attribute
  2732. return -1;
  2733. }
  2734. else {
  2735. return matchTemplateAttribute(attrs, name);
  2736. }
  2737. }
  2738. function isNodeMatchingSelectorList(tNode, selector, isProjectionMode = false) {
  2739. for (let i = 0; i < selector.length; i++) {
  2740. if (isNodeMatchingSelector(tNode, selector[i], isProjectionMode)) {
  2741. return true;
  2742. }
  2743. }
  2744. return false;
  2745. }
  2746. function getProjectAsAttrValue(tNode) {
  2747. const nodeAttrs = tNode.attrs;
  2748. if (nodeAttrs != null) {
  2749. const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */);
  2750. // only check for ngProjectAs in attribute names, don't accidentally match attribute's value
  2751. // (attribute names are stored at even indexes)
  2752. if ((ngProjectAsAttrIdx & 1) === 0) {
  2753. return nodeAttrs[ngProjectAsAttrIdx + 1];
  2754. }
  2755. }
  2756. return null;
  2757. }
  2758. function getNameOnlyMarkerIndex(nodeAttrs) {
  2759. for (let i = 0; i < nodeAttrs.length; i++) {
  2760. const nodeAttr = nodeAttrs[i];
  2761. if (isNameOnlyAttributeMarker(nodeAttr)) {
  2762. return i;
  2763. }
  2764. }
  2765. return nodeAttrs.length;
  2766. }
  2767. function matchTemplateAttribute(attrs, name) {
  2768. let i = attrs.indexOf(4 /* AttributeMarker.Template */);
  2769. if (i > -1) {
  2770. i++;
  2771. while (i < attrs.length) {
  2772. const attr = attrs[i];
  2773. // Return in case we checked all template attrs and are switching to the next section in the
  2774. // attrs array (that starts with a number that represents an attribute marker).
  2775. if (typeof attr === 'number')
  2776. return -1;
  2777. if (attr === name)
  2778. return i;
  2779. i++;
  2780. }
  2781. }
  2782. return -1;
  2783. }
  2784. /**
  2785. * Checks whether a selector is inside a CssSelectorList
  2786. * @param selector Selector to be checked.
  2787. * @param list List in which to look for the selector.
  2788. */
  2789. function isSelectorInSelectorList(selector, list) {
  2790. selectorListLoop: for (let i = 0; i < list.length; i++) {
  2791. const currentSelectorInList = list[i];
  2792. if (selector.length !== currentSelectorInList.length) {
  2793. continue;
  2794. }
  2795. for (let j = 0; j < selector.length; j++) {
  2796. if (selector[j] !== currentSelectorInList[j]) {
  2797. continue selectorListLoop;
  2798. }
  2799. }
  2800. return true;
  2801. }
  2802. return false;
  2803. }
  2804. function maybeWrapInNotSelector(isNegativeMode, chunk) {
  2805. return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
  2806. }
  2807. function stringifyCSSSelector(selector) {
  2808. let result = selector[0];
  2809. let i = 1;
  2810. let mode = 2 /* SelectorFlags.ATTRIBUTE */;
  2811. let currentChunk = '';
  2812. let isNegativeMode = false;
  2813. while (i < selector.length) {
  2814. let valueOrMarker = selector[i];
  2815. if (typeof valueOrMarker === 'string') {
  2816. if (mode & 2 /* SelectorFlags.ATTRIBUTE */) {
  2817. const attrValue = selector[++i];
  2818. currentChunk +=
  2819. '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
  2820. }
  2821. else if (mode & 8 /* SelectorFlags.CLASS */) {
  2822. currentChunk += '.' + valueOrMarker;
  2823. }
  2824. else if (mode & 4 /* SelectorFlags.ELEMENT */) {
  2825. currentChunk += ' ' + valueOrMarker;
  2826. }
  2827. }
  2828. else {
  2829. //
  2830. // Append current chunk to the final result in case we come across SelectorFlag, which
  2831. // indicates that the previous section of a selector is over. We need to accumulate content
  2832. // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
  2833. // ```
  2834. // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
  2835. // ```
  2836. // should be transformed to `.classA :not(.classB .classC)`.
  2837. //
  2838. // Note: for negative selector part, we accumulate content between flags until we find the
  2839. // next negative flag. This is needed to support a case where `:not()` rule contains more than
  2840. // one chunk, e.g. the following selector:
  2841. // ```
  2842. // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
  2843. // ```
  2844. // should be stringified to `:not(p.foo) :not(.bar)`
  2845. //
  2846. if (currentChunk !== '' && !isPositive(valueOrMarker)) {
  2847. result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
  2848. currentChunk = '';
  2849. }
  2850. mode = valueOrMarker;
  2851. // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
  2852. // mode is maintained for remaining chunks of a selector.
  2853. isNegativeMode = isNegativeMode || !isPositive(mode);
  2854. }
  2855. i++;
  2856. }
  2857. if (currentChunk !== '') {
  2858. result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
  2859. }
  2860. return result;
  2861. }
  2862. /**
  2863. * Generates string representation of CSS selector in parsed form.
  2864. *
  2865. * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
  2866. * additional parsing at runtime (for example, for directive matching). However in some cases (for
  2867. * example, while bootstrapping a component), a string version of the selector is required to query
  2868. * for the host element on the page. This function takes the parsed form of a selector and returns
  2869. * its string representation.
  2870. *
  2871. * @param selectorList selector in parsed form
  2872. * @returns string representation of a given selector
  2873. */
  2874. function stringifyCSSSelectorList(selectorList) {
  2875. return selectorList.map(stringifyCSSSelector).join(',');
  2876. }
  2877. /**
  2878. * Extracts attributes and classes information from a given CSS selector.
  2879. *
  2880. * This function is used while creating a component dynamically. In this case, the host element
  2881. * (that is created dynamically) should contain attributes and classes specified in component's CSS
  2882. * selector.
  2883. *
  2884. * @param selector CSS selector in parsed form (in a form of array)
  2885. * @returns object with `attrs` and `classes` fields that contain extracted information
  2886. */
  2887. function extractAttrsAndClassesFromSelector(selector) {
  2888. const attrs = [];
  2889. const classes = [];
  2890. let i = 1;
  2891. let mode = 2 /* SelectorFlags.ATTRIBUTE */;
  2892. while (i < selector.length) {
  2893. let valueOrMarker = selector[i];
  2894. if (typeof valueOrMarker === 'string') {
  2895. if (mode === 2 /* SelectorFlags.ATTRIBUTE */) {
  2896. if (valueOrMarker !== '') {
  2897. attrs.push(valueOrMarker, selector[++i]);
  2898. }
  2899. }
  2900. else if (mode === 8 /* SelectorFlags.CLASS */) {
  2901. classes.push(valueOrMarker);
  2902. }
  2903. }
  2904. else {
  2905. // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
  2906. // mode is maintained for remaining chunks of a selector. Since attributes and classes are
  2907. // extracted only for "positive" part of the selector, we can stop here.
  2908. if (!isPositive(mode))
  2909. break;
  2910. mode = valueOrMarker;
  2911. }
  2912. i++;
  2913. }
  2914. return { attrs, classes };
  2915. }
  2916. /**
  2917. * Create a component definition object.
  2918. *
  2919. *
  2920. * # Example
  2921. * ```
  2922. * class MyComponent {
  2923. * // Generated by Angular Template Compiler
  2924. * // [Symbol] syntax will not be supported by TypeScript until v2.7
  2925. * static ɵcmp = defineComponent({
  2926. * ...
  2927. * });
  2928. * }
  2929. * ```
  2930. * @codeGenApi
  2931. */
  2932. function ɵɵdefineComponent(componentDefinition) {
  2933. return noSideEffects(() => {
  2934. // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent.
  2935. // See the `initNgDevMode` docstring for more information.
  2936. (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
  2937. const baseDef = getNgDirectiveDef(componentDefinition);
  2938. const def = {
  2939. ...baseDef,
  2940. decls: componentDefinition.decls,
  2941. vars: componentDefinition.vars,
  2942. template: componentDefinition.template,
  2943. consts: componentDefinition.consts || null,
  2944. ngContentSelectors: componentDefinition.ngContentSelectors,
  2945. onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
  2946. directiveDefs: null,
  2947. pipeDefs: null,
  2948. dependencies: baseDef.standalone && componentDefinition.dependencies || null,
  2949. getStandaloneInjector: null,
  2950. signals: componentDefinition.signals ?? false,
  2951. data: componentDefinition.data || {},
  2952. encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated,
  2953. styles: componentDefinition.styles || EMPTY_ARRAY,
  2954. _: null,
  2955. schemas: componentDefinition.schemas || null,
  2956. tView: null,
  2957. id: '',
  2958. };
  2959. initFeatures(def);
  2960. const dependencies = componentDefinition.dependencies;
  2961. def.directiveDefs = extractDefListOrFactory(dependencies, /* pipeDef */ false);
  2962. def.pipeDefs = extractDefListOrFactory(dependencies, /* pipeDef */ true);
  2963. def.id = getComponentId(def);
  2964. return def;
  2965. });
  2966. }
  2967. /**
  2968. * Generated next to NgModules to monkey-patch directive and pipe references onto a component's
  2969. * definition, when generating a direct reference in the component file would otherwise create an
  2970. * import cycle.
  2971. *
  2972. * See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details.
  2973. *
  2974. * @codeGenApi
  2975. */
  2976. function ɵɵsetComponentScope(type, directives, pipes) {
  2977. const def = type.ɵcmp;
  2978. def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */ false);
  2979. def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */ true);
  2980. }
  2981. function extractDirectiveDef(type) {
  2982. return getComponentDef$1(type) || getDirectiveDef(type);
  2983. }
  2984. function nonNull(value) {
  2985. return value !== null;
  2986. }
  2987. /**
  2988. * @codeGenApi
  2989. */
  2990. function ɵɵdefineNgModule(def) {
  2991. return noSideEffects(() => {
  2992. const res = {
  2993. type: def.type,
  2994. bootstrap: def.bootstrap || EMPTY_ARRAY,
  2995. declarations: def.declarations || EMPTY_ARRAY,
  2996. imports: def.imports || EMPTY_ARRAY,
  2997. exports: def.exports || EMPTY_ARRAY,
  2998. transitiveCompileScopes: null,
  2999. schemas: def.schemas || null,
  3000. id: def.id || null,
  3001. };
  3002. return res;
  3003. });
  3004. }
  3005. /**
  3006. * Adds the module metadata that is necessary to compute the module's transitive scope to an
  3007. * existing module definition.
  3008. *
  3009. * Scope metadata of modules is not used in production builds, so calls to this function can be
  3010. * marked pure to tree-shake it from the bundle, allowing for all referenced declarations
  3011. * to become eligible for tree-shaking as well.
  3012. *
  3013. * @codeGenApi
  3014. */
  3015. function ɵɵsetNgModuleScope(type, scope) {
  3016. return noSideEffects(() => {
  3017. const ngModuleDef = getNgModuleDef(type, true);
  3018. ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
  3019. ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
  3020. ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
  3021. });
  3022. }
  3023. /**
  3024. * Inverts an inputs or outputs lookup such that the keys, which were the
  3025. * minified keys, are part of the values, and the values are parsed so that
  3026. * the publicName of the property is the new key
  3027. *
  3028. * e.g. for
  3029. *
  3030. * ```
  3031. * class Comp {
  3032. * @Input()
  3033. * propName1: string;
  3034. *
  3035. * @Input('publicName2')
  3036. * declaredPropName2: number;
  3037. * }
  3038. * ```
  3039. *
  3040. * will be serialized as
  3041. *
  3042. * ```
  3043. * {
  3044. * propName1: 'propName1',
  3045. * declaredPropName2: ['publicName2', 'declaredPropName2'],
  3046. * }
  3047. * ```
  3048. *
  3049. * which is than translated by the minifier as:
  3050. *
  3051. * ```
  3052. * {
  3053. * minifiedPropName1: 'propName1',
  3054. * minifiedPropName2: ['publicName2', 'declaredPropName2'],
  3055. * }
  3056. * ```
  3057. *
  3058. * becomes: (public name => minifiedName)
  3059. *
  3060. * ```
  3061. * {
  3062. * 'propName1': 'minifiedPropName1',
  3063. * 'publicName2': 'minifiedPropName2',
  3064. * }
  3065. * ```
  3066. *
  3067. * Optionally the function can take `secondary` which will result in: (public name => declared name)
  3068. *
  3069. * ```
  3070. * {
  3071. * 'propName1': 'propName1',
  3072. * 'publicName2': 'declaredPropName2',
  3073. * }
  3074. * ```
  3075. *
  3076. */
  3077. function invertObject(obj, secondary) {
  3078. if (obj == null)
  3079. return EMPTY_OBJ;
  3080. const newLookup = {};
  3081. for (const minifiedKey in obj) {
  3082. if (obj.hasOwnProperty(minifiedKey)) {
  3083. let publicName = obj[minifiedKey];
  3084. let declaredName = publicName;
  3085. if (Array.isArray(publicName)) {
  3086. declaredName = publicName[1];
  3087. publicName = publicName[0];
  3088. }
  3089. newLookup[publicName] = minifiedKey;
  3090. if (secondary) {
  3091. (secondary[publicName] = declaredName);
  3092. }
  3093. }
  3094. }
  3095. return newLookup;
  3096. }
  3097. /**
  3098. * Create a directive definition object.
  3099. *
  3100. * # Example
  3101. * ```ts
  3102. * class MyDirective {
  3103. * // Generated by Angular Template Compiler
  3104. * // [Symbol] syntax will not be supported by TypeScript until v2.7
  3105. * static ɵdir = ɵɵdefineDirective({
  3106. * ...
  3107. * });
  3108. * }
  3109. * ```
  3110. *
  3111. * @codeGenApi
  3112. */
  3113. function ɵɵdefineDirective(directiveDefinition) {
  3114. return noSideEffects(() => {
  3115. const def = getNgDirectiveDef(directiveDefinition);
  3116. initFeatures(def);
  3117. return def;
  3118. });
  3119. }
  3120. /**
  3121. * Create a pipe definition object.
  3122. *
  3123. * # Example
  3124. * ```
  3125. * class MyPipe implements PipeTransform {
  3126. * // Generated by Angular Template Compiler
  3127. * static ɵpipe = definePipe({
  3128. * ...
  3129. * });
  3130. * }
  3131. * ```
  3132. * @param pipeDef Pipe definition generated by the compiler
  3133. *
  3134. * @codeGenApi
  3135. */
  3136. function ɵɵdefinePipe(pipeDef) {
  3137. return {
  3138. type: pipeDef.type,
  3139. name: pipeDef.name,
  3140. factory: null,
  3141. pure: pipeDef.pure !== false,
  3142. standalone: pipeDef.standalone === true,
  3143. onDestroy: pipeDef.type.prototype.ngOnDestroy || null
  3144. };
  3145. }
  3146. /**
  3147. * The following getter methods retrieve the definition from the type. Currently the retrieval
  3148. * honors inheritance, but in the future we may change the rule to require that definitions are
  3149. * explicit. This would require some sort of migration strategy.
  3150. */
  3151. function getComponentDef$1(type) {
  3152. return type[NG_COMP_DEF] || null;
  3153. }
  3154. function getDirectiveDef(type) {
  3155. return type[NG_DIR_DEF] || null;
  3156. }
  3157. function getPipeDef$1(type) {
  3158. return type[NG_PIPE_DEF] || null;
  3159. }
  3160. /**
  3161. * Checks whether a given Component, Directive or Pipe is marked as standalone.
  3162. * This will return false if passed anything other than a Component, Directive, or Pipe class
  3163. * See [this guide](/guide/standalone-components) for additional information:
  3164. *
  3165. * @param type A reference to a Component, Directive or Pipe.
  3166. * @publicApi
  3167. */
  3168. function isStandalone(type) {
  3169. const def = getComponentDef$1(type) || getDirectiveDef(type) || getPipeDef$1(type);
  3170. return def !== null ? def.standalone : false;
  3171. }
  3172. function getNgModuleDef(type, throwNotFound) {
  3173. const ngModuleDef = type[NG_MOD_DEF] || null;
  3174. if (!ngModuleDef && throwNotFound === true) {
  3175. throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`);
  3176. }
  3177. return ngModuleDef;
  3178. }
  3179. function getNgDirectiveDef(directiveDefinition) {
  3180. const declaredInputs = {};
  3181. return {
  3182. type: directiveDefinition.type,
  3183. providersResolver: null,
  3184. factory: null,
  3185. hostBindings: directiveDefinition.hostBindings || null,
  3186. hostVars: directiveDefinition.hostVars || 0,
  3187. hostAttrs: directiveDefinition.hostAttrs || null,
  3188. contentQueries: directiveDefinition.contentQueries || null,
  3189. declaredInputs,
  3190. inputTransforms: null,
  3191. inputConfig: directiveDefinition.inputs || EMPTY_OBJ,
  3192. exportAs: directiveDefinition.exportAs || null,
  3193. standalone: directiveDefinition.standalone === true,
  3194. signals: directiveDefinition.signals === true,
  3195. selectors: directiveDefinition.selectors || EMPTY_ARRAY,
  3196. viewQuery: directiveDefinition.viewQuery || null,
  3197. features: directiveDefinition.features || null,
  3198. setInput: null,
  3199. findHostDirectiveDefs: null,
  3200. hostDirectives: null,
  3201. inputs: invertObject(directiveDefinition.inputs, declaredInputs),
  3202. outputs: invertObject(directiveDefinition.outputs),
  3203. };
  3204. }
  3205. function initFeatures(definition) {
  3206. definition.features?.forEach((fn) => fn(definition));
  3207. }
  3208. function extractDefListOrFactory(dependencies, pipeDef) {
  3209. if (!dependencies) {
  3210. return null;
  3211. }
  3212. const defExtractor = pipeDef ? getPipeDef$1 : extractDirectiveDef;
  3213. return () => (typeof dependencies === 'function' ? dependencies() : dependencies)
  3214. .map(dep => defExtractor(dep))
  3215. .filter(nonNull);
  3216. }
  3217. /**
  3218. * A map that contains the generated component IDs and type.
  3219. */
  3220. const GENERATED_COMP_IDS = new Map();
  3221. /**
  3222. * A method can returns a component ID from the component definition using a variant of DJB2 hash
  3223. * algorithm.
  3224. */
  3225. function getComponentId(componentDef) {
  3226. let hash = 0;
  3227. // We cannot rely solely on the component selector as the same selector can be used in different
  3228. // modules.
  3229. //
  3230. // `componentDef.style` is not used, due to it causing inconsistencies. Ex: when server
  3231. // component styles has no sourcemaps and browsers do.
  3232. //
  3233. // Example:
  3234. // https://github.com/angular/components/blob/d9f82c8f95309e77a6d82fd574c65871e91354c2/src/material/core/option/option.ts#L248
  3235. // https://github.com/angular/components/blob/285f46dc2b4c5b127d356cb7c4714b221f03ce50/src/material/legacy-core/option/option.ts#L32
  3236. const hashSelectors = [
  3237. componentDef.selectors,
  3238. componentDef.ngContentSelectors,
  3239. componentDef.hostVars,
  3240. componentDef.hostAttrs,
  3241. componentDef.consts,
  3242. componentDef.vars,
  3243. componentDef.decls,
  3244. componentDef.encapsulation,
  3245. componentDef.standalone,
  3246. componentDef.signals,
  3247. componentDef.exportAs,
  3248. JSON.stringify(componentDef.inputs),
  3249. JSON.stringify(componentDef.outputs),
  3250. // We cannot use 'componentDef.type.name' as the name of the symbol will change and will not
  3251. // match in the server and browser bundles.
  3252. Object.getOwnPropertyNames(componentDef.type.prototype),
  3253. !!componentDef.contentQueries,
  3254. !!componentDef.viewQuery,
  3255. ].join('|');
  3256. for (const char of hashSelectors) {
  3257. hash = Math.imul(31, hash) + char.charCodeAt(0) << 0;
  3258. }
  3259. // Force positive number hash.
  3260. // 2147483647 = equivalent of Integer.MAX_VALUE.
  3261. hash += 2147483647 + 1;
  3262. const compId = 'c' + hash;
  3263. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  3264. if (GENERATED_COMP_IDS.has(compId)) {
  3265. const previousCompDefType = GENERATED_COMP_IDS.get(compId);
  3266. if (previousCompDefType !== componentDef.type) {
  3267. console.warn(formatRuntimeError(-912 /* RuntimeErrorCode.COMPONENT_ID_COLLISION */, `Component ID generation collision detected. Components '${previousCompDefType.name}' and '${componentDef.type.name}' with selector '${stringifyCSSSelectorList(componentDef
  3268. .selectors)}' generated the same component ID. To fix this, you can change the selector of one of those components or add an extra host attribute to force a different ID.`));
  3269. }
  3270. }
  3271. else {
  3272. GENERATED_COMP_IDS.set(compId, componentDef.type);
  3273. }
  3274. }
  3275. return compId;
  3276. }
  3277. // Below are constants for LView indices to help us look up LView members
  3278. // without having to remember the specific indices.
  3279. // Uglify will inline these when minifying so there shouldn't be a cost.
  3280. const HOST = 0;
  3281. const TVIEW = 1;
  3282. const FLAGS = 2;
  3283. const PARENT = 3;
  3284. const NEXT = 4;
  3285. const DESCENDANT_VIEWS_TO_REFRESH = 5;
  3286. const T_HOST = 6;
  3287. const CLEANUP = 7;
  3288. const CONTEXT = 8;
  3289. const INJECTOR$1 = 9;
  3290. const ENVIRONMENT = 10;
  3291. const RENDERER = 11;
  3292. const CHILD_HEAD = 12;
  3293. const CHILD_TAIL = 13;
  3294. // FIXME(misko): Investigate if the three declarations aren't all same thing.
  3295. const DECLARATION_VIEW = 14;
  3296. const DECLARATION_COMPONENT_VIEW = 15;
  3297. const DECLARATION_LCONTAINER = 16;
  3298. const PREORDER_HOOK_FLAGS = 17;
  3299. const QUERIES = 18;
  3300. const ID = 19;
  3301. const EMBEDDED_VIEW_INJECTOR = 20;
  3302. const ON_DESTROY_HOOKS = 21;
  3303. const HYDRATION = 22;
  3304. const REACTIVE_TEMPLATE_CONSUMER = 23;
  3305. const REACTIVE_HOST_BINDING_CONSUMER = 24;
  3306. /**
  3307. * Size of LView's header. Necessary to adjust for it when setting slots.
  3308. *
  3309. * IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
  3310. * instruction index into `LView` index. All other indexes should be in the `LView` index space and
  3311. * there should be no need to refer to `HEADER_OFFSET` anywhere else.
  3312. */
  3313. const HEADER_OFFSET = 25;
  3314. // Note: This hack is necessary so we don't erroneously get a circular dependency
  3315. // failure based on types.
  3316. const unusedValueExportToPlacateAjd$4 = 1;
  3317. /**
  3318. * Special location which allows easy identification of type. If we have an array which was
  3319. * retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is
  3320. * `LContainer`.
  3321. */
  3322. const TYPE = 1;
  3323. /**
  3324. * Below are constants for LContainer indices to help us look up LContainer members
  3325. * without having to remember the specific indices.
  3326. * Uglify will inline these when minifying so there shouldn't be a cost.
  3327. */
  3328. /**
  3329. * Flag to signify that this `LContainer` may have transplanted views which need to be change
  3330. * detected. (see: `LView[DECLARATION_COMPONENT_VIEW])`.
  3331. *
  3332. * This flag, once set, is never unset for the `LContainer`. This means that when unset we can skip
  3333. * a lot of work in `refreshEmbeddedViews`. But when set we still need to verify
  3334. * that the `MOVED_VIEWS` are transplanted and on-push.
  3335. */
  3336. const HAS_TRANSPLANTED_VIEWS = 2;
  3337. // PARENT, NEXT, DESCENDANT_VIEWS_TO_REFRESH are indices 3, 4, and 5
  3338. // As we already have these constants in LView, we don't need to re-create them.
  3339. // T_HOST is index 6
  3340. // We already have this constants in LView, we don't need to re-create it.
  3341. const NATIVE = 7;
  3342. const VIEW_REFS = 8;
  3343. const MOVED_VIEWS = 9;
  3344. const DEHYDRATED_VIEWS = 10;
  3345. /**
  3346. * Size of LContainer's header. Represents the index after which all views in the
  3347. * container will be inserted. We need to keep a record of current views so we know
  3348. * which views are already in the DOM (and don't need to be re-added) and so we can
  3349. * remove views from the DOM when they are no longer required.
  3350. */
  3351. const CONTAINER_HEADER_OFFSET = 11;
  3352. // Note: This hack is necessary so we don't erroneously get a circular dependency
  3353. // failure based on types.
  3354. const unusedValueExportToPlacateAjd$3 = 1;
  3355. /**
  3356. * True if `value` is `LView`.
  3357. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  3358. */
  3359. function isLView(value) {
  3360. return Array.isArray(value) && typeof value[TYPE] === 'object';
  3361. }
  3362. /**
  3363. * True if `value` is `LContainer`.
  3364. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  3365. */
  3366. function isLContainer(value) {
  3367. return Array.isArray(value) && value[TYPE] === true;
  3368. }
  3369. function isContentQueryHost(tNode) {
  3370. return (tNode.flags & 4 /* TNodeFlags.hasContentQuery */) !== 0;
  3371. }
  3372. function isComponentHost(tNode) {
  3373. return tNode.componentOffset > -1;
  3374. }
  3375. function isDirectiveHost(tNode) {
  3376. return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */;
  3377. }
  3378. function isComponentDef(def) {
  3379. return !!def.template;
  3380. }
  3381. function isRootView(target) {
  3382. return (target[FLAGS] & 512 /* LViewFlags.IsRoot */) !== 0;
  3383. }
  3384. function isProjectionTNode(tNode) {
  3385. return (tNode.type & 16 /* TNodeType.Projection */) === 16 /* TNodeType.Projection */;
  3386. }
  3387. function hasI18n(lView) {
  3388. return (lView[FLAGS] & 32 /* LViewFlags.HasI18n */) === 32 /* LViewFlags.HasI18n */;
  3389. }
  3390. // [Assert functions do not constraint type when they are guarded by a truthy
  3391. // expression.](https://github.com/microsoft/TypeScript/issues/37295)
  3392. function assertTNodeForLView(tNode, lView) {
  3393. assertTNodeForTView(tNode, lView[TVIEW]);
  3394. }
  3395. function assertTNodeForTView(tNode, tView) {
  3396. assertTNode(tNode);
  3397. const tData = tView.data;
  3398. for (let i = HEADER_OFFSET; i < tData.length; i++) {
  3399. if (tData[i] === tNode) {
  3400. return;
  3401. }
  3402. }
  3403. throwError('This TNode does not belong to this TView.');
  3404. }
  3405. function assertTNode(tNode) {
  3406. assertDefined(tNode, 'TNode must be defined');
  3407. if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) {
  3408. throwError('Not of type TNode, got: ' + tNode);
  3409. }
  3410. }
  3411. function assertTIcu(tIcu) {
  3412. assertDefined(tIcu, 'Expected TIcu to be defined');
  3413. if (!(typeof tIcu.currentCaseLViewIndex === 'number')) {
  3414. throwError('Object is not of TIcu type.');
  3415. }
  3416. }
  3417. function assertComponentType(actual, msg = 'Type passed in is not ComponentType, it does not have \'ɵcmp\' property.') {
  3418. if (!getComponentDef$1(actual)) {
  3419. throwError(msg);
  3420. }
  3421. }
  3422. function assertNgModuleType(actual, msg = 'Type passed in is not NgModuleType, it does not have \'ɵmod\' property.') {
  3423. if (!getNgModuleDef(actual)) {
  3424. throwError(msg);
  3425. }
  3426. }
  3427. function assertCurrentTNodeIsParent(isParent) {
  3428. assertEqual(isParent, true, 'currentTNode should be a parent');
  3429. }
  3430. function assertHasParent(tNode) {
  3431. assertDefined(tNode, 'currentTNode should exist!');
  3432. assertDefined(tNode.parent, 'currentTNode should have a parent');
  3433. }
  3434. function assertLContainer(value) {
  3435. assertDefined(value, 'LContainer must be defined');
  3436. assertEqual(isLContainer(value), true, 'Expecting LContainer');
  3437. }
  3438. function assertLViewOrUndefined(value) {
  3439. value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
  3440. }
  3441. function assertLView(value) {
  3442. assertDefined(value, 'LView must be defined');
  3443. assertEqual(isLView(value), true, 'Expecting LView');
  3444. }
  3445. function assertFirstCreatePass(tView, errMessage) {
  3446. assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.');
  3447. }
  3448. function assertFirstUpdatePass(tView, errMessage) {
  3449. assertEqual(tView.firstUpdatePass, true, errMessage || 'Should only be called in first update pass.');
  3450. }
  3451. /**
  3452. * This is a basic sanity check that an object is probably a directive def. DirectiveDef is
  3453. * an interface, so we can't do a direct instanceof check.
  3454. */
  3455. function assertDirectiveDef(obj) {
  3456. if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
  3457. throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
  3458. }
  3459. }
  3460. function assertIndexInDeclRange(lView, index) {
  3461. const tView = lView[1];
  3462. assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index);
  3463. }
  3464. function assertIndexInExpandoRange(lView, index) {
  3465. const tView = lView[1];
  3466. assertBetween(tView.expandoStartIndex, lView.length, index);
  3467. }
  3468. function assertBetween(lower, upper, index) {
  3469. if (!(lower <= index && index < upper)) {
  3470. throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`);
  3471. }
  3472. }
  3473. function assertProjectionSlots(lView, errMessage) {
  3474. assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.');
  3475. assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, errMessage ||
  3476. 'Components with projection nodes (<ng-content>) must have projection slots defined.');
  3477. }
  3478. function assertParentView(lView, errMessage) {
  3479. assertDefined(lView, errMessage || 'Component views should always have a parent view (component\'s host view)');
  3480. }
  3481. /**
  3482. * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a
  3483. * NodeInjector data structure.
  3484. *
  3485. * @param lView `LView` which should be checked.
  3486. * @param injectorIndex index into the `LView` where the `NodeInjector` is expected.
  3487. */
  3488. function assertNodeInjector(lView, injectorIndex) {
  3489. assertIndexInExpandoRange(lView, injectorIndex);
  3490. assertIndexInExpandoRange(lView, injectorIndex + 8 /* NodeInjectorOffset.PARENT */);
  3491. assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter');
  3492. assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter');
  3493. assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter');
  3494. assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter');
  3495. assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter');
  3496. assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter');
  3497. assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter');
  3498. assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter');
  3499. assertNumber(lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */], 'injectorIndex should point to parent injector');
  3500. }
  3501. function getFactoryDef(type, throwNotFound) {
  3502. const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF);
  3503. if (!hasFactoryDef && throwNotFound === true && ngDevMode) {
  3504. throw new Error(`Type ${stringify(type)} does not have 'ɵfac' property.`);
  3505. }
  3506. return hasFactoryDef ? type[NG_FACTORY_DEF] : null;
  3507. }
  3508. /**
  3509. * Symbol used to tell `Signal`s apart from other functions.
  3510. *
  3511. * This can be used to auto-unwrap signals in various cases, or to auto-wrap non-signal values.
  3512. */
  3513. const SIGNAL = /* @__PURE__ */ Symbol('SIGNAL');
  3514. /**
  3515. * Checks if the given `value` is a reactive `Signal`.
  3516. *
  3517. * @developerPreview
  3518. */
  3519. function isSignal(value) {
  3520. return typeof value === 'function' && value[SIGNAL] !== undefined;
  3521. }
  3522. /**
  3523. * The default equality function used for `signal` and `computed`, which treats objects and arrays
  3524. * as never equal, and all other primitive values using identity semantics.
  3525. *
  3526. * This allows signals to hold non-primitive values (arrays, objects, other collections) and still
  3527. * propagate change notification upon explicit mutation without identity change.
  3528. *
  3529. * @developerPreview
  3530. */
  3531. function defaultEquals(a, b) {
  3532. // `Object.is` compares two values using identity semantics which is desired behavior for
  3533. // primitive values. If `Object.is` determines two values to be equal we need to make sure that
  3534. // those don't represent objects (we want to make sure that 2 objects are always considered
  3535. // "unequal"). The null check is needed for the special case of JavaScript reporting null values
  3536. // as objects (`typeof null === 'object'`).
  3537. return (a === null || typeof a !== 'object') && Object.is(a, b);
  3538. }
  3539. // Required as the signals library is in a separate package, so we need to explicitly ensure the
  3540. /**
  3541. * The currently active consumer `ReactiveNode`, if running code in a reactive context.
  3542. *
  3543. * Change this via `setActiveConsumer`.
  3544. */
  3545. let activeConsumer = null;
  3546. let inNotificationPhase = false;
  3547. function setActiveConsumer(consumer) {
  3548. const prev = activeConsumer;
  3549. activeConsumer = consumer;
  3550. return prev;
  3551. }
  3552. const REACTIVE_NODE = {
  3553. version: 0,
  3554. dirty: false,
  3555. producerNode: undefined,
  3556. producerLastReadVersion: undefined,
  3557. producerIndexOfThis: undefined,
  3558. nextProducerIndex: 0,
  3559. liveConsumerNode: undefined,
  3560. liveConsumerIndexOfThis: undefined,
  3561. consumerAllowSignalWrites: false,
  3562. consumerIsAlwaysLive: false,
  3563. producerMustRecompute: () => false,
  3564. producerRecomputeValue: () => { },
  3565. consumerMarkedDirty: () => { },
  3566. };
  3567. /**
  3568. * Called by implementations when a producer's signal is read.
  3569. */
  3570. function producerAccessed(node) {
  3571. if (inNotificationPhase) {
  3572. throw new Error(typeof ngDevMode !== 'undefined' && ngDevMode ?
  3573. `Assertion error: signal read during notification phase` :
  3574. '');
  3575. }
  3576. if (activeConsumer === null) {
  3577. // Accessed outside of a reactive context, so nothing to record.
  3578. return;
  3579. }
  3580. // This producer is the `idx`th dependency of `activeConsumer`.
  3581. const idx = activeConsumer.nextProducerIndex++;
  3582. assertConsumerNode(activeConsumer);
  3583. if (idx < activeConsumer.producerNode.length && activeConsumer.producerNode[idx] !== node) {
  3584. // There's been a change in producers since the last execution of `activeConsumer`.
  3585. // `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
  3586. // replaced with `this`.
  3587. //
  3588. // If `activeConsumer` isn't live, then this is a no-op, since we can replace the producer in
  3589. // `activeConsumer.producerNode` directly. However, if `activeConsumer` is live, then we need
  3590. // to remove it from the stale producer's `liveConsumer`s.
  3591. if (consumerIsLive(activeConsumer)) {
  3592. const staleProducer = activeConsumer.producerNode[idx];
  3593. producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx]);
  3594. // At this point, the only record of `staleProducer` is the reference at
  3595. // `activeConsumer.producerNode[idx]` which will be overwritten below.
  3596. }
  3597. }
  3598. if (activeConsumer.producerNode[idx] !== node) {
  3599. // We're a new dependency of the consumer (at `idx`).
  3600. activeConsumer.producerNode[idx] = node;
  3601. // If the active consumer is live, then add it as a live consumer. If not, then use 0 as a
  3602. // placeholder value.
  3603. activeConsumer.producerIndexOfThis[idx] =
  3604. consumerIsLive(activeConsumer) ? producerAddLiveConsumer(node, activeConsumer, idx) : 0;
  3605. }
  3606. activeConsumer.producerLastReadVersion[idx] = node.version;
  3607. }
  3608. /**
  3609. * Ensure this producer's `version` is up-to-date.
  3610. */
  3611. function producerUpdateValueVersion(node) {
  3612. if (consumerIsLive(node) && !node.dirty) {
  3613. // A live consumer will be marked dirty by producers, so a clean state means that its version
  3614. // is guaranteed to be up-to-date.
  3615. return;
  3616. }
  3617. if (!node.producerMustRecompute(node) && !consumerPollProducersForChange(node)) {
  3618. // None of our producers report a change since the last time they were read, so no
  3619. // recomputation of our value is necessary, and we can consider ourselves clean.
  3620. node.dirty = false;
  3621. return;
  3622. }
  3623. node.producerRecomputeValue(node);
  3624. // After recomputing the value, we're no longer dirty.
  3625. node.dirty = false;
  3626. }
  3627. /**
  3628. * Propagate a dirty notification to live consumers of this producer.
  3629. */
  3630. function producerNotifyConsumers(node) {
  3631. if (node.liveConsumerNode === undefined) {
  3632. return;
  3633. }
  3634. // Prevent signal reads when we're updating the graph
  3635. const prev = inNotificationPhase;
  3636. inNotificationPhase = true;
  3637. try {
  3638. for (const consumer of node.liveConsumerNode) {
  3639. if (!consumer.dirty) {
  3640. consumerMarkDirty(consumer);
  3641. }
  3642. }
  3643. }
  3644. finally {
  3645. inNotificationPhase = prev;
  3646. }
  3647. }
  3648. /**
  3649. * Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
  3650. * based on the current consumer context.
  3651. */
  3652. function producerUpdatesAllowed() {
  3653. return activeConsumer?.consumerAllowSignalWrites !== false;
  3654. }
  3655. function consumerMarkDirty(node) {
  3656. node.dirty = true;
  3657. producerNotifyConsumers(node);
  3658. node.consumerMarkedDirty?.(node);
  3659. }
  3660. /**
  3661. * Prepare this consumer to run a computation in its reactive context.
  3662. *
  3663. * Must be called by subclasses which represent reactive computations, before those computations
  3664. * begin.
  3665. */
  3666. function consumerBeforeComputation(node) {
  3667. node && (node.nextProducerIndex = 0);
  3668. return setActiveConsumer(node);
  3669. }
  3670. /**
  3671. * Finalize this consumer's state after a reactive computation has run.
  3672. *
  3673. * Must be called by subclasses which represent reactive computations, after those computations
  3674. * have finished.
  3675. */
  3676. function consumerAfterComputation(node, prevConsumer) {
  3677. setActiveConsumer(prevConsumer);
  3678. if (!node || node.producerNode === undefined || node.producerIndexOfThis === undefined ||
  3679. node.producerLastReadVersion === undefined) {
  3680. return;
  3681. }
  3682. if (consumerIsLive(node)) {
  3683. // For live consumers, we need to remove the producer -> consumer edge for any stale producers
  3684. // which weren't dependencies after the recomputation.
  3685. for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
  3686. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  3687. }
  3688. }
  3689. // Truncate the producer tracking arrays.
  3690. // Perf note: this is essentially truncating the length to `node.nextProducerIndex`, but
  3691. // benchmarking has shown that individual pop operations are faster.
  3692. while (node.producerNode.length > node.nextProducerIndex) {
  3693. node.producerNode.pop();
  3694. node.producerLastReadVersion.pop();
  3695. node.producerIndexOfThis.pop();
  3696. }
  3697. }
  3698. /**
  3699. * Determine whether this consumer has any dependencies which have changed since the last time
  3700. * they were read.
  3701. */
  3702. function consumerPollProducersForChange(node) {
  3703. assertConsumerNode(node);
  3704. // Poll producers for change.
  3705. for (let i = 0; i < node.producerNode.length; i++) {
  3706. const producer = node.producerNode[i];
  3707. const seenVersion = node.producerLastReadVersion[i];
  3708. // First check the versions. A mismatch means that the producer's value is known to have
  3709. // changed since the last time we read it.
  3710. if (seenVersion !== producer.version) {
  3711. return true;
  3712. }
  3713. // The producer's version is the same as the last time we read it, but it might itself be
  3714. // stale. Force the producer to recompute its version (calculating a new value if necessary).
  3715. producerUpdateValueVersion(producer);
  3716. // Now when we do this check, `producer.version` is guaranteed to be up to date, so if the
  3717. // versions still match then it has not changed since the last time we read it.
  3718. if (seenVersion !== producer.version) {
  3719. return true;
  3720. }
  3721. }
  3722. return false;
  3723. }
  3724. /**
  3725. * Disconnect this consumer from the graph.
  3726. */
  3727. function consumerDestroy(node) {
  3728. assertConsumerNode(node);
  3729. if (consumerIsLive(node)) {
  3730. // Drop all connections from the graph to this node.
  3731. for (let i = 0; i < node.producerNode.length; i++) {
  3732. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  3733. }
  3734. }
  3735. // Truncate all the arrays to drop all connection from this node to the graph.
  3736. node.producerNode.length = node.producerLastReadVersion.length = node.producerIndexOfThis.length =
  3737. 0;
  3738. if (node.liveConsumerNode) {
  3739. node.liveConsumerNode.length = node.liveConsumerIndexOfThis.length = 0;
  3740. }
  3741. }
  3742. /**
  3743. * Add `consumer` as a live consumer of this node.
  3744. *
  3745. * Note that this operation is potentially transitive. If this node becomes live, then it becomes
  3746. * a live consumer of all of its current producers.
  3747. */
  3748. function producerAddLiveConsumer(node, consumer, indexOfThis) {
  3749. assertProducerNode(node);
  3750. assertConsumerNode(node);
  3751. if (node.liveConsumerNode.length === 0) {
  3752. // When going from 0 to 1 live consumers, we become a live consumer to our producers.
  3753. for (let i = 0; i < node.producerNode.length; i++) {
  3754. node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i);
  3755. }
  3756. }
  3757. node.liveConsumerIndexOfThis.push(indexOfThis);
  3758. return node.liveConsumerNode.push(consumer) - 1;
  3759. }
  3760. /**
  3761. * Remove the live consumer at `idx`.
  3762. */
  3763. function producerRemoveLiveConsumerAtIndex(node, idx) {
  3764. assertProducerNode(node);
  3765. assertConsumerNode(node);
  3766. if (typeof ngDevMode !== 'undefined' && ngDevMode && idx >= node.liveConsumerNode.length) {
  3767. throw new Error(`Assertion error: active consumer index ${idx} is out of bounds of ${node.liveConsumerNode.length} consumers)`);
  3768. }
  3769. if (node.liveConsumerNode.length === 1) {
  3770. // When removing the last live consumer, we will no longer be live. We need to remove
  3771. // ourselves from our producers' tracking (which may cause consumer-producers to lose
  3772. // liveness as well).
  3773. for (let i = 0; i < node.producerNode.length; i++) {
  3774. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  3775. }
  3776. }
  3777. // Move the last value of `liveConsumers` into `idx`. Note that if there's only a single
  3778. // live consumer, this is a no-op.
  3779. const lastIdx = node.liveConsumerNode.length - 1;
  3780. node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx];
  3781. node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx];
  3782. // Truncate the array.
  3783. node.liveConsumerNode.length--;
  3784. node.liveConsumerIndexOfThis.length--;
  3785. // If the index is still valid, then we need to fix the index pointer from the producer to this
  3786. // consumer, and update it from `lastIdx` to `idx` (accounting for the move above).
  3787. if (idx < node.liveConsumerNode.length) {
  3788. const idxProducer = node.liveConsumerIndexOfThis[idx];
  3789. const consumer = node.liveConsumerNode[idx];
  3790. assertConsumerNode(consumer);
  3791. consumer.producerIndexOfThis[idxProducer] = idx;
  3792. }
  3793. }
  3794. function consumerIsLive(node) {
  3795. return node.consumerIsAlwaysLive || (node?.liveConsumerNode?.length ?? 0) > 0;
  3796. }
  3797. function assertConsumerNode(node) {
  3798. node.producerNode ??= [];
  3799. node.producerIndexOfThis ??= [];
  3800. node.producerLastReadVersion ??= [];
  3801. }
  3802. function assertProducerNode(node) {
  3803. node.liveConsumerNode ??= [];
  3804. node.liveConsumerIndexOfThis ??= [];
  3805. }
  3806. /**
  3807. * Create a computed `Signal` which derives a reactive value from an expression.
  3808. *
  3809. * @developerPreview
  3810. */
  3811. function computed(computation, options) {
  3812. const node = Object.create(COMPUTED_NODE);
  3813. node.computation = computation;
  3814. options?.equal && (node.equal = options.equal);
  3815. const computed = () => {
  3816. // Check if the value needs updating before returning it.
  3817. producerUpdateValueVersion(node);
  3818. // Record that someone looked at this signal.
  3819. producerAccessed(node);
  3820. if (node.value === ERRORED) {
  3821. throw node.error;
  3822. }
  3823. return node.value;
  3824. };
  3825. computed[SIGNAL] = node;
  3826. return computed;
  3827. }
  3828. /**
  3829. * A dedicated symbol used before a computed value has been calculated for the first time.
  3830. * Explicitly typed as `any` so we can use it as signal's value.
  3831. */
  3832. const UNSET = /* @__PURE__ */ Symbol('UNSET');
  3833. /**
  3834. * A dedicated symbol used in place of a computed signal value to indicate that a given computation
  3835. * is in progress. Used to detect cycles in computation chains.
  3836. * Explicitly typed as `any` so we can use it as signal's value.
  3837. */
  3838. const COMPUTING = /* @__PURE__ */ Symbol('COMPUTING');
  3839. /**
  3840. * A dedicated symbol used in place of a computed signal value to indicate that a given computation
  3841. * failed. The thrown error is cached until the computation gets dirty again.
  3842. * Explicitly typed as `any` so we can use it as signal's value.
  3843. */
  3844. const ERRORED = /* @__PURE__ */ Symbol('ERRORED');
  3845. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  3846. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  3847. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  3848. const COMPUTED_NODE = /* @__PURE__ */ (() => {
  3849. return {
  3850. ...REACTIVE_NODE,
  3851. value: UNSET,
  3852. dirty: true,
  3853. error: null,
  3854. equal: defaultEquals,
  3855. producerMustRecompute(node) {
  3856. // Force a recomputation if there's no current value, or if the current value is in the
  3857. // process of being calculated (which should throw an error).
  3858. return node.value === UNSET || node.value === COMPUTING;
  3859. },
  3860. producerRecomputeValue(node) {
  3861. if (node.value === COMPUTING) {
  3862. // Our computation somehow led to a cyclic read of itself.
  3863. throw new Error('Detected cycle in computations.');
  3864. }
  3865. const oldValue = node.value;
  3866. node.value = COMPUTING;
  3867. const prevConsumer = consumerBeforeComputation(node);
  3868. let newValue;
  3869. try {
  3870. newValue = node.computation();
  3871. }
  3872. catch (err) {
  3873. newValue = ERRORED;
  3874. node.error = err;
  3875. }
  3876. finally {
  3877. consumerAfterComputation(node, prevConsumer);
  3878. }
  3879. if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
  3880. node.equal(oldValue, newValue)) {
  3881. // No change to `valueVersion` - old and new values are
  3882. // semantically equivalent.
  3883. node.value = oldValue;
  3884. return;
  3885. }
  3886. node.value = newValue;
  3887. node.version++;
  3888. },
  3889. };
  3890. })();
  3891. function defaultThrowError() {
  3892. throw new Error();
  3893. }
  3894. let throwInvalidWriteToSignalErrorFn = defaultThrowError;
  3895. function throwInvalidWriteToSignalError() {
  3896. throwInvalidWriteToSignalErrorFn();
  3897. }
  3898. function setThrowInvalidWriteToSignalError(fn) {
  3899. throwInvalidWriteToSignalErrorFn = fn;
  3900. }
  3901. /**
  3902. * If set, called after `WritableSignal`s are updated.
  3903. *
  3904. * This hook can be used to achieve various effects, such as running effects synchronously as part
  3905. * of setting a signal.
  3906. */
  3907. let postSignalSetFn = null;
  3908. /**
  3909. * Create a `Signal` that can be set or updated directly.
  3910. *
  3911. * @developerPreview
  3912. */
  3913. function signal(initialValue, options) {
  3914. const node = Object.create(SIGNAL_NODE);
  3915. node.value = initialValue;
  3916. options?.equal && (node.equal = options.equal);
  3917. function signalFn() {
  3918. producerAccessed(node);
  3919. return node.value;
  3920. }
  3921. signalFn.set = signalSetFn;
  3922. signalFn.update = signalUpdateFn;
  3923. signalFn.mutate = signalMutateFn;
  3924. signalFn.asReadonly = signalAsReadonlyFn;
  3925. signalFn[SIGNAL] = node;
  3926. return signalFn;
  3927. }
  3928. function setPostSignalSetFn(fn) {
  3929. const prev = postSignalSetFn;
  3930. postSignalSetFn = fn;
  3931. return prev;
  3932. }
  3933. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  3934. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  3935. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  3936. const SIGNAL_NODE = /* @__PURE__ */ (() => {
  3937. return {
  3938. ...REACTIVE_NODE,
  3939. equal: defaultEquals,
  3940. readonlyFn: undefined,
  3941. };
  3942. })();
  3943. function signalValueChanged(node) {
  3944. node.version++;
  3945. producerNotifyConsumers(node);
  3946. postSignalSetFn?.();
  3947. }
  3948. function signalSetFn(newValue) {
  3949. const node = this[SIGNAL];
  3950. if (!producerUpdatesAllowed()) {
  3951. throwInvalidWriteToSignalError();
  3952. }
  3953. if (!node.equal(node.value, newValue)) {
  3954. node.value = newValue;
  3955. signalValueChanged(node);
  3956. }
  3957. }
  3958. function signalUpdateFn(updater) {
  3959. if (!producerUpdatesAllowed()) {
  3960. throwInvalidWriteToSignalError();
  3961. }
  3962. signalSetFn.call(this, updater(this[SIGNAL].value));
  3963. }
  3964. function signalMutateFn(mutator) {
  3965. const node = this[SIGNAL];
  3966. if (!producerUpdatesAllowed()) {
  3967. throwInvalidWriteToSignalError();
  3968. }
  3969. // Mutate bypasses equality checks as it's by definition changing the value.
  3970. mutator(node.value);
  3971. signalValueChanged(node);
  3972. }
  3973. function signalAsReadonlyFn() {
  3974. const node = this[SIGNAL];
  3975. if (node.readonlyFn === undefined) {
  3976. const readonlyFn = () => this();
  3977. readonlyFn[SIGNAL] = node;
  3978. node.readonlyFn = readonlyFn;
  3979. }
  3980. return node.readonlyFn;
  3981. }
  3982. /**
  3983. * Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
  3984. * can, optionally, return a value.
  3985. *
  3986. * @developerPreview
  3987. */
  3988. function untracked(nonReactiveReadsFn) {
  3989. const prevConsumer = setActiveConsumer(null);
  3990. // We are not trying to catch any particular errors here, just making sure that the consumers
  3991. // stack is restored in case of errors.
  3992. try {
  3993. return nonReactiveReadsFn();
  3994. }
  3995. finally {
  3996. setActiveConsumer(prevConsumer);
  3997. }
  3998. }
  3999. function watch(fn, schedule, allowSignalWrites) {
  4000. const node = Object.create(WATCH_NODE);
  4001. if (allowSignalWrites) {
  4002. node.consumerAllowSignalWrites = true;
  4003. }
  4004. node.fn = fn;
  4005. node.schedule = schedule;
  4006. const registerOnCleanup = (cleanupFn) => {
  4007. node.cleanupFn = cleanupFn;
  4008. };
  4009. const run = () => {
  4010. node.dirty = false;
  4011. if (node.hasRun && !consumerPollProducersForChange(node)) {
  4012. return;
  4013. }
  4014. node.hasRun = true;
  4015. const prevConsumer = consumerBeforeComputation(node);
  4016. try {
  4017. node.cleanupFn();
  4018. node.cleanupFn = NOOP_CLEANUP_FN;
  4019. node.fn(registerOnCleanup);
  4020. }
  4021. finally {
  4022. consumerAfterComputation(node, prevConsumer);
  4023. }
  4024. };
  4025. node.ref = {
  4026. notify: () => consumerMarkDirty(node),
  4027. run,
  4028. cleanup: () => node.cleanupFn(),
  4029. };
  4030. return node.ref;
  4031. }
  4032. const NOOP_CLEANUP_FN = () => { };
  4033. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  4034. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  4035. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  4036. const WATCH_NODE = /* @__PURE__ */ (() => {
  4037. return {
  4038. ...REACTIVE_NODE,
  4039. consumerIsAlwaysLive: true,
  4040. consumerAllowSignalWrites: false,
  4041. consumerMarkedDirty: (node) => {
  4042. node.schedule(node.ref);
  4043. },
  4044. hasRun: false,
  4045. cleanupFn: NOOP_CLEANUP_FN,
  4046. };
  4047. })();
  4048. function setAlternateWeakRefImpl(impl) {
  4049. // TODO: remove this function
  4050. }
  4051. /**
  4052. * Represents a basic change from a previous to a new value for a single
  4053. * property on a directive instance. Passed as a value in a
  4054. * {@link SimpleChanges} object to the `ngOnChanges` hook.
  4055. *
  4056. * @see {@link OnChanges}
  4057. *
  4058. * @publicApi
  4059. */
  4060. class SimpleChange {
  4061. constructor(previousValue, currentValue, firstChange) {
  4062. this.previousValue = previousValue;
  4063. this.currentValue = currentValue;
  4064. this.firstChange = firstChange;
  4065. }
  4066. /**
  4067. * Check whether the new value is the first value assigned.
  4068. */
  4069. isFirstChange() {
  4070. return this.firstChange;
  4071. }
  4072. }
  4073. /**
  4074. * The NgOnChangesFeature decorates a component with support for the ngOnChanges
  4075. * lifecycle hook, so it should be included in any component that implements
  4076. * that hook.
  4077. *
  4078. * If the component or directive uses inheritance, the NgOnChangesFeature MUST
  4079. * be included as a feature AFTER {@link InheritDefinitionFeature}, otherwise
  4080. * inherited properties will not be propagated to the ngOnChanges lifecycle
  4081. * hook.
  4082. *
  4083. * Example usage:
  4084. *
  4085. * ```
  4086. * static ɵcmp = defineComponent({
  4087. * ...
  4088. * inputs: {name: 'publicName'},
  4089. * features: [NgOnChangesFeature]
  4090. * });
  4091. * ```
  4092. *
  4093. * @codeGenApi
  4094. */
  4095. function ɵɵNgOnChangesFeature() {
  4096. return NgOnChangesFeatureImpl;
  4097. }
  4098. function NgOnChangesFeatureImpl(definition) {
  4099. if (definition.type.prototype.ngOnChanges) {
  4100. definition.setInput = ngOnChangesSetInput;
  4101. }
  4102. return rememberChangeHistoryAndInvokeOnChangesHook;
  4103. }
  4104. // This option ensures that the ngOnChanges lifecycle hook will be inherited
  4105. // from superclasses (in InheritDefinitionFeature).
  4106. /** @nocollapse */
  4107. // tslint:disable-next-line:no-toplevel-property-access
  4108. ɵɵNgOnChangesFeature.ngInherit = true;
  4109. /**
  4110. * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate
  4111. * `ngOnChanges`.
  4112. *
  4113. * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are
  4114. * found it invokes `ngOnChanges` on the component instance.
  4115. *
  4116. * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`,
  4117. * it is guaranteed to be called with component instance.
  4118. */
  4119. function rememberChangeHistoryAndInvokeOnChangesHook() {
  4120. const simpleChangesStore = getSimpleChangesStore(this);
  4121. const current = simpleChangesStore?.current;
  4122. if (current) {
  4123. const previous = simpleChangesStore.previous;
  4124. if (previous === EMPTY_OBJ) {
  4125. simpleChangesStore.previous = current;
  4126. }
  4127. else {
  4128. // New changes are copied to the previous store, so that we don't lose history for inputs
  4129. // which were not changed this time
  4130. for (let key in current) {
  4131. previous[key] = current[key];
  4132. }
  4133. }
  4134. simpleChangesStore.current = null;
  4135. this.ngOnChanges(current);
  4136. }
  4137. }
  4138. function ngOnChangesSetInput(instance, value, publicName, privateName) {
  4139. const declaredName = this.declaredInputs[publicName];
  4140. ngDevMode && assertString(declaredName, 'Name of input in ngOnChanges has to be a string');
  4141. const simpleChangesStore = getSimpleChangesStore(instance) ||
  4142. setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null });
  4143. const current = simpleChangesStore.current || (simpleChangesStore.current = {});
  4144. const previous = simpleChangesStore.previous;
  4145. const previousChange = previous[declaredName];
  4146. current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ);
  4147. instance[privateName] = value;
  4148. }
  4149. const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__';
  4150. function getSimpleChangesStore(instance) {
  4151. return instance[SIMPLE_CHANGES_STORE] || null;
  4152. }
  4153. function setSimpleChangesStore(instance, store) {
  4154. return instance[SIMPLE_CHANGES_STORE] = store;
  4155. }
  4156. let profilerCallback = null;
  4157. /**
  4158. * Sets the callback function which will be invoked before and after performing certain actions at
  4159. * runtime (for example, before and after running change detection).
  4160. *
  4161. * Warning: this function is *INTERNAL* and should not be relied upon in application's code.
  4162. * The contract of the function might be changed in any release and/or the function can be removed
  4163. * completely.
  4164. *
  4165. * @param profiler function provided by the caller or null value to disable profiling.
  4166. */
  4167. const setProfiler = (profiler) => {
  4168. profilerCallback = profiler;
  4169. };
  4170. /**
  4171. * Profiler function which wraps user code executed by the runtime.
  4172. *
  4173. * @param event ProfilerEvent corresponding to the execution context
  4174. * @param instance component instance
  4175. * @param hookOrListener lifecycle hook function or output listener. The value depends on the
  4176. * execution context
  4177. * @returns
  4178. */
  4179. const profiler = function (event, instance, hookOrListener) {
  4180. if (profilerCallback != null /* both `null` and `undefined` */) {
  4181. profilerCallback(event, instance, hookOrListener);
  4182. }
  4183. };
  4184. const SVG_NAMESPACE = 'svg';
  4185. const MATH_ML_NAMESPACE = 'math';
  4186. /**
  4187. * For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
  4188. * in same location in `LView`. This is because we don't want to pre-allocate space for it
  4189. * because the storage is sparse. This file contains utilities for dealing with such data types.
  4190. *
  4191. * How do we know what is stored at a given location in `LView`.
  4192. * - `Array.isArray(value) === false` => `RNode` (The normal storage value)
  4193. * - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value.
  4194. * - `typeof value[TYPE] === 'object'` => `LView`
  4195. * - This happens when we have a component at a given location
  4196. * - `typeof value[TYPE] === true` => `LContainer`
  4197. * - This happens when we have `LContainer` binding at a given location.
  4198. *
  4199. *
  4200. * NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.
  4201. */
  4202. /**
  4203. * Returns `RNode`.
  4204. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  4205. */
  4206. function unwrapRNode(value) {
  4207. while (Array.isArray(value)) {
  4208. value = value[HOST];
  4209. }
  4210. return value;
  4211. }
  4212. /**
  4213. * Returns `LView` or `null` if not found.
  4214. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  4215. */
  4216. function unwrapLView(value) {
  4217. while (Array.isArray(value)) {
  4218. // This check is same as `isLView()` but we don't call at as we don't want to call
  4219. // `Array.isArray()` twice and give JITer more work for inlining.
  4220. if (typeof value[TYPE] === 'object')
  4221. return value;
  4222. value = value[HOST];
  4223. }
  4224. return null;
  4225. }
  4226. /**
  4227. * Retrieves an element value from the provided `viewData`, by unwrapping
  4228. * from any containers, component views, or style contexts.
  4229. */
  4230. function getNativeByIndex(index, lView) {
  4231. ngDevMode && assertIndexInRange(lView, index);
  4232. ngDevMode && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Expected to be past HEADER_OFFSET');
  4233. return unwrapRNode(lView[index]);
  4234. }
  4235. /**
  4236. * Retrieve an `RNode` for a given `TNode` and `LView`.
  4237. *
  4238. * This function guarantees in dev mode to retrieve a non-null `RNode`.
  4239. *
  4240. * @param tNode
  4241. * @param lView
  4242. */
  4243. function getNativeByTNode(tNode, lView) {
  4244. ngDevMode && assertTNodeForLView(tNode, lView);
  4245. ngDevMode && assertIndexInRange(lView, tNode.index);
  4246. const node = unwrapRNode(lView[tNode.index]);
  4247. return node;
  4248. }
  4249. /**
  4250. * Retrieve an `RNode` or `null` for a given `TNode` and `LView`.
  4251. *
  4252. * Some `TNode`s don't have associated `RNode`s. For example `Projection`
  4253. *
  4254. * @param tNode
  4255. * @param lView
  4256. */
  4257. function getNativeByTNodeOrNull(tNode, lView) {
  4258. const index = tNode === null ? -1 : tNode.index;
  4259. if (index !== -1) {
  4260. ngDevMode && assertTNodeForLView(tNode, lView);
  4261. const node = unwrapRNode(lView[index]);
  4262. return node;
  4263. }
  4264. return null;
  4265. }
  4266. // fixme(misko): The return Type should be `TNode|null`
  4267. function getTNode(tView, index) {
  4268. ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
  4269. ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode');
  4270. const tNode = tView.data[index];
  4271. ngDevMode && tNode !== null && assertTNode(tNode);
  4272. return tNode;
  4273. }
  4274. /** Retrieves a value from any `LView` or `TData`. */
  4275. function load(view, index) {
  4276. ngDevMode && assertIndexInRange(view, index);
  4277. return view[index];
  4278. }
  4279. function getComponentLViewByIndex(nodeIndex, hostView) {
  4280. // Could be an LView or an LContainer. If LContainer, unwrap to find LView.
  4281. ngDevMode && assertIndexInRange(hostView, nodeIndex);
  4282. const slotValue = hostView[nodeIndex];
  4283. const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
  4284. return lView;
  4285. }
  4286. /** Checks whether a given view is in creation mode */
  4287. function isCreationMode(view) {
  4288. return (view[FLAGS] & 4 /* LViewFlags.CreationMode */) === 4 /* LViewFlags.CreationMode */;
  4289. }
  4290. /**
  4291. * Returns a boolean for whether the view is attached to the change detection tree.
  4292. *
  4293. * Note: This determines whether a view should be checked, not whether it's inserted
  4294. * into a container. For that, you'll want `viewAttachedToContainer` below.
  4295. */
  4296. function viewAttachedToChangeDetector(view) {
  4297. return (view[FLAGS] & 128 /* LViewFlags.Attached */) === 128 /* LViewFlags.Attached */;
  4298. }
  4299. /** Returns a boolean for whether the view is attached to a container. */
  4300. function viewAttachedToContainer(view) {
  4301. return isLContainer(view[PARENT]);
  4302. }
  4303. function getConstant(consts, index) {
  4304. if (index === null || index === undefined)
  4305. return null;
  4306. ngDevMode && assertIndexInRange(consts, index);
  4307. return consts[index];
  4308. }
  4309. /**
  4310. * Resets the pre-order hook flags of the view.
  4311. * @param lView the LView on which the flags are reset
  4312. */
  4313. function resetPreOrderHookFlags(lView) {
  4314. lView[PREORDER_HOOK_FLAGS] = 0;
  4315. }
  4316. /**
  4317. * Adds the `RefreshView` flag from the lView and updates DESCENDANT_VIEWS_TO_REFRESH counters of
  4318. * parents.
  4319. */
  4320. function markViewForRefresh(lView) {
  4321. if ((lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) === 0) {
  4322. lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
  4323. updateViewsToRefresh(lView, 1);
  4324. }
  4325. }
  4326. /**
  4327. * Removes the `RefreshView` flag from the lView and updates DESCENDANT_VIEWS_TO_REFRESH counters of
  4328. * parents.
  4329. */
  4330. function clearViewRefreshFlag(lView) {
  4331. if (lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) {
  4332. lView[FLAGS] &= ~1024 /* LViewFlags.RefreshView */;
  4333. updateViewsToRefresh(lView, -1);
  4334. }
  4335. }
  4336. /**
  4337. * Updates the `DESCENDANT_VIEWS_TO_REFRESH` counter on the parents of the `LView` as well as the
  4338. * parents above that whose
  4339. * 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh
  4340. * or
  4341. * 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh
  4342. */
  4343. function updateViewsToRefresh(lView, amount) {
  4344. let parent = lView[PARENT];
  4345. if (parent === null) {
  4346. return;
  4347. }
  4348. parent[DESCENDANT_VIEWS_TO_REFRESH] += amount;
  4349. let viewOrContainer = parent;
  4350. parent = parent[PARENT];
  4351. while (parent !== null &&
  4352. ((amount === 1 && viewOrContainer[DESCENDANT_VIEWS_TO_REFRESH] === 1) ||
  4353. (amount === -1 && viewOrContainer[DESCENDANT_VIEWS_TO_REFRESH] === 0))) {
  4354. parent[DESCENDANT_VIEWS_TO_REFRESH] += amount;
  4355. viewOrContainer = parent;
  4356. parent = parent[PARENT];
  4357. }
  4358. }
  4359. /**
  4360. * Stores a LView-specific destroy callback.
  4361. */
  4362. function storeLViewOnDestroy(lView, onDestroyCallback) {
  4363. if ((lView[FLAGS] & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */) {
  4364. throw new RuntimeError(911 /* RuntimeErrorCode.VIEW_ALREADY_DESTROYED */, ngDevMode && 'View has already been destroyed.');
  4365. }
  4366. if (lView[ON_DESTROY_HOOKS] === null) {
  4367. lView[ON_DESTROY_HOOKS] = [];
  4368. }
  4369. lView[ON_DESTROY_HOOKS].push(onDestroyCallback);
  4370. }
  4371. /**
  4372. * Removes previously registered LView-specific destroy callback.
  4373. */
  4374. function removeLViewOnDestroy(lView, onDestroyCallback) {
  4375. if (lView[ON_DESTROY_HOOKS] === null)
  4376. return;
  4377. const destroyCBIdx = lView[ON_DESTROY_HOOKS].indexOf(onDestroyCallback);
  4378. if (destroyCBIdx !== -1) {
  4379. lView[ON_DESTROY_HOOKS].splice(destroyCBIdx, 1);
  4380. }
  4381. }
  4382. const instructionState = {
  4383. lFrame: createLFrame(null),
  4384. bindingsEnabled: true,
  4385. skipHydrationRootTNode: null,
  4386. };
  4387. /**
  4388. * In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
  4389. *
  4390. * Necessary to support ChangeDetectorRef.checkNoChanges().
  4391. *
  4392. * The `checkNoChanges` function is invoked only in ngDevMode=true and verifies that no unintended
  4393. * changes exist in the change detector or its children.
  4394. */
  4395. let _isInCheckNoChangesMode = false;
  4396. /**
  4397. * Returns true if the instruction state stack is empty.
  4398. *
  4399. * Intended to be called from tests only (tree shaken otherwise).
  4400. */
  4401. function specOnlyIsInstructionStateEmpty() {
  4402. return instructionState.lFrame.parent === null;
  4403. }
  4404. function getElementDepthCount() {
  4405. return instructionState.lFrame.elementDepthCount;
  4406. }
  4407. function increaseElementDepthCount() {
  4408. instructionState.lFrame.elementDepthCount++;
  4409. }
  4410. function decreaseElementDepthCount() {
  4411. instructionState.lFrame.elementDepthCount--;
  4412. }
  4413. function getBindingsEnabled() {
  4414. return instructionState.bindingsEnabled;
  4415. }
  4416. /**
  4417. * Returns true if currently inside a skip hydration block.
  4418. * @returns boolean
  4419. */
  4420. function isInSkipHydrationBlock$1() {
  4421. return instructionState.skipHydrationRootTNode !== null;
  4422. }
  4423. /**
  4424. * Returns true if this is the root TNode of the skip hydration block.
  4425. * @param tNode the current TNode
  4426. * @returns boolean
  4427. */
  4428. function isSkipHydrationRootTNode(tNode) {
  4429. return instructionState.skipHydrationRootTNode === tNode;
  4430. }
  4431. /**
  4432. * Enables directive matching on elements.
  4433. *
  4434. * * Example:
  4435. * ```
  4436. * <my-comp my-directive>
  4437. * Should match component / directive.
  4438. * </my-comp>
  4439. * <div ngNonBindable>
  4440. * <!-- ɵɵdisableBindings() -->
  4441. * <my-comp my-directive>
  4442. * Should not match component / directive because we are in ngNonBindable.
  4443. * </my-comp>
  4444. * <!-- ɵɵenableBindings() -->
  4445. * </div>
  4446. * ```
  4447. *
  4448. * @codeGenApi
  4449. */
  4450. function ɵɵenableBindings() {
  4451. instructionState.bindingsEnabled = true;
  4452. }
  4453. /**
  4454. * Sets a flag to specify that the TNode is in a skip hydration block.
  4455. * @param tNode the current TNode
  4456. */
  4457. function enterSkipHydrationBlock(tNode) {
  4458. instructionState.skipHydrationRootTNode = tNode;
  4459. }
  4460. /**
  4461. * Disables directive matching on element.
  4462. *
  4463. * * Example:
  4464. * ```
  4465. * <my-comp my-directive>
  4466. * Should match component / directive.
  4467. * </my-comp>
  4468. * <div ngNonBindable>
  4469. * <!-- ɵɵdisableBindings() -->
  4470. * <my-comp my-directive>
  4471. * Should not match component / directive because we are in ngNonBindable.
  4472. * </my-comp>
  4473. * <!-- ɵɵenableBindings() -->
  4474. * </div>
  4475. * ```
  4476. *
  4477. * @codeGenApi
  4478. */
  4479. function ɵɵdisableBindings() {
  4480. instructionState.bindingsEnabled = false;
  4481. }
  4482. /**
  4483. * Clears the root skip hydration node when leaving a skip hydration block.
  4484. */
  4485. function leaveSkipHydrationBlock() {
  4486. instructionState.skipHydrationRootTNode = null;
  4487. }
  4488. /**
  4489. * Return the current `LView`.
  4490. */
  4491. function getLView() {
  4492. return instructionState.lFrame.lView;
  4493. }
  4494. /**
  4495. * Return the current `TView`.
  4496. */
  4497. function getTView() {
  4498. return instructionState.lFrame.tView;
  4499. }
  4500. /**
  4501. * Restores `contextViewData` to the given OpaqueViewState instance.
  4502. *
  4503. * Used in conjunction with the getCurrentView() instruction to save a snapshot
  4504. * of the current view and restore it when listeners are invoked. This allows
  4505. * walking the declaration view tree in listeners to get vars from parent views.
  4506. *
  4507. * @param viewToRestore The OpaqueViewState instance to restore.
  4508. * @returns Context of the restored OpaqueViewState instance.
  4509. *
  4510. * @codeGenApi
  4511. */
  4512. function ɵɵrestoreView(viewToRestore) {
  4513. instructionState.lFrame.contextLView = viewToRestore;
  4514. return viewToRestore[CONTEXT];
  4515. }
  4516. /**
  4517. * Clears the view set in `ɵɵrestoreView` from memory. Returns the passed in
  4518. * value so that it can be used as a return value of an instruction.
  4519. *
  4520. * @codeGenApi
  4521. */
  4522. function ɵɵresetView(value) {
  4523. instructionState.lFrame.contextLView = null;
  4524. return value;
  4525. }
  4526. function getCurrentTNode() {
  4527. let currentTNode = getCurrentTNodePlaceholderOk();
  4528. while (currentTNode !== null && currentTNode.type === 64 /* TNodeType.Placeholder */) {
  4529. currentTNode = currentTNode.parent;
  4530. }
  4531. return currentTNode;
  4532. }
  4533. function getCurrentTNodePlaceholderOk() {
  4534. return instructionState.lFrame.currentTNode;
  4535. }
  4536. function getCurrentParentTNode() {
  4537. const lFrame = instructionState.lFrame;
  4538. const currentTNode = lFrame.currentTNode;
  4539. return lFrame.isParent ? currentTNode : currentTNode.parent;
  4540. }
  4541. function setCurrentTNode(tNode, isParent) {
  4542. ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView);
  4543. const lFrame = instructionState.lFrame;
  4544. lFrame.currentTNode = tNode;
  4545. lFrame.isParent = isParent;
  4546. }
  4547. function isCurrentTNodeParent() {
  4548. return instructionState.lFrame.isParent;
  4549. }
  4550. function setCurrentTNodeAsNotParent() {
  4551. instructionState.lFrame.isParent = false;
  4552. }
  4553. function getContextLView() {
  4554. const contextLView = instructionState.lFrame.contextLView;
  4555. ngDevMode && assertDefined(contextLView, 'contextLView must be defined.');
  4556. return contextLView;
  4557. }
  4558. function isInCheckNoChangesMode() {
  4559. !ngDevMode && throwError('Must never be called in production mode');
  4560. return _isInCheckNoChangesMode;
  4561. }
  4562. function setIsInCheckNoChangesMode(mode) {
  4563. !ngDevMode && throwError('Must never be called in production mode');
  4564. _isInCheckNoChangesMode = mode;
  4565. }
  4566. // top level variables should not be exported for performance reasons (PERF_NOTES.md)
  4567. function getBindingRoot() {
  4568. const lFrame = instructionState.lFrame;
  4569. let index = lFrame.bindingRootIndex;
  4570. if (index === -1) {
  4571. index = lFrame.bindingRootIndex = lFrame.tView.bindingStartIndex;
  4572. }
  4573. return index;
  4574. }
  4575. function getBindingIndex() {
  4576. return instructionState.lFrame.bindingIndex;
  4577. }
  4578. function setBindingIndex(value) {
  4579. return instructionState.lFrame.bindingIndex = value;
  4580. }
  4581. function nextBindingIndex() {
  4582. return instructionState.lFrame.bindingIndex++;
  4583. }
  4584. function incrementBindingIndex(count) {
  4585. const lFrame = instructionState.lFrame;
  4586. const index = lFrame.bindingIndex;
  4587. lFrame.bindingIndex = lFrame.bindingIndex + count;
  4588. return index;
  4589. }
  4590. function isInI18nBlock() {
  4591. return instructionState.lFrame.inI18n;
  4592. }
  4593. function setInI18nBlock(isInI18nBlock) {
  4594. instructionState.lFrame.inI18n = isInI18nBlock;
  4595. }
  4596. /**
  4597. * Set a new binding root index so that host template functions can execute.
  4598. *
  4599. * Bindings inside the host template are 0 index. But because we don't know ahead of time
  4600. * how many host bindings we have we can't pre-compute them. For this reason they are all
  4601. * 0 index and we just shift the root so that they match next available location in the LView.
  4602. *
  4603. * @param bindingRootIndex Root index for `hostBindings`
  4604. * @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive
  4605. * whose `hostBindings` are being processed.
  4606. */
  4607. function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) {
  4608. const lFrame = instructionState.lFrame;
  4609. lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex;
  4610. setCurrentDirectiveIndex(currentDirectiveIndex);
  4611. }
  4612. /**
  4613. * When host binding is executing this points to the directive index.
  4614. * `TView.data[getCurrentDirectiveIndex()]` is `DirectiveDef`
  4615. * `LView[getCurrentDirectiveIndex()]` is directive instance.
  4616. */
  4617. function getCurrentDirectiveIndex() {
  4618. return instructionState.lFrame.currentDirectiveIndex;
  4619. }
  4620. /**
  4621. * Sets an index of a directive whose `hostBindings` are being processed.
  4622. *
  4623. * @param currentDirectiveIndex `TData` index where current directive instance can be found.
  4624. */
  4625. function setCurrentDirectiveIndex(currentDirectiveIndex) {
  4626. instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex;
  4627. }
  4628. /**
  4629. * Retrieve the current `DirectiveDef` which is active when `hostBindings` instruction is being
  4630. * executed.
  4631. *
  4632. * @param tData Current `TData` where the `DirectiveDef` will be looked up at.
  4633. */
  4634. function getCurrentDirectiveDef(tData) {
  4635. const currentDirectiveIndex = instructionState.lFrame.currentDirectiveIndex;
  4636. return currentDirectiveIndex === -1 ? null : tData[currentDirectiveIndex];
  4637. }
  4638. function getCurrentQueryIndex() {
  4639. return instructionState.lFrame.currentQueryIndex;
  4640. }
  4641. function setCurrentQueryIndex(value) {
  4642. instructionState.lFrame.currentQueryIndex = value;
  4643. }
  4644. /**
  4645. * Returns a `TNode` of the location where the current `LView` is declared at.
  4646. *
  4647. * @param lView an `LView` that we want to find parent `TNode` for.
  4648. */
  4649. function getDeclarationTNode(lView) {
  4650. const tView = lView[TVIEW];
  4651. // Return the declaration parent for embedded views
  4652. if (tView.type === 2 /* TViewType.Embedded */) {
  4653. ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
  4654. return tView.declTNode;
  4655. }
  4656. // Components don't have `TView.declTNode` because each instance of component could be
  4657. // inserted in different location, hence `TView.declTNode` is meaningless.
  4658. // Falling back to `T_HOST` in case we cross component boundary.
  4659. if (tView.type === 1 /* TViewType.Component */) {
  4660. return lView[T_HOST];
  4661. }
  4662. // Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode.
  4663. return null;
  4664. }
  4665. /**
  4666. * This is a light weight version of the `enterView` which is needed by the DI system.
  4667. *
  4668. * @param lView `LView` location of the DI context.
  4669. * @param tNode `TNode` for DI context
  4670. * @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration
  4671. * tree from `tNode` until we find parent declared `TElementNode`.
  4672. * @returns `true` if we have successfully entered DI associated with `tNode` (or with declared
  4673. * `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated
  4674. * `NodeInjector` can be found and we should instead use `ModuleInjector`.
  4675. * - If `true` than this call must be fallowed by `leaveDI`
  4676. * - If `false` than this call failed and we should NOT call `leaveDI`
  4677. */
  4678. function enterDI(lView, tNode, flags) {
  4679. ngDevMode && assertLViewOrUndefined(lView);
  4680. if (flags & InjectFlags.SkipSelf) {
  4681. ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]);
  4682. let parentTNode = tNode;
  4683. let parentLView = lView;
  4684. while (true) {
  4685. ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined');
  4686. parentTNode = parentTNode.parent;
  4687. if (parentTNode === null && !(flags & InjectFlags.Host)) {
  4688. parentTNode = getDeclarationTNode(parentLView);
  4689. if (parentTNode === null)
  4690. break;
  4691. // In this case, a parent exists and is definitely an element. So it will definitely
  4692. // have an existing lView as the declaration view, which is why we can assume it's defined.
  4693. ngDevMode && assertDefined(parentLView, 'Parent LView should be defined');
  4694. parentLView = parentLView[DECLARATION_VIEW];
  4695. // In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives
  4696. // We want to skip those and look only at Elements and ElementContainers to ensure
  4697. // we're looking at true parent nodes, and not content or other types.
  4698. if (parentTNode.type & (2 /* TNodeType.Element */ | 8 /* TNodeType.ElementContainer */)) {
  4699. break;
  4700. }
  4701. }
  4702. else {
  4703. break;
  4704. }
  4705. }
  4706. if (parentTNode === null) {
  4707. // If we failed to find a parent TNode this means that we should use module injector.
  4708. return false;
  4709. }
  4710. else {
  4711. tNode = parentTNode;
  4712. lView = parentLView;
  4713. }
  4714. }
  4715. ngDevMode && assertTNodeForLView(tNode, lView);
  4716. const lFrame = instructionState.lFrame = allocLFrame();
  4717. lFrame.currentTNode = tNode;
  4718. lFrame.lView = lView;
  4719. return true;
  4720. }
  4721. /**
  4722. * Swap the current lView with a new lView.
  4723. *
  4724. * For performance reasons we store the lView in the top level of the module.
  4725. * This way we minimize the number of properties to read. Whenever a new view
  4726. * is entered we have to store the lView for later, and when the view is
  4727. * exited the state has to be restored
  4728. *
  4729. * @param newView New lView to become active
  4730. * @returns the previously active lView;
  4731. */
  4732. function enterView(newView) {
  4733. ngDevMode && assertNotEqual(newView[0], newView[1], '????');
  4734. ngDevMode && assertLViewOrUndefined(newView);
  4735. const newLFrame = allocLFrame();
  4736. if (ngDevMode) {
  4737. assertEqual(newLFrame.isParent, true, 'Expected clean LFrame');
  4738. assertEqual(newLFrame.lView, null, 'Expected clean LFrame');
  4739. assertEqual(newLFrame.tView, null, 'Expected clean LFrame');
  4740. assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame');
  4741. assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
  4742. assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
  4743. assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
  4744. assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
  4745. assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
  4746. }
  4747. const tView = newView[TVIEW];
  4748. instructionState.lFrame = newLFrame;
  4749. ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView);
  4750. newLFrame.currentTNode = tView.firstChild;
  4751. newLFrame.lView = newView;
  4752. newLFrame.tView = tView;
  4753. newLFrame.contextLView = newView;
  4754. newLFrame.bindingIndex = tView.bindingStartIndex;
  4755. newLFrame.inI18n = false;
  4756. }
  4757. /**
  4758. * Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
  4759. */
  4760. function allocLFrame() {
  4761. const currentLFrame = instructionState.lFrame;
  4762. const childLFrame = currentLFrame === null ? null : currentLFrame.child;
  4763. const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
  4764. return newLFrame;
  4765. }
  4766. function createLFrame(parent) {
  4767. const lFrame = {
  4768. currentTNode: null,
  4769. isParent: true,
  4770. lView: null,
  4771. tView: null,
  4772. selectedIndex: -1,
  4773. contextLView: null,
  4774. elementDepthCount: 0,
  4775. currentNamespace: null,
  4776. currentDirectiveIndex: -1,
  4777. bindingRootIndex: -1,
  4778. bindingIndex: -1,
  4779. currentQueryIndex: 0,
  4780. parent: parent,
  4781. child: null,
  4782. inI18n: false,
  4783. };
  4784. parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
  4785. return lFrame;
  4786. }
  4787. /**
  4788. * A lightweight version of leave which is used with DI.
  4789. *
  4790. * This function only resets `currentTNode` and `LView` as those are the only properties
  4791. * used with DI (`enterDI()`).
  4792. *
  4793. * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where
  4794. * as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`.
  4795. */
  4796. function leaveViewLight() {
  4797. const oldLFrame = instructionState.lFrame;
  4798. instructionState.lFrame = oldLFrame.parent;
  4799. oldLFrame.currentTNode = null;
  4800. oldLFrame.lView = null;
  4801. return oldLFrame;
  4802. }
  4803. /**
  4804. * This is a lightweight version of the `leaveView` which is needed by the DI system.
  4805. *
  4806. * NOTE: this function is an alias so that we can change the type of the function to have `void`
  4807. * return type.
  4808. */
  4809. const leaveDI = leaveViewLight;
  4810. /**
  4811. * Leave the current `LView`
  4812. *
  4813. * This pops the `LFrame` with the associated `LView` from the stack.
  4814. *
  4815. * IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is
  4816. * because for performance reasons we don't release `LFrame` but rather keep it for next use.
  4817. */
  4818. function leaveView() {
  4819. const oldLFrame = leaveViewLight();
  4820. oldLFrame.isParent = true;
  4821. oldLFrame.tView = null;
  4822. oldLFrame.selectedIndex = -1;
  4823. oldLFrame.contextLView = null;
  4824. oldLFrame.elementDepthCount = 0;
  4825. oldLFrame.currentDirectiveIndex = -1;
  4826. oldLFrame.currentNamespace = null;
  4827. oldLFrame.bindingRootIndex = -1;
  4828. oldLFrame.bindingIndex = -1;
  4829. oldLFrame.currentQueryIndex = 0;
  4830. }
  4831. function nextContextImpl(level) {
  4832. const contextLView = instructionState.lFrame.contextLView =
  4833. walkUpViews(level, instructionState.lFrame.contextLView);
  4834. return contextLView[CONTEXT];
  4835. }
  4836. function walkUpViews(nestingLevel, currentView) {
  4837. while (nestingLevel > 0) {
  4838. ngDevMode &&
  4839. assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.');
  4840. currentView = currentView[DECLARATION_VIEW];
  4841. nestingLevel--;
  4842. }
  4843. return currentView;
  4844. }
  4845. /**
  4846. * Gets the currently selected element index.
  4847. *
  4848. * Used with {@link property} instruction (and more in the future) to identify the index in the
  4849. * current `LView` to act on.
  4850. */
  4851. function getSelectedIndex() {
  4852. return instructionState.lFrame.selectedIndex;
  4853. }
  4854. /**
  4855. * Sets the most recent index passed to {@link select}
  4856. *
  4857. * Used with {@link property} instruction (and more in the future) to identify the index in the
  4858. * current `LView` to act on.
  4859. *
  4860. * (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
  4861. * run if and when the provided `index` value is different from the current selected index value.)
  4862. */
  4863. function setSelectedIndex(index) {
  4864. ngDevMode && index !== -1 &&
  4865. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).');
  4866. ngDevMode &&
  4867. assertLessThan(index, instructionState.lFrame.lView.length, 'Can\'t set index passed end of LView');
  4868. instructionState.lFrame.selectedIndex = index;
  4869. }
  4870. /**
  4871. * Gets the `tNode` that represents currently selected element.
  4872. */
  4873. function getSelectedTNode() {
  4874. const lFrame = instructionState.lFrame;
  4875. return getTNode(lFrame.tView, lFrame.selectedIndex);
  4876. }
  4877. /**
  4878. * Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state.
  4879. *
  4880. * @codeGenApi
  4881. */
  4882. function ɵɵnamespaceSVG() {
  4883. instructionState.lFrame.currentNamespace = SVG_NAMESPACE;
  4884. }
  4885. /**
  4886. * Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state.
  4887. *
  4888. * @codeGenApi
  4889. */
  4890. function ɵɵnamespaceMathML() {
  4891. instructionState.lFrame.currentNamespace = MATH_ML_NAMESPACE;
  4892. }
  4893. /**
  4894. * Sets the namespace used to create elements to `null`, which forces element creation to use
  4895. * `createElement` rather than `createElementNS`.
  4896. *
  4897. * @codeGenApi
  4898. */
  4899. function ɵɵnamespaceHTML() {
  4900. namespaceHTMLInternal();
  4901. }
  4902. /**
  4903. * Sets the namespace used to create elements to `null`, which forces element creation to use
  4904. * `createElement` rather than `createElementNS`.
  4905. */
  4906. function namespaceHTMLInternal() {
  4907. instructionState.lFrame.currentNamespace = null;
  4908. }
  4909. function getNamespace$1() {
  4910. return instructionState.lFrame.currentNamespace;
  4911. }
  4912. let _wasLastNodeCreated = true;
  4913. /**
  4914. * Retrieves a global flag that indicates whether the most recent DOM node
  4915. * was created or hydrated.
  4916. */
  4917. function wasLastNodeCreated() {
  4918. return _wasLastNodeCreated;
  4919. }
  4920. /**
  4921. * Sets a global flag to indicate whether the most recent DOM node
  4922. * was created or hydrated.
  4923. */
  4924. function lastNodeWasCreated(flag) {
  4925. _wasLastNodeCreated = flag;
  4926. }
  4927. /**
  4928. * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`.
  4929. *
  4930. * Must be run *only* on the first template pass.
  4931. *
  4932. * Sets up the pre-order hooks on the provided `tView`,
  4933. * see {@link HookData} for details about the data structure.
  4934. *
  4935. * @param directiveIndex The index of the directive in LView
  4936. * @param directiveDef The definition containing the hooks to setup in tView
  4937. * @param tView The current TView
  4938. */
  4939. function registerPreOrderHooks(directiveIndex, directiveDef, tView) {
  4940. ngDevMode && assertFirstCreatePass(tView);
  4941. const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype;
  4942. if (ngOnChanges) {
  4943. const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef);
  4944. (tView.preOrderHooks ??= []).push(directiveIndex, wrappedOnChanges);
  4945. (tView.preOrderCheckHooks ??= []).push(directiveIndex, wrappedOnChanges);
  4946. }
  4947. if (ngOnInit) {
  4948. (tView.preOrderHooks ??= []).push(0 - directiveIndex, ngOnInit);
  4949. }
  4950. if (ngDoCheck) {
  4951. (tView.preOrderHooks ??= []).push(directiveIndex, ngDoCheck);
  4952. (tView.preOrderCheckHooks ??= []).push(directiveIndex, ngDoCheck);
  4953. }
  4954. }
  4955. /**
  4956. *
  4957. * Loops through the directives on the provided `tNode` and queues hooks to be
  4958. * run that are not initialization hooks.
  4959. *
  4960. * Should be executed during `elementEnd()` and similar to
  4961. * preserve hook execution order. Content, view, and destroy hooks for projected
  4962. * components and directives must be called *before* their hosts.
  4963. *
  4964. * Sets up the content, view, and destroy hooks on the provided `tView`,
  4965. * see {@link HookData} for details about the data structure.
  4966. *
  4967. * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up
  4968. * separately at `elementStart`.
  4969. *
  4970. * @param tView The current TView
  4971. * @param tNode The TNode whose directives are to be searched for hooks to queue
  4972. */
  4973. function registerPostOrderHooks(tView, tNode) {
  4974. ngDevMode && assertFirstCreatePass(tView);
  4975. // It's necessary to loop through the directives at elementEnd() (rather than processing in
  4976. // directiveCreate) so we can preserve the current hook order. Content, view, and destroy
  4977. // hooks for projected components and directives must be called *before* their hosts.
  4978. for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) {
  4979. const directiveDef = tView.data[i];
  4980. ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef');
  4981. const lifecycleHooks = directiveDef.type.prototype;
  4982. const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy } = lifecycleHooks;
  4983. if (ngAfterContentInit) {
  4984. (tView.contentHooks ??= []).push(-i, ngAfterContentInit);
  4985. }
  4986. if (ngAfterContentChecked) {
  4987. (tView.contentHooks ??= []).push(i, ngAfterContentChecked);
  4988. (tView.contentCheckHooks ??= []).push(i, ngAfterContentChecked);
  4989. }
  4990. if (ngAfterViewInit) {
  4991. (tView.viewHooks ??= []).push(-i, ngAfterViewInit);
  4992. }
  4993. if (ngAfterViewChecked) {
  4994. (tView.viewHooks ??= []).push(i, ngAfterViewChecked);
  4995. (tView.viewCheckHooks ??= []).push(i, ngAfterViewChecked);
  4996. }
  4997. if (ngOnDestroy != null) {
  4998. (tView.destroyHooks ??= []).push(i, ngOnDestroy);
  4999. }
  5000. }
  5001. }
  5002. /**
  5003. * Executing hooks requires complex logic as we need to deal with 2 constraints.
  5004. *
  5005. * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
  5006. * once, across many change detection cycles. This must be true even if some hooks throw, or if
  5007. * some recursively trigger a change detection cycle.
  5008. * To solve that, it is required to track the state of the execution of these init hooks.
  5009. * This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
  5010. * and the index within that phase. They can be seen as a cursor in the following structure:
  5011. * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
  5012. * They are stored as flags in LView[FLAGS].
  5013. *
  5014. * 2. Pre-order hooks can be executed in batches, because of the select instruction.
  5015. * To be able to pause and resume their execution, we also need some state about the hook's array
  5016. * that is being processed:
  5017. * - the index of the next hook to be executed
  5018. * - the number of init hooks already found in the processed part of the array
  5019. * They are stored as flags in LView[PREORDER_HOOK_FLAGS].
  5020. */
  5021. /**
  5022. * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were
  5023. * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read
  5024. * / write of the init-hooks related flags.
  5025. * @param lView The LView where hooks are defined
  5026. * @param hooks Hooks to be run
  5027. * @param nodeIndex 3 cases depending on the value:
  5028. * - undefined: all hooks from the array should be executed (post-order case)
  5029. * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
  5030. * flushing the remaining hooks)
  5031. * - number: execute hooks only from the saved index until that node index exclusive (pre-order
  5032. * case, when executing select(number))
  5033. */
  5034. function executeCheckHooks(lView, hooks, nodeIndex) {
  5035. callHooks(lView, hooks, 3 /* InitPhaseState.InitPhaseCompleted */, nodeIndex);
  5036. }
  5037. /**
  5038. * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked,
  5039. * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed.
  5040. * @param lView The LView where hooks are defined
  5041. * @param hooks Hooks to be run
  5042. * @param initPhase A phase for which hooks should be run
  5043. * @param nodeIndex 3 cases depending on the value:
  5044. * - undefined: all hooks from the array should be executed (post-order case)
  5045. * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
  5046. * flushing the remaining hooks)
  5047. * - number: execute hooks only from the saved index until that node index exclusive (pre-order
  5048. * case, when executing select(number))
  5049. */
  5050. function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) {
  5051. ngDevMode &&
  5052. assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once');
  5053. if ((lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
  5054. callHooks(lView, hooks, initPhase, nodeIndex);
  5055. }
  5056. }
  5057. function incrementInitPhaseFlags(lView, initPhase) {
  5058. ngDevMode &&
  5059. assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.');
  5060. let flags = lView[FLAGS];
  5061. if ((flags & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
  5062. flags &= 8191 /* LViewFlags.IndexWithinInitPhaseReset */;
  5063. flags += 1 /* LViewFlags.InitPhaseStateIncrementer */;
  5064. lView[FLAGS] = flags;
  5065. }
  5066. }
  5067. /**
  5068. * Calls lifecycle hooks with their contexts, skipping init hooks if it's not
  5069. * the first LView pass
  5070. *
  5071. * @param currentView The current view
  5072. * @param arr The array in which the hooks are found
  5073. * @param initPhaseState the current state of the init phase
  5074. * @param currentNodeIndex 3 cases depending on the value:
  5075. * - undefined: all hooks from the array should be executed (post-order case)
  5076. * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
  5077. * flushing the remaining hooks)
  5078. * - number: execute hooks only from the saved index until that node index exclusive (pre-order
  5079. * case, when executing select(number))
  5080. */
  5081. function callHooks(currentView, arr, initPhase, currentNodeIndex) {
  5082. ngDevMode &&
  5083. assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.');
  5084. const startIndex = currentNodeIndex !== undefined ?
  5085. (currentView[PREORDER_HOOK_FLAGS] & 65535 /* PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask */) :
  5086. 0;
  5087. const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1;
  5088. const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1
  5089. let lastNodeIndexFound = 0;
  5090. for (let i = startIndex; i < max; i++) {
  5091. const hook = arr[i + 1];
  5092. if (typeof hook === 'number') {
  5093. lastNodeIndexFound = arr[i];
  5094. if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) {
  5095. break;
  5096. }
  5097. }
  5098. else {
  5099. const isInitHook = arr[i] < 0;
  5100. if (isInitHook) {
  5101. currentView[PREORDER_HOOK_FLAGS] += 65536 /* PreOrderHookFlags.NumberOfInitHooksCalledIncrementer */;
  5102. }
  5103. if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) {
  5104. callHook(currentView, initPhase, arr, i);
  5105. currentView[PREORDER_HOOK_FLAGS] =
  5106. (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* PreOrderHookFlags.NumberOfInitHooksCalledMask */) + i +
  5107. 2;
  5108. }
  5109. i++;
  5110. }
  5111. }
  5112. }
  5113. /**
  5114. * Executes a single lifecycle hook, making sure that:
  5115. * - it is called in the non-reactive context;
  5116. * - profiling data are registered.
  5117. */
  5118. function callHookInternal(directive, hook) {
  5119. profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook);
  5120. const prevConsumer = setActiveConsumer(null);
  5121. try {
  5122. hook.call(directive);
  5123. }
  5124. finally {
  5125. setActiveConsumer(prevConsumer);
  5126. profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook);
  5127. }
  5128. }
  5129. /**
  5130. * Execute one hook against the current `LView`.
  5131. *
  5132. * @param currentView The current view
  5133. * @param initPhaseState the current state of the init phase
  5134. * @param arr The array in which the hooks are found
  5135. * @param i The current index within the hook data array
  5136. */
  5137. function callHook(currentView, initPhase, arr, i) {
  5138. const isInitHook = arr[i] < 0;
  5139. const hook = arr[i + 1];
  5140. const directiveIndex = isInitHook ? -arr[i] : arr[i];
  5141. const directive = currentView[directiveIndex];
  5142. if (isInitHook) {
  5143. const indexWithintInitPhase = currentView[FLAGS] >> 13 /* LViewFlags.IndexWithinInitPhaseShift */;
  5144. // The init phase state must be always checked here as it may have been recursively updated.
  5145. if (indexWithintInitPhase <
  5146. (currentView[PREORDER_HOOK_FLAGS] >> 16 /* PreOrderHookFlags.NumberOfInitHooksCalledShift */) &&
  5147. (currentView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
  5148. currentView[FLAGS] += 8192 /* LViewFlags.IndexWithinInitPhaseIncrementer */;
  5149. callHookInternal(directive, hook);
  5150. }
  5151. }
  5152. else {
  5153. callHookInternal(directive, hook);
  5154. }
  5155. }
  5156. const NO_PARENT_INJECTOR = -1;
  5157. /**
  5158. * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in
  5159. * `TView.data`. This allows us to store information about the current node's tokens (which
  5160. * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be
  5161. * shared, so they live in `LView`).
  5162. *
  5163. * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter
  5164. * determines whether a directive is available on the associated node or not. This prevents us
  5165. * from searching the directives array at this level unless it's probable the directive is in it.
  5166. *
  5167. * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
  5168. *
  5169. * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed
  5170. * using interfaces as they were previously. The start index of each `LInjector` and `TInjector`
  5171. * will differ based on where it is flattened into the main array, so it's not possible to know
  5172. * the indices ahead of time and save their types here. The interfaces are still included here
  5173. * for documentation purposes.
  5174. *
  5175. * export interface LInjector extends Array<any> {
  5176. *
  5177. * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
  5178. * [0]: number;
  5179. *
  5180. * // Cumulative bloom for directive IDs 32-63
  5181. * [1]: number;
  5182. *
  5183. * // Cumulative bloom for directive IDs 64-95
  5184. * [2]: number;
  5185. *
  5186. * // Cumulative bloom for directive IDs 96-127
  5187. * [3]: number;
  5188. *
  5189. * // Cumulative bloom for directive IDs 128-159
  5190. * [4]: number;
  5191. *
  5192. * // Cumulative bloom for directive IDs 160 - 191
  5193. * [5]: number;
  5194. *
  5195. * // Cumulative bloom for directive IDs 192 - 223
  5196. * [6]: number;
  5197. *
  5198. * // Cumulative bloom for directive IDs 224 - 255
  5199. * [7]: number;
  5200. *
  5201. * // We need to store a reference to the injector's parent so DI can keep looking up
  5202. * // the injector tree until it finds the dependency it's looking for.
  5203. * [PARENT_INJECTOR]: number;
  5204. * }
  5205. *
  5206. * export interface TInjector extends Array<any> {
  5207. *
  5208. * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
  5209. * [0]: number;
  5210. *
  5211. * // Shared node bloom for directive IDs 32-63
  5212. * [1]: number;
  5213. *
  5214. * // Shared node bloom for directive IDs 64-95
  5215. * [2]: number;
  5216. *
  5217. * // Shared node bloom for directive IDs 96-127
  5218. * [3]: number;
  5219. *
  5220. * // Shared node bloom for directive IDs 128-159
  5221. * [4]: number;
  5222. *
  5223. * // Shared node bloom for directive IDs 160 - 191
  5224. * [5]: number;
  5225. *
  5226. * // Shared node bloom for directive IDs 192 - 223
  5227. * [6]: number;
  5228. *
  5229. * // Shared node bloom for directive IDs 224 - 255
  5230. * [7]: number;
  5231. *
  5232. * // Necessary to find directive indices for a particular node.
  5233. * [TNODE]: TElementNode|TElementContainerNode|TContainerNode;
  5234. * }
  5235. */
  5236. /**
  5237. * Factory for creating instances of injectors in the NodeInjector.
  5238. *
  5239. * This factory is complicated by the fact that it can resolve `multi` factories as well.
  5240. *
  5241. * NOTE: Some of the fields are optional which means that this class has two hidden classes.
  5242. * - One without `multi` support (most common)
  5243. * - One with `multi` values, (rare).
  5244. *
  5245. * Since VMs can cache up to 4 inline hidden classes this is OK.
  5246. *
  5247. * - Single factory: Only `resolving` and `factory` is defined.
  5248. * - `providers` factory: `componentProviders` is a number and `index = -1`.
  5249. * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`.
  5250. */
  5251. class NodeInjectorFactory {
  5252. constructor(
  5253. /**
  5254. * Factory to invoke in order to create a new instance.
  5255. */
  5256. factory,
  5257. /**
  5258. * Set to `true` if the token is declared in `viewProviders` (or if it is component).
  5259. */
  5260. isViewProvider, injectImplementation) {
  5261. this.factory = factory;
  5262. /**
  5263. * Marker set to true during factory invocation to see if we get into recursive loop.
  5264. * Recursive loop causes an error to be displayed.
  5265. */
  5266. this.resolving = false;
  5267. ngDevMode && assertDefined(factory, 'Factory not specified');
  5268. ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.');
  5269. this.canSeeViewProviders = isViewProvider;
  5270. this.injectImpl = injectImplementation;
  5271. }
  5272. }
  5273. function isFactory(obj) {
  5274. return obj instanceof NodeInjectorFactory;
  5275. }
  5276. // Note: This hack is necessary so we don't erroneously get a circular dependency
  5277. // failure based on types.
  5278. const unusedValueExportToPlacateAjd$2 = 1;
  5279. /**
  5280. * Converts `TNodeType` into human readable text.
  5281. * Make sure this matches with `TNodeType`
  5282. */
  5283. function toTNodeTypeAsString(tNodeType) {
  5284. let text = '';
  5285. (tNodeType & 1 /* TNodeType.Text */) && (text += '|Text');
  5286. (tNodeType & 2 /* TNodeType.Element */) && (text += '|Element');
  5287. (tNodeType & 4 /* TNodeType.Container */) && (text += '|Container');
  5288. (tNodeType & 8 /* TNodeType.ElementContainer */) && (text += '|ElementContainer');
  5289. (tNodeType & 16 /* TNodeType.Projection */) && (text += '|Projection');
  5290. (tNodeType & 32 /* TNodeType.Icu */) && (text += '|IcuContainer');
  5291. (tNodeType & 64 /* TNodeType.Placeholder */) && (text += '|Placeholder');
  5292. return text.length > 0 ? text.substring(1) : text;
  5293. }
  5294. // Note: This hack is necessary so we don't erroneously get a circular dependency
  5295. // failure based on types.
  5296. const unusedValueExportToPlacateAjd$1 = 1;
  5297. /**
  5298. * Returns `true` if the `TNode` has a directive which has `@Input()` for `class` binding.
  5299. *
  5300. * ```
  5301. * <div my-dir [class]="exp"></div>
  5302. * ```
  5303. * and
  5304. * ```
  5305. * @Directive({
  5306. * })
  5307. * class MyDirective {
  5308. * @Input()
  5309. * class: string;
  5310. * }
  5311. * ```
  5312. *
  5313. * In the above case it is necessary to write the reconciled styling information into the
  5314. * directive's input.
  5315. *
  5316. * @param tNode
  5317. */
  5318. function hasClassInput(tNode) {
  5319. return (tNode.flags & 8 /* TNodeFlags.hasClassInput */) !== 0;
  5320. }
  5321. /**
  5322. * Returns `true` if the `TNode` has a directive which has `@Input()` for `style` binding.
  5323. *
  5324. * ```
  5325. * <div my-dir [style]="exp"></div>
  5326. * ```
  5327. * and
  5328. * ```
  5329. * @Directive({
  5330. * })
  5331. * class MyDirective {
  5332. * @Input()
  5333. * class: string;
  5334. * }
  5335. * ```
  5336. *
  5337. * In the above case it is necessary to write the reconciled styling information into the
  5338. * directive's input.
  5339. *
  5340. * @param tNode
  5341. */
  5342. function hasStyleInput(tNode) {
  5343. return (tNode.flags & 16 /* TNodeFlags.hasStyleInput */) !== 0;
  5344. }
  5345. function assertTNodeType(tNode, expectedTypes, message) {
  5346. assertDefined(tNode, 'should be called with a TNode');
  5347. if ((tNode.type & expectedTypes) === 0) {
  5348. throwError(message ||
  5349. `Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${toTNodeTypeAsString(tNode.type)}.`);
  5350. }
  5351. }
  5352. function assertPureTNodeType(type) {
  5353. if (!(type === 2 /* TNodeType.Element */ || //
  5354. type === 1 /* TNodeType.Text */ || //
  5355. type === 4 /* TNodeType.Container */ || //
  5356. type === 8 /* TNodeType.ElementContainer */ || //
  5357. type === 32 /* TNodeType.Icu */ || //
  5358. type === 16 /* TNodeType.Projection */ || //
  5359. type === 64 /* TNodeType.Placeholder */)) {
  5360. throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`);
  5361. }
  5362. }
  5363. /// Parent Injector Utils ///////////////////////////////////////////////////////////////
  5364. function hasParentInjector(parentLocation) {
  5365. return parentLocation !== NO_PARENT_INJECTOR;
  5366. }
  5367. function getParentInjectorIndex(parentLocation) {
  5368. ngDevMode && assertNumber(parentLocation, 'Number expected');
  5369. ngDevMode && assertNotEqual(parentLocation, -1, 'Not a valid state.');
  5370. const parentInjectorIndex = parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */;
  5371. ngDevMode &&
  5372. assertGreaterThan(parentInjectorIndex, HEADER_OFFSET, 'Parent injector must be pointing past HEADER_OFFSET.');
  5373. return parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */;
  5374. }
  5375. function getParentInjectorViewOffset(parentLocation) {
  5376. return parentLocation >> 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */;
  5377. }
  5378. /**
  5379. * Unwraps a parent injector location number to find the view offset from the current injector,
  5380. * then walks up the declaration view tree until the view is found that contains the parent
  5381. * injector.
  5382. *
  5383. * @param location The location of the parent injector, which contains the view offset
  5384. * @param startView The LView instance from which to start walking up the view tree
  5385. * @returns The LView instance that contains the parent injector
  5386. */
  5387. function getParentInjectorView(location, startView) {
  5388. let viewOffset = getParentInjectorViewOffset(location);
  5389. let parentView = startView;
  5390. // For most cases, the parent injector can be found on the host node (e.g. for component
  5391. // or container), but we must keep the loop here to support the rarer case of deeply nested
  5392. // <ng-template> tags or inline views, where the parent injector might live many views
  5393. // above the child injector.
  5394. while (viewOffset > 0) {
  5395. parentView = parentView[DECLARATION_VIEW];
  5396. viewOffset--;
  5397. }
  5398. return parentView;
  5399. }
  5400. /**
  5401. * Defines if the call to `inject` should include `viewProviders` in its resolution.
  5402. *
  5403. * This is set to true when we try to instantiate a component. This value is reset in
  5404. * `getNodeInjectable` to a value which matches the declaration location of the token about to be
  5405. * instantiated. This is done so that if we are injecting a token which was declared outside of
  5406. * `viewProviders` we don't accidentally pull `viewProviders` in.
  5407. *
  5408. * Example:
  5409. *
  5410. * ```
  5411. * @Injectable()
  5412. * class MyService {
  5413. * constructor(public value: String) {}
  5414. * }
  5415. *
  5416. * @Component({
  5417. * providers: [
  5418. * MyService,
  5419. * {provide: String, value: 'providers' }
  5420. * ]
  5421. * viewProviders: [
  5422. * {provide: String, value: 'viewProviders'}
  5423. * ]
  5424. * })
  5425. * class MyComponent {
  5426. * constructor(myService: MyService, value: String) {
  5427. * // We expect that Component can see into `viewProviders`.
  5428. * expect(value).toEqual('viewProviders');
  5429. * // `MyService` was not declared in `viewProviders` hence it can't see it.
  5430. * expect(myService.value).toEqual('providers');
  5431. * }
  5432. * }
  5433. *
  5434. * ```
  5435. */
  5436. let includeViewProviders = true;
  5437. function setIncludeViewProviders(v) {
  5438. const oldValue = includeViewProviders;
  5439. includeViewProviders = v;
  5440. return oldValue;
  5441. }
  5442. /**
  5443. * The number of slots in each bloom filter (used by DI). The larger this number, the fewer
  5444. * directives that will share slots, and thus, the fewer false positives when checking for
  5445. * the existence of a directive.
  5446. */
  5447. const BLOOM_SIZE = 256;
  5448. const BLOOM_MASK = BLOOM_SIZE - 1;
  5449. /**
  5450. * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits,
  5451. * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash
  5452. * number.
  5453. */
  5454. const BLOOM_BUCKET_BITS = 5;
  5455. /** Counter used to generate unique IDs for directives. */
  5456. let nextNgElementId = 0;
  5457. /** Value used when something wasn't found by an injector. */
  5458. const NOT_FOUND = {};
  5459. /**
  5460. * Registers this directive as present in its node's injector by flipping the directive's
  5461. * corresponding bit in the injector's bloom filter.
  5462. *
  5463. * @param injectorIndex The index of the node injector where this token should be registered
  5464. * @param tView The TView for the injector's bloom filters
  5465. * @param type The directive token to register
  5466. */
  5467. function bloomAdd(injectorIndex, tView, type) {
  5468. ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true');
  5469. let id;
  5470. if (typeof type === 'string') {
  5471. id = type.charCodeAt(0) || 0;
  5472. }
  5473. else if (type.hasOwnProperty(NG_ELEMENT_ID)) {
  5474. id = type[NG_ELEMENT_ID];
  5475. }
  5476. // Set a unique ID on the directive type, so if something tries to inject the directive,
  5477. // we can easily retrieve the ID and hash it into the bloom bit that should be checked.
  5478. if (id == null) {
  5479. id = type[NG_ELEMENT_ID] = nextNgElementId++;
  5480. }
  5481. // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
  5482. // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
  5483. const bloomHash = id & BLOOM_MASK;
  5484. // Create a mask that targets the specific bit associated with the directive.
  5485. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
  5486. // to bit positions 0 - 31 in a 32 bit integer.
  5487. const mask = 1 << bloomHash;
  5488. // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`.
  5489. // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask
  5490. // should be written to.
  5491. tView.data[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask;
  5492. }
  5493. /**
  5494. * Creates (or gets an existing) injector for a given element or container.
  5495. *
  5496. * @param tNode for which an injector should be retrieved / created.
  5497. * @param lView View where the node is stored
  5498. * @returns Node injector
  5499. */
  5500. function getOrCreateNodeInjectorForNode(tNode, lView) {
  5501. const existingInjectorIndex = getInjectorIndex(tNode, lView);
  5502. if (existingInjectorIndex !== -1) {
  5503. return existingInjectorIndex;
  5504. }
  5505. const tView = lView[TVIEW];
  5506. if (tView.firstCreatePass) {
  5507. tNode.injectorIndex = lView.length;
  5508. insertBloom(tView.data, tNode); // foundation for node bloom
  5509. insertBloom(lView, null); // foundation for cumulative bloom
  5510. insertBloom(tView.blueprint, null);
  5511. }
  5512. const parentLoc = getParentInjectorLocation(tNode, lView);
  5513. const injectorIndex = tNode.injectorIndex;
  5514. // If a parent injector can't be found, its location is set to -1.
  5515. // In that case, we don't need to set up a cumulative bloom
  5516. if (hasParentInjector(parentLoc)) {
  5517. const parentIndex = getParentInjectorIndex(parentLoc);
  5518. const parentLView = getParentInjectorView(parentLoc, lView);
  5519. const parentData = parentLView[TVIEW].data;
  5520. // Creates a cumulative bloom filter that merges the parent's bloom filter
  5521. // and its own cumulative bloom (which contains tokens for all ancestors)
  5522. for (let i = 0; i < 8 /* NodeInjectorOffset.BLOOM_SIZE */; i++) {
  5523. lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i];
  5524. }
  5525. }
  5526. lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */] = parentLoc;
  5527. return injectorIndex;
  5528. }
  5529. function insertBloom(arr, footer) {
  5530. arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer);
  5531. }
  5532. function getInjectorIndex(tNode, lView) {
  5533. if (tNode.injectorIndex === -1 ||
  5534. // If the injector index is the same as its parent's injector index, then the index has been
  5535. // copied down from the parent node. No injector has been created yet on this node.
  5536. (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) ||
  5537. // After the first template pass, the injector index might exist but the parent values
  5538. // might not have been calculated yet for this instance
  5539. lView[tNode.injectorIndex + 8 /* NodeInjectorOffset.PARENT */] === null) {
  5540. return -1;
  5541. }
  5542. else {
  5543. ngDevMode && assertIndexInRange(lView, tNode.injectorIndex);
  5544. return tNode.injectorIndex;
  5545. }
  5546. }
  5547. /**
  5548. * Finds the index of the parent injector, with a view offset if applicable. Used to set the
  5549. * parent injector initially.
  5550. *
  5551. * @returns Returns a number that is the combination of the number of LViews that we have to go up
  5552. * to find the LView containing the parent inject AND the index of the injector within that LView.
  5553. */
  5554. function getParentInjectorLocation(tNode, lView) {
  5555. if (tNode.parent && tNode.parent.injectorIndex !== -1) {
  5556. // If we have a parent `TNode` and there is an injector associated with it we are done, because
  5557. // the parent injector is within the current `LView`.
  5558. return tNode.parent.injectorIndex; // ViewOffset is 0
  5559. }
  5560. // When parent injector location is computed it may be outside of the current view. (ie it could
  5561. // be pointing to a declared parent location). This variable stores number of declaration parents
  5562. // we need to walk up in order to find the parent injector location.
  5563. let declarationViewOffset = 0;
  5564. let parentTNode = null;
  5565. let lViewCursor = lView;
  5566. // The parent injector is not in the current `LView`. We will have to walk the declared parent
  5567. // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent
  5568. // `NodeInjector`.
  5569. while (lViewCursor !== null) {
  5570. parentTNode = getTNodeFromLView(lViewCursor);
  5571. if (parentTNode === null) {
  5572. // If we have no parent, than we are done.
  5573. return NO_PARENT_INJECTOR;
  5574. }
  5575. ngDevMode && parentTNode && assertTNodeForLView(parentTNode, lViewCursor[DECLARATION_VIEW]);
  5576. // Every iteration of the loop requires that we go to the declared parent.
  5577. declarationViewOffset++;
  5578. lViewCursor = lViewCursor[DECLARATION_VIEW];
  5579. if (parentTNode.injectorIndex !== -1) {
  5580. // We found a NodeInjector which points to something.
  5581. return (parentTNode.injectorIndex |
  5582. (declarationViewOffset << 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */));
  5583. }
  5584. }
  5585. return NO_PARENT_INJECTOR;
  5586. }
  5587. /**
  5588. * Makes a type or an injection token public to the DI system by adding it to an
  5589. * injector's bloom filter.
  5590. *
  5591. * @param di The node injector in which a directive will be added
  5592. * @param token The type or the injection token to be made public
  5593. */
  5594. function diPublicInInjector(injectorIndex, tView, token) {
  5595. bloomAdd(injectorIndex, tView, token);
  5596. }
  5597. /**
  5598. * Inject static attribute value into directive constructor.
  5599. *
  5600. * This method is used with `factory` functions which are generated as part of
  5601. * `defineDirective` or `defineComponent`. The method retrieves the static value
  5602. * of an attribute. (Dynamic attributes are not supported since they are not resolved
  5603. * at the time of injection and can change over time.)
  5604. *
  5605. * # Example
  5606. * Given:
  5607. * ```
  5608. * @Component(...)
  5609. * class MyComponent {
  5610. * constructor(@Attribute('title') title: string) { ... }
  5611. * }
  5612. * ```
  5613. * When instantiated with
  5614. * ```
  5615. * <my-component title="Hello"></my-component>
  5616. * ```
  5617. *
  5618. * Then factory method generated is:
  5619. * ```
  5620. * MyComponent.ɵcmp = defineComponent({
  5621. * factory: () => new MyComponent(injectAttribute('title'))
  5622. * ...
  5623. * })
  5624. * ```
  5625. *
  5626. * @publicApi
  5627. */
  5628. function injectAttributeImpl(tNode, attrNameToInject) {
  5629. ngDevMode && assertTNodeType(tNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
  5630. ngDevMode && assertDefined(tNode, 'expecting tNode');
  5631. if (attrNameToInject === 'class') {
  5632. return tNode.classes;
  5633. }
  5634. if (attrNameToInject === 'style') {
  5635. return tNode.styles;
  5636. }
  5637. const attrs = tNode.attrs;
  5638. if (attrs) {
  5639. const attrsLength = attrs.length;
  5640. let i = 0;
  5641. while (i < attrsLength) {
  5642. const value = attrs[i];
  5643. // If we hit a `Bindings` or `Template` marker then we are done.
  5644. if (isNameOnlyAttributeMarker(value))
  5645. break;
  5646. // Skip namespaced attributes
  5647. if (value === 0 /* AttributeMarker.NamespaceURI */) {
  5648. // we skip the next two values
  5649. // as namespaced attributes looks like
  5650. // [..., AttributeMarker.NamespaceURI, 'http://someuri.com/test', 'test:exist',
  5651. // 'existValue', ...]
  5652. i = i + 2;
  5653. }
  5654. else if (typeof value === 'number') {
  5655. // Skip to the first value of the marked attribute.
  5656. i++;
  5657. while (i < attrsLength && typeof attrs[i] === 'string') {
  5658. i++;
  5659. }
  5660. }
  5661. else if (value === attrNameToInject) {
  5662. return attrs[i + 1];
  5663. }
  5664. else {
  5665. i = i + 2;
  5666. }
  5667. }
  5668. }
  5669. return null;
  5670. }
  5671. function notFoundValueOrThrow(notFoundValue, token, flags) {
  5672. if ((flags & InjectFlags.Optional) || notFoundValue !== undefined) {
  5673. return notFoundValue;
  5674. }
  5675. else {
  5676. throwProviderNotFoundError(token, 'NodeInjector');
  5677. }
  5678. }
  5679. /**
  5680. * Returns the value associated to the given token from the ModuleInjector or throws exception
  5681. *
  5682. * @param lView The `LView` that contains the `tNode`
  5683. * @param token The token to look for
  5684. * @param flags Injection flags
  5685. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  5686. * @returns the value from the injector or throws an exception
  5687. */
  5688. function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) {
  5689. if ((flags & InjectFlags.Optional) && notFoundValue === undefined) {
  5690. // This must be set or the NullInjector will throw for optional deps
  5691. notFoundValue = null;
  5692. }
  5693. if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) {
  5694. const moduleInjector = lView[INJECTOR$1];
  5695. // switch to `injectInjectorOnly` implementation for module injector, since module injector
  5696. // should not have access to Component/Directive DI scope (that may happen through
  5697. // `directiveInject` implementation)
  5698. const previousInjectImplementation = setInjectImplementation(undefined);
  5699. try {
  5700. if (moduleInjector) {
  5701. return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional);
  5702. }
  5703. else {
  5704. return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional);
  5705. }
  5706. }
  5707. finally {
  5708. setInjectImplementation(previousInjectImplementation);
  5709. }
  5710. }
  5711. return notFoundValueOrThrow(notFoundValue, token, flags);
  5712. }
  5713. /**
  5714. * Returns the value associated to the given token from the NodeInjectors => ModuleInjector.
  5715. *
  5716. * Look for the injector providing the token by walking up the node injector tree and then
  5717. * the module injector tree.
  5718. *
  5719. * This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom
  5720. * filter. `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`)
  5721. *
  5722. * @param tNode The Node where the search for the injector should start
  5723. * @param lView The `LView` that contains the `tNode`
  5724. * @param token The token to look for
  5725. * @param flags Injection flags
  5726. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  5727. * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
  5728. */
  5729. function getOrCreateInjectable(tNode, lView, token, flags = InjectFlags.Default, notFoundValue) {
  5730. if (tNode !== null) {
  5731. // If the view or any of its ancestors have an embedded
  5732. // view injector, we have to look it up there first.
  5733. if (lView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */ &&
  5734. // The token must be present on the current node injector when the `Self`
  5735. // flag is set, so the lookup on embedded view injector(s) can be skipped.
  5736. !(flags & InjectFlags.Self)) {
  5737. const embeddedInjectorValue = lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, NOT_FOUND);
  5738. if (embeddedInjectorValue !== NOT_FOUND) {
  5739. return embeddedInjectorValue;
  5740. }
  5741. }
  5742. // Otherwise try the node injector.
  5743. const value = lookupTokenUsingNodeInjector(tNode, lView, token, flags, NOT_FOUND);
  5744. if (value !== NOT_FOUND) {
  5745. return value;
  5746. }
  5747. }
  5748. // Finally, fall back to the module injector.
  5749. return lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
  5750. }
  5751. /**
  5752. * Returns the value associated to the given token from the node injector.
  5753. *
  5754. * @param tNode The Node where the search for the injector should start
  5755. * @param lView The `LView` that contains the `tNode`
  5756. * @param token The token to look for
  5757. * @param flags Injection flags
  5758. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  5759. * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
  5760. */
  5761. function lookupTokenUsingNodeInjector(tNode, lView, token, flags, notFoundValue) {
  5762. const bloomHash = bloomHashBitOrFactory(token);
  5763. // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
  5764. // so just call the factory function to create it.
  5765. if (typeof bloomHash === 'function') {
  5766. if (!enterDI(lView, tNode, flags)) {
  5767. // Failed to enter DI, try module injector instead. If a token is injected with the @Host
  5768. // flag, the module injector is not searched for that token in Ivy.
  5769. return (flags & InjectFlags.Host) ?
  5770. notFoundValueOrThrow(notFoundValue, token, flags) :
  5771. lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
  5772. }
  5773. try {
  5774. let value;
  5775. if (ngDevMode) {
  5776. runInInjectorProfilerContext(new NodeInjector(getCurrentTNode(), getLView()), token, () => {
  5777. value = bloomHash(flags);
  5778. if (value != null) {
  5779. emitInstanceCreatedByInjectorEvent(value);
  5780. }
  5781. });
  5782. }
  5783. else {
  5784. value = bloomHash(flags);
  5785. }
  5786. if (value == null && !(flags & InjectFlags.Optional)) {
  5787. throwProviderNotFoundError(token);
  5788. }
  5789. else {
  5790. return value;
  5791. }
  5792. }
  5793. finally {
  5794. leaveDI();
  5795. }
  5796. }
  5797. else if (typeof bloomHash === 'number') {
  5798. // A reference to the previous injector TView that was found while climbing the element
  5799. // injector tree. This is used to know if viewProviders can be accessed on the current
  5800. // injector.
  5801. let previousTView = null;
  5802. let injectorIndex = getInjectorIndex(tNode, lView);
  5803. let parentLocation = NO_PARENT_INJECTOR;
  5804. let hostTElementNode = flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null;
  5805. // If we should skip this injector, or if there is no injector on this node, start by
  5806. // searching the parent injector.
  5807. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
  5808. parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
  5809. lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */];
  5810. if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) {
  5811. injectorIndex = -1;
  5812. }
  5813. else {
  5814. previousTView = lView[TVIEW];
  5815. injectorIndex = getParentInjectorIndex(parentLocation);
  5816. lView = getParentInjectorView(parentLocation, lView);
  5817. }
  5818. }
  5819. // Traverse up the injector tree until we find a potential match or until we know there
  5820. // *isn't* a match.
  5821. while (injectorIndex !== -1) {
  5822. ngDevMode && assertNodeInjector(lView, injectorIndex);
  5823. // Check the current injector. If it matches, see if it contains token.
  5824. const tView = lView[TVIEW];
  5825. ngDevMode &&
  5826. assertTNodeForLView(tView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */], lView);
  5827. if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
  5828. // At this point, we have an injector which *may* contain the token, so we step through
  5829. // the providers and directives associated with the injector's corresponding node to get
  5830. // the instance.
  5831. const instance = searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode);
  5832. if (instance !== NOT_FOUND) {
  5833. return instance;
  5834. }
  5835. }
  5836. parentLocation = lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */];
  5837. if (parentLocation !== NO_PARENT_INJECTOR &&
  5838. shouldSearchParent(flags, lView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */] === hostTElementNode) &&
  5839. bloomHasToken(bloomHash, injectorIndex, lView)) {
  5840. // The def wasn't found anywhere on this node, so it was a false positive.
  5841. // Traverse up the tree and continue searching.
  5842. previousTView = tView;
  5843. injectorIndex = getParentInjectorIndex(parentLocation);
  5844. lView = getParentInjectorView(parentLocation, lView);
  5845. }
  5846. else {
  5847. // If we should not search parent OR If the ancestor bloom filter value does not have the
  5848. // bit corresponding to the directive we can give up on traversing up to find the specific
  5849. // injector.
  5850. injectorIndex = -1;
  5851. }
  5852. }
  5853. }
  5854. return notFoundValue;
  5855. }
  5856. function searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode) {
  5857. const currentTView = lView[TVIEW];
  5858. const tNode = currentTView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
  5859. // First, we need to determine if view providers can be accessed by the starting element.
  5860. // There are two possibilities
  5861. const canAccessViewProviders = previousTView == null ?
  5862. // 1) This is the first invocation `previousTView == null` which means that we are at the
  5863. // `TNode` of where injector is starting to look. In such a case the only time we are allowed
  5864. // to look into the ViewProviders is if:
  5865. // - we are on a component
  5866. // - AND the injector set `includeViewProviders` to true (implying that the token can see
  5867. // ViewProviders because it is the Component or a Service which itself was declared in
  5868. // ViewProviders)
  5869. (isComponentHost(tNode) && includeViewProviders) :
  5870. // 2) `previousTView != null` which means that we are now walking across the parent nodes.
  5871. // In such a case we are only allowed to look into the ViewProviders if:
  5872. // - We just crossed from child View to Parent View `previousTView != currentTView`
  5873. // - AND the parent TNode is an Element.
  5874. // This means that we just came from the Component's View and therefore are allowed to see
  5875. // into the ViewProviders.
  5876. (previousTView != currentTView && ((tNode.type & 3 /* TNodeType.AnyRNode */) !== 0));
  5877. // This special case happens when there is a @host on the inject and when we are searching
  5878. // on the host element node.
  5879. const isHostSpecialCase = (flags & InjectFlags.Host) && hostTElementNode === tNode;
  5880. const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase);
  5881. if (injectableIdx !== null) {
  5882. return getNodeInjectable(lView, currentTView, injectableIdx, tNode);
  5883. }
  5884. else {
  5885. return NOT_FOUND;
  5886. }
  5887. }
  5888. /**
  5889. * Searches for the given token among the node's directives and providers.
  5890. *
  5891. * @param tNode TNode on which directives are present.
  5892. * @param tView The tView we are currently processing
  5893. * @param token Provider token or type of a directive to look for.
  5894. * @param canAccessViewProviders Whether view providers should be considered.
  5895. * @param isHostSpecialCase Whether the host special case applies.
  5896. * @returns Index of a found directive or provider, or null when none found.
  5897. */
  5898. function locateDirectiveOrProvider(tNode, tView, token, canAccessViewProviders, isHostSpecialCase) {
  5899. const nodeProviderIndexes = tNode.providerIndexes;
  5900. const tInjectables = tView.data;
  5901. const injectablesStart = nodeProviderIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
  5902. const directivesStart = tNode.directiveStart;
  5903. const directiveEnd = tNode.directiveEnd;
  5904. const cptViewProvidersCount = nodeProviderIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */;
  5905. const startingIndex = canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
  5906. // When the host special case applies, only the viewProviders and the component are visible
  5907. const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd;
  5908. for (let i = startingIndex; i < endIndex; i++) {
  5909. const providerTokenOrDef = tInjectables[i];
  5910. if (i < directivesStart && token === providerTokenOrDef ||
  5911. i >= directivesStart && providerTokenOrDef.type === token) {
  5912. return i;
  5913. }
  5914. }
  5915. if (isHostSpecialCase) {
  5916. const dirDef = tInjectables[directivesStart];
  5917. if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
  5918. return directivesStart;
  5919. }
  5920. }
  5921. return null;
  5922. }
  5923. /**
  5924. * Retrieve or instantiate the injectable from the `LView` at particular `index`.
  5925. *
  5926. * This function checks to see if the value has already been instantiated and if so returns the
  5927. * cached `injectable`. Otherwise if it detects that the value is still a factory it
  5928. * instantiates the `injectable` and caches the value.
  5929. */
  5930. function getNodeInjectable(lView, tView, index, tNode) {
  5931. let value = lView[index];
  5932. const tData = tView.data;
  5933. if (isFactory(value)) {
  5934. const factory = value;
  5935. if (factory.resolving) {
  5936. throwCyclicDependencyError(stringifyForError(tData[index]));
  5937. }
  5938. const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
  5939. factory.resolving = true;
  5940. let prevInjectContext;
  5941. if (ngDevMode) {
  5942. // tData indexes mirror the concrete instances in its corresponding LView.
  5943. // lView[index] here is either the injectable instace itself or a factory,
  5944. // therefore tData[index] is the constructor of that injectable or a
  5945. // definition object that contains the constructor in a `.type` field.
  5946. const token = tData[index].type || tData[index];
  5947. const injector = new NodeInjector(tNode, lView);
  5948. prevInjectContext = setInjectorProfilerContext({ injector, token });
  5949. }
  5950. const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null;
  5951. const success = enterDI(lView, tNode, InjectFlags.Default);
  5952. ngDevMode &&
  5953. assertEqual(success, true, 'Because flags do not contain \`SkipSelf\' we expect this to always succeed.');
  5954. try {
  5955. value = lView[index] = factory.factory(undefined, tData, lView, tNode);
  5956. ngDevMode && emitInstanceCreatedByInjectorEvent(value);
  5957. // This code path is hit for both directives and providers.
  5958. // For perf reasons, we want to avoid searching for hooks on providers.
  5959. // It does no harm to try (the hooks just won't exist), but the extra
  5960. // checks are unnecessary and this is a hot path. So we check to see
  5961. // if the index of the dependency is in the directive range for this
  5962. // tNode. If it's not, we know it's a provider and skip hook registration.
  5963. if (tView.firstCreatePass && index >= tNode.directiveStart) {
  5964. ngDevMode && assertDirectiveDef(tData[index]);
  5965. registerPreOrderHooks(index, tData[index], tView);
  5966. }
  5967. }
  5968. finally {
  5969. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  5970. previousInjectImplementation !== null &&
  5971. setInjectImplementation(previousInjectImplementation);
  5972. setIncludeViewProviders(previousIncludeViewProviders);
  5973. factory.resolving = false;
  5974. leaveDI();
  5975. }
  5976. }
  5977. return value;
  5978. }
  5979. /**
  5980. * Returns the bit in an injector's bloom filter that should be used to determine whether or not
  5981. * the directive might be provided by the injector.
  5982. *
  5983. * When a directive is public, it is added to the bloom filter and given a unique ID that can be
  5984. * retrieved on the Type. When the directive isn't public or the token is not a directive `null`
  5985. * is returned as the node injector can not possibly provide that token.
  5986. *
  5987. * @param token the injection token
  5988. * @returns the matching bit to check in the bloom filter or `null` if the token is not known.
  5989. * When the returned value is negative then it represents special values such as `Injector`.
  5990. */
  5991. function bloomHashBitOrFactory(token) {
  5992. ngDevMode && assertDefined(token, 'token must be defined');
  5993. if (typeof token === 'string') {
  5994. return token.charCodeAt(0) || 0;
  5995. }
  5996. const tokenId =
  5997. // First check with `hasOwnProperty` so we don't get an inherited ID.
  5998. token.hasOwnProperty(NG_ELEMENT_ID) ? token[NG_ELEMENT_ID] : undefined;
  5999. // Negative token IDs are used for special objects such as `Injector`
  6000. if (typeof tokenId === 'number') {
  6001. if (tokenId >= 0) {
  6002. return tokenId & BLOOM_MASK;
  6003. }
  6004. else {
  6005. ngDevMode &&
  6006. assertEqual(tokenId, -1 /* InjectorMarkers.Injector */, 'Expecting to get Special Injector Id');
  6007. return createNodeInjector;
  6008. }
  6009. }
  6010. else {
  6011. return tokenId;
  6012. }
  6013. }
  6014. function bloomHasToken(bloomHash, injectorIndex, injectorView) {
  6015. // Create a mask that targets the specific bit associated with the directive we're looking for.
  6016. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
  6017. // to bit positions 0 - 31 in a 32 bit integer.
  6018. const mask = 1 << bloomHash;
  6019. // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of
  6020. // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset
  6021. // that should be used.
  6022. const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)];
  6023. // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
  6024. // this injector is a potential match.
  6025. return !!(value & mask);
  6026. }
  6027. /** Returns true if flags prevent parent injector from being searched for tokens */
  6028. function shouldSearchParent(flags, isFirstHostTNode) {
  6029. return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
  6030. }
  6031. function getNodeInjectorLView(nodeInjector) {
  6032. return nodeInjector._lView;
  6033. }
  6034. function getNodeInjectorTNode(nodeInjector) {
  6035. return nodeInjector._tNode;
  6036. }
  6037. class NodeInjector {
  6038. constructor(_tNode, _lView) {
  6039. this._tNode = _tNode;
  6040. this._lView = _lView;
  6041. }
  6042. get(token, notFoundValue, flags) {
  6043. return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue);
  6044. }
  6045. }
  6046. /** Creates a `NodeInjector` for the current node. */
  6047. function createNodeInjector() {
  6048. return new NodeInjector(getCurrentTNode(), getLView());
  6049. }
  6050. /**
  6051. * @codeGenApi
  6052. */
  6053. function ɵɵgetInheritedFactory(type) {
  6054. return noSideEffects(() => {
  6055. const ownConstructor = type.prototype.constructor;
  6056. const ownFactory = ownConstructor[NG_FACTORY_DEF] || getFactoryOf(ownConstructor);
  6057. const objectPrototype = Object.prototype;
  6058. let parent = Object.getPrototypeOf(type.prototype).constructor;
  6059. // Go up the prototype until we hit `Object`.
  6060. while (parent && parent !== objectPrototype) {
  6061. const factory = parent[NG_FACTORY_DEF] || getFactoryOf(parent);
  6062. // If we hit something that has a factory and the factory isn't the same as the type,
  6063. // we've found the inherited factory. Note the check that the factory isn't the type's
  6064. // own factory is redundant in most cases, but if the user has custom decorators on the
  6065. // class, this lookup will start one level down in the prototype chain, causing us to
  6066. // find the own factory first and potentially triggering an infinite loop downstream.
  6067. if (factory && factory !== ownFactory) {
  6068. return factory;
  6069. }
  6070. parent = Object.getPrototypeOf(parent);
  6071. }
  6072. // There is no factory defined. Either this was improper usage of inheritance
  6073. // (no Angular decorator on the superclass) or there is no constructor at all
  6074. // in the inheritance chain. Since the two cases cannot be distinguished, the
  6075. // latter has to be assumed.
  6076. return (t) => new t();
  6077. });
  6078. }
  6079. function getFactoryOf(type) {
  6080. if (isForwardRef(type)) {
  6081. return () => {
  6082. const factory = getFactoryOf(resolveForwardRef(type));
  6083. return factory && factory();
  6084. };
  6085. }
  6086. return getFactoryDef(type);
  6087. }
  6088. /**
  6089. * Returns a value from the closest embedded or node injector.
  6090. *
  6091. * @param tNode The Node where the search for the injector should start
  6092. * @param lView The `LView` that contains the `tNode`
  6093. * @param token The token to look for
  6094. * @param flags Injection flags
  6095. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  6096. * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
  6097. */
  6098. function lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, notFoundValue) {
  6099. let currentTNode = tNode;
  6100. let currentLView = lView;
  6101. // When an LView with an embedded view injector is inserted, it'll likely be interlaced with
  6102. // nodes who may have injectors (e.g. node injector -> embedded view injector -> node injector).
  6103. // Since the bloom filters for the node injectors have already been constructed and we don't
  6104. // have a way of extracting the records from an injector, the only way to maintain the correct
  6105. // hierarchy when resolving the value is to walk it node-by-node while attempting to resolve
  6106. // the token at each level.
  6107. while (currentTNode !== null && currentLView !== null &&
  6108. (currentLView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */) &&
  6109. !(currentLView[FLAGS] & 512 /* LViewFlags.IsRoot */)) {
  6110. ngDevMode && assertTNodeForLView(currentTNode, currentLView);
  6111. // Note that this lookup on the node injector is using the `Self` flag, because
  6112. // we don't want the node injector to look at any parent injectors since we
  6113. // may hit the embedded view injector first.
  6114. const nodeInjectorValue = lookupTokenUsingNodeInjector(currentTNode, currentLView, token, flags | InjectFlags.Self, NOT_FOUND);
  6115. if (nodeInjectorValue !== NOT_FOUND) {
  6116. return nodeInjectorValue;
  6117. }
  6118. // Has an explicit type due to a TS bug: https://github.com/microsoft/TypeScript/issues/33191
  6119. let parentTNode = currentTNode.parent;
  6120. // `TNode.parent` includes the parent within the current view only. If it doesn't exist,
  6121. // it means that we've hit the view boundary and we need to go up to the next view.
  6122. if (!parentTNode) {
  6123. // Before we go to the next LView, check if the token exists on the current embedded injector.
  6124. const embeddedViewInjector = currentLView[EMBEDDED_VIEW_INJECTOR];
  6125. if (embeddedViewInjector) {
  6126. const embeddedViewInjectorValue = embeddedViewInjector.get(token, NOT_FOUND, flags);
  6127. if (embeddedViewInjectorValue !== NOT_FOUND) {
  6128. return embeddedViewInjectorValue;
  6129. }
  6130. }
  6131. // Otherwise keep going up the tree.
  6132. parentTNode = getTNodeFromLView(currentLView);
  6133. currentLView = currentLView[DECLARATION_VIEW];
  6134. }
  6135. currentTNode = parentTNode;
  6136. }
  6137. return notFoundValue;
  6138. }
  6139. /** Gets the TNode associated with an LView inside of the declaration view. */
  6140. function getTNodeFromLView(lView) {
  6141. const tView = lView[TVIEW];
  6142. const tViewType = tView.type;
  6143. // The parent pointer differs based on `TView.type`.
  6144. if (tViewType === 2 /* TViewType.Embedded */) {
  6145. ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
  6146. return tView.declTNode;
  6147. }
  6148. else if (tViewType === 1 /* TViewType.Component */) {
  6149. // Components don't have `TView.declTNode` because each instance of component could be
  6150. // inserted in different location, hence `TView.declTNode` is meaningless.
  6151. return lView[T_HOST];
  6152. }
  6153. return null;
  6154. }
  6155. /**
  6156. * Facade for the attribute injection from DI.
  6157. *
  6158. * @codeGenApi
  6159. */
  6160. function ɵɵinjectAttribute(attrNameToInject) {
  6161. return injectAttributeImpl(getCurrentTNode(), attrNameToInject);
  6162. }
  6163. /**
  6164. * Attribute decorator and metadata.
  6165. *
  6166. * @Annotation
  6167. * @publicApi
  6168. */
  6169. const Attribute = makeParamDecorator('Attribute', (attributeName) => ({ attributeName, __NG_ELEMENT_ID__: () => ɵɵinjectAttribute(attributeName) }));
  6170. let _reflect = null;
  6171. function getReflect() {
  6172. return (_reflect = _reflect || new ReflectionCapabilities());
  6173. }
  6174. function reflectDependencies(type) {
  6175. return convertDependencies(getReflect().parameters(type));
  6176. }
  6177. function convertDependencies(deps) {
  6178. return deps.map(dep => reflectDependency(dep));
  6179. }
  6180. function reflectDependency(dep) {
  6181. const meta = {
  6182. token: null,
  6183. attribute: null,
  6184. host: false,
  6185. optional: false,
  6186. self: false,
  6187. skipSelf: false,
  6188. };
  6189. if (Array.isArray(dep) && dep.length > 0) {
  6190. for (let j = 0; j < dep.length; j++) {
  6191. const param = dep[j];
  6192. if (param === undefined) {
  6193. // param may be undefined if type of dep is not set by ngtsc
  6194. continue;
  6195. }
  6196. const proto = Object.getPrototypeOf(param);
  6197. if (param instanceof Optional || proto.ngMetadataName === 'Optional') {
  6198. meta.optional = true;
  6199. }
  6200. else if (param instanceof SkipSelf || proto.ngMetadataName === 'SkipSelf') {
  6201. meta.skipSelf = true;
  6202. }
  6203. else if (param instanceof Self || proto.ngMetadataName === 'Self') {
  6204. meta.self = true;
  6205. }
  6206. else if (param instanceof Host || proto.ngMetadataName === 'Host') {
  6207. meta.host = true;
  6208. }
  6209. else if (param instanceof Inject) {
  6210. meta.token = param.token;
  6211. }
  6212. else if (param instanceof Attribute) {
  6213. if (param.attributeName === undefined) {
  6214. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Attribute name must be defined.`);
  6215. }
  6216. meta.attribute = param.attributeName;
  6217. }
  6218. else {
  6219. meta.token = param;
  6220. }
  6221. }
  6222. }
  6223. else if (dep === undefined || (Array.isArray(dep) && dep.length === 0)) {
  6224. meta.token = null;
  6225. }
  6226. else {
  6227. meta.token = dep;
  6228. }
  6229. return meta;
  6230. }
  6231. /**
  6232. * Map of module-id to the corresponding NgModule.
  6233. */
  6234. const modules = new Map();
  6235. /**
  6236. * Whether to check for duplicate NgModule registrations.
  6237. *
  6238. * This can be disabled for testing.
  6239. */
  6240. let checkForDuplicateNgModules = true;
  6241. function assertSameOrNotExisting(id, type, incoming) {
  6242. if (type && type !== incoming && checkForDuplicateNgModules) {
  6243. throw new Error(`Duplicate module registered for ${id} - ${stringify(type)} vs ${stringify(type.name)}`);
  6244. }
  6245. }
  6246. /**
  6247. * Adds the given NgModule type to Angular's NgModule registry.
  6248. *
  6249. * This is generated as a side-effect of NgModule compilation. Note that the `id` is passed in
  6250. * explicitly and not read from the NgModule definition. This is for two reasons: it avoids a
  6251. * megamorphic read, and in JIT there's a chicken-and-egg problem where the NgModule may not be
  6252. * fully resolved when it's registered.
  6253. *
  6254. * @codeGenApi
  6255. */
  6256. function registerNgModuleType(ngModuleType, id) {
  6257. const existing = modules.get(id) || null;
  6258. assertSameOrNotExisting(id, existing, ngModuleType);
  6259. modules.set(id, ngModuleType);
  6260. }
  6261. function clearModulesForTest() {
  6262. modules.clear();
  6263. }
  6264. function getRegisteredNgModuleType(id) {
  6265. return modules.get(id);
  6266. }
  6267. /**
  6268. * Control whether the NgModule registration system enforces that each NgModule type registered has
  6269. * a unique id.
  6270. *
  6271. * This is useful for testing as the NgModule registry cannot be properly reset between tests with
  6272. * Angular's current API.
  6273. */
  6274. function setAllowDuplicateNgModuleIdsForTest(allowDuplicates) {
  6275. checkForDuplicateNgModules = !allowDuplicates;
  6276. }
  6277. /**
  6278. * Creates a token that can be used in a DI Provider.
  6279. *
  6280. * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
  6281. * runtime representation) such as when injecting an interface, callable type, array or
  6282. * parameterized type.
  6283. *
  6284. * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
  6285. * the `Injector`. This provides an additional level of type safety.
  6286. *
  6287. * <div class="alert is-helpful">
  6288. *
  6289. * **Important Note**: Ensure that you use the same instance of the `InjectionToken` in both the
  6290. * provider and the injection call. Creating a new instance of `InjectionToken` in different places,
  6291. * even with the same description, will be treated as different tokens by Angular's DI system,
  6292. * leading to a `NullInjectorError`.
  6293. *
  6294. * </div>
  6295. *
  6296. * <code-example format="typescript" language="typescript" path="injection-token/src/main.ts"
  6297. * region="InjectionToken"></code-example>
  6298. *
  6299. * When creating an `InjectionToken`, you can optionally specify a factory function which returns
  6300. * (possibly by creating) a default value of the parameterized type `T`. This sets up the
  6301. * `InjectionToken` using this factory as a provider as if it was defined explicitly in the
  6302. * application's root injector. If the factory function, which takes zero arguments, needs to inject
  6303. * dependencies, it can do so using the [`inject`](api/core/inject) function.
  6304. * As you can see in the Tree-shakable InjectionToken example below.
  6305. *
  6306. * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
  6307. * overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note:
  6308. * this option is now deprecated). As mentioned above, `'root'` is the default value for
  6309. * `providedIn`.
  6310. *
  6311. * The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated.
  6312. *
  6313. * @usageNotes
  6314. * ### Basic Examples
  6315. *
  6316. * ### Plain InjectionToken
  6317. *
  6318. * {@example core/di/ts/injector_spec.ts region='InjectionToken'}
  6319. *
  6320. * ### Tree-shakable InjectionToken
  6321. *
  6322. * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
  6323. *
  6324. * @publicApi
  6325. */
  6326. class InjectionToken {
  6327. /**
  6328. * @param _desc Description for the token,
  6329. * used only for debugging purposes,
  6330. * it should but does not need to be unique
  6331. * @param options Options for the token's usage, as described above
  6332. */
  6333. constructor(_desc, options) {
  6334. this._desc = _desc;
  6335. /** @internal */
  6336. this.ngMetadataName = 'InjectionToken';
  6337. this.ɵprov = undefined;
  6338. if (typeof options == 'number') {
  6339. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  6340. assertLessThan(options, 0, 'Only negative numbers are supported here');
  6341. // This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
  6342. // See `InjectorMarkers`
  6343. this.__NG_ELEMENT_ID__ = options;
  6344. }
  6345. else if (options !== undefined) {
  6346. this.ɵprov = ɵɵdefineInjectable({
  6347. token: this,
  6348. providedIn: options.providedIn || 'root',
  6349. factory: options.factory,
  6350. });
  6351. }
  6352. }
  6353. /**
  6354. * @internal
  6355. */
  6356. get multi() {
  6357. return this;
  6358. }
  6359. toString() {
  6360. return `InjectionToken ${this._desc}`;
  6361. }
  6362. }
  6363. /**
  6364. * Most of the use of `document` in Angular is from within the DI system so it is possible to simply
  6365. * inject the `DOCUMENT` token and are done.
  6366. *
  6367. * Ivy is special because it does not rely upon the DI and must get hold of the document some other
  6368. * way.
  6369. *
  6370. * The solution is to define `getDocument()` and `setDocument()` top-level functions for ivy.
  6371. * Wherever ivy needs the global document, it calls `getDocument()` instead.
  6372. *
  6373. * When running ivy outside of a browser environment, it is necessary to call `setDocument()` to
  6374. * tell ivy what the global `document` is.
  6375. *
  6376. * Angular does this for us in each of the standard platforms (`Browser` and `Server`)
  6377. * by calling `setDocument()` when providing the `DOCUMENT` token.
  6378. */
  6379. let DOCUMENT = undefined;
  6380. /**
  6381. * Tell ivy what the `document` is for this platform.
  6382. *
  6383. * It is only necessary to call this if the current platform is not a browser.
  6384. *
  6385. * @param document The object representing the global `document` in this environment.
  6386. */
  6387. function setDocument(document) {
  6388. DOCUMENT = document;
  6389. }
  6390. /**
  6391. * Access the object that represents the `document` for this platform.
  6392. *
  6393. * Ivy calls this whenever it needs to access the `document` object.
  6394. * For example to create the renderer or to do sanitization.
  6395. */
  6396. function getDocument() {
  6397. if (DOCUMENT !== undefined) {
  6398. return DOCUMENT;
  6399. }
  6400. else if (typeof document !== 'undefined') {
  6401. return document;
  6402. }
  6403. throw new RuntimeError(210 /* RuntimeErrorCode.MISSING_DOCUMENT */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
  6404. `The document object is not available in this context. Make sure the DOCUMENT injection token is provided.`);
  6405. // No "document" can be found. This should only happen if we are running ivy outside Angular and
  6406. // the current platform is not a browser. Since this is not a supported scenario at the moment
  6407. // this should not happen in Angular apps.
  6408. // Once we support running ivy outside of Angular we will need to publish `setDocument()` as a
  6409. // public API.
  6410. }
  6411. /**
  6412. * A [DI token](guide/glossary#di-token "DI token definition") representing a string ID, used
  6413. * primarily for prefixing application attributes and CSS styles when
  6414. * {@link ViewEncapsulation#Emulated} is being used.
  6415. *
  6416. * The token is needed in cases when multiple applications are bootstrapped on a page
  6417. * (for example, using `bootstrapApplication` calls). In this case, ensure that those applications
  6418. * have different `APP_ID` value setup. For example:
  6419. *
  6420. * ```
  6421. * bootstrapApplication(ComponentA, {
  6422. * providers: [
  6423. * { provide: APP_ID, useValue: 'app-a' },
  6424. * // ... other providers ...
  6425. * ]
  6426. * });
  6427. *
  6428. * bootstrapApplication(ComponentB, {
  6429. * providers: [
  6430. * { provide: APP_ID, useValue: 'app-b' },
  6431. * // ... other providers ...
  6432. * ]
  6433. * });
  6434. * ```
  6435. *
  6436. * By default, when there is only one application bootstrapped, you don't need to provide the
  6437. * `APP_ID` token (the `ng` will be used as an app ID).
  6438. *
  6439. * @publicApi
  6440. */
  6441. const APP_ID = new InjectionToken('AppId', {
  6442. providedIn: 'root',
  6443. factory: () => DEFAULT_APP_ID,
  6444. });
  6445. /** Default value of the `APP_ID` token. */
  6446. const DEFAULT_APP_ID = 'ng';
  6447. /**
  6448. * A function that is executed when a platform is initialized.
  6449. * @publicApi
  6450. */
  6451. const PLATFORM_INITIALIZER = new InjectionToken('Platform Initializer');
  6452. /**
  6453. * A token that indicates an opaque platform ID.
  6454. * @publicApi
  6455. */
  6456. const PLATFORM_ID = new InjectionToken('Platform ID', {
  6457. providedIn: 'platform',
  6458. factory: () => 'unknown', // set a default platform name, when none set explicitly
  6459. });
  6460. /**
  6461. * A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of
  6462. * the application
  6463. * @publicApi
  6464. * @deprecated
  6465. */
  6466. const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL');
  6467. // We keep this token here, rather than the animations package, so that modules that only care
  6468. // about which animations module is loaded (e.g. the CDK) can retrieve it without having to
  6469. // include extra dependencies. See #44970 for more context.
  6470. /**
  6471. * A [DI token](guide/glossary#di-token "DI token definition") that indicates which animations
  6472. * module has been loaded.
  6473. * @publicApi
  6474. */
  6475. const ANIMATION_MODULE_TYPE = new InjectionToken('AnimationModuleType');
  6476. // TODO(crisbeto): link to CSP guide here.
  6477. /**
  6478. * Token used to configure the [Content Security Policy](https://web.dev/strict-csp/) nonce that
  6479. * Angular will apply when inserting inline styles. If not provided, Angular will look up its value
  6480. * from the `ngCspNonce` attribute of the application root node.
  6481. *
  6482. * @publicApi
  6483. */
  6484. const CSP_NONCE = new InjectionToken('CSP nonce', {
  6485. providedIn: 'root',
  6486. factory: () => {
  6487. // Ideally we wouldn't have to use `querySelector` here since we know that the nonce will be on
  6488. // the root node, but because the token value is used in renderers, it has to be available
  6489. // *very* early in the bootstrapping process. This should be a fairly shallow search, because
  6490. // the app won't have been added to the DOM yet. Some approaches that were considered:
  6491. // 1. Find the root node through `ApplicationRef.components[i].location` - normally this would
  6492. // be enough for our purposes, but the token is injected very early so the `components` array
  6493. // isn't populated yet.
  6494. // 2. Find the root `LView` through the current `LView` - renderers are a prerequisite to
  6495. // creating the `LView`. This means that no `LView` will have been entered when this factory is
  6496. // invoked for the root component.
  6497. // 3. Have the token factory return `() => string` which is invoked when a nonce is requested -
  6498. // the slightly later execution does allow us to get an `LView` reference, but the fact that
  6499. // it is a function means that it could be executed at *any* time (including immediately) which
  6500. // may lead to weird bugs.
  6501. // 4. Have the `ComponentFactory` read the attribute and provide it to the injector under the
  6502. // hood - has the same problem as #1 and #2 in that the renderer is used to query for the root
  6503. // node and the nonce value needs to be available when the renderer is created.
  6504. return getDocument().body?.querySelector('[ngCspNonce]')?.getAttribute('ngCspNonce') || null;
  6505. },
  6506. });
  6507. /**
  6508. * Internal token to collect all SSR-related features enabled for this application.
  6509. *
  6510. * Note: the token is in `core` to let other packages register features (the `core`
  6511. * package is imported in other packages).
  6512. */
  6513. const ENABLED_SSR_FEATURES = new InjectionToken((typeof ngDevMode === 'undefined' || ngDevMode) ? 'ENABLED_SSR_FEATURES' : '', {
  6514. providedIn: 'root',
  6515. factory: () => new Set(),
  6516. });
  6517. /**
  6518. * A multi-provider token for initialization functions that will run upon construction of an
  6519. * environment injector.
  6520. *
  6521. * @publicApi
  6522. */
  6523. const ENVIRONMENT_INITIALIZER = new InjectionToken('ENVIRONMENT_INITIALIZER');
  6524. /**
  6525. * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
  6526. *
  6527. * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
  6528. * project.
  6529. *
  6530. * @publicApi
  6531. */
  6532. const INJECTOR = new InjectionToken('INJECTOR',
  6533. // Disable tslint because this is const enum which gets inlined not top level prop access.
  6534. // tslint:disable-next-line: no-toplevel-property-access
  6535. -1 /* InjectorMarkers.Injector */);
  6536. const INJECTOR_DEF_TYPES = new InjectionToken('INJECTOR_DEF_TYPES');
  6537. class NullInjector {
  6538. get(token, notFoundValue = THROW_IF_NOT_FOUND) {
  6539. if (notFoundValue === THROW_IF_NOT_FOUND) {
  6540. const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
  6541. error.name = 'NullInjectorError';
  6542. throw error;
  6543. }
  6544. return notFoundValue;
  6545. }
  6546. }
  6547. /**
  6548. * Wrap an array of `Provider`s into `EnvironmentProviders`, preventing them from being accidentally
  6549. * referenced in `@Component` in a component injector.
  6550. */
  6551. function makeEnvironmentProviders(providers) {
  6552. return {
  6553. ɵproviders: providers,
  6554. };
  6555. }
  6556. /**
  6557. * Collects providers from all NgModules and standalone components, including transitively imported
  6558. * ones.
  6559. *
  6560. * Providers extracted via `importProvidersFrom` are only usable in an application injector or
  6561. * another environment injector (such as a route injector). They should not be used in component
  6562. * providers.
  6563. *
  6564. * More information about standalone components can be found in [this
  6565. * guide](guide/standalone-components).
  6566. *
  6567. * @usageNotes
  6568. * The results of the `importProvidersFrom` call can be used in the `bootstrapApplication` call:
  6569. *
  6570. * ```typescript
  6571. * await bootstrapApplication(RootComponent, {
  6572. * providers: [
  6573. * importProvidersFrom(NgModuleOne, NgModuleTwo)
  6574. * ]
  6575. * });
  6576. * ```
  6577. *
  6578. * You can also use the `importProvidersFrom` results in the `providers` field of a route, when a
  6579. * standalone component is used:
  6580. *
  6581. * ```typescript
  6582. * export const ROUTES: Route[] = [
  6583. * {
  6584. * path: 'foo',
  6585. * providers: [
  6586. * importProvidersFrom(NgModuleOne, NgModuleTwo)
  6587. * ],
  6588. * component: YourStandaloneComponent
  6589. * }
  6590. * ];
  6591. * ```
  6592. *
  6593. * @returns Collected providers from the specified list of types.
  6594. * @publicApi
  6595. */
  6596. function importProvidersFrom(...sources) {
  6597. return {
  6598. ɵproviders: internalImportProvidersFrom(true, sources),
  6599. ɵfromNgModule: true,
  6600. };
  6601. }
  6602. function internalImportProvidersFrom(checkForStandaloneCmp, ...sources) {
  6603. const providersOut = [];
  6604. const dedup = new Set(); // already seen types
  6605. let injectorTypesWithProviders;
  6606. const collectProviders = (provider) => {
  6607. providersOut.push(provider);
  6608. };
  6609. deepForEach(sources, source => {
  6610. if ((typeof ngDevMode === 'undefined' || ngDevMode) && checkForStandaloneCmp) {
  6611. const cmpDef = getComponentDef$1(source);
  6612. if (cmpDef?.standalone) {
  6613. throw new RuntimeError(800 /* RuntimeErrorCode.IMPORT_PROVIDERS_FROM_STANDALONE */, `Importing providers supports NgModule or ModuleWithProviders but got a standalone component "${stringifyForError(source)}"`);
  6614. }
  6615. }
  6616. // Narrow `source` to access the internal type analogue for `ModuleWithProviders`.
  6617. const internalSource = source;
  6618. if (walkProviderTree(internalSource, collectProviders, [], dedup)) {
  6619. injectorTypesWithProviders ||= [];
  6620. injectorTypesWithProviders.push(internalSource);
  6621. }
  6622. });
  6623. // Collect all providers from `ModuleWithProviders` types.
  6624. if (injectorTypesWithProviders !== undefined) {
  6625. processInjectorTypesWithProviders(injectorTypesWithProviders, collectProviders);
  6626. }
  6627. return providersOut;
  6628. }
  6629. /**
  6630. * Collects all providers from the list of `ModuleWithProviders` and appends them to the provided
  6631. * array.
  6632. */
  6633. function processInjectorTypesWithProviders(typesWithProviders, visitor) {
  6634. for (let i = 0; i < typesWithProviders.length; i++) {
  6635. const { ngModule, providers } = typesWithProviders[i];
  6636. deepForEachProvider(providers, provider => {
  6637. ngDevMode && validateProvider(provider, providers || EMPTY_ARRAY, ngModule);
  6638. visitor(provider, ngModule);
  6639. });
  6640. }
  6641. }
  6642. /**
  6643. * The logic visits an `InjectorType`, an `InjectorTypeWithProviders`, or a standalone
  6644. * `ComponentType`, and all of its transitive providers and collects providers.
  6645. *
  6646. * If an `InjectorTypeWithProviders` that declares providers besides the type is specified,
  6647. * the function will return "true" to indicate that the providers of the type definition need
  6648. * to be processed. This allows us to process providers of injector types after all imports of
  6649. * an injector definition are processed. (following View Engine semantics: see FW-1349)
  6650. */
  6651. function walkProviderTree(container, visitor, parents, dedup) {
  6652. container = resolveForwardRef(container);
  6653. if (!container)
  6654. return false;
  6655. // The actual type which had the definition. Usually `container`, but may be an unwrapped type
  6656. // from `InjectorTypeWithProviders`.
  6657. let defType = null;
  6658. let injDef = getInjectorDef(container);
  6659. const cmpDef = !injDef && getComponentDef$1(container);
  6660. if (!injDef && !cmpDef) {
  6661. // `container` is not an injector type or a component type. It might be:
  6662. // * An `InjectorTypeWithProviders` that wraps an injector type.
  6663. // * A standalone directive or pipe that got pulled in from a standalone component's
  6664. // dependencies.
  6665. // Try to unwrap it as an `InjectorTypeWithProviders` first.
  6666. const ngModule = container.ngModule;
  6667. injDef = getInjectorDef(ngModule);
  6668. if (injDef) {
  6669. defType = ngModule;
  6670. }
  6671. else {
  6672. // Not a component or injector type, so ignore it.
  6673. return false;
  6674. }
  6675. }
  6676. else if (cmpDef && !cmpDef.standalone) {
  6677. return false;
  6678. }
  6679. else {
  6680. defType = container;
  6681. }
  6682. // Check for circular dependencies.
  6683. if (ngDevMode && parents.indexOf(defType) !== -1) {
  6684. const defName = stringify(defType);
  6685. const path = parents.map(stringify);
  6686. throwCyclicDependencyError(defName, path);
  6687. }
  6688. // Check for multiple imports of the same module
  6689. const isDuplicate = dedup.has(defType);
  6690. if (cmpDef) {
  6691. if (isDuplicate) {
  6692. // This component definition has already been processed.
  6693. return false;
  6694. }
  6695. dedup.add(defType);
  6696. if (cmpDef.dependencies) {
  6697. const deps = typeof cmpDef.dependencies === 'function' ? cmpDef.dependencies() : cmpDef.dependencies;
  6698. for (const dep of deps) {
  6699. walkProviderTree(dep, visitor, parents, dedup);
  6700. }
  6701. }
  6702. }
  6703. else if (injDef) {
  6704. // First, include providers from any imports.
  6705. if (injDef.imports != null && !isDuplicate) {
  6706. // Before processing defType's imports, add it to the set of parents. This way, if it ends
  6707. // up deeply importing itself, this can be detected.
  6708. ngDevMode && parents.push(defType);
  6709. // Add it to the set of dedups. This way we can detect multiple imports of the same module
  6710. dedup.add(defType);
  6711. let importTypesWithProviders;
  6712. try {
  6713. deepForEach(injDef.imports, imported => {
  6714. if (walkProviderTree(imported, visitor, parents, dedup)) {
  6715. importTypesWithProviders ||= [];
  6716. // If the processed import is an injector type with providers, we store it in the
  6717. // list of import types with providers, so that we can process those afterwards.
  6718. importTypesWithProviders.push(imported);
  6719. }
  6720. });
  6721. }
  6722. finally {
  6723. // Remove it from the parents set when finished.
  6724. ngDevMode && parents.pop();
  6725. }
  6726. // Imports which are declared with providers (TypeWithProviders) need to be processed
  6727. // after all imported modules are processed. This is similar to how View Engine
  6728. // processes/merges module imports in the metadata resolver. See: FW-1349.
  6729. if (importTypesWithProviders !== undefined) {
  6730. processInjectorTypesWithProviders(importTypesWithProviders, visitor);
  6731. }
  6732. }
  6733. if (!isDuplicate) {
  6734. // Track the InjectorType and add a provider for it.
  6735. // It's important that this is done after the def's imports.
  6736. const factory = getFactoryDef(defType) || (() => new defType());
  6737. // Append extra providers to make more info available for consumers (to retrieve an injector
  6738. // type), as well as internally (to calculate an injection scope correctly and eagerly
  6739. // instantiate a `defType` when an injector is created).
  6740. // Provider to create `defType` using its factory.
  6741. visitor({ provide: defType, useFactory: factory, deps: EMPTY_ARRAY }, defType);
  6742. // Make this `defType` available to an internal logic that calculates injector scope.
  6743. visitor({ provide: INJECTOR_DEF_TYPES, useValue: defType, multi: true }, defType);
  6744. // Provider to eagerly instantiate `defType` via `INJECTOR_INITIALIZER`.
  6745. visitor({ provide: ENVIRONMENT_INITIALIZER, useValue: () => ɵɵinject(defType), multi: true }, defType);
  6746. }
  6747. // Next, include providers listed on the definition itself.
  6748. const defProviders = injDef.providers;
  6749. if (defProviders != null && !isDuplicate) {
  6750. const injectorType = container;
  6751. deepForEachProvider(defProviders, provider => {
  6752. ngDevMode && validateProvider(provider, defProviders, injectorType);
  6753. visitor(provider, injectorType);
  6754. });
  6755. }
  6756. }
  6757. else {
  6758. // Should not happen, but just in case.
  6759. return false;
  6760. }
  6761. return (defType !== container &&
  6762. container.providers !== undefined);
  6763. }
  6764. function validateProvider(provider, providers, containerType) {
  6765. if (isTypeProvider(provider) || isValueProvider(provider) || isFactoryProvider(provider) ||
  6766. isExistingProvider(provider)) {
  6767. return;
  6768. }
  6769. // Here we expect the provider to be a `useClass` provider (by elimination).
  6770. const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide));
  6771. if (!classRef) {
  6772. throwInvalidProviderError(containerType, providers, provider);
  6773. }
  6774. }
  6775. function deepForEachProvider(providers, fn) {
  6776. for (let provider of providers) {
  6777. if (isEnvironmentProviders(provider)) {
  6778. provider = provider.ɵproviders;
  6779. }
  6780. if (Array.isArray(provider)) {
  6781. deepForEachProvider(provider, fn);
  6782. }
  6783. else {
  6784. fn(provider);
  6785. }
  6786. }
  6787. }
  6788. const USE_VALUE$1 = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty });
  6789. function isValueProvider(value) {
  6790. return value !== null && typeof value == 'object' && USE_VALUE$1 in value;
  6791. }
  6792. function isExistingProvider(value) {
  6793. return !!(value && value.useExisting);
  6794. }
  6795. function isFactoryProvider(value) {
  6796. return !!(value && value.useFactory);
  6797. }
  6798. function isTypeProvider(value) {
  6799. return typeof value === 'function';
  6800. }
  6801. function isClassProvider(value) {
  6802. return !!value.useClass;
  6803. }
  6804. /**
  6805. * An internal token whose presence in an injector indicates that the injector should treat itself
  6806. * as a root scoped injector when processing requests for unknown tokens which may indicate
  6807. * they are provided in the root scope.
  6808. */
  6809. const INJECTOR_SCOPE = new InjectionToken('Set Injector scope.');
  6810. /**
  6811. * Marker which indicates that a value has not yet been created from the factory function.
  6812. */
  6813. const NOT_YET = {};
  6814. /**
  6815. * Marker which indicates that the factory function for a token is in the process of being called.
  6816. *
  6817. * If the injector is asked to inject a token with its value set to CIRCULAR, that indicates
  6818. * injection of a dependency has recursively attempted to inject the original token, and there is
  6819. * a circular dependency among the providers.
  6820. */
  6821. const CIRCULAR = {};
  6822. /**
  6823. * A lazily initialized NullInjector.
  6824. */
  6825. let NULL_INJECTOR = undefined;
  6826. function getNullInjector() {
  6827. if (NULL_INJECTOR === undefined) {
  6828. NULL_INJECTOR = new NullInjector();
  6829. }
  6830. return NULL_INJECTOR;
  6831. }
  6832. /**
  6833. * An `Injector` that's part of the environment injector hierarchy, which exists outside of the
  6834. * component tree.
  6835. */
  6836. class EnvironmentInjector {
  6837. }
  6838. class R3Injector extends EnvironmentInjector {
  6839. /**
  6840. * Flag indicating that this injector was previously destroyed.
  6841. */
  6842. get destroyed() {
  6843. return this._destroyed;
  6844. }
  6845. constructor(providers, parent, source, scopes) {
  6846. super();
  6847. this.parent = parent;
  6848. this.source = source;
  6849. this.scopes = scopes;
  6850. /**
  6851. * Map of tokens to records which contain the instances of those tokens.
  6852. * - `null` value implies that we don't have the record. Used by tree-shakable injectors
  6853. * to prevent further searches.
  6854. */
  6855. this.records = new Map();
  6856. /**
  6857. * Set of values instantiated by this injector which contain `ngOnDestroy` lifecycle hooks.
  6858. */
  6859. this._ngOnDestroyHooks = new Set();
  6860. this._onDestroyHooks = [];
  6861. this._destroyed = false;
  6862. // Start off by creating Records for every provider.
  6863. forEachSingleProvider(providers, provider => this.processProvider(provider));
  6864. // Make sure the INJECTOR token provides this injector.
  6865. this.records.set(INJECTOR, makeRecord(undefined, this));
  6866. // And `EnvironmentInjector` if the current injector is supposed to be env-scoped.
  6867. if (scopes.has('environment')) {
  6868. this.records.set(EnvironmentInjector, makeRecord(undefined, this));
  6869. }
  6870. // Detect whether this injector has the APP_ROOT_SCOPE token and thus should provide
  6871. // any injectable scoped to APP_ROOT_SCOPE.
  6872. const record = this.records.get(INJECTOR_SCOPE);
  6873. if (record != null && typeof record.value === 'string') {
  6874. this.scopes.add(record.value);
  6875. }
  6876. this.injectorDefTypes =
  6877. new Set(this.get(INJECTOR_DEF_TYPES.multi, EMPTY_ARRAY, InjectFlags.Self));
  6878. }
  6879. /**
  6880. * Destroy the injector and release references to every instance or provider associated with it.
  6881. *
  6882. * Also calls the `OnDestroy` lifecycle hooks of every instance that was created for which a
  6883. * hook was found.
  6884. */
  6885. destroy() {
  6886. this.assertNotDestroyed();
  6887. // Set destroyed = true first, in case lifecycle hooks re-enter destroy().
  6888. this._destroyed = true;
  6889. try {
  6890. // Call all the lifecycle hooks.
  6891. for (const service of this._ngOnDestroyHooks) {
  6892. service.ngOnDestroy();
  6893. }
  6894. const onDestroyHooks = this._onDestroyHooks;
  6895. // Reset the _onDestroyHooks array before iterating over it to prevent hooks that unregister
  6896. // themselves from mutating the array during iteration.
  6897. this._onDestroyHooks = [];
  6898. for (const hook of onDestroyHooks) {
  6899. hook();
  6900. }
  6901. }
  6902. finally {
  6903. // Release all references.
  6904. this.records.clear();
  6905. this._ngOnDestroyHooks.clear();
  6906. this.injectorDefTypes.clear();
  6907. }
  6908. }
  6909. onDestroy(callback) {
  6910. this.assertNotDestroyed();
  6911. this._onDestroyHooks.push(callback);
  6912. return () => this.removeOnDestroy(callback);
  6913. }
  6914. runInContext(fn) {
  6915. this.assertNotDestroyed();
  6916. const previousInjector = setCurrentInjector(this);
  6917. const previousInjectImplementation = setInjectImplementation(undefined);
  6918. let prevInjectContext;
  6919. if (ngDevMode) {
  6920. prevInjectContext = setInjectorProfilerContext({ injector: this, token: null });
  6921. }
  6922. try {
  6923. return fn();
  6924. }
  6925. finally {
  6926. setCurrentInjector(previousInjector);
  6927. setInjectImplementation(previousInjectImplementation);
  6928. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  6929. }
  6930. }
  6931. get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) {
  6932. this.assertNotDestroyed();
  6933. if (token.hasOwnProperty(NG_ENV_ID)) {
  6934. return token[NG_ENV_ID](this);
  6935. }
  6936. flags = convertToBitFlags(flags);
  6937. // Set the injection context.
  6938. let prevInjectContext;
  6939. if (ngDevMode) {
  6940. prevInjectContext = setInjectorProfilerContext({ injector: this, token: token });
  6941. }
  6942. const previousInjector = setCurrentInjector(this);
  6943. const previousInjectImplementation = setInjectImplementation(undefined);
  6944. try {
  6945. // Check for the SkipSelf flag.
  6946. if (!(flags & InjectFlags.SkipSelf)) {
  6947. // SkipSelf isn't set, check if the record belongs to this injector.
  6948. let record = this.records.get(token);
  6949. if (record === undefined) {
  6950. // No record, but maybe the token is scoped to this injector. Look for an injectable
  6951. // def with a scope matching this injector.
  6952. const def = couldBeInjectableType(token) && getInjectableDef(token);
  6953. if (def && this.injectableDefInScope(def)) {
  6954. // Found an injectable def and it's scoped to this injector. Pretend as if it was here
  6955. // all along.
  6956. record = makeRecord(injectableDefOrInjectorDefFactory(token), NOT_YET);
  6957. }
  6958. else {
  6959. record = null;
  6960. }
  6961. this.records.set(token, record);
  6962. }
  6963. // If a record was found, get the instance for it and return it.
  6964. if (record != null /* NOT null || undefined */) {
  6965. return this.hydrate(token, record);
  6966. }
  6967. }
  6968. // Select the next injector based on the Self flag - if self is set, the next injector is
  6969. // the NullInjector, otherwise it's the parent.
  6970. const nextInjector = !(flags & InjectFlags.Self) ? this.parent : getNullInjector();
  6971. // Set the notFoundValue based on the Optional flag - if optional is set and notFoundValue
  6972. // is undefined, the value is null, otherwise it's the notFoundValue.
  6973. notFoundValue = (flags & InjectFlags.Optional) && notFoundValue === THROW_IF_NOT_FOUND ?
  6974. null :
  6975. notFoundValue;
  6976. return nextInjector.get(token, notFoundValue);
  6977. }
  6978. catch (e) {
  6979. if (e.name === 'NullInjectorError') {
  6980. const path = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
  6981. path.unshift(stringify(token));
  6982. if (previousInjector) {
  6983. // We still have a parent injector, keep throwing
  6984. throw e;
  6985. }
  6986. else {
  6987. // Format & throw the final error message when we don't have any previous injector
  6988. return catchInjectorError(e, token, 'R3InjectorError', this.source);
  6989. }
  6990. }
  6991. else {
  6992. throw e;
  6993. }
  6994. }
  6995. finally {
  6996. // Lastly, restore the previous injection context.
  6997. setInjectImplementation(previousInjectImplementation);
  6998. setCurrentInjector(previousInjector);
  6999. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  7000. }
  7001. }
  7002. /** @internal */
  7003. resolveInjectorInitializers() {
  7004. const previousInjector = setCurrentInjector(this);
  7005. const previousInjectImplementation = setInjectImplementation(undefined);
  7006. let prevInjectContext;
  7007. if (ngDevMode) {
  7008. prevInjectContext = setInjectorProfilerContext({ injector: this, token: null });
  7009. }
  7010. try {
  7011. const initializers = this.get(ENVIRONMENT_INITIALIZER.multi, EMPTY_ARRAY, InjectFlags.Self);
  7012. if (ngDevMode && !Array.isArray(initializers)) {
  7013. throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' +
  7014. `(expected an array, but got ${typeof initializers}). ` +
  7015. 'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' +
  7016. '`multi: true` provider.');
  7017. }
  7018. for (const initializer of initializers) {
  7019. initializer();
  7020. }
  7021. }
  7022. finally {
  7023. setCurrentInjector(previousInjector);
  7024. setInjectImplementation(previousInjectImplementation);
  7025. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  7026. }
  7027. }
  7028. toString() {
  7029. const tokens = [];
  7030. const records = this.records;
  7031. for (const token of records.keys()) {
  7032. tokens.push(stringify(token));
  7033. }
  7034. return `R3Injector[${tokens.join(', ')}]`;
  7035. }
  7036. assertNotDestroyed() {
  7037. if (this._destroyed) {
  7038. throw new RuntimeError(205 /* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED */, ngDevMode && 'Injector has already been destroyed.');
  7039. }
  7040. }
  7041. /**
  7042. * Process a `SingleProvider` and add it.
  7043. */
  7044. processProvider(provider) {
  7045. // Determine the token from the provider. Either it's its own token, or has a {provide: ...}
  7046. // property.
  7047. provider = resolveForwardRef(provider);
  7048. let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider && provider.provide);
  7049. // Construct a `Record` for the provider.
  7050. const record = providerToRecord(provider);
  7051. if (ngDevMode) {
  7052. runInInjectorProfilerContext(this, token, () => {
  7053. // Emit InjectorProfilerEventType.Create if provider is a value provider because
  7054. // these are the only providers that do not go through the value hydration logic
  7055. // where this event would normally be emitted from.
  7056. if (isValueProvider(provider)) {
  7057. emitInstanceCreatedByInjectorEvent(provider.useValue);
  7058. }
  7059. emitProviderConfiguredEvent(provider);
  7060. });
  7061. }
  7062. if (!isTypeProvider(provider) && provider.multi === true) {
  7063. // If the provider indicates that it's a multi-provider, process it specially.
  7064. // First check whether it's been defined already.
  7065. let multiRecord = this.records.get(token);
  7066. if (multiRecord) {
  7067. // It has. Throw a nice error if
  7068. if (ngDevMode && multiRecord.multi === undefined) {
  7069. throwMixedMultiProviderError();
  7070. }
  7071. }
  7072. else {
  7073. multiRecord = makeRecord(undefined, NOT_YET, true);
  7074. multiRecord.factory = () => injectArgs(multiRecord.multi);
  7075. this.records.set(token, multiRecord);
  7076. }
  7077. token = provider;
  7078. multiRecord.multi.push(provider);
  7079. }
  7080. else {
  7081. const existing = this.records.get(token);
  7082. if (ngDevMode && existing && existing.multi !== undefined) {
  7083. throwMixedMultiProviderError();
  7084. }
  7085. }
  7086. this.records.set(token, record);
  7087. }
  7088. hydrate(token, record) {
  7089. if (ngDevMode && record.value === CIRCULAR) {
  7090. throwCyclicDependencyError(stringify(token));
  7091. }
  7092. else if (record.value === NOT_YET) {
  7093. record.value = CIRCULAR;
  7094. if (ngDevMode) {
  7095. runInInjectorProfilerContext(this, token, () => {
  7096. record.value = record.factory();
  7097. emitInstanceCreatedByInjectorEvent(record.value);
  7098. });
  7099. }
  7100. else {
  7101. record.value = record.factory();
  7102. }
  7103. }
  7104. if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) {
  7105. this._ngOnDestroyHooks.add(record.value);
  7106. }
  7107. return record.value;
  7108. }
  7109. injectableDefInScope(def) {
  7110. if (!def.providedIn) {
  7111. return false;
  7112. }
  7113. const providedIn = resolveForwardRef(def.providedIn);
  7114. if (typeof providedIn === 'string') {
  7115. return providedIn === 'any' || (this.scopes.has(providedIn));
  7116. }
  7117. else {
  7118. return this.injectorDefTypes.has(providedIn);
  7119. }
  7120. }
  7121. removeOnDestroy(callback) {
  7122. const destroyCBIdx = this._onDestroyHooks.indexOf(callback);
  7123. if (destroyCBIdx !== -1) {
  7124. this._onDestroyHooks.splice(destroyCBIdx, 1);
  7125. }
  7126. }
  7127. }
  7128. function injectableDefOrInjectorDefFactory(token) {
  7129. // Most tokens will have an injectable def directly on them, which specifies a factory directly.
  7130. const injectableDef = getInjectableDef(token);
  7131. const factory = injectableDef !== null ? injectableDef.factory : getFactoryDef(token);
  7132. if (factory !== null) {
  7133. return factory;
  7134. }
  7135. // InjectionTokens should have an injectable def (ɵprov) and thus should be handled above.
  7136. // If it's missing that, it's an error.
  7137. if (token instanceof InjectionToken) {
  7138. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Token ${stringify(token)} is missing a ɵprov definition.`);
  7139. }
  7140. // Undecorated types can sometimes be created if they have no constructor arguments.
  7141. if (token instanceof Function) {
  7142. return getUndecoratedInjectableFactory(token);
  7143. }
  7144. // There was no way to resolve a factory for this token.
  7145. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && 'unreachable');
  7146. }
  7147. function getUndecoratedInjectableFactory(token) {
  7148. // If the token has parameters then it has dependencies that we cannot resolve implicitly.
  7149. const paramLength = token.length;
  7150. if (paramLength > 0) {
  7151. const args = newArray(paramLength, '?');
  7152. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).`);
  7153. }
  7154. // The constructor function appears to have no parameters.
  7155. // This might be because it inherits from a super-class. In which case, use an injectable
  7156. // def from an ancestor if there is one.
  7157. // Otherwise this really is a simple class with no dependencies, so return a factory that
  7158. // just instantiates the zero-arg constructor.
  7159. const inheritedInjectableDef = getInheritedInjectableDef(token);
  7160. if (inheritedInjectableDef !== null) {
  7161. return () => inheritedInjectableDef.factory(token);
  7162. }
  7163. else {
  7164. return () => new token();
  7165. }
  7166. }
  7167. function providerToRecord(provider) {
  7168. if (isValueProvider(provider)) {
  7169. return makeRecord(undefined, provider.useValue);
  7170. }
  7171. else {
  7172. const factory = providerToFactory(provider);
  7173. return makeRecord(factory, NOT_YET);
  7174. }
  7175. }
  7176. /**
  7177. * Converts a `SingleProvider` into a factory function.
  7178. *
  7179. * @param provider provider to convert to factory
  7180. */
  7181. function providerToFactory(provider, ngModuleType, providers) {
  7182. let factory = undefined;
  7183. if (ngDevMode && isEnvironmentProviders(provider)) {
  7184. throwInvalidProviderError(undefined, providers, provider);
  7185. }
  7186. if (isTypeProvider(provider)) {
  7187. const unwrappedProvider = resolveForwardRef(provider);
  7188. return getFactoryDef(unwrappedProvider) || injectableDefOrInjectorDefFactory(unwrappedProvider);
  7189. }
  7190. else {
  7191. if (isValueProvider(provider)) {
  7192. factory = () => resolveForwardRef(provider.useValue);
  7193. }
  7194. else if (isFactoryProvider(provider)) {
  7195. factory = () => provider.useFactory(...injectArgs(provider.deps || []));
  7196. }
  7197. else if (isExistingProvider(provider)) {
  7198. factory = () => ɵɵinject(resolveForwardRef(provider.useExisting));
  7199. }
  7200. else {
  7201. const classRef = resolveForwardRef(provider &&
  7202. (provider.useClass || provider.provide));
  7203. if (ngDevMode && !classRef) {
  7204. throwInvalidProviderError(ngModuleType, providers, provider);
  7205. }
  7206. if (hasDeps(provider)) {
  7207. factory = () => new (classRef)(...injectArgs(provider.deps));
  7208. }
  7209. else {
  7210. return getFactoryDef(classRef) || injectableDefOrInjectorDefFactory(classRef);
  7211. }
  7212. }
  7213. }
  7214. return factory;
  7215. }
  7216. function makeRecord(factory, value, multi = false) {
  7217. return {
  7218. factory: factory,
  7219. value: value,
  7220. multi: multi ? [] : undefined,
  7221. };
  7222. }
  7223. function hasDeps(value) {
  7224. return !!value.deps;
  7225. }
  7226. function hasOnDestroy(value) {
  7227. return value !== null && typeof value === 'object' &&
  7228. typeof value.ngOnDestroy === 'function';
  7229. }
  7230. function couldBeInjectableType(value) {
  7231. return (typeof value === 'function') ||
  7232. (typeof value === 'object' && value instanceof InjectionToken);
  7233. }
  7234. function forEachSingleProvider(providers, fn) {
  7235. for (const provider of providers) {
  7236. if (Array.isArray(provider)) {
  7237. forEachSingleProvider(provider, fn);
  7238. }
  7239. else if (provider && isEnvironmentProviders(provider)) {
  7240. forEachSingleProvider(provider.ɵproviders, fn);
  7241. }
  7242. else {
  7243. fn(provider);
  7244. }
  7245. }
  7246. }
  7247. /**
  7248. * Runs the given function in the [context](guide/dependency-injection-context) of the given
  7249. * `Injector`.
  7250. *
  7251. * Within the function's stack frame, [`inject`](api/core/inject) can be used to inject dependencies
  7252. * from the given `Injector`. Note that `inject` is only usable synchronously, and cannot be used in
  7253. * any asynchronous callbacks or after any `await` points.
  7254. *
  7255. * @param injector the injector which will satisfy calls to [`inject`](api/core/inject) while `fn`
  7256. * is executing
  7257. * @param fn the closure to be run in the context of `injector`
  7258. * @returns the return value of the function, if any
  7259. * @publicApi
  7260. */
  7261. function runInInjectionContext(injector, fn) {
  7262. if (injector instanceof R3Injector) {
  7263. injector.assertNotDestroyed();
  7264. }
  7265. let prevInjectorProfilerContext;
  7266. if (ngDevMode) {
  7267. prevInjectorProfilerContext = setInjectorProfilerContext({ injector, token: null });
  7268. }
  7269. const prevInjector = setCurrentInjector(injector);
  7270. const previousInjectImplementation = setInjectImplementation(undefined);
  7271. try {
  7272. return fn();
  7273. }
  7274. finally {
  7275. setCurrentInjector(prevInjector);
  7276. ngDevMode && setInjectorProfilerContext(prevInjectorProfilerContext);
  7277. setInjectImplementation(previousInjectImplementation);
  7278. }
  7279. }
  7280. /**
  7281. * Asserts that the current stack frame is within an [injection
  7282. * context](guide/dependency-injection-context) and has access to `inject`.
  7283. *
  7284. * @param debugFn a reference to the function making the assertion (used for the error message).
  7285. *
  7286. * @publicApi
  7287. */
  7288. function assertInInjectionContext(debugFn) {
  7289. // Taking a `Function` instead of a string name here prevents the unminified name of the function
  7290. // from being retained in the bundle regardless of minification.
  7291. if (!getInjectImplementation() && !getCurrentInjector()) {
  7292. throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
  7293. (debugFn.name +
  7294. '() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`'));
  7295. }
  7296. }
  7297. /**
  7298. * A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
  7299. *
  7300. * This should be kept up to date with the public exports of @angular/core.
  7301. */
  7302. const angularCoreDiEnv = {
  7303. 'ɵɵdefineInjectable': ɵɵdefineInjectable,
  7304. 'ɵɵdefineInjector': ɵɵdefineInjector,
  7305. 'ɵɵinject': ɵɵinject,
  7306. 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep,
  7307. 'resolveForwardRef': resolveForwardRef,
  7308. };
  7309. /**
  7310. * Compile an Angular injectable according to its `Injectable` metadata, and patch the resulting
  7311. * injectable def (`ɵprov`) onto the injectable type.
  7312. */
  7313. function compileInjectable(type, meta) {
  7314. let ngInjectableDef = null;
  7315. let ngFactoryDef = null;
  7316. // if NG_PROV_DEF is already defined on this class then don't overwrite it
  7317. if (!type.hasOwnProperty(NG_PROV_DEF)) {
  7318. Object.defineProperty(type, NG_PROV_DEF, {
  7319. get: () => {
  7320. if (ngInjectableDef === null) {
  7321. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type });
  7322. ngInjectableDef = compiler.compileInjectable(angularCoreDiEnv, `ng:///${type.name}/ɵprov.js`, getInjectableMetadata(type, meta));
  7323. }
  7324. return ngInjectableDef;
  7325. },
  7326. });
  7327. }
  7328. // if NG_FACTORY_DEF is already defined on this class then don't overwrite it
  7329. if (!type.hasOwnProperty(NG_FACTORY_DEF)) {
  7330. Object.defineProperty(type, NG_FACTORY_DEF, {
  7331. get: () => {
  7332. if (ngFactoryDef === null) {
  7333. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type });
  7334. ngFactoryDef = compiler.compileFactory(angularCoreDiEnv, `ng:///${type.name}/ɵfac.js`, {
  7335. name: type.name,
  7336. type,
  7337. typeArgumentCount: 0,
  7338. deps: reflectDependencies(type),
  7339. target: compiler.FactoryTarget.Injectable
  7340. });
  7341. }
  7342. return ngFactoryDef;
  7343. },
  7344. // Leave this configurable so that the factories from directives or pipes can take precedence.
  7345. configurable: true
  7346. });
  7347. }
  7348. }
  7349. const USE_VALUE = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty });
  7350. function isUseClassProvider(meta) {
  7351. return meta.useClass !== undefined;
  7352. }
  7353. function isUseValueProvider(meta) {
  7354. return USE_VALUE in meta;
  7355. }
  7356. function isUseFactoryProvider(meta) {
  7357. return meta.useFactory !== undefined;
  7358. }
  7359. function isUseExistingProvider(meta) {
  7360. return meta.useExisting !== undefined;
  7361. }
  7362. function getInjectableMetadata(type, srcMeta) {
  7363. // Allow the compilation of a class with a `@Injectable()` decorator without parameters
  7364. const meta = srcMeta || { providedIn: null };
  7365. const compilerMeta = {
  7366. name: type.name,
  7367. type: type,
  7368. typeArgumentCount: 0,
  7369. providedIn: meta.providedIn,
  7370. };
  7371. if ((isUseClassProvider(meta) || isUseFactoryProvider(meta)) && meta.deps !== undefined) {
  7372. compilerMeta.deps = convertDependencies(meta.deps);
  7373. }
  7374. // Check to see if the user explicitly provided a `useXxxx` property.
  7375. if (isUseClassProvider(meta)) {
  7376. compilerMeta.useClass = meta.useClass;
  7377. }
  7378. else if (isUseValueProvider(meta)) {
  7379. compilerMeta.useValue = meta.useValue;
  7380. }
  7381. else if (isUseFactoryProvider(meta)) {
  7382. compilerMeta.useFactory = meta.useFactory;
  7383. }
  7384. else if (isUseExistingProvider(meta)) {
  7385. compilerMeta.useExisting = meta.useExisting;
  7386. }
  7387. return compilerMeta;
  7388. }
  7389. /**
  7390. * Injectable decorator and metadata.
  7391. *
  7392. * @Annotation
  7393. * @publicApi
  7394. */
  7395. const Injectable = makeDecorator('Injectable', undefined, undefined, undefined, (type, meta) => compileInjectable(type, meta));
  7396. /**
  7397. * Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s.
  7398. */
  7399. function createInjector(defType, parent = null, additionalProviders = null, name) {
  7400. const injector = createInjectorWithoutInjectorInstances(defType, parent, additionalProviders, name);
  7401. injector.resolveInjectorInitializers();
  7402. return injector;
  7403. }
  7404. /**
  7405. * Creates a new injector without eagerly resolving its injector types. Can be used in places
  7406. * where resolving the injector types immediately can lead to an infinite loop. The injector types
  7407. * should be resolved at a later point by calling `_resolveInjectorDefTypes`.
  7408. */
  7409. function createInjectorWithoutInjectorInstances(defType, parent = null, additionalProviders = null, name, scopes = new Set()) {
  7410. const providers = [
  7411. additionalProviders || EMPTY_ARRAY,
  7412. importProvidersFrom(defType),
  7413. ];
  7414. name = name || (typeof defType === 'object' ? undefined : stringify(defType));
  7415. return new R3Injector(providers, parent || getNullInjector(), name || null, scopes);
  7416. }
  7417. /**
  7418. * Concrete injectors implement this interface. Injectors are configured
  7419. * with [providers](guide/glossary#provider) that associate
  7420. * dependencies of various types with [injection tokens](guide/glossary#di-token).
  7421. *
  7422. * @see ["DI Providers"](guide/dependency-injection-providers).
  7423. * @see {@link StaticProvider}
  7424. *
  7425. * @usageNotes
  7426. *
  7427. * The following example creates a service injector instance.
  7428. *
  7429. * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
  7430. *
  7431. * ### Usage example
  7432. *
  7433. * {@example core/di/ts/injector_spec.ts region='Injector'}
  7434. *
  7435. * `Injector` returns itself when given `Injector` as a token:
  7436. *
  7437. * {@example core/di/ts/injector_spec.ts region='injectInjector'}
  7438. *
  7439. * @publicApi
  7440. */
  7441. class Injector {
  7442. static { this.THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND; }
  7443. static { this.NULL = ( /* @__PURE__ */new NullInjector()); }
  7444. static create(options, parent) {
  7445. if (Array.isArray(options)) {
  7446. return createInjector({ name: '' }, parent, options, '');
  7447. }
  7448. else {
  7449. const name = options.name ?? '';
  7450. return createInjector({ name }, options.parent, options.providers, name);
  7451. }
  7452. }
  7453. /** @nocollapse */
  7454. static { this.ɵprov = ɵɵdefineInjectable({
  7455. token: Injector,
  7456. providedIn: 'any',
  7457. factory: () => ɵɵinject(INJECTOR),
  7458. }); }
  7459. /**
  7460. * @internal
  7461. * @nocollapse
  7462. */
  7463. static { this.__NG_ELEMENT_ID__ = -1 /* InjectorMarkers.Injector */; }
  7464. }
  7465. /**
  7466. * @module
  7467. * @description
  7468. * The `di` module provides dependency injection container services.
  7469. */
  7470. /**
  7471. * This file should not be necessary because node resolution should just default to `./di/index`!
  7472. *
  7473. * However it does not seem to work and it breaks:
  7474. * - //packages/animations/browser/test:test_web_chromium-local
  7475. * - //packages/compiler-cli/test:extract_i18n
  7476. * - //packages/compiler-cli/test:ngc
  7477. * - //packages/compiler-cli/test:perform_watch
  7478. * - //packages/compiler-cli/test/diagnostics:check_types
  7479. * - //packages/compiler-cli/test/transformers:test
  7480. * - //packages/compiler/test:test
  7481. * - //tools/public_api_guard:core_api
  7482. *
  7483. * Remove this file once the above is solved or wait until `ngc` is deleted and then it should be
  7484. * safe to delete this file.
  7485. */
  7486. /**
  7487. *
  7488. * @codeGenApi
  7489. */
  7490. function ɵɵresolveWindow(element) {
  7491. return element.ownerDocument.defaultView;
  7492. }
  7493. /**
  7494. *
  7495. * @codeGenApi
  7496. */
  7497. function ɵɵresolveDocument(element) {
  7498. return element.ownerDocument;
  7499. }
  7500. /**
  7501. *
  7502. * @codeGenApi
  7503. */
  7504. function ɵɵresolveBody(element) {
  7505. return element.ownerDocument.body;
  7506. }
  7507. /**
  7508. * The special delimiter we use to separate property names, prefixes, and suffixes
  7509. * in property binding metadata. See storeBindingMetadata().
  7510. *
  7511. * We intentionally use the Unicode "REPLACEMENT CHARACTER" (U+FFFD) as a delimiter
  7512. * because it is a very uncommon character that is unlikely to be part of a user's
  7513. * property names or interpolation strings. If it is in fact used in a property
  7514. * binding, DebugElement.properties will not return the correct value for that
  7515. * binding. However, there should be no runtime effect for real applications.
  7516. *
  7517. * This character is typically rendered as a question mark inside of a diamond.
  7518. * See https://en.wikipedia.org/wiki/Specials_(Unicode_block)
  7519. *
  7520. */
  7521. const INTERPOLATION_DELIMITER = `�`;
  7522. /**
  7523. * Unwrap a value which might be behind a closure (for forward declaration reasons).
  7524. */
  7525. function maybeUnwrapFn$1(value) {
  7526. if (value instanceof Function) {
  7527. return value();
  7528. }
  7529. else {
  7530. return value;
  7531. }
  7532. }
  7533. /**
  7534. * Detects whether the code is invoked in a browser.
  7535. * Later on, this check should be replaced with a tree-shakable
  7536. * flag (e.g. `!isServer`).
  7537. */
  7538. function isPlatformBrowser(injector) {
  7539. return (injector ?? inject$1(Injector)).get(PLATFORM_ID) === 'browser';
  7540. }
  7541. /**
  7542. * Defines a schema that allows an NgModule to contain the following:
  7543. * - Non-Angular elements named with dash case (`-`).
  7544. * - Element properties named with dash case (`-`).
  7545. * Dash case is the naming convention for custom elements.
  7546. *
  7547. * @publicApi
  7548. */
  7549. const CUSTOM_ELEMENTS_SCHEMA = {
  7550. name: 'custom-elements'
  7551. };
  7552. /**
  7553. * Defines a schema that allows any property on any element.
  7554. *
  7555. * This schema allows you to ignore the errors related to any unknown elements or properties in a
  7556. * template. The usage of this schema is generally discouraged because it prevents useful validation
  7557. * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
  7558. *
  7559. * @publicApi
  7560. */
  7561. const NO_ERRORS_SCHEMA = {
  7562. name: 'no-errors-schema'
  7563. };
  7564. let shouldThrowErrorOnUnknownElement = false;
  7565. /**
  7566. * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
  7567. * instead of just logging the error.
  7568. * (for AOT-compiled ones this check happens at build time).
  7569. */
  7570. function ɵsetUnknownElementStrictMode(shouldThrow) {
  7571. shouldThrowErrorOnUnknownElement = shouldThrow;
  7572. }
  7573. /**
  7574. * Gets the current value of the strict mode.
  7575. */
  7576. function ɵgetUnknownElementStrictMode() {
  7577. return shouldThrowErrorOnUnknownElement;
  7578. }
  7579. let shouldThrowErrorOnUnknownProperty = false;
  7580. /**
  7581. * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
  7582. * instead of just logging the error.
  7583. * (for AOT-compiled ones this check happens at build time).
  7584. */
  7585. function ɵsetUnknownPropertyStrictMode(shouldThrow) {
  7586. shouldThrowErrorOnUnknownProperty = shouldThrow;
  7587. }
  7588. /**
  7589. * Gets the current value of the strict mode.
  7590. */
  7591. function ɵgetUnknownPropertyStrictMode() {
  7592. return shouldThrowErrorOnUnknownProperty;
  7593. }
  7594. /**
  7595. * Validates that the element is known at runtime and produces
  7596. * an error if it's not the case.
  7597. * This check is relevant for JIT-compiled components (for AOT-compiled
  7598. * ones this check happens at build time).
  7599. *
  7600. * The element is considered known if either:
  7601. * - it's a known HTML element
  7602. * - it's a known custom element
  7603. * - the element matches any directive
  7604. * - the element is allowed by one of the schemas
  7605. *
  7606. * @param element Element to validate
  7607. * @param lView An `LView` that represents a current component that is being rendered
  7608. * @param tagName Name of the tag to check
  7609. * @param schemas Array of schemas
  7610. * @param hasDirectives Boolean indicating that the element matches any directive
  7611. */
  7612. function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
  7613. // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
  7614. // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
  7615. // defined as an array (as an empty array in case `schemas` field is not defined) and we should
  7616. // execute the check below.
  7617. if (schemas === null)
  7618. return;
  7619. // If the element matches any directive, it's considered as valid.
  7620. if (!hasDirectives && tagName !== null) {
  7621. // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
  7622. // as a custom element. Note that unknown elements with a dash in their name won't be instances
  7623. // of HTMLUnknownElement in browsers that support web components.
  7624. const isUnknown =
  7625. // Note that we can't check for `typeof HTMLUnknownElement === 'function'` because
  7626. // Domino doesn't expose HTMLUnknownElement globally.
  7627. (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
  7628. element instanceof HTMLUnknownElement) ||
  7629. (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
  7630. !customElements.get(tagName));
  7631. if (isUnknown && !matchingSchemas(schemas, tagName)) {
  7632. const isHostStandalone = isHostComponentStandalone(lView);
  7633. const templateLocation = getTemplateLocationDetails(lView);
  7634. const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
  7635. let message = `'${tagName}' is not a known element${templateLocation}:\n`;
  7636. message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
  7637. 'a part of an @NgModule where this component is declared'}.\n`;
  7638. if (tagName && tagName.indexOf('-') > -1) {
  7639. message +=
  7640. `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
  7641. }
  7642. else {
  7643. message +=
  7644. `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
  7645. }
  7646. if (shouldThrowErrorOnUnknownElement) {
  7647. throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
  7648. }
  7649. else {
  7650. console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
  7651. }
  7652. }
  7653. }
  7654. }
  7655. /**
  7656. * Validates that the property of the element is known at runtime and returns
  7657. * false if it's not the case.
  7658. * This check is relevant for JIT-compiled components (for AOT-compiled
  7659. * ones this check happens at build time).
  7660. *
  7661. * The property is considered known if either:
  7662. * - it's a known property of the element
  7663. * - the element is allowed by one of the schemas
  7664. * - the property is used for animations
  7665. *
  7666. * @param element Element to validate
  7667. * @param propName Name of the property to check
  7668. * @param tagName Name of the tag hosting the property
  7669. * @param schemas Array of schemas
  7670. */
  7671. function isPropertyValid(element, propName, tagName, schemas) {
  7672. // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
  7673. // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
  7674. // defined as an array (as an empty array in case `schemas` field is not defined) and we should
  7675. // execute the check below.
  7676. if (schemas === null)
  7677. return true;
  7678. // The property is considered valid if the element matches the schema, it exists on the element,
  7679. // or it is synthetic.
  7680. if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
  7681. return true;
  7682. }
  7683. // Note: `typeof Node` returns 'function' in most browsers, but is undefined with domino.
  7684. return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
  7685. }
  7686. /**
  7687. * Logs or throws an error that a property is not supported on an element.
  7688. *
  7689. * @param propName Name of the invalid property
  7690. * @param tagName Name of the tag hosting the property
  7691. * @param nodeType Type of the node hosting the property
  7692. * @param lView An `LView` that represents a current component
  7693. */
  7694. function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
  7695. // Special-case a situation when a structural directive is applied to
  7696. // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
  7697. // In this case the compiler generates the `ɵɵtemplate` instruction with
  7698. // the `null` as the tagName. The directive matching logic at runtime relies
  7699. // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
  7700. // a default value of the `tNode.value` is not feasible at this moment.
  7701. if (!tagName && nodeType === 4 /* TNodeType.Container */) {
  7702. tagName = 'ng-template';
  7703. }
  7704. const isHostStandalone = isHostComponentStandalone(lView);
  7705. const templateLocation = getTemplateLocationDetails(lView);
  7706. let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
  7707. const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
  7708. const importLocation = isHostStandalone ?
  7709. 'included in the \'@Component.imports\' of this component' :
  7710. 'a part of an @NgModule where this component is declared';
  7711. if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
  7712. // Most likely this is a control flow directive (such as `*ngIf`) used in
  7713. // a template, but the directive or the `CommonModule` is not imported.
  7714. const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
  7715. message += `\nIf the '${propName}' is an Angular control flow directive, ` +
  7716. `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
  7717. }
  7718. else {
  7719. // May be an Angular component, which is not imported/declared?
  7720. message += `\n1. If '${tagName}' is an Angular component and it has the ` +
  7721. `'${propName}' input, then verify that it is ${importLocation}.`;
  7722. // May be a Web Component?
  7723. if (tagName && tagName.indexOf('-') > -1) {
  7724. message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
  7725. `to the ${schemas} of this component to suppress this message.`;
  7726. message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
  7727. `the ${schemas} of this component.`;
  7728. }
  7729. else {
  7730. // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
  7731. message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
  7732. `the ${schemas} of this component.`;
  7733. }
  7734. }
  7735. reportUnknownPropertyError(message);
  7736. }
  7737. function reportUnknownPropertyError(message) {
  7738. if (shouldThrowErrorOnUnknownProperty) {
  7739. throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
  7740. }
  7741. else {
  7742. console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
  7743. }
  7744. }
  7745. /**
  7746. * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
  7747. * and must **not** be used in production bundles. The function makes megamorphic reads, which might
  7748. * be too slow for production mode and also it relies on the constructor function being available.
  7749. *
  7750. * Gets a reference to the host component def (where a current component is declared).
  7751. *
  7752. * @param lView An `LView` that represents a current component that is being rendered.
  7753. */
  7754. function getDeclarationComponentDef(lView) {
  7755. !ngDevMode && throwError('Must never be called in production mode');
  7756. const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
  7757. const context = declarationLView[CONTEXT];
  7758. // Unable to obtain a context.
  7759. if (!context)
  7760. return null;
  7761. return context.constructor ? getComponentDef$1(context.constructor) : null;
  7762. }
  7763. /**
  7764. * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
  7765. * and must **not** be used in production bundles. The function makes megamorphic reads, which might
  7766. * be too slow for production mode.
  7767. *
  7768. * Checks if the current component is declared inside of a standalone component template.
  7769. *
  7770. * @param lView An `LView` that represents a current component that is being rendered.
  7771. */
  7772. function isHostComponentStandalone(lView) {
  7773. !ngDevMode && throwError('Must never be called in production mode');
  7774. const componentDef = getDeclarationComponentDef(lView);
  7775. // Treat host component as non-standalone if we can't obtain the def.
  7776. return !!componentDef?.standalone;
  7777. }
  7778. /**
  7779. * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
  7780. * and must **not** be used in production bundles. The function makes megamorphic reads, which might
  7781. * be too slow for production mode.
  7782. *
  7783. * Constructs a string describing the location of the host component template. The function is used
  7784. * in dev mode to produce error messages.
  7785. *
  7786. * @param lView An `LView` that represents a current component that is being rendered.
  7787. */
  7788. function getTemplateLocationDetails(lView) {
  7789. !ngDevMode && throwError('Must never be called in production mode');
  7790. const hostComponentDef = getDeclarationComponentDef(lView);
  7791. const componentClassName = hostComponentDef?.type?.name;
  7792. return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
  7793. }
  7794. /**
  7795. * The set of known control flow directives and their corresponding imports.
  7796. * We use this set to produce a more precises error message with a note
  7797. * that the `CommonModule` should also be included.
  7798. */
  7799. const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
  7800. ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
  7801. ['ngSwitchDefault', 'NgSwitchDefault']
  7802. ]);
  7803. /**
  7804. * Returns true if the tag name is allowed by specified schemas.
  7805. * @param schemas Array of schemas
  7806. * @param tagName Name of the tag
  7807. */
  7808. function matchingSchemas(schemas, tagName) {
  7809. if (schemas !== null) {
  7810. for (let i = 0; i < schemas.length; i++) {
  7811. const schema = schemas[i];
  7812. if (schema === NO_ERRORS_SCHEMA ||
  7813. schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
  7814. return true;
  7815. }
  7816. }
  7817. }
  7818. return false;
  7819. }
  7820. /**
  7821. * The name of an attribute that can be added to the hydration boundary node
  7822. * (component host node) to disable hydration for the content within that boundary.
  7823. */
  7824. const SKIP_HYDRATION_ATTR_NAME = 'ngSkipHydration';
  7825. /**
  7826. * Helper function to check if a given TNode has the 'ngSkipHydration' attribute.
  7827. */
  7828. function hasSkipHydrationAttrOnTNode(tNode) {
  7829. const SKIP_HYDRATION_ATTR_NAME_LOWER_CASE = SKIP_HYDRATION_ATTR_NAME.toLowerCase();
  7830. const attrs = tNode.mergedAttrs;
  7831. if (attrs === null)
  7832. return false;
  7833. // only ever look at the attribute name and skip the values
  7834. for (let i = 0; i < attrs.length; i += 2) {
  7835. const value = attrs[i];
  7836. // This is a marker, which means that the static attributes section is over,
  7837. // so we can exit early.
  7838. if (typeof value === 'number')
  7839. return false;
  7840. if (typeof value === 'string' && value.toLowerCase() === SKIP_HYDRATION_ATTR_NAME_LOWER_CASE) {
  7841. return true;
  7842. }
  7843. }
  7844. return false;
  7845. }
  7846. /**
  7847. * Helper function to check if a given RElement has the 'ngSkipHydration' attribute.
  7848. */
  7849. function hasSkipHydrationAttrOnRElement(rNode) {
  7850. return rNode.hasAttribute(SKIP_HYDRATION_ATTR_NAME);
  7851. }
  7852. /**
  7853. * Checks whether a TNode has a flag to indicate that it's a part of
  7854. * a skip hydration block.
  7855. */
  7856. function hasInSkipHydrationBlockFlag(tNode) {
  7857. return (tNode.flags & 128 /* TNodeFlags.inSkipHydrationBlock */) === 128 /* TNodeFlags.inSkipHydrationBlock */;
  7858. }
  7859. /**
  7860. * Helper function that determines if a given node is within a skip hydration block
  7861. * by navigating up the TNode tree to see if any parent nodes have skip hydration
  7862. * attribute.
  7863. *
  7864. * TODO(akushnir): this function should contain the logic of `hasInSkipHydrationBlockFlag`,
  7865. * there is no need to traverse parent nodes when we have a TNode flag (which would also
  7866. * make this lookup O(1)).
  7867. */
  7868. function isInSkipHydrationBlock(tNode) {
  7869. let currentTNode = tNode.parent;
  7870. while (currentTNode) {
  7871. if (hasSkipHydrationAttrOnTNode(currentTNode)) {
  7872. return true;
  7873. }
  7874. currentTNode = currentTNode.parent;
  7875. }
  7876. return false;
  7877. }
  7878. /**
  7879. * Flags for renderer-specific style modifiers.
  7880. * @publicApi
  7881. */
  7882. var RendererStyleFlags2;
  7883. (function (RendererStyleFlags2) {
  7884. // TODO(misko): This needs to be refactored into a separate file so that it can be imported from
  7885. // `node_manipulation.ts` Currently doing the import cause resolution order to change and fails
  7886. // the tests. The work around is to have hard coded value in `node_manipulation.ts` for now.
  7887. /**
  7888. * Marks a style as important.
  7889. */
  7890. RendererStyleFlags2[RendererStyleFlags2["Important"] = 1] = "Important";
  7891. /**
  7892. * Marks a style as using dash case naming (this-is-dash-case).
  7893. */
  7894. RendererStyleFlags2[RendererStyleFlags2["DashCase"] = 2] = "DashCase";
  7895. })(RendererStyleFlags2 || (RendererStyleFlags2 = {}));
  7896. /**
  7897. * Disallowed strings in the comment.
  7898. *
  7899. * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
  7900. */
  7901. const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
  7902. /**
  7903. * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
  7904. */
  7905. const COMMENT_DELIMITER = /(<|>)/g;
  7906. const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
  7907. /**
  7908. * Escape the content of comment strings so that it can be safely inserted into a comment node.
  7909. *
  7910. * The issue is that HTML does not specify any way to escape comment end text inside the comment.
  7911. * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
  7912. * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
  7913. * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
  7914. *
  7915. * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
  7916. *
  7917. * ```
  7918. * div.innerHTML = div.innerHTML
  7919. * ```
  7920. *
  7921. * One would expect that the above code would be safe to do, but it turns out that because comment
  7922. * text is not escaped, the comment may contain text which will prematurely close the comment
  7923. * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
  7924. * may contain such text and expect them to be safe.)
  7925. *
  7926. * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
  7927. * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
  7928. * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
  7929. * text it will render normally but it will not cause the HTML parser to close/open the comment.
  7930. *
  7931. * @param value text to make safe for comment node by escaping the comment open/close character
  7932. * sequence.
  7933. */
  7934. function escapeCommentText(value) {
  7935. return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
  7936. }
  7937. // Keeps track of the currently-active LViews.
  7938. const TRACKED_LVIEWS = new Map();
  7939. // Used for generating unique IDs for LViews.
  7940. let uniqueIdCounter = 0;
  7941. /** Gets a unique ID that can be assigned to an LView. */
  7942. function getUniqueLViewId() {
  7943. return uniqueIdCounter++;
  7944. }
  7945. /** Starts tracking an LView. */
  7946. function registerLView(lView) {
  7947. ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
  7948. TRACKED_LVIEWS.set(lView[ID], lView);
  7949. }
  7950. /** Gets an LView by its unique ID. */
  7951. function getLViewById(id) {
  7952. ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
  7953. return TRACKED_LVIEWS.get(id) || null;
  7954. }
  7955. /** Stops tracking an LView. */
  7956. function unregisterLView(lView) {
  7957. ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
  7958. TRACKED_LVIEWS.delete(lView[ID]);
  7959. }
  7960. /**
  7961. * The internal view context which is specific to a given DOM element, directive or
  7962. * component instance. Each value in here (besides the LView and element node details)
  7963. * can be present, null or undefined. If undefined then it implies the value has not been
  7964. * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
  7965. *
  7966. * Each value will get filled when the respective value is examined within the getContext
  7967. * function. The component, element and each directive instance will share the same instance
  7968. * of the context.
  7969. */
  7970. class LContext {
  7971. /** Component's parent view data. */
  7972. get lView() {
  7973. return getLViewById(this.lViewId);
  7974. }
  7975. constructor(
  7976. /**
  7977. * ID of the component's parent view data.
  7978. */
  7979. lViewId,
  7980. /**
  7981. * The index instance of the node.
  7982. */
  7983. nodeIndex,
  7984. /**
  7985. * The instance of the DOM node that is attached to the lNode.
  7986. */
  7987. native) {
  7988. this.lViewId = lViewId;
  7989. this.nodeIndex = nodeIndex;
  7990. this.native = native;
  7991. }
  7992. }
  7993. /**
  7994. * Returns the matching `LContext` data for a given DOM node, directive or component instance.
  7995. *
  7996. * This function will examine the provided DOM element, component, or directive instance\'s
  7997. * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
  7998. * value will be that of the newly created `LContext`.
  7999. *
  8000. * If the monkey-patched value is the `LView` instance then the context value for that
  8001. * target will be created and the monkey-patch reference will be updated. Therefore when this
  8002. * function is called it may mutate the provided element\'s, component\'s or any of the associated
  8003. * directive\'s monkey-patch values.
  8004. *
  8005. * If the monkey-patch value is not detected then the code will walk up the DOM until an element
  8006. * is found which contains a monkey-patch reference. When that occurs then the provided element
  8007. * will be updated with a new context (which is then returned). If the monkey-patch value is not
  8008. * detected for a component/directive instance then it will throw an error (all components and
  8009. * directives should be automatically monkey-patched by ivy).
  8010. *
  8011. * @param target Component, Directive or DOM Node.
  8012. */
  8013. function getLContext(target) {
  8014. let mpValue = readPatchedData(target);
  8015. if (mpValue) {
  8016. // only when it's an array is it considered an LView instance
  8017. // ... otherwise it's an already constructed LContext instance
  8018. if (isLView(mpValue)) {
  8019. const lView = mpValue;
  8020. let nodeIndex;
  8021. let component = undefined;
  8022. let directives = undefined;
  8023. if (isComponentInstance(target)) {
  8024. nodeIndex = findViaComponent(lView, target);
  8025. if (nodeIndex == -1) {
  8026. throw new Error('The provided component was not found in the application');
  8027. }
  8028. component = target;
  8029. }
  8030. else if (isDirectiveInstance(target)) {
  8031. nodeIndex = findViaDirective(lView, target);
  8032. if (nodeIndex == -1) {
  8033. throw new Error('The provided directive was not found in the application');
  8034. }
  8035. directives = getDirectivesAtNodeIndex(nodeIndex, lView);
  8036. }
  8037. else {
  8038. nodeIndex = findViaNativeElement(lView, target);
  8039. if (nodeIndex == -1) {
  8040. return null;
  8041. }
  8042. }
  8043. // the goal is not to fill the entire context full of data because the lookups
  8044. // are expensive. Instead, only the target data (the element, component, container, ICU
  8045. // expression or directive details) are filled into the context. If called multiple times
  8046. // with different target values then the missing target data will be filled in.
  8047. const native = unwrapRNode(lView[nodeIndex]);
  8048. const existingCtx = readPatchedData(native);
  8049. const context = (existingCtx && !Array.isArray(existingCtx)) ?
  8050. existingCtx :
  8051. createLContext(lView, nodeIndex, native);
  8052. // only when the component has been discovered then update the monkey-patch
  8053. if (component && context.component === undefined) {
  8054. context.component = component;
  8055. attachPatchData(context.component, context);
  8056. }
  8057. // only when the directives have been discovered then update the monkey-patch
  8058. if (directives && context.directives === undefined) {
  8059. context.directives = directives;
  8060. for (let i = 0; i < directives.length; i++) {
  8061. attachPatchData(directives[i], context);
  8062. }
  8063. }
  8064. attachPatchData(context.native, context);
  8065. mpValue = context;
  8066. }
  8067. }
  8068. else {
  8069. const rElement = target;
  8070. ngDevMode && assertDomNode(rElement);
  8071. // if the context is not found then we need to traverse upwards up the DOM
  8072. // to find the nearest element that has already been monkey patched with data
  8073. let parent = rElement;
  8074. while (parent = parent.parentNode) {
  8075. const parentContext = readPatchedData(parent);
  8076. if (parentContext) {
  8077. const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
  8078. // the edge of the app was also reached here through another means
  8079. // (maybe because the DOM was changed manually).
  8080. if (!lView) {
  8081. return null;
  8082. }
  8083. const index = findViaNativeElement(lView, rElement);
  8084. if (index >= 0) {
  8085. const native = unwrapRNode(lView[index]);
  8086. const context = createLContext(lView, index, native);
  8087. attachPatchData(native, context);
  8088. mpValue = context;
  8089. break;
  8090. }
  8091. }
  8092. }
  8093. }
  8094. return mpValue || null;
  8095. }
  8096. /**
  8097. * Creates an empty instance of a `LContext` context
  8098. */
  8099. function createLContext(lView, nodeIndex, native) {
  8100. return new LContext(lView[ID], nodeIndex, native);
  8101. }
  8102. /**
  8103. * Takes a component instance and returns the view for that component.
  8104. *
  8105. * @param componentInstance
  8106. * @returns The component's view
  8107. */
  8108. function getComponentViewByInstance(componentInstance) {
  8109. let patchedData = readPatchedData(componentInstance);
  8110. let lView;
  8111. if (isLView(patchedData)) {
  8112. const contextLView = patchedData;
  8113. const nodeIndex = findViaComponent(contextLView, componentInstance);
  8114. lView = getComponentLViewByIndex(nodeIndex, contextLView);
  8115. const context = createLContext(contextLView, nodeIndex, lView[HOST]);
  8116. context.component = componentInstance;
  8117. attachPatchData(componentInstance, context);
  8118. attachPatchData(context.native, context);
  8119. }
  8120. else {
  8121. const context = patchedData;
  8122. const contextLView = context.lView;
  8123. ngDevMode && assertLView(contextLView);
  8124. lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
  8125. }
  8126. return lView;
  8127. }
  8128. /**
  8129. * This property will be monkey-patched on elements, components and directives.
  8130. */
  8131. const MONKEY_PATCH_KEY_NAME = '__ngContext__';
  8132. /**
  8133. * Assigns the given data to the given target (which could be a component,
  8134. * directive or DOM node instance) using monkey-patching.
  8135. */
  8136. function attachPatchData(target, data) {
  8137. ngDevMode && assertDefined(target, 'Target expected');
  8138. // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
  8139. // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
  8140. // we can't know when to remove an `LContext`.
  8141. if (isLView(data)) {
  8142. target[MONKEY_PATCH_KEY_NAME] = data[ID];
  8143. registerLView(data);
  8144. }
  8145. else {
  8146. target[MONKEY_PATCH_KEY_NAME] = data;
  8147. }
  8148. }
  8149. /**
  8150. * Returns the monkey-patch value data present on the target (which could be
  8151. * a component, directive or a DOM node).
  8152. */
  8153. function readPatchedData(target) {
  8154. ngDevMode && assertDefined(target, 'Target expected');
  8155. const data = target[MONKEY_PATCH_KEY_NAME];
  8156. return (typeof data === 'number') ? getLViewById(data) : data || null;
  8157. }
  8158. function readPatchedLView(target) {
  8159. const value = readPatchedData(target);
  8160. if (value) {
  8161. return (isLView(value) ? value : value.lView);
  8162. }
  8163. return null;
  8164. }
  8165. function isComponentInstance(instance) {
  8166. return instance && instance.constructor && instance.constructor.ɵcmp;
  8167. }
  8168. function isDirectiveInstance(instance) {
  8169. return instance && instance.constructor && instance.constructor.ɵdir;
  8170. }
  8171. /**
  8172. * Locates the element within the given LView and returns the matching index
  8173. */
  8174. function findViaNativeElement(lView, target) {
  8175. const tView = lView[TVIEW];
  8176. for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
  8177. if (unwrapRNode(lView[i]) === target) {
  8178. return i;
  8179. }
  8180. }
  8181. return -1;
  8182. }
  8183. /**
  8184. * Locates the next tNode (child, sibling or parent).
  8185. */
  8186. function traverseNextElement(tNode) {
  8187. if (tNode.child) {
  8188. return tNode.child;
  8189. }
  8190. else if (tNode.next) {
  8191. return tNode.next;
  8192. }
  8193. else {
  8194. // Let's take the following template: <div><span>text</span></div><component/>
  8195. // After checking the text node, we need to find the next parent that has a "next" TNode,
  8196. // in this case the parent `div`, so that we can find the component.
  8197. while (tNode.parent && !tNode.parent.next) {
  8198. tNode = tNode.parent;
  8199. }
  8200. return tNode.parent && tNode.parent.next;
  8201. }
  8202. }
  8203. /**
  8204. * Locates the component within the given LView and returns the matching index
  8205. */
  8206. function findViaComponent(lView, componentInstance) {
  8207. const componentIndices = lView[TVIEW].components;
  8208. if (componentIndices) {
  8209. for (let i = 0; i < componentIndices.length; i++) {
  8210. const elementComponentIndex = componentIndices[i];
  8211. const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
  8212. if (componentView[CONTEXT] === componentInstance) {
  8213. return elementComponentIndex;
  8214. }
  8215. }
  8216. }
  8217. else {
  8218. const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
  8219. const rootComponent = rootComponentView[CONTEXT];
  8220. if (rootComponent === componentInstance) {
  8221. // we are dealing with the root element here therefore we know that the
  8222. // element is the very first element after the HEADER data in the lView
  8223. return HEADER_OFFSET;
  8224. }
  8225. }
  8226. return -1;
  8227. }
  8228. /**
  8229. * Locates the directive within the given LView and returns the matching index
  8230. */
  8231. function findViaDirective(lView, directiveInstance) {
  8232. // if a directive is monkey patched then it will (by default)
  8233. // have a reference to the LView of the current view. The
  8234. // element bound to the directive being search lives somewhere
  8235. // in the view data. We loop through the nodes and check their
  8236. // list of directives for the instance.
  8237. let tNode = lView[TVIEW].firstChild;
  8238. while (tNode) {
  8239. const directiveIndexStart = tNode.directiveStart;
  8240. const directiveIndexEnd = tNode.directiveEnd;
  8241. for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
  8242. if (lView[i] === directiveInstance) {
  8243. return tNode.index;
  8244. }
  8245. }
  8246. tNode = traverseNextElement(tNode);
  8247. }
  8248. return -1;
  8249. }
  8250. /**
  8251. * Returns a list of directives applied to a node at a specific index. The list includes
  8252. * directives matched by selector and any host directives, but it excludes components.
  8253. * Use `getComponentAtNodeIndex` to find the component applied to a node.
  8254. *
  8255. * @param nodeIndex The node index
  8256. * @param lView The target view data
  8257. */
  8258. function getDirectivesAtNodeIndex(nodeIndex, lView) {
  8259. const tNode = lView[TVIEW].data[nodeIndex];
  8260. if (tNode.directiveStart === 0)
  8261. return EMPTY_ARRAY;
  8262. const results = [];
  8263. for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
  8264. const directiveInstance = lView[i];
  8265. if (!isComponentInstance(directiveInstance)) {
  8266. results.push(directiveInstance);
  8267. }
  8268. }
  8269. return results;
  8270. }
  8271. function getComponentAtNodeIndex(nodeIndex, lView) {
  8272. const tNode = lView[TVIEW].data[nodeIndex];
  8273. const { directiveStart, componentOffset } = tNode;
  8274. return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
  8275. }
  8276. /**
  8277. * Returns a map of local references (local reference name => element or directive instance) that
  8278. * exist on a given element.
  8279. */
  8280. function discoverLocalRefs(lView, nodeIndex) {
  8281. const tNode = lView[TVIEW].data[nodeIndex];
  8282. if (tNode && tNode.localNames) {
  8283. const result = {};
  8284. let localIndex = tNode.index + 1;
  8285. for (let i = 0; i < tNode.localNames.length; i += 2) {
  8286. result[tNode.localNames[i]] = lView[localIndex];
  8287. localIndex++;
  8288. }
  8289. return result;
  8290. }
  8291. return null;
  8292. }
  8293. let _icuContainerIterate;
  8294. /**
  8295. * Iterator which provides ability to visit all of the `TIcuContainerNode` root `RNode`s.
  8296. */
  8297. function icuContainerIterate(tIcuContainerNode, lView) {
  8298. return _icuContainerIterate(tIcuContainerNode, lView);
  8299. }
  8300. /**
  8301. * Ensures that `IcuContainerVisitor`'s implementation is present.
  8302. *
  8303. * This function is invoked when i18n instruction comes across an ICU. The purpose is to allow the
  8304. * bundler to tree shake ICU logic and only load it if ICU instruction is executed.
  8305. */
  8306. function ensureIcuContainerVisitorLoaded(loader) {
  8307. if (_icuContainerIterate === undefined) {
  8308. // Do not inline this function. We want to keep `ensureIcuContainerVisitorLoaded` light, so it
  8309. // can be inlined into call-site.
  8310. _icuContainerIterate = loader();
  8311. }
  8312. }
  8313. /**
  8314. * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of
  8315. * that LContainer, which is an LView
  8316. * @param lView the lView whose parent to get
  8317. */
  8318. function getLViewParent(lView) {
  8319. ngDevMode && assertLView(lView);
  8320. const parent = lView[PARENT];
  8321. return isLContainer(parent) ? parent[PARENT] : parent;
  8322. }
  8323. /**
  8324. * Retrieve the root view from any component or `LView` by walking the parent `LView` until
  8325. * reaching the root `LView`.
  8326. *
  8327. * @param componentOrLView any component or `LView`
  8328. */
  8329. function getRootView(componentOrLView) {
  8330. ngDevMode && assertDefined(componentOrLView, 'component');
  8331. let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView);
  8332. while (lView && !(lView[FLAGS] & 512 /* LViewFlags.IsRoot */)) {
  8333. lView = getLViewParent(lView);
  8334. }
  8335. ngDevMode && assertLView(lView);
  8336. return lView;
  8337. }
  8338. /**
  8339. * Returns the context information associated with the application where the target is situated. It
  8340. * does this by walking the parent views until it gets to the root view, then getting the context
  8341. * off of that.
  8342. *
  8343. * @param viewOrComponent the `LView` or component to get the root context for.
  8344. */
  8345. function getRootContext(viewOrComponent) {
  8346. const rootView = getRootView(viewOrComponent);
  8347. ngDevMode &&
  8348. assertDefined(rootView[CONTEXT], 'Root view has no context. Perhaps it is disconnected?');
  8349. return rootView[CONTEXT];
  8350. }
  8351. /**
  8352. * Gets the first `LContainer` in the LView or `null` if none exists.
  8353. */
  8354. function getFirstLContainer(lView) {
  8355. return getNearestLContainer(lView[CHILD_HEAD]);
  8356. }
  8357. /**
  8358. * Gets the next `LContainer` that is a sibling of the given container.
  8359. */
  8360. function getNextLContainer(container) {
  8361. return getNearestLContainer(container[NEXT]);
  8362. }
  8363. function getNearestLContainer(viewOrContainer) {
  8364. while (viewOrContainer !== null && !isLContainer(viewOrContainer)) {
  8365. viewOrContainer = viewOrContainer[NEXT];
  8366. }
  8367. return viewOrContainer;
  8368. }
  8369. /**
  8370. * NOTE: for performance reasons, the possible actions are inlined within the function instead of
  8371. * being passed as an argument.
  8372. */
  8373. function applyToElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) {
  8374. // If this slot was allocated for a text node dynamically created by i18n, the text node itself
  8375. // won't be created until i18nApply() in the update block, so this node should be skipped.
  8376. // For more info, see "ICU expressions should work inside an ngTemplateOutlet inside an ngFor"
  8377. // in `i18n_spec.ts`.
  8378. if (lNodeToHandle != null) {
  8379. let lContainer;
  8380. let isComponent = false;
  8381. // We are expecting an RNode, but in the case of a component or LContainer the `RNode` is
  8382. // wrapped in an array which needs to be unwrapped. We need to know if it is a component and if
  8383. // it has LContainer so that we can process all of those cases appropriately.
  8384. if (isLContainer(lNodeToHandle)) {
  8385. lContainer = lNodeToHandle;
  8386. }
  8387. else if (isLView(lNodeToHandle)) {
  8388. isComponent = true;
  8389. ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView');
  8390. lNodeToHandle = lNodeToHandle[HOST];
  8391. }
  8392. const rNode = unwrapRNode(lNodeToHandle);
  8393. if (action === 0 /* WalkTNodeTreeAction.Create */ && parent !== null) {
  8394. if (beforeNode == null) {
  8395. nativeAppendChild(renderer, parent, rNode);
  8396. }
  8397. else {
  8398. nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
  8399. }
  8400. }
  8401. else if (action === 1 /* WalkTNodeTreeAction.Insert */ && parent !== null) {
  8402. nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
  8403. }
  8404. else if (action === 2 /* WalkTNodeTreeAction.Detach */) {
  8405. nativeRemoveNode(renderer, rNode, isComponent);
  8406. }
  8407. else if (action === 3 /* WalkTNodeTreeAction.Destroy */) {
  8408. ngDevMode && ngDevMode.rendererDestroyNode++;
  8409. renderer.destroyNode(rNode);
  8410. }
  8411. if (lContainer != null) {
  8412. applyContainer(renderer, action, lContainer, parent, beforeNode);
  8413. }
  8414. }
  8415. }
  8416. function createTextNode(renderer, value) {
  8417. ngDevMode && ngDevMode.rendererCreateTextNode++;
  8418. ngDevMode && ngDevMode.rendererSetText++;
  8419. return renderer.createText(value);
  8420. }
  8421. function updateTextNode(renderer, rNode, value) {
  8422. ngDevMode && ngDevMode.rendererSetText++;
  8423. renderer.setValue(rNode, value);
  8424. }
  8425. function createCommentNode(renderer, value) {
  8426. ngDevMode && ngDevMode.rendererCreateComment++;
  8427. return renderer.createComment(escapeCommentText(value));
  8428. }
  8429. /**
  8430. * Creates a native element from a tag name, using a renderer.
  8431. * @param renderer A renderer to use
  8432. * @param name the tag name
  8433. * @param namespace Optional namespace for element.
  8434. * @returns the element created
  8435. */
  8436. function createElementNode(renderer, name, namespace) {
  8437. ngDevMode && ngDevMode.rendererCreateElement++;
  8438. return renderer.createElement(name, namespace);
  8439. }
  8440. /**
  8441. * Removes all DOM elements associated with a view.
  8442. *
  8443. * Because some root nodes of the view may be containers, we sometimes need
  8444. * to propagate deeply into the nested containers to remove all elements in the
  8445. * views beneath it.
  8446. *
  8447. * @param tView The `TView' of the `LView` from which elements should be added or removed
  8448. * @param lView The view from which elements should be added or removed
  8449. */
  8450. function removeViewFromDOM(tView, lView) {
  8451. const renderer = lView[RENDERER];
  8452. applyView(tView, lView, renderer, 2 /* WalkTNodeTreeAction.Detach */, null, null);
  8453. lView[HOST] = null;
  8454. lView[T_HOST] = null;
  8455. }
  8456. /**
  8457. * Adds all DOM elements associated with a view.
  8458. *
  8459. * Because some root nodes of the view may be containers, we sometimes need
  8460. * to propagate deeply into the nested containers to add all elements in the
  8461. * views beneath it.
  8462. *
  8463. * @param tView The `TView' of the `LView` from which elements should be added or removed
  8464. * @param parentTNode The `TNode` where the `LView` should be attached to.
  8465. * @param renderer Current renderer to use for DOM manipulations.
  8466. * @param lView The view from which elements should be added or removed
  8467. * @param parentNativeNode The parent `RElement` where it should be inserted into.
  8468. * @param beforeNode The node before which elements should be added, if insert mode
  8469. */
  8470. function addViewToDOM(tView, parentTNode, renderer, lView, parentNativeNode, beforeNode) {
  8471. lView[HOST] = parentNativeNode;
  8472. lView[T_HOST] = parentTNode;
  8473. applyView(tView, lView, renderer, 1 /* WalkTNodeTreeAction.Insert */, parentNativeNode, beforeNode);
  8474. }
  8475. /**
  8476. * Detach a `LView` from the DOM by detaching its nodes.
  8477. *
  8478. * @param tView The `TView' of the `LView` to be detached
  8479. * @param lView the `LView` to be detached.
  8480. */
  8481. function detachViewFromDOM(tView, lView) {
  8482. applyView(tView, lView, lView[RENDERER], 2 /* WalkTNodeTreeAction.Detach */, null, null);
  8483. }
  8484. /**
  8485. * Traverses down and up the tree of views and containers to remove listeners and
  8486. * call onDestroy callbacks.
  8487. *
  8488. * Notes:
  8489. * - Because it's used for onDestroy calls, it needs to be bottom-up.
  8490. * - Must process containers instead of their views to avoid splicing
  8491. * when views are destroyed and re-added.
  8492. * - Using a while loop because it's faster than recursion
  8493. * - Destroy only called on movement to sibling or movement to parent (laterally or up)
  8494. *
  8495. * @param rootView The view to destroy
  8496. */
  8497. function destroyViewTree(rootView) {
  8498. // If the view has no children, we can clean it up and return early.
  8499. let lViewOrLContainer = rootView[CHILD_HEAD];
  8500. if (!lViewOrLContainer) {
  8501. return cleanUpView(rootView[TVIEW], rootView);
  8502. }
  8503. while (lViewOrLContainer) {
  8504. let next = null;
  8505. if (isLView(lViewOrLContainer)) {
  8506. // If LView, traverse down to child.
  8507. next = lViewOrLContainer[CHILD_HEAD];
  8508. }
  8509. else {
  8510. ngDevMode && assertLContainer(lViewOrLContainer);
  8511. // If container, traverse down to its first LView.
  8512. const firstView = lViewOrLContainer[CONTAINER_HEADER_OFFSET];
  8513. if (firstView)
  8514. next = firstView;
  8515. }
  8516. if (!next) {
  8517. // Only clean up view when moving to the side or up, as destroy hooks
  8518. // should be called in order from the bottom up.
  8519. while (lViewOrLContainer && !lViewOrLContainer[NEXT] && lViewOrLContainer !== rootView) {
  8520. if (isLView(lViewOrLContainer)) {
  8521. cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
  8522. }
  8523. lViewOrLContainer = lViewOrLContainer[PARENT];
  8524. }
  8525. if (lViewOrLContainer === null)
  8526. lViewOrLContainer = rootView;
  8527. if (isLView(lViewOrLContainer)) {
  8528. cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
  8529. }
  8530. next = lViewOrLContainer && lViewOrLContainer[NEXT];
  8531. }
  8532. lViewOrLContainer = next;
  8533. }
  8534. }
  8535. /**
  8536. * Inserts a view into a container.
  8537. *
  8538. * This adds the view to the container's array of active views in the correct
  8539. * position. It also adds the view's elements to the DOM if the container isn't a
  8540. * root node of another view (in that case, the view's elements will be added when
  8541. * the container's parent view is added later).
  8542. *
  8543. * @param tView The `TView' of the `LView` to insert
  8544. * @param lView The view to insert
  8545. * @param lContainer The container into which the view should be inserted
  8546. * @param index Which index in the container to insert the child view into
  8547. */
  8548. function insertView(tView, lView, lContainer, index) {
  8549. ngDevMode && assertLView(lView);
  8550. ngDevMode && assertLContainer(lContainer);
  8551. const indexInContainer = CONTAINER_HEADER_OFFSET + index;
  8552. const containerLength = lContainer.length;
  8553. if (index > 0) {
  8554. // This is a new view, we need to add it to the children.
  8555. lContainer[indexInContainer - 1][NEXT] = lView;
  8556. }
  8557. if (index < containerLength - CONTAINER_HEADER_OFFSET) {
  8558. lView[NEXT] = lContainer[indexInContainer];
  8559. addToArray(lContainer, CONTAINER_HEADER_OFFSET + index, lView);
  8560. }
  8561. else {
  8562. lContainer.push(lView);
  8563. lView[NEXT] = null;
  8564. }
  8565. lView[PARENT] = lContainer;
  8566. // track views where declaration and insertion points are different
  8567. const declarationLContainer = lView[DECLARATION_LCONTAINER];
  8568. if (declarationLContainer !== null && lContainer !== declarationLContainer) {
  8569. trackMovedView(declarationLContainer, lView);
  8570. }
  8571. // notify query that a new view has been added
  8572. const lQueries = lView[QUERIES];
  8573. if (lQueries !== null) {
  8574. lQueries.insertView(tView);
  8575. }
  8576. // Sets the attached flag
  8577. lView[FLAGS] |= 128 /* LViewFlags.Attached */;
  8578. }
  8579. /**
  8580. * Track views created from the declaration container (TemplateRef) and inserted into a
  8581. * different LContainer.
  8582. */
  8583. function trackMovedView(declarationContainer, lView) {
  8584. ngDevMode && assertDefined(lView, 'LView required');
  8585. ngDevMode && assertLContainer(declarationContainer);
  8586. const movedViews = declarationContainer[MOVED_VIEWS];
  8587. const insertedLContainer = lView[PARENT];
  8588. ngDevMode && assertLContainer(insertedLContainer);
  8589. const insertedComponentLView = insertedLContainer[PARENT][DECLARATION_COMPONENT_VIEW];
  8590. ngDevMode && assertDefined(insertedComponentLView, 'Missing insertedComponentLView');
  8591. const declaredComponentLView = lView[DECLARATION_COMPONENT_VIEW];
  8592. ngDevMode && assertDefined(declaredComponentLView, 'Missing declaredComponentLView');
  8593. if (declaredComponentLView !== insertedComponentLView) {
  8594. // At this point the declaration-component is not same as insertion-component; this means that
  8595. // this is a transplanted view. Mark the declared lView as having transplanted views so that
  8596. // those views can participate in CD.
  8597. declarationContainer[HAS_TRANSPLANTED_VIEWS] = true;
  8598. }
  8599. if (movedViews === null) {
  8600. declarationContainer[MOVED_VIEWS] = [lView];
  8601. }
  8602. else {
  8603. movedViews.push(lView);
  8604. }
  8605. }
  8606. function detachMovedView(declarationContainer, lView) {
  8607. ngDevMode && assertLContainer(declarationContainer);
  8608. ngDevMode &&
  8609. assertDefined(declarationContainer[MOVED_VIEWS], 'A projected view should belong to a non-empty projected views collection');
  8610. const movedViews = declarationContainer[MOVED_VIEWS];
  8611. const declarationViewIndex = movedViews.indexOf(lView);
  8612. const insertionLContainer = lView[PARENT];
  8613. ngDevMode && assertLContainer(insertionLContainer);
  8614. // If the view was marked for refresh but then detached before it was checked (where the flag
  8615. // would be cleared and the counter decremented), we need to update the status here.
  8616. clearViewRefreshFlag(lView);
  8617. movedViews.splice(declarationViewIndex, 1);
  8618. }
  8619. /**
  8620. * Detaches a view from a container.
  8621. *
  8622. * This method removes the view from the container's array of active views. It also
  8623. * removes the view's elements from the DOM.
  8624. *
  8625. * @param lContainer The container from which to detach a view
  8626. * @param removeIndex The index of the view to detach
  8627. * @returns Detached LView instance.
  8628. */
  8629. function detachView(lContainer, removeIndex) {
  8630. if (lContainer.length <= CONTAINER_HEADER_OFFSET)
  8631. return;
  8632. const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex;
  8633. const viewToDetach = lContainer[indexInContainer];
  8634. if (viewToDetach) {
  8635. const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER];
  8636. if (declarationLContainer !== null && declarationLContainer !== lContainer) {
  8637. detachMovedView(declarationLContainer, viewToDetach);
  8638. }
  8639. if (removeIndex > 0) {
  8640. lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT];
  8641. }
  8642. const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex);
  8643. removeViewFromDOM(viewToDetach[TVIEW], viewToDetach);
  8644. // notify query that a view has been removed
  8645. const lQueries = removedLView[QUERIES];
  8646. if (lQueries !== null) {
  8647. lQueries.detachView(removedLView[TVIEW]);
  8648. }
  8649. viewToDetach[PARENT] = null;
  8650. viewToDetach[NEXT] = null;
  8651. // Unsets the attached flag
  8652. viewToDetach[FLAGS] &= ~128 /* LViewFlags.Attached */;
  8653. }
  8654. return viewToDetach;
  8655. }
  8656. /**
  8657. * A standalone function which destroys an LView,
  8658. * conducting clean up (e.g. removing listeners, calling onDestroys).
  8659. *
  8660. * @param tView The `TView' of the `LView` to be destroyed
  8661. * @param lView The view to be destroyed.
  8662. */
  8663. function destroyLView(tView, lView) {
  8664. if (!(lView[FLAGS] & 256 /* LViewFlags.Destroyed */)) {
  8665. const renderer = lView[RENDERER];
  8666. lView[REACTIVE_TEMPLATE_CONSUMER] && consumerDestroy(lView[REACTIVE_TEMPLATE_CONSUMER]);
  8667. lView[REACTIVE_HOST_BINDING_CONSUMER] && consumerDestroy(lView[REACTIVE_HOST_BINDING_CONSUMER]);
  8668. if (renderer.destroyNode) {
  8669. applyView(tView, lView, renderer, 3 /* WalkTNodeTreeAction.Destroy */, null, null);
  8670. }
  8671. destroyViewTree(lView);
  8672. }
  8673. }
  8674. /**
  8675. * Calls onDestroys hooks for all directives and pipes in a given view and then removes all
  8676. * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks
  8677. * can be propagated to @Output listeners.
  8678. *
  8679. * @param tView `TView` for the `LView` to clean up.
  8680. * @param lView The LView to clean up
  8681. */
  8682. function cleanUpView(tView, lView) {
  8683. if (!(lView[FLAGS] & 256 /* LViewFlags.Destroyed */)) {
  8684. // Usually the Attached flag is removed when the view is detached from its parent, however
  8685. // if it's a root view, the flag won't be unset hence why we're also removing on destroy.
  8686. lView[FLAGS] &= ~128 /* LViewFlags.Attached */;
  8687. // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook
  8688. // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If
  8689. // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop.
  8690. // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is
  8691. // really more of an "afterDestroy" hook if you think about it.
  8692. lView[FLAGS] |= 256 /* LViewFlags.Destroyed */;
  8693. executeOnDestroys(tView, lView);
  8694. processCleanups(tView, lView);
  8695. // For component views only, the local renderer is destroyed at clean up time.
  8696. if (lView[TVIEW].type === 1 /* TViewType.Component */) {
  8697. ngDevMode && ngDevMode.rendererDestroy++;
  8698. lView[RENDERER].destroy();
  8699. }
  8700. const declarationContainer = lView[DECLARATION_LCONTAINER];
  8701. // we are dealing with an embedded view that is still inserted into a container
  8702. if (declarationContainer !== null && isLContainer(lView[PARENT])) {
  8703. // and this is a projected view
  8704. if (declarationContainer !== lView[PARENT]) {
  8705. detachMovedView(declarationContainer, lView);
  8706. }
  8707. // For embedded views still attached to a container: remove query result from this view.
  8708. const lQueries = lView[QUERIES];
  8709. if (lQueries !== null) {
  8710. lQueries.detachView(tView);
  8711. }
  8712. }
  8713. // Unregister the view once everything else has been cleaned up.
  8714. unregisterLView(lView);
  8715. }
  8716. }
  8717. /** Removes listeners and unsubscribes from output subscriptions */
  8718. function processCleanups(tView, lView) {
  8719. const tCleanup = tView.cleanup;
  8720. const lCleanup = lView[CLEANUP];
  8721. if (tCleanup !== null) {
  8722. for (let i = 0; i < tCleanup.length - 1; i += 2) {
  8723. if (typeof tCleanup[i] === 'string') {
  8724. // This is a native DOM listener. It will occupy 4 entries in the TCleanup array (hence i +=
  8725. // 2 at the end of this block).
  8726. const targetIdx = tCleanup[i + 3];
  8727. ngDevMode && assertNumber(targetIdx, 'cleanup target must be a number');
  8728. if (targetIdx >= 0) {
  8729. // unregister
  8730. lCleanup[targetIdx]();
  8731. }
  8732. else {
  8733. // Subscription
  8734. lCleanup[-targetIdx].unsubscribe();
  8735. }
  8736. i += 2;
  8737. }
  8738. else {
  8739. // This is a cleanup function that is grouped with the index of its context
  8740. const context = lCleanup[tCleanup[i + 1]];
  8741. tCleanup[i].call(context);
  8742. }
  8743. }
  8744. }
  8745. if (lCleanup !== null) {
  8746. lView[CLEANUP] = null;
  8747. }
  8748. const destroyHooks = lView[ON_DESTROY_HOOKS];
  8749. if (destroyHooks !== null) {
  8750. // Reset the ON_DESTROY_HOOKS array before iterating over it to prevent hooks that unregister
  8751. // themselves from mutating the array during iteration.
  8752. lView[ON_DESTROY_HOOKS] = null;
  8753. for (let i = 0; i < destroyHooks.length; i++) {
  8754. const destroyHooksFn = destroyHooks[i];
  8755. ngDevMode && assertFunction(destroyHooksFn, 'Expecting destroy hook to be a function.');
  8756. destroyHooksFn();
  8757. }
  8758. }
  8759. }
  8760. /** Calls onDestroy hooks for this view */
  8761. function executeOnDestroys(tView, lView) {
  8762. let destroyHooks;
  8763. if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
  8764. for (let i = 0; i < destroyHooks.length; i += 2) {
  8765. const context = lView[destroyHooks[i]];
  8766. // Only call the destroy hook if the context has been requested.
  8767. if (!(context instanceof NodeInjectorFactory)) {
  8768. const toCall = destroyHooks[i + 1];
  8769. if (Array.isArray(toCall)) {
  8770. for (let j = 0; j < toCall.length; j += 2) {
  8771. const callContext = context[toCall[j]];
  8772. const hook = toCall[j + 1];
  8773. profiler(4 /* ProfilerEvent.LifecycleHookStart */, callContext, hook);
  8774. try {
  8775. hook.call(callContext);
  8776. }
  8777. finally {
  8778. profiler(5 /* ProfilerEvent.LifecycleHookEnd */, callContext, hook);
  8779. }
  8780. }
  8781. }
  8782. else {
  8783. profiler(4 /* ProfilerEvent.LifecycleHookStart */, context, toCall);
  8784. try {
  8785. toCall.call(context);
  8786. }
  8787. finally {
  8788. profiler(5 /* ProfilerEvent.LifecycleHookEnd */, context, toCall);
  8789. }
  8790. }
  8791. }
  8792. }
  8793. }
  8794. }
  8795. /**
  8796. * Returns a native element if a node can be inserted into the given parent.
  8797. *
  8798. * There are two reasons why we may not be able to insert a element immediately.
  8799. * - Projection: When creating a child content element of a component, we have to skip the
  8800. * insertion because the content of a component will be projected.
  8801. * `<component><content>delayed due to projection</content></component>`
  8802. * - Parent container is disconnected: This can happen when we are inserting a view into
  8803. * parent container, which itself is disconnected. For example the parent container is part
  8804. * of a View which has not be inserted or is made for projection but has not been inserted
  8805. * into destination.
  8806. *
  8807. * @param tView: Current `TView`.
  8808. * @param tNode: `TNode` for which we wish to retrieve render parent.
  8809. * @param lView: Current `LView`.
  8810. */
  8811. function getParentRElement(tView, tNode, lView) {
  8812. return getClosestRElement(tView, tNode.parent, lView);
  8813. }
  8814. /**
  8815. * Get closest `RElement` or `null` if it can't be found.
  8816. *
  8817. * If `TNode` is `TNodeType.Element` => return `RElement` at `LView[tNode.index]` location.
  8818. * If `TNode` is `TNodeType.ElementContainer|IcuContain` => return the parent (recursively).
  8819. * If `TNode` is `null` then return host `RElement`:
  8820. * - return `null` if projection
  8821. * - return `null` if parent container is disconnected (we have no parent.)
  8822. *
  8823. * @param tView: Current `TView`.
  8824. * @param tNode: `TNode` for which we wish to retrieve `RElement` (or `null` if host element is
  8825. * needed).
  8826. * @param lView: Current `LView`.
  8827. * @returns `null` if the `RElement` can't be determined at this time (no parent / projection)
  8828. */
  8829. function getClosestRElement(tView, tNode, lView) {
  8830. let parentTNode = tNode;
  8831. // Skip over element and ICU containers as those are represented by a comment node and
  8832. // can't be used as a render parent.
  8833. while (parentTNode !== null &&
  8834. (parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */))) {
  8835. tNode = parentTNode;
  8836. parentTNode = tNode.parent;
  8837. }
  8838. // If the parent tNode is null, then we are inserting across views: either into an embedded view
  8839. // or a component view.
  8840. if (parentTNode === null) {
  8841. // We are inserting a root element of the component view into the component host element and
  8842. // it should always be eager.
  8843. return lView[HOST];
  8844. }
  8845. else {
  8846. ngDevMode && assertTNodeType(parentTNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */);
  8847. const { componentOffset } = parentTNode;
  8848. if (componentOffset > -1) {
  8849. ngDevMode && assertTNodeForLView(parentTNode, lView);
  8850. const { encapsulation } = tView.data[parentTNode.directiveStart + componentOffset];
  8851. // We've got a parent which is an element in the current view. We just need to verify if the
  8852. // parent element is not a component. Component's content nodes are not inserted immediately
  8853. // because they will be projected, and so doing insert at this point would be wasteful.
  8854. // Since the projection would then move it to its final destination. Note that we can't
  8855. // make this assumption when using the Shadow DOM, because the native projection placeholders
  8856. // (<content> or <slot>) have to be in place as elements are being inserted.
  8857. if (encapsulation === ViewEncapsulation.None ||
  8858. encapsulation === ViewEncapsulation.Emulated) {
  8859. return null;
  8860. }
  8861. }
  8862. return getNativeByTNode(parentTNode, lView);
  8863. }
  8864. }
  8865. /**
  8866. * Inserts a native node before another native node for a given parent.
  8867. * This is a utility function that can be used when native nodes were determined.
  8868. */
  8869. function nativeInsertBefore(renderer, parent, child, beforeNode, isMove) {
  8870. ngDevMode && ngDevMode.rendererInsertBefore++;
  8871. renderer.insertBefore(parent, child, beforeNode, isMove);
  8872. }
  8873. function nativeAppendChild(renderer, parent, child) {
  8874. ngDevMode && ngDevMode.rendererAppendChild++;
  8875. ngDevMode && assertDefined(parent, 'parent node must be defined');
  8876. renderer.appendChild(parent, child);
  8877. }
  8878. function nativeAppendOrInsertBefore(renderer, parent, child, beforeNode, isMove) {
  8879. if (beforeNode !== null) {
  8880. nativeInsertBefore(renderer, parent, child, beforeNode, isMove);
  8881. }
  8882. else {
  8883. nativeAppendChild(renderer, parent, child);
  8884. }
  8885. }
  8886. /** Removes a node from the DOM given its native parent. */
  8887. function nativeRemoveChild(renderer, parent, child, isHostElement) {
  8888. renderer.removeChild(parent, child, isHostElement);
  8889. }
  8890. /** Checks if an element is a `<template>` node. */
  8891. function isTemplateNode(node) {
  8892. return node.tagName === 'TEMPLATE' && node.content !== undefined;
  8893. }
  8894. /**
  8895. * Returns a native parent of a given native node.
  8896. */
  8897. function nativeParentNode(renderer, node) {
  8898. return renderer.parentNode(node);
  8899. }
  8900. /**
  8901. * Returns a native sibling of a given native node.
  8902. */
  8903. function nativeNextSibling(renderer, node) {
  8904. return renderer.nextSibling(node);
  8905. }
  8906. /**
  8907. * Find a node in front of which `currentTNode` should be inserted.
  8908. *
  8909. * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
  8910. * takes `TNode.insertBeforeIndex` into account if i18n code has been invoked.
  8911. *
  8912. * @param parentTNode parent `TNode`
  8913. * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
  8914. * @param lView current `LView`
  8915. */
  8916. function getInsertInFrontOfRNode(parentTNode, currentTNode, lView) {
  8917. return _getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView);
  8918. }
  8919. /**
  8920. * Find a node in front of which `currentTNode` should be inserted. (Does not take i18n into
  8921. * account)
  8922. *
  8923. * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
  8924. * does not take `TNode.insertBeforeIndex` into account.
  8925. *
  8926. * @param parentTNode parent `TNode`
  8927. * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
  8928. * @param lView current `LView`
  8929. */
  8930. function getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView) {
  8931. if (parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */)) {
  8932. return getNativeByTNode(parentTNode, lView);
  8933. }
  8934. return null;
  8935. }
  8936. /**
  8937. * Tree shakable boundary for `getInsertInFrontOfRNodeWithI18n` function.
  8938. *
  8939. * This function will only be set if i18n code runs.
  8940. */
  8941. let _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithNoI18n;
  8942. /**
  8943. * Tree shakable boundary for `processI18nInsertBefore` function.
  8944. *
  8945. * This function will only be set if i18n code runs.
  8946. */
  8947. let _processI18nInsertBefore;
  8948. function setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore) {
  8949. _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithI18n;
  8950. _processI18nInsertBefore = processI18nInsertBefore;
  8951. }
  8952. /**
  8953. * Appends the `child` native node (or a collection of nodes) to the `parent`.
  8954. *
  8955. * @param tView The `TView' to be appended
  8956. * @param lView The current LView
  8957. * @param childRNode The native child (or children) that should be appended
  8958. * @param childTNode The TNode of the child element
  8959. */
  8960. function appendChild(tView, lView, childRNode, childTNode) {
  8961. const parentRNode = getParentRElement(tView, childTNode, lView);
  8962. const renderer = lView[RENDERER];
  8963. const parentTNode = childTNode.parent || lView[T_HOST];
  8964. const anchorNode = getInsertInFrontOfRNode(parentTNode, childTNode, lView);
  8965. if (parentRNode != null) {
  8966. if (Array.isArray(childRNode)) {
  8967. for (let i = 0; i < childRNode.length; i++) {
  8968. nativeAppendOrInsertBefore(renderer, parentRNode, childRNode[i], anchorNode, false);
  8969. }
  8970. }
  8971. else {
  8972. nativeAppendOrInsertBefore(renderer, parentRNode, childRNode, anchorNode, false);
  8973. }
  8974. }
  8975. _processI18nInsertBefore !== undefined &&
  8976. _processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRNode);
  8977. }
  8978. /**
  8979. * Returns the first native node for a given LView, starting from the provided TNode.
  8980. *
  8981. * Native nodes are returned in the order in which those appear in the native tree (DOM).
  8982. */
  8983. function getFirstNativeNode(lView, tNode) {
  8984. if (tNode !== null) {
  8985. ngDevMode &&
  8986. assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */ | 16 /* TNodeType.Projection */);
  8987. const tNodeType = tNode.type;
  8988. if (tNodeType & 3 /* TNodeType.AnyRNode */) {
  8989. return getNativeByTNode(tNode, lView);
  8990. }
  8991. else if (tNodeType & 4 /* TNodeType.Container */) {
  8992. return getBeforeNodeForView(-1, lView[tNode.index]);
  8993. }
  8994. else if (tNodeType & 8 /* TNodeType.ElementContainer */) {
  8995. const elIcuContainerChild = tNode.child;
  8996. if (elIcuContainerChild !== null) {
  8997. return getFirstNativeNode(lView, elIcuContainerChild);
  8998. }
  8999. else {
  9000. const rNodeOrLContainer = lView[tNode.index];
  9001. if (isLContainer(rNodeOrLContainer)) {
  9002. return getBeforeNodeForView(-1, rNodeOrLContainer);
  9003. }
  9004. else {
  9005. return unwrapRNode(rNodeOrLContainer);
  9006. }
  9007. }
  9008. }
  9009. else if (tNodeType & 32 /* TNodeType.Icu */) {
  9010. let nextRNode = icuContainerIterate(tNode, lView);
  9011. let rNode = nextRNode();
  9012. // If the ICU container has no nodes, than we use the ICU anchor as the node.
  9013. return rNode || unwrapRNode(lView[tNode.index]);
  9014. }
  9015. else {
  9016. const projectionNodes = getProjectionNodes(lView, tNode);
  9017. if (projectionNodes !== null) {
  9018. if (Array.isArray(projectionNodes)) {
  9019. return projectionNodes[0];
  9020. }
  9021. const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
  9022. ngDevMode && assertParentView(parentView);
  9023. return getFirstNativeNode(parentView, projectionNodes);
  9024. }
  9025. else {
  9026. return getFirstNativeNode(lView, tNode.next);
  9027. }
  9028. }
  9029. }
  9030. return null;
  9031. }
  9032. function getProjectionNodes(lView, tNode) {
  9033. if (tNode !== null) {
  9034. const componentView = lView[DECLARATION_COMPONENT_VIEW];
  9035. const componentHost = componentView[T_HOST];
  9036. const slotIdx = tNode.projection;
  9037. ngDevMode && assertProjectionSlots(lView);
  9038. return componentHost.projection[slotIdx];
  9039. }
  9040. return null;
  9041. }
  9042. function getBeforeNodeForView(viewIndexInContainer, lContainer) {
  9043. const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1;
  9044. if (nextViewIndex < lContainer.length) {
  9045. const lView = lContainer[nextViewIndex];
  9046. const firstTNodeOfView = lView[TVIEW].firstChild;
  9047. if (firstTNodeOfView !== null) {
  9048. return getFirstNativeNode(lView, firstTNodeOfView);
  9049. }
  9050. }
  9051. return lContainer[NATIVE];
  9052. }
  9053. /**
  9054. * Removes a native node itself using a given renderer. To remove the node we are looking up its
  9055. * parent from the native tree as not all platforms / browsers support the equivalent of
  9056. * node.remove().
  9057. *
  9058. * @param renderer A renderer to be used
  9059. * @param rNode The native node that should be removed
  9060. * @param isHostElement A flag indicating if a node to be removed is a host of a component.
  9061. */
  9062. function nativeRemoveNode(renderer, rNode, isHostElement) {
  9063. ngDevMode && ngDevMode.rendererRemoveNode++;
  9064. const nativeParent = nativeParentNode(renderer, rNode);
  9065. if (nativeParent) {
  9066. nativeRemoveChild(renderer, nativeParent, rNode, isHostElement);
  9067. }
  9068. }
  9069. /**
  9070. * Clears the contents of a given RElement.
  9071. *
  9072. * @param rElement the native RElement to be cleared
  9073. */
  9074. function clearElementContents(rElement) {
  9075. rElement.textContent = '';
  9076. }
  9077. /**
  9078. * Performs the operation of `action` on the node. Typically this involves inserting or removing
  9079. * nodes on the LView or projection boundary.
  9080. */
  9081. function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode, isProjection) {
  9082. while (tNode != null) {
  9083. ngDevMode && assertTNodeForLView(tNode, lView);
  9084. ngDevMode &&
  9085. assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
  9086. const rawSlotValue = lView[tNode.index];
  9087. const tNodeType = tNode.type;
  9088. if (isProjection) {
  9089. if (action === 0 /* WalkTNodeTreeAction.Create */) {
  9090. rawSlotValue && attachPatchData(unwrapRNode(rawSlotValue), lView);
  9091. tNode.flags |= 2 /* TNodeFlags.isProjected */;
  9092. }
  9093. }
  9094. if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
  9095. if (tNodeType & 8 /* TNodeType.ElementContainer */) {
  9096. applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false);
  9097. applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
  9098. }
  9099. else if (tNodeType & 32 /* TNodeType.Icu */) {
  9100. const nextRNode = icuContainerIterate(tNode, lView);
  9101. let rNode;
  9102. while (rNode = nextRNode()) {
  9103. applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
  9104. }
  9105. applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
  9106. }
  9107. else if (tNodeType & 16 /* TNodeType.Projection */) {
  9108. applyProjectionRecursive(renderer, action, lView, tNode, parentRElement, beforeNode);
  9109. }
  9110. else {
  9111. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */);
  9112. applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
  9113. }
  9114. }
  9115. tNode = isProjection ? tNode.projectionNext : tNode.next;
  9116. }
  9117. }
  9118. function applyView(tView, lView, renderer, action, parentRElement, beforeNode) {
  9119. applyNodes(renderer, action, tView.firstChild, lView, parentRElement, beforeNode, false);
  9120. }
  9121. /**
  9122. * `applyProjection` performs operation on the projection.
  9123. *
  9124. * Inserting a projection requires us to locate the projected nodes from the parent component. The
  9125. * complication is that those nodes themselves could be re-projected from their parent component.
  9126. *
  9127. * @param tView The `TView` of `LView` which needs to be inserted, detached, destroyed
  9128. * @param lView The `LView` which needs to be inserted, detached, destroyed.
  9129. * @param tProjectionNode node to project
  9130. */
  9131. function applyProjection(tView, lView, tProjectionNode) {
  9132. const renderer = lView[RENDERER];
  9133. const parentRNode = getParentRElement(tView, tProjectionNode, lView);
  9134. const parentTNode = tProjectionNode.parent || lView[T_HOST];
  9135. let beforeNode = getInsertInFrontOfRNode(parentTNode, tProjectionNode, lView);
  9136. applyProjectionRecursive(renderer, 0 /* WalkTNodeTreeAction.Create */, lView, tProjectionNode, parentRNode, beforeNode);
  9137. }
  9138. /**
  9139. * `applyProjectionRecursive` performs operation on the projection specified by `action` (insert,
  9140. * detach, destroy)
  9141. *
  9142. * Inserting a projection requires us to locate the projected nodes from the parent component. The
  9143. * complication is that those nodes themselves could be re-projected from their parent component.
  9144. *
  9145. * @param renderer Render to use
  9146. * @param action action to perform (insert, detach, destroy)
  9147. * @param lView The LView which needs to be inserted, detached, destroyed.
  9148. * @param tProjectionNode node to project
  9149. * @param parentRElement parent DOM element for insertion/removal.
  9150. * @param beforeNode Before which node the insertions should happen.
  9151. */
  9152. function applyProjectionRecursive(renderer, action, lView, tProjectionNode, parentRElement, beforeNode) {
  9153. const componentLView = lView[DECLARATION_COMPONENT_VIEW];
  9154. const componentNode = componentLView[T_HOST];
  9155. ngDevMode &&
  9156. assertEqual(typeof tProjectionNode.projection, 'number', 'expecting projection index');
  9157. const nodeToProjectOrRNodes = componentNode.projection[tProjectionNode.projection];
  9158. if (Array.isArray(nodeToProjectOrRNodes)) {
  9159. // This should not exist, it is a bit of a hack. When we bootstrap a top level node and we
  9160. // need to support passing projectable nodes, so we cheat and put them in the TNode
  9161. // of the Host TView. (Yes we put instance info at the T Level). We can get away with it
  9162. // because we know that that TView is not shared and therefore it will not be a problem.
  9163. // This should be refactored and cleaned up.
  9164. for (let i = 0; i < nodeToProjectOrRNodes.length; i++) {
  9165. const rNode = nodeToProjectOrRNodes[i];
  9166. applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
  9167. }
  9168. }
  9169. else {
  9170. let nodeToProject = nodeToProjectOrRNodes;
  9171. const projectedComponentLView = componentLView[PARENT];
  9172. // If a parent <ng-content> is located within a skip hydration block,
  9173. // annotate an actual node that is being projected with the same flag too.
  9174. if (hasInSkipHydrationBlockFlag(tProjectionNode)) {
  9175. nodeToProject.flags |= 128 /* TNodeFlags.inSkipHydrationBlock */;
  9176. }
  9177. applyNodes(renderer, action, nodeToProject, projectedComponentLView, parentRElement, beforeNode, true);
  9178. }
  9179. }
  9180. /**
  9181. * `applyContainer` performs an operation on the container and its views as specified by
  9182. * `action` (insert, detach, destroy)
  9183. *
  9184. * Inserting a Container is complicated by the fact that the container may have Views which
  9185. * themselves have containers or projections.
  9186. *
  9187. * @param renderer Renderer to use
  9188. * @param action action to perform (insert, detach, destroy)
  9189. * @param lContainer The LContainer which needs to be inserted, detached, destroyed.
  9190. * @param parentRElement parent DOM element for insertion/removal.
  9191. * @param beforeNode Before which node the insertions should happen.
  9192. */
  9193. function applyContainer(renderer, action, lContainer, parentRElement, beforeNode) {
  9194. ngDevMode && assertLContainer(lContainer);
  9195. const anchor = lContainer[NATIVE]; // LContainer has its own before node.
  9196. const native = unwrapRNode(lContainer);
  9197. // An LContainer can be created dynamically on any node by injecting ViewContainerRef.
  9198. // Asking for a ViewContainerRef on an element will result in a creation of a separate anchor
  9199. // node (comment in the DOM) that will be different from the LContainer's host node. In this
  9200. // particular case we need to execute action on 2 nodes:
  9201. // - container's host node (this is done in the executeActionOnElementOrContainer)
  9202. // - container's host node (this is done here)
  9203. if (anchor !== native) {
  9204. // This is very strange to me (Misko). I would expect that the native is same as anchor. I
  9205. // don't see a reason why they should be different, but they are.
  9206. //
  9207. // If they are we need to process the second anchor as well.
  9208. applyToElementOrContainer(action, renderer, parentRElement, anchor, beforeNode);
  9209. }
  9210. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  9211. const lView = lContainer[i];
  9212. applyView(lView[TVIEW], lView, renderer, action, parentRElement, anchor);
  9213. }
  9214. }
  9215. /**
  9216. * Writes class/style to element.
  9217. *
  9218. * @param renderer Renderer to use.
  9219. * @param isClassBased `true` if it should be written to `class` (`false` to write to `style`)
  9220. * @param rNode The Node to write to.
  9221. * @param prop Property to write to. This would be the class/style name.
  9222. * @param value Value to write. If `null`/`undefined`/`false` this is considered a remove (set/add
  9223. * otherwise).
  9224. */
  9225. function applyStyling(renderer, isClassBased, rNode, prop, value) {
  9226. if (isClassBased) {
  9227. // We actually want JS true/false here because any truthy value should add the class
  9228. if (!value) {
  9229. ngDevMode && ngDevMode.rendererRemoveClass++;
  9230. renderer.removeClass(rNode, prop);
  9231. }
  9232. else {
  9233. ngDevMode && ngDevMode.rendererAddClass++;
  9234. renderer.addClass(rNode, prop);
  9235. }
  9236. }
  9237. else {
  9238. let flags = prop.indexOf('-') === -1 ? undefined : RendererStyleFlags2.DashCase;
  9239. if (value == null /** || value === undefined */) {
  9240. ngDevMode && ngDevMode.rendererRemoveStyle++;
  9241. renderer.removeStyle(rNode, prop, flags);
  9242. }
  9243. else {
  9244. // A value is important if it ends with `!important`. The style
  9245. // parser strips any semicolons at the end of the value.
  9246. const isImportant = typeof value === 'string' ? value.endsWith('!important') : false;
  9247. if (isImportant) {
  9248. // !important has to be stripped from the value for it to be valid.
  9249. value = value.slice(0, -10);
  9250. flags |= RendererStyleFlags2.Important;
  9251. }
  9252. ngDevMode && ngDevMode.rendererSetStyle++;
  9253. renderer.setStyle(rNode, prop, value, flags);
  9254. }
  9255. }
  9256. }
  9257. /**
  9258. * Write `cssText` to `RElement`.
  9259. *
  9260. * This function does direct write without any reconciliation. Used for writing initial values, so
  9261. * that static styling values do not pull in the style parser.
  9262. *
  9263. * @param renderer Renderer to use
  9264. * @param element The element which needs to be updated.
  9265. * @param newValue The new class list to write.
  9266. */
  9267. function writeDirectStyle(renderer, element, newValue) {
  9268. ngDevMode && assertString(newValue, '\'newValue\' should be a string');
  9269. renderer.setAttribute(element, 'style', newValue);
  9270. ngDevMode && ngDevMode.rendererSetStyle++;
  9271. }
  9272. /**
  9273. * Write `className` to `RElement`.
  9274. *
  9275. * This function does direct write without any reconciliation. Used for writing initial values, so
  9276. * that static styling values do not pull in the style parser.
  9277. *
  9278. * @param renderer Renderer to use
  9279. * @param element The element which needs to be updated.
  9280. * @param newValue The new class list to write.
  9281. */
  9282. function writeDirectClass(renderer, element, newValue) {
  9283. ngDevMode && assertString(newValue, '\'newValue\' should be a string');
  9284. if (newValue === '') {
  9285. // There are tests in `google3` which expect `element.getAttribute('class')` to be `null`.
  9286. renderer.removeAttribute(element, 'class');
  9287. }
  9288. else {
  9289. renderer.setAttribute(element, 'class', newValue);
  9290. }
  9291. ngDevMode && ngDevMode.rendererSetClassName++;
  9292. }
  9293. /** Sets up the static DOM attributes on an `RNode`. */
  9294. function setupStaticAttributes(renderer, element, tNode) {
  9295. const { mergedAttrs, classes, styles } = tNode;
  9296. if (mergedAttrs !== null) {
  9297. setUpAttributes(renderer, element, mergedAttrs);
  9298. }
  9299. if (classes !== null) {
  9300. writeDirectClass(renderer, element, classes);
  9301. }
  9302. if (styles !== null) {
  9303. writeDirectStyle(renderer, element, styles);
  9304. }
  9305. }
  9306. /**
  9307. * @fileoverview
  9308. * A module to facilitate use of a Trusted Types policy internally within
  9309. * Angular. It lazily constructs the Trusted Types policy, providing helper
  9310. * utilities for promoting strings to Trusted Types. When Trusted Types are not
  9311. * available, strings are used as a fallback.
  9312. * @security All use of this module is security-sensitive and should go through
  9313. * security review.
  9314. */
  9315. /**
  9316. * The Trusted Types policy, or null if Trusted Types are not
  9317. * enabled/supported, or undefined if the policy has not been created yet.
  9318. */
  9319. let policy$1;
  9320. /**
  9321. * Returns the Trusted Types policy, or null if Trusted Types are not
  9322. * enabled/supported. The first call to this function will create the policy.
  9323. */
  9324. function getPolicy$1() {
  9325. if (policy$1 === undefined) {
  9326. policy$1 = null;
  9327. if (_global.trustedTypes) {
  9328. try {
  9329. policy$1 = _global.trustedTypes.createPolicy('angular', {
  9330. createHTML: (s) => s,
  9331. createScript: (s) => s,
  9332. createScriptURL: (s) => s,
  9333. });
  9334. }
  9335. catch {
  9336. // trustedTypes.createPolicy throws if called with a name that is
  9337. // already registered, even in report-only mode. Until the API changes,
  9338. // catch the error not to break the applications functionally. In such
  9339. // cases, the code will fall back to using strings.
  9340. }
  9341. }
  9342. }
  9343. return policy$1;
  9344. }
  9345. /**
  9346. * Unsafely promote a string to a TrustedHTML, falling back to strings when
  9347. * Trusted Types are not available.
  9348. * @security This is a security-sensitive function; any use of this function
  9349. * must go through security review. In particular, it must be assured that the
  9350. * provided string will never cause an XSS vulnerability if used in a context
  9351. * that will be interpreted as HTML by a browser, e.g. when assigning to
  9352. * element.innerHTML.
  9353. */
  9354. function trustedHTMLFromString(html) {
  9355. return getPolicy$1()?.createHTML(html) || html;
  9356. }
  9357. /**
  9358. * Unsafely promote a string to a TrustedScript, falling back to strings when
  9359. * Trusted Types are not available.
  9360. * @security In particular, it must be assured that the provided string will
  9361. * never cause an XSS vulnerability if used in a context that will be
  9362. * interpreted and executed as a script by a browser, e.g. when calling eval.
  9363. */
  9364. function trustedScriptFromString(script) {
  9365. return getPolicy$1()?.createScript(script) || script;
  9366. }
  9367. /**
  9368. * Unsafely promote a string to a TrustedScriptURL, falling back to strings
  9369. * when Trusted Types are not available.
  9370. * @security This is a security-sensitive function; any use of this function
  9371. * must go through security review. In particular, it must be assured that the
  9372. * provided string will never cause an XSS vulnerability if used in a context
  9373. * that will cause a browser to load and execute a resource, e.g. when
  9374. * assigning to script.src.
  9375. */
  9376. function trustedScriptURLFromString(url) {
  9377. return getPolicy$1()?.createScriptURL(url) || url;
  9378. }
  9379. /**
  9380. * Unsafely call the Function constructor with the given string arguments. It
  9381. * is only available in development mode, and should be stripped out of
  9382. * production code.
  9383. * @security This is a security-sensitive function; any use of this function
  9384. * must go through security review. In particular, it must be assured that it
  9385. * is only called from development code, as use in production code can lead to
  9386. * XSS vulnerabilities.
  9387. */
  9388. function newTrustedFunctionForDev(...args) {
  9389. if (typeof ngDevMode === 'undefined') {
  9390. throw new Error('newTrustedFunctionForDev should never be called in production');
  9391. }
  9392. if (!_global.trustedTypes) {
  9393. // In environments that don't support Trusted Types, fall back to the most
  9394. // straightforward implementation:
  9395. return new Function(...args);
  9396. }
  9397. // Chrome currently does not support passing TrustedScript to the Function
  9398. // constructor. The following implements the workaround proposed on the page
  9399. // below, where the Chromium bug is also referenced:
  9400. // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
  9401. const fnArgs = args.slice(0, -1).join(',');
  9402. const fnBody = args[args.length - 1];
  9403. const body = `(function anonymous(${fnArgs}
  9404. ) { ${fnBody}
  9405. })`;
  9406. // Using eval directly confuses the compiler and prevents this module from
  9407. // being stripped out of JS binaries even if not used. The global['eval']
  9408. // indirection fixes that.
  9409. const fn = _global['eval'](trustedScriptFromString(body));
  9410. if (fn.bind === undefined) {
  9411. // Workaround for a browser bug that only exists in Chrome 83, where passing
  9412. // a TrustedScript to eval just returns the TrustedScript back without
  9413. // evaluating it. In that case, fall back to the most straightforward
  9414. // implementation:
  9415. return new Function(...args);
  9416. }
  9417. // To completely mimic the behavior of calling "new Function", two more
  9418. // things need to happen:
  9419. // 1. Stringifying the resulting function should return its source code
  9420. fn.toString = () => body;
  9421. // 2. When calling the resulting function, `this` should refer to `global`
  9422. return fn.bind(_global);
  9423. // When Trusted Types support in Function constructors is widely available,
  9424. // the implementation of this function can be simplified to:
  9425. // return new Function(...args.map(a => trustedScriptFromString(a)));
  9426. }
  9427. /**
  9428. * Validation function invoked at runtime for each binding that might potentially
  9429. * represent a security-sensitive attribute of an <iframe>.
  9430. * See `IFRAME_SECURITY_SENSITIVE_ATTRS` in the
  9431. * `packages/compiler/src/schema/dom_security_schema.ts` script for the full list
  9432. * of such attributes.
  9433. *
  9434. * @codeGenApi
  9435. */
  9436. function ɵɵvalidateIframeAttribute(attrValue, tagName, attrName) {
  9437. const lView = getLView();
  9438. const tNode = getSelectedTNode();
  9439. const element = getNativeByTNode(tNode, lView);
  9440. // Restrict any dynamic bindings of security-sensitive attributes/properties
  9441. // on an <iframe> for security reasons.
  9442. if (tNode.type === 2 /* TNodeType.Element */ && tagName.toLowerCase() === 'iframe') {
  9443. const iframe = element;
  9444. // Unset previously applied `src` and `srcdoc` if we come across a situation when
  9445. // a security-sensitive attribute is set later via an attribute/property binding.
  9446. iframe.src = '';
  9447. iframe.srcdoc = trustedHTMLFromString('');
  9448. // Also remove the <iframe> from the document.
  9449. nativeRemoveNode(lView[RENDERER], iframe);
  9450. const errorMessage = ngDevMode &&
  9451. `Angular has detected that the \`${attrName}\` was applied ` +
  9452. `as a binding to an <iframe>${getTemplateLocationDetails(lView)}. ` +
  9453. `For security reasons, the \`${attrName}\` can be set on an <iframe> ` +
  9454. `as a static attribute only. \n` +
  9455. `To fix this, switch the \`${attrName}\` binding to a static attribute ` +
  9456. `in a template or in host bindings section.`;
  9457. throw new RuntimeError(-910 /* RuntimeErrorCode.UNSAFE_IFRAME_ATTRS */, errorMessage);
  9458. }
  9459. return attrValue;
  9460. }
  9461. /**
  9462. * @fileoverview
  9463. * A module to facilitate use of a Trusted Types policy internally within
  9464. * Angular specifically for bypassSecurityTrust* and custom sanitizers. It
  9465. * lazily constructs the Trusted Types policy, providing helper utilities for
  9466. * promoting strings to Trusted Types. When Trusted Types are not available,
  9467. * strings are used as a fallback.
  9468. * @security All use of this module is security-sensitive and should go through
  9469. * security review.
  9470. */
  9471. /**
  9472. * The Trusted Types policy, or null if Trusted Types are not
  9473. * enabled/supported, or undefined if the policy has not been created yet.
  9474. */
  9475. let policy;
  9476. /**
  9477. * Returns the Trusted Types policy, or null if Trusted Types are not
  9478. * enabled/supported. The first call to this function will create the policy.
  9479. */
  9480. function getPolicy() {
  9481. if (policy === undefined) {
  9482. policy = null;
  9483. if (_global.trustedTypes) {
  9484. try {
  9485. policy = _global.trustedTypes
  9486. .createPolicy('angular#unsafe-bypass', {
  9487. createHTML: (s) => s,
  9488. createScript: (s) => s,
  9489. createScriptURL: (s) => s,
  9490. });
  9491. }
  9492. catch {
  9493. // trustedTypes.createPolicy throws if called with a name that is
  9494. // already registered, even in report-only mode. Until the API changes,
  9495. // catch the error not to break the applications functionally. In such
  9496. // cases, the code will fall back to using strings.
  9497. }
  9498. }
  9499. }
  9500. return policy;
  9501. }
  9502. /**
  9503. * Unsafely promote a string to a TrustedHTML, falling back to strings when
  9504. * Trusted Types are not available.
  9505. * @security This is a security-sensitive function; any use of this function
  9506. * must go through security review. In particular, it must be assured that it
  9507. * is only passed strings that come directly from custom sanitizers or the
  9508. * bypassSecurityTrust* functions.
  9509. */
  9510. function trustedHTMLFromStringBypass(html) {
  9511. return getPolicy()?.createHTML(html) || html;
  9512. }
  9513. /**
  9514. * Unsafely promote a string to a TrustedScript, falling back to strings when
  9515. * Trusted Types are not available.
  9516. * @security This is a security-sensitive function; any use of this function
  9517. * must go through security review. In particular, it must be assured that it
  9518. * is only passed strings that come directly from custom sanitizers or the
  9519. * bypassSecurityTrust* functions.
  9520. */
  9521. function trustedScriptFromStringBypass(script) {
  9522. return getPolicy()?.createScript(script) || script;
  9523. }
  9524. /**
  9525. * Unsafely promote a string to a TrustedScriptURL, falling back to strings
  9526. * when Trusted Types are not available.
  9527. * @security This is a security-sensitive function; any use of this function
  9528. * must go through security review. In particular, it must be assured that it
  9529. * is only passed strings that come directly from custom sanitizers or the
  9530. * bypassSecurityTrust* functions.
  9531. */
  9532. function trustedScriptURLFromStringBypass(url) {
  9533. return getPolicy()?.createScriptURL(url) || url;
  9534. }
  9535. class SafeValueImpl {
  9536. constructor(changingThisBreaksApplicationSecurity) {
  9537. this.changingThisBreaksApplicationSecurity = changingThisBreaksApplicationSecurity;
  9538. }
  9539. toString() {
  9540. return `SafeValue must use [property]=binding: ${this.changingThisBreaksApplicationSecurity}` +
  9541. ` (see ${XSS_SECURITY_URL})`;
  9542. }
  9543. }
  9544. class SafeHtmlImpl extends SafeValueImpl {
  9545. getTypeName() {
  9546. return "HTML" /* BypassType.Html */;
  9547. }
  9548. }
  9549. class SafeStyleImpl extends SafeValueImpl {
  9550. getTypeName() {
  9551. return "Style" /* BypassType.Style */;
  9552. }
  9553. }
  9554. class SafeScriptImpl extends SafeValueImpl {
  9555. getTypeName() {
  9556. return "Script" /* BypassType.Script */;
  9557. }
  9558. }
  9559. class SafeUrlImpl extends SafeValueImpl {
  9560. getTypeName() {
  9561. return "URL" /* BypassType.Url */;
  9562. }
  9563. }
  9564. class SafeResourceUrlImpl extends SafeValueImpl {
  9565. getTypeName() {
  9566. return "ResourceURL" /* BypassType.ResourceUrl */;
  9567. }
  9568. }
  9569. function unwrapSafeValue(value) {
  9570. return value instanceof SafeValueImpl ? value.changingThisBreaksApplicationSecurity :
  9571. value;
  9572. }
  9573. function allowSanitizationBypassAndThrow(value, type) {
  9574. const actualType = getSanitizationBypassType(value);
  9575. if (actualType != null && actualType !== type) {
  9576. // Allow ResourceURLs in URL contexts, they are strictly more trusted.
  9577. if (actualType === "ResourceURL" /* BypassType.ResourceUrl */ && type === "URL" /* BypassType.Url */)
  9578. return true;
  9579. throw new Error(`Required a safe ${type}, got a ${actualType} (see ${XSS_SECURITY_URL})`);
  9580. }
  9581. return actualType === type;
  9582. }
  9583. function getSanitizationBypassType(value) {
  9584. return value instanceof SafeValueImpl && value.getTypeName() || null;
  9585. }
  9586. /**
  9587. * Mark `html` string as trusted.
  9588. *
  9589. * This function wraps the trusted string in `String` and brands it in a way which makes it
  9590. * recognizable to {@link htmlSanitizer} to be trusted implicitly.
  9591. *
  9592. * @param trustedHtml `html` string which needs to be implicitly trusted.
  9593. * @returns a `html` which has been branded to be implicitly trusted.
  9594. */
  9595. function bypassSanitizationTrustHtml(trustedHtml) {
  9596. return new SafeHtmlImpl(trustedHtml);
  9597. }
  9598. /**
  9599. * Mark `style` string as trusted.
  9600. *
  9601. * This function wraps the trusted string in `String` and brands it in a way which makes it
  9602. * recognizable to {@link styleSanitizer} to be trusted implicitly.
  9603. *
  9604. * @param trustedStyle `style` string which needs to be implicitly trusted.
  9605. * @returns a `style` hich has been branded to be implicitly trusted.
  9606. */
  9607. function bypassSanitizationTrustStyle(trustedStyle) {
  9608. return new SafeStyleImpl(trustedStyle);
  9609. }
  9610. /**
  9611. * Mark `script` string as trusted.
  9612. *
  9613. * This function wraps the trusted string in `String` and brands it in a way which makes it
  9614. * recognizable to {@link scriptSanitizer} to be trusted implicitly.
  9615. *
  9616. * @param trustedScript `script` string which needs to be implicitly trusted.
  9617. * @returns a `script` which has been branded to be implicitly trusted.
  9618. */
  9619. function bypassSanitizationTrustScript(trustedScript) {
  9620. return new SafeScriptImpl(trustedScript);
  9621. }
  9622. /**
  9623. * Mark `url` string as trusted.
  9624. *
  9625. * This function wraps the trusted string in `String` and brands it in a way which makes it
  9626. * recognizable to {@link urlSanitizer} to be trusted implicitly.
  9627. *
  9628. * @param trustedUrl `url` string which needs to be implicitly trusted.
  9629. * @returns a `url` which has been branded to be implicitly trusted.
  9630. */
  9631. function bypassSanitizationTrustUrl(trustedUrl) {
  9632. return new SafeUrlImpl(trustedUrl);
  9633. }
  9634. /**
  9635. * Mark `url` string as trusted.
  9636. *
  9637. * This function wraps the trusted string in `String` and brands it in a way which makes it
  9638. * recognizable to {@link resourceUrlSanitizer} to be trusted implicitly.
  9639. *
  9640. * @param trustedResourceUrl `url` string which needs to be implicitly trusted.
  9641. * @returns a `url` which has been branded to be implicitly trusted.
  9642. */
  9643. function bypassSanitizationTrustResourceUrl(trustedResourceUrl) {
  9644. return new SafeResourceUrlImpl(trustedResourceUrl);
  9645. }
  9646. /**
  9647. * This helper is used to get hold of an inert tree of DOM elements containing dirty HTML
  9648. * that needs sanitizing.
  9649. * Depending upon browser support we use one of two strategies for doing this.
  9650. * Default: DOMParser strategy
  9651. * Fallback: InertDocument strategy
  9652. */
  9653. function getInertBodyHelper(defaultDoc) {
  9654. const inertDocumentHelper = new InertDocumentHelper(defaultDoc);
  9655. return isDOMParserAvailable() ? new DOMParserHelper(inertDocumentHelper) : inertDocumentHelper;
  9656. }
  9657. /**
  9658. * Uses DOMParser to create and fill an inert body element.
  9659. * This is the default strategy used in browsers that support it.
  9660. */
  9661. class DOMParserHelper {
  9662. constructor(inertDocumentHelper) {
  9663. this.inertDocumentHelper = inertDocumentHelper;
  9664. }
  9665. getInertBodyElement(html) {
  9666. // We add these extra elements to ensure that the rest of the content is parsed as expected
  9667. // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the
  9668. // `<head>` tag. Note that the `<body>` tag is closed implicitly to prevent unclosed tags
  9669. // in `html` from consuming the otherwise explicit `</body>` tag.
  9670. html = '<body><remove></remove>' + html;
  9671. try {
  9672. const body = new window.DOMParser()
  9673. .parseFromString(trustedHTMLFromString(html), 'text/html')
  9674. .body;
  9675. if (body === null) {
  9676. // In some browsers (e.g. Mozilla/5.0 iPad AppleWebKit Mobile) the `body` property only
  9677. // becomes available in the following tick of the JS engine. In that case we fall back to
  9678. // the `inertDocumentHelper` instead.
  9679. return this.inertDocumentHelper.getInertBodyElement(html);
  9680. }
  9681. body.removeChild(body.firstChild);
  9682. return body;
  9683. }
  9684. catch {
  9685. return null;
  9686. }
  9687. }
  9688. }
  9689. /**
  9690. * Use an HTML5 `template` element to create and fill an inert DOM element.
  9691. * This is the fallback strategy if the browser does not support DOMParser.
  9692. */
  9693. class InertDocumentHelper {
  9694. constructor(defaultDoc) {
  9695. this.defaultDoc = defaultDoc;
  9696. this.inertDocument = this.defaultDoc.implementation.createHTMLDocument('sanitization-inert');
  9697. }
  9698. getInertBodyElement(html) {
  9699. const templateEl = this.inertDocument.createElement('template');
  9700. templateEl.innerHTML = trustedHTMLFromString(html);
  9701. return templateEl;
  9702. }
  9703. }
  9704. /**
  9705. * We need to determine whether the DOMParser exists in the global context and
  9706. * supports parsing HTML; HTML parsing support is not as wide as other formats, see
  9707. * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Browser_compatibility.
  9708. *
  9709. * @suppress {uselessCode}
  9710. */
  9711. function isDOMParserAvailable() {
  9712. try {
  9713. return !!new window.DOMParser().parseFromString(trustedHTMLFromString(''), 'text/html');
  9714. }
  9715. catch {
  9716. return false;
  9717. }
  9718. }
  9719. /**
  9720. * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation
  9721. * contexts.
  9722. *
  9723. * This regular expression matches a subset of URLs that will not cause script
  9724. * execution if used in URL context within a HTML document. Specifically, this
  9725. * regular expression matches if:
  9726. * (1) Either a protocol that is not javascript:, and that has valid characters
  9727. * (alphanumeric or [+-.]).
  9728. * (2) or no protocol. A protocol must be followed by a colon. The below
  9729. * allows that by allowing colons only after one of the characters [/?#].
  9730. * A colon after a hash (#) must be in the fragment.
  9731. * Otherwise, a colon after a (?) must be in a query.
  9732. * Otherwise, a colon after a single solidus (/) must be in a path.
  9733. * Otherwise, a colon after a double solidus (//) must be in the authority
  9734. * (before port).
  9735. *
  9736. * The pattern disallows &, used in HTML entity declarations before
  9737. * one of the characters in [/?#]. This disallows HTML entities used in the
  9738. * protocol name, which should never happen, e.g. "h&#116;tp" for "http".
  9739. * It also disallows HTML entities in the first path part of a relative path,
  9740. * e.g. "foo&lt;bar/baz". Our existing escaping functions should not produce
  9741. * that. More importantly, it disallows masking of a colon,
  9742. * e.g. "javascript&#58;...".
  9743. *
  9744. * This regular expression was taken from the Closure sanitization library.
  9745. */
  9746. const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:\/?#]*(?:[\/?#]|$))/i;
  9747. function _sanitizeUrl(url) {
  9748. url = String(url);
  9749. if (url.match(SAFE_URL_PATTERN))
  9750. return url;
  9751. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  9752. console.warn(`WARNING: sanitizing unsafe URL value ${url} (see ${XSS_SECURITY_URL})`);
  9753. }
  9754. return 'unsafe:' + url;
  9755. }
  9756. function tagSet(tags) {
  9757. const res = {};
  9758. for (const t of tags.split(','))
  9759. res[t] = true;
  9760. return res;
  9761. }
  9762. function merge(...sets) {
  9763. const res = {};
  9764. for (const s of sets) {
  9765. for (const v in s) {
  9766. if (s.hasOwnProperty(v))
  9767. res[v] = true;
  9768. }
  9769. }
  9770. return res;
  9771. }
  9772. // Good source of info about elements and attributes
  9773. // https://html.spec.whatwg.org/#semantics
  9774. // https://simon.html5.org/html-elements
  9775. // Safe Void Elements - HTML5
  9776. // https://html.spec.whatwg.org/#void-elements
  9777. const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr');
  9778. // Elements that you can, intentionally, leave open (and which close themselves)
  9779. // https://html.spec.whatwg.org/#optional-tags
  9780. const OPTIONAL_END_TAG_BLOCK_ELEMENTS = tagSet('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr');
  9781. const OPTIONAL_END_TAG_INLINE_ELEMENTS = tagSet('rp,rt');
  9782. const OPTIONAL_END_TAG_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, OPTIONAL_END_TAG_BLOCK_ELEMENTS);
  9783. // Safe Block Elements - HTML5
  9784. const BLOCK_ELEMENTS = merge(OPTIONAL_END_TAG_BLOCK_ELEMENTS, tagSet('address,article,' +
  9785. 'aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
  9786. 'h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul'));
  9787. // Inline Elements - HTML5
  9788. const INLINE_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, tagSet('a,abbr,acronym,audio,b,' +
  9789. 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' +
  9790. 'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video'));
  9791. const VALID_ELEMENTS = merge(VOID_ELEMENTS, BLOCK_ELEMENTS, INLINE_ELEMENTS, OPTIONAL_END_TAG_ELEMENTS);
  9792. // Attributes that have href and hence need to be sanitized
  9793. const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href');
  9794. const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' +
  9795. 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' +
  9796. 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' +
  9797. 'scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,' +
  9798. 'valign,value,vspace,width');
  9799. // Accessibility attributes as per WAI-ARIA 1.1 (W3C Working Draft 14 December 2018)
  9800. const ARIA_ATTRS = tagSet('aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,' +
  9801. 'aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,' +
  9802. 'aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,' +
  9803. 'aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,' +
  9804. 'aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,' +
  9805. 'aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,' +
  9806. 'aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext');
  9807. // NB: This currently consciously doesn't support SVG. SVG sanitization has had several security
  9808. // issues in the past, so it seems safer to leave it out if possible. If support for binding SVG via
  9809. // innerHTML is required, SVG attributes should be added here.
  9810. // NB: Sanitization does not allow <form> elements or other active elements (<button> etc). Those
  9811. // can be sanitized, but they increase security surface area without a legitimate use case, so they
  9812. // are left out here.
  9813. const VALID_ATTRS = merge(URI_ATTRS, HTML_ATTRS, ARIA_ATTRS);
  9814. // Elements whose content should not be traversed/preserved, if the elements themselves are invalid.
  9815. //
  9816. // Typically, `<invalid>Some content</invalid>` would traverse (and in this case preserve)
  9817. // `Some content`, but strip `invalid-element` opening/closing tags. For some elements, though, we
  9818. // don't want to preserve the content, if the elements themselves are going to be removed.
  9819. const SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS = tagSet('script,style,template');
  9820. /**
  9821. * SanitizingHtmlSerializer serializes a DOM fragment, stripping out any unsafe elements and unsafe
  9822. * attributes.
  9823. */
  9824. class SanitizingHtmlSerializer {
  9825. constructor() {
  9826. // Explicitly track if something was stripped, to avoid accidentally warning of sanitization just
  9827. // because characters were re-encoded.
  9828. this.sanitizedSomething = false;
  9829. this.buf = [];
  9830. }
  9831. sanitizeChildren(el) {
  9832. // This cannot use a TreeWalker, as it has to run on Angular's various DOM adapters.
  9833. // However this code never accesses properties off of `document` before deleting its contents
  9834. // again, so it shouldn't be vulnerable to DOM clobbering.
  9835. let current = el.firstChild;
  9836. let traverseContent = true;
  9837. while (current) {
  9838. if (current.nodeType === Node.ELEMENT_NODE) {
  9839. traverseContent = this.startElement(current);
  9840. }
  9841. else if (current.nodeType === Node.TEXT_NODE) {
  9842. this.chars(current.nodeValue);
  9843. }
  9844. else {
  9845. // Strip non-element, non-text nodes.
  9846. this.sanitizedSomething = true;
  9847. }
  9848. if (traverseContent && current.firstChild) {
  9849. current = current.firstChild;
  9850. continue;
  9851. }
  9852. while (current) {
  9853. // Leaving the element. Walk up and to the right, closing tags as we go.
  9854. if (current.nodeType === Node.ELEMENT_NODE) {
  9855. this.endElement(current);
  9856. }
  9857. let next = this.checkClobberedElement(current, current.nextSibling);
  9858. if (next) {
  9859. current = next;
  9860. break;
  9861. }
  9862. current = this.checkClobberedElement(current, current.parentNode);
  9863. }
  9864. }
  9865. return this.buf.join('');
  9866. }
  9867. /**
  9868. * Sanitizes an opening element tag (if valid) and returns whether the element's contents should
  9869. * be traversed. Element content must always be traversed (even if the element itself is not
  9870. * valid/safe), unless the element is one of `SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS`.
  9871. *
  9872. * @param element The element to sanitize.
  9873. * @return True if the element's contents should be traversed.
  9874. */
  9875. startElement(element) {
  9876. const tagName = element.nodeName.toLowerCase();
  9877. if (!VALID_ELEMENTS.hasOwnProperty(tagName)) {
  9878. this.sanitizedSomething = true;
  9879. return !SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS.hasOwnProperty(tagName);
  9880. }
  9881. this.buf.push('<');
  9882. this.buf.push(tagName);
  9883. const elAttrs = element.attributes;
  9884. for (let i = 0; i < elAttrs.length; i++) {
  9885. const elAttr = elAttrs.item(i);
  9886. const attrName = elAttr.name;
  9887. const lower = attrName.toLowerCase();
  9888. if (!VALID_ATTRS.hasOwnProperty(lower)) {
  9889. this.sanitizedSomething = true;
  9890. continue;
  9891. }
  9892. let value = elAttr.value;
  9893. // TODO(martinprobst): Special case image URIs for data:image/...
  9894. if (URI_ATTRS[lower])
  9895. value = _sanitizeUrl(value);
  9896. this.buf.push(' ', attrName, '="', encodeEntities(value), '"');
  9897. }
  9898. this.buf.push('>');
  9899. return true;
  9900. }
  9901. endElement(current) {
  9902. const tagName = current.nodeName.toLowerCase();
  9903. if (VALID_ELEMENTS.hasOwnProperty(tagName) && !VOID_ELEMENTS.hasOwnProperty(tagName)) {
  9904. this.buf.push('</');
  9905. this.buf.push(tagName);
  9906. this.buf.push('>');
  9907. }
  9908. }
  9909. chars(chars) {
  9910. this.buf.push(encodeEntities(chars));
  9911. }
  9912. checkClobberedElement(node, nextNode) {
  9913. if (nextNode &&
  9914. (node.compareDocumentPosition(nextNode) &
  9915. Node.DOCUMENT_POSITION_CONTAINED_BY) === Node.DOCUMENT_POSITION_CONTAINED_BY) {
  9916. throw new Error(`Failed to sanitize html because the element is clobbered: ${node.outerHTML}`);
  9917. }
  9918. return nextNode;
  9919. }
  9920. }
  9921. // Regular Expressions for parsing tags and attributes
  9922. const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
  9923. // ! to ~ is the ASCII range.
  9924. const NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
  9925. /**
  9926. * Escapes all potentially dangerous characters, so that the
  9927. * resulting string can be safely inserted into attribute or
  9928. * element text.
  9929. * @param value
  9930. */
  9931. function encodeEntities(value) {
  9932. return value.replace(/&/g, '&amp;')
  9933. .replace(SURROGATE_PAIR_REGEXP, function (match) {
  9934. const hi = match.charCodeAt(0);
  9935. const low = match.charCodeAt(1);
  9936. return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
  9937. })
  9938. .replace(NON_ALPHANUMERIC_REGEXP, function (match) {
  9939. return '&#' + match.charCodeAt(0) + ';';
  9940. })
  9941. .replace(/</g, '&lt;')
  9942. .replace(/>/g, '&gt;');
  9943. }
  9944. let inertBodyHelper;
  9945. /**
  9946. * Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to
  9947. * the DOM in a browser environment.
  9948. */
  9949. function _sanitizeHtml(defaultDoc, unsafeHtmlInput) {
  9950. let inertBodyElement = null;
  9951. try {
  9952. inertBodyHelper = inertBodyHelper || getInertBodyHelper(defaultDoc);
  9953. // Make sure unsafeHtml is actually a string (TypeScript types are not enforced at runtime).
  9954. let unsafeHtml = unsafeHtmlInput ? String(unsafeHtmlInput) : '';
  9955. inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
  9956. // mXSS protection. Repeatedly parse the document to make sure it stabilizes, so that a browser
  9957. // trying to auto-correct incorrect HTML cannot cause formerly inert HTML to become dangerous.
  9958. let mXSSAttempts = 5;
  9959. let parsedHtml = unsafeHtml;
  9960. do {
  9961. if (mXSSAttempts === 0) {
  9962. throw new Error('Failed to sanitize html because the input is unstable');
  9963. }
  9964. mXSSAttempts--;
  9965. unsafeHtml = parsedHtml;
  9966. parsedHtml = inertBodyElement.innerHTML;
  9967. inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
  9968. } while (unsafeHtml !== parsedHtml);
  9969. const sanitizer = new SanitizingHtmlSerializer();
  9970. const safeHtml = sanitizer.sanitizeChildren(getTemplateContent(inertBodyElement) || inertBodyElement);
  9971. if ((typeof ngDevMode === 'undefined' || ngDevMode) && sanitizer.sanitizedSomething) {
  9972. console.warn(`WARNING: sanitizing HTML stripped some content, see ${XSS_SECURITY_URL}`);
  9973. }
  9974. return trustedHTMLFromString(safeHtml);
  9975. }
  9976. finally {
  9977. // In case anything goes wrong, clear out inertElement to reset the entire DOM structure.
  9978. if (inertBodyElement) {
  9979. const parent = getTemplateContent(inertBodyElement) || inertBodyElement;
  9980. while (parent.firstChild) {
  9981. parent.removeChild(parent.firstChild);
  9982. }
  9983. }
  9984. }
  9985. }
  9986. function getTemplateContent(el) {
  9987. return 'content' in el /** Microsoft/TypeScript#21517 */ && isTemplateElement(el) ?
  9988. el.content :
  9989. null;
  9990. }
  9991. function isTemplateElement(el) {
  9992. return el.nodeType === Node.ELEMENT_NODE && el.nodeName === 'TEMPLATE';
  9993. }
  9994. /**
  9995. * A SecurityContext marks a location that has dangerous security implications, e.g. a DOM property
  9996. * like `innerHTML` that could cause Cross Site Scripting (XSS) security bugs when improperly
  9997. * handled.
  9998. *
  9999. * See DomSanitizer for more details on security in Angular applications.
  10000. *
  10001. * @publicApi
  10002. */
  10003. var SecurityContext;
  10004. (function (SecurityContext) {
  10005. SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
  10006. SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
  10007. SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
  10008. SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
  10009. SecurityContext[SecurityContext["URL"] = 4] = "URL";
  10010. SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
  10011. })(SecurityContext || (SecurityContext = {}));
  10012. /**
  10013. * An `html` sanitizer which converts untrusted `html` **string** into trusted string by removing
  10014. * dangerous content.
  10015. *
  10016. * This method parses the `html` and locates potentially dangerous content (such as urls and
  10017. * javascript) and removes it.
  10018. *
  10019. * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustHtml}.
  10020. *
  10021. * @param unsafeHtml untrusted `html`, typically from the user.
  10022. * @returns `html` string which is safe to display to user, because all of the dangerous javascript
  10023. * and urls have been removed.
  10024. *
  10025. * @codeGenApi
  10026. */
  10027. function ɵɵsanitizeHtml(unsafeHtml) {
  10028. const sanitizer = getSanitizer();
  10029. if (sanitizer) {
  10030. return trustedHTMLFromStringBypass(sanitizer.sanitize(SecurityContext.HTML, unsafeHtml) || '');
  10031. }
  10032. if (allowSanitizationBypassAndThrow(unsafeHtml, "HTML" /* BypassType.Html */)) {
  10033. return trustedHTMLFromStringBypass(unwrapSafeValue(unsafeHtml));
  10034. }
  10035. return _sanitizeHtml(getDocument(), renderStringify(unsafeHtml));
  10036. }
  10037. /**
  10038. * A `style` sanitizer which converts untrusted `style` **string** into trusted string by removing
  10039. * dangerous content.
  10040. *
  10041. * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustStyle}.
  10042. *
  10043. * @param unsafeStyle untrusted `style`, typically from the user.
  10044. * @returns `style` string which is safe to bind to the `style` properties.
  10045. *
  10046. * @codeGenApi
  10047. */
  10048. function ɵɵsanitizeStyle(unsafeStyle) {
  10049. const sanitizer = getSanitizer();
  10050. if (sanitizer) {
  10051. return sanitizer.sanitize(SecurityContext.STYLE, unsafeStyle) || '';
  10052. }
  10053. if (allowSanitizationBypassAndThrow(unsafeStyle, "Style" /* BypassType.Style */)) {
  10054. return unwrapSafeValue(unsafeStyle);
  10055. }
  10056. return renderStringify(unsafeStyle);
  10057. }
  10058. /**
  10059. * A `url` sanitizer which converts untrusted `url` **string** into trusted string by removing
  10060. * dangerous
  10061. * content.
  10062. *
  10063. * This method parses the `url` and locates potentially dangerous content (such as javascript) and
  10064. * removes it.
  10065. *
  10066. * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustUrl}.
  10067. *
  10068. * @param unsafeUrl untrusted `url`, typically from the user.
  10069. * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
  10070. * all of the dangerous javascript has been removed.
  10071. *
  10072. * @codeGenApi
  10073. */
  10074. function ɵɵsanitizeUrl(unsafeUrl) {
  10075. const sanitizer = getSanitizer();
  10076. if (sanitizer) {
  10077. return sanitizer.sanitize(SecurityContext.URL, unsafeUrl) || '';
  10078. }
  10079. if (allowSanitizationBypassAndThrow(unsafeUrl, "URL" /* BypassType.Url */)) {
  10080. return unwrapSafeValue(unsafeUrl);
  10081. }
  10082. return _sanitizeUrl(renderStringify(unsafeUrl));
  10083. }
  10084. /**
  10085. * A `url` sanitizer which only lets trusted `url`s through.
  10086. *
  10087. * This passes only `url`s marked trusted by calling {@link bypassSanitizationTrustResourceUrl}.
  10088. *
  10089. * @param unsafeResourceUrl untrusted `url`, typically from the user.
  10090. * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
  10091. * only trusted `url`s have been allowed to pass.
  10092. *
  10093. * @codeGenApi
  10094. */
  10095. function ɵɵsanitizeResourceUrl(unsafeResourceUrl) {
  10096. const sanitizer = getSanitizer();
  10097. if (sanitizer) {
  10098. return trustedScriptURLFromStringBypass(sanitizer.sanitize(SecurityContext.RESOURCE_URL, unsafeResourceUrl) || '');
  10099. }
  10100. if (allowSanitizationBypassAndThrow(unsafeResourceUrl, "ResourceURL" /* BypassType.ResourceUrl */)) {
  10101. return trustedScriptURLFromStringBypass(unwrapSafeValue(unsafeResourceUrl));
  10102. }
  10103. throw new RuntimeError(904 /* RuntimeErrorCode.UNSAFE_VALUE_IN_RESOURCE_URL */, ngDevMode && `unsafe value used in a resource URL context (see ${XSS_SECURITY_URL})`);
  10104. }
  10105. /**
  10106. * A `script` sanitizer which only lets trusted javascript through.
  10107. *
  10108. * This passes only `script`s marked trusted by calling {@link
  10109. * bypassSanitizationTrustScript}.
  10110. *
  10111. * @param unsafeScript untrusted `script`, typically from the user.
  10112. * @returns `url` string which is safe to bind to the `<script>` element such as `<img src>`,
  10113. * because only trusted `scripts` have been allowed to pass.
  10114. *
  10115. * @codeGenApi
  10116. */
  10117. function ɵɵsanitizeScript(unsafeScript) {
  10118. const sanitizer = getSanitizer();
  10119. if (sanitizer) {
  10120. return trustedScriptFromStringBypass(sanitizer.sanitize(SecurityContext.SCRIPT, unsafeScript) || '');
  10121. }
  10122. if (allowSanitizationBypassAndThrow(unsafeScript, "Script" /* BypassType.Script */)) {
  10123. return trustedScriptFromStringBypass(unwrapSafeValue(unsafeScript));
  10124. }
  10125. throw new RuntimeError(905 /* RuntimeErrorCode.UNSAFE_VALUE_IN_SCRIPT */, ngDevMode && 'unsafe value used in a script context');
  10126. }
  10127. /**
  10128. * A template tag function for promoting the associated constant literal to a
  10129. * TrustedHTML. Interpolation is explicitly not allowed.
  10130. *
  10131. * @param html constant template literal containing trusted HTML.
  10132. * @returns TrustedHTML wrapping `html`.
  10133. *
  10134. * @security This is a security-sensitive function and should only be used to
  10135. * convert constant values of attributes and properties found in
  10136. * application-provided Angular templates to TrustedHTML.
  10137. *
  10138. * @codeGenApi
  10139. */
  10140. function ɵɵtrustConstantHtml(html) {
  10141. // The following runtime check ensures that the function was called as a
  10142. // template tag (e.g. ɵɵtrustConstantHtml`content`), without any interpolation
  10143. // (e.g. not ɵɵtrustConstantHtml`content ${variable}`). A TemplateStringsArray
  10144. // is an array with a `raw` property that is also an array. The associated
  10145. // template literal has no interpolation if and only if the length of the
  10146. // TemplateStringsArray is 1.
  10147. if (ngDevMode && (!Array.isArray(html) || !Array.isArray(html.raw) || html.length !== 1)) {
  10148. throw new Error(`Unexpected interpolation in trusted HTML constant: ${html.join('?')}`);
  10149. }
  10150. return trustedHTMLFromString(html[0]);
  10151. }
  10152. /**
  10153. * A template tag function for promoting the associated constant literal to a
  10154. * TrustedScriptURL. Interpolation is explicitly not allowed.
  10155. *
  10156. * @param url constant template literal containing a trusted script URL.
  10157. * @returns TrustedScriptURL wrapping `url`.
  10158. *
  10159. * @security This is a security-sensitive function and should only be used to
  10160. * convert constant values of attributes and properties found in
  10161. * application-provided Angular templates to TrustedScriptURL.
  10162. *
  10163. * @codeGenApi
  10164. */
  10165. function ɵɵtrustConstantResourceUrl(url) {
  10166. // The following runtime check ensures that the function was called as a
  10167. // template tag (e.g. ɵɵtrustConstantResourceUrl`content`), without any
  10168. // interpolation (e.g. not ɵɵtrustConstantResourceUrl`content ${variable}`). A
  10169. // TemplateStringsArray is an array with a `raw` property that is also an
  10170. // array. The associated template literal has no interpolation if and only if
  10171. // the length of the TemplateStringsArray is 1.
  10172. if (ngDevMode && (!Array.isArray(url) || !Array.isArray(url.raw) || url.length !== 1)) {
  10173. throw new Error(`Unexpected interpolation in trusted URL constant: ${url.join('?')}`);
  10174. }
  10175. return trustedScriptURLFromString(url[0]);
  10176. }
  10177. /**
  10178. * Detects which sanitizer to use for URL property, based on tag name and prop name.
  10179. *
  10180. * The rules are based on the RESOURCE_URL context config from
  10181. * `packages/compiler/src/schema/dom_security_schema.ts`.
  10182. * If tag and prop names don't match Resource URL schema, use URL sanitizer.
  10183. */
  10184. function getUrlSanitizer(tag, prop) {
  10185. if ((prop === 'src' &&
  10186. (tag === 'embed' || tag === 'frame' || tag === 'iframe' || tag === 'media' ||
  10187. tag === 'script')) ||
  10188. (prop === 'href' && (tag === 'base' || tag === 'link'))) {
  10189. return ɵɵsanitizeResourceUrl;
  10190. }
  10191. return ɵɵsanitizeUrl;
  10192. }
  10193. /**
  10194. * Sanitizes URL, selecting sanitizer function based on tag and property names.
  10195. *
  10196. * This function is used in case we can't define security context at compile time, when only prop
  10197. * name is available. This happens when we generate host bindings for Directives/Components. The
  10198. * host element is unknown at compile time, so we defer calculation of specific sanitizer to
  10199. * runtime.
  10200. *
  10201. * @param unsafeUrl untrusted `url`, typically from the user.
  10202. * @param tag target element tag name.
  10203. * @param prop name of the property that contains the value.
  10204. * @returns `url` string which is safe to bind.
  10205. *
  10206. * @codeGenApi
  10207. */
  10208. function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl, tag, prop) {
  10209. return getUrlSanitizer(tag, prop)(unsafeUrl);
  10210. }
  10211. function validateAgainstEventProperties(name) {
  10212. if (name.toLowerCase().startsWith('on')) {
  10213. const errorMessage = `Binding to event property '${name}' is disallowed for security reasons, ` +
  10214. `please use (${name.slice(2)})=...` +
  10215. `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
  10216. ` current module.`;
  10217. throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage);
  10218. }
  10219. }
  10220. function validateAgainstEventAttributes(name) {
  10221. if (name.toLowerCase().startsWith('on')) {
  10222. const errorMessage = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
  10223. `please use (${name.slice(2)})=...`;
  10224. throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage);
  10225. }
  10226. }
  10227. function getSanitizer() {
  10228. const lView = getLView();
  10229. return lView && lView[ENVIRONMENT].sanitizer;
  10230. }
  10231. /**
  10232. * Create a `StateKey<T>` that can be used to store value of type T with `TransferState`.
  10233. *
  10234. * Example:
  10235. *
  10236. * ```
  10237. * const COUNTER_KEY = makeStateKey<number>('counter');
  10238. * let value = 10;
  10239. *
  10240. * transferState.set(COUNTER_KEY, value);
  10241. * ```
  10242. *
  10243. * @publicApi
  10244. */
  10245. function makeStateKey(key) {
  10246. return key;
  10247. }
  10248. function initTransferState() {
  10249. const transferState = new TransferState();
  10250. if (inject$1(PLATFORM_ID) === 'browser') {
  10251. transferState.store = retrieveTransferredState(getDocument(), inject$1(APP_ID));
  10252. }
  10253. return transferState;
  10254. }
  10255. /**
  10256. * A key value store that is transferred from the application on the server side to the application
  10257. * on the client side.
  10258. *
  10259. * The `TransferState` is available as an injectable token.
  10260. * On the client, just inject this token using DI and use it, it will be lazily initialized.
  10261. * On the server it's already included if `renderApplication` function is used. Otherwise, import
  10262. * the `ServerTransferStateModule` module to make the `TransferState` available.
  10263. *
  10264. * The values in the store are serialized/deserialized using JSON.stringify/JSON.parse. So only
  10265. * boolean, number, string, null and non-class objects will be serialized and deserialized in a
  10266. * non-lossy manner.
  10267. *
  10268. * @publicApi
  10269. */
  10270. class TransferState {
  10271. constructor() {
  10272. /** @internal */
  10273. this.store = {};
  10274. this.onSerializeCallbacks = {};
  10275. }
  10276. /** @nocollapse */
  10277. static { this.ɵprov =
  10278. /** @pureOrBreakMyCode */ ɵɵdefineInjectable({
  10279. token: TransferState,
  10280. providedIn: 'root',
  10281. factory: initTransferState,
  10282. }); }
  10283. /**
  10284. * Get the value corresponding to a key. Return `defaultValue` if key is not found.
  10285. */
  10286. get(key, defaultValue) {
  10287. return this.store[key] !== undefined ? this.store[key] : defaultValue;
  10288. }
  10289. /**
  10290. * Set the value corresponding to a key.
  10291. */
  10292. set(key, value) {
  10293. this.store[key] = value;
  10294. }
  10295. /**
  10296. * Remove a key from the store.
  10297. */
  10298. remove(key) {
  10299. delete this.store[key];
  10300. }
  10301. /**
  10302. * Test whether a key exists in the store.
  10303. */
  10304. hasKey(key) {
  10305. return this.store.hasOwnProperty(key);
  10306. }
  10307. /**
  10308. * Indicates whether the state is empty.
  10309. */
  10310. get isEmpty() {
  10311. return Object.keys(this.store).length === 0;
  10312. }
  10313. /**
  10314. * Register a callback to provide the value for a key when `toJson` is called.
  10315. */
  10316. onSerialize(key, callback) {
  10317. this.onSerializeCallbacks[key] = callback;
  10318. }
  10319. /**
  10320. * Serialize the current state of the store to JSON.
  10321. */
  10322. toJson() {
  10323. // Call the onSerialize callbacks and put those values into the store.
  10324. for (const key in this.onSerializeCallbacks) {
  10325. if (this.onSerializeCallbacks.hasOwnProperty(key)) {
  10326. try {
  10327. this.store[key] = this.onSerializeCallbacks[key]();
  10328. }
  10329. catch (e) {
  10330. console.warn('Exception in onSerialize callback: ', e);
  10331. }
  10332. }
  10333. }
  10334. // Escape script tag to avoid break out of <script> tag in serialized output.
  10335. // Encoding of `<` is the same behaviour as G3 script_builders.
  10336. return JSON.stringify(this.store).replace(/</g, '\\u003C');
  10337. }
  10338. }
  10339. function retrieveTransferredState(doc, appId) {
  10340. // Locate the script tag with the JSON data transferred from the server.
  10341. // The id of the script tag is set to the Angular appId + 'state'.
  10342. const script = doc.getElementById(appId + '-state');
  10343. if (script?.textContent) {
  10344. try {
  10345. // Avoid using any here as it triggers lint errors in google3 (any is not allowed).
  10346. // Decoding of `<` is done of the box by browsers and node.js, same behaviour as G3
  10347. // script_builders.
  10348. return JSON.parse(script.textContent);
  10349. }
  10350. catch (e) {
  10351. console.warn('Exception while restoring TransferState for app ' + appId, e);
  10352. }
  10353. }
  10354. return {};
  10355. }
  10356. /** Encodes that the node lookup should start from the host node of this component. */
  10357. const REFERENCE_NODE_HOST = 'h';
  10358. /** Encodes that the node lookup should start from the document body node. */
  10359. const REFERENCE_NODE_BODY = 'b';
  10360. /**
  10361. * Describes navigation steps that the runtime logic need to perform,
  10362. * starting from a given (known) element.
  10363. */
  10364. var NodeNavigationStep;
  10365. (function (NodeNavigationStep) {
  10366. NodeNavigationStep["FirstChild"] = "f";
  10367. NodeNavigationStep["NextSibling"] = "n";
  10368. })(NodeNavigationStep || (NodeNavigationStep = {}));
  10369. /**
  10370. * Keys within serialized view data structure to represent various
  10371. * parts. See the `SerializedView` interface below for additional information.
  10372. */
  10373. const ELEMENT_CONTAINERS = 'e';
  10374. const TEMPLATES = 't';
  10375. const CONTAINERS = 'c';
  10376. const MULTIPLIER = 'x';
  10377. const NUM_ROOT_NODES = 'r';
  10378. const TEMPLATE_ID = 'i'; // as it's also an "id"
  10379. const NODES = 'n';
  10380. const DISCONNECTED_NODES = 'd';
  10381. /**
  10382. * The name of the key used in the TransferState collection,
  10383. * where hydration information is located.
  10384. */
  10385. const TRANSFER_STATE_TOKEN_ID = '__ɵnghData__';
  10386. /**
  10387. * Lookup key used to reference DOM hydration data (ngh) in `TransferState`.
  10388. */
  10389. const NGH_DATA_KEY = makeStateKey(TRANSFER_STATE_TOKEN_ID);
  10390. /**
  10391. * The name of the attribute that would be added to host component
  10392. * nodes and contain a reference to a particular slot in transferred
  10393. * state that contains the necessary hydration info for this component.
  10394. */
  10395. const NGH_ATTR_NAME = 'ngh';
  10396. /**
  10397. * Marker used in a comment node to ensure hydration content integrity
  10398. */
  10399. const SSR_CONTENT_INTEGRITY_MARKER = 'nghm';
  10400. /**
  10401. * Reference to a function that reads `ngh` attribute value from a given RNode
  10402. * and retrieves hydration information from the TransferState using that value
  10403. * as an index. Returns `null` by default, when hydration is not enabled.
  10404. *
  10405. * @param rNode Component's host element.
  10406. * @param injector Injector that this component has access to.
  10407. * @param isRootView Specifies whether we trying to read hydration info for the root view.
  10408. */
  10409. let _retrieveHydrationInfoImpl = (rNode, injector, isRootView) => null;
  10410. function retrieveHydrationInfoImpl(rNode, injector, isRootView = false) {
  10411. let nghAttrValue = rNode.getAttribute(NGH_ATTR_NAME);
  10412. if (nghAttrValue == null)
  10413. return null;
  10414. // For cases when a root component also acts as an anchor node for a ViewContainerRef
  10415. // (for example, when ViewContainerRef is injected in a root component), there is a need
  10416. // to serialize information about the component itself, as well as an LContainer that
  10417. // represents this ViewContainerRef. Effectively, we need to serialize 2 pieces of info:
  10418. // (1) hydration info for the root component itself and (2) hydration info for the
  10419. // ViewContainerRef instance (an LContainer). Each piece of information is included into
  10420. // the hydration data (in the TransferState object) separately, thus we end up with 2 ids.
  10421. // Since we only have 1 root element, we encode both bits of info into a single string:
  10422. // ids are separated by the `|` char (e.g. `10|25`, where `10` is the ngh for a component view
  10423. // and 25 is the `ngh` for a root view which holds LContainer).
  10424. const [componentViewNgh, rootViewNgh] = nghAttrValue.split('|');
  10425. nghAttrValue = isRootView ? rootViewNgh : componentViewNgh;
  10426. if (!nghAttrValue)
  10427. return null;
  10428. // We've read one of the ngh ids, keep the remaining one, so that
  10429. // we can set it back on the DOM element.
  10430. const remainingNgh = isRootView ? componentViewNgh : (rootViewNgh ? `|${rootViewNgh}` : '');
  10431. let data = {};
  10432. // An element might have an empty `ngh` attribute value (e.g. `<comp ngh="" />`),
  10433. // which means that no special annotations are required. Do not attempt to read
  10434. // from the TransferState in this case.
  10435. if (nghAttrValue !== '') {
  10436. const transferState = injector.get(TransferState, null, { optional: true });
  10437. if (transferState !== null) {
  10438. const nghData = transferState.get(NGH_DATA_KEY, []);
  10439. // The nghAttrValue is always a number referencing an index
  10440. // in the hydration TransferState data.
  10441. data = nghData[Number(nghAttrValue)];
  10442. // If the `ngh` attribute exists and has a non-empty value,
  10443. // the hydration info *must* be present in the TransferState.
  10444. // If there is no data for some reasons, this is an error.
  10445. ngDevMode && assertDefined(data, 'Unable to retrieve hydration info from the TransferState.');
  10446. }
  10447. }
  10448. const dehydratedView = {
  10449. data,
  10450. firstChild: rNode.firstChild ?? null,
  10451. };
  10452. if (isRootView) {
  10453. // If there is hydration info present for the root view, it means that there was
  10454. // a ViewContainerRef injected in the root component. The root component host element
  10455. // acted as an anchor node in this scenario. As a result, the DOM nodes that represent
  10456. // embedded views in this ViewContainerRef are located as siblings to the host node,
  10457. // i.e. `<app-root /><#VIEW1><#VIEW2>...<!--container-->`. In this case, the current
  10458. // node becomes the first child of this root view and the next sibling is the first
  10459. // element in the DOM segment.
  10460. dehydratedView.firstChild = rNode;
  10461. // We use `0` here, since this is the slot (right after the HEADER_OFFSET)
  10462. // where a component LView or an LContainer is located in a root LView.
  10463. setSegmentHead(dehydratedView, 0, rNode.nextSibling);
  10464. }
  10465. if (remainingNgh) {
  10466. // If we have only used one of the ngh ids, store the remaining one
  10467. // back on this RNode.
  10468. rNode.setAttribute(NGH_ATTR_NAME, remainingNgh);
  10469. }
  10470. else {
  10471. // The `ngh` attribute is cleared from the DOM node now
  10472. // that the data has been retrieved for all indices.
  10473. rNode.removeAttribute(NGH_ATTR_NAME);
  10474. }
  10475. // Note: don't check whether this node was claimed for hydration,
  10476. // because this node might've been previously claimed while processing
  10477. // template instructions.
  10478. ngDevMode && markRNodeAsClaimedByHydration(rNode, /* checkIfAlreadyClaimed */ false);
  10479. ngDevMode && ngDevMode.hydratedComponents++;
  10480. return dehydratedView;
  10481. }
  10482. /**
  10483. * Sets the implementation for the `retrieveHydrationInfo` function.
  10484. */
  10485. function enableRetrieveHydrationInfoImpl() {
  10486. _retrieveHydrationInfoImpl = retrieveHydrationInfoImpl;
  10487. }
  10488. /**
  10489. * Retrieves hydration info by reading the value from the `ngh` attribute
  10490. * and accessing a corresponding slot in TransferState storage.
  10491. */
  10492. function retrieveHydrationInfo(rNode, injector, isRootView = false) {
  10493. return _retrieveHydrationInfoImpl(rNode, injector, isRootView);
  10494. }
  10495. /**
  10496. * Retrieves the necessary object from a given ViewRef to serialize:
  10497. * - an LView for component views
  10498. * - an LContainer for cases when component acts as a ViewContainerRef anchor
  10499. * - `null` in case of an embedded view
  10500. */
  10501. function getLNodeForHydration(viewRef) {
  10502. // Reading an internal field from `ViewRef` instance.
  10503. let lView = viewRef._lView;
  10504. const tView = lView[TVIEW];
  10505. // A registered ViewRef might represent an instance of an
  10506. // embedded view, in which case we do not need to annotate it.
  10507. if (tView.type === 2 /* TViewType.Embedded */) {
  10508. return null;
  10509. }
  10510. // Check if it's a root view and if so, retrieve component's
  10511. // LView from the first slot after the header.
  10512. if (isRootView(lView)) {
  10513. lView = lView[HEADER_OFFSET];
  10514. }
  10515. return lView;
  10516. }
  10517. function getTextNodeContent(node) {
  10518. return node.textContent?.replace(/\s/gm, '');
  10519. }
  10520. /**
  10521. * Restores text nodes and separators into the DOM that were lost during SSR
  10522. * serialization. The hydration process replaces empty text nodes and text
  10523. * nodes that are immediately adjacent to other text nodes with comment nodes
  10524. * that this method filters on to restore those missing nodes that the
  10525. * hydration process is expecting to be present.
  10526. *
  10527. * @param node The app's root HTML Element
  10528. */
  10529. function processTextNodeMarkersBeforeHydration(node) {
  10530. const doc = getDocument();
  10531. const commentNodesIterator = doc.createNodeIterator(node, NodeFilter.SHOW_COMMENT, {
  10532. acceptNode(node) {
  10533. const content = getTextNodeContent(node);
  10534. const isTextNodeMarker = content === "ngetn" /* TextNodeMarker.EmptyNode */ || content === "ngtns" /* TextNodeMarker.Separator */;
  10535. return isTextNodeMarker ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
  10536. }
  10537. });
  10538. let currentNode;
  10539. // We cannot modify the DOM while using the commentIterator,
  10540. // because it throws off the iterator state.
  10541. // So we collect all marker nodes first and then follow up with
  10542. // applying the changes to the DOM: either inserting an empty node
  10543. // or just removing the marker if it was used as a separator.
  10544. const nodes = [];
  10545. while (currentNode = commentNodesIterator.nextNode()) {
  10546. nodes.push(currentNode);
  10547. }
  10548. for (const node of nodes) {
  10549. if (node.textContent === "ngetn" /* TextNodeMarker.EmptyNode */) {
  10550. node.replaceWith(doc.createTextNode(''));
  10551. }
  10552. else {
  10553. node.remove();
  10554. }
  10555. }
  10556. }
  10557. /**
  10558. * Marks a node as "claimed" by hydration process.
  10559. * This is needed to make assessments in tests whether
  10560. * the hydration process handled all nodes.
  10561. */
  10562. function markRNodeAsClaimedByHydration(node, checkIfAlreadyClaimed = true) {
  10563. if (!ngDevMode) {
  10564. throw new Error('Calling `markRNodeAsClaimedByHydration` in prod mode ' +
  10565. 'is not supported and likely a mistake.');
  10566. }
  10567. if (checkIfAlreadyClaimed && isRNodeClaimedForHydration(node)) {
  10568. throw new Error('Trying to claim a node, which was claimed already.');
  10569. }
  10570. node.__claimed = true;
  10571. ngDevMode.hydratedNodes++;
  10572. }
  10573. function isRNodeClaimedForHydration(node) {
  10574. return !!node.__claimed;
  10575. }
  10576. function setSegmentHead(hydrationInfo, index, node) {
  10577. hydrationInfo.segmentHeads ??= {};
  10578. hydrationInfo.segmentHeads[index] = node;
  10579. }
  10580. function getSegmentHead(hydrationInfo, index) {
  10581. return hydrationInfo.segmentHeads?.[index] ?? null;
  10582. }
  10583. /**
  10584. * Returns the size of an <ng-container>, using either the information
  10585. * serialized in `ELEMENT_CONTAINERS` (element container size) or by
  10586. * computing the sum of root nodes in all dehydrated views in a given
  10587. * container (in case this `<ng-container>` was also used as a view
  10588. * container host node, e.g. <ng-container *ngIf>).
  10589. */
  10590. function getNgContainerSize(hydrationInfo, index) {
  10591. const data = hydrationInfo.data;
  10592. let size = data[ELEMENT_CONTAINERS]?.[index] ?? null;
  10593. // If there is no serialized information available in the `ELEMENT_CONTAINERS` slot,
  10594. // check if we have info about view containers at this location (e.g.
  10595. // `<ng-container *ngIf>`) and use container size as a number of root nodes in this
  10596. // element container.
  10597. if (size === null && data[CONTAINERS]?.[index]) {
  10598. size = calcSerializedContainerSize(hydrationInfo, index);
  10599. }
  10600. return size;
  10601. }
  10602. function getSerializedContainerViews(hydrationInfo, index) {
  10603. return hydrationInfo.data[CONTAINERS]?.[index] ?? null;
  10604. }
  10605. /**
  10606. * Computes the size of a serialized container (the number of root nodes)
  10607. * by calculating the sum of root nodes in all dehydrated views in this container.
  10608. */
  10609. function calcSerializedContainerSize(hydrationInfo, index) {
  10610. const views = getSerializedContainerViews(hydrationInfo, index) ?? [];
  10611. let numNodes = 0;
  10612. for (let view of views) {
  10613. numNodes += view[NUM_ROOT_NODES] * (view[MULTIPLIER] ?? 1);
  10614. }
  10615. return numNodes;
  10616. }
  10617. /**
  10618. * Checks whether a node is annotated as "disconnected", i.e. not present
  10619. * in the DOM at serialization time. We should not attempt hydration for
  10620. * such nodes and instead, use a regular "creation mode".
  10621. */
  10622. function isDisconnectedNode(hydrationInfo, index) {
  10623. // Check if we are processing disconnected info for the first time.
  10624. if (typeof hydrationInfo.disconnectedNodes === 'undefined') {
  10625. const nodeIds = hydrationInfo.data[DISCONNECTED_NODES];
  10626. hydrationInfo.disconnectedNodes = nodeIds ? (new Set(nodeIds)) : null;
  10627. }
  10628. return !!hydrationInfo.disconnectedNodes?.has(index);
  10629. }
  10630. /**
  10631. * Represents a component created by a `ComponentFactory`.
  10632. * Provides access to the component instance and related objects,
  10633. * and provides the means of destroying the instance.
  10634. *
  10635. * @publicApi
  10636. */
  10637. class ComponentRef$1 {
  10638. }
  10639. /**
  10640. * Base class for a factory that can create a component dynamically.
  10641. * Instantiate a factory for a given type of component with `resolveComponentFactory()`.
  10642. * Use the resulting `ComponentFactory.create()` method to create a component of that type.
  10643. *
  10644. * @see [Dynamic Components](guide/dynamic-component-loader)
  10645. *
  10646. * @publicApi
  10647. *
  10648. * @deprecated Angular no longer requires Component factories. Please use other APIs where
  10649. * Component class can be used directly.
  10650. */
  10651. class ComponentFactory$1 {
  10652. }
  10653. function noComponentFactoryError(component) {
  10654. const error = Error(`No component factory found for ${stringify(component)}.`);
  10655. error[ERROR_COMPONENT] = component;
  10656. return error;
  10657. }
  10658. const ERROR_COMPONENT = 'ngComponent';
  10659. function getComponent$1(error) {
  10660. return error[ERROR_COMPONENT];
  10661. }
  10662. class _NullComponentFactoryResolver {
  10663. resolveComponentFactory(component) {
  10664. throw noComponentFactoryError(component);
  10665. }
  10666. }
  10667. /**
  10668. * A simple registry that maps `Components` to generated `ComponentFactory` classes
  10669. * that can be used to create instances of components.
  10670. * Use to obtain the factory for a given component type,
  10671. * then use the factory's `create()` method to create a component of that type.
  10672. *
  10673. * Note: since v13, dynamic component creation via
  10674. * [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent)
  10675. * does **not** require resolving component factory: component class can be used directly.
  10676. *
  10677. * @publicApi
  10678. *
  10679. * @deprecated Angular no longer requires Component factories. Please use other APIs where
  10680. * Component class can be used directly.
  10681. */
  10682. class ComponentFactoryResolver$1 {
  10683. static { this.NULL = ( /* @__PURE__ */new _NullComponentFactoryResolver()); }
  10684. }
  10685. /**
  10686. * Creates an ElementRef from the most recent node.
  10687. *
  10688. * @returns The ElementRef instance to use
  10689. */
  10690. function injectElementRef() {
  10691. return createElementRef(getCurrentTNode(), getLView());
  10692. }
  10693. /**
  10694. * Creates an ElementRef given a node.
  10695. *
  10696. * @param tNode The node for which you'd like an ElementRef
  10697. * @param lView The view to which the node belongs
  10698. * @returns The ElementRef instance to use
  10699. */
  10700. function createElementRef(tNode, lView) {
  10701. return new ElementRef(getNativeByTNode(tNode, lView));
  10702. }
  10703. /**
  10704. * A wrapper around a native element inside of a View.
  10705. *
  10706. * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
  10707. * element.
  10708. *
  10709. * @security Permitting direct access to the DOM can make your application more vulnerable to
  10710. * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
  10711. * [Security Guide](https://g.co/ng/security).
  10712. *
  10713. * @publicApi
  10714. */
  10715. // Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
  10716. // i.e. users have to ask for what they need. With that, we can build better analysis tools
  10717. // and could do better codegen in the future.
  10718. class ElementRef {
  10719. constructor(nativeElement) {
  10720. this.nativeElement = nativeElement;
  10721. }
  10722. /**
  10723. * @internal
  10724. * @nocollapse
  10725. */
  10726. static { this.__NG_ELEMENT_ID__ = injectElementRef; }
  10727. }
  10728. /**
  10729. * Unwraps `ElementRef` and return the `nativeElement`.
  10730. *
  10731. * @param value value to unwrap
  10732. * @returns `nativeElement` if `ElementRef` otherwise returns value as is.
  10733. */
  10734. function unwrapElementRef(value) {
  10735. return value instanceof ElementRef ? value.nativeElement : value;
  10736. }
  10737. /**
  10738. * Creates and initializes a custom renderer that implements the `Renderer2` base class.
  10739. *
  10740. * @publicApi
  10741. */
  10742. class RendererFactory2 {
  10743. }
  10744. /**
  10745. * Extend this base class to implement custom rendering. By default, Angular
  10746. * renders a template into DOM. You can use custom rendering to intercept
  10747. * rendering calls, or to render to something other than DOM.
  10748. *
  10749. * Create your custom renderer using `RendererFactory2`.
  10750. *
  10751. * Use a custom renderer to bypass Angular's templating and
  10752. * make custom UI changes that can't be expressed declaratively.
  10753. * For example if you need to set a property or an attribute whose name is
  10754. * not statically known, use the `setProperty()` or
  10755. * `setAttribute()` method.
  10756. *
  10757. * @publicApi
  10758. */
  10759. class Renderer2 {
  10760. constructor() {
  10761. /**
  10762. * If null or undefined, the view engine won't call it.
  10763. * This is used as a performance optimization for production mode.
  10764. */
  10765. this.destroyNode = null;
  10766. }
  10767. /**
  10768. * @internal
  10769. * @nocollapse
  10770. */
  10771. static { this.__NG_ELEMENT_ID__ = () => injectRenderer2(); }
  10772. }
  10773. /** Injects a Renderer2 for the current component. */
  10774. function injectRenderer2() {
  10775. // We need the Renderer to be based on the component that it's being injected into, however since
  10776. // DI happens before we've entered its view, `getLView` will return the parent view instead.
  10777. const lView = getLView();
  10778. const tNode = getCurrentTNode();
  10779. const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
  10780. return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
  10781. }
  10782. /**
  10783. * Sanitizer is used by the views to sanitize potentially dangerous values.
  10784. *
  10785. * @publicApi
  10786. */
  10787. class Sanitizer {
  10788. /** @nocollapse */
  10789. static { this.ɵprov = ɵɵdefineInjectable({
  10790. token: Sanitizer,
  10791. providedIn: 'root',
  10792. factory: () => null,
  10793. }); }
  10794. }
  10795. /**
  10796. * @description Represents the version of Angular
  10797. *
  10798. * @publicApi
  10799. */
  10800. class Version {
  10801. constructor(full) {
  10802. this.full = full;
  10803. this.major = full.split('.')[0];
  10804. this.minor = full.split('.')[1];
  10805. this.patch = full.split('.').slice(2).join('.');
  10806. }
  10807. }
  10808. /**
  10809. * @publicApi
  10810. */
  10811. const VERSION = new Version('16.2.9');
  10812. // This default value is when checking the hierarchy for a token.
  10813. //
  10814. // It means both:
  10815. // - the token is not provided by the current injector,
  10816. // - only the element injectors should be checked (ie do not check module injectors
  10817. //
  10818. // mod1
  10819. // /
  10820. // el1 mod2
  10821. // \ /
  10822. // el2
  10823. //
  10824. // When requesting el2.injector.get(token), we should check in the following order and return the
  10825. // first found value:
  10826. // - el2.injector.get(token, default)
  10827. // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
  10828. // - mod2.injector.get(token, default)
  10829. const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
  10830. const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
  10831. function wrappedError(message, originalError) {
  10832. const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
  10833. const error = Error(msg);
  10834. error[ERROR_ORIGINAL_ERROR] = originalError;
  10835. return error;
  10836. }
  10837. function getOriginalError(error) {
  10838. return error[ERROR_ORIGINAL_ERROR];
  10839. }
  10840. /**
  10841. * Provides a hook for centralized exception handling.
  10842. *
  10843. * The default implementation of `ErrorHandler` prints error messages to the `console`. To
  10844. * intercept error handling, write a custom exception handler that replaces this default as
  10845. * appropriate for your app.
  10846. *
  10847. * @usageNotes
  10848. * ### Example
  10849. *
  10850. * ```
  10851. * class MyErrorHandler implements ErrorHandler {
  10852. * handleError(error) {
  10853. * // do something with the exception
  10854. * }
  10855. * }
  10856. *
  10857. * @NgModule({
  10858. * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
  10859. * })
  10860. * class MyModule {}
  10861. * ```
  10862. *
  10863. * @publicApi
  10864. */
  10865. class ErrorHandler {
  10866. constructor() {
  10867. /**
  10868. * @internal
  10869. */
  10870. this._console = console;
  10871. }
  10872. handleError(error) {
  10873. const originalError = this._findOriginalError(error);
  10874. this._console.error('ERROR', error);
  10875. if (originalError) {
  10876. this._console.error('ORIGINAL ERROR', originalError);
  10877. }
  10878. }
  10879. /** @internal */
  10880. _findOriginalError(error) {
  10881. let e = error && getOriginalError(error);
  10882. while (e && getOriginalError(e)) {
  10883. e = getOriginalError(e);
  10884. }
  10885. return e || null;
  10886. }
  10887. }
  10888. /**
  10889. * `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
  10890. * The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
  10891. * is injected in a component or directive, the callbacks run when that component or
  10892. * directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.
  10893. *
  10894. * @publicApi
  10895. */
  10896. class DestroyRef {
  10897. /**
  10898. * @internal
  10899. * @nocollapse
  10900. */
  10901. static { this.__NG_ELEMENT_ID__ = injectDestroyRef; }
  10902. /**
  10903. * @internal
  10904. * @nocollapse
  10905. */
  10906. static { this.__NG_ENV_ID__ = (injector) => injector; }
  10907. }
  10908. class NodeInjectorDestroyRef extends DestroyRef {
  10909. constructor(_lView) {
  10910. super();
  10911. this._lView = _lView;
  10912. }
  10913. onDestroy(callback) {
  10914. storeLViewOnDestroy(this._lView, callback);
  10915. return () => removeLViewOnDestroy(this._lView, callback);
  10916. }
  10917. }
  10918. function injectDestroyRef() {
  10919. return new NodeInjectorDestroyRef(getLView());
  10920. }
  10921. /// <reference types="rxjs" />
  10922. class EventEmitter_ extends Subject {
  10923. constructor(isAsync = false) {
  10924. super();
  10925. this.__isAsync = isAsync;
  10926. }
  10927. emit(value) {
  10928. super.next(value);
  10929. }
  10930. subscribe(observerOrNext, error, complete) {
  10931. let nextFn = observerOrNext;
  10932. let errorFn = error || (() => null);
  10933. let completeFn = complete;
  10934. if (observerOrNext && typeof observerOrNext === 'object') {
  10935. const observer = observerOrNext;
  10936. nextFn = observer.next?.bind(observer);
  10937. errorFn = observer.error?.bind(observer);
  10938. completeFn = observer.complete?.bind(observer);
  10939. }
  10940. if (this.__isAsync) {
  10941. errorFn = _wrapInTimeout(errorFn);
  10942. if (nextFn) {
  10943. nextFn = _wrapInTimeout(nextFn);
  10944. }
  10945. if (completeFn) {
  10946. completeFn = _wrapInTimeout(completeFn);
  10947. }
  10948. }
  10949. const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn });
  10950. if (observerOrNext instanceof Subscription) {
  10951. observerOrNext.add(sink);
  10952. }
  10953. return sink;
  10954. }
  10955. }
  10956. function _wrapInTimeout(fn) {
  10957. return (value) => {
  10958. setTimeout(fn, undefined, value);
  10959. };
  10960. }
  10961. /**
  10962. * @publicApi
  10963. */
  10964. const EventEmitter = EventEmitter_;
  10965. function noop(...args) {
  10966. // Do nothing.
  10967. }
  10968. function getNativeRequestAnimationFrame() {
  10969. // Note: the `getNativeRequestAnimationFrame` is used in the `NgZone` class, but we cannot use the
  10970. // `inject` function. The `NgZone` instance may be created manually, and thus the injection
  10971. // context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
  10972. // available because otherwise, we'll fall back to `setTimeout`.
  10973. const isBrowser = typeof _global['requestAnimationFrame'] === 'function';
  10974. // Note: `requestAnimationFrame` is unavailable when the code runs in the Node.js environment. We
  10975. // use `setTimeout` because no changes are required other than checking if the current platform is
  10976. // the browser. `setTimeout` is a well-established API that is available in both environments.
  10977. // `requestAnimationFrame` is used in the browser to coalesce event tasks since event tasks are
  10978. // usually executed within the same rendering frame (but this is more implementation details of
  10979. // browsers).
  10980. let nativeRequestAnimationFrame = _global[isBrowser ? 'requestAnimationFrame' : 'setTimeout'];
  10981. let nativeCancelAnimationFrame = _global[isBrowser ? 'cancelAnimationFrame' : 'clearTimeout'];
  10982. if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
  10983. // Note: zone.js sets original implementations on patched APIs behind the
  10984. // `__zone_symbol__OriginalDelegate` key (see `attachOriginToPatched`). Given the following
  10985. // example: `window.requestAnimationFrame.__zone_symbol__OriginalDelegate`; this would return an
  10986. // unpatched implementation of the `requestAnimationFrame`, which isn't intercepted by the
  10987. // Angular zone. We use the unpatched implementation to avoid another change detection when
  10988. // coalescing tasks.
  10989. const unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__('OriginalDelegate')];
  10990. if (unpatchedRequestAnimationFrame) {
  10991. nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
  10992. }
  10993. const unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__('OriginalDelegate')];
  10994. if (unpatchedCancelAnimationFrame) {
  10995. nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
  10996. }
  10997. }
  10998. return { nativeRequestAnimationFrame, nativeCancelAnimationFrame };
  10999. }
  11000. class AsyncStackTaggingZoneSpec {
  11001. constructor(namePrefix, consoleAsyncStackTaggingImpl = console) {
  11002. this.name = 'asyncStackTagging for ' + namePrefix;
  11003. this.createTask = consoleAsyncStackTaggingImpl?.createTask ?? (() => null);
  11004. }
  11005. onScheduleTask(delegate, _current, target, task) {
  11006. task.consoleTask = this.createTask(`Zone - ${task.source || task.type}`);
  11007. return delegate.scheduleTask(target, task);
  11008. }
  11009. onInvokeTask(delegate, _currentZone, targetZone, task, applyThis, applyArgs) {
  11010. let ret;
  11011. if (task.consoleTask) {
  11012. ret = task.consoleTask.run(() => delegate.invokeTask(targetZone, task, applyThis, applyArgs));
  11013. }
  11014. else {
  11015. ret = delegate.invokeTask(targetZone, task, applyThis, applyArgs);
  11016. }
  11017. return ret;
  11018. }
  11019. }
  11020. /**
  11021. * An injectable service for executing work inside or outside of the Angular zone.
  11022. *
  11023. * The most common use of this service is to optimize performance when starting a work consisting of
  11024. * one or more asynchronous tasks that don't require UI updates or error handling to be handled by
  11025. * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
  11026. * can reenter the Angular zone via {@link #run}.
  11027. *
  11028. * <!-- TODO: add/fix links to:
  11029. * - docs explaining zones and the use of zones in Angular and change-detection
  11030. * - link to runOutsideAngular/run (throughout this file!)
  11031. * -->
  11032. *
  11033. * @usageNotes
  11034. * ### Example
  11035. *
  11036. * ```
  11037. * import {Component, NgZone} from '@angular/core';
  11038. * import {NgIf} from '@angular/common';
  11039. *
  11040. * @Component({
  11041. * selector: 'ng-zone-demo',
  11042. * template: `
  11043. * <h2>Demo: NgZone</h2>
  11044. *
  11045. * <p>Progress: {{progress}}%</p>
  11046. * <p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p>
  11047. *
  11048. * <button (click)="processWithinAngularZone()">Process within Angular zone</button>
  11049. * <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
  11050. * `,
  11051. * })
  11052. * export class NgZoneDemo {
  11053. * progress: number = 0;
  11054. * label: string;
  11055. *
  11056. * constructor(private _ngZone: NgZone) {}
  11057. *
  11058. * // Loop inside the Angular zone
  11059. * // so the UI DOES refresh after each setTimeout cycle
  11060. * processWithinAngularZone() {
  11061. * this.label = 'inside';
  11062. * this.progress = 0;
  11063. * this._increaseProgress(() => console.log('Inside Done!'));
  11064. * }
  11065. *
  11066. * // Loop outside of the Angular zone
  11067. * // so the UI DOES NOT refresh after each setTimeout cycle
  11068. * processOutsideOfAngularZone() {
  11069. * this.label = 'outside';
  11070. * this.progress = 0;
  11071. * this._ngZone.runOutsideAngular(() => {
  11072. * this._increaseProgress(() => {
  11073. * // reenter the Angular zone and display done
  11074. * this._ngZone.run(() => { console.log('Outside Done!'); });
  11075. * });
  11076. * });
  11077. * }
  11078. *
  11079. * _increaseProgress(doneCallback: () => void) {
  11080. * this.progress += 1;
  11081. * console.log(`Current progress: ${this.progress}%`);
  11082. *
  11083. * if (this.progress < 100) {
  11084. * window.setTimeout(() => this._increaseProgress(doneCallback), 10);
  11085. * } else {
  11086. * doneCallback();
  11087. * }
  11088. * }
  11089. * }
  11090. * ```
  11091. *
  11092. * @publicApi
  11093. */
  11094. class NgZone {
  11095. constructor({ enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false }) {
  11096. this.hasPendingMacrotasks = false;
  11097. this.hasPendingMicrotasks = false;
  11098. /**
  11099. * Whether there are no outstanding microtasks or macrotasks.
  11100. */
  11101. this.isStable = true;
  11102. /**
  11103. * Notifies when code enters Angular Zone. This gets fired first on VM Turn.
  11104. */
  11105. this.onUnstable = new EventEmitter(false);
  11106. /**
  11107. * Notifies when there is no more microtasks enqueued in the current VM Turn.
  11108. * This is a hint for Angular to do change detection, which may enqueue more microtasks.
  11109. * For this reason this event can fire multiple times per VM Turn.
  11110. */
  11111. this.onMicrotaskEmpty = new EventEmitter(false);
  11112. /**
  11113. * Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
  11114. * implies we are about to relinquish VM turn.
  11115. * This event gets called just once.
  11116. */
  11117. this.onStable = new EventEmitter(false);
  11118. /**
  11119. * Notifies that an error has been delivered.
  11120. */
  11121. this.onError = new EventEmitter(false);
  11122. if (typeof Zone == 'undefined') {
  11123. throw new RuntimeError(908 /* RuntimeErrorCode.MISSING_ZONEJS */, ngDevMode && `In this configuration Angular requires Zone.js`);
  11124. }
  11125. Zone.assertZonePatched();
  11126. const self = this;
  11127. self._nesting = 0;
  11128. self._outer = self._inner = Zone.current;
  11129. // AsyncStackTaggingZoneSpec provides `linked stack traces` to show
  11130. // where the async operation is scheduled. For more details, refer
  11131. // to this article, https://developer.chrome.com/blog/devtools-better-angular-debugging/
  11132. // And we only import this AsyncStackTaggingZoneSpec in development mode,
  11133. // in the production mode, the AsyncStackTaggingZoneSpec will be tree shaken away.
  11134. if (ngDevMode) {
  11135. self._inner = self._inner.fork(new AsyncStackTaggingZoneSpec('Angular'));
  11136. }
  11137. if (Zone['TaskTrackingZoneSpec']) {
  11138. self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']);
  11139. }
  11140. if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) {
  11141. self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']);
  11142. }
  11143. // if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be
  11144. // coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped.
  11145. self.shouldCoalesceEventChangeDetection =
  11146. !shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
  11147. self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
  11148. self.lastRequestAnimationFrameId = -1;
  11149. self.nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame;
  11150. forkInnerZoneWithAngularBehavior(self);
  11151. }
  11152. /**
  11153. This method checks whether the method call happens within an Angular Zone instance.
  11154. */
  11155. static isInAngularZone() {
  11156. // Zone needs to be checked, because this method might be called even when NoopNgZone is used.
  11157. return typeof Zone !== 'undefined' && Zone.current.get('isAngularZone') === true;
  11158. }
  11159. /**
  11160. Assures that the method is called within the Angular Zone, otherwise throws an error.
  11161. */
  11162. static assertInAngularZone() {
  11163. if (!NgZone.isInAngularZone()) {
  11164. throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to be in Angular Zone, but it is not!');
  11165. }
  11166. }
  11167. /**
  11168. Assures that the method is called outside of the Angular Zone, otherwise throws an error.
  11169. */
  11170. static assertNotInAngularZone() {
  11171. if (NgZone.isInAngularZone()) {
  11172. throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to not be in Angular Zone, but it is!');
  11173. }
  11174. }
  11175. /**
  11176. * Executes the `fn` function synchronously within the Angular zone and returns value returned by
  11177. * the function.
  11178. *
  11179. * Running functions via `run` allows you to reenter Angular zone from a task that was executed
  11180. * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
  11181. *
  11182. * Any future tasks or microtasks scheduled from within this function will continue executing from
  11183. * within the Angular zone.
  11184. *
  11185. * If a synchronous error happens it will be rethrown and not reported via `onError`.
  11186. */
  11187. run(fn, applyThis, applyArgs) {
  11188. return this._inner.run(fn, applyThis, applyArgs);
  11189. }
  11190. /**
  11191. * Executes the `fn` function synchronously within the Angular zone as a task and returns value
  11192. * returned by the function.
  11193. *
  11194. * Running functions via `run` allows you to reenter Angular zone from a task that was executed
  11195. * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
  11196. *
  11197. * Any future tasks or microtasks scheduled from within this function will continue executing from
  11198. * within the Angular zone.
  11199. *
  11200. * If a synchronous error happens it will be rethrown and not reported via `onError`.
  11201. */
  11202. runTask(fn, applyThis, applyArgs, name) {
  11203. const zone = this._inner;
  11204. const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop);
  11205. try {
  11206. return zone.runTask(task, applyThis, applyArgs);
  11207. }
  11208. finally {
  11209. zone.cancelTask(task);
  11210. }
  11211. }
  11212. /**
  11213. * Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not
  11214. * rethrown.
  11215. */
  11216. runGuarded(fn, applyThis, applyArgs) {
  11217. return this._inner.runGuarded(fn, applyThis, applyArgs);
  11218. }
  11219. /**
  11220. * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
  11221. * the function.
  11222. *
  11223. * Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do
  11224. * work that
  11225. * doesn't trigger Angular change-detection or is subject to Angular's error handling.
  11226. *
  11227. * Any future tasks or microtasks scheduled from within this function will continue executing from
  11228. * outside of the Angular zone.
  11229. *
  11230. * Use {@link #run} to reenter the Angular zone and do work that updates the application model.
  11231. */
  11232. runOutsideAngular(fn) {
  11233. return this._outer.run(fn);
  11234. }
  11235. }
  11236. const EMPTY_PAYLOAD = {};
  11237. function checkStable(zone) {
  11238. // TODO: @JiaLiPassion, should check zone.isCheckStableRunning to prevent
  11239. // re-entry. The case is:
  11240. //
  11241. // @Component({...})
  11242. // export class AppComponent {
  11243. // constructor(private ngZone: NgZone) {
  11244. // this.ngZone.onStable.subscribe(() => {
  11245. // this.ngZone.run(() => console.log('stable'););
  11246. // });
  11247. // }
  11248. //
  11249. // The onStable subscriber run another function inside ngZone
  11250. // which causes `checkStable()` re-entry.
  11251. // But this fix causes some issues in g3, so this fix will be
  11252. // launched in another PR.
  11253. if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) {
  11254. try {
  11255. zone._nesting++;
  11256. zone.onMicrotaskEmpty.emit(null);
  11257. }
  11258. finally {
  11259. zone._nesting--;
  11260. if (!zone.hasPendingMicrotasks) {
  11261. try {
  11262. zone.runOutsideAngular(() => zone.onStable.emit(null));
  11263. }
  11264. finally {
  11265. zone.isStable = true;
  11266. }
  11267. }
  11268. }
  11269. }
  11270. }
  11271. function delayChangeDetectionForEvents(zone) {
  11272. /**
  11273. * We also need to check _nesting here
  11274. * Consider the following case with shouldCoalesceRunChangeDetection = true
  11275. *
  11276. * ngZone.run(() => {});
  11277. * ngZone.run(() => {});
  11278. *
  11279. * We want the two `ngZone.run()` only trigger one change detection
  11280. * when shouldCoalesceRunChangeDetection is true.
  11281. * And because in this case, change detection run in async way(requestAnimationFrame),
  11282. * so we also need to check the _nesting here to prevent multiple
  11283. * change detections.
  11284. */
  11285. if (zone.isCheckStableRunning || zone.lastRequestAnimationFrameId !== -1) {
  11286. return;
  11287. }
  11288. zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(_global, () => {
  11289. // This is a work around for https://github.com/angular/angular/issues/36839.
  11290. // The core issue is that when event coalescing is enabled it is possible for microtasks
  11291. // to get flushed too early (As is the case with `Promise.then`) between the
  11292. // coalescing eventTasks.
  11293. //
  11294. // To workaround this we schedule a "fake" eventTask before we process the
  11295. // coalescing eventTasks. The benefit of this is that the "fake" container eventTask
  11296. // will prevent the microtasks queue from getting drained in between the coalescing
  11297. // eventTask execution.
  11298. if (!zone.fakeTopEventTask) {
  11299. zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
  11300. zone.lastRequestAnimationFrameId = -1;
  11301. updateMicroTaskStatus(zone);
  11302. zone.isCheckStableRunning = true;
  11303. checkStable(zone);
  11304. zone.isCheckStableRunning = false;
  11305. }, undefined, () => { }, () => { });
  11306. }
  11307. zone.fakeTopEventTask.invoke();
  11308. });
  11309. updateMicroTaskStatus(zone);
  11310. }
  11311. function forkInnerZoneWithAngularBehavior(zone) {
  11312. const delayChangeDetectionForEventsDelegate = () => {
  11313. delayChangeDetectionForEvents(zone);
  11314. };
  11315. zone._inner = zone._inner.fork({
  11316. name: 'angular',
  11317. properties: { 'isAngularZone': true },
  11318. onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
  11319. if (shouldBeIgnoredByZone(applyArgs)) {
  11320. return delegate.invokeTask(target, task, applyThis, applyArgs);
  11321. }
  11322. try {
  11323. onEnter(zone);
  11324. return delegate.invokeTask(target, task, applyThis, applyArgs);
  11325. }
  11326. finally {
  11327. if ((zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask') ||
  11328. zone.shouldCoalesceRunChangeDetection) {
  11329. delayChangeDetectionForEventsDelegate();
  11330. }
  11331. onLeave(zone);
  11332. }
  11333. },
  11334. onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => {
  11335. try {
  11336. onEnter(zone);
  11337. return delegate.invoke(target, callback, applyThis, applyArgs, source);
  11338. }
  11339. finally {
  11340. if (zone.shouldCoalesceRunChangeDetection) {
  11341. delayChangeDetectionForEventsDelegate();
  11342. }
  11343. onLeave(zone);
  11344. }
  11345. },
  11346. onHasTask: (delegate, current, target, hasTaskState) => {
  11347. delegate.hasTask(target, hasTaskState);
  11348. if (current === target) {
  11349. // We are only interested in hasTask events which originate from our zone
  11350. // (A child hasTask event is not interesting to us)
  11351. if (hasTaskState.change == 'microTask') {
  11352. zone._hasPendingMicrotasks = hasTaskState.microTask;
  11353. updateMicroTaskStatus(zone);
  11354. checkStable(zone);
  11355. }
  11356. else if (hasTaskState.change == 'macroTask') {
  11357. zone.hasPendingMacrotasks = hasTaskState.macroTask;
  11358. }
  11359. }
  11360. },
  11361. onHandleError: (delegate, current, target, error) => {
  11362. delegate.handleError(target, error);
  11363. zone.runOutsideAngular(() => zone.onError.emit(error));
  11364. return false;
  11365. }
  11366. });
  11367. }
  11368. function updateMicroTaskStatus(zone) {
  11369. if (zone._hasPendingMicrotasks ||
  11370. ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
  11371. zone.lastRequestAnimationFrameId !== -1)) {
  11372. zone.hasPendingMicrotasks = true;
  11373. }
  11374. else {
  11375. zone.hasPendingMicrotasks = false;
  11376. }
  11377. }
  11378. function onEnter(zone) {
  11379. zone._nesting++;
  11380. if (zone.isStable) {
  11381. zone.isStable = false;
  11382. zone.onUnstable.emit(null);
  11383. }
  11384. }
  11385. function onLeave(zone) {
  11386. zone._nesting--;
  11387. checkStable(zone);
  11388. }
  11389. /**
  11390. * Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls
  11391. * to framework to perform rendering.
  11392. */
  11393. class NoopNgZone {
  11394. constructor() {
  11395. this.hasPendingMicrotasks = false;
  11396. this.hasPendingMacrotasks = false;
  11397. this.isStable = true;
  11398. this.onUnstable = new EventEmitter();
  11399. this.onMicrotaskEmpty = new EventEmitter();
  11400. this.onStable = new EventEmitter();
  11401. this.onError = new EventEmitter();
  11402. }
  11403. run(fn, applyThis, applyArgs) {
  11404. return fn.apply(applyThis, applyArgs);
  11405. }
  11406. runGuarded(fn, applyThis, applyArgs) {
  11407. return fn.apply(applyThis, applyArgs);
  11408. }
  11409. runOutsideAngular(fn) {
  11410. return fn();
  11411. }
  11412. runTask(fn, applyThis, applyArgs, name) {
  11413. return fn.apply(applyThis, applyArgs);
  11414. }
  11415. }
  11416. /**
  11417. * Token used to drive ApplicationRef.isStable
  11418. *
  11419. * TODO: This should be moved entirely to NgZone (as a breaking change) so it can be tree-shakeable
  11420. * for `NoopNgZone` which is always just an `Observable` of `true`. Additionally, we should consider
  11421. * whether the property on `NgZone` should be `Observable` or `Signal`.
  11422. */
  11423. const ZONE_IS_STABLE_OBSERVABLE = new InjectionToken(ngDevMode ? 'isStable Observable' : '', {
  11424. providedIn: 'root',
  11425. // TODO(atscott): Replace this with a suitable default like `new
  11426. // BehaviorSubject(true).asObservable`. Again, long term this won't exist on ApplicationRef at
  11427. // all but until we can remove it, we need a default value zoneless.
  11428. factory: isStableFactory,
  11429. });
  11430. function isStableFactory() {
  11431. const zone = inject$1(NgZone);
  11432. let _stable = true;
  11433. const isCurrentlyStable = new Observable((observer) => {
  11434. _stable = zone.isStable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks;
  11435. zone.runOutsideAngular(() => {
  11436. observer.next(_stable);
  11437. observer.complete();
  11438. });
  11439. });
  11440. const isStable = new Observable((observer) => {
  11441. // Create the subscription to onStable outside the Angular Zone so that
  11442. // the callback is run outside the Angular Zone.
  11443. let stableSub;
  11444. zone.runOutsideAngular(() => {
  11445. stableSub = zone.onStable.subscribe(() => {
  11446. NgZone.assertNotInAngularZone();
  11447. // Check whether there are no pending macro/micro tasks in the next tick
  11448. // to allow for NgZone to update the state.
  11449. queueMicrotask(() => {
  11450. if (!_stable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks) {
  11451. _stable = true;
  11452. observer.next(true);
  11453. }
  11454. });
  11455. });
  11456. });
  11457. const unstableSub = zone.onUnstable.subscribe(() => {
  11458. NgZone.assertInAngularZone();
  11459. if (_stable) {
  11460. _stable = false;
  11461. zone.runOutsideAngular(() => {
  11462. observer.next(false);
  11463. });
  11464. }
  11465. });
  11466. return () => {
  11467. stableSub.unsubscribe();
  11468. unstableSub.unsubscribe();
  11469. };
  11470. });
  11471. return merge$1(isCurrentlyStable, isStable.pipe(share()));
  11472. }
  11473. function shouldBeIgnoredByZone(applyArgs) {
  11474. if (!Array.isArray(applyArgs)) {
  11475. return false;
  11476. }
  11477. // We should only ever get 1 arg passed through to invokeTask.
  11478. // Short circuit here incase that behavior changes.
  11479. if (applyArgs.length !== 1) {
  11480. return false;
  11481. }
  11482. // Prevent triggering change detection when the __ignore_ng_zone__ flag is detected.
  11483. return applyArgs[0].data?.['__ignore_ng_zone__'] === true;
  11484. }
  11485. // Public API for Zone
  11486. /**
  11487. * Register a callback to be invoked each time the application
  11488. * finishes rendering.
  11489. *
  11490. * Note that the callback will run
  11491. * - in the order it was registered
  11492. * - once per render
  11493. * - on browser platforms only
  11494. *
  11495. * <div class="alert is-important">
  11496. *
  11497. * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.
  11498. * You must use caution when directly reading or writing the DOM and layout.
  11499. *
  11500. * </div>
  11501. *
  11502. * @param callback A callback function to register
  11503. *
  11504. * @usageNotes
  11505. *
  11506. * Use `afterRender` to read or write the DOM after each render.
  11507. *
  11508. * ### Example
  11509. * ```ts
  11510. * @Component({
  11511. * selector: 'my-cmp',
  11512. * template: `<span #content>{{ ... }}</span>`,
  11513. * })
  11514. * export class MyComponent {
  11515. * @ViewChild('content') contentRef: ElementRef;
  11516. *
  11517. * constructor() {
  11518. * afterRender(() => {
  11519. * console.log('content height: ' + this.contentRef.nativeElement.scrollHeight);
  11520. * });
  11521. * }
  11522. * }
  11523. * ```
  11524. *
  11525. * @developerPreview
  11526. */
  11527. function afterRender(callback, options) {
  11528. !options && assertInInjectionContext(afterRender);
  11529. const injector = options?.injector ?? inject$1(Injector);
  11530. if (!isPlatformBrowser(injector)) {
  11531. return { destroy() { } };
  11532. }
  11533. let destroy;
  11534. const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
  11535. const afterRenderEventManager = injector.get(AfterRenderEventManager);
  11536. // Lazily initialize the handler implementation, if necessary. This is so that it can be
  11537. // tree-shaken if `afterRender` and `afterNextRender` aren't used.
  11538. const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
  11539. const ngZone = injector.get(NgZone);
  11540. const errorHandler = injector.get(ErrorHandler, null, { optional: true });
  11541. const instance = new AfterRenderCallback(ngZone, errorHandler, callback);
  11542. destroy = () => {
  11543. callbackHandler.unregister(instance);
  11544. unregisterFn();
  11545. };
  11546. callbackHandler.register(instance);
  11547. return { destroy };
  11548. }
  11549. /**
  11550. * Register a callback to be invoked the next time the application
  11551. * finishes rendering.
  11552. *
  11553. * Note that the callback will run
  11554. * - in the order it was registered
  11555. * - on browser platforms only
  11556. *
  11557. * <div class="alert is-important">
  11558. *
  11559. * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.
  11560. * You must use caution when directly reading or writing the DOM and layout.
  11561. *
  11562. * </div>
  11563. *
  11564. * @param callback A callback function to register
  11565. *
  11566. * @usageNotes
  11567. *
  11568. * Use `afterNextRender` to read or write the DOM once,
  11569. * for example to initialize a non-Angular library.
  11570. *
  11571. * ### Example
  11572. * ```ts
  11573. * @Component({
  11574. * selector: 'my-chart-cmp',
  11575. * template: `<div #chart>{{ ... }}</div>`,
  11576. * })
  11577. * export class MyChartCmp {
  11578. * @ViewChild('chart') chartRef: ElementRef;
  11579. * chart: MyChart|null;
  11580. *
  11581. * constructor() {
  11582. * afterNextRender(() => {
  11583. * this.chart = new MyChart(this.chartRef.nativeElement);
  11584. * });
  11585. * }
  11586. * }
  11587. * ```
  11588. *
  11589. * @developerPreview
  11590. */
  11591. function afterNextRender(callback, options) {
  11592. !options && assertInInjectionContext(afterNextRender);
  11593. const injector = options?.injector ?? inject$1(Injector);
  11594. if (!isPlatformBrowser(injector)) {
  11595. return { destroy() { } };
  11596. }
  11597. let destroy;
  11598. const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
  11599. const afterRenderEventManager = injector.get(AfterRenderEventManager);
  11600. // Lazily initialize the handler implementation, if necessary. This is so that it can be
  11601. // tree-shaken if `afterRender` and `afterNextRender` aren't used.
  11602. const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
  11603. const ngZone = injector.get(NgZone);
  11604. const errorHandler = injector.get(ErrorHandler, null, { optional: true });
  11605. const instance = new AfterRenderCallback(ngZone, errorHandler, () => {
  11606. destroy?.();
  11607. callback();
  11608. });
  11609. destroy = () => {
  11610. callbackHandler.unregister(instance);
  11611. unregisterFn();
  11612. };
  11613. callbackHandler.register(instance);
  11614. return { destroy };
  11615. }
  11616. /**
  11617. * A wrapper around a function to be used as an after render callback.
  11618. */
  11619. class AfterRenderCallback {
  11620. constructor(zone, errorHandler, callbackFn) {
  11621. this.zone = zone;
  11622. this.errorHandler = errorHandler;
  11623. this.callbackFn = callbackFn;
  11624. }
  11625. invoke() {
  11626. try {
  11627. this.zone.runOutsideAngular(this.callbackFn);
  11628. }
  11629. catch (err) {
  11630. this.errorHandler?.handleError(err);
  11631. }
  11632. }
  11633. }
  11634. /**
  11635. * Core functionality for `afterRender` and `afterNextRender`. Kept separate from
  11636. * `AfterRenderEventManager` for tree-shaking.
  11637. */
  11638. class AfterRenderCallbackHandlerImpl {
  11639. constructor() {
  11640. this.executingCallbacks = false;
  11641. this.callbacks = new Set();
  11642. this.deferredCallbacks = new Set();
  11643. }
  11644. validateBegin() {
  11645. if (this.executingCallbacks) {
  11646. throw new RuntimeError(102 /* RuntimeErrorCode.RECURSIVE_APPLICATION_RENDER */, ngDevMode &&
  11647. 'A new render operation began before the previous operation ended. ' +
  11648. 'Did you trigger change detection from afterRender or afterNextRender?');
  11649. }
  11650. }
  11651. register(callback) {
  11652. // If we're currently running callbacks, new callbacks should be deferred
  11653. // until the next render operation.
  11654. const target = this.executingCallbacks ? this.deferredCallbacks : this.callbacks;
  11655. target.add(callback);
  11656. }
  11657. unregister(callback) {
  11658. this.callbacks.delete(callback);
  11659. this.deferredCallbacks.delete(callback);
  11660. }
  11661. execute() {
  11662. this.executingCallbacks = true;
  11663. for (const callback of this.callbacks) {
  11664. callback.invoke();
  11665. }
  11666. this.executingCallbacks = false;
  11667. for (const callback of this.deferredCallbacks) {
  11668. this.callbacks.add(callback);
  11669. }
  11670. this.deferredCallbacks.clear();
  11671. }
  11672. destroy() {
  11673. this.callbacks.clear();
  11674. this.deferredCallbacks.clear();
  11675. }
  11676. }
  11677. /**
  11678. * Implements core timing for `afterRender` and `afterNextRender` events.
  11679. * Delegates to an optional `AfterRenderCallbackHandler` for implementation.
  11680. */
  11681. class AfterRenderEventManager {
  11682. constructor() {
  11683. this.renderDepth = 0;
  11684. /* @internal */
  11685. this.handler = null;
  11686. }
  11687. /**
  11688. * Mark the beginning of a render operation (i.e. CD cycle).
  11689. * Throws if called while executing callbacks.
  11690. */
  11691. begin() {
  11692. this.handler?.validateBegin();
  11693. this.renderDepth++;
  11694. }
  11695. /**
  11696. * Mark the end of a render operation. Callbacks will be
  11697. * executed if there are no more pending operations.
  11698. */
  11699. end() {
  11700. ngDevMode && assertGreaterThan(this.renderDepth, 0, 'renderDepth must be greater than 0');
  11701. this.renderDepth--;
  11702. if (this.renderDepth === 0) {
  11703. this.handler?.execute();
  11704. }
  11705. }
  11706. ngOnDestroy() {
  11707. this.handler?.destroy();
  11708. this.handler = null;
  11709. }
  11710. /** @nocollapse */
  11711. static { this.ɵprov = ɵɵdefineInjectable({
  11712. token: AfterRenderEventManager,
  11713. providedIn: 'root',
  11714. factory: () => new AfterRenderEventManager(),
  11715. }); }
  11716. }
  11717. /**
  11718. * Marks current view and all ancestors dirty.
  11719. *
  11720. * Returns the root view because it is found as a byproduct of marking the view tree
  11721. * dirty, and can be used by methods that consume markViewDirty() to easily schedule
  11722. * change detection. Otherwise, such methods would need to traverse up the view tree
  11723. * an additional time to get the root view and schedule a tick on it.
  11724. *
  11725. * @param lView The starting LView to mark dirty
  11726. * @returns the root LView
  11727. */
  11728. function markViewDirty(lView) {
  11729. while (lView) {
  11730. lView[FLAGS] |= 64 /* LViewFlags.Dirty */;
  11731. const parent = getLViewParent(lView);
  11732. // Stop traversing up as soon as you find a root view that wasn't attached to any container
  11733. if (isRootView(lView) && !parent) {
  11734. return lView;
  11735. }
  11736. // continue otherwise
  11737. lView = parent;
  11738. }
  11739. return null;
  11740. }
  11741. /**
  11742. * Internal token that specifies whether DOM reuse logic
  11743. * during hydration is enabled.
  11744. */
  11745. const IS_HYDRATION_DOM_REUSE_ENABLED = new InjectionToken((typeof ngDevMode === 'undefined' || !!ngDevMode) ? 'IS_HYDRATION_DOM_REUSE_ENABLED' : '');
  11746. // By default (in client rendering mode), we remove all the contents
  11747. // of the host element and render an application after that.
  11748. const PRESERVE_HOST_CONTENT_DEFAULT = false;
  11749. /**
  11750. * Internal token that indicates whether host element content should be
  11751. * retained during the bootstrap.
  11752. */
  11753. const PRESERVE_HOST_CONTENT = new InjectionToken((typeof ngDevMode === 'undefined' || !!ngDevMode) ? 'PRESERVE_HOST_CONTENT' : '', {
  11754. providedIn: 'root',
  11755. factory: () => PRESERVE_HOST_CONTENT_DEFAULT,
  11756. });
  11757. function normalizeDebugBindingName(name) {
  11758. // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
  11759. name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
  11760. return `ng-reflect-${name}`;
  11761. }
  11762. const CAMEL_CASE_REGEXP = /([A-Z])/g;
  11763. function camelCaseToDashCase(input) {
  11764. return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
  11765. }
  11766. function normalizeDebugBindingValue(value) {
  11767. try {
  11768. // Limit the size of the value as otherwise the DOM just gets polluted.
  11769. return value != null ? value.toString().slice(0, 30) : value;
  11770. }
  11771. catch (e) {
  11772. return '[ERROR] Exception while trying to serialize the value';
  11773. }
  11774. }
  11775. /**
  11776. * The max length of the string representation of a value in an error message
  11777. */
  11778. const VALUE_STRING_LENGTH_LIMIT = 200;
  11779. /** Verifies that a given type is a Standalone Component. */
  11780. function assertStandaloneComponentType(type) {
  11781. assertComponentDef(type);
  11782. const componentDef = getComponentDef$1(type);
  11783. if (!componentDef.standalone) {
  11784. throw new RuntimeError(907 /* RuntimeErrorCode.TYPE_IS_NOT_STANDALONE */, `The ${stringifyForError(type)} component is not marked as standalone, ` +
  11785. `but Angular expects to have a standalone component here. ` +
  11786. `Please make sure the ${stringifyForError(type)} component has ` +
  11787. `the \`standalone: true\` flag in the decorator.`);
  11788. }
  11789. }
  11790. /** Verifies whether a given type is a component */
  11791. function assertComponentDef(type) {
  11792. if (!getComponentDef$1(type)) {
  11793. throw new RuntimeError(906 /* RuntimeErrorCode.MISSING_GENERATED_DEF */, `The ${stringifyForError(type)} is not an Angular component, ` +
  11794. `make sure it has the \`@Component\` decorator.`);
  11795. }
  11796. }
  11797. /** Called when there are multiple component selectors that match a given node */
  11798. function throwMultipleComponentError(tNode, first, second) {
  11799. throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` +
  11800. `${stringifyForError(first)} and ` +
  11801. `${stringifyForError(second)}`);
  11802. }
  11803. /** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */
  11804. function throwErrorIfNoChangesMode(creationMode, oldValue, currValue, propName, lView) {
  11805. const hostComponentDef = getDeclarationComponentDef(lView);
  11806. const componentClassName = hostComponentDef?.type?.name;
  11807. const field = propName ? ` for '${propName}'` : '';
  11808. let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${formatValue(oldValue)}'. Current value: '${formatValue(currValue)}'.${componentClassName ? ` Expression location: ${componentClassName} component` : ''}`;
  11809. if (creationMode) {
  11810. msg +=
  11811. ` It seems like the view has been created after its parent and its children have been dirty checked.` +
  11812. ` Has it been created in a change detection hook?`;
  11813. }
  11814. throw new RuntimeError(-100 /* RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED */, msg);
  11815. }
  11816. function formatValue(value) {
  11817. let strValue = String(value);
  11818. // JSON.stringify will throw on circular references
  11819. try {
  11820. if (Array.isArray(value) || strValue === '[object Object]') {
  11821. strValue = JSON.stringify(value);
  11822. }
  11823. }
  11824. catch (error) {
  11825. }
  11826. return strValue.length > VALUE_STRING_LENGTH_LIMIT ?
  11827. (strValue.substring(0, VALUE_STRING_LENGTH_LIMIT) + '…') :
  11828. strValue;
  11829. }
  11830. function constructDetailsForInterpolation(lView, rootIndex, expressionIndex, meta, changedValue) {
  11831. const [propName, prefix, ...chunks] = meta.split(INTERPOLATION_DELIMITER);
  11832. let oldValue = prefix, newValue = prefix;
  11833. for (let i = 0; i < chunks.length; i++) {
  11834. const slotIdx = rootIndex + i;
  11835. oldValue += `${lView[slotIdx]}${chunks[i]}`;
  11836. newValue += `${slotIdx === expressionIndex ? changedValue : lView[slotIdx]}${chunks[i]}`;
  11837. }
  11838. return { propName, oldValue, newValue };
  11839. }
  11840. /**
  11841. * Constructs an object that contains details for the ExpressionChangedAfterItHasBeenCheckedError:
  11842. * - property name (for property bindings or interpolations)
  11843. * - old and new values, enriched using information from metadata
  11844. *
  11845. * More information on the metadata storage format can be found in `storePropertyBindingMetadata`
  11846. * function description.
  11847. */
  11848. function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValue) {
  11849. const tData = lView[TVIEW].data;
  11850. const metadata = tData[bindingIndex];
  11851. if (typeof metadata === 'string') {
  11852. // metadata for property interpolation
  11853. if (metadata.indexOf(INTERPOLATION_DELIMITER) > -1) {
  11854. return constructDetailsForInterpolation(lView, bindingIndex, bindingIndex, metadata, newValue);
  11855. }
  11856. // metadata for property binding
  11857. return { propName: metadata, oldValue, newValue };
  11858. }
  11859. // metadata is not available for this expression, check if this expression is a part of the
  11860. // property interpolation by going from the current binding index left and look for a string that
  11861. // contains INTERPOLATION_DELIMITER, the layout in tView.data for this case will look like this:
  11862. // [..., 'id�Prefix � and � suffix', null, null, null, ...]
  11863. if (metadata === null) {
  11864. let idx = bindingIndex - 1;
  11865. while (typeof tData[idx] !== 'string' && tData[idx + 1] === null) {
  11866. idx--;
  11867. }
  11868. const meta = tData[idx];
  11869. if (typeof meta === 'string') {
  11870. const matches = meta.match(new RegExp(INTERPOLATION_DELIMITER, 'g'));
  11871. // first interpolation delimiter separates property name from interpolation parts (in case of
  11872. // property interpolations), so we subtract one from total number of found delimiters
  11873. if (matches && (matches.length - 1) > bindingIndex - idx) {
  11874. return constructDetailsForInterpolation(lView, idx, bindingIndex, meta, newValue);
  11875. }
  11876. }
  11877. }
  11878. return { propName: undefined, oldValue, newValue };
  11879. }
  11880. let currentConsumer = null;
  11881. function setLViewForConsumer(node, lView) {
  11882. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  11883. assertEqual(node.lView, null, 'Consumer already associated with a view.');
  11884. node.lView = lView;
  11885. }
  11886. /**
  11887. * Create a new template consumer pointing at the specified LView.
  11888. * Sometimes, a previously created consumer may be reused, in order to save on allocations. In that
  11889. * case, the LView will be updated.
  11890. */
  11891. function getReactiveLViewConsumer(lView, slot) {
  11892. return lView[slot] ?? getOrCreateCurrentLViewConsumer();
  11893. }
  11894. /**
  11895. * Assigns the `currentTemplateContext` to its LView's `REACTIVE_CONSUMER` slot if there are tracked
  11896. * producers.
  11897. *
  11898. * The presence of producers means that a signal was read while the consumer was the active
  11899. * consumer.
  11900. *
  11901. * If no producers are present, we do not assign the current template context. This also means we
  11902. * can just reuse the template context for the next LView.
  11903. */
  11904. function commitLViewConsumerIfHasProducers(lView, slot) {
  11905. const consumer = getOrCreateCurrentLViewConsumer();
  11906. if (!consumer.producerNode?.length) {
  11907. return;
  11908. }
  11909. lView[slot] = currentConsumer;
  11910. consumer.lView = lView;
  11911. currentConsumer = createLViewConsumer();
  11912. }
  11913. const REACTIVE_LVIEW_CONSUMER_NODE = {
  11914. ...REACTIVE_NODE,
  11915. consumerIsAlwaysLive: true,
  11916. consumerMarkedDirty: (node) => {
  11917. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  11918. assertDefined(node.lView, 'Updating a signal during template or host binding execution is not allowed.');
  11919. markViewDirty(node.lView);
  11920. },
  11921. lView: null,
  11922. };
  11923. function createLViewConsumer() {
  11924. return Object.create(REACTIVE_LVIEW_CONSUMER_NODE);
  11925. }
  11926. function getOrCreateCurrentLViewConsumer() {
  11927. currentConsumer ??= createLViewConsumer();
  11928. return currentConsumer;
  11929. }
  11930. /** A special value which designates that a value has not changed. */
  11931. const NO_CHANGE = (typeof ngDevMode === 'undefined' || ngDevMode) ? { __brand__: 'NO_CHANGE' } : {};
  11932. /**
  11933. * Advances to an element for later binding instructions.
  11934. *
  11935. * Used in conjunction with instructions like {@link property} to act on elements with specified
  11936. * indices, for example those created with {@link element} or {@link elementStart}.
  11937. *
  11938. * ```ts
  11939. * (rf: RenderFlags, ctx: any) => {
  11940. * if (rf & 1) {
  11941. * text(0, 'Hello');
  11942. * text(1, 'Goodbye')
  11943. * element(2, 'div');
  11944. * }
  11945. * if (rf & 2) {
  11946. * advance(2); // Advance twice to the <div>.
  11947. * property('title', 'test');
  11948. * }
  11949. * }
  11950. * ```
  11951. * @param delta Number of elements to advance forwards by.
  11952. *
  11953. * @codeGenApi
  11954. */
  11955. function ɵɵadvance(delta) {
  11956. ngDevMode && assertGreaterThan(delta, 0, 'Can only advance forward');
  11957. selectIndexInternal(getTView(), getLView(), getSelectedIndex() + delta, !!ngDevMode && isInCheckNoChangesMode());
  11958. }
  11959. function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
  11960. ngDevMode && assertIndexInDeclRange(lView, index);
  11961. // Flush the initial hooks for elements in the view that have been added up to this point.
  11962. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  11963. if (!checkNoChangesMode) {
  11964. const hooksInitPhaseCompleted = (lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
  11965. if (hooksInitPhaseCompleted) {
  11966. const preOrderCheckHooks = tView.preOrderCheckHooks;
  11967. if (preOrderCheckHooks !== null) {
  11968. executeCheckHooks(lView, preOrderCheckHooks, index);
  11969. }
  11970. }
  11971. else {
  11972. const preOrderHooks = tView.preOrderHooks;
  11973. if (preOrderHooks !== null) {
  11974. executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, index);
  11975. }
  11976. }
  11977. }
  11978. // We must set the selected index *after* running the hooks, because hooks may have side-effects
  11979. // that cause other template functions to run, thus updating the selected index, which is global
  11980. // state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
  11981. // will be altered by the time we leave the `ɵɵadvance` instruction.
  11982. setSelectedIndex(index);
  11983. }
  11984. function ɵɵdirectiveInject(token, flags = InjectFlags.Default) {
  11985. const lView = getLView();
  11986. // Fall back to inject() if view hasn't been created. This situation can happen in tests
  11987. // if inject utilities are used before bootstrapping.
  11988. if (lView === null) {
  11989. // Verify that we will not get into infinite loop.
  11990. ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject);
  11991. return ɵɵinject(token, flags);
  11992. }
  11993. const tNode = getCurrentTNode();
  11994. const value = getOrCreateInjectable(tNode, lView, resolveForwardRef(token), flags);
  11995. ngDevMode && emitInjectEvent(token, value, flags);
  11996. return value;
  11997. }
  11998. /**
  11999. * Throws an error indicating that a factory function could not be generated by the compiler for a
  12000. * particular class.
  12001. *
  12002. * This instruction allows the actual error message to be optimized away when ngDevMode is turned
  12003. * off, saving bytes of generated code while still providing a good experience in dev mode.
  12004. *
  12005. * The name of the class is not mentioned here, but will be in the generated factory function name
  12006. * and thus in the stack trace.
  12007. *
  12008. * @codeGenApi
  12009. */
  12010. function ɵɵinvalidFactory() {
  12011. const msg = ngDevMode ? `This constructor was not compatible with Dependency Injection.` : 'invalid';
  12012. throw new Error(msg);
  12013. }
  12014. /**
  12015. * Invoke `HostBindingsFunction`s for view.
  12016. *
  12017. * This methods executes `TView.hostBindingOpCodes`. It is used to execute the
  12018. * `HostBindingsFunction`s associated with the current `LView`.
  12019. *
  12020. * @param tView Current `TView`.
  12021. * @param lView Current `LView`.
  12022. */
  12023. function processHostBindingOpCodes(tView, lView) {
  12024. const hostBindingOpCodes = tView.hostBindingOpCodes;
  12025. if (hostBindingOpCodes === null)
  12026. return;
  12027. const consumer = getReactiveLViewConsumer(lView, REACTIVE_HOST_BINDING_CONSUMER);
  12028. try {
  12029. for (let i = 0; i < hostBindingOpCodes.length; i++) {
  12030. const opCode = hostBindingOpCodes[i];
  12031. if (opCode < 0) {
  12032. // Negative numbers are element indexes.
  12033. setSelectedIndex(~opCode);
  12034. }
  12035. else {
  12036. // Positive numbers are NumberTuple which store bindingRootIndex and directiveIndex.
  12037. const directiveIdx = opCode;
  12038. const bindingRootIndx = hostBindingOpCodes[++i];
  12039. const hostBindingFn = hostBindingOpCodes[++i];
  12040. setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
  12041. consumer.dirty = false;
  12042. const prevConsumer = consumerBeforeComputation(consumer);
  12043. try {
  12044. const context = lView[directiveIdx];
  12045. hostBindingFn(2 /* RenderFlags.Update */, context);
  12046. }
  12047. finally {
  12048. consumerAfterComputation(consumer, prevConsumer);
  12049. }
  12050. }
  12051. }
  12052. }
  12053. finally {
  12054. if (lView[REACTIVE_HOST_BINDING_CONSUMER] === null) {
  12055. commitLViewConsumerIfHasProducers(lView, REACTIVE_HOST_BINDING_CONSUMER);
  12056. }
  12057. setSelectedIndex(-1);
  12058. }
  12059. }
  12060. function createLView(parentLView, tView, context, flags, host, tHostNode, environment, renderer, injector, embeddedViewInjector, hydrationInfo) {
  12061. const lView = tView.blueprint.slice();
  12062. lView[HOST] = host;
  12063. lView[FLAGS] = flags | 4 /* LViewFlags.CreationMode */ | 128 /* LViewFlags.Attached */ | 8 /* LViewFlags.FirstLViewPass */;
  12064. if (embeddedViewInjector !== null ||
  12065. (parentLView && (parentLView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */))) {
  12066. lView[FLAGS] |= 2048 /* LViewFlags.HasEmbeddedViewInjector */;
  12067. }
  12068. resetPreOrderHookFlags(lView);
  12069. ngDevMode && tView.declTNode && parentLView && assertTNodeForLView(tView.declTNode, parentLView);
  12070. lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
  12071. lView[CONTEXT] = context;
  12072. lView[ENVIRONMENT] = (environment || parentLView && parentLView[ENVIRONMENT]);
  12073. ngDevMode && assertDefined(lView[ENVIRONMENT], 'LViewEnvironment is required');
  12074. lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER]);
  12075. ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required');
  12076. lView[INJECTOR$1] = injector || parentLView && parentLView[INJECTOR$1] || null;
  12077. lView[T_HOST] = tHostNode;
  12078. lView[ID] = getUniqueLViewId();
  12079. lView[HYDRATION] = hydrationInfo;
  12080. lView[EMBEDDED_VIEW_INJECTOR] = embeddedViewInjector;
  12081. ngDevMode &&
  12082. assertEqual(tView.type == 2 /* TViewType.Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView');
  12083. lView[DECLARATION_COMPONENT_VIEW] =
  12084. tView.type == 2 /* TViewType.Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView;
  12085. return lView;
  12086. }
  12087. function getOrCreateTNode(tView, index, type, name, attrs) {
  12088. ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
  12089. // `view_engine_compatibility` for additional context.
  12090. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
  12091. // Keep this function short, so that the VM will inline it.
  12092. ngDevMode && assertPureTNodeType(type);
  12093. let tNode = tView.data[index];
  12094. if (tNode === null) {
  12095. tNode = createTNodeAtIndex(tView, index, type, name, attrs);
  12096. if (isInI18nBlock()) {
  12097. // If we are in i18n block then all elements should be pre declared through `Placeholder`
  12098. // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
  12099. // If the `TNode` was not pre-declared than it means it was not mentioned which means it was
  12100. // removed, so we mark it as detached.
  12101. tNode.flags |= 32 /* TNodeFlags.isDetached */;
  12102. }
  12103. }
  12104. else if (tNode.type & 64 /* TNodeType.Placeholder */) {
  12105. tNode.type = type;
  12106. tNode.value = name;
  12107. tNode.attrs = attrs;
  12108. const parent = getCurrentParentTNode();
  12109. tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex;
  12110. ngDevMode && assertTNodeForTView(tNode, tView);
  12111. ngDevMode && assertEqual(index, tNode.index, 'Expecting same index');
  12112. }
  12113. setCurrentTNode(tNode, true);
  12114. return tNode;
  12115. }
  12116. function createTNodeAtIndex(tView, index, type, name, attrs) {
  12117. const currentTNode = getCurrentTNodePlaceholderOk();
  12118. const isParent = isCurrentTNodeParent();
  12119. const parent = isParent ? currentTNode : currentTNode && currentTNode.parent;
  12120. // Parents cannot cross component boundaries because components will be used in multiple places.
  12121. const tNode = tView.data[index] =
  12122. createTNode(tView, parent, type, index, name, attrs);
  12123. // Assign a pointer to the first child node of a given view. The first node is not always the one
  12124. // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has
  12125. // the index 1 or more, so we can't just check node index.
  12126. if (tView.firstChild === null) {
  12127. tView.firstChild = tNode;
  12128. }
  12129. if (currentTNode !== null) {
  12130. if (isParent) {
  12131. // FIXME(misko): This logic looks unnecessarily complicated. Could we simplify?
  12132. if (currentTNode.child == null && tNode.parent !== null) {
  12133. // We are in the same view, which means we are adding content node to the parent view.
  12134. currentTNode.child = tNode;
  12135. }
  12136. }
  12137. else {
  12138. if (currentTNode.next === null) {
  12139. // In the case of i18n the `currentTNode` may already be linked, in which case we don't want
  12140. // to break the links which i18n created.
  12141. currentTNode.next = tNode;
  12142. tNode.prev = currentTNode;
  12143. }
  12144. }
  12145. }
  12146. return tNode;
  12147. }
  12148. /**
  12149. * When elements are created dynamically after a view blueprint is created (e.g. through
  12150. * i18nApply()), we need to adjust the blueprint for future
  12151. * template passes.
  12152. *
  12153. * @param tView `TView` associated with `LView`
  12154. * @param lView The `LView` containing the blueprint to adjust
  12155. * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
  12156. * @param initialValue Initial value to store in blueprint
  12157. */
  12158. function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
  12159. if (numSlotsToAlloc === 0)
  12160. return -1;
  12161. if (ngDevMode) {
  12162. assertFirstCreatePass(tView);
  12163. assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
  12164. assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
  12165. assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
  12166. assertFirstUpdatePass(tView);
  12167. }
  12168. const allocIdx = lView.length;
  12169. for (let i = 0; i < numSlotsToAlloc; i++) {
  12170. lView.push(initialValue);
  12171. tView.blueprint.push(initialValue);
  12172. tView.data.push(null);
  12173. }
  12174. return allocIdx;
  12175. }
  12176. function executeTemplate(tView, lView, templateFn, rf, context) {
  12177. const consumer = getReactiveLViewConsumer(lView, REACTIVE_TEMPLATE_CONSUMER);
  12178. const prevSelectedIndex = getSelectedIndex();
  12179. const isUpdatePhase = rf & 2 /* RenderFlags.Update */;
  12180. try {
  12181. setSelectedIndex(-1);
  12182. if (isUpdatePhase && lView.length > HEADER_OFFSET) {
  12183. // When we're updating, inherently select 0 so we don't
  12184. // have to generate that instruction for most update blocks.
  12185. selectIndexInternal(tView, lView, HEADER_OFFSET, !!ngDevMode && isInCheckNoChangesMode());
  12186. }
  12187. const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */;
  12188. profiler(preHookType, context);
  12189. const effectiveConsumer = isUpdatePhase ? consumer : null;
  12190. const prevConsumer = consumerBeforeComputation(effectiveConsumer);
  12191. try {
  12192. if (effectiveConsumer !== null) {
  12193. effectiveConsumer.dirty = false;
  12194. }
  12195. templateFn(rf, context);
  12196. }
  12197. finally {
  12198. consumerAfterComputation(effectiveConsumer, prevConsumer);
  12199. }
  12200. }
  12201. finally {
  12202. if (isUpdatePhase && lView[REACTIVE_TEMPLATE_CONSUMER] === null) {
  12203. commitLViewConsumerIfHasProducers(lView, REACTIVE_TEMPLATE_CONSUMER);
  12204. }
  12205. setSelectedIndex(prevSelectedIndex);
  12206. const postHookType = isUpdatePhase ? 3 /* ProfilerEvent.TemplateUpdateEnd */ : 1 /* ProfilerEvent.TemplateCreateEnd */;
  12207. profiler(postHookType, context);
  12208. }
  12209. }
  12210. //////////////////////////
  12211. //// Element
  12212. //////////////////////////
  12213. function executeContentQueries(tView, tNode, lView) {
  12214. if (isContentQueryHost(tNode)) {
  12215. const prevConsumer = setActiveConsumer(null);
  12216. try {
  12217. const start = tNode.directiveStart;
  12218. const end = tNode.directiveEnd;
  12219. for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
  12220. const def = tView.data[directiveIndex];
  12221. if (def.contentQueries) {
  12222. def.contentQueries(1 /* RenderFlags.Create */, lView[directiveIndex], directiveIndex);
  12223. }
  12224. }
  12225. }
  12226. finally {
  12227. setActiveConsumer(prevConsumer);
  12228. }
  12229. }
  12230. }
  12231. /**
  12232. * Creates directive instances.
  12233. */
  12234. function createDirectivesInstances(tView, lView, tNode) {
  12235. if (!getBindingsEnabled())
  12236. return;
  12237. instantiateAllDirectives(tView, lView, tNode, getNativeByTNode(tNode, lView));
  12238. if ((tNode.flags & 64 /* TNodeFlags.hasHostBindings */) === 64 /* TNodeFlags.hasHostBindings */) {
  12239. invokeDirectivesHostBindings(tView, lView, tNode);
  12240. }
  12241. }
  12242. /**
  12243. * Takes a list of local names and indices and pushes the resolved local variable values
  12244. * to LView in the same order as they are loaded in the template with load().
  12245. */
  12246. function saveResolvedLocalsInData(viewData, tNode, localRefExtractor = getNativeByTNode) {
  12247. const localNames = tNode.localNames;
  12248. if (localNames !== null) {
  12249. let localIndex = tNode.index + 1;
  12250. for (let i = 0; i < localNames.length; i += 2) {
  12251. const index = localNames[i + 1];
  12252. const value = index === -1 ?
  12253. localRefExtractor(tNode, viewData) :
  12254. viewData[index];
  12255. viewData[localIndex++] = value;
  12256. }
  12257. }
  12258. }
  12259. /**
  12260. * Gets TView from a template function or creates a new TView
  12261. * if it doesn't already exist.
  12262. *
  12263. * @param def ComponentDef
  12264. * @returns TView
  12265. */
  12266. function getOrCreateComponentTView(def) {
  12267. const tView = def.tView;
  12268. // Create a TView if there isn't one, or recreate it if the first create pass didn't
  12269. // complete successfully since we can't know for sure whether it's in a usable shape.
  12270. if (tView === null || tView.incompleteFirstPass) {
  12271. // Declaration node here is null since this function is called when we dynamically create a
  12272. // component and hence there is no declaration.
  12273. const declTNode = null;
  12274. return def.tView = createTView(1 /* TViewType.Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts, def.id);
  12275. }
  12276. return tView;
  12277. }
  12278. /**
  12279. * Creates a TView instance
  12280. *
  12281. * @param type Type of `TView`.
  12282. * @param declTNode Declaration location of this `TView`.
  12283. * @param templateFn Template function
  12284. * @param decls The number of nodes, local refs, and pipes in this template
  12285. * @param directives Registry of directives for this view
  12286. * @param pipes Registry of pipes for this view
  12287. * @param viewQuery View queries for this view
  12288. * @param schemas Schemas for this view
  12289. * @param consts Constants for this view
  12290. */
  12291. function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory, ssrId) {
  12292. ngDevMode && ngDevMode.tView++;
  12293. const bindingStartIndex = HEADER_OFFSET + decls;
  12294. // This length does not yet contain host bindings from child directives because at this point,
  12295. // we don't know which directives are active on this template. As soon as a directive is matched
  12296. // that has a host binding, we will update the blueprint with that def's hostVars count.
  12297. const initialViewLength = bindingStartIndex + vars;
  12298. const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength);
  12299. const consts = typeof constsOrFactory === 'function' ? constsOrFactory() : constsOrFactory;
  12300. const tView = blueprint[TVIEW] = {
  12301. type: type,
  12302. blueprint: blueprint,
  12303. template: templateFn,
  12304. queries: null,
  12305. viewQuery: viewQuery,
  12306. declTNode: declTNode,
  12307. data: blueprint.slice().fill(null, bindingStartIndex),
  12308. bindingStartIndex: bindingStartIndex,
  12309. expandoStartIndex: initialViewLength,
  12310. hostBindingOpCodes: null,
  12311. firstCreatePass: true,
  12312. firstUpdatePass: true,
  12313. staticViewQueries: false,
  12314. staticContentQueries: false,
  12315. preOrderHooks: null,
  12316. preOrderCheckHooks: null,
  12317. contentHooks: null,
  12318. contentCheckHooks: null,
  12319. viewHooks: null,
  12320. viewCheckHooks: null,
  12321. destroyHooks: null,
  12322. cleanup: null,
  12323. contentQueries: null,
  12324. components: null,
  12325. directiveRegistry: typeof directives === 'function' ? directives() : directives,
  12326. pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
  12327. firstChild: null,
  12328. schemas: schemas,
  12329. consts: consts,
  12330. incompleteFirstPass: false,
  12331. ssrId,
  12332. };
  12333. if (ngDevMode) {
  12334. // For performance reasons it is important that the tView retains the same shape during runtime.
  12335. // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
  12336. // prevent class transitions.
  12337. Object.seal(tView);
  12338. }
  12339. return tView;
  12340. }
  12341. function createViewBlueprint(bindingStartIndex, initialViewLength) {
  12342. const blueprint = [];
  12343. for (let i = 0; i < initialViewLength; i++) {
  12344. blueprint.push(i < bindingStartIndex ? null : NO_CHANGE);
  12345. }
  12346. return blueprint;
  12347. }
  12348. /**
  12349. * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
  12350. *
  12351. * @param renderer the renderer used to locate the element.
  12352. * @param elementOrSelector Render element or CSS selector to locate the element.
  12353. * @param encapsulation View Encapsulation defined for component that requests host element.
  12354. * @param injector Root view injector instance.
  12355. */
  12356. function locateHostElement(renderer, elementOrSelector, encapsulation, injector) {
  12357. // Note: we use default value for the `PRESERVE_HOST_CONTENT` here even though it's a
  12358. // tree-shakable one (providedIn:'root'). This code path can be triggered during dynamic
  12359. // component creation (after calling ViewContainerRef.createComponent) when an injector
  12360. // instance can be provided. The injector instance might be disconnected from the main DI
  12361. // tree, thus the `PRESERVE_HOST_CONTENT` would not be able to instantiate. In this case, the
  12362. // default value will be used.
  12363. const preserveHostContent = injector.get(PRESERVE_HOST_CONTENT, PRESERVE_HOST_CONTENT_DEFAULT);
  12364. // When using native Shadow DOM, do not clear host element to allow native slot
  12365. // projection.
  12366. const preserveContent = preserveHostContent || encapsulation === ViewEncapsulation.ShadowDom;
  12367. const rootElement = renderer.selectRootElement(elementOrSelector, preserveContent);
  12368. applyRootElementTransform(rootElement);
  12369. return rootElement;
  12370. }
  12371. /**
  12372. * Applies any root element transformations that are needed. If hydration is enabled,
  12373. * this will process corrupted text nodes.
  12374. *
  12375. * @param rootElement the app root HTML Element
  12376. */
  12377. function applyRootElementTransform(rootElement) {
  12378. _applyRootElementTransformImpl(rootElement);
  12379. }
  12380. /**
  12381. * Reference to a function that applies transformations to the root HTML element
  12382. * of an app. When hydration is enabled, this processes any corrupt text nodes
  12383. * so they are properly hydratable on the client.
  12384. *
  12385. * @param rootElement the app root HTML Element
  12386. */
  12387. let _applyRootElementTransformImpl = (rootElement) => null;
  12388. /**
  12389. * Processes text node markers before hydration begins. This replaces any special comment
  12390. * nodes that were added prior to serialization are swapped out to restore proper text
  12391. * nodes before hydration.
  12392. *
  12393. * @param rootElement the app root HTML Element
  12394. */
  12395. function applyRootElementTransformImpl(rootElement) {
  12396. if (hasSkipHydrationAttrOnRElement(rootElement)) {
  12397. // Handle a situation when the `ngSkipHydration` attribute is applied
  12398. // to the root node of an application. In this case, we should clear
  12399. // the contents and render everything from scratch.
  12400. clearElementContents(rootElement);
  12401. }
  12402. else {
  12403. processTextNodeMarkersBeforeHydration(rootElement);
  12404. }
  12405. }
  12406. /**
  12407. * Sets the implementation for the `applyRootElementTransform` function.
  12408. */
  12409. function enableApplyRootElementTransformImpl() {
  12410. _applyRootElementTransformImpl = applyRootElementTransformImpl;
  12411. }
  12412. /**
  12413. * Saves context for this cleanup function in LView.cleanupInstances.
  12414. *
  12415. * On the first template pass, saves in TView:
  12416. * - Cleanup function
  12417. * - Index of context we just saved in LView.cleanupInstances
  12418. */
  12419. function storeCleanupWithContext(tView, lView, context, cleanupFn) {
  12420. const lCleanup = getOrCreateLViewCleanup(lView);
  12421. // Historically the `storeCleanupWithContext` was used to register both framework-level and
  12422. // user-defined cleanup callbacks, but over time those two types of cleanups were separated.
  12423. // This dev mode checks assures that user-level cleanup callbacks are _not_ stored in data
  12424. // structures reserved for framework-specific hooks.
  12425. ngDevMode &&
  12426. assertDefined(context, 'Cleanup context is mandatory when registering framework-level destroy hooks');
  12427. lCleanup.push(context);
  12428. if (tView.firstCreatePass) {
  12429. getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1);
  12430. }
  12431. else {
  12432. // Make sure that no new framework-level cleanup functions are registered after the first
  12433. // template pass is done (and TView data structures are meant to fully constructed).
  12434. if (ngDevMode) {
  12435. Object.freeze(getOrCreateTViewCleanup(tView));
  12436. }
  12437. }
  12438. }
  12439. function createTNode(tView, tParent, type, index, value, attrs) {
  12440. ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
  12441. // `view_engine_compatibility` for additional context.
  12442. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
  12443. ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\'');
  12444. ngDevMode && ngDevMode.tNode++;
  12445. ngDevMode && tParent && assertTNodeForTView(tParent, tView);
  12446. let injectorIndex = tParent ? tParent.injectorIndex : -1;
  12447. let flags = 0;
  12448. if (isInSkipHydrationBlock$1()) {
  12449. flags |= 128 /* TNodeFlags.inSkipHydrationBlock */;
  12450. }
  12451. const tNode = {
  12452. type,
  12453. index,
  12454. insertBeforeIndex: null,
  12455. injectorIndex,
  12456. directiveStart: -1,
  12457. directiveEnd: -1,
  12458. directiveStylingLast: -1,
  12459. componentOffset: -1,
  12460. propertyBindings: null,
  12461. flags,
  12462. providerIndexes: 0,
  12463. value: value,
  12464. attrs: attrs,
  12465. mergedAttrs: null,
  12466. localNames: null,
  12467. initialInputs: undefined,
  12468. inputs: null,
  12469. outputs: null,
  12470. tView: null,
  12471. next: null,
  12472. prev: null,
  12473. projectionNext: null,
  12474. child: null,
  12475. parent: tParent,
  12476. projection: null,
  12477. styles: null,
  12478. stylesWithoutHost: null,
  12479. residualStyles: undefined,
  12480. classes: null,
  12481. classesWithoutHost: null,
  12482. residualClasses: undefined,
  12483. classBindings: 0,
  12484. styleBindings: 0,
  12485. };
  12486. if (ngDevMode) {
  12487. // For performance reasons it is important that the tNode retains the same shape during runtime.
  12488. // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
  12489. // prevent class transitions.
  12490. Object.seal(tNode);
  12491. }
  12492. return tNode;
  12493. }
  12494. /**
  12495. * Generates the `PropertyAliases` data structure from the provided input/output mapping.
  12496. * @param aliasMap Input/output mapping from the directive definition.
  12497. * @param directiveIndex Index of the directive.
  12498. * @param propertyAliases Object in which to store the results.
  12499. * @param hostDirectiveAliasMap Object used to alias or filter out properties for host directives.
  12500. * If the mapping is provided, it'll act as an allowlist, as well as a mapping of what public
  12501. * name inputs/outputs should be exposed under.
  12502. */
  12503. function generatePropertyAliases(aliasMap, directiveIndex, propertyAliases, hostDirectiveAliasMap) {
  12504. for (let publicName in aliasMap) {
  12505. if (aliasMap.hasOwnProperty(publicName)) {
  12506. propertyAliases = propertyAliases === null ? {} : propertyAliases;
  12507. const internalName = aliasMap[publicName];
  12508. // If there are no host directive mappings, we want to remap using the alias map from the
  12509. // definition itself. If there is an alias map, it has two functions:
  12510. // 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
  12511. // ones inside the host directive map will be exposed on the host.
  12512. // 2. The public name of the property is aliased using the host directive alias map, rather
  12513. // than the alias map from the definition.
  12514. if (hostDirectiveAliasMap === null) {
  12515. addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName);
  12516. }
  12517. else if (hostDirectiveAliasMap.hasOwnProperty(publicName)) {
  12518. addPropertyAlias(propertyAliases, directiveIndex, hostDirectiveAliasMap[publicName], internalName);
  12519. }
  12520. }
  12521. }
  12522. return propertyAliases;
  12523. }
  12524. function addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName) {
  12525. if (propertyAliases.hasOwnProperty(publicName)) {
  12526. propertyAliases[publicName].push(directiveIndex, internalName);
  12527. }
  12528. else {
  12529. propertyAliases[publicName] = [directiveIndex, internalName];
  12530. }
  12531. }
  12532. /**
  12533. * Initializes data structures required to work with directive inputs and outputs.
  12534. * Initialization is done for all directives matched on a given TNode.
  12535. */
  12536. function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
  12537. ngDevMode && assertFirstCreatePass(tView);
  12538. const start = tNode.directiveStart;
  12539. const end = tNode.directiveEnd;
  12540. const tViewData = tView.data;
  12541. const tNodeAttrs = tNode.attrs;
  12542. const inputsFromAttrs = [];
  12543. let inputsStore = null;
  12544. let outputsStore = null;
  12545. for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
  12546. const directiveDef = tViewData[directiveIndex];
  12547. const aliasData = hostDirectiveDefinitionMap ? hostDirectiveDefinitionMap.get(directiveDef) : null;
  12548. const aliasedInputs = aliasData ? aliasData.inputs : null;
  12549. const aliasedOutputs = aliasData ? aliasData.outputs : null;
  12550. inputsStore =
  12551. generatePropertyAliases(directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
  12552. outputsStore =
  12553. generatePropertyAliases(directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
  12554. // Do not use unbound attributes as inputs to structural directives, since structural
  12555. // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
  12556. // TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which
  12557. // should be set for inline templates.
  12558. const initialInputs = (inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)) ?
  12559. generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs) :
  12560. null;
  12561. inputsFromAttrs.push(initialInputs);
  12562. }
  12563. if (inputsStore !== null) {
  12564. if (inputsStore.hasOwnProperty('class')) {
  12565. tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
  12566. }
  12567. if (inputsStore.hasOwnProperty('style')) {
  12568. tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
  12569. }
  12570. }
  12571. tNode.initialInputs = inputsFromAttrs;
  12572. tNode.inputs = inputsStore;
  12573. tNode.outputs = outputsStore;
  12574. }
  12575. /**
  12576. * Mapping between attributes names that don't correspond to their element property names.
  12577. *
  12578. * Performance note: this function is written as a series of if checks (instead of, say, a property
  12579. * object lookup) for performance reasons - the series of `if` checks seems to be the fastest way of
  12580. * mapping property names. Do NOT change without benchmarking.
  12581. *
  12582. * Note: this mapping has to be kept in sync with the equally named mapping in the template
  12583. * type-checking machinery of ngtsc.
  12584. */
  12585. function mapPropName(name) {
  12586. if (name === 'class')
  12587. return 'className';
  12588. if (name === 'for')
  12589. return 'htmlFor';
  12590. if (name === 'formaction')
  12591. return 'formAction';
  12592. if (name === 'innerHtml')
  12593. return 'innerHTML';
  12594. if (name === 'readonly')
  12595. return 'readOnly';
  12596. if (name === 'tabindex')
  12597. return 'tabIndex';
  12598. return name;
  12599. }
  12600. function elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, nativeOnly) {
  12601. ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
  12602. const element = getNativeByTNode(tNode, lView);
  12603. let inputData = tNode.inputs;
  12604. let dataValue;
  12605. if (!nativeOnly && inputData != null && (dataValue = inputData[propName])) {
  12606. setInputsForProperty(tView, lView, dataValue, propName, value);
  12607. if (isComponentHost(tNode))
  12608. markDirtyIfOnPush(lView, tNode.index);
  12609. if (ngDevMode) {
  12610. setNgReflectProperties(lView, element, tNode.type, dataValue, value);
  12611. }
  12612. }
  12613. else if (tNode.type & 3 /* TNodeType.AnyRNode */) {
  12614. propName = mapPropName(propName);
  12615. if (ngDevMode) {
  12616. validateAgainstEventProperties(propName);
  12617. if (!isPropertyValid(element, propName, tNode.value, tView.schemas)) {
  12618. handleUnknownPropertyError(propName, tNode.value, tNode.type, lView);
  12619. }
  12620. ngDevMode.rendererSetProperty++;
  12621. }
  12622. // It is assumed that the sanitizer is only added when the compiler determines that the
  12623. // property is risky, so sanitization can be done without further checks.
  12624. value = sanitizer != null ? sanitizer(value, tNode.value || '', propName) : value;
  12625. renderer.setProperty(element, propName, value);
  12626. }
  12627. else if (tNode.type & 12 /* TNodeType.AnyContainer */) {
  12628. // If the node is a container and the property didn't
  12629. // match any of the inputs or schemas we should throw.
  12630. if (ngDevMode && !matchingSchemas(tView.schemas, tNode.value)) {
  12631. handleUnknownPropertyError(propName, tNode.value, tNode.type, lView);
  12632. }
  12633. }
  12634. }
  12635. /** If node is an OnPush component, marks its LView dirty. */
  12636. function markDirtyIfOnPush(lView, viewIndex) {
  12637. ngDevMode && assertLView(lView);
  12638. const childComponentLView = getComponentLViewByIndex(viewIndex, lView);
  12639. if (!(childComponentLView[FLAGS] & 16 /* LViewFlags.CheckAlways */)) {
  12640. childComponentLView[FLAGS] |= 64 /* LViewFlags.Dirty */;
  12641. }
  12642. }
  12643. function setNgReflectProperty(lView, element, type, attrName, value) {
  12644. const renderer = lView[RENDERER];
  12645. attrName = normalizeDebugBindingName(attrName);
  12646. const debugValue = normalizeDebugBindingValue(value);
  12647. if (type & 3 /* TNodeType.AnyRNode */) {
  12648. if (value == null) {
  12649. renderer.removeAttribute(element, attrName);
  12650. }
  12651. else {
  12652. renderer.setAttribute(element, attrName, debugValue);
  12653. }
  12654. }
  12655. else {
  12656. const textContent = escapeCommentText(`bindings=${JSON.stringify({ [attrName]: debugValue }, null, 2)}`);
  12657. renderer.setValue(element, textContent);
  12658. }
  12659. }
  12660. function setNgReflectProperties(lView, element, type, dataValue, value) {
  12661. if (type & (3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */)) {
  12662. /**
  12663. * dataValue is an array containing runtime input or output names for the directives:
  12664. * i+0: directive instance index
  12665. * i+1: privateName
  12666. *
  12667. * e.g. [0, 'change', 'change-minified']
  12668. * we want to set the reflected property with the privateName: dataValue[i+1]
  12669. */
  12670. for (let i = 0; i < dataValue.length; i += 2) {
  12671. setNgReflectProperty(lView, element, type, dataValue[i + 1], value);
  12672. }
  12673. }
  12674. }
  12675. /**
  12676. * Resolve the matched directives on a node.
  12677. */
  12678. function resolveDirectives(tView, lView, tNode, localRefs) {
  12679. // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
  12680. // tsickle.
  12681. ngDevMode && assertFirstCreatePass(tView);
  12682. if (getBindingsEnabled()) {
  12683. const exportsMap = localRefs === null ? null : { '': -1 };
  12684. const matchResult = findDirectiveDefMatches(tView, tNode);
  12685. let directiveDefs;
  12686. let hostDirectiveDefs;
  12687. if (matchResult === null) {
  12688. directiveDefs = hostDirectiveDefs = null;
  12689. }
  12690. else {
  12691. [directiveDefs, hostDirectiveDefs] = matchResult;
  12692. }
  12693. if (directiveDefs !== null) {
  12694. initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
  12695. }
  12696. if (exportsMap)
  12697. cacheMatchingLocalNames(tNode, localRefs, exportsMap);
  12698. }
  12699. // Merge the template attrs last so that they have the highest priority.
  12700. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
  12701. }
  12702. /** Initializes the data structures necessary for a list of directives to be instantiated. */
  12703. function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
  12704. ngDevMode && assertFirstCreatePass(tView);
  12705. // Publishes the directive types to DI so they can be injected. Needs to
  12706. // happen in a separate pass before the TNode flags have been initialized.
  12707. for (let i = 0; i < directives.length; i++) {
  12708. diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
  12709. }
  12710. initTNodeFlags(tNode, tView.data.length, directives.length);
  12711. // When the same token is provided by several directives on the same node, some rules apply in
  12712. // the viewEngine:
  12713. // - viewProviders have priority over providers
  12714. // - the last directive in NgModule.declarations has priority over the previous one
  12715. // So to match these rules, the order in which providers are added in the arrays is very
  12716. // important.
  12717. for (let i = 0; i < directives.length; i++) {
  12718. const def = directives[i];
  12719. if (def.providersResolver)
  12720. def.providersResolver(def);
  12721. }
  12722. let preOrderHooksFound = false;
  12723. let preOrderCheckHooksFound = false;
  12724. let directiveIdx = allocExpando(tView, lView, directives.length, null);
  12725. ngDevMode &&
  12726. assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
  12727. for (let i = 0; i < directives.length; i++) {
  12728. const def = directives[i];
  12729. // Merge the attrs in the order of matches. This assumes that the first directive is the
  12730. // component itself, so that the component has the least priority.
  12731. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
  12732. configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
  12733. saveNameToExportMap(directiveIdx, def, exportsMap);
  12734. if (def.contentQueries !== null)
  12735. tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
  12736. if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
  12737. tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
  12738. const lifeCycleHooks = def.type.prototype;
  12739. // Only push a node index into the preOrderHooks array if this is the first
  12740. // pre-order hook found on this node.
  12741. if (!preOrderHooksFound &&
  12742. (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
  12743. // We will push the actual hook function into this array later during dir instantiation.
  12744. // We cannot do it now because we must ensure hooks are registered in the same
  12745. // order that directives are created (i.e. injection order).
  12746. (tView.preOrderHooks ??= []).push(tNode.index);
  12747. preOrderHooksFound = true;
  12748. }
  12749. if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
  12750. (tView.preOrderCheckHooks ??= []).push(tNode.index);
  12751. preOrderCheckHooksFound = true;
  12752. }
  12753. directiveIdx++;
  12754. }
  12755. initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs);
  12756. }
  12757. /**
  12758. * Add `hostBindings` to the `TView.hostBindingOpCodes`.
  12759. *
  12760. * @param tView `TView` to which the `hostBindings` should be added.
  12761. * @param tNode `TNode` the element which contains the directive
  12762. * @param directiveIdx Directive index in view.
  12763. * @param directiveVarsIdx Where will the directive's vars be stored
  12764. * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
  12765. */
  12766. function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
  12767. ngDevMode && assertFirstCreatePass(tView);
  12768. const hostBindings = def.hostBindings;
  12769. if (hostBindings) {
  12770. let hostBindingOpCodes = tView.hostBindingOpCodes;
  12771. if (hostBindingOpCodes === null) {
  12772. hostBindingOpCodes = tView.hostBindingOpCodes = [];
  12773. }
  12774. const elementIndx = ~tNode.index;
  12775. if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
  12776. // Conditionally add select element so that we are more efficient in execution.
  12777. // NOTE: this is strictly not necessary and it trades code size for runtime perf.
  12778. // (We could just always add it.)
  12779. hostBindingOpCodes.push(elementIndx);
  12780. }
  12781. hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
  12782. }
  12783. }
  12784. /**
  12785. * Returns the last selected element index in the `HostBindingOpCodes`
  12786. *
  12787. * For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
  12788. * if it changes. This method returns the last index (or '0' if not found.)
  12789. *
  12790. * Selected element index are only the ones which are negative.
  12791. */
  12792. function lastSelectedElementIdx(hostBindingOpCodes) {
  12793. let i = hostBindingOpCodes.length;
  12794. while (i > 0) {
  12795. const value = hostBindingOpCodes[--i];
  12796. if (typeof value === 'number' && value < 0) {
  12797. return value;
  12798. }
  12799. }
  12800. return 0;
  12801. }
  12802. /**
  12803. * Instantiate all the directives that were previously resolved on the current node.
  12804. */
  12805. function instantiateAllDirectives(tView, lView, tNode, native) {
  12806. const start = tNode.directiveStart;
  12807. const end = tNode.directiveEnd;
  12808. // The component view needs to be created before creating the node injector
  12809. // since it is used to inject some special symbols like `ChangeDetectorRef`.
  12810. if (isComponentHost(tNode)) {
  12811. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
  12812. addComponentLogic(lView, tNode, tView.data[start + tNode.componentOffset]);
  12813. }
  12814. if (!tView.firstCreatePass) {
  12815. getOrCreateNodeInjectorForNode(tNode, lView);
  12816. }
  12817. attachPatchData(native, lView);
  12818. const initialInputs = tNode.initialInputs;
  12819. for (let i = start; i < end; i++) {
  12820. const def = tView.data[i];
  12821. const directive = getNodeInjectable(lView, tView, i, tNode);
  12822. attachPatchData(directive, lView);
  12823. if (initialInputs !== null) {
  12824. setInputsFromAttrs(lView, i - start, directive, def, tNode, initialInputs);
  12825. }
  12826. if (isComponentDef(def)) {
  12827. const componentView = getComponentLViewByIndex(tNode.index, lView);
  12828. componentView[CONTEXT] = getNodeInjectable(lView, tView, i, tNode);
  12829. }
  12830. }
  12831. }
  12832. function invokeDirectivesHostBindings(tView, lView, tNode) {
  12833. const start = tNode.directiveStart;
  12834. const end = tNode.directiveEnd;
  12835. const elementIndex = tNode.index;
  12836. const currentDirectiveIndex = getCurrentDirectiveIndex();
  12837. try {
  12838. setSelectedIndex(elementIndex);
  12839. for (let dirIndex = start; dirIndex < end; dirIndex++) {
  12840. const def = tView.data[dirIndex];
  12841. const directive = lView[dirIndex];
  12842. setCurrentDirectiveIndex(dirIndex);
  12843. if (def.hostBindings !== null || def.hostVars !== 0 || def.hostAttrs !== null) {
  12844. invokeHostBindingsInCreationMode(def, directive);
  12845. }
  12846. }
  12847. }
  12848. finally {
  12849. setSelectedIndex(-1);
  12850. setCurrentDirectiveIndex(currentDirectiveIndex);
  12851. }
  12852. }
  12853. /**
  12854. * Invoke the host bindings in creation mode.
  12855. *
  12856. * @param def `DirectiveDef` which may contain the `hostBindings` function.
  12857. * @param directive Instance of directive.
  12858. */
  12859. function invokeHostBindingsInCreationMode(def, directive) {
  12860. if (def.hostBindings !== null) {
  12861. def.hostBindings(1 /* RenderFlags.Create */, directive);
  12862. }
  12863. }
  12864. /**
  12865. * Matches the current node against all available selectors.
  12866. * If a component is matched (at most one), it is returned in first position in the array.
  12867. */
  12868. function findDirectiveDefMatches(tView, tNode) {
  12869. ngDevMode && assertFirstCreatePass(tView);
  12870. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
  12871. const registry = tView.directiveRegistry;
  12872. let matches = null;
  12873. let hostDirectiveDefs = null;
  12874. if (registry) {
  12875. for (let i = 0; i < registry.length; i++) {
  12876. const def = registry[i];
  12877. if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */ false)) {
  12878. matches || (matches = []);
  12879. if (isComponentDef(def)) {
  12880. if (ngDevMode) {
  12881. assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` +
  12882. `Please use a different tag to activate the ${stringify(def.type)} component.`);
  12883. if (isComponentHost(tNode)) {
  12884. throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type);
  12885. }
  12886. }
  12887. // Components are inserted at the front of the matches array so that their lifecycle
  12888. // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
  12889. // compatibility. This logic doesn't make sense with host directives, because it
  12890. // would allow the host directives to undo any overrides the host may have made.
  12891. // To handle this case, the host directives of components are inserted at the beginning
  12892. // of the array, followed by the component. As such, the insertion order is as follows:
  12893. // 1. Host directives belonging to the selector-matched component.
  12894. // 2. Selector-matched component.
  12895. // 3. Host directives belonging to selector-matched directives.
  12896. // 4. Selector-matched directives.
  12897. if (def.findHostDirectiveDefs !== null) {
  12898. const hostDirectiveMatches = [];
  12899. hostDirectiveDefs = hostDirectiveDefs || new Map();
  12900. def.findHostDirectiveDefs(def, hostDirectiveMatches, hostDirectiveDefs);
  12901. // Add all host directives declared on this component, followed by the component itself.
  12902. // Host directives should execute first so the host has a chance to override changes
  12903. // to the DOM made by them.
  12904. matches.unshift(...hostDirectiveMatches, def);
  12905. // Component is offset starting from the beginning of the host directives array.
  12906. const componentOffset = hostDirectiveMatches.length;
  12907. markAsComponentHost(tView, tNode, componentOffset);
  12908. }
  12909. else {
  12910. // No host directives on this component, just add the
  12911. // component def to the beginning of the matches.
  12912. matches.unshift(def);
  12913. markAsComponentHost(tView, tNode, 0);
  12914. }
  12915. }
  12916. else {
  12917. // Append any host directives to the matches first.
  12918. hostDirectiveDefs = hostDirectiveDefs || new Map();
  12919. def.findHostDirectiveDefs?.(def, matches, hostDirectiveDefs);
  12920. matches.push(def);
  12921. }
  12922. }
  12923. }
  12924. }
  12925. return matches === null ? null : [matches, hostDirectiveDefs];
  12926. }
  12927. /**
  12928. * Marks a given TNode as a component's host. This consists of:
  12929. * - setting the component offset on the TNode.
  12930. * - storing index of component's host element so it will be queued for view refresh during CD.
  12931. */
  12932. function markAsComponentHost(tView, hostTNode, componentOffset) {
  12933. ngDevMode && assertFirstCreatePass(tView);
  12934. ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1');
  12935. hostTNode.componentOffset = componentOffset;
  12936. (tView.components ??= []).push(hostTNode.index);
  12937. }
  12938. /** Caches local names and their matching directive indices for query and template lookups. */
  12939. function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
  12940. if (localRefs) {
  12941. const localNames = tNode.localNames = [];
  12942. // Local names must be stored in tNode in the same order that localRefs are defined
  12943. // in the template to ensure the data is loaded in the same slots as their refs
  12944. // in the template (for template queries).
  12945. for (let i = 0; i < localRefs.length; i += 2) {
  12946. const index = exportsMap[localRefs[i + 1]];
  12947. if (index == null)
  12948. throw new RuntimeError(-301 /* RuntimeErrorCode.EXPORT_NOT_FOUND */, ngDevMode && `Export of name '${localRefs[i + 1]}' not found!`);
  12949. localNames.push(localRefs[i], index);
  12950. }
  12951. }
  12952. }
  12953. /**
  12954. * Builds up an export map as directives are created, so local refs can be quickly mapped
  12955. * to their directive instances.
  12956. */
  12957. function saveNameToExportMap(directiveIdx, def, exportsMap) {
  12958. if (exportsMap) {
  12959. if (def.exportAs) {
  12960. for (let i = 0; i < def.exportAs.length; i++) {
  12961. exportsMap[def.exportAs[i]] = directiveIdx;
  12962. }
  12963. }
  12964. if (isComponentDef(def))
  12965. exportsMap[''] = directiveIdx;
  12966. }
  12967. }
  12968. /**
  12969. * Initializes the flags on the current node, setting all indices to the initial index,
  12970. * the directive count to 0, and adding the isComponent flag.
  12971. * @param index the initial index
  12972. */
  12973. function initTNodeFlags(tNode, index, numberOfDirectives) {
  12974. ngDevMode &&
  12975. assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
  12976. tNode.flags |= 1 /* TNodeFlags.isDirectiveHost */;
  12977. // When the first directive is created on a node, save the index
  12978. tNode.directiveStart = index;
  12979. tNode.directiveEnd = index + numberOfDirectives;
  12980. tNode.providerIndexes = index;
  12981. }
  12982. /**
  12983. * Setup directive for instantiation.
  12984. *
  12985. * We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
  12986. * as `LView`. `TView` gets the `DirectiveDef`.
  12987. *
  12988. * @param tView `TView`
  12989. * @param tNode `TNode`
  12990. * @param lView `LView`
  12991. * @param directiveIndex Index where the directive will be stored in the Expando.
  12992. * @param def `DirectiveDef`
  12993. */
  12994. function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
  12995. ngDevMode &&
  12996. assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
  12997. tView.data[directiveIndex] = def;
  12998. const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
  12999. // Even though `directiveFactory` will already be using `ɵɵdirectiveInject` in its generated code,
  13000. // we also want to support `inject()` directly from the directive constructor context so we set
  13001. // `ɵɵdirectiveInject` as the inject implementation here too.
  13002. const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
  13003. tView.blueprint[directiveIndex] = nodeInjectorFactory;
  13004. lView[directiveIndex] = nodeInjectorFactory;
  13005. registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
  13006. }
  13007. function addComponentLogic(lView, hostTNode, def) {
  13008. const native = getNativeByTNode(hostTNode, lView);
  13009. const tView = getOrCreateComponentTView(def);
  13010. // Only component views should be added to the view tree directly. Embedded views are
  13011. // accessed through their containers because they may be removed / re-added later.
  13012. const rendererFactory = lView[ENVIRONMENT].rendererFactory;
  13013. let lViewFlags = 16 /* LViewFlags.CheckAlways */;
  13014. if (def.signals) {
  13015. lViewFlags = 4096 /* LViewFlags.SignalView */;
  13016. }
  13017. else if (def.onPush) {
  13018. lViewFlags = 64 /* LViewFlags.Dirty */;
  13019. }
  13020. const componentView = addToViewTree(lView, createLView(lView, tView, null, lViewFlags, native, hostTNode, null, rendererFactory.createRenderer(native, def), null, null, null));
  13021. // Component view will always be created before any injected LContainers,
  13022. // so this is a regular element, wrap it with the component view
  13023. lView[hostTNode.index] = componentView;
  13024. }
  13025. function elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace) {
  13026. if (ngDevMode) {
  13027. assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
  13028. validateAgainstEventAttributes(name);
  13029. assertTNodeType(tNode, 2 /* TNodeType.Element */, `Attempted to set attribute \`${name}\` on a container node. ` +
  13030. `Host bindings are not valid on ng-container or ng-template.`);
  13031. }
  13032. const element = getNativeByTNode(tNode, lView);
  13033. setElementAttribute(lView[RENDERER], element, namespace, tNode.value, name, value, sanitizer);
  13034. }
  13035. function setElementAttribute(renderer, element, namespace, tagName, name, value, sanitizer) {
  13036. if (value == null) {
  13037. ngDevMode && ngDevMode.rendererRemoveAttribute++;
  13038. renderer.removeAttribute(element, name, namespace);
  13039. }
  13040. else {
  13041. ngDevMode && ngDevMode.rendererSetAttribute++;
  13042. const strValue = sanitizer == null ? renderStringify(value) : sanitizer(value, tagName || '', name);
  13043. renderer.setAttribute(element, name, strValue, namespace);
  13044. }
  13045. }
  13046. /**
  13047. * Sets initial input properties on directive instances from attribute data
  13048. *
  13049. * @param lView Current LView that is being processed.
  13050. * @param directiveIndex Index of the directive in directives array
  13051. * @param instance Instance of the directive on which to set the initial inputs
  13052. * @param def The directive def that contains the list of inputs
  13053. * @param tNode The static data for this node
  13054. */
  13055. function setInputsFromAttrs(lView, directiveIndex, instance, def, tNode, initialInputData) {
  13056. const initialInputs = initialInputData[directiveIndex];
  13057. if (initialInputs !== null) {
  13058. for (let i = 0; i < initialInputs.length;) {
  13059. const publicName = initialInputs[i++];
  13060. const privateName = initialInputs[i++];
  13061. const value = initialInputs[i++];
  13062. writeToDirectiveInput(def, instance, publicName, privateName, value);
  13063. if (ngDevMode) {
  13064. const nativeElement = getNativeByTNode(tNode, lView);
  13065. setNgReflectProperty(lView, nativeElement, tNode.type, privateName, value);
  13066. }
  13067. }
  13068. }
  13069. }
  13070. function writeToDirectiveInput(def, instance, publicName, privateName, value) {
  13071. const prevConsumer = setActiveConsumer(null);
  13072. try {
  13073. const inputTransforms = def.inputTransforms;
  13074. if (inputTransforms !== null && inputTransforms.hasOwnProperty(privateName)) {
  13075. value = inputTransforms[privateName].call(instance, value);
  13076. }
  13077. if (def.setInput !== null) {
  13078. def.setInput(instance, value, publicName, privateName);
  13079. }
  13080. else {
  13081. instance[privateName] = value;
  13082. }
  13083. }
  13084. finally {
  13085. setActiveConsumer(prevConsumer);
  13086. }
  13087. }
  13088. /**
  13089. * Generates initialInputData for a node and stores it in the template's static storage
  13090. * so subsequent template invocations don't have to recalculate it.
  13091. *
  13092. * initialInputData is an array containing values that need to be set as input properties
  13093. * for directives on this node, but only once on creation. We need this array to support
  13094. * the case where you set an @Input property of a directive using attribute-like syntax.
  13095. * e.g. if you have a `name` @Input, you can set it once like this:
  13096. *
  13097. * <my-component name="Bess"></my-component>
  13098. *
  13099. * @param inputs Input alias map that was generated from the directive def inputs.
  13100. * @param directiveIndex Index of the directive that is currently being processed.
  13101. * @param attrs Static attrs on this node.
  13102. */
  13103. function generateInitialInputs(inputs, directiveIndex, attrs) {
  13104. let inputsToStore = null;
  13105. let i = 0;
  13106. while (i < attrs.length) {
  13107. const attrName = attrs[i];
  13108. if (attrName === 0 /* AttributeMarker.NamespaceURI */) {
  13109. // We do not allow inputs on namespaced attributes.
  13110. i += 4;
  13111. continue;
  13112. }
  13113. else if (attrName === 5 /* AttributeMarker.ProjectAs */) {
  13114. // Skip over the `ngProjectAs` value.
  13115. i += 2;
  13116. continue;
  13117. }
  13118. // If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
  13119. if (typeof attrName === 'number')
  13120. break;
  13121. if (inputs.hasOwnProperty(attrName)) {
  13122. if (inputsToStore === null)
  13123. inputsToStore = [];
  13124. // Find the input's public name from the input store. Note that we can be found easier
  13125. // through the directive def, but we want to do it using the inputs store so that it can
  13126. // account for host directive aliases.
  13127. const inputConfig = inputs[attrName];
  13128. for (let j = 0; j < inputConfig.length; j += 2) {
  13129. if (inputConfig[j] === directiveIndex) {
  13130. inputsToStore.push(attrName, inputConfig[j + 1], attrs[i + 1]);
  13131. // A directive can't have multiple inputs with the same name so we can break here.
  13132. break;
  13133. }
  13134. }
  13135. }
  13136. i += 2;
  13137. }
  13138. return inputsToStore;
  13139. }
  13140. //////////////////////////
  13141. //// ViewContainer & View
  13142. //////////////////////////
  13143. /**
  13144. * Creates a LContainer, either from a container instruction, or for a ViewContainerRef.
  13145. *
  13146. * @param hostNative The host element for the LContainer
  13147. * @param hostTNode The host TNode for the LContainer
  13148. * @param currentView The parent view of the LContainer
  13149. * @param native The native comment element
  13150. * @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case
  13151. * @returns LContainer
  13152. */
  13153. function createLContainer(hostNative, currentView, native, tNode) {
  13154. ngDevMode && assertLView(currentView);
  13155. const lContainer = [
  13156. hostNative,
  13157. true,
  13158. false,
  13159. currentView,
  13160. null,
  13161. 0,
  13162. tNode,
  13163. native,
  13164. null,
  13165. null,
  13166. null, // dehydrated views
  13167. ];
  13168. ngDevMode &&
  13169. assertEqual(lContainer.length, CONTAINER_HEADER_OFFSET, 'Should allocate correct number of slots for LContainer header.');
  13170. return lContainer;
  13171. }
  13172. /** Refreshes all content queries declared by directives in a given view */
  13173. function refreshContentQueries(tView, lView) {
  13174. const contentQueries = tView.contentQueries;
  13175. if (contentQueries !== null) {
  13176. for (let i = 0; i < contentQueries.length; i += 2) {
  13177. const queryStartIdx = contentQueries[i];
  13178. const directiveDefIdx = contentQueries[i + 1];
  13179. if (directiveDefIdx !== -1) {
  13180. const directiveDef = tView.data[directiveDefIdx];
  13181. ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.');
  13182. ngDevMode &&
  13183. assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
  13184. setCurrentQueryIndex(queryStartIdx);
  13185. directiveDef.contentQueries(2 /* RenderFlags.Update */, lView[directiveDefIdx], directiveDefIdx);
  13186. }
  13187. }
  13188. }
  13189. }
  13190. /**
  13191. * Adds LView or LContainer to the end of the current view tree.
  13192. *
  13193. * This structure will be used to traverse through nested views to remove listeners
  13194. * and call onDestroy callbacks.
  13195. *
  13196. * @param lView The view where LView or LContainer should be added
  13197. * @param adjustedHostIndex Index of the view's host node in LView[], adjusted for header
  13198. * @param lViewOrLContainer The LView or LContainer to add to the view tree
  13199. * @returns The state passed in
  13200. */
  13201. function addToViewTree(lView, lViewOrLContainer) {
  13202. // TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer
  13203. // to the end of the queue, which means if the developer retrieves the LContainers from RNodes out
  13204. // of order, the change detection will run out of order, as the act of retrieving the the
  13205. // LContainer from the RNode is what adds it to the queue.
  13206. if (lView[CHILD_HEAD]) {
  13207. lView[CHILD_TAIL][NEXT] = lViewOrLContainer;
  13208. }
  13209. else {
  13210. lView[CHILD_HEAD] = lViewOrLContainer;
  13211. }
  13212. lView[CHILD_TAIL] = lViewOrLContainer;
  13213. return lViewOrLContainer;
  13214. }
  13215. ///////////////////////////////
  13216. //// Change detection
  13217. ///////////////////////////////
  13218. function executeViewQueryFn(flags, viewQueryFn, component) {
  13219. ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');
  13220. setCurrentQueryIndex(0);
  13221. const prevConsumer = setActiveConsumer(null);
  13222. try {
  13223. viewQueryFn(flags, component);
  13224. }
  13225. finally {
  13226. setActiveConsumer(prevConsumer);
  13227. }
  13228. }
  13229. ///////////////////////////////
  13230. //// Bindings & interpolations
  13231. ///////////////////////////////
  13232. /**
  13233. * Stores meta-data for a property binding to be used by TestBed's `DebugElement.properties`.
  13234. *
  13235. * In order to support TestBed's `DebugElement.properties` we need to save, for each binding:
  13236. * - a bound property name;
  13237. * - a static parts of interpolated strings;
  13238. *
  13239. * A given property metadata is saved at the binding's index in the `TView.data` (in other words, a
  13240. * property binding metadata will be stored in `TView.data` at the same index as a bound value in
  13241. * `LView`). Metadata are represented as `INTERPOLATION_DELIMITER`-delimited string with the
  13242. * following format:
  13243. * - `propertyName` for bound properties;
  13244. * - `propertyName�prefix�interpolation_static_part1�..interpolation_static_partN�suffix` for
  13245. * interpolated properties.
  13246. *
  13247. * @param tData `TData` where meta-data will be saved;
  13248. * @param tNode `TNode` that is a target of the binding;
  13249. * @param propertyName bound property name;
  13250. * @param bindingIndex binding index in `LView`
  13251. * @param interpolationParts static interpolation parts (for property interpolations)
  13252. */
  13253. function storePropertyBindingMetadata(tData, tNode, propertyName, bindingIndex, ...interpolationParts) {
  13254. // Binding meta-data are stored only the first time a given property instruction is processed.
  13255. // Since we don't have a concept of the "first update pass" we need to check for presence of the
  13256. // binding meta-data to decide if one should be stored (or if was stored already).
  13257. if (tData[bindingIndex] === null) {
  13258. if (tNode.inputs == null || !tNode.inputs[propertyName]) {
  13259. const propBindingIdxs = tNode.propertyBindings || (tNode.propertyBindings = []);
  13260. propBindingIdxs.push(bindingIndex);
  13261. let bindingMetadata = propertyName;
  13262. if (interpolationParts.length > 0) {
  13263. bindingMetadata +=
  13264. INTERPOLATION_DELIMITER + interpolationParts.join(INTERPOLATION_DELIMITER);
  13265. }
  13266. tData[bindingIndex] = bindingMetadata;
  13267. }
  13268. }
  13269. }
  13270. function getOrCreateLViewCleanup(view) {
  13271. // top level variables should not be exported for performance reasons (PERF_NOTES.md)
  13272. return view[CLEANUP] || (view[CLEANUP] = []);
  13273. }
  13274. function getOrCreateTViewCleanup(tView) {
  13275. return tView.cleanup || (tView.cleanup = []);
  13276. }
  13277. /**
  13278. * There are cases where the sub component's renderer needs to be included
  13279. * instead of the current renderer (see the componentSyntheticHost* instructions).
  13280. */
  13281. function loadComponentRenderer(currentDef, tNode, lView) {
  13282. // TODO(FW-2043): the `currentDef` is null when host bindings are invoked while creating root
  13283. // component (see packages/core/src/render3/component.ts). This is not consistent with the process
  13284. // of creating inner components, when current directive index is available in the state. In order
  13285. // to avoid relying on current def being `null` (thus special-casing root component creation), the
  13286. // process of creating root component should be unified with the process of creating inner
  13287. // components.
  13288. if (currentDef === null || isComponentDef(currentDef)) {
  13289. lView = unwrapLView(lView[tNode.index]);
  13290. }
  13291. return lView[RENDERER];
  13292. }
  13293. /** Handles an error thrown in an LView. */
  13294. function handleError(lView, error) {
  13295. const injector = lView[INJECTOR$1];
  13296. const errorHandler = injector ? injector.get(ErrorHandler, null) : null;
  13297. errorHandler && errorHandler.handleError(error);
  13298. }
  13299. /**
  13300. * Set the inputs of directives at the current node to corresponding value.
  13301. *
  13302. * @param tView The current TView
  13303. * @param lView the `LView` which contains the directives.
  13304. * @param inputs mapping between the public "input" name and privately-known,
  13305. * possibly minified, property names to write to.
  13306. * @param value Value to set.
  13307. */
  13308. function setInputsForProperty(tView, lView, inputs, publicName, value) {
  13309. for (let i = 0; i < inputs.length;) {
  13310. const index = inputs[i++];
  13311. const privateName = inputs[i++];
  13312. const instance = lView[index];
  13313. ngDevMode && assertIndexInRange(lView, index);
  13314. const def = tView.data[index];
  13315. writeToDirectiveInput(def, instance, publicName, privateName, value);
  13316. }
  13317. }
  13318. /**
  13319. * Updates a text binding at a given index in a given LView.
  13320. */
  13321. function textBindingInternal(lView, index, value) {
  13322. ngDevMode && assertString(value, 'Value should be a string');
  13323. ngDevMode && assertNotSame(value, NO_CHANGE, 'value should not be NO_CHANGE');
  13324. ngDevMode && assertIndexInRange(lView, index);
  13325. const element = getNativeByIndex(index, lView);
  13326. ngDevMode && assertDefined(element, 'native element should exist');
  13327. updateTextNode(lView[RENDERER], element, value);
  13328. }
  13329. function renderComponent(hostLView, componentHostIdx) {
  13330. ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
  13331. const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
  13332. const componentTView = componentView[TVIEW];
  13333. syncViewWithBlueprint(componentTView, componentView);
  13334. const hostRNode = componentView[HOST];
  13335. // Populate an LView with hydration info retrieved from the DOM via TransferState.
  13336. if (hostRNode !== null && componentView[HYDRATION] === null) {
  13337. componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR$1]);
  13338. }
  13339. renderView(componentTView, componentView, componentView[CONTEXT]);
  13340. }
  13341. /**
  13342. * Syncs an LView instance with its blueprint if they have gotten out of sync.
  13343. *
  13344. * Typically, blueprints and their view instances should always be in sync, so the loop here
  13345. * will be skipped. However, consider this case of two components side-by-side:
  13346. *
  13347. * App template:
  13348. * ```
  13349. * <comp></comp>
  13350. * <comp></comp>
  13351. * ```
  13352. *
  13353. * The following will happen:
  13354. * 1. App template begins processing.
  13355. * 2. First <comp> is matched as a component and its LView is created.
  13356. * 3. Second <comp> is matched as a component and its LView is created.
  13357. * 4. App template completes processing, so it's time to check child templates.
  13358. * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
  13359. * 6. Second <comp> template is checked. Its blueprint has been updated by the first
  13360. * <comp> template, but its LView was created before this update, so it is out of sync.
  13361. *
  13362. * Note that embedded views inside ngFor loops will never be out of sync because these views
  13363. * are processed as soon as they are created.
  13364. *
  13365. * @param tView The `TView` that contains the blueprint for syncing
  13366. * @param lView The view to sync
  13367. */
  13368. function syncViewWithBlueprint(tView, lView) {
  13369. for (let i = lView.length; i < tView.blueprint.length; i++) {
  13370. lView.push(tView.blueprint[i]);
  13371. }
  13372. }
  13373. /**
  13374. * Processes a view in the creation mode. This includes a number of steps in a specific order:
  13375. * - creating view query functions (if any);
  13376. * - executing a template function in the creation mode;
  13377. * - updating static queries (if any);
  13378. * - creating child components defined in a given view.
  13379. */
  13380. function renderView(tView, lView, context) {
  13381. ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
  13382. enterView(lView);
  13383. try {
  13384. const viewQuery = tView.viewQuery;
  13385. if (viewQuery !== null) {
  13386. executeViewQueryFn(1 /* RenderFlags.Create */, viewQuery, context);
  13387. }
  13388. // Execute a template associated with this view, if it exists. A template function might not be
  13389. // defined for the root component views.
  13390. const templateFn = tView.template;
  13391. if (templateFn !== null) {
  13392. executeTemplate(tView, lView, templateFn, 1 /* RenderFlags.Create */, context);
  13393. }
  13394. // This needs to be set before children are processed to support recursive components.
  13395. // This must be set to false immediately after the first creation run because in an
  13396. // ngFor loop, all the views will be created together before update mode runs and turns
  13397. // off firstCreatePass. If we don't set it here, instances will perform directive
  13398. // matching, etc again and again.
  13399. if (tView.firstCreatePass) {
  13400. tView.firstCreatePass = false;
  13401. }
  13402. // We resolve content queries specifically marked as `static` in creation mode. Dynamic
  13403. // content queries are resolved during change detection (i.e. update mode), after embedded
  13404. // views are refreshed (see block above).
  13405. if (tView.staticContentQueries) {
  13406. refreshContentQueries(tView, lView);
  13407. }
  13408. // We must materialize query results before child components are processed
  13409. // in case a child component has projected a container. The LContainer needs
  13410. // to exist so the embedded views are properly attached by the container.
  13411. if (tView.staticViewQueries) {
  13412. executeViewQueryFn(2 /* RenderFlags.Update */, tView.viewQuery, context);
  13413. }
  13414. // Render child component views.
  13415. const components = tView.components;
  13416. if (components !== null) {
  13417. renderChildComponents(lView, components);
  13418. }
  13419. }
  13420. catch (error) {
  13421. // If we didn't manage to get past the first template pass due to
  13422. // an error, mark the view as corrupted so we can try to recover.
  13423. if (tView.firstCreatePass) {
  13424. tView.incompleteFirstPass = true;
  13425. tView.firstCreatePass = false;
  13426. }
  13427. throw error;
  13428. }
  13429. finally {
  13430. lView[FLAGS] &= ~4 /* LViewFlags.CreationMode */;
  13431. leaveView();
  13432. }
  13433. }
  13434. /** Renders child components in the current view (creation mode). */
  13435. function renderChildComponents(hostLView, components) {
  13436. for (let i = 0; i < components.length; i++) {
  13437. renderComponent(hostLView, components[i]);
  13438. }
  13439. }
  13440. /**
  13441. * Tracks all effects registered within a given application and runs them via `flush`.
  13442. */
  13443. class EffectManager {
  13444. constructor() {
  13445. this.all = new Set();
  13446. this.queue = new Map();
  13447. }
  13448. create(effectFn, destroyRef, allowSignalWrites) {
  13449. const zone = (typeof Zone === 'undefined') ? null : Zone.current;
  13450. const w = watch(effectFn, (watch) => {
  13451. if (!this.all.has(watch)) {
  13452. return;
  13453. }
  13454. this.queue.set(watch, zone);
  13455. }, allowSignalWrites);
  13456. this.all.add(w);
  13457. // Effects start dirty.
  13458. w.notify();
  13459. let unregisterOnDestroy;
  13460. const destroy = () => {
  13461. w.cleanup();
  13462. unregisterOnDestroy?.();
  13463. this.all.delete(w);
  13464. this.queue.delete(w);
  13465. };
  13466. unregisterOnDestroy = destroyRef?.onDestroy(destroy);
  13467. return {
  13468. destroy,
  13469. };
  13470. }
  13471. flush() {
  13472. if (this.queue.size === 0) {
  13473. return;
  13474. }
  13475. for (const [watch, zone] of this.queue) {
  13476. this.queue.delete(watch);
  13477. if (zone) {
  13478. zone.run(() => watch.run());
  13479. }
  13480. else {
  13481. watch.run();
  13482. }
  13483. }
  13484. }
  13485. get isQueueEmpty() {
  13486. return this.queue.size === 0;
  13487. }
  13488. /** @nocollapse */
  13489. static { this.ɵprov = ɵɵdefineInjectable({
  13490. token: EffectManager,
  13491. providedIn: 'root',
  13492. factory: () => new EffectManager(),
  13493. }); }
  13494. }
  13495. /**
  13496. * Create a global `Effect` for the given reactive function.
  13497. *
  13498. * @developerPreview
  13499. */
  13500. function effect(effectFn, options) {
  13501. !options?.injector && assertInInjectionContext(effect);
  13502. const injector = options?.injector ?? inject$1(Injector);
  13503. const effectManager = injector.get(EffectManager);
  13504. const destroyRef = options?.manualCleanup !== true ? injector.get(DestroyRef) : null;
  13505. return effectManager.create(effectFn, destroyRef, !!options?.allowSignalWrites);
  13506. }
  13507. /**
  13508. * Compute the static styling (class/style) from `TAttributes`.
  13509. *
  13510. * This function should be called during `firstCreatePass` only.
  13511. *
  13512. * @param tNode The `TNode` into which the styling information should be loaded.
  13513. * @param attrs `TAttributes` containing the styling information.
  13514. * @param writeToHost Where should the resulting static styles be written?
  13515. * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost`
  13516. * - `true` Write to `TNode.styles` / `TNode.classes`
  13517. */
  13518. function computeStaticStyling(tNode, attrs, writeToHost) {
  13519. ngDevMode &&
  13520. assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only');
  13521. let styles = writeToHost ? tNode.styles : null;
  13522. let classes = writeToHost ? tNode.classes : null;
  13523. let mode = 0;
  13524. if (attrs !== null) {
  13525. for (let i = 0; i < attrs.length; i++) {
  13526. const value = attrs[i];
  13527. if (typeof value === 'number') {
  13528. mode = value;
  13529. }
  13530. else if (mode == 1 /* AttributeMarker.Classes */) {
  13531. classes = concatStringsWithSpace(classes, value);
  13532. }
  13533. else if (mode == 2 /* AttributeMarker.Styles */) {
  13534. const style = value;
  13535. const styleValue = attrs[++i];
  13536. styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';');
  13537. }
  13538. }
  13539. }
  13540. writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles;
  13541. writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes;
  13542. }
  13543. function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
  13544. while (tNode !== null) {
  13545. ngDevMode &&
  13546. assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
  13547. const lNode = lView[tNode.index];
  13548. if (lNode !== null) {
  13549. result.push(unwrapRNode(lNode));
  13550. }
  13551. // A given lNode can represent either a native node or a LContainer (when it is a host of a
  13552. // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
  13553. // from the views in this container.
  13554. if (isLContainer(lNode)) {
  13555. collectNativeNodesInLContainer(lNode, result);
  13556. }
  13557. const tNodeType = tNode.type;
  13558. if (tNodeType & 8 /* TNodeType.ElementContainer */) {
  13559. collectNativeNodes(tView, lView, tNode.child, result);
  13560. }
  13561. else if (tNodeType & 32 /* TNodeType.Icu */) {
  13562. const nextRNode = icuContainerIterate(tNode, lView);
  13563. let rNode;
  13564. while (rNode = nextRNode()) {
  13565. result.push(rNode);
  13566. }
  13567. }
  13568. else if (tNodeType & 16 /* TNodeType.Projection */) {
  13569. const nodesInSlot = getProjectionNodes(lView, tNode);
  13570. if (Array.isArray(nodesInSlot)) {
  13571. result.push(...nodesInSlot);
  13572. }
  13573. else {
  13574. const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
  13575. ngDevMode && assertParentView(parentView);
  13576. collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
  13577. }
  13578. }
  13579. tNode = isProjection ? tNode.projectionNext : tNode.next;
  13580. }
  13581. return result;
  13582. }
  13583. /**
  13584. * Collects all root nodes in all views in a given LContainer.
  13585. */
  13586. function collectNativeNodesInLContainer(lContainer, result) {
  13587. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  13588. const lViewInAContainer = lContainer[i];
  13589. const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
  13590. if (lViewFirstChildTNode !== null) {
  13591. collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
  13592. }
  13593. }
  13594. // When an LContainer is created, the anchor (comment) node is:
  13595. // - (1) either reused in case of an ElementContainer (<ng-container>)
  13596. // - (2) or a new comment node is created
  13597. // In the first case, the anchor comment node would be added to the final
  13598. // list by the code in the `collectNativeNodes` function
  13599. // (see the `result.push(unwrapRNode(lNode))` line), but the second
  13600. // case requires extra handling: the anchor node needs to be added to the
  13601. // final list manually. See additional information in the `createAnchorNode`
  13602. // function in the `view_container_ref.ts`.
  13603. //
  13604. // In the first case, the same reference would be stored in the `NATIVE`
  13605. // and `HOST` slots in an LContainer. Otherwise, this is the second case and
  13606. // we should add an element to the final list.
  13607. if (lContainer[NATIVE] !== lContainer[HOST]) {
  13608. result.push(lContainer[NATIVE]);
  13609. }
  13610. }
  13611. function detectChangesInternal(tView, lView, context, notifyErrorHandler = true) {
  13612. const environment = lView[ENVIRONMENT];
  13613. const rendererFactory = environment.rendererFactory;
  13614. const afterRenderEventManager = environment.afterRenderEventManager;
  13615. // Check no changes mode is a dev only mode used to verify that bindings have not changed
  13616. // since they were assigned. We do not want to invoke renderer factory functions in that mode
  13617. // to avoid any possible side-effects.
  13618. const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode();
  13619. if (!checkNoChangesMode) {
  13620. rendererFactory.begin?.();
  13621. afterRenderEventManager?.begin();
  13622. }
  13623. try {
  13624. refreshView(tView, lView, tView.template, context);
  13625. }
  13626. catch (error) {
  13627. if (notifyErrorHandler) {
  13628. handleError(lView, error);
  13629. }
  13630. throw error;
  13631. }
  13632. finally {
  13633. if (!checkNoChangesMode) {
  13634. rendererFactory.end?.();
  13635. // One final flush of the effects queue to catch any effects created in `ngAfterViewInit` or
  13636. // other post-order hooks.
  13637. environment.effectManager?.flush();
  13638. // Invoke all callbacks registered via `after*Render`, if needed.
  13639. afterRenderEventManager?.end();
  13640. }
  13641. }
  13642. }
  13643. function checkNoChangesInternal(tView, lView, context, notifyErrorHandler = true) {
  13644. setIsInCheckNoChangesMode(true);
  13645. try {
  13646. detectChangesInternal(tView, lView, context, notifyErrorHandler);
  13647. }
  13648. finally {
  13649. setIsInCheckNoChangesMode(false);
  13650. }
  13651. }
  13652. /**
  13653. * Synchronously perform change detection on a component (and possibly its sub-components).
  13654. *
  13655. * This function triggers change detection in a synchronous way on a component.
  13656. *
  13657. * @param component The component which the change detection should be performed on.
  13658. */
  13659. function detectChanges(component) {
  13660. const view = getComponentViewByInstance(component);
  13661. detectChangesInternal(view[TVIEW], view, component);
  13662. }
  13663. /**
  13664. * Processes a view in update mode. This includes a number of steps in a specific order:
  13665. * - executing a template function in update mode;
  13666. * - executing hooks;
  13667. * - refreshing queries;
  13668. * - setting host bindings;
  13669. * - refreshing child (embedded and component) views.
  13670. */
  13671. function refreshView(tView, lView, templateFn, context) {
  13672. ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
  13673. const flags = lView[FLAGS];
  13674. if ((flags & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */)
  13675. return;
  13676. // Check no changes mode is a dev only mode used to verify that bindings have not changed
  13677. // since they were assigned. We do not want to execute lifecycle hooks in that mode.
  13678. const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
  13679. !isInCheckNoChangesPass && lView[ENVIRONMENT].effectManager?.flush();
  13680. enterView(lView);
  13681. try {
  13682. resetPreOrderHookFlags(lView);
  13683. setBindingIndex(tView.bindingStartIndex);
  13684. if (templateFn !== null) {
  13685. executeTemplate(tView, lView, templateFn, 2 /* RenderFlags.Update */, context);
  13686. }
  13687. const hooksInitPhaseCompleted = (flags & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
  13688. // execute pre-order hooks (OnInit, OnChanges, DoCheck)
  13689. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  13690. if (!isInCheckNoChangesPass) {
  13691. if (hooksInitPhaseCompleted) {
  13692. const preOrderCheckHooks = tView.preOrderCheckHooks;
  13693. if (preOrderCheckHooks !== null) {
  13694. executeCheckHooks(lView, preOrderCheckHooks, null);
  13695. }
  13696. }
  13697. else {
  13698. const preOrderHooks = tView.preOrderHooks;
  13699. if (preOrderHooks !== null) {
  13700. executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, null);
  13701. }
  13702. incrementInitPhaseFlags(lView, 0 /* InitPhaseState.OnInitHooksToBeRun */);
  13703. }
  13704. }
  13705. // First mark transplanted views that are declared in this lView as needing a refresh at their
  13706. // insertion points. This is needed to avoid the situation where the template is defined in this
  13707. // `LView` but its declaration appears after the insertion component.
  13708. markTransplantedViewsForRefresh(lView);
  13709. detectChangesInEmbeddedViews(lView, 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */);
  13710. // Content query results must be refreshed before content hooks are called.
  13711. if (tView.contentQueries !== null) {
  13712. refreshContentQueries(tView, lView);
  13713. }
  13714. // execute content hooks (AfterContentInit, AfterContentChecked)
  13715. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  13716. if (!isInCheckNoChangesPass) {
  13717. if (hooksInitPhaseCompleted) {
  13718. const contentCheckHooks = tView.contentCheckHooks;
  13719. if (contentCheckHooks !== null) {
  13720. executeCheckHooks(lView, contentCheckHooks);
  13721. }
  13722. }
  13723. else {
  13724. const contentHooks = tView.contentHooks;
  13725. if (contentHooks !== null) {
  13726. executeInitAndCheckHooks(lView, contentHooks, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
  13727. }
  13728. incrementInitPhaseFlags(lView, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
  13729. }
  13730. }
  13731. processHostBindingOpCodes(tView, lView);
  13732. // Refresh child component views.
  13733. const components = tView.components;
  13734. if (components !== null) {
  13735. detectChangesInChildComponents(lView, components, 0 /* ChangeDetectionMode.Global */);
  13736. }
  13737. // View queries must execute after refreshing child components because a template in this view
  13738. // could be inserted in a child component. If the view query executes before child component
  13739. // refresh, the template might not yet be inserted.
  13740. const viewQuery = tView.viewQuery;
  13741. if (viewQuery !== null) {
  13742. executeViewQueryFn(2 /* RenderFlags.Update */, viewQuery, context);
  13743. }
  13744. // execute view hooks (AfterViewInit, AfterViewChecked)
  13745. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  13746. if (!isInCheckNoChangesPass) {
  13747. if (hooksInitPhaseCompleted) {
  13748. const viewCheckHooks = tView.viewCheckHooks;
  13749. if (viewCheckHooks !== null) {
  13750. executeCheckHooks(lView, viewCheckHooks);
  13751. }
  13752. }
  13753. else {
  13754. const viewHooks = tView.viewHooks;
  13755. if (viewHooks !== null) {
  13756. executeInitAndCheckHooks(lView, viewHooks, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
  13757. }
  13758. incrementInitPhaseFlags(lView, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
  13759. }
  13760. }
  13761. if (tView.firstUpdatePass === true) {
  13762. // We need to make sure that we only flip the flag on successful `refreshView` only
  13763. // Don't do this in `finally` block.
  13764. // If we did this in `finally` block then an exception could block the execution of styling
  13765. // instructions which in turn would be unable to insert themselves into the styling linked
  13766. // list. The result of this would be that if the exception would not be throw on subsequent CD
  13767. // the styling would be unable to process it data and reflect to the DOM.
  13768. tView.firstUpdatePass = false;
  13769. }
  13770. // Do not reset the dirty state when running in check no changes mode. We don't want components
  13771. // to behave differently depending on whether check no changes is enabled or not. For example:
  13772. // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to
  13773. // refresh a `NgClass` binding should work. If we would reset the dirty state in the check
  13774. // no changes cycle, the component would be not be dirty for the next update pass. This would
  13775. // be different in production mode where the component dirty state is not reset.
  13776. if (!isInCheckNoChangesPass) {
  13777. lView[FLAGS] &= ~(64 /* LViewFlags.Dirty */ | 8 /* LViewFlags.FirstLViewPass */);
  13778. }
  13779. clearViewRefreshFlag(lView);
  13780. }
  13781. finally {
  13782. leaveView();
  13783. }
  13784. }
  13785. /**
  13786. * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
  13787. * them by executing an associated template function.
  13788. */
  13789. function detectChangesInEmbeddedViews(lView, mode) {
  13790. for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
  13791. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  13792. const embeddedLView = lContainer[i];
  13793. detectChangesInView(embeddedLView, mode);
  13794. }
  13795. }
  13796. }
  13797. /**
  13798. * Mark transplanted views as needing to be refreshed at their insertion points.
  13799. *
  13800. * @param lView The `LView` that may have transplanted views.
  13801. */
  13802. function markTransplantedViewsForRefresh(lView) {
  13803. for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
  13804. if (!lContainer[HAS_TRANSPLANTED_VIEWS])
  13805. continue;
  13806. const movedViews = lContainer[MOVED_VIEWS];
  13807. ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS');
  13808. for (let i = 0; i < movedViews.length; i++) {
  13809. const movedLView = movedViews[i];
  13810. const insertionLContainer = movedLView[PARENT];
  13811. ngDevMode && assertLContainer(insertionLContainer);
  13812. markViewForRefresh(movedLView);
  13813. }
  13814. }
  13815. }
  13816. /**
  13817. * Detects changes in a component by entering the component view and processing its bindings,
  13818. * queries, etc. if it is CheckAlways, OnPush and Dirty, etc.
  13819. *
  13820. * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
  13821. */
  13822. function detectChangesInComponent(hostLView, componentHostIdx, mode) {
  13823. ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
  13824. const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
  13825. detectChangesInView(componentView, mode);
  13826. }
  13827. /**
  13828. * Visits a view as part of change detection traversal.
  13829. *
  13830. * - If the view is detached, no additional traversal happens.
  13831. *
  13832. * The view is refreshed if:
  13833. * - If the view is CheckAlways or Dirty and ChangeDetectionMode is `Global`
  13834. * - If the view has the `RefreshTransplantedView` flag
  13835. *
  13836. * The view is not refreshed, but descendants are traversed in `ChangeDetectionMode.Targeted` if the
  13837. * view has a non-zero TRANSPLANTED_VIEWS_TO_REFRESH counter.
  13838. *
  13839. */
  13840. function detectChangesInView(lView, mode) {
  13841. if (!viewAttachedToChangeDetector(lView)) {
  13842. return;
  13843. }
  13844. const tView = lView[TVIEW];
  13845. const flags = lView[FLAGS];
  13846. if ((flags & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */) &&
  13847. mode === 0 /* ChangeDetectionMode.Global */) ||
  13848. flags & 1024 /* LViewFlags.RefreshView */ ||
  13849. mode === 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */) {
  13850. refreshView(tView, lView, tView.template, lView[CONTEXT]);
  13851. }
  13852. else if (lView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
  13853. detectChangesInEmbeddedViews(lView, 1 /* ChangeDetectionMode.Targeted */);
  13854. const components = tView.components;
  13855. if (components !== null) {
  13856. detectChangesInChildComponents(lView, components, 1 /* ChangeDetectionMode.Targeted */);
  13857. }
  13858. }
  13859. }
  13860. /** Refreshes child components in the current view (update mode). */
  13861. function detectChangesInChildComponents(hostLView, components, mode) {
  13862. for (let i = 0; i < components.length; i++) {
  13863. detectChangesInComponent(hostLView, components[i], mode);
  13864. }
  13865. }
  13866. class ViewRef {
  13867. get rootNodes() {
  13868. const lView = this._lView;
  13869. const tView = lView[TVIEW];
  13870. return collectNativeNodes(tView, lView, tView.firstChild, []);
  13871. }
  13872. constructor(
  13873. /**
  13874. * This represents `LView` associated with the component when ViewRef is a ChangeDetectorRef.
  13875. *
  13876. * When ViewRef is created for a dynamic component, this also represents the `LView` for the
  13877. * component.
  13878. *
  13879. * For a "regular" ViewRef created for an embedded view, this is the `LView` for the embedded
  13880. * view.
  13881. *
  13882. * @internal
  13883. */
  13884. _lView,
  13885. /**
  13886. * This represents the `LView` associated with the point where `ChangeDetectorRef` was
  13887. * requested.
  13888. *
  13889. * This may be different from `_lView` if the `_cdRefInjectingView` is an embedded view.
  13890. */
  13891. _cdRefInjectingView) {
  13892. this._lView = _lView;
  13893. this._cdRefInjectingView = _cdRefInjectingView;
  13894. this._appRef = null;
  13895. this._attachedToViewContainer = false;
  13896. }
  13897. get context() {
  13898. return this._lView[CONTEXT];
  13899. }
  13900. set context(value) {
  13901. this._lView[CONTEXT] = value;
  13902. }
  13903. get destroyed() {
  13904. return (this._lView[FLAGS] & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */;
  13905. }
  13906. destroy() {
  13907. if (this._appRef) {
  13908. this._appRef.detachView(this);
  13909. }
  13910. else if (this._attachedToViewContainer) {
  13911. const parent = this._lView[PARENT];
  13912. if (isLContainer(parent)) {
  13913. const viewRefs = parent[VIEW_REFS];
  13914. const index = viewRefs ? viewRefs.indexOf(this) : -1;
  13915. if (index > -1) {
  13916. ngDevMode &&
  13917. assertEqual(index, parent.indexOf(this._lView) - CONTAINER_HEADER_OFFSET, 'An attached view should be in the same position within its container as its ViewRef in the VIEW_REFS array.');
  13918. detachView(parent, index);
  13919. removeFromArray(viewRefs, index);
  13920. }
  13921. }
  13922. this._attachedToViewContainer = false;
  13923. }
  13924. destroyLView(this._lView[TVIEW], this._lView);
  13925. }
  13926. onDestroy(callback) {
  13927. storeLViewOnDestroy(this._lView, callback);
  13928. }
  13929. /**
  13930. * Marks a view and all of its ancestors dirty.
  13931. *
  13932. * This can be used to ensure an {@link ChangeDetectionStrategy#OnPush} component is
  13933. * checked when it needs to be re-rendered but the two normal triggers haven't marked it
  13934. * dirty (i.e. inputs haven't changed and events haven't fired in the view).
  13935. *
  13936. * <!-- TODO: Add a link to a chapter on OnPush components -->
  13937. *
  13938. * @usageNotes
  13939. * ### Example
  13940. *
  13941. * ```typescript
  13942. * @Component({
  13943. * selector: 'app-root',
  13944. * template: `Number of ticks: {{numberOfTicks}}`
  13945. * changeDetection: ChangeDetectionStrategy.OnPush,
  13946. * })
  13947. * class AppComponent {
  13948. * numberOfTicks = 0;
  13949. *
  13950. * constructor(private ref: ChangeDetectorRef) {
  13951. * setInterval(() => {
  13952. * this.numberOfTicks++;
  13953. * // the following is required, otherwise the view will not be updated
  13954. * this.ref.markForCheck();
  13955. * }, 1000);
  13956. * }
  13957. * }
  13958. * ```
  13959. */
  13960. markForCheck() {
  13961. markViewDirty(this._cdRefInjectingView || this._lView);
  13962. }
  13963. /**
  13964. * Detaches the view from the change detection tree.
  13965. *
  13966. * Detached views will not be checked during change detection runs until they are
  13967. * re-attached, even if they are dirty. `detach` can be used in combination with
  13968. * {@link ChangeDetectorRef#detectChanges} to implement local change
  13969. * detection checks.
  13970. *
  13971. * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
  13972. * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
  13973. *
  13974. * @usageNotes
  13975. * ### Example
  13976. *
  13977. * The following example defines a component with a large list of readonly data.
  13978. * Imagine the data changes constantly, many times per second. For performance reasons,
  13979. * we want to check and update the list every five seconds. We can do that by detaching
  13980. * the component's change detector and doing a local check every five seconds.
  13981. *
  13982. * ```typescript
  13983. * class DataProvider {
  13984. * // in a real application the returned data will be different every time
  13985. * get data() {
  13986. * return [1,2,3,4,5];
  13987. * }
  13988. * }
  13989. *
  13990. * @Component({
  13991. * selector: 'giant-list',
  13992. * template: `
  13993. * <li *ngFor="let d of dataProvider.data">Data {{d}}</li>
  13994. * `,
  13995. * })
  13996. * class GiantList {
  13997. * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
  13998. * ref.detach();
  13999. * setInterval(() => {
  14000. * this.ref.detectChanges();
  14001. * }, 5000);
  14002. * }
  14003. * }
  14004. *
  14005. * @Component({
  14006. * selector: 'app',
  14007. * providers: [DataProvider],
  14008. * template: `
  14009. * <giant-list><giant-list>
  14010. * `,
  14011. * })
  14012. * class App {
  14013. * }
  14014. * ```
  14015. */
  14016. detach() {
  14017. this._lView[FLAGS] &= ~128 /* LViewFlags.Attached */;
  14018. }
  14019. /**
  14020. * Re-attaches a view to the change detection tree.
  14021. *
  14022. * This can be used to re-attach views that were previously detached from the tree
  14023. * using {@link ChangeDetectorRef#detach}. Views are attached to the tree by default.
  14024. *
  14025. * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
  14026. *
  14027. * @usageNotes
  14028. * ### Example
  14029. *
  14030. * The following example creates a component displaying `live` data. The component will detach
  14031. * its change detector from the main change detector tree when the component's live property
  14032. * is set to false.
  14033. *
  14034. * ```typescript
  14035. * class DataProvider {
  14036. * data = 1;
  14037. *
  14038. * constructor() {
  14039. * setInterval(() => {
  14040. * this.data = this.data * 2;
  14041. * }, 500);
  14042. * }
  14043. * }
  14044. *
  14045. * @Component({
  14046. * selector: 'live-data',
  14047. * inputs: ['live'],
  14048. * template: 'Data: {{dataProvider.data}}'
  14049. * })
  14050. * class LiveData {
  14051. * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
  14052. *
  14053. * set live(value) {
  14054. * if (value) {
  14055. * this.ref.reattach();
  14056. * } else {
  14057. * this.ref.detach();
  14058. * }
  14059. * }
  14060. * }
  14061. *
  14062. * @Component({
  14063. * selector: 'app-root',
  14064. * providers: [DataProvider],
  14065. * template: `
  14066. * Live Update: <input type="checkbox" [(ngModel)]="live">
  14067. * <live-data [live]="live"><live-data>
  14068. * `,
  14069. * })
  14070. * class AppComponent {
  14071. * live = true;
  14072. * }
  14073. * ```
  14074. */
  14075. reattach() {
  14076. this._lView[FLAGS] |= 128 /* LViewFlags.Attached */;
  14077. }
  14078. /**
  14079. * Checks the view and its children.
  14080. *
  14081. * This can also be used in combination with {@link ChangeDetectorRef#detach} to implement
  14082. * local change detection checks.
  14083. *
  14084. * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
  14085. * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
  14086. *
  14087. * @usageNotes
  14088. * ### Example
  14089. *
  14090. * The following example defines a component with a large list of readonly data.
  14091. * Imagine, the data changes constantly, many times per second. For performance reasons,
  14092. * we want to check and update the list every five seconds.
  14093. *
  14094. * We can do that by detaching the component's change detector and doing a local change detection
  14095. * check every five seconds.
  14096. *
  14097. * See {@link ChangeDetectorRef#detach} for more information.
  14098. */
  14099. detectChanges() {
  14100. detectChangesInternal(this._lView[TVIEW], this._lView, this.context);
  14101. }
  14102. /**
  14103. * Checks the change detector and its children, and throws if any changes are detected.
  14104. *
  14105. * This is used in development mode to verify that running change detection doesn't
  14106. * introduce other changes.
  14107. */
  14108. checkNoChanges() {
  14109. if (ngDevMode) {
  14110. checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context);
  14111. }
  14112. }
  14113. attachToViewContainerRef() {
  14114. if (this._appRef) {
  14115. throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached directly to the ApplicationRef!');
  14116. }
  14117. this._attachedToViewContainer = true;
  14118. }
  14119. detachFromAppRef() {
  14120. this._appRef = null;
  14121. detachViewFromDOM(this._lView[TVIEW], this._lView);
  14122. }
  14123. attachToAppRef(appRef) {
  14124. if (this._attachedToViewContainer) {
  14125. throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached to a ViewContainer!');
  14126. }
  14127. this._appRef = appRef;
  14128. }
  14129. }
  14130. /** @internal */
  14131. class RootViewRef extends ViewRef {
  14132. constructor(_view) {
  14133. super(_view);
  14134. this._view = _view;
  14135. }
  14136. detectChanges() {
  14137. const lView = this._view;
  14138. const tView = lView[TVIEW];
  14139. const context = lView[CONTEXT];
  14140. detectChangesInternal(tView, lView, context, false);
  14141. }
  14142. checkNoChanges() {
  14143. if (ngDevMode) {
  14144. const lView = this._view;
  14145. const tView = lView[TVIEW];
  14146. const context = lView[CONTEXT];
  14147. checkNoChangesInternal(tView, lView, context, false);
  14148. }
  14149. }
  14150. get context() {
  14151. return null;
  14152. }
  14153. }
  14154. class ComponentFactoryResolver extends ComponentFactoryResolver$1 {
  14155. /**
  14156. * @param ngModule The NgModuleRef to which all resolved factories are bound.
  14157. */
  14158. constructor(ngModule) {
  14159. super();
  14160. this.ngModule = ngModule;
  14161. }
  14162. resolveComponentFactory(component) {
  14163. ngDevMode && assertComponentType(component);
  14164. const componentDef = getComponentDef$1(component);
  14165. return new ComponentFactory(componentDef, this.ngModule);
  14166. }
  14167. }
  14168. function toRefArray(map) {
  14169. const array = [];
  14170. for (let nonMinified in map) {
  14171. if (map.hasOwnProperty(nonMinified)) {
  14172. const minified = map[nonMinified];
  14173. array.push({ propName: minified, templateName: nonMinified });
  14174. }
  14175. }
  14176. return array;
  14177. }
  14178. function getNamespace(elementName) {
  14179. const name = elementName.toLowerCase();
  14180. return name === 'svg' ? SVG_NAMESPACE : (name === 'math' ? MATH_ML_NAMESPACE : null);
  14181. }
  14182. /**
  14183. * Injector that looks up a value using a specific injector, before falling back to the module
  14184. * injector. Used primarily when creating components or embedded views dynamically.
  14185. */
  14186. class ChainedInjector {
  14187. constructor(injector, parentInjector) {
  14188. this.injector = injector;
  14189. this.parentInjector = parentInjector;
  14190. }
  14191. get(token, notFoundValue, flags) {
  14192. flags = convertToBitFlags(flags);
  14193. const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
  14194. if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
  14195. notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
  14196. // Return the value from the root element injector when
  14197. // - it provides it
  14198. // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
  14199. // - the module injector should not be checked
  14200. // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
  14201. return value;
  14202. }
  14203. return this.parentInjector.get(token, notFoundValue, flags);
  14204. }
  14205. }
  14206. /**
  14207. * ComponentFactory interface implementation.
  14208. */
  14209. class ComponentFactory extends ComponentFactory$1 {
  14210. get inputs() {
  14211. const componentDef = this.componentDef;
  14212. const inputTransforms = componentDef.inputTransforms;
  14213. const refArray = toRefArray(componentDef.inputs);
  14214. if (inputTransforms !== null) {
  14215. for (const input of refArray) {
  14216. if (inputTransforms.hasOwnProperty(input.propName)) {
  14217. input.transform = inputTransforms[input.propName];
  14218. }
  14219. }
  14220. }
  14221. return refArray;
  14222. }
  14223. get outputs() {
  14224. return toRefArray(this.componentDef.outputs);
  14225. }
  14226. /**
  14227. * @param componentDef The component definition.
  14228. * @param ngModule The NgModuleRef to which the factory is bound.
  14229. */
  14230. constructor(componentDef, ngModule) {
  14231. super();
  14232. this.componentDef = componentDef;
  14233. this.ngModule = ngModule;
  14234. this.componentType = componentDef.type;
  14235. this.selector = stringifyCSSSelectorList(componentDef.selectors);
  14236. this.ngContentSelectors =
  14237. componentDef.ngContentSelectors ? componentDef.ngContentSelectors : [];
  14238. this.isBoundToModule = !!ngModule;
  14239. }
  14240. create(injector, projectableNodes, rootSelectorOrNode, environmentInjector) {
  14241. environmentInjector = environmentInjector || this.ngModule;
  14242. let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector ?
  14243. environmentInjector :
  14244. environmentInjector?.injector;
  14245. if (realEnvironmentInjector && this.componentDef.getStandaloneInjector !== null) {
  14246. realEnvironmentInjector = this.componentDef.getStandaloneInjector(realEnvironmentInjector) ||
  14247. realEnvironmentInjector;
  14248. }
  14249. const rootViewInjector = realEnvironmentInjector ? new ChainedInjector(injector, realEnvironmentInjector) : injector;
  14250. const rendererFactory = rootViewInjector.get(RendererFactory2, null);
  14251. if (rendererFactory === null) {
  14252. throw new RuntimeError(407 /* RuntimeErrorCode.RENDERER_NOT_FOUND */, ngDevMode &&
  14253. 'Angular was not able to inject a renderer (RendererFactory2). ' +
  14254. 'Likely this is due to a broken DI hierarchy. ' +
  14255. 'Make sure that any injector used to create this component has a correct parent.');
  14256. }
  14257. const sanitizer = rootViewInjector.get(Sanitizer, null);
  14258. const effectManager = rootViewInjector.get(EffectManager, null);
  14259. const afterRenderEventManager = rootViewInjector.get(AfterRenderEventManager, null);
  14260. const environment = {
  14261. rendererFactory,
  14262. sanitizer,
  14263. effectManager,
  14264. afterRenderEventManager,
  14265. };
  14266. const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
  14267. // Determine a tag name used for creating host elements when this component is created
  14268. // dynamically. Default to 'div' if this component did not specify any tag name in its selector.
  14269. const elementName = this.componentDef.selectors[0][0] || 'div';
  14270. const hostRNode = rootSelectorOrNode ?
  14271. locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation, rootViewInjector) :
  14272. createElementNode(hostRenderer, elementName, getNamespace(elementName));
  14273. // Signal components use the granular "RefreshView" for change detection
  14274. const signalFlags = (4096 /* LViewFlags.SignalView */ | 512 /* LViewFlags.IsRoot */);
  14275. // Non-signal components use the traditional "CheckAlways or OnPush/Dirty" change detection
  14276. const nonSignalFlags = this.componentDef.onPush ? 64 /* LViewFlags.Dirty */ | 512 /* LViewFlags.IsRoot */ :
  14277. 16 /* LViewFlags.CheckAlways */ | 512 /* LViewFlags.IsRoot */;
  14278. const rootFlags = this.componentDef.signals ? signalFlags : nonSignalFlags;
  14279. let hydrationInfo = null;
  14280. if (hostRNode !== null) {
  14281. hydrationInfo = retrieveHydrationInfo(hostRNode, rootViewInjector, true /* isRootView */);
  14282. }
  14283. // Create the root view. Uses empty TView and ContentTemplate.
  14284. const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null, null);
  14285. const rootLView = createLView(null, rootTView, null, rootFlags, null, null, environment, hostRenderer, rootViewInjector, null, hydrationInfo);
  14286. // rootView is the parent when bootstrapping
  14287. // TODO(misko): it looks like we are entering view here but we don't really need to as
  14288. // `renderView` does that. However as the code is written it is needed because
  14289. // `createRootComponentView` and `createRootComponent` both read global state. Fixing those
  14290. // issues would allow us to drop this.
  14291. enterView(rootLView);
  14292. let component;
  14293. let tElementNode;
  14294. try {
  14295. const rootComponentDef = this.componentDef;
  14296. let rootDirectives;
  14297. let hostDirectiveDefs = null;
  14298. if (rootComponentDef.findHostDirectiveDefs) {
  14299. rootDirectives = [];
  14300. hostDirectiveDefs = new Map();
  14301. rootComponentDef.findHostDirectiveDefs(rootComponentDef, rootDirectives, hostDirectiveDefs);
  14302. rootDirectives.push(rootComponentDef);
  14303. }
  14304. else {
  14305. rootDirectives = [rootComponentDef];
  14306. }
  14307. const hostTNode = createRootComponentTNode(rootLView, hostRNode);
  14308. const componentView = createRootComponentView(hostTNode, hostRNode, rootComponentDef, rootDirectives, rootLView, environment, hostRenderer);
  14309. tElementNode = getTNode(rootTView, HEADER_OFFSET);
  14310. // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some tests
  14311. // where the renderer is mocked out and `undefined` is returned. We should update the tests so
  14312. // that this check can be removed.
  14313. if (hostRNode) {
  14314. setRootNodeAttributes(hostRenderer, rootComponentDef, hostRNode, rootSelectorOrNode);
  14315. }
  14316. if (projectableNodes !== undefined) {
  14317. projectNodes(tElementNode, this.ngContentSelectors, projectableNodes);
  14318. }
  14319. // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
  14320. // executed here?
  14321. // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
  14322. component = createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, [LifecycleHooksFeature]);
  14323. renderView(rootTView, rootLView, null);
  14324. }
  14325. finally {
  14326. leaveView();
  14327. }
  14328. return new ComponentRef(this.componentType, component, createElementRef(tElementNode, rootLView), rootLView, tElementNode);
  14329. }
  14330. }
  14331. /**
  14332. * Represents an instance of a Component created via a {@link ComponentFactory}.
  14333. *
  14334. * `ComponentRef` provides access to the Component Instance as well other objects related to this
  14335. * Component Instance and allows you to destroy the Component Instance via the {@link #destroy}
  14336. * method.
  14337. *
  14338. */
  14339. class ComponentRef extends ComponentRef$1 {
  14340. constructor(componentType, instance, location, _rootLView, _tNode) {
  14341. super();
  14342. this.location = location;
  14343. this._rootLView = _rootLView;
  14344. this._tNode = _tNode;
  14345. this.previousInputValues = null;
  14346. this.instance = instance;
  14347. this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView);
  14348. this.componentType = componentType;
  14349. }
  14350. setInput(name, value) {
  14351. const inputData = this._tNode.inputs;
  14352. let dataValue;
  14353. if (inputData !== null && (dataValue = inputData[name])) {
  14354. this.previousInputValues ??= new Map();
  14355. // Do not set the input if it is the same as the last value
  14356. // This behavior matches `bindingUpdated` when binding inputs in templates.
  14357. if (this.previousInputValues.has(name) &&
  14358. Object.is(this.previousInputValues.get(name), value)) {
  14359. return;
  14360. }
  14361. const lView = this._rootLView;
  14362. setInputsForProperty(lView[TVIEW], lView, dataValue, name, value);
  14363. this.previousInputValues.set(name, value);
  14364. const childComponentLView = getComponentLViewByIndex(this._tNode.index, lView);
  14365. markViewDirty(childComponentLView);
  14366. }
  14367. else {
  14368. if (ngDevMode) {
  14369. const cmpNameForError = stringifyForError(this.componentType);
  14370. let message = `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `;
  14371. message += `Make sure that the '${name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`;
  14372. reportUnknownPropertyError(message);
  14373. }
  14374. }
  14375. }
  14376. get injector() {
  14377. return new NodeInjector(this._tNode, this._rootLView);
  14378. }
  14379. destroy() {
  14380. this.hostView.destroy();
  14381. }
  14382. onDestroy(callback) {
  14383. this.hostView.onDestroy(callback);
  14384. }
  14385. }
  14386. /** Creates a TNode that can be used to instantiate a root component. */
  14387. function createRootComponentTNode(lView, rNode) {
  14388. const tView = lView[TVIEW];
  14389. const index = HEADER_OFFSET;
  14390. ngDevMode && assertIndexInRange(lView, index);
  14391. lView[index] = rNode;
  14392. // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
  14393. // the same time we want to communicate the debug `TNode` that this is a special `TNode`
  14394. // representing a host element.
  14395. return getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
  14396. }
  14397. /**
  14398. * Creates the root component view and the root component node.
  14399. *
  14400. * @param hostRNode Render host element.
  14401. * @param rootComponentDef ComponentDef
  14402. * @param rootView The parent view where the host node is stored
  14403. * @param rendererFactory Factory to be used for creating child renderers.
  14404. * @param hostRenderer The current renderer
  14405. * @param sanitizer The sanitizer, if provided
  14406. *
  14407. * @returns Component view created
  14408. */
  14409. function createRootComponentView(tNode, hostRNode, rootComponentDef, rootDirectives, rootView, environment, hostRenderer) {
  14410. const tView = rootView[TVIEW];
  14411. applyRootComponentStyling(rootDirectives, tNode, hostRNode, hostRenderer);
  14412. // Hydration info is on the host element and needs to be retrieved
  14413. // and passed to the component LView.
  14414. let hydrationInfo = null;
  14415. if (hostRNode !== null) {
  14416. hydrationInfo = retrieveHydrationInfo(hostRNode, rootView[INJECTOR$1]);
  14417. }
  14418. const viewRenderer = environment.rendererFactory.createRenderer(hostRNode, rootComponentDef);
  14419. let lViewFlags = 16 /* LViewFlags.CheckAlways */;
  14420. if (rootComponentDef.signals) {
  14421. lViewFlags = 4096 /* LViewFlags.SignalView */;
  14422. }
  14423. else if (rootComponentDef.onPush) {
  14424. lViewFlags = 64 /* LViewFlags.Dirty */;
  14425. }
  14426. const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, lViewFlags, rootView[tNode.index], tNode, environment, viewRenderer, null, null, hydrationInfo);
  14427. if (tView.firstCreatePass) {
  14428. markAsComponentHost(tView, tNode, rootDirectives.length - 1);
  14429. }
  14430. addToViewTree(rootView, componentView);
  14431. // Store component view at node index, with node as the HOST
  14432. return rootView[tNode.index] = componentView;
  14433. }
  14434. /** Sets up the styling information on a root component. */
  14435. function applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer) {
  14436. for (const def of rootDirectives) {
  14437. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
  14438. }
  14439. if (tNode.mergedAttrs !== null) {
  14440. computeStaticStyling(tNode, tNode.mergedAttrs, true);
  14441. if (rNode !== null) {
  14442. setupStaticAttributes(hostRenderer, rNode, tNode);
  14443. }
  14444. }
  14445. }
  14446. /**
  14447. * Creates a root component and sets it up with features and host bindings.Shared by
  14448. * renderComponent() and ViewContainerRef.createComponent().
  14449. */
  14450. function createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, hostFeatures) {
  14451. const rootTNode = getCurrentTNode();
  14452. ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
  14453. const tView = rootLView[TVIEW];
  14454. const native = getNativeByTNode(rootTNode, rootLView);
  14455. initializeDirectives(tView, rootLView, rootTNode, rootDirectives, null, hostDirectiveDefs);
  14456. for (let i = 0; i < rootDirectives.length; i++) {
  14457. const directiveIndex = rootTNode.directiveStart + i;
  14458. const directiveInstance = getNodeInjectable(rootLView, tView, directiveIndex, rootTNode);
  14459. attachPatchData(directiveInstance, rootLView);
  14460. }
  14461. invokeDirectivesHostBindings(tView, rootLView, rootTNode);
  14462. if (native) {
  14463. attachPatchData(native, rootLView);
  14464. }
  14465. // We're guaranteed for the `componentOffset` to be positive here
  14466. // since a root component always matches a component def.
  14467. ngDevMode &&
  14468. assertGreaterThan(rootTNode.componentOffset, -1, 'componentOffset must be great than -1');
  14469. const component = getNodeInjectable(rootLView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
  14470. componentView[CONTEXT] = rootLView[CONTEXT] = component;
  14471. if (hostFeatures !== null) {
  14472. for (const feature of hostFeatures) {
  14473. feature(component, rootComponentDef);
  14474. }
  14475. }
  14476. // We want to generate an empty QueryList for root content queries for backwards
  14477. // compatibility with ViewEngine.
  14478. executeContentQueries(tView, rootTNode, componentView);
  14479. return component;
  14480. }
  14481. /** Sets the static attributes on a root component. */
  14482. function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
  14483. if (rootSelectorOrNode) {
  14484. setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
  14485. }
  14486. else {
  14487. // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
  14488. // is not defined), also apply attributes and classes extracted from component selector.
  14489. // Extract attributes and classes from the first selector only to match VE behavior.
  14490. const { attrs, classes } = extractAttrsAndClassesFromSelector(componentDef.selectors[0]);
  14491. if (attrs) {
  14492. setUpAttributes(hostRenderer, hostRNode, attrs);
  14493. }
  14494. if (classes && classes.length > 0) {
  14495. writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
  14496. }
  14497. }
  14498. }
  14499. /** Projects the `projectableNodes` that were specified when creating a root component. */
  14500. function projectNodes(tNode, ngContentSelectors, projectableNodes) {
  14501. const projection = tNode.projection = [];
  14502. for (let i = 0; i < ngContentSelectors.length; i++) {
  14503. const nodesforSlot = projectableNodes[i];
  14504. // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
  14505. // case). Here we do normalize passed data structure to be an array of arrays to avoid
  14506. // complex checks down the line.
  14507. // We also normalize the length of the passed in projectable nodes (to match the number of
  14508. // <ng-container> slots defined by a component).
  14509. projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
  14510. }
  14511. }
  14512. /**
  14513. * Used to enable lifecycle hooks on the root component.
  14514. *
  14515. * Include this feature when calling `renderComponent` if the root component
  14516. * you are rendering has lifecycle hooks defined. Otherwise, the hooks won't
  14517. * be called properly.
  14518. *
  14519. * Example:
  14520. *
  14521. * ```
  14522. * renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]});
  14523. * ```
  14524. */
  14525. function LifecycleHooksFeature() {
  14526. const tNode = getCurrentTNode();
  14527. ngDevMode && assertDefined(tNode, 'TNode is required');
  14528. registerPostOrderHooks(getLView()[TVIEW], tNode);
  14529. }
  14530. function getSuperType(type) {
  14531. return Object.getPrototypeOf(type.prototype).constructor;
  14532. }
  14533. /**
  14534. * Merges the definition from a super class to a sub class.
  14535. * @param definition The definition that is a SubClass of another directive of component
  14536. *
  14537. * @codeGenApi
  14538. */
  14539. function ɵɵInheritDefinitionFeature(definition) {
  14540. let superType = getSuperType(definition.type);
  14541. let shouldInheritFields = true;
  14542. const inheritanceChain = [definition];
  14543. while (superType) {
  14544. let superDef = undefined;
  14545. if (isComponentDef(definition)) {
  14546. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14547. superDef = superType.ɵcmp || superType.ɵdir;
  14548. }
  14549. else {
  14550. if (superType.ɵcmp) {
  14551. throw new RuntimeError(903 /* RuntimeErrorCode.INVALID_INHERITANCE */, ngDevMode &&
  14552. `Directives cannot inherit Components. Directive ${stringifyForError(definition.type)} is attempting to extend component ${stringifyForError(superType)}`);
  14553. }
  14554. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14555. superDef = superType.ɵdir;
  14556. }
  14557. if (superDef) {
  14558. if (shouldInheritFields) {
  14559. inheritanceChain.push(superDef);
  14560. // Some fields in the definition may be empty, if there were no values to put in them that
  14561. // would've justified object creation. Unwrap them if necessary.
  14562. const writeableDef = definition;
  14563. writeableDef.inputs = maybeUnwrapEmpty(definition.inputs);
  14564. writeableDef.inputTransforms = maybeUnwrapEmpty(definition.inputTransforms);
  14565. writeableDef.declaredInputs = maybeUnwrapEmpty(definition.declaredInputs);
  14566. writeableDef.outputs = maybeUnwrapEmpty(definition.outputs);
  14567. // Merge hostBindings
  14568. const superHostBindings = superDef.hostBindings;
  14569. superHostBindings && inheritHostBindings(definition, superHostBindings);
  14570. // Merge queries
  14571. const superViewQuery = superDef.viewQuery;
  14572. const superContentQueries = superDef.contentQueries;
  14573. superViewQuery && inheritViewQuery(definition, superViewQuery);
  14574. superContentQueries && inheritContentQueries(definition, superContentQueries);
  14575. // Merge inputs and outputs
  14576. fillProperties(definition.inputs, superDef.inputs);
  14577. fillProperties(definition.declaredInputs, superDef.declaredInputs);
  14578. fillProperties(definition.outputs, superDef.outputs);
  14579. if (superDef.inputTransforms !== null) {
  14580. if (writeableDef.inputTransforms === null) {
  14581. writeableDef.inputTransforms = {};
  14582. }
  14583. fillProperties(writeableDef.inputTransforms, superDef.inputTransforms);
  14584. }
  14585. // Merge animations metadata.
  14586. // If `superDef` is a Component, the `data` field is present (defaults to an empty object).
  14587. if (isComponentDef(superDef) && superDef.data.animation) {
  14588. // If super def is a Component, the `definition` is also a Component, since Directives can
  14589. // not inherit Components (we throw an error above and cannot reach this code).
  14590. const defData = definition.data;
  14591. defData.animation = (defData.animation || []).concat(superDef.data.animation);
  14592. }
  14593. }
  14594. // Run parent features
  14595. const features = superDef.features;
  14596. if (features) {
  14597. for (let i = 0; i < features.length; i++) {
  14598. const feature = features[i];
  14599. if (feature && feature.ngInherit) {
  14600. feature(definition);
  14601. }
  14602. // If `InheritDefinitionFeature` is a part of the current `superDef`, it means that this
  14603. // def already has all the necessary information inherited from its super class(es), so we
  14604. // can stop merging fields from super classes. However we need to iterate through the
  14605. // prototype chain to look for classes that might contain other "features" (like
  14606. // NgOnChanges), which we should invoke for the original `definition`. We set the
  14607. // `shouldInheritFields` flag to indicate that, essentially skipping fields inheritance
  14608. // logic and only invoking functions from the "features" list.
  14609. if (feature === ɵɵInheritDefinitionFeature) {
  14610. shouldInheritFields = false;
  14611. }
  14612. }
  14613. }
  14614. }
  14615. superType = Object.getPrototypeOf(superType);
  14616. }
  14617. mergeHostAttrsAcrossInheritance(inheritanceChain);
  14618. }
  14619. /**
  14620. * Merge the `hostAttrs` and `hostVars` from the inherited parent to the base class.
  14621. *
  14622. * @param inheritanceChain A list of `WritableDefs` starting at the top most type and listing
  14623. * sub-types in order. For each type take the `hostAttrs` and `hostVars` and merge it with the child
  14624. * type.
  14625. */
  14626. function mergeHostAttrsAcrossInheritance(inheritanceChain) {
  14627. let hostVars = 0;
  14628. let hostAttrs = null;
  14629. // We process the inheritance order from the base to the leaves here.
  14630. for (let i = inheritanceChain.length - 1; i >= 0; i--) {
  14631. const def = inheritanceChain[i];
  14632. // For each `hostVars`, we need to add the superclass amount.
  14633. def.hostVars = (hostVars += def.hostVars);
  14634. // for each `hostAttrs` we need to merge it with superclass.
  14635. def.hostAttrs =
  14636. mergeHostAttrs(def.hostAttrs, hostAttrs = mergeHostAttrs(hostAttrs, def.hostAttrs));
  14637. }
  14638. }
  14639. function maybeUnwrapEmpty(value) {
  14640. if (value === EMPTY_OBJ) {
  14641. return {};
  14642. }
  14643. else if (value === EMPTY_ARRAY) {
  14644. return [];
  14645. }
  14646. else {
  14647. return value;
  14648. }
  14649. }
  14650. function inheritViewQuery(definition, superViewQuery) {
  14651. const prevViewQuery = definition.viewQuery;
  14652. if (prevViewQuery) {
  14653. definition.viewQuery = (rf, ctx) => {
  14654. superViewQuery(rf, ctx);
  14655. prevViewQuery(rf, ctx);
  14656. };
  14657. }
  14658. else {
  14659. definition.viewQuery = superViewQuery;
  14660. }
  14661. }
  14662. function inheritContentQueries(definition, superContentQueries) {
  14663. const prevContentQueries = definition.contentQueries;
  14664. if (prevContentQueries) {
  14665. definition.contentQueries = (rf, ctx, directiveIndex) => {
  14666. superContentQueries(rf, ctx, directiveIndex);
  14667. prevContentQueries(rf, ctx, directiveIndex);
  14668. };
  14669. }
  14670. else {
  14671. definition.contentQueries = superContentQueries;
  14672. }
  14673. }
  14674. function inheritHostBindings(definition, superHostBindings) {
  14675. const prevHostBindings = definition.hostBindings;
  14676. if (prevHostBindings) {
  14677. definition.hostBindings = (rf, ctx) => {
  14678. superHostBindings(rf, ctx);
  14679. prevHostBindings(rf, ctx);
  14680. };
  14681. }
  14682. else {
  14683. definition.hostBindings = superHostBindings;
  14684. }
  14685. }
  14686. /**
  14687. * Fields which exist on either directive or component definitions, and need to be copied from
  14688. * parent to child classes by the `ɵɵCopyDefinitionFeature`.
  14689. */
  14690. const COPY_DIRECTIVE_FIELDS = [
  14691. // The child class should use the providers of its parent.
  14692. 'providersResolver',
  14693. // Not listed here are any fields which are handled by the `ɵɵInheritDefinitionFeature`, such
  14694. // as inputs, outputs, and host binding functions.
  14695. ];
  14696. /**
  14697. * Fields which exist only on component definitions, and need to be copied from parent to child
  14698. * classes by the `ɵɵCopyDefinitionFeature`.
  14699. *
  14700. * The type here allows any field of `ComponentDef` which is not also a property of `DirectiveDef`,
  14701. * since those should go in `COPY_DIRECTIVE_FIELDS` above.
  14702. */
  14703. const COPY_COMPONENT_FIELDS = [
  14704. // The child class should use the template function of its parent, including all template
  14705. // semantics.
  14706. 'template',
  14707. 'decls',
  14708. 'consts',
  14709. 'vars',
  14710. 'onPush',
  14711. 'ngContentSelectors',
  14712. // The child class should use the CSS styles of its parent, including all styling semantics.
  14713. 'styles',
  14714. 'encapsulation',
  14715. // The child class should be checked by the runtime in the same way as its parent.
  14716. 'schemas',
  14717. ];
  14718. /**
  14719. * Copies the fields not handled by the `ɵɵInheritDefinitionFeature` from the supertype of a
  14720. * definition.
  14721. *
  14722. * This exists primarily to support ngcc migration of an existing View Engine pattern, where an
  14723. * entire decorator is inherited from a parent to a child class. When ngcc detects this case, it
  14724. * generates a skeleton definition on the child class, and applies this feature.
  14725. *
  14726. * The `ɵɵCopyDefinitionFeature` then copies any needed fields from the parent class' definition,
  14727. * including things like the component template function.
  14728. *
  14729. * @param definition The definition of a child class which inherits from a parent class with its
  14730. * own definition.
  14731. *
  14732. * @codeGenApi
  14733. */
  14734. function ɵɵCopyDefinitionFeature(definition) {
  14735. let superType = getSuperType(definition.type);
  14736. let superDef = undefined;
  14737. if (isComponentDef(definition)) {
  14738. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14739. superDef = superType.ɵcmp;
  14740. }
  14741. else {
  14742. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14743. superDef = superType.ɵdir;
  14744. }
  14745. // Needed because `definition` fields are readonly.
  14746. const defAny = definition;
  14747. // Copy over any fields that apply to either directives or components.
  14748. for (const field of COPY_DIRECTIVE_FIELDS) {
  14749. defAny[field] = superDef[field];
  14750. }
  14751. if (isComponentDef(superDef)) {
  14752. // Copy over any component-specific fields.
  14753. for (const field of COPY_COMPONENT_FIELDS) {
  14754. defAny[field] = superDef[field];
  14755. }
  14756. }
  14757. }
  14758. /**
  14759. * This feature adds the host directives behavior to a directive definition by patching a
  14760. * function onto it. The expectation is that the runtime will invoke the function during
  14761. * directive matching.
  14762. *
  14763. * For example:
  14764. * ```ts
  14765. * class ComponentWithHostDirective {
  14766. * static ɵcmp = defineComponent({
  14767. * type: ComponentWithHostDirective,
  14768. * features: [ɵɵHostDirectivesFeature([
  14769. * SimpleHostDirective,
  14770. * {directive: AdvancedHostDirective, inputs: ['foo: alias'], outputs: ['bar']},
  14771. * ])]
  14772. * });
  14773. * }
  14774. * ```
  14775. *
  14776. * @codeGenApi
  14777. */
  14778. function ɵɵHostDirectivesFeature(rawHostDirectives) {
  14779. return (definition) => {
  14780. definition.findHostDirectiveDefs = findHostDirectiveDefs;
  14781. definition.hostDirectives =
  14782. (Array.isArray(rawHostDirectives) ? rawHostDirectives : rawHostDirectives()).map(dir => {
  14783. return typeof dir === 'function' ?
  14784. { directive: resolveForwardRef(dir), inputs: EMPTY_OBJ, outputs: EMPTY_OBJ } :
  14785. {
  14786. directive: resolveForwardRef(dir.directive),
  14787. inputs: bindingArrayToMap(dir.inputs),
  14788. outputs: bindingArrayToMap(dir.outputs)
  14789. };
  14790. });
  14791. };
  14792. }
  14793. function findHostDirectiveDefs(currentDef, matchedDefs, hostDirectiveDefs) {
  14794. if (currentDef.hostDirectives !== null) {
  14795. for (const hostDirectiveConfig of currentDef.hostDirectives) {
  14796. const hostDirectiveDef = getDirectiveDef(hostDirectiveConfig.directive);
  14797. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  14798. validateHostDirective(hostDirectiveConfig, hostDirectiveDef, matchedDefs);
  14799. }
  14800. // We need to patch the `declaredInputs` so that
  14801. // `ngOnChanges` can map the properties correctly.
  14802. patchDeclaredInputs(hostDirectiveDef.declaredInputs, hostDirectiveConfig.inputs);
  14803. // Host directives execute before the host so that its host bindings can be overwritten.
  14804. findHostDirectiveDefs(hostDirectiveDef, matchedDefs, hostDirectiveDefs);
  14805. hostDirectiveDefs.set(hostDirectiveDef, hostDirectiveConfig);
  14806. matchedDefs.push(hostDirectiveDef);
  14807. }
  14808. }
  14809. }
  14810. /**
  14811. * Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into
  14812. * a map in the form of `{publicName: 'alias', otherPublicName: 'otherAlias'}`.
  14813. */
  14814. function bindingArrayToMap(bindings) {
  14815. if (bindings === undefined || bindings.length === 0) {
  14816. return EMPTY_OBJ;
  14817. }
  14818. const result = {};
  14819. for (let i = 0; i < bindings.length; i += 2) {
  14820. result[bindings[i]] = bindings[i + 1];
  14821. }
  14822. return result;
  14823. }
  14824. /**
  14825. * `ngOnChanges` has some leftover legacy ViewEngine behavior where the keys inside the
  14826. * `SimpleChanges` event refer to the *declared* name of the input, not its public name or its
  14827. * minified name. E.g. in `@Input('alias') foo: string`, the name in the `SimpleChanges` object
  14828. * will always be `foo`, and not `alias` or the minified name of `foo` in apps using property
  14829. * minification.
  14830. *
  14831. * This is achieved through the `DirectiveDef.declaredInputs` map that is constructed when the
  14832. * definition is declared. When a property is written to the directive instance, the
  14833. * `NgOnChangesFeature` will try to remap the property name being written to using the
  14834. * `declaredInputs`.
  14835. *
  14836. * Since the host directive input remapping happens during directive matching, `declaredInputs`
  14837. * won't contain the new alias that the input is available under. This function addresses the
  14838. * issue by patching the host directive aliases to the `declaredInputs`. There is *not* a risk of
  14839. * this patching accidentally introducing new inputs to the host directive, because `declaredInputs`
  14840. * is used *only* by the `NgOnChangesFeature` when determining what name is used in the
  14841. * `SimpleChanges` object which won't be reached if an input doesn't exist.
  14842. */
  14843. function patchDeclaredInputs(declaredInputs, exposedInputs) {
  14844. for (const publicName in exposedInputs) {
  14845. if (exposedInputs.hasOwnProperty(publicName)) {
  14846. const remappedPublicName = exposedInputs[publicName];
  14847. const privateName = declaredInputs[publicName];
  14848. // We *technically* shouldn't be able to hit this case because we can't have multiple
  14849. // inputs on the same property and we have validations against conflicting aliases in
  14850. // `validateMappings`. If we somehow did, it would lead to `ngOnChanges` being invoked
  14851. // with the wrong name so we have a non-user-friendly assertion here just in case.
  14852. if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
  14853. declaredInputs.hasOwnProperty(remappedPublicName)) {
  14854. assertEqual(declaredInputs[remappedPublicName], declaredInputs[publicName], `Conflicting host directive input alias ${publicName}.`);
  14855. }
  14856. declaredInputs[remappedPublicName] = privateName;
  14857. }
  14858. }
  14859. }
  14860. /**
  14861. * Verifies that the host directive has been configured correctly.
  14862. * @param hostDirectiveConfig Host directive configuration object.
  14863. * @param directiveDef Directive definition of the host directive.
  14864. * @param matchedDefs Directives that have been matched so far.
  14865. */
  14866. function validateHostDirective(hostDirectiveConfig, directiveDef, matchedDefs) {
  14867. const type = hostDirectiveConfig.directive;
  14868. if (directiveDef === null) {
  14869. if (getComponentDef$1(type) !== null) {
  14870. throw new RuntimeError(310 /* RuntimeErrorCode.HOST_DIRECTIVE_COMPONENT */, `Host directive ${type.name} cannot be a component.`);
  14871. }
  14872. throw new RuntimeError(307 /* RuntimeErrorCode.HOST_DIRECTIVE_UNRESOLVABLE */, `Could not resolve metadata for host directive ${type.name}. ` +
  14873. `Make sure that the ${type.name} class is annotated with an @Directive decorator.`);
  14874. }
  14875. if (!directiveDef.standalone) {
  14876. throw new RuntimeError(308 /* RuntimeErrorCode.HOST_DIRECTIVE_NOT_STANDALONE */, `Host directive ${directiveDef.type.name} must be standalone.`);
  14877. }
  14878. if (matchedDefs.indexOf(directiveDef) > -1) {
  14879. throw new RuntimeError(309 /* RuntimeErrorCode.DUPLICATE_DIRECTITVE */, `Directive ${directiveDef.type.name} matches multiple times on the same element. ` +
  14880. `Directives can only match an element once.`);
  14881. }
  14882. validateMappings('input', directiveDef, hostDirectiveConfig.inputs);
  14883. validateMappings('output', directiveDef, hostDirectiveConfig.outputs);
  14884. }
  14885. /**
  14886. * Checks that the host directive inputs/outputs configuration is valid.
  14887. * @param bindingType Kind of binding that is being validated. Used in the error message.
  14888. * @param def Definition of the host directive that is being validated against.
  14889. * @param hostDirectiveBindings Host directive mapping object that shold be validated.
  14890. */
  14891. function validateMappings(bindingType, def, hostDirectiveBindings) {
  14892. const className = def.type.name;
  14893. const bindings = bindingType === 'input' ? def.inputs : def.outputs;
  14894. for (const publicName in hostDirectiveBindings) {
  14895. if (hostDirectiveBindings.hasOwnProperty(publicName)) {
  14896. if (!bindings.hasOwnProperty(publicName)) {
  14897. throw new RuntimeError(311 /* RuntimeErrorCode.HOST_DIRECTIVE_UNDEFINED_BINDING */, `Directive ${className} does not have an ${bindingType} with a public name of ${publicName}.`);
  14898. }
  14899. const remappedPublicName = hostDirectiveBindings[publicName];
  14900. if (bindings.hasOwnProperty(remappedPublicName) && remappedPublicName !== publicName &&
  14901. bindings[remappedPublicName] !== publicName) {
  14902. throw new RuntimeError(312 /* RuntimeErrorCode.HOST_DIRECTIVE_CONFLICTING_ALIAS */, `Cannot alias ${bindingType} ${publicName} of host directive ${className} to ${remappedPublicName}, because it already has a different ${bindingType} with the same public name.`);
  14903. }
  14904. }
  14905. }
  14906. }
  14907. /**
  14908. * Decorates the directive definition with support for input transform functions.
  14909. *
  14910. * If the directive uses inheritance, the feature should be included before the
  14911. * `InheritDefinitionFeature` to ensure that the `inputTransforms` field is populated.
  14912. *
  14913. * @codeGenApi
  14914. */
  14915. function ɵɵInputTransformsFeature(definition) {
  14916. const inputs = definition.inputConfig;
  14917. const inputTransforms = {};
  14918. for (const minifiedKey in inputs) {
  14919. if (inputs.hasOwnProperty(minifiedKey)) {
  14920. // Note: the private names are used for the keys, rather than the public ones, because public
  14921. // names can be re-aliased in host directives which would invalidate the lookup.
  14922. const value = inputs[minifiedKey];
  14923. if (Array.isArray(value) && value[2]) {
  14924. inputTransforms[minifiedKey] = value[2];
  14925. }
  14926. }
  14927. }
  14928. definition.inputTransforms =
  14929. inputTransforms;
  14930. }
  14931. function isIterable(obj) {
  14932. return obj !== null && typeof obj === 'object' && obj[Symbol.iterator] !== undefined;
  14933. }
  14934. function isListLikeIterable(obj) {
  14935. if (!isJsObject(obj))
  14936. return false;
  14937. return Array.isArray(obj) ||
  14938. (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
  14939. Symbol.iterator in obj); // JS Iterable have a Symbol.iterator prop
  14940. }
  14941. function areIterablesEqual(a, b, comparator) {
  14942. const iterator1 = a[Symbol.iterator]();
  14943. const iterator2 = b[Symbol.iterator]();
  14944. while (true) {
  14945. const item1 = iterator1.next();
  14946. const item2 = iterator2.next();
  14947. if (item1.done && item2.done)
  14948. return true;
  14949. if (item1.done || item2.done)
  14950. return false;
  14951. if (!comparator(item1.value, item2.value))
  14952. return false;
  14953. }
  14954. }
  14955. function iterateListLike(obj, fn) {
  14956. if (Array.isArray(obj)) {
  14957. for (let i = 0; i < obj.length; i++) {
  14958. fn(obj[i]);
  14959. }
  14960. }
  14961. else {
  14962. const iterator = obj[Symbol.iterator]();
  14963. let item;
  14964. while (!((item = iterator.next()).done)) {
  14965. fn(item.value);
  14966. }
  14967. }
  14968. }
  14969. function isJsObject(o) {
  14970. return o !== null && (typeof o === 'function' || typeof o === 'object');
  14971. }
  14972. function devModeEqual(a, b) {
  14973. const isListLikeIterableA = isListLikeIterable(a);
  14974. const isListLikeIterableB = isListLikeIterable(b);
  14975. if (isListLikeIterableA && isListLikeIterableB) {
  14976. return areIterablesEqual(a, b, devModeEqual);
  14977. }
  14978. else {
  14979. const isAObject = a && (typeof a === 'object' || typeof a === 'function');
  14980. const isBObject = b && (typeof b === 'object' || typeof b === 'function');
  14981. if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) {
  14982. return true;
  14983. }
  14984. else {
  14985. return Object.is(a, b);
  14986. }
  14987. }
  14988. }
  14989. // TODO(misko): consider inlining
  14990. /** Updates binding and returns the value. */
  14991. function updateBinding(lView, bindingIndex, value) {
  14992. return lView[bindingIndex] = value;
  14993. }
  14994. /** Gets the current binding value. */
  14995. function getBinding(lView, bindingIndex) {
  14996. ngDevMode && assertIndexInRange(lView, bindingIndex);
  14997. ngDevMode &&
  14998. assertNotSame(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.');
  14999. return lView[bindingIndex];
  15000. }
  15001. /**
  15002. * Updates binding if changed, then returns whether it was updated.
  15003. *
  15004. * This function also checks the `CheckNoChangesMode` and throws if changes are made.
  15005. * Some changes (Objects/iterables) during `CheckNoChangesMode` are exempt to comply with VE
  15006. * behavior.
  15007. *
  15008. * @param lView current `LView`
  15009. * @param bindingIndex The binding in the `LView` to check
  15010. * @param value New value to check against `lView[bindingIndex]`
  15011. * @returns `true` if the bindings has changed. (Throws if binding has changed during
  15012. * `CheckNoChangesMode`)
  15013. */
  15014. function bindingUpdated(lView, bindingIndex, value) {
  15015. ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
  15016. ngDevMode &&
  15017. assertLessThan(bindingIndex, lView.length, `Slot should have been initialized to NO_CHANGE`);
  15018. const oldValue = lView[bindingIndex];
  15019. if (Object.is(oldValue, value)) {
  15020. return false;
  15021. }
  15022. else {
  15023. if (ngDevMode && isInCheckNoChangesMode()) {
  15024. // View engine didn't report undefined values as changed on the first checkNoChanges pass
  15025. // (before the change detection was run).
  15026. const oldValueToCompare = oldValue !== NO_CHANGE ? oldValue : undefined;
  15027. if (!devModeEqual(oldValueToCompare, value)) {
  15028. const details = getExpressionChangedErrorDetails(lView, bindingIndex, oldValueToCompare, value);
  15029. throwErrorIfNoChangesMode(oldValue === NO_CHANGE, details.oldValue, details.newValue, details.propName, lView);
  15030. }
  15031. // There was a change, but the `devModeEqual` decided that the change is exempt from an error.
  15032. // For this reason we exit as if no change. The early exit is needed to prevent the changed
  15033. // value to be written into `LView` (If we would write the new value that we would not see it
  15034. // as change on next CD.)
  15035. return false;
  15036. }
  15037. lView[bindingIndex] = value;
  15038. return true;
  15039. }
  15040. }
  15041. /** Updates 2 bindings if changed, then returns whether either was updated. */
  15042. function bindingUpdated2(lView, bindingIndex, exp1, exp2) {
  15043. const different = bindingUpdated(lView, bindingIndex, exp1);
  15044. return bindingUpdated(lView, bindingIndex + 1, exp2) || different;
  15045. }
  15046. /** Updates 3 bindings if changed, then returns whether any was updated. */
  15047. function bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) {
  15048. const different = bindingUpdated2(lView, bindingIndex, exp1, exp2);
  15049. return bindingUpdated(lView, bindingIndex + 2, exp3) || different;
  15050. }
  15051. /** Updates 4 bindings if changed, then returns whether any was updated. */
  15052. function bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) {
  15053. const different = bindingUpdated2(lView, bindingIndex, exp1, exp2);
  15054. return bindingUpdated2(lView, bindingIndex + 2, exp3, exp4) || different;
  15055. }
  15056. /**
  15057. * Updates the value of or removes a bound attribute on an Element.
  15058. *
  15059. * Used in the case of `[attr.title]="value"`
  15060. *
  15061. * @param name name The name of the attribute.
  15062. * @param value value The attribute is removed when value is `null` or `undefined`.
  15063. * Otherwise the attribute value is set to the stringified value.
  15064. * @param sanitizer An optional function used to sanitize the value.
  15065. * @param namespace Optional namespace to use when setting the attribute.
  15066. *
  15067. * @codeGenApi
  15068. */
  15069. function ɵɵattribute(name, value, sanitizer, namespace) {
  15070. const lView = getLView();
  15071. const bindingIndex = nextBindingIndex();
  15072. if (bindingUpdated(lView, bindingIndex, value)) {
  15073. const tView = getTView();
  15074. const tNode = getSelectedTNode();
  15075. elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace);
  15076. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, 'attr.' + name, bindingIndex);
  15077. }
  15078. return ɵɵattribute;
  15079. }
  15080. /**
  15081. * Create interpolation bindings with a variable number of expressions.
  15082. *
  15083. * If there are 1 to 8 expressions `interpolation1()` to `interpolation8()` should be used instead.
  15084. * Those are faster because there is no need to create an array of expressions and iterate over it.
  15085. *
  15086. * `values`:
  15087. * - has static text at even indexes,
  15088. * - has evaluated expressions at odd indexes.
  15089. *
  15090. * Returns the concatenated string when any of the arguments changes, `NO_CHANGE` otherwise.
  15091. */
  15092. function interpolationV(lView, values) {
  15093. ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values');
  15094. ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values');
  15095. let isBindingUpdated = false;
  15096. let bindingIndex = getBindingIndex();
  15097. for (let i = 1; i < values.length; i += 2) {
  15098. // Check if bindings (odd indexes) have changed
  15099. isBindingUpdated = bindingUpdated(lView, bindingIndex++, values[i]) || isBindingUpdated;
  15100. }
  15101. setBindingIndex(bindingIndex);
  15102. if (!isBindingUpdated) {
  15103. return NO_CHANGE;
  15104. }
  15105. // Build the updated content
  15106. let content = values[0];
  15107. for (let i = 1; i < values.length; i += 2) {
  15108. content += renderStringify(values[i]) + values[i + 1];
  15109. }
  15110. return content;
  15111. }
  15112. /**
  15113. * Creates an interpolation binding with 1 expression.
  15114. *
  15115. * @param prefix static value used for concatenation only.
  15116. * @param v0 value checked for change.
  15117. * @param suffix static value used for concatenation only.
  15118. */
  15119. function interpolation1(lView, prefix, v0, suffix) {
  15120. const different = bindingUpdated(lView, nextBindingIndex(), v0);
  15121. return different ? prefix + renderStringify(v0) + suffix : NO_CHANGE;
  15122. }
  15123. /**
  15124. * Creates an interpolation binding with 2 expressions.
  15125. */
  15126. function interpolation2(lView, prefix, v0, i0, v1, suffix) {
  15127. const bindingIndex = getBindingIndex();
  15128. const different = bindingUpdated2(lView, bindingIndex, v0, v1);
  15129. incrementBindingIndex(2);
  15130. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + suffix : NO_CHANGE;
  15131. }
  15132. /**
  15133. * Creates an interpolation binding with 3 expressions.
  15134. */
  15135. function interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix) {
  15136. const bindingIndex = getBindingIndex();
  15137. const different = bindingUpdated3(lView, bindingIndex, v0, v1, v2);
  15138. incrementBindingIndex(3);
  15139. return different ?
  15140. prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + suffix :
  15141. NO_CHANGE;
  15142. }
  15143. /**
  15144. * Create an interpolation binding with 4 expressions.
  15145. */
  15146. function interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  15147. const bindingIndex = getBindingIndex();
  15148. const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  15149. incrementBindingIndex(4);
  15150. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  15151. renderStringify(v2) + i2 + renderStringify(v3) + suffix :
  15152. NO_CHANGE;
  15153. }
  15154. /**
  15155. * Creates an interpolation binding with 5 expressions.
  15156. */
  15157. function interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  15158. const bindingIndex = getBindingIndex();
  15159. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  15160. different = bindingUpdated(lView, bindingIndex + 4, v4) || different;
  15161. incrementBindingIndex(5);
  15162. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  15163. renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + suffix :
  15164. NO_CHANGE;
  15165. }
  15166. /**
  15167. * Creates an interpolation binding with 6 expressions.
  15168. */
  15169. function interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  15170. const bindingIndex = getBindingIndex();
  15171. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  15172. different = bindingUpdated2(lView, bindingIndex + 4, v4, v5) || different;
  15173. incrementBindingIndex(6);
  15174. return different ?
  15175. prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
  15176. renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + suffix :
  15177. NO_CHANGE;
  15178. }
  15179. /**
  15180. * Creates an interpolation binding with 7 expressions.
  15181. */
  15182. function interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  15183. const bindingIndex = getBindingIndex();
  15184. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  15185. different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different;
  15186. incrementBindingIndex(7);
  15187. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  15188. renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 +
  15189. renderStringify(v5) + i5 + renderStringify(v6) + suffix :
  15190. NO_CHANGE;
  15191. }
  15192. /**
  15193. * Creates an interpolation binding with 8 expressions.
  15194. */
  15195. function interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  15196. const bindingIndex = getBindingIndex();
  15197. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  15198. different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different;
  15199. incrementBindingIndex(8);
  15200. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  15201. renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 +
  15202. renderStringify(v5) + i5 + renderStringify(v6) + i6 + renderStringify(v7) + suffix :
  15203. NO_CHANGE;
  15204. }
  15205. /**
  15206. *
  15207. * Update an interpolated attribute on an element with single bound value surrounded by text.
  15208. *
  15209. * Used when the value passed to a property has 1 interpolated value in it:
  15210. *
  15211. * ```html
  15212. * <div attr.title="prefix{{v0}}suffix"></div>
  15213. * ```
  15214. *
  15215. * Its compiled representation is::
  15216. *
  15217. * ```ts
  15218. * ɵɵattributeInterpolate1('title', 'prefix', v0, 'suffix');
  15219. * ```
  15220. *
  15221. * @param attrName The name of the attribute to update
  15222. * @param prefix Static value used for concatenation only.
  15223. * @param v0 Value checked for change.
  15224. * @param suffix Static value used for concatenation only.
  15225. * @param sanitizer An optional sanitizer function
  15226. * @returns itself, so that it may be chained.
  15227. * @codeGenApi
  15228. */
  15229. function ɵɵattributeInterpolate1(attrName, prefix, v0, suffix, sanitizer, namespace) {
  15230. const lView = getLView();
  15231. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  15232. if (interpolatedValue !== NO_CHANGE) {
  15233. const tNode = getSelectedTNode();
  15234. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15235. ngDevMode &&
  15236. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 1, prefix, suffix);
  15237. }
  15238. return ɵɵattributeInterpolate1;
  15239. }
  15240. /**
  15241. *
  15242. * Update an interpolated attribute on an element with 2 bound values surrounded by text.
  15243. *
  15244. * Used when the value passed to a property has 2 interpolated values in it:
  15245. *
  15246. * ```html
  15247. * <div attr.title="prefix{{v0}}-{{v1}}suffix"></div>
  15248. * ```
  15249. *
  15250. * Its compiled representation is::
  15251. *
  15252. * ```ts
  15253. * ɵɵattributeInterpolate2('title', 'prefix', v0, '-', v1, 'suffix');
  15254. * ```
  15255. *
  15256. * @param attrName The name of the attribute to update
  15257. * @param prefix Static value used for concatenation only.
  15258. * @param v0 Value checked for change.
  15259. * @param i0 Static value used for concatenation only.
  15260. * @param v1 Value checked for change.
  15261. * @param suffix Static value used for concatenation only.
  15262. * @param sanitizer An optional sanitizer function
  15263. * @returns itself, so that it may be chained.
  15264. * @codeGenApi
  15265. */
  15266. function ɵɵattributeInterpolate2(attrName, prefix, v0, i0, v1, suffix, sanitizer, namespace) {
  15267. const lView = getLView();
  15268. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  15269. if (interpolatedValue !== NO_CHANGE) {
  15270. const tNode = getSelectedTNode();
  15271. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15272. ngDevMode &&
  15273. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 2, prefix, i0, suffix);
  15274. }
  15275. return ɵɵattributeInterpolate2;
  15276. }
  15277. /**
  15278. *
  15279. * Update an interpolated attribute on an element with 3 bound values surrounded by text.
  15280. *
  15281. * Used when the value passed to a property has 3 interpolated values in it:
  15282. *
  15283. * ```html
  15284. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  15285. * ```
  15286. *
  15287. * Its compiled representation is::
  15288. *
  15289. * ```ts
  15290. * ɵɵattributeInterpolate3(
  15291. * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix');
  15292. * ```
  15293. *
  15294. * @param attrName The name of the attribute to update
  15295. * @param prefix Static value used for concatenation only.
  15296. * @param v0 Value checked for change.
  15297. * @param i0 Static value used for concatenation only.
  15298. * @param v1 Value checked for change.
  15299. * @param i1 Static value used for concatenation only.
  15300. * @param v2 Value checked for change.
  15301. * @param suffix Static value used for concatenation only.
  15302. * @param sanitizer An optional sanitizer function
  15303. * @returns itself, so that it may be chained.
  15304. * @codeGenApi
  15305. */
  15306. function ɵɵattributeInterpolate3(attrName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer, namespace) {
  15307. const lView = getLView();
  15308. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  15309. if (interpolatedValue !== NO_CHANGE) {
  15310. const tNode = getSelectedTNode();
  15311. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15312. ngDevMode &&
  15313. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 3, prefix, i0, i1, suffix);
  15314. }
  15315. return ɵɵattributeInterpolate3;
  15316. }
  15317. /**
  15318. *
  15319. * Update an interpolated attribute on an element with 4 bound values surrounded by text.
  15320. *
  15321. * Used when the value passed to a property has 4 interpolated values in it:
  15322. *
  15323. * ```html
  15324. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  15325. * ```
  15326. *
  15327. * Its compiled representation is::
  15328. *
  15329. * ```ts
  15330. * ɵɵattributeInterpolate4(
  15331. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  15332. * ```
  15333. *
  15334. * @param attrName The name of the attribute to update
  15335. * @param prefix Static value used for concatenation only.
  15336. * @param v0 Value checked for change.
  15337. * @param i0 Static value used for concatenation only.
  15338. * @param v1 Value checked for change.
  15339. * @param i1 Static value used for concatenation only.
  15340. * @param v2 Value checked for change.
  15341. * @param i2 Static value used for concatenation only.
  15342. * @param v3 Value checked for change.
  15343. * @param suffix Static value used for concatenation only.
  15344. * @param sanitizer An optional sanitizer function
  15345. * @returns itself, so that it may be chained.
  15346. * @codeGenApi
  15347. */
  15348. function ɵɵattributeInterpolate4(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer, namespace) {
  15349. const lView = getLView();
  15350. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  15351. if (interpolatedValue !== NO_CHANGE) {
  15352. const tNode = getSelectedTNode();
  15353. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15354. ngDevMode &&
  15355. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix);
  15356. }
  15357. return ɵɵattributeInterpolate4;
  15358. }
  15359. /**
  15360. *
  15361. * Update an interpolated attribute on an element with 5 bound values surrounded by text.
  15362. *
  15363. * Used when the value passed to a property has 5 interpolated values in it:
  15364. *
  15365. * ```html
  15366. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  15367. * ```
  15368. *
  15369. * Its compiled representation is::
  15370. *
  15371. * ```ts
  15372. * ɵɵattributeInterpolate5(
  15373. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  15374. * ```
  15375. *
  15376. * @param attrName The name of the attribute to update
  15377. * @param prefix Static value used for concatenation only.
  15378. * @param v0 Value checked for change.
  15379. * @param i0 Static value used for concatenation only.
  15380. * @param v1 Value checked for change.
  15381. * @param i1 Static value used for concatenation only.
  15382. * @param v2 Value checked for change.
  15383. * @param i2 Static value used for concatenation only.
  15384. * @param v3 Value checked for change.
  15385. * @param i3 Static value used for concatenation only.
  15386. * @param v4 Value checked for change.
  15387. * @param suffix Static value used for concatenation only.
  15388. * @param sanitizer An optional sanitizer function
  15389. * @returns itself, so that it may be chained.
  15390. * @codeGenApi
  15391. */
  15392. function ɵɵattributeInterpolate5(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer, namespace) {
  15393. const lView = getLView();
  15394. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  15395. if (interpolatedValue !== NO_CHANGE) {
  15396. const tNode = getSelectedTNode();
  15397. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15398. ngDevMode &&
  15399. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix);
  15400. }
  15401. return ɵɵattributeInterpolate5;
  15402. }
  15403. /**
  15404. *
  15405. * Update an interpolated attribute on an element with 6 bound values surrounded by text.
  15406. *
  15407. * Used when the value passed to a property has 6 interpolated values in it:
  15408. *
  15409. * ```html
  15410. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  15411. * ```
  15412. *
  15413. * Its compiled representation is::
  15414. *
  15415. * ```ts
  15416. * ɵɵattributeInterpolate6(
  15417. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  15418. * ```
  15419. *
  15420. * @param attrName The name of the attribute to update
  15421. * @param prefix Static value used for concatenation only.
  15422. * @param v0 Value checked for change.
  15423. * @param i0 Static value used for concatenation only.
  15424. * @param v1 Value checked for change.
  15425. * @param i1 Static value used for concatenation only.
  15426. * @param v2 Value checked for change.
  15427. * @param i2 Static value used for concatenation only.
  15428. * @param v3 Value checked for change.
  15429. * @param i3 Static value used for concatenation only.
  15430. * @param v4 Value checked for change.
  15431. * @param i4 Static value used for concatenation only.
  15432. * @param v5 Value checked for change.
  15433. * @param suffix Static value used for concatenation only.
  15434. * @param sanitizer An optional sanitizer function
  15435. * @returns itself, so that it may be chained.
  15436. * @codeGenApi
  15437. */
  15438. function ɵɵattributeInterpolate6(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer, namespace) {
  15439. const lView = getLView();
  15440. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  15441. if (interpolatedValue !== NO_CHANGE) {
  15442. const tNode = getSelectedTNode();
  15443. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15444. ngDevMode &&
  15445. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix);
  15446. }
  15447. return ɵɵattributeInterpolate6;
  15448. }
  15449. /**
  15450. *
  15451. * Update an interpolated attribute on an element with 7 bound values surrounded by text.
  15452. *
  15453. * Used when the value passed to a property has 7 interpolated values in it:
  15454. *
  15455. * ```html
  15456. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  15457. * ```
  15458. *
  15459. * Its compiled representation is::
  15460. *
  15461. * ```ts
  15462. * ɵɵattributeInterpolate7(
  15463. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  15464. * ```
  15465. *
  15466. * @param attrName The name of the attribute to update
  15467. * @param prefix Static value used for concatenation only.
  15468. * @param v0 Value checked for change.
  15469. * @param i0 Static value used for concatenation only.
  15470. * @param v1 Value checked for change.
  15471. * @param i1 Static value used for concatenation only.
  15472. * @param v2 Value checked for change.
  15473. * @param i2 Static value used for concatenation only.
  15474. * @param v3 Value checked for change.
  15475. * @param i3 Static value used for concatenation only.
  15476. * @param v4 Value checked for change.
  15477. * @param i4 Static value used for concatenation only.
  15478. * @param v5 Value checked for change.
  15479. * @param i5 Static value used for concatenation only.
  15480. * @param v6 Value checked for change.
  15481. * @param suffix Static value used for concatenation only.
  15482. * @param sanitizer An optional sanitizer function
  15483. * @returns itself, so that it may be chained.
  15484. * @codeGenApi
  15485. */
  15486. function ɵɵattributeInterpolate7(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer, namespace) {
  15487. const lView = getLView();
  15488. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  15489. if (interpolatedValue !== NO_CHANGE) {
  15490. const tNode = getSelectedTNode();
  15491. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15492. ngDevMode &&
  15493. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix);
  15494. }
  15495. return ɵɵattributeInterpolate7;
  15496. }
  15497. /**
  15498. *
  15499. * Update an interpolated attribute on an element with 8 bound values surrounded by text.
  15500. *
  15501. * Used when the value passed to a property has 8 interpolated values in it:
  15502. *
  15503. * ```html
  15504. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  15505. * ```
  15506. *
  15507. * Its compiled representation is::
  15508. *
  15509. * ```ts
  15510. * ɵɵattributeInterpolate8(
  15511. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  15512. * ```
  15513. *
  15514. * @param attrName The name of the attribute to update
  15515. * @param prefix Static value used for concatenation only.
  15516. * @param v0 Value checked for change.
  15517. * @param i0 Static value used for concatenation only.
  15518. * @param v1 Value checked for change.
  15519. * @param i1 Static value used for concatenation only.
  15520. * @param v2 Value checked for change.
  15521. * @param i2 Static value used for concatenation only.
  15522. * @param v3 Value checked for change.
  15523. * @param i3 Static value used for concatenation only.
  15524. * @param v4 Value checked for change.
  15525. * @param i4 Static value used for concatenation only.
  15526. * @param v5 Value checked for change.
  15527. * @param i5 Static value used for concatenation only.
  15528. * @param v6 Value checked for change.
  15529. * @param i6 Static value used for concatenation only.
  15530. * @param v7 Value checked for change.
  15531. * @param suffix Static value used for concatenation only.
  15532. * @param sanitizer An optional sanitizer function
  15533. * @returns itself, so that it may be chained.
  15534. * @codeGenApi
  15535. */
  15536. function ɵɵattributeInterpolate8(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer, namespace) {
  15537. const lView = getLView();
  15538. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  15539. if (interpolatedValue !== NO_CHANGE) {
  15540. const tNode = getSelectedTNode();
  15541. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15542. ngDevMode &&
  15543. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix);
  15544. }
  15545. return ɵɵattributeInterpolate8;
  15546. }
  15547. /**
  15548. * Update an interpolated attribute on an element with 9 or more bound values surrounded by text.
  15549. *
  15550. * Used when the number of interpolated values exceeds 8.
  15551. *
  15552. * ```html
  15553. * <div
  15554. * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
  15555. * ```
  15556. *
  15557. * Its compiled representation is::
  15558. *
  15559. * ```ts
  15560. * ɵɵattributeInterpolateV(
  15561. * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  15562. * 'suffix']);
  15563. * ```
  15564. *
  15565. * @param attrName The name of the attribute to update.
  15566. * @param values The collection of values and the strings in-between those values, beginning with
  15567. * a string prefix and ending with a string suffix.
  15568. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  15569. * @param sanitizer An optional sanitizer function
  15570. * @returns itself, so that it may be chained.
  15571. * @codeGenApi
  15572. */
  15573. function ɵɵattributeInterpolateV(attrName, values, sanitizer, namespace) {
  15574. const lView = getLView();
  15575. const interpolated = interpolationV(lView, values);
  15576. if (interpolated !== NO_CHANGE) {
  15577. const tNode = getSelectedTNode();
  15578. elementAttributeInternal(tNode, lView, attrName, interpolated, sanitizer, namespace);
  15579. if (ngDevMode) {
  15580. const interpolationInBetween = [values[0]]; // prefix
  15581. for (let i = 2; i < values.length; i += 2) {
  15582. interpolationInBetween.push(values[i]);
  15583. }
  15584. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
  15585. }
  15586. }
  15587. return ɵɵattributeInterpolateV;
  15588. }
  15589. const AT_THIS_LOCATION = '<-- AT THIS LOCATION';
  15590. /**
  15591. * Retrieves a user friendly string for a given TNodeType for use in
  15592. * friendly error messages
  15593. *
  15594. * @param tNodeType
  15595. * @returns
  15596. */
  15597. function getFriendlyStringFromTNodeType(tNodeType) {
  15598. switch (tNodeType) {
  15599. case 4 /* TNodeType.Container */:
  15600. return 'view container';
  15601. case 2 /* TNodeType.Element */:
  15602. return 'element';
  15603. case 8 /* TNodeType.ElementContainer */:
  15604. return 'ng-container';
  15605. case 32 /* TNodeType.Icu */:
  15606. return 'icu';
  15607. case 64 /* TNodeType.Placeholder */:
  15608. return 'i18n';
  15609. case 16 /* TNodeType.Projection */:
  15610. return 'projection';
  15611. case 1 /* TNodeType.Text */:
  15612. return 'text';
  15613. default:
  15614. // This should not happen as we cover all possible TNode types above.
  15615. return '<unknown>';
  15616. }
  15617. }
  15618. /**
  15619. * Validates that provided nodes match during the hydration process.
  15620. */
  15621. function validateMatchingNode(node, nodeType, tagName, lView, tNode, isViewContainerAnchor = false) {
  15622. if (!node ||
  15623. (node.nodeType !== nodeType ||
  15624. (node.nodeType === Node.ELEMENT_NODE &&
  15625. node.tagName.toLowerCase() !== tagName?.toLowerCase()))) {
  15626. const expectedNode = shortRNodeDescription(nodeType, tagName, null);
  15627. let header = `During hydration Angular expected ${expectedNode} but `;
  15628. const hostComponentDef = getDeclarationComponentDef(lView);
  15629. const componentClassName = hostComponentDef?.type?.name;
  15630. const expected = `Angular expected this DOM:\n\n${describeExpectedDom(lView, tNode, isViewContainerAnchor)}\n\n`;
  15631. let actual = '';
  15632. if (!node) {
  15633. // No node found during hydration.
  15634. header += `the node was not found.\n\n`;
  15635. }
  15636. else {
  15637. const actualNode = shortRNodeDescription(node.nodeType, node.tagName ?? null, node.textContent ?? null);
  15638. header += `found ${actualNode}.\n\n`;
  15639. actual = `Actual DOM is:\n\n${describeDomFromNode(node)}\n\n`;
  15640. }
  15641. const footer = getHydrationErrorFooter(componentClassName);
  15642. const message = header + expected + actual + getHydrationAttributeNote() + footer;
  15643. throw new RuntimeError(-500 /* RuntimeErrorCode.HYDRATION_NODE_MISMATCH */, message);
  15644. }
  15645. }
  15646. /**
  15647. * Validates that a given node has sibling nodes
  15648. */
  15649. function validateSiblingNodeExists(node) {
  15650. validateNodeExists(node);
  15651. if (!node.nextSibling) {
  15652. const header = 'During hydration Angular expected more sibling nodes to be present.\n\n';
  15653. const actual = `Actual DOM is:\n\n${describeDomFromNode(node)}\n\n`;
  15654. const footer = getHydrationErrorFooter();
  15655. const message = header + actual + footer;
  15656. throw new RuntimeError(-501 /* RuntimeErrorCode.HYDRATION_MISSING_SIBLINGS */, message);
  15657. }
  15658. }
  15659. /**
  15660. * Validates that a node exists or throws
  15661. */
  15662. function validateNodeExists(node, lView = null, tNode = null) {
  15663. if (!node) {
  15664. const header = 'During hydration, Angular expected an element to be present at this location.\n\n';
  15665. let expected = '';
  15666. let footer = '';
  15667. if (lView !== null && tNode !== null) {
  15668. expected = `${describeExpectedDom(lView, tNode, false)}\n\n`;
  15669. footer = getHydrationErrorFooter();
  15670. }
  15671. throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + expected + footer);
  15672. }
  15673. }
  15674. /**
  15675. * Builds the hydration error message when a node is not found
  15676. *
  15677. * @param lView the LView where the node exists
  15678. * @param tNode the TNode
  15679. */
  15680. function nodeNotFoundError(lView, tNode) {
  15681. const header = 'During serialization, Angular was unable to find an element in the DOM:\n\n';
  15682. const expected = `${describeExpectedDom(lView, tNode, false)}\n\n`;
  15683. const footer = getHydrationErrorFooter();
  15684. throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + expected + footer);
  15685. }
  15686. /**
  15687. * Builds a hydration error message when a node is not found at a path location
  15688. *
  15689. * @param host the Host Node
  15690. * @param path the path to the node
  15691. */
  15692. function nodeNotFoundAtPathError(host, path) {
  15693. const header = `During hydration Angular was unable to locate a node ` +
  15694. `using the "${path}" path, starting from the ${describeRNode(host)} node.\n\n`;
  15695. const footer = getHydrationErrorFooter();
  15696. throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + footer);
  15697. }
  15698. /**
  15699. * Builds the hydration error message in the case that dom nodes are created outside of
  15700. * the Angular context and are being used as projected nodes
  15701. *
  15702. * @param lView the LView
  15703. * @param tNode the TNode
  15704. * @returns an error
  15705. */
  15706. function unsupportedProjectionOfDomNodes(rNode) {
  15707. const header = 'During serialization, Angular detected DOM nodes ' +
  15708. 'that were created outside of Angular context and provided as projectable nodes ' +
  15709. '(likely via `ViewContainerRef.createComponent` or `createComponent` APIs). ' +
  15710. 'Hydration is not supported for such cases, consider refactoring the code to avoid ' +
  15711. 'this pattern or using `ngSkipHydration` on the host element of the component.\n\n';
  15712. const actual = `${describeDomFromNode(rNode)}\n\n`;
  15713. const message = header + actual + getHydrationAttributeNote();
  15714. return new RuntimeError(-503 /* RuntimeErrorCode.UNSUPPORTED_PROJECTION_DOM_NODES */, message);
  15715. }
  15716. /**
  15717. * Builds the hydration error message in the case that ngSkipHydration was used on a
  15718. * node that is not a component host element or host binding
  15719. *
  15720. * @param rNode the HTML Element
  15721. * @returns an error
  15722. */
  15723. function invalidSkipHydrationHost(rNode) {
  15724. const header = 'The `ngSkipHydration` flag is applied on a node ' +
  15725. 'that doesn\'t act as a component host. Hydration can be ' +
  15726. 'skipped only on per-component basis.\n\n';
  15727. const actual = `${describeDomFromNode(rNode)}\n\n`;
  15728. const footer = 'Please move the `ngSkipHydration` attribute to the component host element.\n\n';
  15729. const message = header + actual + footer;
  15730. return new RuntimeError(-504 /* RuntimeErrorCode.INVALID_SKIP_HYDRATION_HOST */, message);
  15731. }
  15732. // Stringification methods
  15733. /**
  15734. * Stringifies a given TNode's attributes
  15735. *
  15736. * @param tNode a provided TNode
  15737. * @returns string
  15738. */
  15739. function stringifyTNodeAttrs(tNode) {
  15740. const results = [];
  15741. if (tNode.attrs) {
  15742. for (let i = 0; i < tNode.attrs.length;) {
  15743. const attrName = tNode.attrs[i++];
  15744. // Once we reach the first flag, we know that the list of
  15745. // attributes is over.
  15746. if (typeof attrName == 'number') {
  15747. break;
  15748. }
  15749. const attrValue = tNode.attrs[i++];
  15750. results.push(`${attrName}="${shorten(attrValue)}"`);
  15751. }
  15752. }
  15753. return results.join(' ');
  15754. }
  15755. /**
  15756. * The list of internal attributes that should be filtered out while
  15757. * producing an error message.
  15758. */
  15759. const internalAttrs = new Set(['ngh', 'ng-version', 'ng-server-context']);
  15760. /**
  15761. * Stringifies an HTML Element's attributes
  15762. *
  15763. * @param rNode an HTML Element
  15764. * @returns string
  15765. */
  15766. function stringifyRNodeAttrs(rNode) {
  15767. const results = [];
  15768. for (let i = 0; i < rNode.attributes.length; i++) {
  15769. const attr = rNode.attributes[i];
  15770. if (internalAttrs.has(attr.name))
  15771. continue;
  15772. results.push(`${attr.name}="${shorten(attr.value)}"`);
  15773. }
  15774. return results.join(' ');
  15775. }
  15776. // Methods for Describing the DOM
  15777. /**
  15778. * Converts a tNode to a helpful readable string value for use in error messages
  15779. *
  15780. * @param tNode a given TNode
  15781. * @param innerContent the content of the node
  15782. * @returns string
  15783. */
  15784. function describeTNode(tNode, innerContent = '…') {
  15785. switch (tNode.type) {
  15786. case 1 /* TNodeType.Text */:
  15787. const content = tNode.value ? `(${tNode.value})` : '';
  15788. return `#text${content}`;
  15789. case 2 /* TNodeType.Element */:
  15790. const attrs = stringifyTNodeAttrs(tNode);
  15791. const tag = tNode.value.toLowerCase();
  15792. return `<${tag}${attrs ? ' ' + attrs : ''}>${innerContent}</${tag}>`;
  15793. case 8 /* TNodeType.ElementContainer */:
  15794. return '<!-- ng-container -->';
  15795. case 4 /* TNodeType.Container */:
  15796. return '<!-- container -->';
  15797. default:
  15798. const typeAsString = getFriendlyStringFromTNodeType(tNode.type);
  15799. return `#node(${typeAsString})`;
  15800. }
  15801. }
  15802. /**
  15803. * Converts an RNode to a helpful readable string value for use in error messages
  15804. *
  15805. * @param rNode a given RNode
  15806. * @param innerContent the content of the node
  15807. * @returns string
  15808. */
  15809. function describeRNode(rNode, innerContent = '…') {
  15810. const node = rNode;
  15811. switch (node.nodeType) {
  15812. case Node.ELEMENT_NODE:
  15813. const tag = node.tagName.toLowerCase();
  15814. const attrs = stringifyRNodeAttrs(node);
  15815. return `<${tag}${attrs ? ' ' + attrs : ''}>${innerContent}</${tag}>`;
  15816. case Node.TEXT_NODE:
  15817. const content = node.textContent ? shorten(node.textContent) : '';
  15818. return `#text${content ? `(${content})` : ''}`;
  15819. case Node.COMMENT_NODE:
  15820. return `<!-- ${shorten(node.textContent ?? '')} -->`;
  15821. default:
  15822. return `#node(${node.nodeType})`;
  15823. }
  15824. }
  15825. /**
  15826. * Builds the string containing the expected DOM present given the LView and TNode
  15827. * values for a readable error message
  15828. *
  15829. * @param lView the lView containing the DOM
  15830. * @param tNode the tNode
  15831. * @param isViewContainerAnchor boolean
  15832. * @returns string
  15833. */
  15834. function describeExpectedDom(lView, tNode, isViewContainerAnchor) {
  15835. const spacer = ' ';
  15836. let content = '';
  15837. if (tNode.prev) {
  15838. content += spacer + '…\n';
  15839. content += spacer + describeTNode(tNode.prev) + '\n';
  15840. }
  15841. else if (tNode.type && tNode.type & 12 /* TNodeType.AnyContainer */) {
  15842. content += spacer + '…\n';
  15843. }
  15844. if (isViewContainerAnchor) {
  15845. content += spacer + describeTNode(tNode) + '\n';
  15846. content += spacer + `<!-- container --> ${AT_THIS_LOCATION}\n`;
  15847. }
  15848. else {
  15849. content += spacer + describeTNode(tNode) + ` ${AT_THIS_LOCATION}\n`;
  15850. }
  15851. content += spacer + '…\n';
  15852. const parentRNode = tNode.type ? getParentRElement(lView[TVIEW], tNode, lView) : null;
  15853. if (parentRNode) {
  15854. content = describeRNode(parentRNode, '\n' + content);
  15855. }
  15856. return content;
  15857. }
  15858. /**
  15859. * Builds the string containing the DOM present around a given RNode for a
  15860. * readable error message
  15861. *
  15862. * @param node the RNode
  15863. * @returns string
  15864. */
  15865. function describeDomFromNode(node) {
  15866. const spacer = ' ';
  15867. let content = '';
  15868. const currentNode = node;
  15869. if (currentNode.previousSibling) {
  15870. content += spacer + '…\n';
  15871. content += spacer + describeRNode(currentNode.previousSibling) + '\n';
  15872. }
  15873. content += spacer + describeRNode(currentNode) + ` ${AT_THIS_LOCATION}\n`;
  15874. if (node.nextSibling) {
  15875. content += spacer + '…\n';
  15876. }
  15877. if (node.parentNode) {
  15878. content = describeRNode(currentNode.parentNode, '\n' + content);
  15879. }
  15880. return content;
  15881. }
  15882. /**
  15883. * Shortens the description of a given RNode by its type for readability
  15884. *
  15885. * @param nodeType the type of node
  15886. * @param tagName the node tag name
  15887. * @param textContent the text content in the node
  15888. * @returns string
  15889. */
  15890. function shortRNodeDescription(nodeType, tagName, textContent) {
  15891. switch (nodeType) {
  15892. case Node.ELEMENT_NODE:
  15893. return `<${tagName.toLowerCase()}>`;
  15894. case Node.TEXT_NODE:
  15895. const content = textContent ? ` (with the "${shorten(textContent)}" content)` : '';
  15896. return `a text node${content}`;
  15897. case Node.COMMENT_NODE:
  15898. return 'a comment node';
  15899. default:
  15900. return `#node(nodeType=${nodeType})`;
  15901. }
  15902. }
  15903. /**
  15904. * Builds the footer hydration error message
  15905. *
  15906. * @param componentClassName the name of the component class
  15907. * @returns string
  15908. */
  15909. function getHydrationErrorFooter(componentClassName) {
  15910. const componentInfo = componentClassName ? `the "${componentClassName}"` : 'corresponding';
  15911. return `To fix this problem:\n` +
  15912. ` * check ${componentInfo} component for hydration-related issues\n` +
  15913. ` * check to see if your template has valid HTML structure\n` +
  15914. ` * or skip hydration by adding the \`ngSkipHydration\` attribute ` +
  15915. `to its host node in a template\n\n`;
  15916. }
  15917. /**
  15918. * An attribute related note for hydration errors
  15919. */
  15920. function getHydrationAttributeNote() {
  15921. return 'Note: attributes are only displayed to better represent the DOM' +
  15922. ' but have no effect on hydration mismatches.\n\n';
  15923. }
  15924. // Node string utility functions
  15925. /**
  15926. * Strips all newlines out of a given string
  15927. *
  15928. * @param input a string to be cleared of new line characters
  15929. * @returns
  15930. */
  15931. function stripNewlines(input) {
  15932. return input.replace(/\s+/gm, '');
  15933. }
  15934. /**
  15935. * Reduces a string down to a maximum length of characters with ellipsis for readability
  15936. *
  15937. * @param input a string input
  15938. * @param maxLength a maximum length in characters
  15939. * @returns string
  15940. */
  15941. function shorten(input, maxLength = 50) {
  15942. if (!input) {
  15943. return '';
  15944. }
  15945. input = stripNewlines(input);
  15946. return input.length > maxLength ? `${input.substring(0, maxLength - 1)}…` : input;
  15947. }
  15948. /**
  15949. * Regexp that extracts a reference node information from the compressed node location.
  15950. * The reference node is represented as either:
  15951. * - a number which points to an LView slot
  15952. * - the `b` char which indicates that the lookup should start from the `document.body`
  15953. * - the `h` char to start lookup from the component host node (`lView[HOST]`)
  15954. */
  15955. const REF_EXTRACTOR_REGEXP = new RegExp(`^(\\d+)*(${REFERENCE_NODE_BODY}|${REFERENCE_NODE_HOST})*(.*)`);
  15956. /**
  15957. * Helper function that takes a reference node location and a set of navigation steps
  15958. * (from the reference node) to a target node and outputs a string that represents
  15959. * a location.
  15960. *
  15961. * For example, given: referenceNode = 'b' (body) and path = ['firstChild', 'firstChild',
  15962. * 'nextSibling'], the function returns: `bf2n`.
  15963. */
  15964. function compressNodeLocation(referenceNode, path) {
  15965. const result = [referenceNode];
  15966. for (const segment of path) {
  15967. const lastIdx = result.length - 1;
  15968. if (lastIdx > 0 && result[lastIdx - 1] === segment) {
  15969. // An empty string in a count slot represents 1 occurrence of an instruction.
  15970. const value = (result[lastIdx] || 1);
  15971. result[lastIdx] = value + 1;
  15972. }
  15973. else {
  15974. // Adding a new segment to the path.
  15975. // Using an empty string in a counter field to avoid encoding `1`s
  15976. // into the path, since they are implicit (e.g. `f1n1` vs `fn`), so
  15977. // it's enough to have a single char in this case.
  15978. result.push(segment, '');
  15979. }
  15980. }
  15981. return result.join('');
  15982. }
  15983. /**
  15984. * Helper function that reverts the `compressNodeLocation` and transforms a given
  15985. * string into an array where at 0th position there is a reference node info and
  15986. * after that it contains information (in pairs) about a navigation step and the
  15987. * number of repetitions.
  15988. *
  15989. * For example, the path like 'bf2n' will be transformed to:
  15990. * ['b', 'firstChild', 2, 'nextSibling', 1].
  15991. *
  15992. * This information is later consumed by the code that navigates the DOM to find
  15993. * a given node by its location.
  15994. */
  15995. function decompressNodeLocation(path) {
  15996. const matches = path.match(REF_EXTRACTOR_REGEXP);
  15997. const [_, refNodeId, refNodeName, rest] = matches;
  15998. // If a reference node is represented by an index, transform it to a number.
  15999. const ref = refNodeId ? parseInt(refNodeId, 10) : refNodeName;
  16000. const steps = [];
  16001. // Match all segments in a path.
  16002. for (const [_, step, count] of rest.matchAll(/(f|n)(\d*)/g)) {
  16003. const repeat = parseInt(count, 10) || 1;
  16004. steps.push(step, repeat);
  16005. }
  16006. return [ref, ...steps];
  16007. }
  16008. /** Whether current TNode is a first node in an <ng-container>. */
  16009. function isFirstElementInNgContainer(tNode) {
  16010. return !tNode.prev && tNode.parent?.type === 8 /* TNodeType.ElementContainer */;
  16011. }
  16012. /** Returns an instruction index (subtracting HEADER_OFFSET). */
  16013. function getNoOffsetIndex(tNode) {
  16014. return tNode.index - HEADER_OFFSET;
  16015. }
  16016. /**
  16017. * Locate a node in DOM tree that corresponds to a given TNode.
  16018. *
  16019. * @param hydrationInfo The hydration annotation data
  16020. * @param tView the current tView
  16021. * @param lView the current lView
  16022. * @param tNode the current tNode
  16023. * @returns an RNode that represents a given tNode
  16024. */
  16025. function locateNextRNode(hydrationInfo, tView, lView, tNode) {
  16026. let native = null;
  16027. const noOffsetIndex = getNoOffsetIndex(tNode);
  16028. const nodes = hydrationInfo.data[NODES];
  16029. if (nodes?.[noOffsetIndex]) {
  16030. // We know the exact location of the node.
  16031. native = locateRNodeByPath(nodes[noOffsetIndex], lView);
  16032. }
  16033. else if (tView.firstChild === tNode) {
  16034. // We create a first node in this view, so we use a reference
  16035. // to the first child in this DOM segment.
  16036. native = hydrationInfo.firstChild;
  16037. }
  16038. else {
  16039. // Locate a node based on a previous sibling or a parent node.
  16040. const previousTNodeParent = tNode.prev === null;
  16041. const previousTNode = (tNode.prev ?? tNode.parent);
  16042. ngDevMode &&
  16043. assertDefined(previousTNode, 'Unexpected state: current TNode does not have a connection ' +
  16044. 'to the previous node or a parent node.');
  16045. if (isFirstElementInNgContainer(tNode)) {
  16046. const noOffsetParentIndex = getNoOffsetIndex(tNode.parent);
  16047. native = getSegmentHead(hydrationInfo, noOffsetParentIndex);
  16048. }
  16049. else {
  16050. let previousRElement = getNativeByTNode(previousTNode, lView);
  16051. if (previousTNodeParent) {
  16052. native = previousRElement.firstChild;
  16053. }
  16054. else {
  16055. // If the previous node is an element, but it also has container info,
  16056. // this means that we are processing a node like `<div #vcrTarget>`, which is
  16057. // represented in the DOM as `<div></div>...<!--container-->`.
  16058. // In this case, there are nodes *after* this element and we need to skip
  16059. // all of them to reach an element that we are looking for.
  16060. const noOffsetPrevSiblingIndex = getNoOffsetIndex(previousTNode);
  16061. const segmentHead = getSegmentHead(hydrationInfo, noOffsetPrevSiblingIndex);
  16062. if (previousTNode.type === 2 /* TNodeType.Element */ && segmentHead) {
  16063. const numRootNodesToSkip = calcSerializedContainerSize(hydrationInfo, noOffsetPrevSiblingIndex);
  16064. // `+1` stands for an anchor comment node after all the views in this container.
  16065. const nodesToSkip = numRootNodesToSkip + 1;
  16066. // First node after this segment.
  16067. native = siblingAfter(nodesToSkip, segmentHead);
  16068. }
  16069. else {
  16070. native = previousRElement.nextSibling;
  16071. }
  16072. }
  16073. }
  16074. }
  16075. return native;
  16076. }
  16077. /**
  16078. * Skips over a specified number of nodes and returns the next sibling node after that.
  16079. */
  16080. function siblingAfter(skip, from) {
  16081. let currentNode = from;
  16082. for (let i = 0; i < skip; i++) {
  16083. ngDevMode && validateSiblingNodeExists(currentNode);
  16084. currentNode = currentNode.nextSibling;
  16085. }
  16086. return currentNode;
  16087. }
  16088. /**
  16089. * Helper function to produce a string representation of the navigation steps
  16090. * (in terms of `nextSibling` and `firstChild` navigations). Used in error
  16091. * messages in dev mode.
  16092. */
  16093. function stringifyNavigationInstructions(instructions) {
  16094. const container = [];
  16095. for (let i = 0; i < instructions.length; i += 2) {
  16096. const step = instructions[i];
  16097. const repeat = instructions[i + 1];
  16098. for (let r = 0; r < repeat; r++) {
  16099. container.push(step === NodeNavigationStep.FirstChild ? 'firstChild' : 'nextSibling');
  16100. }
  16101. }
  16102. return container.join('.');
  16103. }
  16104. /**
  16105. * Helper function that navigates from a starting point node (the `from` node)
  16106. * using provided set of navigation instructions (within `path` argument).
  16107. */
  16108. function navigateToNode(from, instructions) {
  16109. let node = from;
  16110. for (let i = 0; i < instructions.length; i += 2) {
  16111. const step = instructions[i];
  16112. const repeat = instructions[i + 1];
  16113. for (let r = 0; r < repeat; r++) {
  16114. if (ngDevMode && !node) {
  16115. throw nodeNotFoundAtPathError(from, stringifyNavigationInstructions(instructions));
  16116. }
  16117. switch (step) {
  16118. case NodeNavigationStep.FirstChild:
  16119. node = node.firstChild;
  16120. break;
  16121. case NodeNavigationStep.NextSibling:
  16122. node = node.nextSibling;
  16123. break;
  16124. }
  16125. }
  16126. }
  16127. if (ngDevMode && !node) {
  16128. throw nodeNotFoundAtPathError(from, stringifyNavigationInstructions(instructions));
  16129. }
  16130. return node;
  16131. }
  16132. /**
  16133. * Locates an RNode given a set of navigation instructions (which also contains
  16134. * a starting point node info).
  16135. */
  16136. function locateRNodeByPath(path, lView) {
  16137. const [referenceNode, ...navigationInstructions] = decompressNodeLocation(path);
  16138. let ref;
  16139. if (referenceNode === REFERENCE_NODE_HOST) {
  16140. ref = lView[DECLARATION_COMPONENT_VIEW][HOST];
  16141. }
  16142. else if (referenceNode === REFERENCE_NODE_BODY) {
  16143. ref = ɵɵresolveBody(lView[DECLARATION_COMPONENT_VIEW][HOST]);
  16144. }
  16145. else {
  16146. const parentElementId = Number(referenceNode);
  16147. ref = unwrapRNode(lView[parentElementId + HEADER_OFFSET]);
  16148. }
  16149. return navigateToNode(ref, navigationInstructions);
  16150. }
  16151. /**
  16152. * Generate a list of DOM navigation operations to get from node `start` to node `finish`.
  16153. *
  16154. * Note: assumes that node `start` occurs before node `finish` in an in-order traversal of the DOM
  16155. * tree. That is, we should be able to get from `start` to `finish` purely by using `.firstChild`
  16156. * and `.nextSibling` operations.
  16157. */
  16158. function navigateBetween(start, finish) {
  16159. if (start === finish) {
  16160. return [];
  16161. }
  16162. else if (start.parentElement == null || finish.parentElement == null) {
  16163. return null;
  16164. }
  16165. else if (start.parentElement === finish.parentElement) {
  16166. return navigateBetweenSiblings(start, finish);
  16167. }
  16168. else {
  16169. // `finish` is a child of its parent, so the parent will always have a child.
  16170. const parent = finish.parentElement;
  16171. const parentPath = navigateBetween(start, parent);
  16172. const childPath = navigateBetween(parent.firstChild, finish);
  16173. if (!parentPath || !childPath)
  16174. return null;
  16175. return [
  16176. // First navigate to `finish`'s parent
  16177. ...parentPath,
  16178. // Then to its first child.
  16179. NodeNavigationStep.FirstChild,
  16180. // And finally from that node to `finish` (maybe a no-op if we're already there).
  16181. ...childPath,
  16182. ];
  16183. }
  16184. }
  16185. /**
  16186. * Calculates a path between 2 sibling nodes (generates a number of `NextSibling` navigations).
  16187. * Returns `null` if no such path exists between the given nodes.
  16188. */
  16189. function navigateBetweenSiblings(start, finish) {
  16190. const nav = [];
  16191. let node = null;
  16192. for (node = start; node != null && node !== finish; node = node.nextSibling) {
  16193. nav.push(NodeNavigationStep.NextSibling);
  16194. }
  16195. // If the `node` becomes `null` or `undefined` at the end, that means that we
  16196. // didn't find the `end` node, thus return `null` (which would trigger serialization
  16197. // error to be produced).
  16198. return node == null ? null : nav;
  16199. }
  16200. /**
  16201. * Calculates a path between 2 nodes in terms of `nextSibling` and `firstChild`
  16202. * navigations:
  16203. * - the `from` node is a known node, used as an starting point for the lookup
  16204. * (the `fromNodeName` argument is a string representation of the node).
  16205. * - the `to` node is a node that the runtime logic would be looking up,
  16206. * using the path generated by this function.
  16207. */
  16208. function calcPathBetween(from, to, fromNodeName) {
  16209. const path = navigateBetween(from, to);
  16210. return path === null ? null : compressNodeLocation(fromNodeName, path);
  16211. }
  16212. /**
  16213. * Invoked at serialization time (on the server) when a set of navigation
  16214. * instructions needs to be generated for a TNode.
  16215. */
  16216. function calcPathForNode(tNode, lView) {
  16217. const parentTNode = tNode.parent;
  16218. let parentIndex;
  16219. let parentRNode;
  16220. let referenceNodeName;
  16221. if (parentTNode === null || !(parentTNode.type & 3 /* TNodeType.AnyRNode */)) {
  16222. // If there is no parent TNode or a parent TNode does not represent an RNode
  16223. // (i.e. not a DOM node), use component host element as a reference node.
  16224. parentIndex = referenceNodeName = REFERENCE_NODE_HOST;
  16225. parentRNode = lView[DECLARATION_COMPONENT_VIEW][HOST];
  16226. }
  16227. else {
  16228. // Use parent TNode as a reference node.
  16229. parentIndex = parentTNode.index;
  16230. parentRNode = unwrapRNode(lView[parentIndex]);
  16231. referenceNodeName = renderStringify(parentIndex - HEADER_OFFSET);
  16232. }
  16233. let rNode = unwrapRNode(lView[tNode.index]);
  16234. if (tNode.type & 12 /* TNodeType.AnyContainer */) {
  16235. // For <ng-container> nodes, instead of serializing a reference
  16236. // to the anchor comment node, serialize a location of the first
  16237. // DOM element. Paired with the container size (serialized as a part
  16238. // of `ngh.containers`), it should give enough information for runtime
  16239. // to hydrate nodes in this container.
  16240. const firstRNode = getFirstNativeNode(lView, tNode);
  16241. // If container is not empty, use a reference to the first element,
  16242. // otherwise, rNode would point to an anchor comment node.
  16243. if (firstRNode) {
  16244. rNode = firstRNode;
  16245. }
  16246. }
  16247. let path = calcPathBetween(parentRNode, rNode, referenceNodeName);
  16248. if (path === null && parentRNode !== rNode) {
  16249. // Searching for a path between elements within a host node failed.
  16250. // Trying to find a path to an element starting from the `document.body` instead.
  16251. //
  16252. // Important note: this type of reference is relatively unstable, since Angular
  16253. // may not be able to control parts of the page that the runtime logic navigates
  16254. // through. This is mostly needed to cover "portals" use-case (like menus, dialog boxes,
  16255. // etc), where nodes are content-projected (including direct DOM manipulations) outside
  16256. // of the host node. The better solution is to provide APIs to work with "portals",
  16257. // at which point this code path would not be needed.
  16258. const body = parentRNode.ownerDocument.body;
  16259. path = calcPathBetween(body, rNode, REFERENCE_NODE_BODY);
  16260. if (path === null) {
  16261. // If the path is still empty, it's likely that this node is detached and
  16262. // won't be found during hydration.
  16263. throw nodeNotFoundError(lView, tNode);
  16264. }
  16265. }
  16266. return path;
  16267. }
  16268. function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) {
  16269. ngDevMode && assertFirstCreatePass(tView);
  16270. ngDevMode && ngDevMode.firstCreatePass++;
  16271. const tViewConsts = tView.consts;
  16272. // TODO(pk): refactor getOrCreateTNode to have the "create" only version
  16273. const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex));
  16274. resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
  16275. registerPostOrderHooks(tView, tNode);
  16276. const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, null /* ssrId */);
  16277. if (tView.queries !== null) {
  16278. tView.queries.template(tView, tNode);
  16279. embeddedTView.queries = tView.queries.embeddedTView(tNode);
  16280. }
  16281. return tNode;
  16282. }
  16283. /**
  16284. * Creates an LContainer for an ng-template (dynamically-inserted view), e.g.
  16285. *
  16286. * <ng-template #foo>
  16287. * <div></div>
  16288. * </ng-template>
  16289. *
  16290. * @param index The index of the container in the data array
  16291. * @param templateFn Inline template
  16292. * @param decls The number of nodes, local refs, and pipes for this template
  16293. * @param vars The number of bindings for this template
  16294. * @param tagName The name of the container element, if applicable
  16295. * @param attrsIndex Index of template attributes in the `consts` array.
  16296. * @param localRefs Index of the local references in the `consts` array.
  16297. * @param localRefExtractor A function which extracts local-refs values from the template.
  16298. * Defaults to the current element associated with the local-ref.
  16299. *
  16300. * @codeGenApi
  16301. */
  16302. function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex, localRefExtractor) {
  16303. const lView = getLView();
  16304. const tView = getTView();
  16305. const adjustedIndex = index + HEADER_OFFSET;
  16306. const tNode = tView.firstCreatePass ? templateFirstCreatePass(adjustedIndex, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) :
  16307. tView.data[adjustedIndex];
  16308. setCurrentTNode(tNode, false);
  16309. const comment = _locateOrCreateContainerAnchor(tView, lView, tNode, index);
  16310. if (wasLastNodeCreated()) {
  16311. appendChild(tView, lView, comment, tNode);
  16312. }
  16313. attachPatchData(comment, lView);
  16314. addToViewTree(lView, lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode));
  16315. if (isDirectiveHost(tNode)) {
  16316. createDirectivesInstances(tView, lView, tNode);
  16317. }
  16318. if (localRefsIndex != null) {
  16319. saveResolvedLocalsInData(lView, tNode, localRefExtractor);
  16320. }
  16321. }
  16322. let _locateOrCreateContainerAnchor = createContainerAnchorImpl;
  16323. /**
  16324. * Regular creation mode for LContainers and their anchor (comment) nodes.
  16325. */
  16326. function createContainerAnchorImpl(tView, lView, tNode, index) {
  16327. lastNodeWasCreated(true);
  16328. return lView[RENDERER].createComment(ngDevMode ? 'container' : '');
  16329. }
  16330. /**
  16331. * Enables hydration code path (to lookup existing elements in DOM)
  16332. * in addition to the regular creation mode for LContainers and their
  16333. * anchor (comment) nodes.
  16334. */
  16335. function locateOrCreateContainerAnchorImpl(tView, lView, tNode, index) {
  16336. const hydrationInfo = lView[HYDRATION];
  16337. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode(hydrationInfo, index);
  16338. lastNodeWasCreated(isNodeCreationMode);
  16339. // Regular creation mode.
  16340. if (isNodeCreationMode) {
  16341. return createContainerAnchorImpl(tView, lView, tNode, index);
  16342. }
  16343. const ssrId = hydrationInfo.data[TEMPLATES]?.[index] ?? null;
  16344. // Apply `ssrId` value to the underlying TView if it was not previously set.
  16345. //
  16346. // There might be situations when the same component is present in a template
  16347. // multiple times and some instances are opted-out of using hydration via
  16348. // `ngSkipHydration` attribute. In this scenario, at the time a TView is created,
  16349. // the `ssrId` might be `null` (if the first component is opted-out of hydration).
  16350. // The code below makes sure that the `ssrId` is applied to the TView if it's still
  16351. // `null` and verifies we never try to override it with a different value.
  16352. if (ssrId !== null && tNode.tView !== null) {
  16353. if (tNode.tView.ssrId === null) {
  16354. tNode.tView.ssrId = ssrId;
  16355. }
  16356. else {
  16357. ngDevMode &&
  16358. assertEqual(tNode.tView.ssrId, ssrId, 'Unexpected value of the `ssrId` for this TView');
  16359. }
  16360. }
  16361. // Hydration mode, looking up existing elements in DOM.
  16362. const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
  16363. ngDevMode && validateNodeExists(currentRNode, lView, tNode);
  16364. setSegmentHead(hydrationInfo, index, currentRNode);
  16365. const viewContainerSize = calcSerializedContainerSize(hydrationInfo, index);
  16366. const comment = siblingAfter(viewContainerSize, currentRNode);
  16367. if (ngDevMode) {
  16368. validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
  16369. markRNodeAsClaimedByHydration(comment);
  16370. }
  16371. return comment;
  16372. }
  16373. function enableLocateOrCreateContainerAnchorImpl() {
  16374. _locateOrCreateContainerAnchor = locateOrCreateContainerAnchorImpl;
  16375. }
  16376. /** Store a value in the `data` at a given `index`. */
  16377. function store(tView, lView, index, value) {
  16378. // We don't store any static data for local variables, so the first time
  16379. // we see the template, we should store as null to avoid a sparse array
  16380. if (index >= tView.data.length) {
  16381. tView.data[index] = null;
  16382. tView.blueprint[index] = null;
  16383. }
  16384. lView[index] = value;
  16385. }
  16386. /**
  16387. * Retrieves a local reference from the current contextViewData.
  16388. *
  16389. * If the reference to retrieve is in a parent view, this instruction is used in conjunction
  16390. * with a nextContext() call, which walks up the tree and updates the contextViewData instance.
  16391. *
  16392. * @param index The index of the local ref in contextViewData.
  16393. *
  16394. * @codeGenApi
  16395. */
  16396. function ɵɵreference(index) {
  16397. const contextLView = getContextLView();
  16398. return load(contextLView, HEADER_OFFSET + index);
  16399. }
  16400. /**
  16401. * Update a property on a selected element.
  16402. *
  16403. * Operates on the element selected by index via the {@link select} instruction.
  16404. *
  16405. * If the property name also exists as an input property on one of the element's directives,
  16406. * the component property will be set instead of the element property. This check must
  16407. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled
  16408. *
  16409. * @param propName Name of property. Because it is going to DOM, this is not subject to
  16410. * renaming as part of minification.
  16411. * @param value New value to write.
  16412. * @param sanitizer An optional function used to sanitize the value.
  16413. * @returns This function returns itself so that it may be chained
  16414. * (e.g. `property('name', ctx.name)('title', ctx.title)`)
  16415. *
  16416. * @codeGenApi
  16417. */
  16418. function ɵɵproperty(propName, value, sanitizer) {
  16419. const lView = getLView();
  16420. const bindingIndex = nextBindingIndex();
  16421. if (bindingUpdated(lView, bindingIndex, value)) {
  16422. const tView = getTView();
  16423. const tNode = getSelectedTNode();
  16424. elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, false);
  16425. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
  16426. }
  16427. return ɵɵproperty;
  16428. }
  16429. /**
  16430. * Given `<div style="..." my-dir>` and `MyDir` with `@Input('style')` we need to write to
  16431. * directive input.
  16432. */
  16433. function setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased) {
  16434. const inputs = tNode.inputs;
  16435. const property = isClassBased ? 'class' : 'style';
  16436. // We support both 'class' and `className` hence the fallback.
  16437. setInputsForProperty(tView, lView, inputs[property], property, value);
  16438. }
  16439. function elementStartFirstCreatePass(index, tView, lView, name, attrsIndex, localRefsIndex) {
  16440. ngDevMode && assertFirstCreatePass(tView);
  16441. ngDevMode && ngDevMode.firstCreatePass++;
  16442. const tViewConsts = tView.consts;
  16443. const attrs = getConstant(tViewConsts, attrsIndex);
  16444. const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, name, attrs);
  16445. resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
  16446. if (tNode.attrs !== null) {
  16447. computeStaticStyling(tNode, tNode.attrs, false);
  16448. }
  16449. if (tNode.mergedAttrs !== null) {
  16450. computeStaticStyling(tNode, tNode.mergedAttrs, true);
  16451. }
  16452. if (tView.queries !== null) {
  16453. tView.queries.elementStart(tView, tNode);
  16454. }
  16455. return tNode;
  16456. }
  16457. /**
  16458. * Create DOM element. The instruction must later be followed by `elementEnd()` call.
  16459. *
  16460. * @param index Index of the element in the LView array
  16461. * @param name Name of the DOM Node
  16462. * @param attrsIndex Index of the element's attributes in the `consts` array.
  16463. * @param localRefsIndex Index of the element's local references in the `consts` array.
  16464. * @returns This function returns itself so that it may be chained.
  16465. *
  16466. * Attributes and localRefs are passed as an array of strings where elements with an even index
  16467. * hold an attribute name and elements with an odd index hold an attribute value, ex.:
  16468. * ['id', 'warning5', 'class', 'alert']
  16469. *
  16470. * @codeGenApi
  16471. */
  16472. function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
  16473. const lView = getLView();
  16474. const tView = getTView();
  16475. const adjustedIndex = HEADER_OFFSET + index;
  16476. ngDevMode &&
  16477. assertEqual(getBindingIndex(), tView.bindingStartIndex, 'elements should be created before any bindings');
  16478. ngDevMode && assertIndexInRange(lView, adjustedIndex);
  16479. const renderer = lView[RENDERER];
  16480. const tNode = tView.firstCreatePass ?
  16481. elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex) :
  16482. tView.data[adjustedIndex];
  16483. const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name, index);
  16484. lView[adjustedIndex] = native;
  16485. const hasDirectives = isDirectiveHost(tNode);
  16486. if (ngDevMode && tView.firstCreatePass) {
  16487. validateElementIsKnown(native, lView, tNode.value, tView.schemas, hasDirectives);
  16488. }
  16489. setCurrentTNode(tNode, true);
  16490. setupStaticAttributes(renderer, native, tNode);
  16491. if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */ && wasLastNodeCreated()) {
  16492. // In the i18n case, the translation may have removed this element, so only add it if it is not
  16493. // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
  16494. appendChild(tView, lView, native, tNode);
  16495. }
  16496. // any immediate children of a component or template container must be pre-emptively
  16497. // monkey-patched with the component view data so that the element can be inspected
  16498. // later on using any element discovery utility methods (see `element_discovery.ts`)
  16499. if (getElementDepthCount() === 0) {
  16500. attachPatchData(native, lView);
  16501. }
  16502. increaseElementDepthCount();
  16503. if (hasDirectives) {
  16504. createDirectivesInstances(tView, lView, tNode);
  16505. executeContentQueries(tView, tNode, lView);
  16506. }
  16507. if (localRefsIndex !== null) {
  16508. saveResolvedLocalsInData(lView, tNode);
  16509. }
  16510. return ɵɵelementStart;
  16511. }
  16512. /**
  16513. * Mark the end of the element.
  16514. * @returns This function returns itself so that it may be chained.
  16515. *
  16516. * @codeGenApi
  16517. */
  16518. function ɵɵelementEnd() {
  16519. let currentTNode = getCurrentTNode();
  16520. ngDevMode && assertDefined(currentTNode, 'No parent node to close.');
  16521. if (isCurrentTNodeParent()) {
  16522. setCurrentTNodeAsNotParent();
  16523. }
  16524. else {
  16525. ngDevMode && assertHasParent(getCurrentTNode());
  16526. currentTNode = currentTNode.parent;
  16527. setCurrentTNode(currentTNode, false);
  16528. }
  16529. const tNode = currentTNode;
  16530. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
  16531. if (isSkipHydrationRootTNode(tNode)) {
  16532. leaveSkipHydrationBlock();
  16533. }
  16534. decreaseElementDepthCount();
  16535. const tView = getTView();
  16536. if (tView.firstCreatePass) {
  16537. registerPostOrderHooks(tView, currentTNode);
  16538. if (isContentQueryHost(currentTNode)) {
  16539. tView.queries.elementEnd(currentTNode);
  16540. }
  16541. }
  16542. if (tNode.classesWithoutHost != null && hasClassInput(tNode)) {
  16543. setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.classesWithoutHost, true);
  16544. }
  16545. if (tNode.stylesWithoutHost != null && hasStyleInput(tNode)) {
  16546. setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.stylesWithoutHost, false);
  16547. }
  16548. return ɵɵelementEnd;
  16549. }
  16550. /**
  16551. * Creates an empty element using {@link elementStart} and {@link elementEnd}
  16552. *
  16553. * @param index Index of the element in the data array
  16554. * @param name Name of the DOM Node
  16555. * @param attrsIndex Index of the element's attributes in the `consts` array.
  16556. * @param localRefsIndex Index of the element's local references in the `consts` array.
  16557. * @returns This function returns itself so that it may be chained.
  16558. *
  16559. * @codeGenApi
  16560. */
  16561. function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
  16562. ɵɵelementStart(index, name, attrsIndex, localRefsIndex);
  16563. ɵɵelementEnd();
  16564. return ɵɵelement;
  16565. }
  16566. let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name, index) => {
  16567. lastNodeWasCreated(true);
  16568. return createElementNode(renderer, name, getNamespace$1());
  16569. };
  16570. /**
  16571. * Enables hydration code path (to lookup existing elements in DOM)
  16572. * in addition to the regular creation mode of element nodes.
  16573. */
  16574. function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name, index) {
  16575. const hydrationInfo = lView[HYDRATION];
  16576. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode(hydrationInfo, index);
  16577. lastNodeWasCreated(isNodeCreationMode);
  16578. // Regular creation mode.
  16579. if (isNodeCreationMode) {
  16580. return createElementNode(renderer, name, getNamespace$1());
  16581. }
  16582. // Hydration mode, looking up an existing element in DOM.
  16583. const native = locateNextRNode(hydrationInfo, tView, lView, tNode);
  16584. ngDevMode && validateMatchingNode(native, Node.ELEMENT_NODE, name, lView, tNode);
  16585. ngDevMode && markRNodeAsClaimedByHydration(native);
  16586. // This element might also be an anchor of a view container.
  16587. if (getSerializedContainerViews(hydrationInfo, index)) {
  16588. // Important note: this element acts as an anchor, but it's **not** a part
  16589. // of the embedded view, so we start the segment **after** this element, taking
  16590. // a reference to the next sibling. For example, the following template:
  16591. // `<div #vcrTarget>` is represented in the DOM as `<div></div>...<!--container-->`,
  16592. // so while processing a `<div>` instruction, point to the next sibling as a
  16593. // start of a segment.
  16594. ngDevMode && validateNodeExists(native.nextSibling, lView, tNode);
  16595. setSegmentHead(hydrationInfo, index, native.nextSibling);
  16596. }
  16597. // Checks if the skip hydration attribute is present during hydration so we know to
  16598. // skip attempting to hydrate this block. We check both TNode and RElement for an
  16599. // attribute: the RElement case is needed for i18n cases, when we add it to host
  16600. // elements during the annotation phase (after all internal data structures are setup).
  16601. if (hydrationInfo &&
  16602. (hasSkipHydrationAttrOnTNode(tNode) || hasSkipHydrationAttrOnRElement(native))) {
  16603. if (isComponentHost(tNode)) {
  16604. enterSkipHydrationBlock(tNode);
  16605. // Since this isn't hydratable, we need to empty the node
  16606. // so there's no duplicate content after render
  16607. clearElementContents(native);
  16608. ngDevMode && ngDevMode.componentsSkippedHydration++;
  16609. }
  16610. else if (ngDevMode) {
  16611. // If this is not a component host, throw an error.
  16612. // Hydration can be skipped on per-component basis only.
  16613. throw invalidSkipHydrationHost(native);
  16614. }
  16615. }
  16616. return native;
  16617. }
  16618. function enableLocateOrCreateElementNodeImpl() {
  16619. _locateOrCreateElementNode = locateOrCreateElementNodeImpl;
  16620. }
  16621. function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) {
  16622. ngDevMode && ngDevMode.firstCreatePass++;
  16623. const tViewConsts = tView.consts;
  16624. const attrs = getConstant(tViewConsts, attrsIndex);
  16625. const tNode = getOrCreateTNode(tView, index, 8 /* TNodeType.ElementContainer */, 'ng-container', attrs);
  16626. // While ng-container doesn't necessarily support styling, we use the style context to identify
  16627. // and execute directives on the ng-container.
  16628. if (attrs !== null) {
  16629. computeStaticStyling(tNode, attrs, true);
  16630. }
  16631. const localRefs = getConstant(tViewConsts, localRefsIndex);
  16632. resolveDirectives(tView, lView, tNode, localRefs);
  16633. if (tView.queries !== null) {
  16634. tView.queries.elementStart(tView, tNode);
  16635. }
  16636. return tNode;
  16637. }
  16638. /**
  16639. * Creates a logical container for other nodes (<ng-container>) backed by a comment node in the DOM.
  16640. * The instruction must later be followed by `elementContainerEnd()` call.
  16641. *
  16642. * @param index Index of the element in the LView array
  16643. * @param attrsIndex Index of the container attributes in the `consts` array.
  16644. * @param localRefsIndex Index of the container's local references in the `consts` array.
  16645. * @returns This function returns itself so that it may be chained.
  16646. *
  16647. * Even if this instruction accepts a set of attributes no actual attribute values are propagated to
  16648. * the DOM (as a comment node can't have attributes). Attributes are here only for directive
  16649. * matching purposes and setting initial inputs of directives.
  16650. *
  16651. * @codeGenApi
  16652. */
  16653. function ɵɵelementContainerStart(index, attrsIndex, localRefsIndex) {
  16654. const lView = getLView();
  16655. const tView = getTView();
  16656. const adjustedIndex = index + HEADER_OFFSET;
  16657. ngDevMode && assertIndexInRange(lView, adjustedIndex);
  16658. ngDevMode &&
  16659. assertEqual(getBindingIndex(), tView.bindingStartIndex, 'element containers should be created before any bindings');
  16660. const tNode = tView.firstCreatePass ?
  16661. elementContainerStartFirstCreatePass(adjustedIndex, tView, lView, attrsIndex, localRefsIndex) :
  16662. tView.data[adjustedIndex];
  16663. setCurrentTNode(tNode, true);
  16664. const comment = _locateOrCreateElementContainerNode(tView, lView, tNode, index);
  16665. lView[adjustedIndex] = comment;
  16666. if (wasLastNodeCreated()) {
  16667. appendChild(tView, lView, comment, tNode);
  16668. }
  16669. attachPatchData(comment, lView);
  16670. if (isDirectiveHost(tNode)) {
  16671. createDirectivesInstances(tView, lView, tNode);
  16672. executeContentQueries(tView, tNode, lView);
  16673. }
  16674. if (localRefsIndex != null) {
  16675. saveResolvedLocalsInData(lView, tNode);
  16676. }
  16677. return ɵɵelementContainerStart;
  16678. }
  16679. /**
  16680. * Mark the end of the <ng-container>.
  16681. * @returns This function returns itself so that it may be chained.
  16682. *
  16683. * @codeGenApi
  16684. */
  16685. function ɵɵelementContainerEnd() {
  16686. let currentTNode = getCurrentTNode();
  16687. const tView = getTView();
  16688. if (isCurrentTNodeParent()) {
  16689. setCurrentTNodeAsNotParent();
  16690. }
  16691. else {
  16692. ngDevMode && assertHasParent(currentTNode);
  16693. currentTNode = currentTNode.parent;
  16694. setCurrentTNode(currentTNode, false);
  16695. }
  16696. ngDevMode && assertTNodeType(currentTNode, 8 /* TNodeType.ElementContainer */);
  16697. if (tView.firstCreatePass) {
  16698. registerPostOrderHooks(tView, currentTNode);
  16699. if (isContentQueryHost(currentTNode)) {
  16700. tView.queries.elementEnd(currentTNode);
  16701. }
  16702. }
  16703. return ɵɵelementContainerEnd;
  16704. }
  16705. /**
  16706. * Creates an empty logical container using {@link elementContainerStart}
  16707. * and {@link elementContainerEnd}
  16708. *
  16709. * @param index Index of the element in the LView array
  16710. * @param attrsIndex Index of the container attributes in the `consts` array.
  16711. * @param localRefsIndex Index of the container's local references in the `consts` array.
  16712. * @returns This function returns itself so that it may be chained.
  16713. *
  16714. * @codeGenApi
  16715. */
  16716. function ɵɵelementContainer(index, attrsIndex, localRefsIndex) {
  16717. ɵɵelementContainerStart(index, attrsIndex, localRefsIndex);
  16718. ɵɵelementContainerEnd();
  16719. return ɵɵelementContainer;
  16720. }
  16721. let _locateOrCreateElementContainerNode = (tView, lView, tNode, index) => {
  16722. lastNodeWasCreated(true);
  16723. return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
  16724. };
  16725. /**
  16726. * Enables hydration code path (to lookup existing elements in DOM)
  16727. * in addition to the regular creation mode of comment nodes that
  16728. * represent <ng-container>'s anchor.
  16729. */
  16730. function locateOrCreateElementContainerNode(tView, lView, tNode, index) {
  16731. let comment;
  16732. const hydrationInfo = lView[HYDRATION];
  16733. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
  16734. lastNodeWasCreated(isNodeCreationMode);
  16735. // Regular creation mode.
  16736. if (isNodeCreationMode) {
  16737. return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
  16738. }
  16739. // Hydration mode, looking up existing elements in DOM.
  16740. const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
  16741. ngDevMode && validateNodeExists(currentRNode, lView, tNode);
  16742. const ngContainerSize = getNgContainerSize(hydrationInfo, index);
  16743. ngDevMode &&
  16744. assertNumber(ngContainerSize, 'Unexpected state: hydrating an <ng-container>, ' +
  16745. 'but no hydration info is available.');
  16746. setSegmentHead(hydrationInfo, index, currentRNode);
  16747. comment = siblingAfter(ngContainerSize, currentRNode);
  16748. if (ngDevMode) {
  16749. validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
  16750. markRNodeAsClaimedByHydration(comment);
  16751. }
  16752. return comment;
  16753. }
  16754. function enableLocateOrCreateElementContainerNodeImpl() {
  16755. _locateOrCreateElementContainerNode = locateOrCreateElementContainerNode;
  16756. }
  16757. /**
  16758. * Returns the current OpaqueViewState instance.
  16759. *
  16760. * Used in conjunction with the restoreView() instruction to save a snapshot
  16761. * of the current view and restore it when listeners are invoked. This allows
  16762. * walking the declaration view tree in listeners to get vars from parent views.
  16763. *
  16764. * @codeGenApi
  16765. */
  16766. function ɵɵgetCurrentView() {
  16767. return getLView();
  16768. }
  16769. /**
  16770. * Determine if the argument is shaped like a Promise
  16771. */
  16772. function isPromise(obj) {
  16773. // allow any Promise/A+ compliant thenable.
  16774. // It's up to the caller to ensure that obj.then conforms to the spec
  16775. return !!obj && typeof obj.then === 'function';
  16776. }
  16777. /**
  16778. * Determine if the argument is a Subscribable
  16779. */
  16780. function isSubscribable(obj) {
  16781. return !!obj && typeof obj.subscribe === 'function';
  16782. }
  16783. /**
  16784. * Adds an event listener to the current node.
  16785. *
  16786. * If an output exists on one of the node's directives, it also subscribes to the output
  16787. * and saves the subscription for later cleanup.
  16788. *
  16789. * @param eventName Name of the event
  16790. * @param listenerFn The function to be called when event emits
  16791. * @param useCapture Whether or not to use capture in event listener - this argument is a reminder
  16792. * from the Renderer3 infrastructure and should be removed from the instruction arguments
  16793. * @param eventTargetResolver Function that returns global target information in case this listener
  16794. * should be attached to a global object like window, document or body
  16795. *
  16796. * @codeGenApi
  16797. */
  16798. function ɵɵlistener(eventName, listenerFn, useCapture, eventTargetResolver) {
  16799. const lView = getLView();
  16800. const tView = getTView();
  16801. const tNode = getCurrentTNode();
  16802. listenerInternal(tView, lView, lView[RENDERER], tNode, eventName, listenerFn, eventTargetResolver);
  16803. return ɵɵlistener;
  16804. }
  16805. /**
  16806. * Registers a synthetic host listener (e.g. `(@foo.start)`) on a component or directive.
  16807. *
  16808. * This instruction is for compatibility purposes and is designed to ensure that a
  16809. * synthetic host listener (e.g. `@HostListener('@foo.start')`) properly gets rendered
  16810. * in the component's renderer. Normally all host listeners are evaluated with the
  16811. * parent component's renderer, but, in the case of animation @triggers, they need
  16812. * to be evaluated with the sub component's renderer (because that's where the
  16813. * animation triggers are defined).
  16814. *
  16815. * Do not use this instruction as a replacement for `listener`. This instruction
  16816. * only exists to ensure compatibility with the ViewEngine's host binding behavior.
  16817. *
  16818. * @param eventName Name of the event
  16819. * @param listenerFn The function to be called when event emits
  16820. * @param useCapture Whether or not to use capture in event listener
  16821. * @param eventTargetResolver Function that returns global target information in case this listener
  16822. * should be attached to a global object like window, document or body
  16823. *
  16824. * @codeGenApi
  16825. */
  16826. function ɵɵsyntheticHostListener(eventName, listenerFn) {
  16827. const tNode = getCurrentTNode();
  16828. const lView = getLView();
  16829. const tView = getTView();
  16830. const currentDef = getCurrentDirectiveDef(tView.data);
  16831. const renderer = loadComponentRenderer(currentDef, tNode, lView);
  16832. listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn);
  16833. return ɵɵsyntheticHostListener;
  16834. }
  16835. /**
  16836. * A utility function that checks if a given element has already an event handler registered for an
  16837. * event with a specified name. The TView.cleanup data structure is used to find out which events
  16838. * are registered for a given element.
  16839. */
  16840. function findExistingListener(tView, lView, eventName, tNodeIdx) {
  16841. const tCleanup = tView.cleanup;
  16842. if (tCleanup != null) {
  16843. for (let i = 0; i < tCleanup.length - 1; i += 2) {
  16844. const cleanupEventName = tCleanup[i];
  16845. if (cleanupEventName === eventName && tCleanup[i + 1] === tNodeIdx) {
  16846. // We have found a matching event name on the same node but it might not have been
  16847. // registered yet, so we must explicitly verify entries in the LView cleanup data
  16848. // structures.
  16849. const lCleanup = lView[CLEANUP];
  16850. const listenerIdxInLCleanup = tCleanup[i + 2];
  16851. return lCleanup.length > listenerIdxInLCleanup ? lCleanup[listenerIdxInLCleanup] : null;
  16852. }
  16853. // TView.cleanup can have a mix of 4-elements entries (for event handler cleanups) or
  16854. // 2-element entries (for directive and queries destroy hooks). As such we can encounter
  16855. // blocks of 4 or 2 items in the tView.cleanup and this is why we iterate over 2 elements
  16856. // first and jump another 2 elements if we detect listeners cleanup (4 elements). Also check
  16857. // documentation of TView.cleanup for more details of this data structure layout.
  16858. if (typeof cleanupEventName === 'string') {
  16859. i += 2;
  16860. }
  16861. }
  16862. }
  16863. return null;
  16864. }
  16865. function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn, eventTargetResolver) {
  16866. const isTNodeDirectiveHost = isDirectiveHost(tNode);
  16867. const firstCreatePass = tView.firstCreatePass;
  16868. const tCleanup = firstCreatePass && getOrCreateTViewCleanup(tView);
  16869. const context = lView[CONTEXT];
  16870. // When the ɵɵlistener instruction was generated and is executed we know that there is either a
  16871. // native listener or a directive output on this element. As such we we know that we will have to
  16872. // register a listener and store its cleanup function on LView.
  16873. const lCleanup = getOrCreateLViewCleanup(lView);
  16874. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
  16875. let processOutputs = true;
  16876. // Adding a native event listener is applicable when:
  16877. // - The corresponding TNode represents a DOM element.
  16878. // - The event target has a resolver (usually resulting in a global object,
  16879. // such as `window` or `document`).
  16880. if ((tNode.type & 3 /* TNodeType.AnyRNode */) || eventTargetResolver) {
  16881. const native = getNativeByTNode(tNode, lView);
  16882. const target = eventTargetResolver ? eventTargetResolver(native) : native;
  16883. const lCleanupIndex = lCleanup.length;
  16884. const idxOrTargetGetter = eventTargetResolver ?
  16885. (_lView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])) :
  16886. tNode.index;
  16887. // In order to match current behavior, native DOM event listeners must be added for all
  16888. // events (including outputs).
  16889. // There might be cases where multiple directives on the same element try to register an event
  16890. // handler function for the same event. In this situation we want to avoid registration of
  16891. // several native listeners as each registration would be intercepted by NgZone and
  16892. // trigger change detection. This would mean that a single user action would result in several
  16893. // change detections being invoked. To avoid this situation we want to have only one call to
  16894. // native handler registration (for the same element and same type of event).
  16895. //
  16896. // In order to have just one native event handler in presence of multiple handler functions,
  16897. // we just register a first handler function as a native event listener and then chain
  16898. // (coalesce) other handler functions on top of the first native handler function.
  16899. let existingListener = null;
  16900. // Please note that the coalescing described here doesn't happen for events specifying an
  16901. // alternative target (ex. (document:click)) - this is to keep backward compatibility with the
  16902. // view engine.
  16903. // Also, we don't have to search for existing listeners is there are no directives
  16904. // matching on a given node as we can't register multiple event handlers for the same event in
  16905. // a template (this would mean having duplicate attributes).
  16906. if (!eventTargetResolver && isTNodeDirectiveHost) {
  16907. existingListener = findExistingListener(tView, lView, eventName, tNode.index);
  16908. }
  16909. if (existingListener !== null) {
  16910. // Attach a new listener to coalesced listeners list, maintaining the order in which
  16911. // listeners are registered. For performance reasons, we keep a reference to the last
  16912. // listener in that list (in `__ngLastListenerFn__` field), so we can avoid going through
  16913. // the entire set each time we need to add a new listener.
  16914. const lastListenerFn = existingListener.__ngLastListenerFn__ || existingListener;
  16915. lastListenerFn.__ngNextListenerFn__ = listenerFn;
  16916. existingListener.__ngLastListenerFn__ = listenerFn;
  16917. processOutputs = false;
  16918. }
  16919. else {
  16920. listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */);
  16921. const cleanupFn = renderer.listen(target, eventName, listenerFn);
  16922. ngDevMode && ngDevMode.rendererAddEventListener++;
  16923. lCleanup.push(listenerFn, cleanupFn);
  16924. tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, lCleanupIndex + 1);
  16925. }
  16926. }
  16927. else {
  16928. // Even if there is no native listener to add, we still need to wrap the listener so that OnPush
  16929. // ancestors are marked dirty when an event occurs.
  16930. listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */);
  16931. }
  16932. // subscribe to directive outputs
  16933. const outputs = tNode.outputs;
  16934. let props;
  16935. if (processOutputs && outputs !== null && (props = outputs[eventName])) {
  16936. const propsLength = props.length;
  16937. if (propsLength) {
  16938. for (let i = 0; i < propsLength; i += 2) {
  16939. const index = props[i];
  16940. ngDevMode && assertIndexInRange(lView, index);
  16941. const minifiedName = props[i + 1];
  16942. const directiveInstance = lView[index];
  16943. const output = directiveInstance[minifiedName];
  16944. if (ngDevMode && !isSubscribable(output)) {
  16945. throw new Error(`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`);
  16946. }
  16947. const subscription = output.subscribe(listenerFn);
  16948. const idx = lCleanup.length;
  16949. lCleanup.push(listenerFn, subscription);
  16950. tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
  16951. }
  16952. }
  16953. }
  16954. }
  16955. function executeListenerWithErrorHandling(lView, context, listenerFn, e) {
  16956. try {
  16957. profiler(6 /* ProfilerEvent.OutputStart */, context, listenerFn);
  16958. // Only explicitly returning false from a listener should preventDefault
  16959. return listenerFn(e) !== false;
  16960. }
  16961. catch (error) {
  16962. handleError(lView, error);
  16963. return false;
  16964. }
  16965. finally {
  16966. profiler(7 /* ProfilerEvent.OutputEnd */, context, listenerFn);
  16967. }
  16968. }
  16969. /**
  16970. * Wraps an event listener with a function that marks ancestors dirty and prevents default behavior,
  16971. * if applicable.
  16972. *
  16973. * @param tNode The TNode associated with this listener
  16974. * @param lView The LView that contains this listener
  16975. * @param listenerFn The listener function to call
  16976. * @param wrapWithPreventDefault Whether or not to prevent default behavior
  16977. * (the procedural renderer does this already, so in those cases, we should skip)
  16978. */
  16979. function wrapListener(tNode, lView, context, listenerFn, wrapWithPreventDefault) {
  16980. // Note: we are performing most of the work in the listener function itself
  16981. // to optimize listener registration.
  16982. return function wrapListenerIn_markDirtyAndPreventDefault(e) {
  16983. // Ivy uses `Function` as a special token that allows us to unwrap the function
  16984. // so that it can be invoked programmatically by `DebugNode.triggerEventHandler`.
  16985. if (e === Function) {
  16986. return listenerFn;
  16987. }
  16988. // In order to be backwards compatible with View Engine, events on component host nodes
  16989. // must also mark the component view itself dirty (i.e. the view that it owns).
  16990. const startView = tNode.componentOffset > -1 ? getComponentLViewByIndex(tNode.index, lView) : lView;
  16991. markViewDirty(startView);
  16992. let result = executeListenerWithErrorHandling(lView, context, listenerFn, e);
  16993. // A just-invoked listener function might have coalesced listeners so we need to check for
  16994. // their presence and invoke as needed.
  16995. let nextListenerFn = wrapListenerIn_markDirtyAndPreventDefault.__ngNextListenerFn__;
  16996. while (nextListenerFn) {
  16997. // We should prevent default if any of the listeners explicitly return false
  16998. result = executeListenerWithErrorHandling(lView, context, nextListenerFn, e) && result;
  16999. nextListenerFn = nextListenerFn.__ngNextListenerFn__;
  17000. }
  17001. if (wrapWithPreventDefault && result === false) {
  17002. e.preventDefault();
  17003. }
  17004. return result;
  17005. };
  17006. }
  17007. /**
  17008. * Retrieves a context at the level specified and saves it as the global, contextViewData.
  17009. * Will get the next level up if level is not specified.
  17010. *
  17011. * This is used to save contexts of parent views so they can be bound in embedded views, or
  17012. * in conjunction with reference() to bind a ref from a parent view.
  17013. *
  17014. * @param level The relative level of the view from which to grab context compared to contextVewData
  17015. * @returns context
  17016. *
  17017. * @codeGenApi
  17018. */
  17019. function ɵɵnextContext(level = 1) {
  17020. return nextContextImpl(level);
  17021. }
  17022. /**
  17023. * Checks a given node against matching projection slots and returns the
  17024. * determined slot index. Returns "null" if no slot matched the given node.
  17025. *
  17026. * This function takes into account the parsed ngProjectAs selector from the
  17027. * node's attributes. If present, it will check whether the ngProjectAs selector
  17028. * matches any of the projection slot selectors.
  17029. */
  17030. function matchingProjectionSlotIndex(tNode, projectionSlots) {
  17031. let wildcardNgContentIndex = null;
  17032. const ngProjectAsAttrVal = getProjectAsAttrValue(tNode);
  17033. for (let i = 0; i < projectionSlots.length; i++) {
  17034. const slotValue = projectionSlots[i];
  17035. // The last wildcard projection slot should match all nodes which aren't matching
  17036. // any selector. This is necessary to be backwards compatible with view engine.
  17037. if (slotValue === '*') {
  17038. wildcardNgContentIndex = i;
  17039. continue;
  17040. }
  17041. // If we ran into an `ngProjectAs` attribute, we should match its parsed selector
  17042. // to the list of selectors, otherwise we fall back to matching against the node.
  17043. if (ngProjectAsAttrVal === null ?
  17044. isNodeMatchingSelectorList(tNode, slotValue, /* isProjectionMode */ true) :
  17045. isSelectorInSelectorList(ngProjectAsAttrVal, slotValue)) {
  17046. return i; // first matching selector "captures" a given node
  17047. }
  17048. }
  17049. return wildcardNgContentIndex;
  17050. }
  17051. /**
  17052. * Instruction to distribute projectable nodes among <ng-content> occurrences in a given template.
  17053. * It takes all the selectors from the entire component's template and decides where
  17054. * each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is
  17055. * backed by a selector).
  17056. *
  17057. * This function requires CSS selectors to be provided in 2 forms: parsed (by a compiler) and text,
  17058. * un-parsed form.
  17059. *
  17060. * The parsed form is needed for efficient matching of a node against a given CSS selector.
  17061. * The un-parsed, textual form is needed for support of the ngProjectAs attribute.
  17062. *
  17063. * Having a CSS selector in 2 different formats is not ideal, but alternatives have even more
  17064. * drawbacks:
  17065. * - having only a textual form would require runtime parsing of CSS selectors;
  17066. * - we can't have only a parsed as we can't re-construct textual form from it (as entered by a
  17067. * template author).
  17068. *
  17069. * @param projectionSlots? A collection of projection slots. A projection slot can be based
  17070. * on a parsed CSS selectors or set to the wildcard selector ("*") in order to match
  17071. * all nodes which do not match any selector. If not specified, a single wildcard
  17072. * selector projection slot will be defined.
  17073. *
  17074. * @codeGenApi
  17075. */
  17076. function ɵɵprojectionDef(projectionSlots) {
  17077. const componentNode = getLView()[DECLARATION_COMPONENT_VIEW][T_HOST];
  17078. if (!componentNode.projection) {
  17079. // If no explicit projection slots are defined, fall back to a single
  17080. // projection slot with the wildcard selector.
  17081. const numProjectionSlots = projectionSlots ? projectionSlots.length : 1;
  17082. const projectionHeads = componentNode.projection =
  17083. newArray(numProjectionSlots, null);
  17084. const tails = projectionHeads.slice();
  17085. let componentChild = componentNode.child;
  17086. while (componentChild !== null) {
  17087. const slotIndex = projectionSlots ? matchingProjectionSlotIndex(componentChild, projectionSlots) : 0;
  17088. if (slotIndex !== null) {
  17089. if (tails[slotIndex]) {
  17090. tails[slotIndex].projectionNext = componentChild;
  17091. }
  17092. else {
  17093. projectionHeads[slotIndex] = componentChild;
  17094. }
  17095. tails[slotIndex] = componentChild;
  17096. }
  17097. componentChild = componentChild.next;
  17098. }
  17099. }
  17100. }
  17101. /**
  17102. * Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
  17103. * to the projectionDef instruction.
  17104. *
  17105. * @param nodeIndex
  17106. * @param selectorIndex:
  17107. * - 0 when the selector is `*` (or unspecified as this is the default value),
  17108. * - 1 based index of the selector from the {@link projectionDef}
  17109. *
  17110. * @codeGenApi
  17111. */
  17112. function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs) {
  17113. const lView = getLView();
  17114. const tView = getTView();
  17115. const tProjectionNode = getOrCreateTNode(tView, HEADER_OFFSET + nodeIndex, 16 /* TNodeType.Projection */, null, attrs || null);
  17116. // We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
  17117. if (tProjectionNode.projection === null)
  17118. tProjectionNode.projection = selectorIndex;
  17119. // `<ng-content>` has no content
  17120. setCurrentTNodeAsNotParent();
  17121. const hydrationInfo = lView[HYDRATION];
  17122. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
  17123. if (isNodeCreationMode &&
  17124. (tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
  17125. // re-distribution of projectable nodes is stored on a component's view level
  17126. applyProjection(tView, lView, tProjectionNode);
  17127. }
  17128. }
  17129. /**
  17130. *
  17131. * Update an interpolated property on an element with a lone bound value
  17132. *
  17133. * Used when the value passed to a property has 1 interpolated value in it, an no additional text
  17134. * surrounds that interpolated value:
  17135. *
  17136. * ```html
  17137. * <div title="{{v0}}"></div>
  17138. * ```
  17139. *
  17140. * Its compiled representation is::
  17141. *
  17142. * ```ts
  17143. * ɵɵpropertyInterpolate('title', v0);
  17144. * ```
  17145. *
  17146. * If the property name also exists as an input property on one of the element's directives,
  17147. * the component property will be set instead of the element property. This check must
  17148. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17149. *
  17150. * @param propName The name of the property to update
  17151. * @param prefix Static value used for concatenation only.
  17152. * @param v0 Value checked for change.
  17153. * @param suffix Static value used for concatenation only.
  17154. * @param sanitizer An optional sanitizer function
  17155. * @returns itself, so that it may be chained.
  17156. * @codeGenApi
  17157. */
  17158. function ɵɵpropertyInterpolate(propName, v0, sanitizer) {
  17159. ɵɵpropertyInterpolate1(propName, '', v0, '', sanitizer);
  17160. return ɵɵpropertyInterpolate;
  17161. }
  17162. /**
  17163. *
  17164. * Update an interpolated property on an element with single bound value surrounded by text.
  17165. *
  17166. * Used when the value passed to a property has 1 interpolated value in it:
  17167. *
  17168. * ```html
  17169. * <div title="prefix{{v0}}suffix"></div>
  17170. * ```
  17171. *
  17172. * Its compiled representation is::
  17173. *
  17174. * ```ts
  17175. * ɵɵpropertyInterpolate1('title', 'prefix', v0, 'suffix');
  17176. * ```
  17177. *
  17178. * If the property name also exists as an input property on one of the element's directives,
  17179. * the component property will be set instead of the element property. This check must
  17180. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17181. *
  17182. * @param propName The name of the property to update
  17183. * @param prefix Static value used for concatenation only.
  17184. * @param v0 Value checked for change.
  17185. * @param suffix Static value used for concatenation only.
  17186. * @param sanitizer An optional sanitizer function
  17187. * @returns itself, so that it may be chained.
  17188. * @codeGenApi
  17189. */
  17190. function ɵɵpropertyInterpolate1(propName, prefix, v0, suffix, sanitizer) {
  17191. const lView = getLView();
  17192. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  17193. if (interpolatedValue !== NO_CHANGE) {
  17194. const tView = getTView();
  17195. const tNode = getSelectedTNode();
  17196. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17197. ngDevMode &&
  17198. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 1, prefix, suffix);
  17199. }
  17200. return ɵɵpropertyInterpolate1;
  17201. }
  17202. /**
  17203. *
  17204. * Update an interpolated property on an element with 2 bound values surrounded by text.
  17205. *
  17206. * Used when the value passed to a property has 2 interpolated values in it:
  17207. *
  17208. * ```html
  17209. * <div title="prefix{{v0}}-{{v1}}suffix"></div>
  17210. * ```
  17211. *
  17212. * Its compiled representation is::
  17213. *
  17214. * ```ts
  17215. * ɵɵpropertyInterpolate2('title', 'prefix', v0, '-', v1, 'suffix');
  17216. * ```
  17217. *
  17218. * If the property name also exists as an input property on one of the element's directives,
  17219. * the component property will be set instead of the element property. This check must
  17220. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17221. *
  17222. * @param propName The name of the property to update
  17223. * @param prefix Static value used for concatenation only.
  17224. * @param v0 Value checked for change.
  17225. * @param i0 Static value used for concatenation only.
  17226. * @param v1 Value checked for change.
  17227. * @param suffix Static value used for concatenation only.
  17228. * @param sanitizer An optional sanitizer function
  17229. * @returns itself, so that it may be chained.
  17230. * @codeGenApi
  17231. */
  17232. function ɵɵpropertyInterpolate2(propName, prefix, v0, i0, v1, suffix, sanitizer) {
  17233. const lView = getLView();
  17234. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  17235. if (interpolatedValue !== NO_CHANGE) {
  17236. const tView = getTView();
  17237. const tNode = getSelectedTNode();
  17238. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17239. ngDevMode &&
  17240. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 2, prefix, i0, suffix);
  17241. }
  17242. return ɵɵpropertyInterpolate2;
  17243. }
  17244. /**
  17245. *
  17246. * Update an interpolated property on an element with 3 bound values surrounded by text.
  17247. *
  17248. * Used when the value passed to a property has 3 interpolated values in it:
  17249. *
  17250. * ```html
  17251. * <div title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  17252. * ```
  17253. *
  17254. * Its compiled representation is::
  17255. *
  17256. * ```ts
  17257. * ɵɵpropertyInterpolate3(
  17258. * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix');
  17259. * ```
  17260. *
  17261. * If the property name also exists as an input property on one of the element's directives,
  17262. * the component property will be set instead of the element property. This check must
  17263. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17264. *
  17265. * @param propName The name of the property to update
  17266. * @param prefix Static value used for concatenation only.
  17267. * @param v0 Value checked for change.
  17268. * @param i0 Static value used for concatenation only.
  17269. * @param v1 Value checked for change.
  17270. * @param i1 Static value used for concatenation only.
  17271. * @param v2 Value checked for change.
  17272. * @param suffix Static value used for concatenation only.
  17273. * @param sanitizer An optional sanitizer function
  17274. * @returns itself, so that it may be chained.
  17275. * @codeGenApi
  17276. */
  17277. function ɵɵpropertyInterpolate3(propName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer) {
  17278. const lView = getLView();
  17279. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  17280. if (interpolatedValue !== NO_CHANGE) {
  17281. const tView = getTView();
  17282. const tNode = getSelectedTNode();
  17283. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17284. ngDevMode &&
  17285. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 3, prefix, i0, i1, suffix);
  17286. }
  17287. return ɵɵpropertyInterpolate3;
  17288. }
  17289. /**
  17290. *
  17291. * Update an interpolated property on an element with 4 bound values surrounded by text.
  17292. *
  17293. * Used when the value passed to a property has 4 interpolated values in it:
  17294. *
  17295. * ```html
  17296. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  17297. * ```
  17298. *
  17299. * Its compiled representation is::
  17300. *
  17301. * ```ts
  17302. * ɵɵpropertyInterpolate4(
  17303. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  17304. * ```
  17305. *
  17306. * If the property name also exists as an input property on one of the element's directives,
  17307. * the component property will be set instead of the element property. This check must
  17308. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17309. *
  17310. * @param propName The name of the property to update
  17311. * @param prefix Static value used for concatenation only.
  17312. * @param v0 Value checked for change.
  17313. * @param i0 Static value used for concatenation only.
  17314. * @param v1 Value checked for change.
  17315. * @param i1 Static value used for concatenation only.
  17316. * @param v2 Value checked for change.
  17317. * @param i2 Static value used for concatenation only.
  17318. * @param v3 Value checked for change.
  17319. * @param suffix Static value used for concatenation only.
  17320. * @param sanitizer An optional sanitizer function
  17321. * @returns itself, so that it may be chained.
  17322. * @codeGenApi
  17323. */
  17324. function ɵɵpropertyInterpolate4(propName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer) {
  17325. const lView = getLView();
  17326. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  17327. if (interpolatedValue !== NO_CHANGE) {
  17328. const tView = getTView();
  17329. const tNode = getSelectedTNode();
  17330. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17331. ngDevMode &&
  17332. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix);
  17333. }
  17334. return ɵɵpropertyInterpolate4;
  17335. }
  17336. /**
  17337. *
  17338. * Update an interpolated property on an element with 5 bound values surrounded by text.
  17339. *
  17340. * Used when the value passed to a property has 5 interpolated values in it:
  17341. *
  17342. * ```html
  17343. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  17344. * ```
  17345. *
  17346. * Its compiled representation is::
  17347. *
  17348. * ```ts
  17349. * ɵɵpropertyInterpolate5(
  17350. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  17351. * ```
  17352. *
  17353. * If the property name also exists as an input property on one of the element's directives,
  17354. * the component property will be set instead of the element property. This check must
  17355. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17356. *
  17357. * @param propName The name of the property to update
  17358. * @param prefix Static value used for concatenation only.
  17359. * @param v0 Value checked for change.
  17360. * @param i0 Static value used for concatenation only.
  17361. * @param v1 Value checked for change.
  17362. * @param i1 Static value used for concatenation only.
  17363. * @param v2 Value checked for change.
  17364. * @param i2 Static value used for concatenation only.
  17365. * @param v3 Value checked for change.
  17366. * @param i3 Static value used for concatenation only.
  17367. * @param v4 Value checked for change.
  17368. * @param suffix Static value used for concatenation only.
  17369. * @param sanitizer An optional sanitizer function
  17370. * @returns itself, so that it may be chained.
  17371. * @codeGenApi
  17372. */
  17373. function ɵɵpropertyInterpolate5(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer) {
  17374. const lView = getLView();
  17375. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  17376. if (interpolatedValue !== NO_CHANGE) {
  17377. const tView = getTView();
  17378. const tNode = getSelectedTNode();
  17379. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17380. ngDevMode &&
  17381. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix);
  17382. }
  17383. return ɵɵpropertyInterpolate5;
  17384. }
  17385. /**
  17386. *
  17387. * Update an interpolated property on an element with 6 bound values surrounded by text.
  17388. *
  17389. * Used when the value passed to a property has 6 interpolated values in it:
  17390. *
  17391. * ```html
  17392. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  17393. * ```
  17394. *
  17395. * Its compiled representation is::
  17396. *
  17397. * ```ts
  17398. * ɵɵpropertyInterpolate6(
  17399. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  17400. * ```
  17401. *
  17402. * If the property name also exists as an input property on one of the element's directives,
  17403. * the component property will be set instead of the element property. This check must
  17404. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17405. *
  17406. * @param propName The name of the property to update
  17407. * @param prefix Static value used for concatenation only.
  17408. * @param v0 Value checked for change.
  17409. * @param i0 Static value used for concatenation only.
  17410. * @param v1 Value checked for change.
  17411. * @param i1 Static value used for concatenation only.
  17412. * @param v2 Value checked for change.
  17413. * @param i2 Static value used for concatenation only.
  17414. * @param v3 Value checked for change.
  17415. * @param i3 Static value used for concatenation only.
  17416. * @param v4 Value checked for change.
  17417. * @param i4 Static value used for concatenation only.
  17418. * @param v5 Value checked for change.
  17419. * @param suffix Static value used for concatenation only.
  17420. * @param sanitizer An optional sanitizer function
  17421. * @returns itself, so that it may be chained.
  17422. * @codeGenApi
  17423. */
  17424. function ɵɵpropertyInterpolate6(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer) {
  17425. const lView = getLView();
  17426. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  17427. if (interpolatedValue !== NO_CHANGE) {
  17428. const tView = getTView();
  17429. const tNode = getSelectedTNode();
  17430. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17431. ngDevMode &&
  17432. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix);
  17433. }
  17434. return ɵɵpropertyInterpolate6;
  17435. }
  17436. /**
  17437. *
  17438. * Update an interpolated property on an element with 7 bound values surrounded by text.
  17439. *
  17440. * Used when the value passed to a property has 7 interpolated values in it:
  17441. *
  17442. * ```html
  17443. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  17444. * ```
  17445. *
  17446. * Its compiled representation is::
  17447. *
  17448. * ```ts
  17449. * ɵɵpropertyInterpolate7(
  17450. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  17451. * ```
  17452. *
  17453. * If the property name also exists as an input property on one of the element's directives,
  17454. * the component property will be set instead of the element property. This check must
  17455. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17456. *
  17457. * @param propName The name of the property to update
  17458. * @param prefix Static value used for concatenation only.
  17459. * @param v0 Value checked for change.
  17460. * @param i0 Static value used for concatenation only.
  17461. * @param v1 Value checked for change.
  17462. * @param i1 Static value used for concatenation only.
  17463. * @param v2 Value checked for change.
  17464. * @param i2 Static value used for concatenation only.
  17465. * @param v3 Value checked for change.
  17466. * @param i3 Static value used for concatenation only.
  17467. * @param v4 Value checked for change.
  17468. * @param i4 Static value used for concatenation only.
  17469. * @param v5 Value checked for change.
  17470. * @param i5 Static value used for concatenation only.
  17471. * @param v6 Value checked for change.
  17472. * @param suffix Static value used for concatenation only.
  17473. * @param sanitizer An optional sanitizer function
  17474. * @returns itself, so that it may be chained.
  17475. * @codeGenApi
  17476. */
  17477. function ɵɵpropertyInterpolate7(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer) {
  17478. const lView = getLView();
  17479. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  17480. if (interpolatedValue !== NO_CHANGE) {
  17481. const tView = getTView();
  17482. const tNode = getSelectedTNode();
  17483. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17484. ngDevMode &&
  17485. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix);
  17486. }
  17487. return ɵɵpropertyInterpolate7;
  17488. }
  17489. /**
  17490. *
  17491. * Update an interpolated property on an element with 8 bound values surrounded by text.
  17492. *
  17493. * Used when the value passed to a property has 8 interpolated values in it:
  17494. *
  17495. * ```html
  17496. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  17497. * ```
  17498. *
  17499. * Its compiled representation is::
  17500. *
  17501. * ```ts
  17502. * ɵɵpropertyInterpolate8(
  17503. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  17504. * ```
  17505. *
  17506. * If the property name also exists as an input property on one of the element's directives,
  17507. * the component property will be set instead of the element property. This check must
  17508. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17509. *
  17510. * @param propName The name of the property to update
  17511. * @param prefix Static value used for concatenation only.
  17512. * @param v0 Value checked for change.
  17513. * @param i0 Static value used for concatenation only.
  17514. * @param v1 Value checked for change.
  17515. * @param i1 Static value used for concatenation only.
  17516. * @param v2 Value checked for change.
  17517. * @param i2 Static value used for concatenation only.
  17518. * @param v3 Value checked for change.
  17519. * @param i3 Static value used for concatenation only.
  17520. * @param v4 Value checked for change.
  17521. * @param i4 Static value used for concatenation only.
  17522. * @param v5 Value checked for change.
  17523. * @param i5 Static value used for concatenation only.
  17524. * @param v6 Value checked for change.
  17525. * @param i6 Static value used for concatenation only.
  17526. * @param v7 Value checked for change.
  17527. * @param suffix Static value used for concatenation only.
  17528. * @param sanitizer An optional sanitizer function
  17529. * @returns itself, so that it may be chained.
  17530. * @codeGenApi
  17531. */
  17532. function ɵɵpropertyInterpolate8(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer) {
  17533. const lView = getLView();
  17534. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  17535. if (interpolatedValue !== NO_CHANGE) {
  17536. const tView = getTView();
  17537. const tNode = getSelectedTNode();
  17538. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17539. ngDevMode &&
  17540. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix);
  17541. }
  17542. return ɵɵpropertyInterpolate8;
  17543. }
  17544. /**
  17545. * Update an interpolated property on an element with 9 or more bound values surrounded by text.
  17546. *
  17547. * Used when the number of interpolated values exceeds 8.
  17548. *
  17549. * ```html
  17550. * <div
  17551. * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
  17552. * ```
  17553. *
  17554. * Its compiled representation is::
  17555. *
  17556. * ```ts
  17557. * ɵɵpropertyInterpolateV(
  17558. * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  17559. * 'suffix']);
  17560. * ```
  17561. *
  17562. * If the property name also exists as an input property on one of the element's directives,
  17563. * the component property will be set instead of the element property. This check must
  17564. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17565. *
  17566. * @param propName The name of the property to update.
  17567. * @param values The collection of values and the strings in between those values, beginning with a
  17568. * string prefix and ending with a string suffix.
  17569. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  17570. * @param sanitizer An optional sanitizer function
  17571. * @returns itself, so that it may be chained.
  17572. * @codeGenApi
  17573. */
  17574. function ɵɵpropertyInterpolateV(propName, values, sanitizer) {
  17575. const lView = getLView();
  17576. const interpolatedValue = interpolationV(lView, values);
  17577. if (interpolatedValue !== NO_CHANGE) {
  17578. const tView = getTView();
  17579. const tNode = getSelectedTNode();
  17580. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17581. if (ngDevMode) {
  17582. const interpolationInBetween = [values[0]]; // prefix
  17583. for (let i = 2; i < values.length; i += 2) {
  17584. interpolationInBetween.push(values[i]);
  17585. }
  17586. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
  17587. }
  17588. }
  17589. return ɵɵpropertyInterpolateV;
  17590. }
  17591. function toTStylingRange(prev, next) {
  17592. ngDevMode && assertNumberInRange(prev, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17593. ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17594. return (prev << 17 /* StylingRange.PREV_SHIFT */ | next << 2 /* StylingRange.NEXT_SHIFT */);
  17595. }
  17596. function getTStylingRangePrev(tStylingRange) {
  17597. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17598. return (tStylingRange >> 17 /* StylingRange.PREV_SHIFT */) & 32767 /* StylingRange.UNSIGNED_MASK */;
  17599. }
  17600. function getTStylingRangePrevDuplicate(tStylingRange) {
  17601. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17602. return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) == 2 /* StylingRange.PREV_DUPLICATE */;
  17603. }
  17604. function setTStylingRangePrev(tStylingRange, previous) {
  17605. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17606. ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17607. return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) | (previous << 17 /* StylingRange.PREV_SHIFT */));
  17608. }
  17609. function setTStylingRangePrevDuplicate(tStylingRange) {
  17610. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17611. return (tStylingRange | 2 /* StylingRange.PREV_DUPLICATE */);
  17612. }
  17613. function getTStylingRangeNext(tStylingRange) {
  17614. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17615. return (tStylingRange & 131068 /* StylingRange.NEXT_MASK */) >> 2 /* StylingRange.NEXT_SHIFT */;
  17616. }
  17617. function setTStylingRangeNext(tStylingRange, next) {
  17618. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17619. ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17620. return ((tStylingRange & ~131068 /* StylingRange.NEXT_MASK */) | //
  17621. next << 2 /* StylingRange.NEXT_SHIFT */);
  17622. }
  17623. function getTStylingRangeNextDuplicate(tStylingRange) {
  17624. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17625. return ((tStylingRange) & 1 /* StylingRange.NEXT_DUPLICATE */) === 1 /* StylingRange.NEXT_DUPLICATE */;
  17626. }
  17627. function setTStylingRangeNextDuplicate(tStylingRange) {
  17628. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17629. return (tStylingRange | 1 /* StylingRange.NEXT_DUPLICATE */);
  17630. }
  17631. function getTStylingRangeTail(tStylingRange) {
  17632. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17633. const next = getTStylingRangeNext(tStylingRange);
  17634. return next === 0 ? getTStylingRangePrev(tStylingRange) : next;
  17635. }
  17636. /**
  17637. * NOTE: The word `styling` is used interchangeably as style or class styling.
  17638. *
  17639. * This file contains code to link styling instructions together so that they can be replayed in
  17640. * priority order. The file exists because Ivy styling instruction execution order does not match
  17641. * that of the priority order. The purpose of this code is to create a linked list so that the
  17642. * instructions can be traversed in priority order when computing the styles.
  17643. *
  17644. * Assume we are dealing with the following code:
  17645. * ```
  17646. * @Component({
  17647. * template: `
  17648. * <my-cmp [style]=" {color: '#001'} "
  17649. * [style.color]=" #002 "
  17650. * dir-style-color-1
  17651. * dir-style-color-2> `
  17652. * })
  17653. * class ExampleComponent {
  17654. * static ngComp = ... {
  17655. * ...
  17656. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17657. * ɵɵstyleMap({color: '#001'});
  17658. * ɵɵstyleProp('color', '#002');
  17659. * ...
  17660. * }
  17661. * }
  17662. *
  17663. * @Directive({
  17664. * selector: `[dir-style-color-1]',
  17665. * })
  17666. * class Style1Directive {
  17667. * @HostBinding('style') style = {color: '#005'};
  17668. * @HostBinding('style.color') color = '#006';
  17669. *
  17670. * static ngDir = ... {
  17671. * ...
  17672. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17673. * ɵɵstyleMap({color: '#005'});
  17674. * ɵɵstyleProp('color', '#006');
  17675. * ...
  17676. * }
  17677. * }
  17678. *
  17679. * @Directive({
  17680. * selector: `[dir-style-color-2]',
  17681. * })
  17682. * class Style2Directive {
  17683. * @HostBinding('style') style = {color: '#007'};
  17684. * @HostBinding('style.color') color = '#008';
  17685. *
  17686. * static ngDir = ... {
  17687. * ...
  17688. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17689. * ɵɵstyleMap({color: '#007'});
  17690. * ɵɵstyleProp('color', '#008');
  17691. * ...
  17692. * }
  17693. * }
  17694. *
  17695. * @Directive({
  17696. * selector: `my-cmp',
  17697. * })
  17698. * class MyComponent {
  17699. * @HostBinding('style') style = {color: '#003'};
  17700. * @HostBinding('style.color') color = '#004';
  17701. *
  17702. * static ngComp = ... {
  17703. * ...
  17704. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17705. * ɵɵstyleMap({color: '#003'});
  17706. * ɵɵstyleProp('color', '#004');
  17707. * ...
  17708. * }
  17709. * }
  17710. * ```
  17711. *
  17712. * The Order of instruction execution is:
  17713. *
  17714. * NOTE: the comment binding location is for illustrative purposes only.
  17715. *
  17716. * ```
  17717. * // Template: (ExampleComponent)
  17718. * ɵɵstyleMap({color: '#001'}); // Binding index: 10
  17719. * ɵɵstyleProp('color', '#002'); // Binding index: 12
  17720. * // MyComponent
  17721. * ɵɵstyleMap({color: '#003'}); // Binding index: 20
  17722. * ɵɵstyleProp('color', '#004'); // Binding index: 22
  17723. * // Style1Directive
  17724. * ɵɵstyleMap({color: '#005'}); // Binding index: 24
  17725. * ɵɵstyleProp('color', '#006'); // Binding index: 26
  17726. * // Style2Directive
  17727. * ɵɵstyleMap({color: '#007'}); // Binding index: 28
  17728. * ɵɵstyleProp('color', '#008'); // Binding index: 30
  17729. * ```
  17730. *
  17731. * The correct priority order of concatenation is:
  17732. *
  17733. * ```
  17734. * // MyComponent
  17735. * ɵɵstyleMap({color: '#003'}); // Binding index: 20
  17736. * ɵɵstyleProp('color', '#004'); // Binding index: 22
  17737. * // Style1Directive
  17738. * ɵɵstyleMap({color: '#005'}); // Binding index: 24
  17739. * ɵɵstyleProp('color', '#006'); // Binding index: 26
  17740. * // Style2Directive
  17741. * ɵɵstyleMap({color: '#007'}); // Binding index: 28
  17742. * ɵɵstyleProp('color', '#008'); // Binding index: 30
  17743. * // Template: (ExampleComponent)
  17744. * ɵɵstyleMap({color: '#001'}); // Binding index: 10
  17745. * ɵɵstyleProp('color', '#002'); // Binding index: 12
  17746. * ```
  17747. *
  17748. * What color should be rendered?
  17749. *
  17750. * Once the items are correctly sorted in the list, the answer is simply the last item in the
  17751. * concatenation list which is `#002`.
  17752. *
  17753. * To do so we keep a linked list of all of the bindings which pertain to this element.
  17754. * Notice that the bindings are inserted in the order of execution, but the `TView.data` allows
  17755. * us to traverse them in the order of priority.
  17756. *
  17757. * |Idx|`TView.data`|`LView` | Notes
  17758. * |---|------------|-----------------|--------------
  17759. * |...| | |
  17760. * |10 |`null` |`{color: '#001'}`| `ɵɵstyleMap('color', {color: '#001'})`
  17761. * |11 |`30 | 12` | ... |
  17762. * |12 |`color` |`'#002'` | `ɵɵstyleProp('color', '#002')`
  17763. * |13 |`10 | 0` | ... |
  17764. * |...| | |
  17765. * |20 |`null` |`{color: '#003'}`| `ɵɵstyleMap('color', {color: '#003'})`
  17766. * |21 |`0 | 22` | ... |
  17767. * |22 |`color` |`'#004'` | `ɵɵstyleProp('color', '#004')`
  17768. * |23 |`20 | 24` | ... |
  17769. * |24 |`null` |`{color: '#005'}`| `ɵɵstyleMap('color', {color: '#005'})`
  17770. * |25 |`22 | 26` | ... |
  17771. * |26 |`color` |`'#006'` | `ɵɵstyleProp('color', '#006')`
  17772. * |27 |`24 | 28` | ... |
  17773. * |28 |`null` |`{color: '#007'}`| `ɵɵstyleMap('color', {color: '#007'})`
  17774. * |29 |`26 | 30` | ... |
  17775. * |30 |`color` |`'#008'` | `ɵɵstyleProp('color', '#008')`
  17776. * |31 |`28 | 10` | ... |
  17777. *
  17778. * The above data structure allows us to re-concatenate the styling no matter which data binding
  17779. * changes.
  17780. *
  17781. * NOTE: in addition to keeping track of next/previous index the `TView.data` also stores prev/next
  17782. * duplicate bit. The duplicate bit if true says there either is a binding with the same name or
  17783. * there is a map (which may contain the name). This information is useful in knowing if other
  17784. * styles with higher priority need to be searched for overwrites.
  17785. *
  17786. * NOTE: See `should support example in 'tnode_linked_list.ts' documentation` in
  17787. * `tnode_linked_list_spec.ts` for working example.
  17788. */
  17789. let __unused_const_as_closure_does_not_like_standalone_comment_blocks__;
  17790. /**
  17791. * Insert new `tStyleValue` at `TData` and link existing style bindings such that we maintain linked
  17792. * list of styles and compute the duplicate flag.
  17793. *
  17794. * Note: this function is executed during `firstUpdatePass` only to populate the `TView.data`.
  17795. *
  17796. * The function works by keeping track of `tStylingRange` which contains two pointers pointing to
  17797. * the head/tail of the template portion of the styles.
  17798. * - if `isHost === false` (we are template) then insertion is at tail of `TStylingRange`
  17799. * - if `isHost === true` (we are host binding) then insertion is at head of `TStylingRange`
  17800. *
  17801. * @param tData The `TData` to insert into.
  17802. * @param tNode `TNode` associated with the styling element.
  17803. * @param tStylingKey See `TStylingKey`.
  17804. * @param index location of where `tStyleValue` should be stored (and linked into list.)
  17805. * @param isHostBinding `true` if the insertion is for a `hostBinding`. (insertion is in front of
  17806. * template.)
  17807. * @param isClassBinding True if the associated `tStylingKey` as a `class` styling.
  17808. * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.)
  17809. */
  17810. function insertTStylingBinding(tData, tNode, tStylingKeyWithStatic, index, isHostBinding, isClassBinding) {
  17811. ngDevMode && assertFirstUpdatePass(getTView());
  17812. let tBindings = isClassBinding ? tNode.classBindings : tNode.styleBindings;
  17813. let tmplHead = getTStylingRangePrev(tBindings);
  17814. let tmplTail = getTStylingRangeNext(tBindings);
  17815. tData[index] = tStylingKeyWithStatic;
  17816. let isKeyDuplicateOfStatic = false;
  17817. let tStylingKey;
  17818. if (Array.isArray(tStylingKeyWithStatic)) {
  17819. // We are case when the `TStylingKey` contains static fields as well.
  17820. const staticKeyValueArray = tStylingKeyWithStatic;
  17821. tStylingKey = staticKeyValueArray[1]; // unwrap.
  17822. // We need to check if our key is present in the static so that we can mark it as duplicate.
  17823. if (tStylingKey === null ||
  17824. keyValueArrayIndexOf(staticKeyValueArray, tStylingKey) > 0) {
  17825. // tStylingKey is present in the statics, need to mark it as duplicate.
  17826. isKeyDuplicateOfStatic = true;
  17827. }
  17828. }
  17829. else {
  17830. tStylingKey = tStylingKeyWithStatic;
  17831. }
  17832. if (isHostBinding) {
  17833. // We are inserting host bindings
  17834. // If we don't have template bindings then `tail` is 0.
  17835. const hasTemplateBindings = tmplTail !== 0;
  17836. // This is important to know because that means that the `head` can't point to the first
  17837. // template bindings (there are none.) Instead the head points to the tail of the template.
  17838. if (hasTemplateBindings) {
  17839. // template head's "prev" will point to last host binding or to 0 if no host bindings yet
  17840. const previousNode = getTStylingRangePrev(tData[tmplHead + 1]);
  17841. tData[index + 1] = toTStylingRange(previousNode, tmplHead);
  17842. // if a host binding has already been registered, we need to update the next of that host
  17843. // binding to point to this one
  17844. if (previousNode !== 0) {
  17845. // We need to update the template-tail value to point to us.
  17846. tData[previousNode + 1] =
  17847. setTStylingRangeNext(tData[previousNode + 1], index);
  17848. }
  17849. // The "previous" of the template binding head should point to this host binding
  17850. tData[tmplHead + 1] = setTStylingRangePrev(tData[tmplHead + 1], index);
  17851. }
  17852. else {
  17853. tData[index + 1] = toTStylingRange(tmplHead, 0);
  17854. // if a host binding has already been registered, we need to update the next of that host
  17855. // binding to point to this one
  17856. if (tmplHead !== 0) {
  17857. // We need to update the template-tail value to point to us.
  17858. tData[tmplHead + 1] = setTStylingRangeNext(tData[tmplHead + 1], index);
  17859. }
  17860. // if we don't have template, the head points to template-tail, and needs to be advanced.
  17861. tmplHead = index;
  17862. }
  17863. }
  17864. else {
  17865. // We are inserting in template section.
  17866. // We need to set this binding's "previous" to the current template tail
  17867. tData[index + 1] = toTStylingRange(tmplTail, 0);
  17868. ngDevMode &&
  17869. assertEqual(tmplHead !== 0 && tmplTail === 0, false, 'Adding template bindings after hostBindings is not allowed.');
  17870. if (tmplHead === 0) {
  17871. tmplHead = index;
  17872. }
  17873. else {
  17874. // We need to update the previous value "next" to point to this binding
  17875. tData[tmplTail + 1] = setTStylingRangeNext(tData[tmplTail + 1], index);
  17876. }
  17877. tmplTail = index;
  17878. }
  17879. // Now we need to update / compute the duplicates.
  17880. // Starting with our location search towards head (least priority)
  17881. if (isKeyDuplicateOfStatic) {
  17882. tData[index + 1] = setTStylingRangePrevDuplicate(tData[index + 1]);
  17883. }
  17884. markDuplicates(tData, tStylingKey, index, true, isClassBinding);
  17885. markDuplicates(tData, tStylingKey, index, false, isClassBinding);
  17886. markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding);
  17887. tBindings = toTStylingRange(tmplHead, tmplTail);
  17888. if (isClassBinding) {
  17889. tNode.classBindings = tBindings;
  17890. }
  17891. else {
  17892. tNode.styleBindings = tBindings;
  17893. }
  17894. }
  17895. /**
  17896. * Look into the residual styling to see if the current `tStylingKey` is duplicate of residual.
  17897. *
  17898. * @param tNode `TNode` where the residual is stored.
  17899. * @param tStylingKey `TStylingKey` to store.
  17900. * @param tData `TData` associated with the current `LView`.
  17901. * @param index location of where `tStyleValue` should be stored (and linked into list.)
  17902. * @param isClassBinding True if the associated `tStylingKey` as a `class` styling.
  17903. * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.)
  17904. */
  17905. function markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding) {
  17906. const residual = isClassBinding ? tNode.residualClasses : tNode.residualStyles;
  17907. if (residual != null /* or undefined */ && typeof tStylingKey == 'string' &&
  17908. keyValueArrayIndexOf(residual, tStylingKey) >= 0) {
  17909. // We have duplicate in the residual so mark ourselves as duplicate.
  17910. tData[index + 1] = setTStylingRangeNextDuplicate(tData[index + 1]);
  17911. }
  17912. }
  17913. /**
  17914. * Marks `TStyleValue`s as duplicates if another style binding in the list has the same
  17915. * `TStyleValue`.
  17916. *
  17917. * NOTE: this function is intended to be called twice once with `isPrevDir` set to `true` and once
  17918. * with it set to `false` to search both the previous as well as next items in the list.
  17919. *
  17920. * No duplicate case
  17921. * ```
  17922. * [style.color]
  17923. * [style.width.px] <<- index
  17924. * [style.height.px]
  17925. * ```
  17926. *
  17927. * In the above case adding `[style.width.px]` to the existing `[style.color]` produces no
  17928. * duplicates because `width` is not found in any other part of the linked list.
  17929. *
  17930. * Duplicate case
  17931. * ```
  17932. * [style.color]
  17933. * [style.width.em]
  17934. * [style.width.px] <<- index
  17935. * ```
  17936. * In the above case adding `[style.width.px]` will produce a duplicate with `[style.width.em]`
  17937. * because `width` is found in the chain.
  17938. *
  17939. * Map case 1
  17940. * ```
  17941. * [style.width.px]
  17942. * [style.color]
  17943. * [style] <<- index
  17944. * ```
  17945. * In the above case adding `[style]` will produce a duplicate with any other bindings because
  17946. * `[style]` is a Map and as such is fully dynamic and could produce `color` or `width`.
  17947. *
  17948. * Map case 2
  17949. * ```
  17950. * [style]
  17951. * [style.width.px]
  17952. * [style.color] <<- index
  17953. * ```
  17954. * In the above case adding `[style.color]` will produce a duplicate because there is already a
  17955. * `[style]` binding which is a Map and as such is fully dynamic and could produce `color` or
  17956. * `width`.
  17957. *
  17958. * NOTE: Once `[style]` (Map) is added into the system all things are mapped as duplicates.
  17959. * NOTE: We use `style` as example, but same logic is applied to `class`es as well.
  17960. *
  17961. * @param tData `TData` where the linked list is stored.
  17962. * @param tStylingKey `TStylingKeyPrimitive` which contains the value to compare to other keys in
  17963. * the linked list.
  17964. * @param index Starting location in the linked list to search from
  17965. * @param isPrevDir Direction.
  17966. * - `true` for previous (lower priority);
  17967. * - `false` for next (higher priority).
  17968. */
  17969. function markDuplicates(tData, tStylingKey, index, isPrevDir, isClassBinding) {
  17970. const tStylingAtIndex = tData[index + 1];
  17971. const isMap = tStylingKey === null;
  17972. let cursor = isPrevDir ? getTStylingRangePrev(tStylingAtIndex) : getTStylingRangeNext(tStylingAtIndex);
  17973. let foundDuplicate = false;
  17974. // We keep iterating as long as we have a cursor
  17975. // AND either:
  17976. // - we found what we are looking for, OR
  17977. // - we are a map in which case we have to continue searching even after we find what we were
  17978. // looking for since we are a wild card and everything needs to be flipped to duplicate.
  17979. while (cursor !== 0 && (foundDuplicate === false || isMap)) {
  17980. ngDevMode && assertIndexInRange(tData, cursor);
  17981. const tStylingValueAtCursor = tData[cursor];
  17982. const tStyleRangeAtCursor = tData[cursor + 1];
  17983. if (isStylingMatch(tStylingValueAtCursor, tStylingKey)) {
  17984. foundDuplicate = true;
  17985. tData[cursor + 1] = isPrevDir ? setTStylingRangeNextDuplicate(tStyleRangeAtCursor) :
  17986. setTStylingRangePrevDuplicate(tStyleRangeAtCursor);
  17987. }
  17988. cursor = isPrevDir ? getTStylingRangePrev(tStyleRangeAtCursor) :
  17989. getTStylingRangeNext(tStyleRangeAtCursor);
  17990. }
  17991. if (foundDuplicate) {
  17992. // if we found a duplicate, than mark ourselves.
  17993. tData[index + 1] = isPrevDir ? setTStylingRangePrevDuplicate(tStylingAtIndex) :
  17994. setTStylingRangeNextDuplicate(tStylingAtIndex);
  17995. }
  17996. }
  17997. /**
  17998. * Determines if two `TStylingKey`s are a match.
  17999. *
  18000. * When computing whether a binding contains a duplicate, we need to compare if the instruction
  18001. * `TStylingKey` has a match.
  18002. *
  18003. * Here are examples of `TStylingKey`s which match given `tStylingKeyCursor` is:
  18004. * - `color`
  18005. * - `color` // Match another color
  18006. * - `null` // That means that `tStylingKey` is a `classMap`/`styleMap` instruction
  18007. * - `['', 'color', 'other', true]` // wrapped `color` so match
  18008. * - `['', null, 'other', true]` // wrapped `null` so match
  18009. * - `['', 'width', 'color', 'value']` // wrapped static value contains a match on `'color'`
  18010. * - `null` // `tStylingKeyCursor` always match as it is `classMap`/`styleMap` instruction
  18011. *
  18012. * @param tStylingKeyCursor
  18013. * @param tStylingKey
  18014. */
  18015. function isStylingMatch(tStylingKeyCursor, tStylingKey) {
  18016. ngDevMode &&
  18017. assertNotEqual(Array.isArray(tStylingKey), true, 'Expected that \'tStylingKey\' has been unwrapped');
  18018. if (tStylingKeyCursor === null || // If the cursor is `null` it means that we have map at that
  18019. // location so we must assume that we have a match.
  18020. tStylingKey == null || // If `tStylingKey` is `null` then it is a map therefor assume that it
  18021. // contains a match.
  18022. (Array.isArray(tStylingKeyCursor) ? tStylingKeyCursor[1] : tStylingKeyCursor) ===
  18023. tStylingKey // If the keys match explicitly than we are a match.
  18024. ) {
  18025. return true;
  18026. }
  18027. else if (Array.isArray(tStylingKeyCursor) && typeof tStylingKey === 'string') {
  18028. // if we did not find a match, but `tStylingKeyCursor` is `KeyValueArray` that means cursor has
  18029. // statics and we need to check those as well.
  18030. return keyValueArrayIndexOf(tStylingKeyCursor, tStylingKey) >=
  18031. 0; // see if we are matching the key
  18032. }
  18033. return false;
  18034. }
  18035. // Global state of the parser. (This makes parser non-reentrant, but that is not an issue)
  18036. const parserState = {
  18037. textEnd: 0,
  18038. key: 0,
  18039. keyEnd: 0,
  18040. value: 0,
  18041. valueEnd: 0,
  18042. };
  18043. /**
  18044. * Retrieves the last parsed `key` of style.
  18045. * @param text the text to substring the key from.
  18046. */
  18047. function getLastParsedKey(text) {
  18048. return text.substring(parserState.key, parserState.keyEnd);
  18049. }
  18050. /**
  18051. * Retrieves the last parsed `value` of style.
  18052. * @param text the text to substring the key from.
  18053. */
  18054. function getLastParsedValue(text) {
  18055. return text.substring(parserState.value, parserState.valueEnd);
  18056. }
  18057. /**
  18058. * Initializes `className` string for parsing and parses the first token.
  18059. *
  18060. * This function is intended to be used in this format:
  18061. * ```
  18062. * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
  18063. * const key = getLastParsedKey();
  18064. * ...
  18065. * }
  18066. * ```
  18067. * @param text `className` to parse
  18068. * @returns index where the next invocation of `parseClassNameNext` should resume.
  18069. */
  18070. function parseClassName(text) {
  18071. resetParserState(text);
  18072. return parseClassNameNext(text, consumeWhitespace(text, 0, parserState.textEnd));
  18073. }
  18074. /**
  18075. * Parses next `className` token.
  18076. *
  18077. * This function is intended to be used in this format:
  18078. * ```
  18079. * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
  18080. * const key = getLastParsedKey();
  18081. * ...
  18082. * }
  18083. * ```
  18084. *
  18085. * @param text `className` to parse
  18086. * @param index where the parsing should resume.
  18087. * @returns index where the next invocation of `parseClassNameNext` should resume.
  18088. */
  18089. function parseClassNameNext(text, index) {
  18090. const end = parserState.textEnd;
  18091. if (end === index) {
  18092. return -1;
  18093. }
  18094. index = parserState.keyEnd = consumeClassToken(text, parserState.key = index, end);
  18095. return consumeWhitespace(text, index, end);
  18096. }
  18097. /**
  18098. * Initializes `cssText` string for parsing and parses the first key/values.
  18099. *
  18100. * This function is intended to be used in this format:
  18101. * ```
  18102. * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) {
  18103. * const key = getLastParsedKey();
  18104. * const value = getLastParsedValue();
  18105. * ...
  18106. * }
  18107. * ```
  18108. * @param text `cssText` to parse
  18109. * @returns index where the next invocation of `parseStyleNext` should resume.
  18110. */
  18111. function parseStyle(text) {
  18112. resetParserState(text);
  18113. return parseStyleNext(text, consumeWhitespace(text, 0, parserState.textEnd));
  18114. }
  18115. /**
  18116. * Parses the next `cssText` key/values.
  18117. *
  18118. * This function is intended to be used in this format:
  18119. * ```
  18120. * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) {
  18121. * const key = getLastParsedKey();
  18122. * const value = getLastParsedValue();
  18123. * ...
  18124. * }
  18125. *
  18126. * @param text `cssText` to parse
  18127. * @param index where the parsing should resume.
  18128. * @returns index where the next invocation of `parseStyleNext` should resume.
  18129. */
  18130. function parseStyleNext(text, startIndex) {
  18131. const end = parserState.textEnd;
  18132. let index = parserState.key = consumeWhitespace(text, startIndex, end);
  18133. if (end === index) {
  18134. // we reached an end so just quit
  18135. return -1;
  18136. }
  18137. index = parserState.keyEnd = consumeStyleKey(text, index, end);
  18138. index = consumeSeparator(text, index, end, 58 /* CharCode.COLON */);
  18139. index = parserState.value = consumeWhitespace(text, index, end);
  18140. index = parserState.valueEnd = consumeStyleValue(text, index, end);
  18141. return consumeSeparator(text, index, end, 59 /* CharCode.SEMI_COLON */);
  18142. }
  18143. /**
  18144. * Reset the global state of the styling parser.
  18145. * @param text The styling text to parse.
  18146. */
  18147. function resetParserState(text) {
  18148. parserState.key = 0;
  18149. parserState.keyEnd = 0;
  18150. parserState.value = 0;
  18151. parserState.valueEnd = 0;
  18152. parserState.textEnd = text.length;
  18153. }
  18154. /**
  18155. * Returns index of next non-whitespace character.
  18156. *
  18157. * @param text Text to scan
  18158. * @param startIndex Starting index of character where the scan should start.
  18159. * @param endIndex Ending index of character where the scan should end.
  18160. * @returns Index of next non-whitespace character (May be the same as `start` if no whitespace at
  18161. * that location.)
  18162. */
  18163. function consumeWhitespace(text, startIndex, endIndex) {
  18164. while (startIndex < endIndex && text.charCodeAt(startIndex) <= 32 /* CharCode.SPACE */) {
  18165. startIndex++;
  18166. }
  18167. return startIndex;
  18168. }
  18169. /**
  18170. * Returns index of last char in class token.
  18171. *
  18172. * @param text Text to scan
  18173. * @param startIndex Starting index of character where the scan should start.
  18174. * @param endIndex Ending index of character where the scan should end.
  18175. * @returns Index after last char in class token.
  18176. */
  18177. function consumeClassToken(text, startIndex, endIndex) {
  18178. while (startIndex < endIndex && text.charCodeAt(startIndex) > 32 /* CharCode.SPACE */) {
  18179. startIndex++;
  18180. }
  18181. return startIndex;
  18182. }
  18183. /**
  18184. * Consumes all of the characters belonging to style key and token.
  18185. *
  18186. * @param text Text to scan
  18187. * @param startIndex Starting index of character where the scan should start.
  18188. * @param endIndex Ending index of character where the scan should end.
  18189. * @returns Index after last style key character.
  18190. */
  18191. function consumeStyleKey(text, startIndex, endIndex) {
  18192. let ch;
  18193. while (startIndex < endIndex &&
  18194. ((ch = text.charCodeAt(startIndex)) === 45 /* CharCode.DASH */ || ch === 95 /* CharCode.UNDERSCORE */ ||
  18195. ((ch & -33 /* CharCode.UPPER_CASE */) >= 65 /* CharCode.A */ && (ch & -33 /* CharCode.UPPER_CASE */) <= 90 /* CharCode.Z */) ||
  18196. (ch >= 48 /* CharCode.ZERO */ && ch <= 57 /* CharCode.NINE */))) {
  18197. startIndex++;
  18198. }
  18199. return startIndex;
  18200. }
  18201. /**
  18202. * Consumes all whitespace and the separator `:` after the style key.
  18203. *
  18204. * @param text Text to scan
  18205. * @param startIndex Starting index of character where the scan should start.
  18206. * @param endIndex Ending index of character where the scan should end.
  18207. * @returns Index after separator and surrounding whitespace.
  18208. */
  18209. function consumeSeparator(text, startIndex, endIndex, separator) {
  18210. startIndex = consumeWhitespace(text, startIndex, endIndex);
  18211. if (startIndex < endIndex) {
  18212. if (ngDevMode && text.charCodeAt(startIndex) !== separator) {
  18213. malformedStyleError(text, String.fromCharCode(separator), startIndex);
  18214. }
  18215. startIndex++;
  18216. }
  18217. return startIndex;
  18218. }
  18219. /**
  18220. * Consumes style value honoring `url()` and `""` text.
  18221. *
  18222. * @param text Text to scan
  18223. * @param startIndex Starting index of character where the scan should start.
  18224. * @param endIndex Ending index of character where the scan should end.
  18225. * @returns Index after last style value character.
  18226. */
  18227. function consumeStyleValue(text, startIndex, endIndex) {
  18228. let ch1 = -1; // 1st previous character
  18229. let ch2 = -1; // 2nd previous character
  18230. let ch3 = -1; // 3rd previous character
  18231. let i = startIndex;
  18232. let lastChIndex = i;
  18233. while (i < endIndex) {
  18234. const ch = text.charCodeAt(i++);
  18235. if (ch === 59 /* CharCode.SEMI_COLON */) {
  18236. return lastChIndex;
  18237. }
  18238. else if (ch === 34 /* CharCode.DOUBLE_QUOTE */ || ch === 39 /* CharCode.SINGLE_QUOTE */) {
  18239. lastChIndex = i = consumeQuotedText(text, ch, i, endIndex);
  18240. }
  18241. else if (startIndex ===
  18242. i - 4 && // We have seen only 4 characters so far "URL(" (Ignore "foo_URL()")
  18243. ch3 === 85 /* CharCode.U */ &&
  18244. ch2 === 82 /* CharCode.R */ && ch1 === 76 /* CharCode.L */ && ch === 40 /* CharCode.OPEN_PAREN */) {
  18245. lastChIndex = i = consumeQuotedText(text, 41 /* CharCode.CLOSE_PAREN */, i, endIndex);
  18246. }
  18247. else if (ch > 32 /* CharCode.SPACE */) {
  18248. // if we have a non-whitespace character then capture its location
  18249. lastChIndex = i;
  18250. }
  18251. ch3 = ch2;
  18252. ch2 = ch1;
  18253. ch1 = ch & -33 /* CharCode.UPPER_CASE */;
  18254. }
  18255. return lastChIndex;
  18256. }
  18257. /**
  18258. * Consumes all of the quoted characters.
  18259. *
  18260. * @param text Text to scan
  18261. * @param quoteCharCode CharCode of either `"` or `'` quote or `)` for `url(...)`.
  18262. * @param startIndex Starting index of character where the scan should start.
  18263. * @param endIndex Ending index of character where the scan should end.
  18264. * @returns Index after quoted characters.
  18265. */
  18266. function consumeQuotedText(text, quoteCharCode, startIndex, endIndex) {
  18267. let ch1 = -1; // 1st previous character
  18268. let index = startIndex;
  18269. while (index < endIndex) {
  18270. const ch = text.charCodeAt(index++);
  18271. if (ch == quoteCharCode && ch1 !== 92 /* CharCode.BACK_SLASH */) {
  18272. return index;
  18273. }
  18274. if (ch == 92 /* CharCode.BACK_SLASH */ && ch1 === 92 /* CharCode.BACK_SLASH */) {
  18275. // two back slashes cancel each other out. For example `"\\"` should properly end the
  18276. // quotation. (It should not assume that the last `"` is escaped.)
  18277. ch1 = 0;
  18278. }
  18279. else {
  18280. ch1 = ch;
  18281. }
  18282. }
  18283. throw ngDevMode ? malformedStyleError(text, String.fromCharCode(quoteCharCode), endIndex) :
  18284. new Error();
  18285. }
  18286. function malformedStyleError(text, expecting, index) {
  18287. ngDevMode && assertEqual(typeof text === 'string', true, 'String expected here');
  18288. throw throwError(`Malformed style at location ${index} in string '` + text.substring(0, index) + '[>>' +
  18289. text.substring(index, index + 1) + '<<]' + text.slice(index + 1) +
  18290. `'. Expecting '${expecting}'.`);
  18291. }
  18292. /**
  18293. * Update a style binding on an element with the provided value.
  18294. *
  18295. * If the style value is falsy then it will be removed from the element
  18296. * (or assigned a different value depending if there are any styles placed
  18297. * on the element with `styleMap` or any static styles that are
  18298. * present from when the element was created with `styling`).
  18299. *
  18300. * Note that the styling element is updated as part of `stylingApply`.
  18301. *
  18302. * @param prop A valid CSS property.
  18303. * @param value New value to write (`null` or an empty string to remove).
  18304. * @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
  18305. *
  18306. * Note that this will apply the provided style value to the host element if this function is called
  18307. * within a host binding function.
  18308. *
  18309. * @codeGenApi
  18310. */
  18311. function ɵɵstyleProp(prop, value, suffix) {
  18312. checkStylingProperty(prop, value, suffix, false);
  18313. return ɵɵstyleProp;
  18314. }
  18315. /**
  18316. * Update a class binding on an element with the provided value.
  18317. *
  18318. * This instruction is meant to handle the `[class.foo]="exp"` case and,
  18319. * therefore, the class binding itself must already be allocated using
  18320. * `styling` within the creation block.
  18321. *
  18322. * @param prop A valid CSS class (only one).
  18323. * @param value A true/false value which will turn the class on or off.
  18324. *
  18325. * Note that this will apply the provided class value to the host element if this function
  18326. * is called within a host binding function.
  18327. *
  18328. * @codeGenApi
  18329. */
  18330. function ɵɵclassProp(className, value) {
  18331. checkStylingProperty(className, value, null, true);
  18332. return ɵɵclassProp;
  18333. }
  18334. /**
  18335. * Update style bindings using an object literal on an element.
  18336. *
  18337. * This instruction is meant to apply styling via the `[style]="exp"` template bindings.
  18338. * When styles are applied to the element they will then be updated with respect to
  18339. * any styles/classes set via `styleProp`. If any styles are set to falsy
  18340. * then they will be removed from the element.
  18341. *
  18342. * Note that the styling instruction will not be applied until `stylingApply` is called.
  18343. *
  18344. * @param styles A key/value style map of the styles that will be applied to the given element.
  18345. * Any missing styles (that have already been applied to the element beforehand) will be
  18346. * removed (unset) from the element's styling.
  18347. *
  18348. * Note that this will apply the provided styleMap value to the host element if this function
  18349. * is called within a host binding.
  18350. *
  18351. * @codeGenApi
  18352. */
  18353. function ɵɵstyleMap(styles) {
  18354. checkStylingMap(styleKeyValueArraySet, styleStringParser, styles, false);
  18355. }
  18356. /**
  18357. * Parse text as style and add values to KeyValueArray.
  18358. *
  18359. * This code is pulled out to a separate function so that it can be tree shaken away if it is not
  18360. * needed. It is only referenced from `ɵɵstyleMap`.
  18361. *
  18362. * @param keyValueArray KeyValueArray to add parsed values to.
  18363. * @param text text to parse.
  18364. */
  18365. function styleStringParser(keyValueArray, text) {
  18366. for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i)) {
  18367. styleKeyValueArraySet(keyValueArray, getLastParsedKey(text), getLastParsedValue(text));
  18368. }
  18369. }
  18370. /**
  18371. * Update class bindings using an object literal or class-string on an element.
  18372. *
  18373. * This instruction is meant to apply styling via the `[class]="exp"` template bindings.
  18374. * When classes are applied to the element they will then be updated with
  18375. * respect to any styles/classes set via `classProp`. If any
  18376. * classes are set to falsy then they will be removed from the element.
  18377. *
  18378. * Note that the styling instruction will not be applied until `stylingApply` is called.
  18379. * Note that this will the provided classMap value to the host element if this function is called
  18380. * within a host binding.
  18381. *
  18382. * @param classes A key/value map or string of CSS classes that will be added to the
  18383. * given element. Any missing classes (that have already been applied to the element
  18384. * beforehand) will be removed (unset) from the element's list of CSS classes.
  18385. *
  18386. * @codeGenApi
  18387. */
  18388. function ɵɵclassMap(classes) {
  18389. checkStylingMap(classKeyValueArraySet, classStringParser, classes, true);
  18390. }
  18391. /**
  18392. * Parse text as class and add values to KeyValueArray.
  18393. *
  18394. * This code is pulled out to a separate function so that it can be tree shaken away if it is not
  18395. * needed. It is only referenced from `ɵɵclassMap`.
  18396. *
  18397. * @param keyValueArray KeyValueArray to add parsed values to.
  18398. * @param text text to parse.
  18399. */
  18400. function classStringParser(keyValueArray, text) {
  18401. for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
  18402. keyValueArraySet(keyValueArray, getLastParsedKey(text), true);
  18403. }
  18404. }
  18405. /**
  18406. * Common code between `ɵɵclassProp` and `ɵɵstyleProp`.
  18407. *
  18408. * @param prop property name.
  18409. * @param value binding value.
  18410. * @param suffix suffix for the property (e.g. `em` or `px`)
  18411. * @param isClassBased `true` if `class` change (`false` if `style`)
  18412. */
  18413. function checkStylingProperty(prop, value, suffix, isClassBased) {
  18414. const lView = getLView();
  18415. const tView = getTView();
  18416. // Styling instructions use 2 slots per binding.
  18417. // 1. one for the value / TStylingKey
  18418. // 2. one for the intermittent-value / TStylingRange
  18419. const bindingIndex = incrementBindingIndex(2);
  18420. if (tView.firstUpdatePass) {
  18421. stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
  18422. }
  18423. if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
  18424. const tNode = tView.data[getSelectedIndex()];
  18425. updateStyling(tView, tNode, lView, lView[RENDERER], prop, lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex);
  18426. }
  18427. }
  18428. /**
  18429. * Common code between `ɵɵclassMap` and `ɵɵstyleMap`.
  18430. *
  18431. * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
  18432. * function so that `style` can be processed. This is done for tree shaking purposes.
  18433. * @param stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class`
  18434. * have different parsers.)
  18435. * @param value bound value from application
  18436. * @param isClassBased `true` if `class` change (`false` if `style`)
  18437. */
  18438. function checkStylingMap(keyValueArraySet, stringParser, value, isClassBased) {
  18439. const tView = getTView();
  18440. const bindingIndex = incrementBindingIndex(2);
  18441. if (tView.firstUpdatePass) {
  18442. stylingFirstUpdatePass(tView, null, bindingIndex, isClassBased);
  18443. }
  18444. const lView = getLView();
  18445. if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
  18446. // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
  18447. // if so as not to read unnecessarily.
  18448. const tNode = tView.data[getSelectedIndex()];
  18449. if (hasStylingInputShadow(tNode, isClassBased) && !isInHostBindings(tView, bindingIndex)) {
  18450. if (ngDevMode) {
  18451. // verify that if we are shadowing then `TData` is appropriately marked so that we skip
  18452. // processing this binding in styling resolution.
  18453. const tStylingKey = tView.data[bindingIndex];
  18454. assertEqual(Array.isArray(tStylingKey) ? tStylingKey[1] : tStylingKey, false, 'Styling linked list shadow input should be marked as \'false\'');
  18455. }
  18456. // VE does not concatenate the static portion like we are doing here.
  18457. // Instead VE just ignores the static completely if dynamic binding is present.
  18458. // Because of locality we have already set the static portion because we don't know if there
  18459. // is a dynamic portion until later. If we would ignore the static portion it would look like
  18460. // the binding has removed it. This would confuse `[ngStyle]`/`[ngClass]` to do the wrong
  18461. // thing as it would think that the static portion was removed. For this reason we
  18462. // concatenate it so that `[ngStyle]`/`[ngClass]` can continue to work on changed.
  18463. let staticPrefix = isClassBased ? tNode.classesWithoutHost : tNode.stylesWithoutHost;
  18464. ngDevMode && isClassBased === false && staticPrefix !== null &&
  18465. assertEqual(staticPrefix.endsWith(';'), true, 'Expecting static portion to end with \';\'');
  18466. if (staticPrefix !== null) {
  18467. // We want to make sure that falsy values of `value` become empty strings.
  18468. value = concatStringsWithSpace(staticPrefix, value ? value : '');
  18469. }
  18470. // Given `<div [style] my-dir>` such that `my-dir` has `@Input('style')`.
  18471. // This takes over the `[style]` binding. (Same for `[class]`)
  18472. setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased);
  18473. }
  18474. else {
  18475. updateStylingMap(tView, tNode, lView, lView[RENDERER], lView[bindingIndex + 1], lView[bindingIndex + 1] = toStylingKeyValueArray(keyValueArraySet, stringParser, value), isClassBased, bindingIndex);
  18476. }
  18477. }
  18478. }
  18479. /**
  18480. * Determines when the binding is in `hostBindings` section
  18481. *
  18482. * @param tView Current `TView`
  18483. * @param bindingIndex index of binding which we would like if it is in `hostBindings`
  18484. */
  18485. function isInHostBindings(tView, bindingIndex) {
  18486. // All host bindings are placed after the expando section.
  18487. return bindingIndex >= tView.expandoStartIndex;
  18488. }
  18489. /**
  18490. * Collects the necessary information to insert the binding into a linked list of style bindings
  18491. * using `insertTStylingBinding`.
  18492. *
  18493. * @param tView `TView` where the binding linked list will be stored.
  18494. * @param tStylingKey Property/key of the binding.
  18495. * @param bindingIndex Index of binding associated with the `prop`
  18496. * @param isClassBased `true` if `class` change (`false` if `style`)
  18497. */
  18498. function stylingFirstUpdatePass(tView, tStylingKey, bindingIndex, isClassBased) {
  18499. ngDevMode && assertFirstUpdatePass(tView);
  18500. const tData = tView.data;
  18501. if (tData[bindingIndex + 1] === null) {
  18502. // The above check is necessary because we don't clear first update pass until first successful
  18503. // (no exception) template execution. This prevents the styling instruction from double adding
  18504. // itself to the list.
  18505. // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
  18506. // if so as not to read unnecessarily.
  18507. const tNode = tData[getSelectedIndex()];
  18508. ngDevMode && assertDefined(tNode, 'TNode expected');
  18509. const isHostBindings = isInHostBindings(tView, bindingIndex);
  18510. if (hasStylingInputShadow(tNode, isClassBased) && tStylingKey === null && !isHostBindings) {
  18511. // `tStylingKey === null` implies that we are either `[style]` or `[class]` binding.
  18512. // If there is a directive which uses `@Input('style')` or `@Input('class')` than
  18513. // we need to neutralize this binding since that directive is shadowing it.
  18514. // We turn this into a noop by setting the key to `false`
  18515. tStylingKey = false;
  18516. }
  18517. tStylingKey = wrapInStaticStylingKey(tData, tNode, tStylingKey, isClassBased);
  18518. insertTStylingBinding(tData, tNode, tStylingKey, bindingIndex, isHostBindings, isClassBased);
  18519. }
  18520. }
  18521. /**
  18522. * Adds static styling information to the binding if applicable.
  18523. *
  18524. * The linked list of styles not only stores the list and keys, but also stores static styling
  18525. * information on some of the keys. This function determines if the key should contain the styling
  18526. * information and computes it.
  18527. *
  18528. * See `TStylingStatic` for more details.
  18529. *
  18530. * @param tData `TData` where the linked list is stored.
  18531. * @param tNode `TNode` for which the styling is being computed.
  18532. * @param stylingKey `TStylingKeyPrimitive` which may need to be wrapped into `TStylingKey`
  18533. * @param isClassBased `true` if `class` (`false` if `style`)
  18534. */
  18535. function wrapInStaticStylingKey(tData, tNode, stylingKey, isClassBased) {
  18536. const hostDirectiveDef = getCurrentDirectiveDef(tData);
  18537. let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles;
  18538. if (hostDirectiveDef === null) {
  18539. // We are in template node.
  18540. // If template node already had styling instruction then it has already collected the static
  18541. // styling and there is no need to collect them again. We know that we are the first styling
  18542. // instruction because the `TNode.*Bindings` points to 0 (nothing has been inserted yet).
  18543. const isFirstStylingInstructionInTemplate = (isClassBased ? tNode.classBindings : tNode.styleBindings) === 0;
  18544. if (isFirstStylingInstructionInTemplate) {
  18545. // It would be nice to be able to get the statics from `mergeAttrs`, however, at this point
  18546. // they are already merged and it would not be possible to figure which property belongs where
  18547. // in the priority.
  18548. stylingKey = collectStylingFromDirectives(null, tData, tNode, stylingKey, isClassBased);
  18549. stylingKey = collectStylingFromTAttrs(stylingKey, tNode.attrs, isClassBased);
  18550. // We know that if we have styling binding in template we can't have residual.
  18551. residual = null;
  18552. }
  18553. }
  18554. else {
  18555. // We are in host binding node and there was no binding instruction in template node.
  18556. // This means that we need to compute the residual.
  18557. const directiveStylingLast = tNode.directiveStylingLast;
  18558. const isFirstStylingInstructionInHostBinding = directiveStylingLast === -1 || tData[directiveStylingLast] !== hostDirectiveDef;
  18559. if (isFirstStylingInstructionInHostBinding) {
  18560. stylingKey =
  18561. collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased);
  18562. if (residual === null) {
  18563. // - If `null` than either:
  18564. // - Template styling instruction already ran and it has consumed the static
  18565. // styling into its `TStylingKey` and so there is no need to update residual. Instead
  18566. // we need to update the `TStylingKey` associated with the first template node
  18567. // instruction. OR
  18568. // - Some other styling instruction ran and determined that there are no residuals
  18569. let templateStylingKey = getTemplateHeadTStylingKey(tData, tNode, isClassBased);
  18570. if (templateStylingKey !== undefined && Array.isArray(templateStylingKey)) {
  18571. // Only recompute if `templateStylingKey` had static values. (If no static value found
  18572. // then there is nothing to do since this operation can only produce less static keys, not
  18573. // more.)
  18574. templateStylingKey = collectStylingFromDirectives(null, tData, tNode, templateStylingKey[1] /* unwrap previous statics */, isClassBased);
  18575. templateStylingKey =
  18576. collectStylingFromTAttrs(templateStylingKey, tNode.attrs, isClassBased);
  18577. setTemplateHeadTStylingKey(tData, tNode, isClassBased, templateStylingKey);
  18578. }
  18579. }
  18580. else {
  18581. // We only need to recompute residual if it is not `null`.
  18582. // - If existing residual (implies there was no template styling). This means that some of
  18583. // the statics may have moved from the residual to the `stylingKey` and so we have to
  18584. // recompute.
  18585. // - If `undefined` this is the first time we are running.
  18586. residual = collectResidual(tData, tNode, isClassBased);
  18587. }
  18588. }
  18589. }
  18590. if (residual !== undefined) {
  18591. isClassBased ? (tNode.residualClasses = residual) : (tNode.residualStyles = residual);
  18592. }
  18593. return stylingKey;
  18594. }
  18595. /**
  18596. * Retrieve the `TStylingKey` for the template styling instruction.
  18597. *
  18598. * This is needed since `hostBinding` styling instructions are inserted after the template
  18599. * instruction. While the template instruction needs to update the residual in `TNode` the
  18600. * `hostBinding` instructions need to update the `TStylingKey` of the template instruction because
  18601. * the template instruction is downstream from the `hostBindings` instructions.
  18602. *
  18603. * @param tData `TData` where the linked list is stored.
  18604. * @param tNode `TNode` for which the styling is being computed.
  18605. * @param isClassBased `true` if `class` (`false` if `style`)
  18606. * @return `TStylingKey` if found or `undefined` if not found.
  18607. */
  18608. function getTemplateHeadTStylingKey(tData, tNode, isClassBased) {
  18609. const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings;
  18610. if (getTStylingRangeNext(bindings) === 0) {
  18611. // There does not seem to be a styling instruction in the `template`.
  18612. return undefined;
  18613. }
  18614. return tData[getTStylingRangePrev(bindings)];
  18615. }
  18616. /**
  18617. * Update the `TStylingKey` of the first template instruction in `TNode`.
  18618. *
  18619. * Logically `hostBindings` styling instructions are of lower priority than that of the template.
  18620. * However, they execute after the template styling instructions. This means that they get inserted
  18621. * in front of the template styling instructions.
  18622. *
  18623. * If we have a template styling instruction and a new `hostBindings` styling instruction is
  18624. * executed it means that it may need to steal static fields from the template instruction. This
  18625. * method allows us to update the first template instruction `TStylingKey` with a new value.
  18626. *
  18627. * Assume:
  18628. * ```
  18629. * <div my-dir style="color: red" [style.color]="tmplExp"></div>
  18630. *
  18631. * @Directive({
  18632. * host: {
  18633. * 'style': 'width: 100px',
  18634. * '[style.color]': 'dirExp',
  18635. * }
  18636. * })
  18637. * class MyDir {}
  18638. * ```
  18639. *
  18640. * when `[style.color]="tmplExp"` executes it creates this data structure.
  18641. * ```
  18642. * ['', 'color', 'color', 'red', 'width', '100px'],
  18643. * ```
  18644. *
  18645. * The reason for this is that the template instruction does not know if there are styling
  18646. * instructions and must assume that there are none and must collect all of the static styling.
  18647. * (both
  18648. * `color' and 'width`)
  18649. *
  18650. * When `'[style.color]': 'dirExp',` executes we need to insert a new data into the linked list.
  18651. * ```
  18652. * ['', 'color', 'width', '100px'], // newly inserted
  18653. * ['', 'color', 'color', 'red', 'width', '100px'], // this is wrong
  18654. * ```
  18655. *
  18656. * Notice that the template statics is now wrong as it incorrectly contains `width` so we need to
  18657. * update it like so:
  18658. * ```
  18659. * ['', 'color', 'width', '100px'],
  18660. * ['', 'color', 'color', 'red'], // UPDATE
  18661. * ```
  18662. *
  18663. * @param tData `TData` where the linked list is stored.
  18664. * @param tNode `TNode` for which the styling is being computed.
  18665. * @param isClassBased `true` if `class` (`false` if `style`)
  18666. * @param tStylingKey New `TStylingKey` which is replacing the old one.
  18667. */
  18668. function setTemplateHeadTStylingKey(tData, tNode, isClassBased, tStylingKey) {
  18669. const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings;
  18670. ngDevMode &&
  18671. assertNotEqual(getTStylingRangeNext(bindings), 0, 'Expecting to have at least one template styling binding.');
  18672. tData[getTStylingRangePrev(bindings)] = tStylingKey;
  18673. }
  18674. /**
  18675. * Collect all static values after the current `TNode.directiveStylingLast` index.
  18676. *
  18677. * Collect the remaining styling information which has not yet been collected by an existing
  18678. * styling instruction.
  18679. *
  18680. * @param tData `TData` where the `DirectiveDefs` are stored.
  18681. * @param tNode `TNode` which contains the directive range.
  18682. * @param isClassBased `true` if `class` (`false` if `style`)
  18683. */
  18684. function collectResidual(tData, tNode, isClassBased) {
  18685. let residual = undefined;
  18686. const directiveEnd = tNode.directiveEnd;
  18687. ngDevMode &&
  18688. assertNotEqual(tNode.directiveStylingLast, -1, 'By the time this function gets called at least one hostBindings-node styling instruction must have executed.');
  18689. // We add `1 + tNode.directiveStart` because we need to skip the current directive (as we are
  18690. // collecting things after the last `hostBindings` directive which had a styling instruction.)
  18691. for (let i = 1 + tNode.directiveStylingLast; i < directiveEnd; i++) {
  18692. const attrs = tData[i].hostAttrs;
  18693. residual = collectStylingFromTAttrs(residual, attrs, isClassBased);
  18694. }
  18695. return collectStylingFromTAttrs(residual, tNode.attrs, isClassBased);
  18696. }
  18697. /**
  18698. * Collect the static styling information with lower priority than `hostDirectiveDef`.
  18699. *
  18700. * (This is opposite of residual styling.)
  18701. *
  18702. * @param hostDirectiveDef `DirectiveDef` for which we want to collect lower priority static
  18703. * styling. (Or `null` if template styling)
  18704. * @param tData `TData` where the linked list is stored.
  18705. * @param tNode `TNode` for which the styling is being computed.
  18706. * @param stylingKey Existing `TStylingKey` to update or wrap.
  18707. * @param isClassBased `true` if `class` (`false` if `style`)
  18708. */
  18709. function collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased) {
  18710. // We need to loop because there can be directives which have `hostAttrs` but don't have
  18711. // `hostBindings` so this loop catches up to the current directive..
  18712. let currentDirective = null;
  18713. const directiveEnd = tNode.directiveEnd;
  18714. let directiveStylingLast = tNode.directiveStylingLast;
  18715. if (directiveStylingLast === -1) {
  18716. directiveStylingLast = tNode.directiveStart;
  18717. }
  18718. else {
  18719. directiveStylingLast++;
  18720. }
  18721. while (directiveStylingLast < directiveEnd) {
  18722. currentDirective = tData[directiveStylingLast];
  18723. ngDevMode && assertDefined(currentDirective, 'expected to be defined');
  18724. stylingKey = collectStylingFromTAttrs(stylingKey, currentDirective.hostAttrs, isClassBased);
  18725. if (currentDirective === hostDirectiveDef)
  18726. break;
  18727. directiveStylingLast++;
  18728. }
  18729. if (hostDirectiveDef !== null) {
  18730. // we only advance the styling cursor if we are collecting data from host bindings.
  18731. // Template executes before host bindings and so if we would update the index,
  18732. // host bindings would not get their statics.
  18733. tNode.directiveStylingLast = directiveStylingLast;
  18734. }
  18735. return stylingKey;
  18736. }
  18737. /**
  18738. * Convert `TAttrs` into `TStylingStatic`.
  18739. *
  18740. * @param stylingKey existing `TStylingKey` to update or wrap.
  18741. * @param attrs `TAttributes` to process.
  18742. * @param isClassBased `true` if `class` (`false` if `style`)
  18743. */
  18744. function collectStylingFromTAttrs(stylingKey, attrs, isClassBased) {
  18745. const desiredMarker = isClassBased ? 1 /* AttributeMarker.Classes */ : 2 /* AttributeMarker.Styles */;
  18746. let currentMarker = -1 /* AttributeMarker.ImplicitAttributes */;
  18747. if (attrs !== null) {
  18748. for (let i = 0; i < attrs.length; i++) {
  18749. const item = attrs[i];
  18750. if (typeof item === 'number') {
  18751. currentMarker = item;
  18752. }
  18753. else {
  18754. if (currentMarker === desiredMarker) {
  18755. if (!Array.isArray(stylingKey)) {
  18756. stylingKey = stylingKey === undefined ? [] : ['', stylingKey];
  18757. }
  18758. keyValueArraySet(stylingKey, item, isClassBased ? true : attrs[++i]);
  18759. }
  18760. }
  18761. }
  18762. }
  18763. return stylingKey === undefined ? null : stylingKey;
  18764. }
  18765. /**
  18766. * Convert user input to `KeyValueArray`.
  18767. *
  18768. * This function takes user input which could be `string`, Object literal, or iterable and converts
  18769. * it into a consistent representation. The output of this is `KeyValueArray` (which is an array
  18770. * where
  18771. * even indexes contain keys and odd indexes contain values for those keys).
  18772. *
  18773. * The advantage of converting to `KeyValueArray` is that we can perform diff in an input
  18774. * independent
  18775. * way.
  18776. * (ie we can compare `foo bar` to `['bar', 'baz'] and determine a set of changes which need to be
  18777. * applied)
  18778. *
  18779. * The fact that `KeyValueArray` is sorted is very important because it allows us to compute the
  18780. * difference in linear fashion without the need to allocate any additional data.
  18781. *
  18782. * For example if we kept this as a `Map` we would have to iterate over previous `Map` to determine
  18783. * which values need to be deleted, over the new `Map` to determine additions, and we would have to
  18784. * keep additional `Map` to keep track of duplicates or items which have not yet been visited.
  18785. *
  18786. * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
  18787. * function so that `style` can be processed. This is done
  18788. * for tree shaking purposes.
  18789. * @param stringParser The parser is passed in so that it will be tree shakable. See
  18790. * `styleStringParser` and `classStringParser`
  18791. * @param value The value to parse/convert to `KeyValueArray`
  18792. */
  18793. function toStylingKeyValueArray(keyValueArraySet, stringParser, value) {
  18794. if (value == null /*|| value === undefined */ || value === '')
  18795. return EMPTY_ARRAY;
  18796. const styleKeyValueArray = [];
  18797. const unwrappedValue = unwrapSafeValue(value);
  18798. if (Array.isArray(unwrappedValue)) {
  18799. for (let i = 0; i < unwrappedValue.length; i++) {
  18800. keyValueArraySet(styleKeyValueArray, unwrappedValue[i], true);
  18801. }
  18802. }
  18803. else if (typeof unwrappedValue === 'object') {
  18804. for (const key in unwrappedValue) {
  18805. if (unwrappedValue.hasOwnProperty(key)) {
  18806. keyValueArraySet(styleKeyValueArray, key, unwrappedValue[key]);
  18807. }
  18808. }
  18809. }
  18810. else if (typeof unwrappedValue === 'string') {
  18811. stringParser(styleKeyValueArray, unwrappedValue);
  18812. }
  18813. else {
  18814. ngDevMode &&
  18815. throwError('Unsupported styling type ' + typeof unwrappedValue + ': ' + unwrappedValue);
  18816. }
  18817. return styleKeyValueArray;
  18818. }
  18819. /**
  18820. * Set a `value` for a `key`.
  18821. *
  18822. * See: `keyValueArraySet` for details
  18823. *
  18824. * @param keyValueArray KeyValueArray to add to.
  18825. * @param key Style key to add.
  18826. * @param value The value to set.
  18827. */
  18828. function styleKeyValueArraySet(keyValueArray, key, value) {
  18829. keyValueArraySet(keyValueArray, key, unwrapSafeValue(value));
  18830. }
  18831. /**
  18832. * Class-binding-specific function for setting the `value` for a `key`.
  18833. *
  18834. * See: `keyValueArraySet` for details
  18835. *
  18836. * @param keyValueArray KeyValueArray to add to.
  18837. * @param key Style key to add.
  18838. * @param value The value to set.
  18839. */
  18840. function classKeyValueArraySet(keyValueArray, key, value) {
  18841. // We use `classList.add` to eventually add the CSS classes to the DOM node. Any value passed into
  18842. // `add` is stringified and added to the `class` attribute, e.g. even null, undefined or numbers
  18843. // will be added. Stringify the key here so that our internal data structure matches the value in
  18844. // the DOM. The only exceptions are empty strings and strings that contain spaces for which
  18845. // the browser throws an error. We ignore such values, because the error is somewhat cryptic.
  18846. const stringKey = String(key);
  18847. if (stringKey !== '' && !stringKey.includes(' ')) {
  18848. keyValueArraySet(keyValueArray, stringKey, value);
  18849. }
  18850. }
  18851. /**
  18852. * Update map based styling.
  18853. *
  18854. * Map based styling could be anything which contains more than one binding. For example `string`,
  18855. * or object literal. Dealing with all of these types would complicate the logic so
  18856. * instead this function expects that the complex input is first converted into normalized
  18857. * `KeyValueArray`. The advantage of normalization is that we get the values sorted, which makes it
  18858. * very cheap to compute deltas between the previous and current value.
  18859. *
  18860. * @param tView Associated `TView.data` contains the linked list of binding priorities.
  18861. * @param tNode `TNode` where the binding is located.
  18862. * @param lView `LView` contains the values associated with other styling binding at this `TNode`.
  18863. * @param renderer Renderer to use if any updates.
  18864. * @param oldKeyValueArray Previous value represented as `KeyValueArray`
  18865. * @param newKeyValueArray Current value represented as `KeyValueArray`
  18866. * @param isClassBased `true` if `class` (`false` if `style`)
  18867. * @param bindingIndex Binding index of the binding.
  18868. */
  18869. function updateStylingMap(tView, tNode, lView, renderer, oldKeyValueArray, newKeyValueArray, isClassBased, bindingIndex) {
  18870. if (oldKeyValueArray === NO_CHANGE) {
  18871. // On first execution the oldKeyValueArray is NO_CHANGE => treat it as empty KeyValueArray.
  18872. oldKeyValueArray = EMPTY_ARRAY;
  18873. }
  18874. let oldIndex = 0;
  18875. let newIndex = 0;
  18876. let oldKey = 0 < oldKeyValueArray.length ? oldKeyValueArray[0] : null;
  18877. let newKey = 0 < newKeyValueArray.length ? newKeyValueArray[0] : null;
  18878. while (oldKey !== null || newKey !== null) {
  18879. ngDevMode && assertLessThan(oldIndex, 999, 'Are we stuck in infinite loop?');
  18880. ngDevMode && assertLessThan(newIndex, 999, 'Are we stuck in infinite loop?');
  18881. const oldValue = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex + 1] : undefined;
  18882. const newValue = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex + 1] : undefined;
  18883. let setKey = null;
  18884. let setValue = undefined;
  18885. if (oldKey === newKey) {
  18886. // UPDATE: Keys are equal => new value is overwriting old value.
  18887. oldIndex += 2;
  18888. newIndex += 2;
  18889. if (oldValue !== newValue) {
  18890. setKey = newKey;
  18891. setValue = newValue;
  18892. }
  18893. }
  18894. else if (newKey === null || oldKey !== null && oldKey < newKey) {
  18895. // DELETE: oldKey key is missing or we did not find the oldKey in the newValue
  18896. // (because the keyValueArray is sorted and `newKey` is found later alphabetically).
  18897. // `"background" < "color"` so we need to delete `"background"` because it is not found in the
  18898. // new array.
  18899. oldIndex += 2;
  18900. setKey = oldKey;
  18901. }
  18902. else {
  18903. // CREATE: newKey's is earlier alphabetically than oldKey's (or no oldKey) => we have new key.
  18904. // `"color" > "background"` so we need to add `color` because it is in new array but not in
  18905. // old array.
  18906. ngDevMode && assertDefined(newKey, 'Expecting to have a valid key');
  18907. newIndex += 2;
  18908. setKey = newKey;
  18909. setValue = newValue;
  18910. }
  18911. if (setKey !== null) {
  18912. updateStyling(tView, tNode, lView, renderer, setKey, setValue, isClassBased, bindingIndex);
  18913. }
  18914. oldKey = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex] : null;
  18915. newKey = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex] : null;
  18916. }
  18917. }
  18918. /**
  18919. * Update a simple (property name) styling.
  18920. *
  18921. * This function takes `prop` and updates the DOM to that value. The function takes the binding
  18922. * value as well as binding priority into consideration to determine which value should be written
  18923. * to DOM. (For example it may be determined that there is a higher priority overwrite which blocks
  18924. * the DOM write, or if the value goes to `undefined` a lower priority overwrite may be consulted.)
  18925. *
  18926. * @param tView Associated `TView.data` contains the linked list of binding priorities.
  18927. * @param tNode `TNode` where the binding is located.
  18928. * @param lView `LView` contains the values associated with other styling binding at this `TNode`.
  18929. * @param renderer Renderer to use if any updates.
  18930. * @param prop Either style property name or a class name.
  18931. * @param value Either style value for `prop` or `true`/`false` if `prop` is class.
  18932. * @param isClassBased `true` if `class` (`false` if `style`)
  18933. * @param bindingIndex Binding index of the binding.
  18934. */
  18935. function updateStyling(tView, tNode, lView, renderer, prop, value, isClassBased, bindingIndex) {
  18936. if (!(tNode.type & 3 /* TNodeType.AnyRNode */)) {
  18937. // It is possible to have styling on non-elements (such as ng-container).
  18938. // This is rare, but it does happen. In such a case, just ignore the binding.
  18939. return;
  18940. }
  18941. const tData = tView.data;
  18942. const tRange = tData[bindingIndex + 1];
  18943. const higherPriorityValue = getTStylingRangeNextDuplicate(tRange) ?
  18944. findStylingValue(tData, tNode, lView, prop, getTStylingRangeNext(tRange), isClassBased) :
  18945. undefined;
  18946. if (!isStylingValuePresent(higherPriorityValue)) {
  18947. // We don't have a next duplicate, or we did not find a duplicate value.
  18948. if (!isStylingValuePresent(value)) {
  18949. // We should delete current value or restore to lower priority value.
  18950. if (getTStylingRangePrevDuplicate(tRange)) {
  18951. // We have a possible prev duplicate, let's retrieve it.
  18952. value = findStylingValue(tData, null, lView, prop, bindingIndex, isClassBased);
  18953. }
  18954. }
  18955. const rNode = getNativeByIndex(getSelectedIndex(), lView);
  18956. applyStyling(renderer, isClassBased, rNode, prop, value);
  18957. }
  18958. }
  18959. /**
  18960. * Search for styling value with higher priority which is overwriting current value, or a
  18961. * value of lower priority to which we should fall back if the value is `undefined`.
  18962. *
  18963. * When value is being applied at a location, related values need to be consulted.
  18964. * - If there is a higher priority binding, we should be using that one instead.
  18965. * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp1`
  18966. * requires that we check `exp2` to see if it is set to value other than `undefined`.
  18967. * - If there is a lower priority binding and we are changing to `undefined`
  18968. * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp2` to
  18969. * `undefined` requires that we check `exp1` (and static values) and use that as new value.
  18970. *
  18971. * NOTE: The styling stores two values.
  18972. * 1. The raw value which came from the application is stored at `index + 0` location. (This value
  18973. * is used for dirty checking).
  18974. * 2. The normalized value is stored at `index + 1`.
  18975. *
  18976. * @param tData `TData` used for traversing the priority.
  18977. * @param tNode `TNode` to use for resolving static styling. Also controls search direction.
  18978. * - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true.
  18979. * If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value.
  18980. * - `null` search prev and go all the way to end. Return last value where
  18981. * `isStylingValuePresent(value)` is true.
  18982. * @param lView `LView` used for retrieving the actual values.
  18983. * @param prop Property which we are interested in.
  18984. * @param index Starting index in the linked list of styling bindings where the search should start.
  18985. * @param isClassBased `true` if `class` (`false` if `style`)
  18986. */
  18987. function findStylingValue(tData, tNode, lView, prop, index, isClassBased) {
  18988. // `TNode` to use for resolving static styling. Also controls search direction.
  18989. // - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true.
  18990. // If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value.
  18991. // - `null` search prev and go all the way to end. Return last value where
  18992. // `isStylingValuePresent(value)` is true.
  18993. const isPrevDirection = tNode === null;
  18994. let value = undefined;
  18995. while (index > 0) {
  18996. const rawKey = tData[index];
  18997. const containsStatics = Array.isArray(rawKey);
  18998. // Unwrap the key if we contain static values.
  18999. const key = containsStatics ? rawKey[1] : rawKey;
  19000. const isStylingMap = key === null;
  19001. let valueAtLViewIndex = lView[index + 1];
  19002. if (valueAtLViewIndex === NO_CHANGE) {
  19003. // In firstUpdatePass the styling instructions create a linked list of styling.
  19004. // On subsequent passes it is possible for a styling instruction to try to read a binding
  19005. // which
  19006. // has not yet executed. In that case we will find `NO_CHANGE` and we should assume that
  19007. // we have `undefined` (or empty array in case of styling-map instruction) instead. This
  19008. // allows the resolution to apply the value (which may later be overwritten when the
  19009. // binding actually executes.)
  19010. valueAtLViewIndex = isStylingMap ? EMPTY_ARRAY : undefined;
  19011. }
  19012. let currentValue = isStylingMap ? keyValueArrayGet(valueAtLViewIndex, prop) :
  19013. (key === prop ? valueAtLViewIndex : undefined);
  19014. if (containsStatics && !isStylingValuePresent(currentValue)) {
  19015. currentValue = keyValueArrayGet(rawKey, prop);
  19016. }
  19017. if (isStylingValuePresent(currentValue)) {
  19018. value = currentValue;
  19019. if (isPrevDirection) {
  19020. return value;
  19021. }
  19022. }
  19023. const tRange = tData[index + 1];
  19024. index = isPrevDirection ? getTStylingRangePrev(tRange) : getTStylingRangeNext(tRange);
  19025. }
  19026. if (tNode !== null) {
  19027. // in case where we are going in next direction AND we did not find anything, we need to
  19028. // consult residual styling
  19029. let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles;
  19030. if (residual != null /** OR residual !=== undefined */) {
  19031. value = keyValueArrayGet(residual, prop);
  19032. }
  19033. }
  19034. return value;
  19035. }
  19036. /**
  19037. * Determines if the binding value should be used (or if the value is 'undefined' and hence priority
  19038. * resolution should be used.)
  19039. *
  19040. * @param value Binding style value.
  19041. */
  19042. function isStylingValuePresent(value) {
  19043. // Currently only `undefined` value is considered non-binding. That is `undefined` says I don't
  19044. // have an opinion as to what this binding should be and you should consult other bindings by
  19045. // priority to determine the valid value.
  19046. // This is extracted into a single function so that we have a single place to control this.
  19047. return value !== undefined;
  19048. }
  19049. /**
  19050. * Normalizes and/or adds a suffix to the value.
  19051. *
  19052. * If value is `null`/`undefined` no suffix is added
  19053. * @param value
  19054. * @param suffix
  19055. */
  19056. function normalizeSuffix(value, suffix) {
  19057. if (value == null || value === '') {
  19058. // do nothing
  19059. // Do not add the suffix if the value is going to be empty.
  19060. // As it produce invalid CSS, which the browsers will automatically omit but Domino will not.
  19061. // Example: `"left": "px;"` instead of `"left": ""`.
  19062. }
  19063. else if (typeof suffix === 'string') {
  19064. value = value + suffix;
  19065. }
  19066. else if (typeof value === 'object') {
  19067. value = stringify(unwrapSafeValue(value));
  19068. }
  19069. return value;
  19070. }
  19071. /**
  19072. * Tests if the `TNode` has input shadow.
  19073. *
  19074. * An input shadow is when a directive steals (shadows) the input by using `@Input('style')` or
  19075. * `@Input('class')` as input.
  19076. *
  19077. * @param tNode `TNode` which we would like to see if it has shadow.
  19078. * @param isClassBased `true` if `class` (`false` if `style`)
  19079. */
  19080. function hasStylingInputShadow(tNode, isClassBased) {
  19081. return (tNode.flags & (isClassBased ? 8 /* TNodeFlags.hasClassInput */ : 16 /* TNodeFlags.hasStyleInput */)) !== 0;
  19082. }
  19083. /**
  19084. * Create static text node
  19085. *
  19086. * @param index Index of the node in the data array
  19087. * @param value Static string value to write.
  19088. *
  19089. * @codeGenApi
  19090. */
  19091. function ɵɵtext(index, value = '') {
  19092. const lView = getLView();
  19093. const tView = getTView();
  19094. const adjustedIndex = index + HEADER_OFFSET;
  19095. ngDevMode &&
  19096. assertEqual(getBindingIndex(), tView.bindingStartIndex, 'text nodes should be created before any bindings');
  19097. ngDevMode && assertIndexInRange(lView, adjustedIndex);
  19098. const tNode = tView.firstCreatePass ?
  19099. getOrCreateTNode(tView, adjustedIndex, 1 /* TNodeType.Text */, value, null) :
  19100. tView.data[adjustedIndex];
  19101. const textNative = _locateOrCreateTextNode(tView, lView, tNode, value, index);
  19102. lView[adjustedIndex] = textNative;
  19103. if (wasLastNodeCreated()) {
  19104. appendChild(tView, lView, textNative, tNode);
  19105. }
  19106. // Text nodes are self closing.
  19107. setCurrentTNode(tNode, false);
  19108. }
  19109. let _locateOrCreateTextNode = (tView, lView, tNode, value, index) => {
  19110. lastNodeWasCreated(true);
  19111. return createTextNode(lView[RENDERER], value);
  19112. };
  19113. /**
  19114. * Enables hydration code path (to lookup existing elements in DOM)
  19115. * in addition to the regular creation mode of text nodes.
  19116. */
  19117. function locateOrCreateTextNodeImpl(tView, lView, tNode, value, index) {
  19118. const hydrationInfo = lView[HYDRATION];
  19119. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode(hydrationInfo, index);
  19120. lastNodeWasCreated(isNodeCreationMode);
  19121. // Regular creation mode.
  19122. if (isNodeCreationMode) {
  19123. return createTextNode(lView[RENDERER], value);
  19124. }
  19125. // Hydration mode, looking up an existing element in DOM.
  19126. const textNative = locateNextRNode(hydrationInfo, tView, lView, tNode);
  19127. ngDevMode && validateMatchingNode(textNative, Node.TEXT_NODE, null, lView, tNode);
  19128. ngDevMode && markRNodeAsClaimedByHydration(textNative);
  19129. return textNative;
  19130. }
  19131. function enableLocateOrCreateTextNodeImpl() {
  19132. _locateOrCreateTextNode = locateOrCreateTextNodeImpl;
  19133. }
  19134. /**
  19135. *
  19136. * Update text content with a lone bound value
  19137. *
  19138. * Used when a text node has 1 interpolated value in it, an no additional text
  19139. * surrounds that interpolated value:
  19140. *
  19141. * ```html
  19142. * <div>{{v0}}</div>
  19143. * ```
  19144. *
  19145. * Its compiled representation is:
  19146. *
  19147. * ```ts
  19148. * ɵɵtextInterpolate(v0);
  19149. * ```
  19150. * @returns itself, so that it may be chained.
  19151. * @see textInterpolateV
  19152. * @codeGenApi
  19153. */
  19154. function ɵɵtextInterpolate(v0) {
  19155. ɵɵtextInterpolate1('', v0, '');
  19156. return ɵɵtextInterpolate;
  19157. }
  19158. /**
  19159. *
  19160. * Update text content with single bound value surrounded by other text.
  19161. *
  19162. * Used when a text node has 1 interpolated value in it:
  19163. *
  19164. * ```html
  19165. * <div>prefix{{v0}}suffix</div>
  19166. * ```
  19167. *
  19168. * Its compiled representation is:
  19169. *
  19170. * ```ts
  19171. * ɵɵtextInterpolate1('prefix', v0, 'suffix');
  19172. * ```
  19173. * @returns itself, so that it may be chained.
  19174. * @see textInterpolateV
  19175. * @codeGenApi
  19176. */
  19177. function ɵɵtextInterpolate1(prefix, v0, suffix) {
  19178. const lView = getLView();
  19179. const interpolated = interpolation1(lView, prefix, v0, suffix);
  19180. if (interpolated !== NO_CHANGE) {
  19181. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19182. }
  19183. return ɵɵtextInterpolate1;
  19184. }
  19185. /**
  19186. *
  19187. * Update text content with 2 bound values surrounded by other text.
  19188. *
  19189. * Used when a text node has 2 interpolated values in it:
  19190. *
  19191. * ```html
  19192. * <div>prefix{{v0}}-{{v1}}suffix</div>
  19193. * ```
  19194. *
  19195. * Its compiled representation is:
  19196. *
  19197. * ```ts
  19198. * ɵɵtextInterpolate2('prefix', v0, '-', v1, 'suffix');
  19199. * ```
  19200. * @returns itself, so that it may be chained.
  19201. * @see textInterpolateV
  19202. * @codeGenApi
  19203. */
  19204. function ɵɵtextInterpolate2(prefix, v0, i0, v1, suffix) {
  19205. const lView = getLView();
  19206. const interpolated = interpolation2(lView, prefix, v0, i0, v1, suffix);
  19207. if (interpolated !== NO_CHANGE) {
  19208. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19209. }
  19210. return ɵɵtextInterpolate2;
  19211. }
  19212. /**
  19213. *
  19214. * Update text content with 3 bound values surrounded by other text.
  19215. *
  19216. * Used when a text node has 3 interpolated values in it:
  19217. *
  19218. * ```html
  19219. * <div>prefix{{v0}}-{{v1}}-{{v2}}suffix</div>
  19220. * ```
  19221. *
  19222. * Its compiled representation is:
  19223. *
  19224. * ```ts
  19225. * ɵɵtextInterpolate3(
  19226. * 'prefix', v0, '-', v1, '-', v2, 'suffix');
  19227. * ```
  19228. * @returns itself, so that it may be chained.
  19229. * @see textInterpolateV
  19230. * @codeGenApi
  19231. */
  19232. function ɵɵtextInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
  19233. const lView = getLView();
  19234. const interpolated = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  19235. if (interpolated !== NO_CHANGE) {
  19236. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19237. }
  19238. return ɵɵtextInterpolate3;
  19239. }
  19240. /**
  19241. *
  19242. * Update text content with 4 bound values surrounded by other text.
  19243. *
  19244. * Used when a text node has 4 interpolated values in it:
  19245. *
  19246. * ```html
  19247. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix</div>
  19248. * ```
  19249. *
  19250. * Its compiled representation is:
  19251. *
  19252. * ```ts
  19253. * ɵɵtextInterpolate4(
  19254. * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  19255. * ```
  19256. * @returns itself, so that it may be chained.
  19257. * @see ɵɵtextInterpolateV
  19258. * @codeGenApi
  19259. */
  19260. function ɵɵtextInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  19261. const lView = getLView();
  19262. const interpolated = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  19263. if (interpolated !== NO_CHANGE) {
  19264. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19265. }
  19266. return ɵɵtextInterpolate4;
  19267. }
  19268. /**
  19269. *
  19270. * Update text content with 5 bound values surrounded by other text.
  19271. *
  19272. * Used when a text node has 5 interpolated values in it:
  19273. *
  19274. * ```html
  19275. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix</div>
  19276. * ```
  19277. *
  19278. * Its compiled representation is:
  19279. *
  19280. * ```ts
  19281. * ɵɵtextInterpolate5(
  19282. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  19283. * ```
  19284. * @returns itself, so that it may be chained.
  19285. * @see textInterpolateV
  19286. * @codeGenApi
  19287. */
  19288. function ɵɵtextInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  19289. const lView = getLView();
  19290. const interpolated = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  19291. if (interpolated !== NO_CHANGE) {
  19292. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19293. }
  19294. return ɵɵtextInterpolate5;
  19295. }
  19296. /**
  19297. *
  19298. * Update text content with 6 bound values surrounded by other text.
  19299. *
  19300. * Used when a text node has 6 interpolated values in it:
  19301. *
  19302. * ```html
  19303. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix</div>
  19304. * ```
  19305. *
  19306. * Its compiled representation is:
  19307. *
  19308. * ```ts
  19309. * ɵɵtextInterpolate6(
  19310. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  19311. * ```
  19312. *
  19313. * @param i4 Static value used for concatenation only.
  19314. * @param v5 Value checked for change. @returns itself, so that it may be chained.
  19315. * @see textInterpolateV
  19316. * @codeGenApi
  19317. */
  19318. function ɵɵtextInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  19319. const lView = getLView();
  19320. const interpolated = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  19321. if (interpolated !== NO_CHANGE) {
  19322. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19323. }
  19324. return ɵɵtextInterpolate6;
  19325. }
  19326. /**
  19327. *
  19328. * Update text content with 7 bound values surrounded by other text.
  19329. *
  19330. * Used when a text node has 7 interpolated values in it:
  19331. *
  19332. * ```html
  19333. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix</div>
  19334. * ```
  19335. *
  19336. * Its compiled representation is:
  19337. *
  19338. * ```ts
  19339. * ɵɵtextInterpolate7(
  19340. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  19341. * ```
  19342. * @returns itself, so that it may be chained.
  19343. * @see textInterpolateV
  19344. * @codeGenApi
  19345. */
  19346. function ɵɵtextInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  19347. const lView = getLView();
  19348. const interpolated = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  19349. if (interpolated !== NO_CHANGE) {
  19350. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19351. }
  19352. return ɵɵtextInterpolate7;
  19353. }
  19354. /**
  19355. *
  19356. * Update text content with 8 bound values surrounded by other text.
  19357. *
  19358. * Used when a text node has 8 interpolated values in it:
  19359. *
  19360. * ```html
  19361. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix</div>
  19362. * ```
  19363. *
  19364. * Its compiled representation is:
  19365. *
  19366. * ```ts
  19367. * ɵɵtextInterpolate8(
  19368. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  19369. * ```
  19370. * @returns itself, so that it may be chained.
  19371. * @see textInterpolateV
  19372. * @codeGenApi
  19373. */
  19374. function ɵɵtextInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  19375. const lView = getLView();
  19376. const interpolated = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  19377. if (interpolated !== NO_CHANGE) {
  19378. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19379. }
  19380. return ɵɵtextInterpolate8;
  19381. }
  19382. /**
  19383. * Update text content with 9 or more bound values other surrounded by text.
  19384. *
  19385. * Used when the number of interpolated values exceeds 8.
  19386. *
  19387. * ```html
  19388. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix</div>
  19389. * ```
  19390. *
  19391. * Its compiled representation is:
  19392. *
  19393. * ```ts
  19394. * ɵɵtextInterpolateV(
  19395. * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  19396. * 'suffix']);
  19397. * ```
  19398. *.
  19399. * @param values The collection of values and the strings in between those values, beginning with
  19400. * a string prefix and ending with a string suffix.
  19401. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  19402. *
  19403. * @returns itself, so that it may be chained.
  19404. * @codeGenApi
  19405. */
  19406. function ɵɵtextInterpolateV(values) {
  19407. const lView = getLView();
  19408. const interpolated = interpolationV(lView, values);
  19409. if (interpolated !== NO_CHANGE) {
  19410. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19411. }
  19412. return ɵɵtextInterpolateV;
  19413. }
  19414. /**
  19415. *
  19416. * Update an interpolated class on an element with single bound value surrounded by text.
  19417. *
  19418. * Used when the value passed to a property has 1 interpolated value in it:
  19419. *
  19420. * ```html
  19421. * <div class="prefix{{v0}}suffix"></div>
  19422. * ```
  19423. *
  19424. * Its compiled representation is:
  19425. *
  19426. * ```ts
  19427. * ɵɵclassMapInterpolate1('prefix', v0, 'suffix');
  19428. * ```
  19429. *
  19430. * @param prefix Static value used for concatenation only.
  19431. * @param v0 Value checked for change.
  19432. * @param suffix Static value used for concatenation only.
  19433. * @codeGenApi
  19434. */
  19435. function ɵɵclassMapInterpolate1(prefix, v0, suffix) {
  19436. const lView = getLView();
  19437. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  19438. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19439. }
  19440. /**
  19441. *
  19442. * Update an interpolated class on an element with 2 bound values surrounded by text.
  19443. *
  19444. * Used when the value passed to a property has 2 interpolated values in it:
  19445. *
  19446. * ```html
  19447. * <div class="prefix{{v0}}-{{v1}}suffix"></div>
  19448. * ```
  19449. *
  19450. * Its compiled representation is:
  19451. *
  19452. * ```ts
  19453. * ɵɵclassMapInterpolate2('prefix', v0, '-', v1, 'suffix');
  19454. * ```
  19455. *
  19456. * @param prefix Static value used for concatenation only.
  19457. * @param v0 Value checked for change.
  19458. * @param i0 Static value used for concatenation only.
  19459. * @param v1 Value checked for change.
  19460. * @param suffix Static value used for concatenation only.
  19461. * @codeGenApi
  19462. */
  19463. function ɵɵclassMapInterpolate2(prefix, v0, i0, v1, suffix) {
  19464. const lView = getLView();
  19465. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  19466. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19467. }
  19468. /**
  19469. *
  19470. * Update an interpolated class on an element with 3 bound values surrounded by text.
  19471. *
  19472. * Used when the value passed to a property has 3 interpolated values in it:
  19473. *
  19474. * ```html
  19475. * <div class="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  19476. * ```
  19477. *
  19478. * Its compiled representation is:
  19479. *
  19480. * ```ts
  19481. * ɵɵclassMapInterpolate3(
  19482. * 'prefix', v0, '-', v1, '-', v2, 'suffix');
  19483. * ```
  19484. *
  19485. * @param prefix Static value used for concatenation only.
  19486. * @param v0 Value checked for change.
  19487. * @param i0 Static value used for concatenation only.
  19488. * @param v1 Value checked for change.
  19489. * @param i1 Static value used for concatenation only.
  19490. * @param v2 Value checked for change.
  19491. * @param suffix Static value used for concatenation only.
  19492. * @codeGenApi
  19493. */
  19494. function ɵɵclassMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
  19495. const lView = getLView();
  19496. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  19497. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19498. }
  19499. /**
  19500. *
  19501. * Update an interpolated class on an element with 4 bound values surrounded by text.
  19502. *
  19503. * Used when the value passed to a property has 4 interpolated values in it:
  19504. *
  19505. * ```html
  19506. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  19507. * ```
  19508. *
  19509. * Its compiled representation is:
  19510. *
  19511. * ```ts
  19512. * ɵɵclassMapInterpolate4(
  19513. * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  19514. * ```
  19515. *
  19516. * @param prefix Static value used for concatenation only.
  19517. * @param v0 Value checked for change.
  19518. * @param i0 Static value used for concatenation only.
  19519. * @param v1 Value checked for change.
  19520. * @param i1 Static value used for concatenation only.
  19521. * @param v2 Value checked for change.
  19522. * @param i2 Static value used for concatenation only.
  19523. * @param v3 Value checked for change.
  19524. * @param suffix Static value used for concatenation only.
  19525. * @codeGenApi
  19526. */
  19527. function ɵɵclassMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  19528. const lView = getLView();
  19529. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  19530. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19531. }
  19532. /**
  19533. *
  19534. * Update an interpolated class on an element with 5 bound values surrounded by text.
  19535. *
  19536. * Used when the value passed to a property has 5 interpolated values in it:
  19537. *
  19538. * ```html
  19539. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  19540. * ```
  19541. *
  19542. * Its compiled representation is:
  19543. *
  19544. * ```ts
  19545. * ɵɵclassMapInterpolate5(
  19546. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  19547. * ```
  19548. *
  19549. * @param prefix Static value used for concatenation only.
  19550. * @param v0 Value checked for change.
  19551. * @param i0 Static value used for concatenation only.
  19552. * @param v1 Value checked for change.
  19553. * @param i1 Static value used for concatenation only.
  19554. * @param v2 Value checked for change.
  19555. * @param i2 Static value used for concatenation only.
  19556. * @param v3 Value checked for change.
  19557. * @param i3 Static value used for concatenation only.
  19558. * @param v4 Value checked for change.
  19559. * @param suffix Static value used for concatenation only.
  19560. * @codeGenApi
  19561. */
  19562. function ɵɵclassMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  19563. const lView = getLView();
  19564. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  19565. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19566. }
  19567. /**
  19568. *
  19569. * Update an interpolated class on an element with 6 bound values surrounded by text.
  19570. *
  19571. * Used when the value passed to a property has 6 interpolated values in it:
  19572. *
  19573. * ```html
  19574. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  19575. * ```
  19576. *
  19577. * Its compiled representation is:
  19578. *
  19579. * ```ts
  19580. * ɵɵclassMapInterpolate6(
  19581. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  19582. * ```
  19583. *
  19584. * @param prefix Static value used for concatenation only.
  19585. * @param v0 Value checked for change.
  19586. * @param i0 Static value used for concatenation only.
  19587. * @param v1 Value checked for change.
  19588. * @param i1 Static value used for concatenation only.
  19589. * @param v2 Value checked for change.
  19590. * @param i2 Static value used for concatenation only.
  19591. * @param v3 Value checked for change.
  19592. * @param i3 Static value used for concatenation only.
  19593. * @param v4 Value checked for change.
  19594. * @param i4 Static value used for concatenation only.
  19595. * @param v5 Value checked for change.
  19596. * @param suffix Static value used for concatenation only.
  19597. * @codeGenApi
  19598. */
  19599. function ɵɵclassMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  19600. const lView = getLView();
  19601. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  19602. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19603. }
  19604. /**
  19605. *
  19606. * Update an interpolated class on an element with 7 bound values surrounded by text.
  19607. *
  19608. * Used when the value passed to a property has 7 interpolated values in it:
  19609. *
  19610. * ```html
  19611. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  19612. * ```
  19613. *
  19614. * Its compiled representation is:
  19615. *
  19616. * ```ts
  19617. * ɵɵclassMapInterpolate7(
  19618. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  19619. * ```
  19620. *
  19621. * @param prefix Static value used for concatenation only.
  19622. * @param v0 Value checked for change.
  19623. * @param i0 Static value used for concatenation only.
  19624. * @param v1 Value checked for change.
  19625. * @param i1 Static value used for concatenation only.
  19626. * @param v2 Value checked for change.
  19627. * @param i2 Static value used for concatenation only.
  19628. * @param v3 Value checked for change.
  19629. * @param i3 Static value used for concatenation only.
  19630. * @param v4 Value checked for change.
  19631. * @param i4 Static value used for concatenation only.
  19632. * @param v5 Value checked for change.
  19633. * @param i5 Static value used for concatenation only.
  19634. * @param v6 Value checked for change.
  19635. * @param suffix Static value used for concatenation only.
  19636. * @codeGenApi
  19637. */
  19638. function ɵɵclassMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  19639. const lView = getLView();
  19640. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  19641. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19642. }
  19643. /**
  19644. *
  19645. * Update an interpolated class on an element with 8 bound values surrounded by text.
  19646. *
  19647. * Used when the value passed to a property has 8 interpolated values in it:
  19648. *
  19649. * ```html
  19650. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  19651. * ```
  19652. *
  19653. * Its compiled representation is:
  19654. *
  19655. * ```ts
  19656. * ɵɵclassMapInterpolate8(
  19657. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  19658. * ```
  19659. *
  19660. * @param prefix Static value used for concatenation only.
  19661. * @param v0 Value checked for change.
  19662. * @param i0 Static value used for concatenation only.
  19663. * @param v1 Value checked for change.
  19664. * @param i1 Static value used for concatenation only.
  19665. * @param v2 Value checked for change.
  19666. * @param i2 Static value used for concatenation only.
  19667. * @param v3 Value checked for change.
  19668. * @param i3 Static value used for concatenation only.
  19669. * @param v4 Value checked for change.
  19670. * @param i4 Static value used for concatenation only.
  19671. * @param v5 Value checked for change.
  19672. * @param i5 Static value used for concatenation only.
  19673. * @param v6 Value checked for change.
  19674. * @param i6 Static value used for concatenation only.
  19675. * @param v7 Value checked for change.
  19676. * @param suffix Static value used for concatenation only.
  19677. * @codeGenApi
  19678. */
  19679. function ɵɵclassMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  19680. const lView = getLView();
  19681. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  19682. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19683. }
  19684. /**
  19685. * Update an interpolated class on an element with 9 or more bound values surrounded by text.
  19686. *
  19687. * Used when the number of interpolated values exceeds 8.
  19688. *
  19689. * ```html
  19690. * <div
  19691. * class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
  19692. * ```
  19693. *
  19694. * Its compiled representation is:
  19695. *
  19696. * ```ts
  19697. * ɵɵclassMapInterpolateV(
  19698. * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  19699. * 'suffix']);
  19700. * ```
  19701. *.
  19702. * @param values The collection of values and the strings in-between those values, beginning with
  19703. * a string prefix and ending with a string suffix.
  19704. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  19705. * @codeGenApi
  19706. */
  19707. function ɵɵclassMapInterpolateV(values) {
  19708. const lView = getLView();
  19709. const interpolatedValue = interpolationV(lView, values);
  19710. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19711. }
  19712. /**
  19713. *
  19714. * Update an interpolated style on an element with single bound value surrounded by text.
  19715. *
  19716. * Used when the value passed to a property has 1 interpolated value in it:
  19717. *
  19718. * ```html
  19719. * <div style="key: {{v0}}suffix"></div>
  19720. * ```
  19721. *
  19722. * Its compiled representation is:
  19723. *
  19724. * ```ts
  19725. * ɵɵstyleMapInterpolate1('key: ', v0, 'suffix');
  19726. * ```
  19727. *
  19728. * @param prefix Static value used for concatenation only.
  19729. * @param v0 Value checked for change.
  19730. * @param suffix Static value used for concatenation only.
  19731. * @codeGenApi
  19732. */
  19733. function ɵɵstyleMapInterpolate1(prefix, v0, suffix) {
  19734. const lView = getLView();
  19735. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  19736. ɵɵstyleMap(interpolatedValue);
  19737. }
  19738. /**
  19739. *
  19740. * Update an interpolated style on an element with 2 bound values surrounded by text.
  19741. *
  19742. * Used when the value passed to a property has 2 interpolated values in it:
  19743. *
  19744. * ```html
  19745. * <div style="key: {{v0}}; key1: {{v1}}suffix"></div>
  19746. * ```
  19747. *
  19748. * Its compiled representation is:
  19749. *
  19750. * ```ts
  19751. * ɵɵstyleMapInterpolate2('key: ', v0, '; key1: ', v1, 'suffix');
  19752. * ```
  19753. *
  19754. * @param prefix Static value used for concatenation only.
  19755. * @param v0 Value checked for change.
  19756. * @param i0 Static value used for concatenation only.
  19757. * @param v1 Value checked for change.
  19758. * @param suffix Static value used for concatenation only.
  19759. * @codeGenApi
  19760. */
  19761. function ɵɵstyleMapInterpolate2(prefix, v0, i0, v1, suffix) {
  19762. const lView = getLView();
  19763. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  19764. ɵɵstyleMap(interpolatedValue);
  19765. }
  19766. /**
  19767. *
  19768. * Update an interpolated style on an element with 3 bound values surrounded by text.
  19769. *
  19770. * Used when the value passed to a property has 3 interpolated values in it:
  19771. *
  19772. * ```html
  19773. * <div style="key: {{v0}}; key2: {{v1}}; key2: {{v2}}suffix"></div>
  19774. * ```
  19775. *
  19776. * Its compiled representation is:
  19777. *
  19778. * ```ts
  19779. * ɵɵstyleMapInterpolate3(
  19780. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, 'suffix');
  19781. * ```
  19782. *
  19783. * @param prefix Static value used for concatenation only.
  19784. * @param v0 Value checked for change.
  19785. * @param i0 Static value used for concatenation only.
  19786. * @param v1 Value checked for change.
  19787. * @param i1 Static value used for concatenation only.
  19788. * @param v2 Value checked for change.
  19789. * @param suffix Static value used for concatenation only.
  19790. * @codeGenApi
  19791. */
  19792. function ɵɵstyleMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
  19793. const lView = getLView();
  19794. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  19795. ɵɵstyleMap(interpolatedValue);
  19796. }
  19797. /**
  19798. *
  19799. * Update an interpolated style on an element with 4 bound values surrounded by text.
  19800. *
  19801. * Used when the value passed to a property has 4 interpolated values in it:
  19802. *
  19803. * ```html
  19804. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}suffix"></div>
  19805. * ```
  19806. *
  19807. * Its compiled representation is:
  19808. *
  19809. * ```ts
  19810. * ɵɵstyleMapInterpolate4(
  19811. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, 'suffix');
  19812. * ```
  19813. *
  19814. * @param prefix Static value used for concatenation only.
  19815. * @param v0 Value checked for change.
  19816. * @param i0 Static value used for concatenation only.
  19817. * @param v1 Value checked for change.
  19818. * @param i1 Static value used for concatenation only.
  19819. * @param v2 Value checked for change.
  19820. * @param i2 Static value used for concatenation only.
  19821. * @param v3 Value checked for change.
  19822. * @param suffix Static value used for concatenation only.
  19823. * @codeGenApi
  19824. */
  19825. function ɵɵstyleMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  19826. const lView = getLView();
  19827. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  19828. ɵɵstyleMap(interpolatedValue);
  19829. }
  19830. /**
  19831. *
  19832. * Update an interpolated style on an element with 5 bound values surrounded by text.
  19833. *
  19834. * Used when the value passed to a property has 5 interpolated values in it:
  19835. *
  19836. * ```html
  19837. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}suffix"></div>
  19838. * ```
  19839. *
  19840. * Its compiled representation is:
  19841. *
  19842. * ```ts
  19843. * ɵɵstyleMapInterpolate5(
  19844. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, 'suffix');
  19845. * ```
  19846. *
  19847. * @param prefix Static value used for concatenation only.
  19848. * @param v0 Value checked for change.
  19849. * @param i0 Static value used for concatenation only.
  19850. * @param v1 Value checked for change.
  19851. * @param i1 Static value used for concatenation only.
  19852. * @param v2 Value checked for change.
  19853. * @param i2 Static value used for concatenation only.
  19854. * @param v3 Value checked for change.
  19855. * @param i3 Static value used for concatenation only.
  19856. * @param v4 Value checked for change.
  19857. * @param suffix Static value used for concatenation only.
  19858. * @codeGenApi
  19859. */
  19860. function ɵɵstyleMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  19861. const lView = getLView();
  19862. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  19863. ɵɵstyleMap(interpolatedValue);
  19864. }
  19865. /**
  19866. *
  19867. * Update an interpolated style on an element with 6 bound values surrounded by text.
  19868. *
  19869. * Used when the value passed to a property has 6 interpolated values in it:
  19870. *
  19871. * ```html
  19872. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}};
  19873. * key5: {{v5}}suffix"></div>
  19874. * ```
  19875. *
  19876. * Its compiled representation is:
  19877. *
  19878. * ```ts
  19879. * ɵɵstyleMapInterpolate6(
  19880. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  19881. * 'suffix');
  19882. * ```
  19883. *
  19884. * @param prefix Static value used for concatenation only.
  19885. * @param v0 Value checked for change.
  19886. * @param i0 Static value used for concatenation only.
  19887. * @param v1 Value checked for change.
  19888. * @param i1 Static value used for concatenation only.
  19889. * @param v2 Value checked for change.
  19890. * @param i2 Static value used for concatenation only.
  19891. * @param v3 Value checked for change.
  19892. * @param i3 Static value used for concatenation only.
  19893. * @param v4 Value checked for change.
  19894. * @param i4 Static value used for concatenation only.
  19895. * @param v5 Value checked for change.
  19896. * @param suffix Static value used for concatenation only.
  19897. * @codeGenApi
  19898. */
  19899. function ɵɵstyleMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  19900. const lView = getLView();
  19901. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  19902. ɵɵstyleMap(interpolatedValue);
  19903. }
  19904. /**
  19905. *
  19906. * Update an interpolated style on an element with 7 bound values surrounded by text.
  19907. *
  19908. * Used when the value passed to a property has 7 interpolated values in it:
  19909. *
  19910. * ```html
  19911. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
  19912. * key6: {{v6}}suffix"></div>
  19913. * ```
  19914. *
  19915. * Its compiled representation is:
  19916. *
  19917. * ```ts
  19918. * ɵɵstyleMapInterpolate7(
  19919. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  19920. * '; key6: ', v6, 'suffix');
  19921. * ```
  19922. *
  19923. * @param prefix Static value used for concatenation only.
  19924. * @param v0 Value checked for change.
  19925. * @param i0 Static value used for concatenation only.
  19926. * @param v1 Value checked for change.
  19927. * @param i1 Static value used for concatenation only.
  19928. * @param v2 Value checked for change.
  19929. * @param i2 Static value used for concatenation only.
  19930. * @param v3 Value checked for change.
  19931. * @param i3 Static value used for concatenation only.
  19932. * @param v4 Value checked for change.
  19933. * @param i4 Static value used for concatenation only.
  19934. * @param v5 Value checked for change.
  19935. * @param i5 Static value used for concatenation only.
  19936. * @param v6 Value checked for change.
  19937. * @param suffix Static value used for concatenation only.
  19938. * @codeGenApi
  19939. */
  19940. function ɵɵstyleMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  19941. const lView = getLView();
  19942. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  19943. ɵɵstyleMap(interpolatedValue);
  19944. }
  19945. /**
  19946. *
  19947. * Update an interpolated style on an element with 8 bound values surrounded by text.
  19948. *
  19949. * Used when the value passed to a property has 8 interpolated values in it:
  19950. *
  19951. * ```html
  19952. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
  19953. * key6: {{v6}}; key7: {{v7}}suffix"></div>
  19954. * ```
  19955. *
  19956. * Its compiled representation is:
  19957. *
  19958. * ```ts
  19959. * ɵɵstyleMapInterpolate8(
  19960. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  19961. * '; key6: ', v6, '; key7: ', v7, 'suffix');
  19962. * ```
  19963. *
  19964. * @param prefix Static value used for concatenation only.
  19965. * @param v0 Value checked for change.
  19966. * @param i0 Static value used for concatenation only.
  19967. * @param v1 Value checked for change.
  19968. * @param i1 Static value used for concatenation only.
  19969. * @param v2 Value checked for change.
  19970. * @param i2 Static value used for concatenation only.
  19971. * @param v3 Value checked for change.
  19972. * @param i3 Static value used for concatenation only.
  19973. * @param v4 Value checked for change.
  19974. * @param i4 Static value used for concatenation only.
  19975. * @param v5 Value checked for change.
  19976. * @param i5 Static value used for concatenation only.
  19977. * @param v6 Value checked for change.
  19978. * @param i6 Static value used for concatenation only.
  19979. * @param v7 Value checked for change.
  19980. * @param suffix Static value used for concatenation only.
  19981. * @codeGenApi
  19982. */
  19983. function ɵɵstyleMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  19984. const lView = getLView();
  19985. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  19986. ɵɵstyleMap(interpolatedValue);
  19987. }
  19988. /**
  19989. * Update an interpolated style on an element with 9 or more bound values surrounded by text.
  19990. *
  19991. * Used when the number of interpolated values exceeds 8.
  19992. *
  19993. * ```html
  19994. * <div
  19995. * class="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
  19996. * key6: {{v6}}; key7: {{v7}}; key8: {{v8}}; key9: {{v9}}suffix"></div>
  19997. * ```
  19998. *
  19999. * Its compiled representation is:
  20000. *
  20001. * ```ts
  20002. * ɵɵstyleMapInterpolateV(
  20003. * ['key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  20004. * '; key6: ', v6, '; key7: ', v7, '; key8: ', v8, '; key9: ', v9, 'suffix']);
  20005. * ```
  20006. *.
  20007. * @param values The collection of values and the strings in-between those values, beginning with
  20008. * a string prefix and ending with a string suffix.
  20009. * (e.g. `['prefix', value0, '; key2: ', value1, '; key2: ', value2, ..., value99, 'suffix']`)
  20010. * @codeGenApi
  20011. */
  20012. function ɵɵstyleMapInterpolateV(values) {
  20013. const lView = getLView();
  20014. const interpolatedValue = interpolationV(lView, values);
  20015. ɵɵstyleMap(interpolatedValue);
  20016. }
  20017. /**
  20018. *
  20019. * Update an interpolated style property on an element with single bound value surrounded by text.
  20020. *
  20021. * Used when the value passed to a property has 1 interpolated value in it:
  20022. *
  20023. * ```html
  20024. * <div style.color="prefix{{v0}}suffix"></div>
  20025. * ```
  20026. *
  20027. * Its compiled representation is:
  20028. *
  20029. * ```ts
  20030. * ɵɵstylePropInterpolate1(0, 'prefix', v0, 'suffix');
  20031. * ```
  20032. *
  20033. * @param styleIndex Index of style to update. This index value refers to the
  20034. * index of the style in the style bindings array that was passed into
  20035. * `styling`.
  20036. * @param prefix Static value used for concatenation only.
  20037. * @param v0 Value checked for change.
  20038. * @param suffix Static value used for concatenation only.
  20039. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20040. * @returns itself, so that it may be chained.
  20041. * @codeGenApi
  20042. */
  20043. function ɵɵstylePropInterpolate1(prop, prefix, v0, suffix, valueSuffix) {
  20044. const lView = getLView();
  20045. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  20046. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20047. return ɵɵstylePropInterpolate1;
  20048. }
  20049. /**
  20050. *
  20051. * Update an interpolated style property on an element with 2 bound values surrounded by text.
  20052. *
  20053. * Used when the value passed to a property has 2 interpolated values in it:
  20054. *
  20055. * ```html
  20056. * <div style.color="prefix{{v0}}-{{v1}}suffix"></div>
  20057. * ```
  20058. *
  20059. * Its compiled representation is:
  20060. *
  20061. * ```ts
  20062. * ɵɵstylePropInterpolate2(0, 'prefix', v0, '-', v1, 'suffix');
  20063. * ```
  20064. *
  20065. * @param styleIndex Index of style to update. This index value refers to the
  20066. * index of the style in the style bindings array that was passed into
  20067. * `styling`.
  20068. * @param prefix Static value used for concatenation only.
  20069. * @param v0 Value checked for change.
  20070. * @param i0 Static value used for concatenation only.
  20071. * @param v1 Value checked for change.
  20072. * @param suffix Static value used for concatenation only.
  20073. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20074. * @returns itself, so that it may be chained.
  20075. * @codeGenApi
  20076. */
  20077. function ɵɵstylePropInterpolate2(prop, prefix, v0, i0, v1, suffix, valueSuffix) {
  20078. const lView = getLView();
  20079. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  20080. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20081. return ɵɵstylePropInterpolate2;
  20082. }
  20083. /**
  20084. *
  20085. * Update an interpolated style property on an element with 3 bound values surrounded by text.
  20086. *
  20087. * Used when the value passed to a property has 3 interpolated values in it:
  20088. *
  20089. * ```html
  20090. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  20091. * ```
  20092. *
  20093. * Its compiled representation is:
  20094. *
  20095. * ```ts
  20096. * ɵɵstylePropInterpolate3(0, 'prefix', v0, '-', v1, '-', v2, 'suffix');
  20097. * ```
  20098. *
  20099. * @param styleIndex Index of style to update. This index value refers to the
  20100. * index of the style in the style bindings array that was passed into
  20101. * `styling`.
  20102. * @param prefix Static value used for concatenation only.
  20103. * @param v0 Value checked for change.
  20104. * @param i0 Static value used for concatenation only.
  20105. * @param v1 Value checked for change.
  20106. * @param i1 Static value used for concatenation only.
  20107. * @param v2 Value checked for change.
  20108. * @param suffix Static value used for concatenation only.
  20109. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20110. * @returns itself, so that it may be chained.
  20111. * @codeGenApi
  20112. */
  20113. function ɵɵstylePropInterpolate3(prop, prefix, v0, i0, v1, i1, v2, suffix, valueSuffix) {
  20114. const lView = getLView();
  20115. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  20116. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20117. return ɵɵstylePropInterpolate3;
  20118. }
  20119. /**
  20120. *
  20121. * Update an interpolated style property on an element with 4 bound values surrounded by text.
  20122. *
  20123. * Used when the value passed to a property has 4 interpolated values in it:
  20124. *
  20125. * ```html
  20126. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  20127. * ```
  20128. *
  20129. * Its compiled representation is:
  20130. *
  20131. * ```ts
  20132. * ɵɵstylePropInterpolate4(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  20133. * ```
  20134. *
  20135. * @param styleIndex Index of style to update. This index value refers to the
  20136. * index of the style in the style bindings array that was passed into
  20137. * `styling`.
  20138. * @param prefix Static value used for concatenation only.
  20139. * @param v0 Value checked for change.
  20140. * @param i0 Static value used for concatenation only.
  20141. * @param v1 Value checked for change.
  20142. * @param i1 Static value used for concatenation only.
  20143. * @param v2 Value checked for change.
  20144. * @param i2 Static value used for concatenation only.
  20145. * @param v3 Value checked for change.
  20146. * @param suffix Static value used for concatenation only.
  20147. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20148. * @returns itself, so that it may be chained.
  20149. * @codeGenApi
  20150. */
  20151. function ɵɵstylePropInterpolate4(prop, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, valueSuffix) {
  20152. const lView = getLView();
  20153. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  20154. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20155. return ɵɵstylePropInterpolate4;
  20156. }
  20157. /**
  20158. *
  20159. * Update an interpolated style property on an element with 5 bound values surrounded by text.
  20160. *
  20161. * Used when the value passed to a property has 5 interpolated values in it:
  20162. *
  20163. * ```html
  20164. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  20165. * ```
  20166. *
  20167. * Its compiled representation is:
  20168. *
  20169. * ```ts
  20170. * ɵɵstylePropInterpolate5(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  20171. * ```
  20172. *
  20173. * @param styleIndex Index of style to update. This index value refers to the
  20174. * index of the style in the style bindings array that was passed into
  20175. * `styling`.
  20176. * @param prefix Static value used for concatenation only.
  20177. * @param v0 Value checked for change.
  20178. * @param i0 Static value used for concatenation only.
  20179. * @param v1 Value checked for change.
  20180. * @param i1 Static value used for concatenation only.
  20181. * @param v2 Value checked for change.
  20182. * @param i2 Static value used for concatenation only.
  20183. * @param v3 Value checked for change.
  20184. * @param i3 Static value used for concatenation only.
  20185. * @param v4 Value checked for change.
  20186. * @param suffix Static value used for concatenation only.
  20187. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20188. * @returns itself, so that it may be chained.
  20189. * @codeGenApi
  20190. */
  20191. function ɵɵstylePropInterpolate5(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, valueSuffix) {
  20192. const lView = getLView();
  20193. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  20194. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20195. return ɵɵstylePropInterpolate5;
  20196. }
  20197. /**
  20198. *
  20199. * Update an interpolated style property on an element with 6 bound values surrounded by text.
  20200. *
  20201. * Used when the value passed to a property has 6 interpolated values in it:
  20202. *
  20203. * ```html
  20204. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  20205. * ```
  20206. *
  20207. * Its compiled representation is:
  20208. *
  20209. * ```ts
  20210. * ɵɵstylePropInterpolate6(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  20211. * ```
  20212. *
  20213. * @param styleIndex Index of style to update. This index value refers to the
  20214. * index of the style in the style bindings array that was passed into
  20215. * `styling`.
  20216. * @param prefix Static value used for concatenation only.
  20217. * @param v0 Value checked for change.
  20218. * @param i0 Static value used for concatenation only.
  20219. * @param v1 Value checked for change.
  20220. * @param i1 Static value used for concatenation only.
  20221. * @param v2 Value checked for change.
  20222. * @param i2 Static value used for concatenation only.
  20223. * @param v3 Value checked for change.
  20224. * @param i3 Static value used for concatenation only.
  20225. * @param v4 Value checked for change.
  20226. * @param i4 Static value used for concatenation only.
  20227. * @param v5 Value checked for change.
  20228. * @param suffix Static value used for concatenation only.
  20229. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20230. * @returns itself, so that it may be chained.
  20231. * @codeGenApi
  20232. */
  20233. function ɵɵstylePropInterpolate6(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, valueSuffix) {
  20234. const lView = getLView();
  20235. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  20236. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20237. return ɵɵstylePropInterpolate6;
  20238. }
  20239. /**
  20240. *
  20241. * Update an interpolated style property on an element with 7 bound values surrounded by text.
  20242. *
  20243. * Used when the value passed to a property has 7 interpolated values in it:
  20244. *
  20245. * ```html
  20246. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  20247. * ```
  20248. *
  20249. * Its compiled representation is:
  20250. *
  20251. * ```ts
  20252. * ɵɵstylePropInterpolate7(
  20253. * 0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  20254. * ```
  20255. *
  20256. * @param styleIndex Index of style to update. This index value refers to the
  20257. * index of the style in the style bindings array that was passed into
  20258. * `styling`.
  20259. * @param prefix Static value used for concatenation only.
  20260. * @param v0 Value checked for change.
  20261. * @param i0 Static value used for concatenation only.
  20262. * @param v1 Value checked for change.
  20263. * @param i1 Static value used for concatenation only.
  20264. * @param v2 Value checked for change.
  20265. * @param i2 Static value used for concatenation only.
  20266. * @param v3 Value checked for change.
  20267. * @param i3 Static value used for concatenation only.
  20268. * @param v4 Value checked for change.
  20269. * @param i4 Static value used for concatenation only.
  20270. * @param v5 Value checked for change.
  20271. * @param i5 Static value used for concatenation only.
  20272. * @param v6 Value checked for change.
  20273. * @param suffix Static value used for concatenation only.
  20274. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20275. * @returns itself, so that it may be chained.
  20276. * @codeGenApi
  20277. */
  20278. function ɵɵstylePropInterpolate7(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, valueSuffix) {
  20279. const lView = getLView();
  20280. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  20281. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20282. return ɵɵstylePropInterpolate7;
  20283. }
  20284. /**
  20285. *
  20286. * Update an interpolated style property on an element with 8 bound values surrounded by text.
  20287. *
  20288. * Used when the value passed to a property has 8 interpolated values in it:
  20289. *
  20290. * ```html
  20291. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  20292. * ```
  20293. *
  20294. * Its compiled representation is:
  20295. *
  20296. * ```ts
  20297. * ɵɵstylePropInterpolate8(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6,
  20298. * '-', v7, 'suffix');
  20299. * ```
  20300. *
  20301. * @param styleIndex Index of style to update. This index value refers to the
  20302. * index of the style in the style bindings array that was passed into
  20303. * `styling`.
  20304. * @param prefix Static value used for concatenation only.
  20305. * @param v0 Value checked for change.
  20306. * @param i0 Static value used for concatenation only.
  20307. * @param v1 Value checked for change.
  20308. * @param i1 Static value used for concatenation only.
  20309. * @param v2 Value checked for change.
  20310. * @param i2 Static value used for concatenation only.
  20311. * @param v3 Value checked for change.
  20312. * @param i3 Static value used for concatenation only.
  20313. * @param v4 Value checked for change.
  20314. * @param i4 Static value used for concatenation only.
  20315. * @param v5 Value checked for change.
  20316. * @param i5 Static value used for concatenation only.
  20317. * @param v6 Value checked for change.
  20318. * @param i6 Static value used for concatenation only.
  20319. * @param v7 Value checked for change.
  20320. * @param suffix Static value used for concatenation only.
  20321. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20322. * @returns itself, so that it may be chained.
  20323. * @codeGenApi
  20324. */
  20325. function ɵɵstylePropInterpolate8(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, valueSuffix) {
  20326. const lView = getLView();
  20327. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  20328. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20329. return ɵɵstylePropInterpolate8;
  20330. }
  20331. /**
  20332. * Update an interpolated style property on an element with 9 or more bound values surrounded by
  20333. * text.
  20334. *
  20335. * Used when the number of interpolated values exceeds 8.
  20336. *
  20337. * ```html
  20338. * <div
  20339. * style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix">
  20340. * </div>
  20341. * ```
  20342. *
  20343. * Its compiled representation is:
  20344. *
  20345. * ```ts
  20346. * ɵɵstylePropInterpolateV(
  20347. * 0, ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  20348. * 'suffix']);
  20349. * ```
  20350. *
  20351. * @param styleIndex Index of style to update. This index value refers to the
  20352. * index of the style in the style bindings array that was passed into
  20353. * `styling`..
  20354. * @param values The collection of values and the strings in-between those values, beginning with
  20355. * a string prefix and ending with a string suffix.
  20356. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  20357. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20358. * @returns itself, so that it may be chained.
  20359. * @codeGenApi
  20360. */
  20361. function ɵɵstylePropInterpolateV(prop, values, valueSuffix) {
  20362. const lView = getLView();
  20363. const interpolatedValue = interpolationV(lView, values);
  20364. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20365. return ɵɵstylePropInterpolateV;
  20366. }
  20367. /**
  20368. * Update a property on a host element. Only applies to native node properties, not inputs.
  20369. *
  20370. * Operates on the element selected by index via the {@link select} instruction.
  20371. *
  20372. * @param propName Name of property. Because it is going to DOM, this is not subject to
  20373. * renaming as part of minification.
  20374. * @param value New value to write.
  20375. * @param sanitizer An optional function used to sanitize the value.
  20376. * @returns This function returns itself so that it may be chained
  20377. * (e.g. `property('name', ctx.name)('title', ctx.title)`)
  20378. *
  20379. * @codeGenApi
  20380. */
  20381. function ɵɵhostProperty(propName, value, sanitizer) {
  20382. const lView = getLView();
  20383. const bindingIndex = nextBindingIndex();
  20384. if (bindingUpdated(lView, bindingIndex, value)) {
  20385. const tView = getTView();
  20386. const tNode = getSelectedTNode();
  20387. elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, true);
  20388. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
  20389. }
  20390. return ɵɵhostProperty;
  20391. }
  20392. /**
  20393. * Updates a synthetic host binding (e.g. `[@foo]`) on a component or directive.
  20394. *
  20395. * This instruction is for compatibility purposes and is designed to ensure that a
  20396. * synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
  20397. * the component's renderer. Normally all host bindings are evaluated with the parent
  20398. * component's renderer, but, in the case of animation @triggers, they need to be
  20399. * evaluated with the sub component's renderer (because that's where the animation
  20400. * triggers are defined).
  20401. *
  20402. * Do not use this instruction as a replacement for `elementProperty`. This instruction
  20403. * only exists to ensure compatibility with the ViewEngine's host binding behavior.
  20404. *
  20405. * @param index The index of the element to update in the data array
  20406. * @param propName Name of property. Because it is going to DOM, this is not subject to
  20407. * renaming as part of minification.
  20408. * @param value New value to write.
  20409. * @param sanitizer An optional function used to sanitize the value.
  20410. *
  20411. * @codeGenApi
  20412. */
  20413. function ɵɵsyntheticHostProperty(propName, value, sanitizer) {
  20414. const lView = getLView();
  20415. const bindingIndex = nextBindingIndex();
  20416. if (bindingUpdated(lView, bindingIndex, value)) {
  20417. const tView = getTView();
  20418. const tNode = getSelectedTNode();
  20419. const currentDef = getCurrentDirectiveDef(tView.data);
  20420. const renderer = loadComponentRenderer(currentDef, tNode, lView);
  20421. elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, true);
  20422. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
  20423. }
  20424. return ɵɵsyntheticHostProperty;
  20425. }
  20426. /**
  20427. * NOTE: changes to the `ngI18nClosureMode` name must be synced with `compiler-cli/src/tooling.ts`.
  20428. */
  20429. if (typeof ngI18nClosureMode === 'undefined') {
  20430. // These property accesses can be ignored because ngI18nClosureMode will be set to false
  20431. // when optimizing code and the whole if statement will be dropped.
  20432. // Make sure to refer to ngI18nClosureMode as ['ngI18nClosureMode'] for closure.
  20433. // NOTE: we need to have it in IIFE so that the tree-shaker is happy.
  20434. (function () {
  20435. // tslint:disable-next-line:no-toplevel-property-access
  20436. _global['ngI18nClosureMode'] =
  20437. // TODO(FW-1250): validate that this actually, you know, works.
  20438. // tslint:disable-next-line:no-toplevel-property-access
  20439. typeof goog !== 'undefined' && typeof goog.getMsg === 'function';
  20440. })();
  20441. }
  20442. // THIS CODE IS GENERATED - DO NOT MODIFY.
  20443. const u = undefined;
  20444. function plural(val) {
  20445. const n = val, i = Math.floor(Math.abs(val)), v = val.toString().replace(/^[^.]*\.?/, '').length;
  20446. if (i === 1 && v === 0)
  20447. return 1;
  20448. return 5;
  20449. }
  20450. var localeEn = ["en", [["a", "p"], ["AM", "PM"], u], [["AM", "PM"], u, u], [["S", "M", "T", "W", "T", "F", "S"], ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"]], u, [["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"], ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]], u, [["B", "A"], ["BC", "AD"], ["Before Christ", "Anno Domini"]], 0, [6, 0], ["M/d/yy", "MMM d, y", "MMMM d, y", "EEEE, MMMM d, y"], ["h:mm a", "h:mm:ss a", "h:mm:ss a z", "h:mm:ss a zzzz"], ["{1}, {0}", u, "{1} 'at' {0}", u], [".", ",", ";", "%", "+", "-", "E", "×", "‰", "∞", "NaN", ":"], ["#,##0.###", "#,##0%", "¤#,##0.00", "#E0"], "USD", "$", "US Dollar", {}, "ltr", plural];
  20451. /**
  20452. * This const is used to store the locale data registered with `registerLocaleData`
  20453. */
  20454. let LOCALE_DATA = {};
  20455. /**
  20456. * Register locale data to be used internally by Angular. See the
  20457. * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale
  20458. * data.
  20459. *
  20460. * The signature `registerLocaleData(data: any, extraData?: any)` is deprecated since v5.1
  20461. */
  20462. function registerLocaleData(data, localeId, extraData) {
  20463. if (typeof localeId !== 'string') {
  20464. extraData = localeId;
  20465. localeId = data[LocaleDataIndex.LocaleId];
  20466. }
  20467. localeId = localeId.toLowerCase().replace(/_/g, '-');
  20468. LOCALE_DATA[localeId] = data;
  20469. if (extraData) {
  20470. LOCALE_DATA[localeId][LocaleDataIndex.ExtraData] = extraData;
  20471. }
  20472. }
  20473. /**
  20474. * Finds the locale data for a given locale.
  20475. *
  20476. * @param locale The locale code.
  20477. * @returns The locale data.
  20478. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
  20479. */
  20480. function findLocaleData(locale) {
  20481. const normalizedLocale = normalizeLocale(locale);
  20482. let match = getLocaleData(normalizedLocale);
  20483. if (match) {
  20484. return match;
  20485. }
  20486. // let's try to find a parent locale
  20487. const parentLocale = normalizedLocale.split('-')[0];
  20488. match = getLocaleData(parentLocale);
  20489. if (match) {
  20490. return match;
  20491. }
  20492. if (parentLocale === 'en') {
  20493. return localeEn;
  20494. }
  20495. throw new RuntimeError(701 /* RuntimeErrorCode.MISSING_LOCALE_DATA */, ngDevMode && `Missing locale data for the locale "${locale}".`);
  20496. }
  20497. /**
  20498. * Retrieves the default currency code for the given locale.
  20499. *
  20500. * The default is defined as the first currency which is still in use.
  20501. *
  20502. * @param locale The code of the locale whose currency code we want.
  20503. * @returns The code of the default currency for the given locale.
  20504. *
  20505. */
  20506. function getLocaleCurrencyCode(locale) {
  20507. const data = findLocaleData(locale);
  20508. return data[LocaleDataIndex.CurrencyCode] || null;
  20509. }
  20510. /**
  20511. * Retrieves the plural function used by ICU expressions to determine the plural case to use
  20512. * for a given locale.
  20513. * @param locale A locale code for the locale format rules to use.
  20514. * @returns The plural function for the locale.
  20515. * @see {@link NgPlural}
  20516. * @see [Internationalization (i18n) Guide](/guide/i18n-overview)
  20517. */
  20518. function getLocalePluralCase(locale) {
  20519. const data = findLocaleData(locale);
  20520. return data[LocaleDataIndex.PluralCase];
  20521. }
  20522. /**
  20523. * Helper function to get the given `normalizedLocale` from `LOCALE_DATA`
  20524. * or from the global `ng.common.locale`.
  20525. */
  20526. function getLocaleData(normalizedLocale) {
  20527. if (!(normalizedLocale in LOCALE_DATA)) {
  20528. LOCALE_DATA[normalizedLocale] = _global.ng && _global.ng.common && _global.ng.common.locales &&
  20529. _global.ng.common.locales[normalizedLocale];
  20530. }
  20531. return LOCALE_DATA[normalizedLocale];
  20532. }
  20533. /**
  20534. * Helper function to remove all the locale data from `LOCALE_DATA`.
  20535. */
  20536. function unregisterAllLocaleData() {
  20537. LOCALE_DATA = {};
  20538. }
  20539. /**
  20540. * Index of each type of locale data from the locale data array
  20541. */
  20542. var LocaleDataIndex;
  20543. (function (LocaleDataIndex) {
  20544. LocaleDataIndex[LocaleDataIndex["LocaleId"] = 0] = "LocaleId";
  20545. LocaleDataIndex[LocaleDataIndex["DayPeriodsFormat"] = 1] = "DayPeriodsFormat";
  20546. LocaleDataIndex[LocaleDataIndex["DayPeriodsStandalone"] = 2] = "DayPeriodsStandalone";
  20547. LocaleDataIndex[LocaleDataIndex["DaysFormat"] = 3] = "DaysFormat";
  20548. LocaleDataIndex[LocaleDataIndex["DaysStandalone"] = 4] = "DaysStandalone";
  20549. LocaleDataIndex[LocaleDataIndex["MonthsFormat"] = 5] = "MonthsFormat";
  20550. LocaleDataIndex[LocaleDataIndex["MonthsStandalone"] = 6] = "MonthsStandalone";
  20551. LocaleDataIndex[LocaleDataIndex["Eras"] = 7] = "Eras";
  20552. LocaleDataIndex[LocaleDataIndex["FirstDayOfWeek"] = 8] = "FirstDayOfWeek";
  20553. LocaleDataIndex[LocaleDataIndex["WeekendRange"] = 9] = "WeekendRange";
  20554. LocaleDataIndex[LocaleDataIndex["DateFormat"] = 10] = "DateFormat";
  20555. LocaleDataIndex[LocaleDataIndex["TimeFormat"] = 11] = "TimeFormat";
  20556. LocaleDataIndex[LocaleDataIndex["DateTimeFormat"] = 12] = "DateTimeFormat";
  20557. LocaleDataIndex[LocaleDataIndex["NumberSymbols"] = 13] = "NumberSymbols";
  20558. LocaleDataIndex[LocaleDataIndex["NumberFormats"] = 14] = "NumberFormats";
  20559. LocaleDataIndex[LocaleDataIndex["CurrencyCode"] = 15] = "CurrencyCode";
  20560. LocaleDataIndex[LocaleDataIndex["CurrencySymbol"] = 16] = "CurrencySymbol";
  20561. LocaleDataIndex[LocaleDataIndex["CurrencyName"] = 17] = "CurrencyName";
  20562. LocaleDataIndex[LocaleDataIndex["Currencies"] = 18] = "Currencies";
  20563. LocaleDataIndex[LocaleDataIndex["Directionality"] = 19] = "Directionality";
  20564. LocaleDataIndex[LocaleDataIndex["PluralCase"] = 20] = "PluralCase";
  20565. LocaleDataIndex[LocaleDataIndex["ExtraData"] = 21] = "ExtraData";
  20566. })(LocaleDataIndex || (LocaleDataIndex = {}));
  20567. /**
  20568. * Returns the canonical form of a locale name - lowercase with `_` replaced with `-`.
  20569. */
  20570. function normalizeLocale(locale) {
  20571. return locale.toLowerCase().replace(/_/g, '-');
  20572. }
  20573. const pluralMapping = ['zero', 'one', 'two', 'few', 'many'];
  20574. /**
  20575. * Returns the plural case based on the locale
  20576. */
  20577. function getPluralCase(value, locale) {
  20578. const plural = getLocalePluralCase(locale)(parseInt(value, 10));
  20579. const result = pluralMapping[plural];
  20580. return (result !== undefined) ? result : 'other';
  20581. }
  20582. /**
  20583. * The locale id that the application is using by default (for translations and ICU expressions).
  20584. */
  20585. const DEFAULT_LOCALE_ID = 'en-US';
  20586. /**
  20587. * USD currency code that the application uses by default for CurrencyPipe when no
  20588. * DEFAULT_CURRENCY_CODE is provided.
  20589. */
  20590. const USD_CURRENCY_CODE = 'USD';
  20591. /**
  20592. * Marks that the next string is an element name.
  20593. *
  20594. * See `I18nMutateOpCodes` documentation.
  20595. */
  20596. const ELEMENT_MARKER = {
  20597. marker: 'element'
  20598. };
  20599. /**
  20600. * Marks that the next string is comment text need for ICU.
  20601. *
  20602. * See `I18nMutateOpCodes` documentation.
  20603. */
  20604. const ICU_MARKER = {
  20605. marker: 'ICU'
  20606. };
  20607. /**
  20608. * See `I18nCreateOpCodes`
  20609. */
  20610. var I18nCreateOpCode;
  20611. (function (I18nCreateOpCode) {
  20612. /**
  20613. * Number of bits to shift index so that it can be combined with the `APPEND_EAGERLY` and
  20614. * `COMMENT`.
  20615. */
  20616. I18nCreateOpCode[I18nCreateOpCode["SHIFT"] = 2] = "SHIFT";
  20617. /**
  20618. * Should the node be appended to parent immediately after creation.
  20619. */
  20620. I18nCreateOpCode[I18nCreateOpCode["APPEND_EAGERLY"] = 1] = "APPEND_EAGERLY";
  20621. /**
  20622. * If set the node should be comment (rather than a text) node.
  20623. */
  20624. I18nCreateOpCode[I18nCreateOpCode["COMMENT"] = 2] = "COMMENT";
  20625. })(I18nCreateOpCode || (I18nCreateOpCode = {}));
  20626. // Note: This hack is necessary so we don't erroneously get a circular dependency
  20627. // failure based on types.
  20628. const unusedValueExportToPlacateAjd = 1;
  20629. /**
  20630. * The locale id that the application is currently using (for translations and ICU expressions).
  20631. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
  20632. * but is now defined as a global value.
  20633. */
  20634. let LOCALE_ID = DEFAULT_LOCALE_ID;
  20635. /**
  20636. * Sets the locale id that will be used for translations and ICU expressions.
  20637. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
  20638. * but is now defined as a global value.
  20639. *
  20640. * @param localeId
  20641. */
  20642. function setLocaleId(localeId) {
  20643. assertDefined(localeId, `Expected localeId to be defined`);
  20644. if (typeof localeId === 'string') {
  20645. LOCALE_ID = localeId.toLowerCase().replace(/_/g, '-');
  20646. }
  20647. }
  20648. /**
  20649. * Gets the locale id that will be used for translations and ICU expressions.
  20650. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
  20651. * but is now defined as a global value.
  20652. */
  20653. function getLocaleId() {
  20654. return LOCALE_ID;
  20655. }
  20656. /**
  20657. * Find a node in front of which `currentTNode` should be inserted (takes i18n into account).
  20658. *
  20659. * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
  20660. * takes `TNode.insertBeforeIndex` into account.
  20661. *
  20662. * @param parentTNode parent `TNode`
  20663. * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
  20664. * @param lView current `LView`
  20665. */
  20666. function getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView) {
  20667. const tNodeInsertBeforeIndex = currentTNode.insertBeforeIndex;
  20668. const insertBeforeIndex = Array.isArray(tNodeInsertBeforeIndex) ? tNodeInsertBeforeIndex[0] : tNodeInsertBeforeIndex;
  20669. if (insertBeforeIndex === null) {
  20670. return getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView);
  20671. }
  20672. else {
  20673. ngDevMode && assertIndexInRange(lView, insertBeforeIndex);
  20674. return unwrapRNode(lView[insertBeforeIndex]);
  20675. }
  20676. }
  20677. /**
  20678. * Process `TNode.insertBeforeIndex` by adding i18n text nodes.
  20679. *
  20680. * See `TNode.insertBeforeIndex`
  20681. */
  20682. function processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRElement) {
  20683. const tNodeInsertBeforeIndex = childTNode.insertBeforeIndex;
  20684. if (Array.isArray(tNodeInsertBeforeIndex)) {
  20685. // An array indicates that there are i18n nodes that need to be added as children of this
  20686. // `childRNode`. These i18n nodes were created before this `childRNode` was available and so
  20687. // only now can be added. The first element of the array is the normal index where we should
  20688. // insert the `childRNode`. Additional elements are the extra nodes to be added as children of
  20689. // `childRNode`.
  20690. ngDevMode && assertDomNode(childRNode);
  20691. let i18nParent = childRNode;
  20692. let anchorRNode = null;
  20693. if (!(childTNode.type & 3 /* TNodeType.AnyRNode */)) {
  20694. anchorRNode = i18nParent;
  20695. i18nParent = parentRElement;
  20696. }
  20697. if (i18nParent !== null && childTNode.componentOffset === -1) {
  20698. for (let i = 1; i < tNodeInsertBeforeIndex.length; i++) {
  20699. // No need to `unwrapRNode` because all of the indexes point to i18n text nodes.
  20700. // see `assertDomNode` below.
  20701. const i18nChild = lView[tNodeInsertBeforeIndex[i]];
  20702. nativeInsertBefore(renderer, i18nParent, i18nChild, anchorRNode, false);
  20703. }
  20704. }
  20705. }
  20706. }
  20707. /**
  20708. * Add `tNode` to `previousTNodes` list and update relevant `TNode`s in `previousTNodes` list
  20709. * `tNode.insertBeforeIndex`.
  20710. *
  20711. * Things to keep in mind:
  20712. * 1. All i18n text nodes are encoded as `TNodeType.Element` and are created eagerly by the
  20713. * `ɵɵi18nStart` instruction.
  20714. * 2. All `TNodeType.Placeholder` `TNodes` are elements which will be created later by
  20715. * `ɵɵelementStart` instruction.
  20716. * 3. `ɵɵelementStart` instruction will create `TNode`s in the ascending `TNode.index` order. (So a
  20717. * smaller index `TNode` is guaranteed to be created before a larger one)
  20718. *
  20719. * We use the above three invariants to determine `TNode.insertBeforeIndex`.
  20720. *
  20721. * In an ideal world `TNode.insertBeforeIndex` would always be `TNode.next.index`. However,
  20722. * this will not work because `TNode.next.index` may be larger than `TNode.index` which means that
  20723. * the next node is not yet created and therefore we can't insert in front of it.
  20724. *
  20725. * Rule1: `TNode.insertBeforeIndex = null` if `TNode.next === null` (Initial condition, as we don't
  20726. * know if there will be further `TNode`s inserted after.)
  20727. * Rule2: If `previousTNode` is created after the `tNode` being inserted, then
  20728. * `previousTNode.insertBeforeNode = tNode.index` (So when a new `tNode` is added we check
  20729. * previous to see if we can update its `insertBeforeTNode`)
  20730. *
  20731. * See `TNode.insertBeforeIndex` for more context.
  20732. *
  20733. * @param previousTNodes A list of previous TNodes so that we can easily traverse `TNode`s in
  20734. * reverse order. (If `TNode` would have `previous` this would not be necessary.)
  20735. * @param newTNode A TNode to add to the `previousTNodes` list.
  20736. */
  20737. function addTNodeAndUpdateInsertBeforeIndex(previousTNodes, newTNode) {
  20738. // Start with Rule1
  20739. ngDevMode &&
  20740. assertEqual(newTNode.insertBeforeIndex, null, 'We expect that insertBeforeIndex is not set');
  20741. previousTNodes.push(newTNode);
  20742. if (previousTNodes.length > 1) {
  20743. for (let i = previousTNodes.length - 2; i >= 0; i--) {
  20744. const existingTNode = previousTNodes[i];
  20745. // Text nodes are created eagerly and so they don't need their `indexBeforeIndex` updated.
  20746. // It is safe to ignore them.
  20747. if (!isI18nText(existingTNode)) {
  20748. if (isNewTNodeCreatedBefore(existingTNode, newTNode) &&
  20749. getInsertBeforeIndex(existingTNode) === null) {
  20750. // If it was created before us in time, (and it does not yet have `insertBeforeIndex`)
  20751. // then add the `insertBeforeIndex`.
  20752. setInsertBeforeIndex(existingTNode, newTNode.index);
  20753. }
  20754. }
  20755. }
  20756. }
  20757. }
  20758. function isI18nText(tNode) {
  20759. return !(tNode.type & 64 /* TNodeType.Placeholder */);
  20760. }
  20761. function isNewTNodeCreatedBefore(existingTNode, newTNode) {
  20762. return isI18nText(newTNode) || existingTNode.index > newTNode.index;
  20763. }
  20764. function getInsertBeforeIndex(tNode) {
  20765. const index = tNode.insertBeforeIndex;
  20766. return Array.isArray(index) ? index[0] : index;
  20767. }
  20768. function setInsertBeforeIndex(tNode, value) {
  20769. const index = tNode.insertBeforeIndex;
  20770. if (Array.isArray(index)) {
  20771. // Array is stored if we have to insert child nodes. See `TNode.insertBeforeIndex`
  20772. index[0] = value;
  20773. }
  20774. else {
  20775. setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
  20776. tNode.insertBeforeIndex = value;
  20777. }
  20778. }
  20779. /**
  20780. * Retrieve `TIcu` at a given `index`.
  20781. *
  20782. * The `TIcu` can be stored either directly (if it is nested ICU) OR
  20783. * it is stored inside tho `TIcuContainer` if it is top level ICU.
  20784. *
  20785. * The reason for this is that the top level ICU need a `TNode` so that they are part of the render
  20786. * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
  20787. * expressed (parent ICU may have selected a case which does not contain it.)
  20788. *
  20789. * @param tView Current `TView`.
  20790. * @param index Index where the value should be read from.
  20791. */
  20792. function getTIcu(tView, index) {
  20793. const value = tView.data[index];
  20794. if (value === null || typeof value === 'string')
  20795. return null;
  20796. if (ngDevMode &&
  20797. !(value.hasOwnProperty('tView') || value.hasOwnProperty('currentCaseLViewIndex'))) {
  20798. throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value);
  20799. }
  20800. // Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be
  20801. // either TIcu or TIcuContainerNode. This is not ideal, but we still think it is OK because it
  20802. // will be just two cases which fits into the browser inline cache (inline cache can take up to
  20803. // 4)
  20804. const tIcu = value.hasOwnProperty('currentCaseLViewIndex') ? value :
  20805. value.value;
  20806. ngDevMode && assertTIcu(tIcu);
  20807. return tIcu;
  20808. }
  20809. /**
  20810. * Store `TIcu` at a give `index`.
  20811. *
  20812. * The `TIcu` can be stored either directly (if it is nested ICU) OR
  20813. * it is stored inside tho `TIcuContainer` if it is top level ICU.
  20814. *
  20815. * The reason for this is that the top level ICU need a `TNode` so that they are part of the render
  20816. * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
  20817. * expressed (parent ICU may have selected a case which does not contain it.)
  20818. *
  20819. * @param tView Current `TView`.
  20820. * @param index Index where the value should be stored at in `Tview.data`
  20821. * @param tIcu The TIcu to store.
  20822. */
  20823. function setTIcu(tView, index, tIcu) {
  20824. const tNode = tView.data[index];
  20825. ngDevMode &&
  20826. assertEqual(tNode === null || tNode.hasOwnProperty('tView'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
  20827. if (tNode === null) {
  20828. tView.data[index] = tIcu;
  20829. }
  20830. else {
  20831. ngDevMode && assertTNodeType(tNode, 32 /* TNodeType.Icu */);
  20832. tNode.value = tIcu;
  20833. }
  20834. }
  20835. /**
  20836. * Set `TNode.insertBeforeIndex` taking the `Array` into account.
  20837. *
  20838. * See `TNode.insertBeforeIndex`
  20839. */
  20840. function setTNodeInsertBeforeIndex(tNode, index) {
  20841. ngDevMode && assertTNode(tNode);
  20842. let insertBeforeIndex = tNode.insertBeforeIndex;
  20843. if (insertBeforeIndex === null) {
  20844. setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
  20845. insertBeforeIndex = tNode.insertBeforeIndex =
  20846. [null /* may be updated to number later */, index];
  20847. }
  20848. else {
  20849. assertEqual(Array.isArray(insertBeforeIndex), true, 'Expecting array here');
  20850. insertBeforeIndex.push(index);
  20851. }
  20852. }
  20853. /**
  20854. * Create `TNode.type=TNodeType.Placeholder` node.
  20855. *
  20856. * See `TNodeType.Placeholder` for more information.
  20857. */
  20858. function createTNodePlaceholder(tView, previousTNodes, index) {
  20859. const tNode = createTNodeAtIndex(tView, index, 64 /* TNodeType.Placeholder */, null, null);
  20860. addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tNode);
  20861. return tNode;
  20862. }
  20863. /**
  20864. * Returns current ICU case.
  20865. *
  20866. * ICU cases are stored as index into the `TIcu.cases`.
  20867. * At times it is necessary to communicate that the ICU case just switched and that next ICU update
  20868. * should update all bindings regardless of the mask. In such a case the we store negative numbers
  20869. * for cases which have just been switched. This function removes the negative flag.
  20870. */
  20871. function getCurrentICUCaseIndex(tIcu, lView) {
  20872. const currentCase = lView[tIcu.currentCaseLViewIndex];
  20873. return currentCase === null ? currentCase : (currentCase < 0 ? ~currentCase : currentCase);
  20874. }
  20875. function getParentFromIcuCreateOpCode(mergedCode) {
  20876. return mergedCode >>> 17 /* IcuCreateOpCode.SHIFT_PARENT */;
  20877. }
  20878. function getRefFromIcuCreateOpCode(mergedCode) {
  20879. return (mergedCode & 131070 /* IcuCreateOpCode.MASK_REF */) >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
  20880. }
  20881. function getInstructionFromIcuCreateOpCode(mergedCode) {
  20882. return mergedCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */;
  20883. }
  20884. function icuCreateOpCode(opCode, parentIdx, refIdx) {
  20885. ngDevMode && assertGreaterThanOrEqual(parentIdx, 0, 'Missing parent index');
  20886. ngDevMode && assertGreaterThan(refIdx, 0, 'Missing ref index');
  20887. return opCode | parentIdx << 17 /* IcuCreateOpCode.SHIFT_PARENT */ | refIdx << 1 /* IcuCreateOpCode.SHIFT_REF */;
  20888. }
  20889. /**
  20890. * Keep track of which input bindings in `ɵɵi18nExp` have changed.
  20891. *
  20892. * This is used to efficiently update expressions in i18n only when the corresponding input has
  20893. * changed.
  20894. *
  20895. * 1) Each bit represents which of the `ɵɵi18nExp` has changed.
  20896. * 2) There are 32 bits allowed in JS.
  20897. * 3) Bit 32 is special as it is shared for all changes past 32. (In other words if you have more
  20898. * than 32 `ɵɵi18nExp` then all changes past 32nd `ɵɵi18nExp` will be mapped to same bit. This means
  20899. * that we may end up changing more than we need to. But i18n expressions with 32 bindings is rare
  20900. * so in practice it should not be an issue.)
  20901. */
  20902. let changeMask = 0b0;
  20903. /**
  20904. * Keeps track of which bit needs to be updated in `changeMask`
  20905. *
  20906. * This value gets incremented on every call to `ɵɵi18nExp`
  20907. */
  20908. let changeMaskCounter = 0;
  20909. /**
  20910. * Keep track of which input bindings in `ɵɵi18nExp` have changed.
  20911. *
  20912. * `setMaskBit` gets invoked by each call to `ɵɵi18nExp`.
  20913. *
  20914. * @param hasChange did `ɵɵi18nExp` detect a change.
  20915. */
  20916. function setMaskBit(hasChange) {
  20917. if (hasChange) {
  20918. changeMask = changeMask | (1 << Math.min(changeMaskCounter, 31));
  20919. }
  20920. changeMaskCounter++;
  20921. }
  20922. function applyI18n(tView, lView, index) {
  20923. if (changeMaskCounter > 0) {
  20924. ngDevMode && assertDefined(tView, `tView should be defined`);
  20925. const tI18n = tView.data[index];
  20926. // When `index` points to an `ɵɵi18nAttributes` then we have an array otherwise `TI18n`
  20927. const updateOpCodes = Array.isArray(tI18n) ? tI18n : tI18n.update;
  20928. const bindingsStartIndex = getBindingIndex() - changeMaskCounter - 1;
  20929. applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask);
  20930. }
  20931. // Reset changeMask & maskBit to default for the next update cycle
  20932. changeMask = 0b0;
  20933. changeMaskCounter = 0;
  20934. }
  20935. /**
  20936. * Apply `I18nCreateOpCodes` op-codes as stored in `TI18n.create`.
  20937. *
  20938. * Creates text (and comment) nodes which are internationalized.
  20939. *
  20940. * @param lView Current lView
  20941. * @param createOpCodes Set of op-codes to apply
  20942. * @param parentRNode Parent node (so that direct children can be added eagerly) or `null` if it is
  20943. * a root node.
  20944. * @param insertInFrontOf DOM node that should be used as an anchor.
  20945. */
  20946. function applyCreateOpCodes(lView, createOpCodes, parentRNode, insertInFrontOf) {
  20947. const renderer = lView[RENDERER];
  20948. for (let i = 0; i < createOpCodes.length; i++) {
  20949. const opCode = createOpCodes[i++];
  20950. const text = createOpCodes[i];
  20951. const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT;
  20952. const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY;
  20953. const index = opCode >>> I18nCreateOpCode.SHIFT;
  20954. let rNode = lView[index];
  20955. if (rNode === null) {
  20956. // We only create new DOM nodes if they don't already exist: If ICU switches case back to a
  20957. // case which was already instantiated, no need to create new DOM nodes.
  20958. rNode = lView[index] =
  20959. isComment ? renderer.createComment(text) : createTextNode(renderer, text);
  20960. }
  20961. if (appendNow && parentRNode !== null) {
  20962. nativeInsertBefore(renderer, parentRNode, rNode, insertInFrontOf, false);
  20963. }
  20964. }
  20965. }
  20966. /**
  20967. * Apply `I18nMutateOpCodes` OpCodes.
  20968. *
  20969. * @param tView Current `TView`
  20970. * @param mutableOpCodes Mutable OpCodes to process
  20971. * @param lView Current `LView`
  20972. * @param anchorRNode place where the i18n node should be inserted.
  20973. */
  20974. function applyMutableOpCodes(tView, mutableOpCodes, lView, anchorRNode) {
  20975. ngDevMode && assertDomNode(anchorRNode);
  20976. const renderer = lView[RENDERER];
  20977. // `rootIdx` represents the node into which all inserts happen.
  20978. let rootIdx = null;
  20979. // `rootRNode` represents the real node into which we insert. This can be different from
  20980. // `lView[rootIdx]` if we have projection.
  20981. // - null we don't have a parent (as can be the case in when we are inserting into a root of
  20982. // LView which has no parent.)
  20983. // - `RElement` The element representing the root after taking projection into account.
  20984. let rootRNode;
  20985. for (let i = 0; i < mutableOpCodes.length; i++) {
  20986. const opCode = mutableOpCodes[i];
  20987. if (typeof opCode == 'string') {
  20988. const textNodeIndex = mutableOpCodes[++i];
  20989. if (lView[textNodeIndex] === null) {
  20990. ngDevMode && ngDevMode.rendererCreateTextNode++;
  20991. ngDevMode && assertIndexInRange(lView, textNodeIndex);
  20992. lView[textNodeIndex] = createTextNode(renderer, opCode);
  20993. }
  20994. }
  20995. else if (typeof opCode == 'number') {
  20996. switch (opCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */) {
  20997. case 0 /* IcuCreateOpCode.AppendChild */:
  20998. const parentIdx = getParentFromIcuCreateOpCode(opCode);
  20999. if (rootIdx === null) {
  21000. // The first operation should save the `rootIdx` because the first operation
  21001. // must insert into the root. (Only subsequent operations can insert into a dynamic
  21002. // parent)
  21003. rootIdx = parentIdx;
  21004. rootRNode = nativeParentNode(renderer, anchorRNode);
  21005. }
  21006. let insertInFrontOf;
  21007. let parentRNode;
  21008. if (parentIdx === rootIdx) {
  21009. insertInFrontOf = anchorRNode;
  21010. parentRNode = rootRNode;
  21011. }
  21012. else {
  21013. insertInFrontOf = null;
  21014. parentRNode = unwrapRNode(lView[parentIdx]);
  21015. }
  21016. // FIXME(misko): Refactor with `processI18nText`
  21017. if (parentRNode !== null) {
  21018. // This can happen if the `LView` we are adding to is not attached to a parent `LView`.
  21019. // In such a case there is no "root" we can attach to. This is fine, as we still need to
  21020. // create the elements. When the `LView` gets later added to a parent these "root" nodes
  21021. // get picked up and added.
  21022. ngDevMode && assertDomNode(parentRNode);
  21023. const refIdx = getRefFromIcuCreateOpCode(opCode);
  21024. ngDevMode && assertGreaterThan(refIdx, HEADER_OFFSET, 'Missing ref');
  21025. // `unwrapRNode` is not needed here as all of these point to RNodes as part of the i18n
  21026. // which can't have components.
  21027. const child = lView[refIdx];
  21028. ngDevMode && assertDomNode(child);
  21029. nativeInsertBefore(renderer, parentRNode, child, insertInFrontOf, false);
  21030. const tIcu = getTIcu(tView, refIdx);
  21031. if (tIcu !== null && typeof tIcu === 'object') {
  21032. // If we just added a comment node which has ICU then that ICU may have already been
  21033. // rendered and therefore we need to re-add it here.
  21034. ngDevMode && assertTIcu(tIcu);
  21035. const caseIndex = getCurrentICUCaseIndex(tIcu, lView);
  21036. if (caseIndex !== null) {
  21037. applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, lView[tIcu.anchorIdx]);
  21038. }
  21039. }
  21040. }
  21041. break;
  21042. case 1 /* IcuCreateOpCode.Attr */:
  21043. const elementNodeIndex = opCode >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
  21044. const attrName = mutableOpCodes[++i];
  21045. const attrValue = mutableOpCodes[++i];
  21046. // This code is used for ICU expressions only, since we don't support
  21047. // directives/components in ICUs, we don't need to worry about inputs here
  21048. setElementAttribute(renderer, getNativeByIndex(elementNodeIndex, lView), null, null, attrName, attrValue, null);
  21049. break;
  21050. default:
  21051. if (ngDevMode) {
  21052. throw new RuntimeError(700 /* RuntimeErrorCode.INVALID_I18N_STRUCTURE */, `Unable to determine the type of mutate operation for "${opCode}"`);
  21053. }
  21054. }
  21055. }
  21056. else {
  21057. switch (opCode) {
  21058. case ICU_MARKER:
  21059. const commentValue = mutableOpCodes[++i];
  21060. const commentNodeIndex = mutableOpCodes[++i];
  21061. if (lView[commentNodeIndex] === null) {
  21062. ngDevMode &&
  21063. assertEqual(typeof commentValue, 'string', `Expected "${commentValue}" to be a comment node value`);
  21064. ngDevMode && ngDevMode.rendererCreateComment++;
  21065. ngDevMode && assertIndexInExpandoRange(lView, commentNodeIndex);
  21066. const commentRNode = lView[commentNodeIndex] =
  21067. createCommentNode(renderer, commentValue);
  21068. // FIXME(misko): Attaching patch data is only needed for the root (Also add tests)
  21069. attachPatchData(commentRNode, lView);
  21070. }
  21071. break;
  21072. case ELEMENT_MARKER:
  21073. const tagName = mutableOpCodes[++i];
  21074. const elementNodeIndex = mutableOpCodes[++i];
  21075. if (lView[elementNodeIndex] === null) {
  21076. ngDevMode &&
  21077. assertEqual(typeof tagName, 'string', `Expected "${tagName}" to be an element node tag name`);
  21078. ngDevMode && ngDevMode.rendererCreateElement++;
  21079. ngDevMode && assertIndexInExpandoRange(lView, elementNodeIndex);
  21080. const elementRNode = lView[elementNodeIndex] =
  21081. createElementNode(renderer, tagName, null);
  21082. // FIXME(misko): Attaching patch data is only needed for the root (Also add tests)
  21083. attachPatchData(elementRNode, lView);
  21084. }
  21085. break;
  21086. default:
  21087. ngDevMode &&
  21088. throwError(`Unable to determine the type of mutate operation for "${opCode}"`);
  21089. }
  21090. }
  21091. }
  21092. }
  21093. /**
  21094. * Apply `I18nUpdateOpCodes` OpCodes
  21095. *
  21096. * @param tView Current `TView`
  21097. * @param lView Current `LView`
  21098. * @param updateOpCodes OpCodes to process
  21099. * @param bindingsStartIndex Location of the first `ɵɵi18nApply`
  21100. * @param changeMask Each bit corresponds to a `ɵɵi18nExp` (Counting backwards from
  21101. * `bindingsStartIndex`)
  21102. */
  21103. function applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask) {
  21104. for (let i = 0; i < updateOpCodes.length; i++) {
  21105. // bit code to check if we should apply the next update
  21106. const checkBit = updateOpCodes[i];
  21107. // Number of opCodes to skip until next set of update codes
  21108. const skipCodes = updateOpCodes[++i];
  21109. if (checkBit & changeMask) {
  21110. // The value has been updated since last checked
  21111. let value = '';
  21112. for (let j = i + 1; j <= (i + skipCodes); j++) {
  21113. const opCode = updateOpCodes[j];
  21114. if (typeof opCode == 'string') {
  21115. value += opCode;
  21116. }
  21117. else if (typeof opCode == 'number') {
  21118. if (opCode < 0) {
  21119. // Negative opCode represent `i18nExp` values offset.
  21120. value += renderStringify(lView[bindingsStartIndex - opCode]);
  21121. }
  21122. else {
  21123. const nodeIndex = (opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */);
  21124. switch (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) {
  21125. case 1 /* I18nUpdateOpCode.Attr */:
  21126. const propName = updateOpCodes[++j];
  21127. const sanitizeFn = updateOpCodes[++j];
  21128. const tNodeOrTagName = tView.data[nodeIndex];
  21129. ngDevMode && assertDefined(tNodeOrTagName, 'Experting TNode or string');
  21130. if (typeof tNodeOrTagName === 'string') {
  21131. // IF we don't have a `TNode`, then we are an element in ICU (as ICU content does
  21132. // not have TNode), in which case we know that there are no directives, and hence
  21133. // we use attribute setting.
  21134. setElementAttribute(lView[RENDERER], lView[nodeIndex], null, tNodeOrTagName, propName, value, sanitizeFn);
  21135. }
  21136. else {
  21137. elementPropertyInternal(tView, tNodeOrTagName, lView, propName, value, lView[RENDERER], sanitizeFn, false);
  21138. }
  21139. break;
  21140. case 0 /* I18nUpdateOpCode.Text */:
  21141. const rText = lView[nodeIndex];
  21142. rText !== null && updateTextNode(lView[RENDERER], rText, value);
  21143. break;
  21144. case 2 /* I18nUpdateOpCode.IcuSwitch */:
  21145. applyIcuSwitchCase(tView, getTIcu(tView, nodeIndex), lView, value);
  21146. break;
  21147. case 3 /* I18nUpdateOpCode.IcuUpdate */:
  21148. applyIcuUpdateCase(tView, getTIcu(tView, nodeIndex), bindingsStartIndex, lView);
  21149. break;
  21150. }
  21151. }
  21152. }
  21153. }
  21154. }
  21155. else {
  21156. const opCode = updateOpCodes[i + 1];
  21157. if (opCode > 0 && (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) === 3 /* I18nUpdateOpCode.IcuUpdate */) {
  21158. // Special case for the `icuUpdateCase`. It could be that the mask did not match, but
  21159. // we still need to execute `icuUpdateCase` because the case has changed recently due to
  21160. // previous `icuSwitchCase` instruction. (`icuSwitchCase` and `icuUpdateCase` always come in
  21161. // pairs.)
  21162. const nodeIndex = (opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */);
  21163. const tIcu = getTIcu(tView, nodeIndex);
  21164. const currentIndex = lView[tIcu.currentCaseLViewIndex];
  21165. if (currentIndex < 0) {
  21166. applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView);
  21167. }
  21168. }
  21169. }
  21170. i += skipCodes;
  21171. }
  21172. }
  21173. /**
  21174. * Apply OpCodes associated with updating an existing ICU.
  21175. *
  21176. * @param tView Current `TView`
  21177. * @param tIcu Current `TIcu`
  21178. * @param bindingsStartIndex Location of the first `ɵɵi18nApply`
  21179. * @param lView Current `LView`
  21180. */
  21181. function applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView) {
  21182. ngDevMode && assertIndexInRange(lView, tIcu.currentCaseLViewIndex);
  21183. let activeCaseIndex = lView[tIcu.currentCaseLViewIndex];
  21184. if (activeCaseIndex !== null) {
  21185. let mask = changeMask;
  21186. if (activeCaseIndex < 0) {
  21187. // Clear the flag.
  21188. // Negative number means that the ICU was freshly created and we need to force the update.
  21189. activeCaseIndex = lView[tIcu.currentCaseLViewIndex] = ~activeCaseIndex;
  21190. // -1 is same as all bits on, which simulates creation since it marks all bits dirty
  21191. mask = -1;
  21192. }
  21193. applyUpdateOpCodes(tView, lView, tIcu.update[activeCaseIndex], bindingsStartIndex, mask);
  21194. }
  21195. }
  21196. /**
  21197. * Apply OpCodes associated with switching a case on ICU.
  21198. *
  21199. * This involves tearing down existing case and than building up a new case.
  21200. *
  21201. * @param tView Current `TView`
  21202. * @param tIcu Current `TIcu`
  21203. * @param lView Current `LView`
  21204. * @param value Value of the case to update to.
  21205. */
  21206. function applyIcuSwitchCase(tView, tIcu, lView, value) {
  21207. // Rebuild a new case for this ICU
  21208. const caseIndex = getCaseIndex(tIcu, value);
  21209. let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView);
  21210. if (activeCaseIndex !== caseIndex) {
  21211. applyIcuSwitchCaseRemove(tView, tIcu, lView);
  21212. lView[tIcu.currentCaseLViewIndex] = caseIndex === null ? null : ~caseIndex;
  21213. if (caseIndex !== null) {
  21214. // Add the nodes for the new case
  21215. const anchorRNode = lView[tIcu.anchorIdx];
  21216. if (anchorRNode) {
  21217. ngDevMode && assertDomNode(anchorRNode);
  21218. applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, anchorRNode);
  21219. }
  21220. }
  21221. }
  21222. }
  21223. /**
  21224. * Apply OpCodes associated with tearing ICU case.
  21225. *
  21226. * This involves tearing down existing case and than building up a new case.
  21227. *
  21228. * @param tView Current `TView`
  21229. * @param tIcu Current `TIcu`
  21230. * @param lView Current `LView`
  21231. */
  21232. function applyIcuSwitchCaseRemove(tView, tIcu, lView) {
  21233. let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView);
  21234. if (activeCaseIndex !== null) {
  21235. const removeCodes = tIcu.remove[activeCaseIndex];
  21236. for (let i = 0; i < removeCodes.length; i++) {
  21237. const nodeOrIcuIndex = removeCodes[i];
  21238. if (nodeOrIcuIndex > 0) {
  21239. // Positive numbers are `RNode`s.
  21240. const rNode = getNativeByIndex(nodeOrIcuIndex, lView);
  21241. rNode !== null && nativeRemoveNode(lView[RENDERER], rNode);
  21242. }
  21243. else {
  21244. // Negative numbers are ICUs
  21245. applyIcuSwitchCaseRemove(tView, getTIcu(tView, ~nodeOrIcuIndex), lView);
  21246. }
  21247. }
  21248. }
  21249. }
  21250. /**
  21251. * Returns the index of the current case of an ICU expression depending on the main binding value
  21252. *
  21253. * @param icuExpression
  21254. * @param bindingValue The value of the main binding used by this ICU expression
  21255. */
  21256. function getCaseIndex(icuExpression, bindingValue) {
  21257. let index = icuExpression.cases.indexOf(bindingValue);
  21258. if (index === -1) {
  21259. switch (icuExpression.type) {
  21260. case 1 /* IcuType.plural */: {
  21261. const resolvedCase = getPluralCase(bindingValue, getLocaleId());
  21262. index = icuExpression.cases.indexOf(resolvedCase);
  21263. if (index === -1 && resolvedCase !== 'other') {
  21264. index = icuExpression.cases.indexOf('other');
  21265. }
  21266. break;
  21267. }
  21268. case 0 /* IcuType.select */: {
  21269. index = icuExpression.cases.indexOf('other');
  21270. break;
  21271. }
  21272. }
  21273. }
  21274. return index === -1 ? null : index;
  21275. }
  21276. function loadIcuContainerVisitor() {
  21277. const _stack = [];
  21278. let _index = -1;
  21279. let _lView;
  21280. let _removes;
  21281. /**
  21282. * Retrieves a set of root nodes from `TIcu.remove`. Used by `TNodeType.ICUContainer`
  21283. * to determine which root belong to the ICU.
  21284. *
  21285. * Example of usage.
  21286. * ```
  21287. * const nextRNode = icuContainerIteratorStart(tIcuContainerNode, lView);
  21288. * let rNode: RNode|null;
  21289. * while(rNode = nextRNode()) {
  21290. * console.log(rNode);
  21291. * }
  21292. * ```
  21293. *
  21294. * @param tIcuContainerNode Current `TIcuContainerNode`
  21295. * @param lView `LView` where the `RNode`s should be looked up.
  21296. */
  21297. function icuContainerIteratorStart(tIcuContainerNode, lView) {
  21298. _lView = lView;
  21299. while (_stack.length)
  21300. _stack.pop();
  21301. ngDevMode && assertTNodeForLView(tIcuContainerNode, lView);
  21302. enterIcu(tIcuContainerNode.value, lView);
  21303. return icuContainerIteratorNext;
  21304. }
  21305. function enterIcu(tIcu, lView) {
  21306. _index = 0;
  21307. const currentCase = getCurrentICUCaseIndex(tIcu, lView);
  21308. if (currentCase !== null) {
  21309. ngDevMode && assertNumberInRange(currentCase, 0, tIcu.cases.length - 1);
  21310. _removes = tIcu.remove[currentCase];
  21311. }
  21312. else {
  21313. _removes = EMPTY_ARRAY;
  21314. }
  21315. }
  21316. function icuContainerIteratorNext() {
  21317. if (_index < _removes.length) {
  21318. const removeOpCode = _removes[_index++];
  21319. ngDevMode && assertNumber(removeOpCode, 'Expecting OpCode number');
  21320. if (removeOpCode > 0) {
  21321. const rNode = _lView[removeOpCode];
  21322. ngDevMode && assertDomNode(rNode);
  21323. return rNode;
  21324. }
  21325. else {
  21326. _stack.push(_index, _removes);
  21327. // ICUs are represented by negative indices
  21328. const tIcuIndex = ~removeOpCode;
  21329. const tIcu = _lView[TVIEW].data[tIcuIndex];
  21330. ngDevMode && assertTIcu(tIcu);
  21331. enterIcu(tIcu, _lView);
  21332. return icuContainerIteratorNext();
  21333. }
  21334. }
  21335. else {
  21336. if (_stack.length === 0) {
  21337. return null;
  21338. }
  21339. else {
  21340. _removes = _stack.pop();
  21341. _index = _stack.pop();
  21342. return icuContainerIteratorNext();
  21343. }
  21344. }
  21345. }
  21346. return icuContainerIteratorStart;
  21347. }
  21348. /**
  21349. * Converts `I18nCreateOpCodes` array into a human readable format.
  21350. *
  21351. * This function is attached to the `I18nCreateOpCodes.debug` property if `ngDevMode` is enabled.
  21352. * This function provides a human readable view of the opcodes. This is useful when debugging the
  21353. * application as well as writing more readable tests.
  21354. *
  21355. * @param this `I18nCreateOpCodes` if attached as a method.
  21356. * @param opcodes `I18nCreateOpCodes` if invoked as a function.
  21357. */
  21358. function i18nCreateOpCodesToString(opcodes) {
  21359. const createOpCodes = opcodes || (Array.isArray(this) ? this : []);
  21360. let lines = [];
  21361. for (let i = 0; i < createOpCodes.length; i++) {
  21362. const opCode = createOpCodes[i++];
  21363. const text = createOpCodes[i];
  21364. const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT;
  21365. const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY;
  21366. const index = opCode >>> I18nCreateOpCode.SHIFT;
  21367. lines.push(`lView[${index}] = document.${isComment ? 'createComment' : 'createText'}(${JSON.stringify(text)});`);
  21368. if (appendNow) {
  21369. lines.push(`parent.appendChild(lView[${index}]);`);
  21370. }
  21371. }
  21372. return lines;
  21373. }
  21374. /**
  21375. * Converts `I18nUpdateOpCodes` array into a human readable format.
  21376. *
  21377. * This function is attached to the `I18nUpdateOpCodes.debug` property if `ngDevMode` is enabled.
  21378. * This function provides a human readable view of the opcodes. This is useful when debugging the
  21379. * application as well as writing more readable tests.
  21380. *
  21381. * @param this `I18nUpdateOpCodes` if attached as a method.
  21382. * @param opcodes `I18nUpdateOpCodes` if invoked as a function.
  21383. */
  21384. function i18nUpdateOpCodesToString(opcodes) {
  21385. const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : []));
  21386. let lines = [];
  21387. function consumeOpCode(value) {
  21388. const ref = value >>> 2 /* I18nUpdateOpCode.SHIFT_REF */;
  21389. const opCode = value & 3 /* I18nUpdateOpCode.MASK_OPCODE */;
  21390. switch (opCode) {
  21391. case 0 /* I18nUpdateOpCode.Text */:
  21392. return `(lView[${ref}] as Text).textContent = $$$`;
  21393. case 1 /* I18nUpdateOpCode.Attr */:
  21394. const attrName = parser.consumeString();
  21395. const sanitizationFn = parser.consumeFunction();
  21396. const value = sanitizationFn ? `(${sanitizationFn})($$$)` : '$$$';
  21397. return `(lView[${ref}] as Element).setAttribute('${attrName}', ${value})`;
  21398. case 2 /* I18nUpdateOpCode.IcuSwitch */:
  21399. return `icuSwitchCase(${ref}, $$$)`;
  21400. case 3 /* I18nUpdateOpCode.IcuUpdate */:
  21401. return `icuUpdateCase(${ref})`;
  21402. }
  21403. throw new Error('unexpected OpCode');
  21404. }
  21405. while (parser.hasMore()) {
  21406. let mask = parser.consumeNumber();
  21407. let size = parser.consumeNumber();
  21408. const end = parser.i + size;
  21409. const statements = [];
  21410. let statement = '';
  21411. while (parser.i < end) {
  21412. let value = parser.consumeNumberOrString();
  21413. if (typeof value === 'string') {
  21414. statement += value;
  21415. }
  21416. else if (value < 0) {
  21417. // Negative numbers are ref indexes
  21418. // Here `i` refers to current binding index. It is to signify that the value is relative,
  21419. // rather than absolute.
  21420. statement += '${lView[i' + value + ']}';
  21421. }
  21422. else {
  21423. // Positive numbers are operations.
  21424. const opCodeText = consumeOpCode(value);
  21425. statements.push(opCodeText.replace('$$$', '`' + statement + '`') + ';');
  21426. statement = '';
  21427. }
  21428. }
  21429. lines.push(`if (mask & 0b${mask.toString(2)}) { ${statements.join(' ')} }`);
  21430. }
  21431. return lines;
  21432. }
  21433. /**
  21434. * Converts `I18nCreateOpCodes` array into a human readable format.
  21435. *
  21436. * This function is attached to the `I18nCreateOpCodes.debug` if `ngDevMode` is enabled. This
  21437. * function provides a human readable view of the opcodes. This is useful when debugging the
  21438. * application as well as writing more readable tests.
  21439. *
  21440. * @param this `I18nCreateOpCodes` if attached as a method.
  21441. * @param opcodes `I18nCreateOpCodes` if invoked as a function.
  21442. */
  21443. function icuCreateOpCodesToString(opcodes) {
  21444. const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : []));
  21445. let lines = [];
  21446. function consumeOpCode(opCode) {
  21447. const parent = getParentFromIcuCreateOpCode(opCode);
  21448. const ref = getRefFromIcuCreateOpCode(opCode);
  21449. switch (getInstructionFromIcuCreateOpCode(opCode)) {
  21450. case 0 /* IcuCreateOpCode.AppendChild */:
  21451. return `(lView[${parent}] as Element).appendChild(lView[${lastRef}])`;
  21452. case 1 /* IcuCreateOpCode.Attr */:
  21453. return `(lView[${ref}] as Element).setAttribute("${parser.consumeString()}", "${parser.consumeString()}")`;
  21454. }
  21455. throw new Error('Unexpected OpCode: ' + getInstructionFromIcuCreateOpCode(opCode));
  21456. }
  21457. let lastRef = -1;
  21458. while (parser.hasMore()) {
  21459. let value = parser.consumeNumberStringOrMarker();
  21460. if (value === ICU_MARKER) {
  21461. const text = parser.consumeString();
  21462. lastRef = parser.consumeNumber();
  21463. lines.push(`lView[${lastRef}] = document.createComment("${text}")`);
  21464. }
  21465. else if (value === ELEMENT_MARKER) {
  21466. const text = parser.consumeString();
  21467. lastRef = parser.consumeNumber();
  21468. lines.push(`lView[${lastRef}] = document.createElement("${text}")`);
  21469. }
  21470. else if (typeof value === 'string') {
  21471. lastRef = parser.consumeNumber();
  21472. lines.push(`lView[${lastRef}] = document.createTextNode("${value}")`);
  21473. }
  21474. else if (typeof value === 'number') {
  21475. const line = consumeOpCode(value);
  21476. line && lines.push(line);
  21477. }
  21478. else {
  21479. throw new Error('Unexpected value');
  21480. }
  21481. }
  21482. return lines;
  21483. }
  21484. /**
  21485. * Converts `I18nRemoveOpCodes` array into a human readable format.
  21486. *
  21487. * This function is attached to the `I18nRemoveOpCodes.debug` if `ngDevMode` is enabled. This
  21488. * function provides a human readable view of the opcodes. This is useful when debugging the
  21489. * application as well as writing more readable tests.
  21490. *
  21491. * @param this `I18nRemoveOpCodes` if attached as a method.
  21492. * @param opcodes `I18nRemoveOpCodes` if invoked as a function.
  21493. */
  21494. function i18nRemoveOpCodesToString(opcodes) {
  21495. const removeCodes = opcodes || (Array.isArray(this) ? this : []);
  21496. let lines = [];
  21497. for (let i = 0; i < removeCodes.length; i++) {
  21498. const nodeOrIcuIndex = removeCodes[i];
  21499. if (nodeOrIcuIndex > 0) {
  21500. // Positive numbers are `RNode`s.
  21501. lines.push(`remove(lView[${nodeOrIcuIndex}])`);
  21502. }
  21503. else {
  21504. // Negative numbers are ICUs
  21505. lines.push(`removeNestedICU(${~nodeOrIcuIndex})`);
  21506. }
  21507. }
  21508. return lines;
  21509. }
  21510. class OpCodeParser {
  21511. constructor(codes) {
  21512. this.i = 0;
  21513. this.codes = codes;
  21514. }
  21515. hasMore() {
  21516. return this.i < this.codes.length;
  21517. }
  21518. consumeNumber() {
  21519. let value = this.codes[this.i++];
  21520. assertNumber(value, 'expecting number in OpCode');
  21521. return value;
  21522. }
  21523. consumeString() {
  21524. let value = this.codes[this.i++];
  21525. assertString(value, 'expecting string in OpCode');
  21526. return value;
  21527. }
  21528. consumeFunction() {
  21529. let value = this.codes[this.i++];
  21530. if (value === null || typeof value === 'function') {
  21531. return value;
  21532. }
  21533. throw new Error('expecting function in OpCode');
  21534. }
  21535. consumeNumberOrString() {
  21536. let value = this.codes[this.i++];
  21537. if (typeof value === 'string') {
  21538. return value;
  21539. }
  21540. assertNumber(value, 'expecting number or string in OpCode');
  21541. return value;
  21542. }
  21543. consumeNumberStringOrMarker() {
  21544. let value = this.codes[this.i++];
  21545. if (typeof value === 'string' || typeof value === 'number' || value == ICU_MARKER ||
  21546. value == ELEMENT_MARKER) {
  21547. return value;
  21548. }
  21549. assertNumber(value, 'expecting number, string, ICU_MARKER or ELEMENT_MARKER in OpCode');
  21550. return value;
  21551. }
  21552. }
  21553. const BINDING_REGEXP = /�(\d+):?\d*�/gi;
  21554. const ICU_REGEXP = /({\s*�\d+:?\d*�\s*,\s*\S{6}\s*,[\s\S]*})/gi;
  21555. const NESTED_ICU = /�(\d+)�/;
  21556. const ICU_BLOCK_REGEXP = /^\s*(�\d+:?\d*�)\s*,\s*(select|plural)\s*,/;
  21557. const MARKER = `�`;
  21558. const SUBTEMPLATE_REGEXP = /�\/?\*(\d+:\d+)�/gi;
  21559. const PH_REGEXP = /�(\/?[#*]\d+):?\d*�/gi;
  21560. /**
  21561. * Angular uses the special entity &ngsp; as a placeholder for non-removable space.
  21562. * It's replaced by the 0xE500 PUA (Private Use Areas) unicode character and later on replaced by a
  21563. * space.
  21564. * We are re-implementing the same idea since translations might contain this special character.
  21565. */
  21566. const NGSP_UNICODE_REGEXP = /\uE500/g;
  21567. function replaceNgsp(value) {
  21568. return value.replace(NGSP_UNICODE_REGEXP, ' ');
  21569. }
  21570. /**
  21571. * Patch a `debug` property getter on top of the existing object.
  21572. *
  21573. * NOTE: always call this method with `ngDevMode && attachDebugObject(...)`
  21574. *
  21575. * @param obj Object to patch
  21576. * @param debugGetter Getter returning a value to patch
  21577. */
  21578. function attachDebugGetter(obj, debugGetter) {
  21579. if (ngDevMode) {
  21580. Object.defineProperty(obj, 'debug', { get: debugGetter, enumerable: false });
  21581. }
  21582. else {
  21583. throw new Error('This method should be guarded with `ngDevMode` so that it can be tree shaken in production!');
  21584. }
  21585. }
  21586. /**
  21587. * Create dynamic nodes from i18n translation block.
  21588. *
  21589. * - Text nodes are created synchronously
  21590. * - TNodes are linked into tree lazily
  21591. *
  21592. * @param tView Current `TView`
  21593. * @parentTNodeIndex index to the parent TNode of this i18n block
  21594. * @param lView Current `LView`
  21595. * @param index Index of `ɵɵi18nStart` instruction.
  21596. * @param message Message to translate.
  21597. * @param subTemplateIndex Index into the sub template of message translation. (ie in case of
  21598. * `ngIf`) (-1 otherwise)
  21599. */
  21600. function i18nStartFirstCreatePass(tView, parentTNodeIndex, lView, index, message, subTemplateIndex) {
  21601. const rootTNode = getCurrentParentTNode();
  21602. const createOpCodes = [];
  21603. const updateOpCodes = [];
  21604. const existingTNodeStack = [[]];
  21605. if (ngDevMode) {
  21606. attachDebugGetter(createOpCodes, i18nCreateOpCodesToString);
  21607. attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
  21608. }
  21609. message = getTranslationForTemplate(message, subTemplateIndex);
  21610. const msgParts = replaceNgsp(message).split(PH_REGEXP);
  21611. for (let i = 0; i < msgParts.length; i++) {
  21612. let value = msgParts[i];
  21613. if ((i & 1) === 0) {
  21614. // Even indexes are text (including bindings & ICU expressions)
  21615. const parts = i18nParseTextIntoPartsAndICU(value);
  21616. for (let j = 0; j < parts.length; j++) {
  21617. let part = parts[j];
  21618. if ((j & 1) === 0) {
  21619. // `j` is odd therefore `part` is string
  21620. const text = part;
  21621. ngDevMode && assertString(text, 'Parsed ICU part should be string');
  21622. if (text !== '') {
  21623. i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodeStack[0], createOpCodes, updateOpCodes, lView, text);
  21624. }
  21625. }
  21626. else {
  21627. // `j` is Even therefor `part` is an `ICUExpression`
  21628. const icuExpression = part;
  21629. // Verify that ICU expression has the right shape. Translations might contain invalid
  21630. // constructions (while original messages were correct), so ICU parsing at runtime may
  21631. // not succeed (thus `icuExpression` remains a string).
  21632. // Note: we intentionally retain the error here by not using `ngDevMode`, because
  21633. // the value can change based on the locale and users aren't guaranteed to hit
  21634. // an invalid string while they're developing.
  21635. if (typeof icuExpression !== 'object') {
  21636. throw new Error(`Unable to parse ICU expression in "${message}" message.`);
  21637. }
  21638. const icuContainerTNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodeStack[0], lView, createOpCodes, ngDevMode ? `ICU ${index}:${icuExpression.mainBinding}` : '', true);
  21639. const icuNodeIndex = icuContainerTNode.index;
  21640. ngDevMode &&
  21641. assertGreaterThanOrEqual(icuNodeIndex, HEADER_OFFSET, 'Index must be in absolute LView offset');
  21642. icuStart(tView, lView, updateOpCodes, parentTNodeIndex, icuExpression, icuNodeIndex);
  21643. }
  21644. }
  21645. }
  21646. else {
  21647. // Odd indexes are placeholders (elements and sub-templates)
  21648. // At this point value is something like: '/#1:2' (originally coming from '�/#1:2�')
  21649. const isClosing = value.charCodeAt(0) === 47 /* CharCode.SLASH */;
  21650. const type = value.charCodeAt(isClosing ? 1 : 0);
  21651. ngDevMode && assertOneOf(type, 42 /* CharCode.STAR */, 35 /* CharCode.HASH */);
  21652. const index = HEADER_OFFSET + Number.parseInt(value.substring((isClosing ? 2 : 1)));
  21653. if (isClosing) {
  21654. existingTNodeStack.shift();
  21655. setCurrentTNode(getCurrentParentTNode(), false);
  21656. }
  21657. else {
  21658. const tNode = createTNodePlaceholder(tView, existingTNodeStack[0], index);
  21659. existingTNodeStack.unshift([]);
  21660. setCurrentTNode(tNode, true);
  21661. }
  21662. }
  21663. }
  21664. tView.data[index] = {
  21665. create: createOpCodes,
  21666. update: updateOpCodes,
  21667. };
  21668. }
  21669. /**
  21670. * Allocate space in i18n Range add create OpCode instruction to create a text or comment node.
  21671. *
  21672. * @param tView Current `TView` needed to allocate space in i18n range.
  21673. * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will be
  21674. * added as part of the `i18nStart` instruction or as part of the `TNode.insertBeforeIndex`.
  21675. * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
  21676. * @param lView Current `LView` needed to allocate space in i18n range.
  21677. * @param createOpCodes Array storing `I18nCreateOpCodes` where new opCodes will be added.
  21678. * @param text Text to be added when the `Text` or `Comment` node will be created.
  21679. * @param isICU true if a `Comment` node for ICU (instead of `Text`) node should be created.
  21680. */
  21681. function createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, text, isICU) {
  21682. const i18nNodeIdx = allocExpando(tView, lView, 1, null);
  21683. let opCode = i18nNodeIdx << I18nCreateOpCode.SHIFT;
  21684. let parentTNode = getCurrentParentTNode();
  21685. if (rootTNode === parentTNode) {
  21686. // FIXME(misko): A null `parentTNode` should represent when we fall of the `LView` boundary.
  21687. // (there is no parent), but in some circumstances (because we are inconsistent about how we set
  21688. // `previousOrParentTNode`) it could point to `rootTNode` So this is a work around.
  21689. parentTNode = null;
  21690. }
  21691. if (parentTNode === null) {
  21692. // If we don't have a parent that means that we can eagerly add nodes.
  21693. // If we have a parent than these nodes can't be added now (as the parent has not been created
  21694. // yet) and instead the `parentTNode` is responsible for adding it. See
  21695. // `TNode.insertBeforeIndex`
  21696. opCode |= I18nCreateOpCode.APPEND_EAGERLY;
  21697. }
  21698. if (isICU) {
  21699. opCode |= I18nCreateOpCode.COMMENT;
  21700. ensureIcuContainerVisitorLoaded(loadIcuContainerVisitor);
  21701. }
  21702. createOpCodes.push(opCode, text === null ? '' : text);
  21703. // We store `{{?}}` so that when looking at debug `TNodeType.template` we can see where the
  21704. // bindings are.
  21705. const tNode = createTNodeAtIndex(tView, i18nNodeIdx, isICU ? 32 /* TNodeType.Icu */ : 1 /* TNodeType.Text */, text === null ? (ngDevMode ? '{{?}}' : '') : text, null);
  21706. addTNodeAndUpdateInsertBeforeIndex(existingTNodes, tNode);
  21707. const tNodeIdx = tNode.index;
  21708. setCurrentTNode(tNode, false /* Text nodes are self closing */);
  21709. if (parentTNode !== null && rootTNode !== parentTNode) {
  21710. // We are a child of deeper node (rather than a direct child of `i18nStart` instruction.)
  21711. // We have to make sure to add ourselves to the parent.
  21712. setTNodeInsertBeforeIndex(parentTNode, tNodeIdx);
  21713. }
  21714. return tNode;
  21715. }
  21716. /**
  21717. * Processes text node in i18n block.
  21718. *
  21719. * Text nodes can have:
  21720. * - Create instruction in `createOpCodes` for creating the text node.
  21721. * - Allocate spec for text node in i18n range of `LView`
  21722. * - If contains binding:
  21723. * - bindings => allocate space in i18n range of `LView` to store the binding value.
  21724. * - populate `updateOpCodes` with update instructions.
  21725. *
  21726. * @param tView Current `TView`
  21727. * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will
  21728. * be added as part of the `i18nStart` instruction or as part of the
  21729. * `TNode.insertBeforeIndex`.
  21730. * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
  21731. * @param createOpCodes Location where the creation OpCodes will be stored.
  21732. * @param lView Current `LView`
  21733. * @param text The translated text (which may contain binding)
  21734. */
  21735. function i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodes, createOpCodes, updateOpCodes, lView, text) {
  21736. const hasBinding = text.match(BINDING_REGEXP);
  21737. const tNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, hasBinding ? null : text, false);
  21738. if (hasBinding) {
  21739. generateBindingUpdateOpCodes(updateOpCodes, text, tNode.index, null, 0, null);
  21740. }
  21741. }
  21742. /**
  21743. * See `i18nAttributes` above.
  21744. */
  21745. function i18nAttributesFirstPass(tView, index, values) {
  21746. const previousElement = getCurrentTNode();
  21747. const previousElementIndex = previousElement.index;
  21748. const updateOpCodes = [];
  21749. if (ngDevMode) {
  21750. attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
  21751. }
  21752. if (tView.firstCreatePass && tView.data[index] === null) {
  21753. for (let i = 0; i < values.length; i += 2) {
  21754. const attrName = values[i];
  21755. const message = values[i + 1];
  21756. if (message !== '') {
  21757. // Check if attribute value contains an ICU and throw an error if that's the case.
  21758. // ICUs in element attributes are not supported.
  21759. // Note: we intentionally retain the error here by not using `ngDevMode`, because
  21760. // the `value` can change based on the locale and users aren't guaranteed to hit
  21761. // an invalid string while they're developing.
  21762. if (ICU_REGEXP.test(message)) {
  21763. throw new Error(`ICU expressions are not supported in attributes. Message: "${message}".`);
  21764. }
  21765. // i18n attributes that hit this code path are guaranteed to have bindings, because
  21766. // the compiler treats static i18n attributes as regular attribute bindings.
  21767. // Since this may not be the first i18n attribute on this element we need to pass in how
  21768. // many previous bindings there have already been.
  21769. generateBindingUpdateOpCodes(updateOpCodes, message, previousElementIndex, attrName, countBindings(updateOpCodes), null);
  21770. }
  21771. }
  21772. tView.data[index] = updateOpCodes;
  21773. }
  21774. }
  21775. /**
  21776. * Generate the OpCodes to update the bindings of a string.
  21777. *
  21778. * @param updateOpCodes Place where the update opcodes will be stored.
  21779. * @param str The string containing the bindings.
  21780. * @param destinationNode Index of the destination node which will receive the binding.
  21781. * @param attrName Name of the attribute, if the string belongs to an attribute.
  21782. * @param sanitizeFn Sanitization function used to sanitize the string after update, if necessary.
  21783. * @param bindingStart The lView index of the next expression that can be bound via an opCode.
  21784. * @returns The mask value for these bindings
  21785. */
  21786. function generateBindingUpdateOpCodes(updateOpCodes, str, destinationNode, attrName, bindingStart, sanitizeFn) {
  21787. ngDevMode &&
  21788. assertGreaterThanOrEqual(destinationNode, HEADER_OFFSET, 'Index must be in absolute LView offset');
  21789. const maskIndex = updateOpCodes.length; // Location of mask
  21790. const sizeIndex = maskIndex + 1; // location of size for skipping
  21791. updateOpCodes.push(null, null); // Alloc space for mask and size
  21792. const startIndex = maskIndex + 2; // location of first allocation.
  21793. if (ngDevMode) {
  21794. attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
  21795. }
  21796. const textParts = str.split(BINDING_REGEXP);
  21797. let mask = 0;
  21798. for (let j = 0; j < textParts.length; j++) {
  21799. const textValue = textParts[j];
  21800. if (j & 1) {
  21801. // Odd indexes are bindings
  21802. const bindingIndex = bindingStart + parseInt(textValue, 10);
  21803. updateOpCodes.push(-1 - bindingIndex);
  21804. mask = mask | toMaskBit(bindingIndex);
  21805. }
  21806. else if (textValue !== '') {
  21807. // Even indexes are text
  21808. updateOpCodes.push(textValue);
  21809. }
  21810. }
  21811. updateOpCodes.push(destinationNode << 2 /* I18nUpdateOpCode.SHIFT_REF */ |
  21812. (attrName ? 1 /* I18nUpdateOpCode.Attr */ : 0 /* I18nUpdateOpCode.Text */));
  21813. if (attrName) {
  21814. updateOpCodes.push(attrName, sanitizeFn);
  21815. }
  21816. updateOpCodes[maskIndex] = mask;
  21817. updateOpCodes[sizeIndex] = updateOpCodes.length - startIndex;
  21818. return mask;
  21819. }
  21820. /**
  21821. * Count the number of bindings in the given `opCodes`.
  21822. *
  21823. * It could be possible to speed this up, by passing the number of bindings found back from
  21824. * `generateBindingUpdateOpCodes()` to `i18nAttributesFirstPass()` but this would then require more
  21825. * complexity in the code and/or transient objects to be created.
  21826. *
  21827. * Since this function is only called once when the template is instantiated, is trivial in the
  21828. * first instance (since `opCodes` will be an empty array), and it is not common for elements to
  21829. * contain multiple i18n bound attributes, it seems like this is a reasonable compromise.
  21830. */
  21831. function countBindings(opCodes) {
  21832. let count = 0;
  21833. for (let i = 0; i < opCodes.length; i++) {
  21834. const opCode = opCodes[i];
  21835. // Bindings are negative numbers.
  21836. if (typeof opCode === 'number' && opCode < 0) {
  21837. count++;
  21838. }
  21839. }
  21840. return count;
  21841. }
  21842. /**
  21843. * Convert binding index to mask bit.
  21844. *
  21845. * Each index represents a single bit on the bit-mask. Because bit-mask only has 32 bits, we make
  21846. * the 32nd bit share all masks for all bindings higher than 32. Since it is extremely rare to
  21847. * have more than 32 bindings this will be hit very rarely. The downside of hitting this corner
  21848. * case is that we will execute binding code more often than necessary. (penalty of performance)
  21849. */
  21850. function toMaskBit(bindingIndex) {
  21851. return 1 << Math.min(bindingIndex, 31);
  21852. }
  21853. function isRootTemplateMessage(subTemplateIndex) {
  21854. return subTemplateIndex === -1;
  21855. }
  21856. /**
  21857. * Removes everything inside the sub-templates of a message.
  21858. */
  21859. function removeInnerTemplateTranslation(message) {
  21860. let match;
  21861. let res = '';
  21862. let index = 0;
  21863. let inTemplate = false;
  21864. let tagMatched;
  21865. while ((match = SUBTEMPLATE_REGEXP.exec(message)) !== null) {
  21866. if (!inTemplate) {
  21867. res += message.substring(index, match.index + match[0].length);
  21868. tagMatched = match[1];
  21869. inTemplate = true;
  21870. }
  21871. else {
  21872. if (match[0] === `${MARKER}/*${tagMatched}${MARKER}`) {
  21873. index = match.index;
  21874. inTemplate = false;
  21875. }
  21876. }
  21877. }
  21878. ngDevMode &&
  21879. assertEqual(inTemplate, false, `Tag mismatch: unable to find the end of the sub-template in the translation "${message}"`);
  21880. res += message.slice(index);
  21881. return res;
  21882. }
  21883. /**
  21884. * Extracts a part of a message and removes the rest.
  21885. *
  21886. * This method is used for extracting a part of the message associated with a template. A
  21887. * translated message can span multiple templates.
  21888. *
  21889. * Example:
  21890. * ```
  21891. * <div i18n>Translate <span *ngIf>me</span>!</div>
  21892. * ```
  21893. *
  21894. * @param message The message to crop
  21895. * @param subTemplateIndex Index of the sub-template to extract. If undefined it returns the
  21896. * external template and removes all sub-templates.
  21897. */
  21898. function getTranslationForTemplate(message, subTemplateIndex) {
  21899. if (isRootTemplateMessage(subTemplateIndex)) {
  21900. // We want the root template message, ignore all sub-templates
  21901. return removeInnerTemplateTranslation(message);
  21902. }
  21903. else {
  21904. // We want a specific sub-template
  21905. const start = message.indexOf(`:${subTemplateIndex}${MARKER}`) + 2 + subTemplateIndex.toString().length;
  21906. const end = message.search(new RegExp(`${MARKER}\\/\\*\\d+:${subTemplateIndex}${MARKER}`));
  21907. return removeInnerTemplateTranslation(message.substring(start, end));
  21908. }
  21909. }
  21910. /**
  21911. * Generate the OpCodes for ICU expressions.
  21912. *
  21913. * @param icuExpression
  21914. * @param index Index where the anchor is stored and an optional `TIcuContainerNode`
  21915. * - `lView[anchorIdx]` points to a `Comment` node representing the anchor for the ICU.
  21916. * - `tView.data[anchorIdx]` points to the `TIcuContainerNode` if ICU is root (`null` otherwise)
  21917. */
  21918. function icuStart(tView, lView, updateOpCodes, parentIdx, icuExpression, anchorIdx) {
  21919. ngDevMode && assertDefined(icuExpression, 'ICU expression must be defined');
  21920. let bindingMask = 0;
  21921. const tIcu = {
  21922. type: icuExpression.type,
  21923. currentCaseLViewIndex: allocExpando(tView, lView, 1, null),
  21924. anchorIdx,
  21925. cases: [],
  21926. create: [],
  21927. remove: [],
  21928. update: []
  21929. };
  21930. addUpdateIcuSwitch(updateOpCodes, icuExpression, anchorIdx);
  21931. setTIcu(tView, anchorIdx, tIcu);
  21932. const values = icuExpression.values;
  21933. for (let i = 0; i < values.length; i++) {
  21934. // Each value is an array of strings & other ICU expressions
  21935. const valueArr = values[i];
  21936. const nestedIcus = [];
  21937. for (let j = 0; j < valueArr.length; j++) {
  21938. const value = valueArr[j];
  21939. if (typeof value !== 'string') {
  21940. // It is an nested ICU expression
  21941. const icuIndex = nestedIcus.push(value) - 1;
  21942. // Replace nested ICU expression by a comment node
  21943. valueArr[j] = `<!--�${icuIndex}�-->`;
  21944. }
  21945. }
  21946. bindingMask = parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, icuExpression.cases[i], valueArr.join(''), nestedIcus) |
  21947. bindingMask;
  21948. }
  21949. if (bindingMask) {
  21950. addUpdateIcuUpdate(updateOpCodes, bindingMask, anchorIdx);
  21951. }
  21952. }
  21953. /**
  21954. * Parses text containing an ICU expression and produces a JSON object for it.
  21955. * Original code from closure library, modified for Angular.
  21956. *
  21957. * @param pattern Text containing an ICU expression that needs to be parsed.
  21958. *
  21959. */
  21960. function parseICUBlock(pattern) {
  21961. const cases = [];
  21962. const values = [];
  21963. let icuType = 1 /* IcuType.plural */;
  21964. let mainBinding = 0;
  21965. pattern = pattern.replace(ICU_BLOCK_REGEXP, function (str, binding, type) {
  21966. if (type === 'select') {
  21967. icuType = 0 /* IcuType.select */;
  21968. }
  21969. else {
  21970. icuType = 1 /* IcuType.plural */;
  21971. }
  21972. mainBinding = parseInt(binding.slice(1), 10);
  21973. return '';
  21974. });
  21975. const parts = i18nParseTextIntoPartsAndICU(pattern);
  21976. // Looking for (key block)+ sequence. One of the keys has to be "other".
  21977. for (let pos = 0; pos < parts.length;) {
  21978. let key = parts[pos++].trim();
  21979. if (icuType === 1 /* IcuType.plural */) {
  21980. // Key can be "=x", we just want "x"
  21981. key = key.replace(/\s*(?:=)?(\w+)\s*/, '$1');
  21982. }
  21983. if (key.length) {
  21984. cases.push(key);
  21985. }
  21986. const blocks = i18nParseTextIntoPartsAndICU(parts[pos++]);
  21987. if (cases.length > values.length) {
  21988. values.push(blocks);
  21989. }
  21990. }
  21991. // TODO(ocombe): support ICU expressions in attributes, see #21615
  21992. return { type: icuType, mainBinding: mainBinding, cases, values };
  21993. }
  21994. /**
  21995. * Breaks pattern into strings and top level {...} blocks.
  21996. * Can be used to break a message into text and ICU expressions, or to break an ICU expression
  21997. * into keys and cases. Original code from closure library, modified for Angular.
  21998. *
  21999. * @param pattern (sub)Pattern to be broken.
  22000. * @returns An `Array<string|IcuExpression>` where:
  22001. * - odd positions: `string` => text between ICU expressions
  22002. * - even positions: `ICUExpression` => ICU expression parsed into `ICUExpression` record.
  22003. */
  22004. function i18nParseTextIntoPartsAndICU(pattern) {
  22005. if (!pattern) {
  22006. return [];
  22007. }
  22008. let prevPos = 0;
  22009. const braceStack = [];
  22010. const results = [];
  22011. const braces = /[{}]/g;
  22012. // lastIndex doesn't get set to 0 so we have to.
  22013. braces.lastIndex = 0;
  22014. let match;
  22015. while (match = braces.exec(pattern)) {
  22016. const pos = match.index;
  22017. if (match[0] == '}') {
  22018. braceStack.pop();
  22019. if (braceStack.length == 0) {
  22020. // End of the block.
  22021. const block = pattern.substring(prevPos, pos);
  22022. if (ICU_BLOCK_REGEXP.test(block)) {
  22023. results.push(parseICUBlock(block));
  22024. }
  22025. else {
  22026. results.push(block);
  22027. }
  22028. prevPos = pos + 1;
  22029. }
  22030. }
  22031. else {
  22032. if (braceStack.length == 0) {
  22033. const substring = pattern.substring(prevPos, pos);
  22034. results.push(substring);
  22035. prevPos = pos + 1;
  22036. }
  22037. braceStack.push('{');
  22038. }
  22039. }
  22040. const substring = pattern.substring(prevPos);
  22041. results.push(substring);
  22042. return results;
  22043. }
  22044. /**
  22045. * Parses a node, its children and its siblings, and generates the mutate & update OpCodes.
  22046. *
  22047. */
  22048. function parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, caseName, unsafeCaseHtml, nestedIcus) {
  22049. const create = [];
  22050. const remove = [];
  22051. const update = [];
  22052. if (ngDevMode) {
  22053. attachDebugGetter(create, icuCreateOpCodesToString);
  22054. attachDebugGetter(remove, i18nRemoveOpCodesToString);
  22055. attachDebugGetter(update, i18nUpdateOpCodesToString);
  22056. }
  22057. tIcu.cases.push(caseName);
  22058. tIcu.create.push(create);
  22059. tIcu.remove.push(remove);
  22060. tIcu.update.push(update);
  22061. const inertBodyHelper = getInertBodyHelper(getDocument());
  22062. const inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeCaseHtml);
  22063. ngDevMode && assertDefined(inertBodyElement, 'Unable to generate inert body element');
  22064. const inertRootNode = getTemplateContent(inertBodyElement) || inertBodyElement;
  22065. if (inertRootNode) {
  22066. return walkIcuTree(tView, tIcu, lView, updateOpCodes, create, remove, update, inertRootNode, parentIdx, nestedIcus, 0);
  22067. }
  22068. else {
  22069. return 0;
  22070. }
  22071. }
  22072. function walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, parentNode, parentIdx, nestedIcus, depth) {
  22073. let bindingMask = 0;
  22074. let currentNode = parentNode.firstChild;
  22075. while (currentNode) {
  22076. const newIndex = allocExpando(tView, lView, 1, null);
  22077. switch (currentNode.nodeType) {
  22078. case Node.ELEMENT_NODE:
  22079. const element = currentNode;
  22080. const tagName = element.tagName.toLowerCase();
  22081. if (VALID_ELEMENTS.hasOwnProperty(tagName)) {
  22082. addCreateNodeAndAppend(create, ELEMENT_MARKER, tagName, parentIdx, newIndex);
  22083. tView.data[newIndex] = tagName;
  22084. const elAttrs = element.attributes;
  22085. for (let i = 0; i < elAttrs.length; i++) {
  22086. const attr = elAttrs.item(i);
  22087. const lowerAttrName = attr.name.toLowerCase();
  22088. const hasBinding = !!attr.value.match(BINDING_REGEXP);
  22089. // we assume the input string is safe, unless it's using a binding
  22090. if (hasBinding) {
  22091. if (VALID_ATTRS.hasOwnProperty(lowerAttrName)) {
  22092. if (URI_ATTRS[lowerAttrName]) {
  22093. generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, _sanitizeUrl);
  22094. }
  22095. else {
  22096. generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, null);
  22097. }
  22098. }
  22099. else {
  22100. ngDevMode &&
  22101. console.warn(`WARNING: ignoring unsafe attribute value ` +
  22102. `${lowerAttrName} on element ${tagName} ` +
  22103. `(see ${XSS_SECURITY_URL})`);
  22104. }
  22105. }
  22106. else {
  22107. addCreateAttribute(create, newIndex, attr);
  22108. }
  22109. }
  22110. // Parse the children of this node (if any)
  22111. bindingMask = walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, currentNode, newIndex, nestedIcus, depth + 1) |
  22112. bindingMask;
  22113. addRemoveNode(remove, newIndex, depth);
  22114. }
  22115. break;
  22116. case Node.TEXT_NODE:
  22117. const value = currentNode.textContent || '';
  22118. const hasBinding = value.match(BINDING_REGEXP);
  22119. addCreateNodeAndAppend(create, null, hasBinding ? '' : value, parentIdx, newIndex);
  22120. addRemoveNode(remove, newIndex, depth);
  22121. if (hasBinding) {
  22122. bindingMask =
  22123. generateBindingUpdateOpCodes(update, value, newIndex, null, 0, null) | bindingMask;
  22124. }
  22125. break;
  22126. case Node.COMMENT_NODE:
  22127. // Check if the comment node is a placeholder for a nested ICU
  22128. const isNestedIcu = NESTED_ICU.exec(currentNode.textContent || '');
  22129. if (isNestedIcu) {
  22130. const nestedIcuIndex = parseInt(isNestedIcu[1], 10);
  22131. const icuExpression = nestedIcus[nestedIcuIndex];
  22132. // Create the comment node that will anchor the ICU expression
  22133. addCreateNodeAndAppend(create, ICU_MARKER, ngDevMode ? `nested ICU ${nestedIcuIndex}` : '', parentIdx, newIndex);
  22134. icuStart(tView, lView, sharedUpdateOpCodes, parentIdx, icuExpression, newIndex);
  22135. addRemoveNestedIcu(remove, newIndex, depth);
  22136. }
  22137. break;
  22138. }
  22139. currentNode = currentNode.nextSibling;
  22140. }
  22141. return bindingMask;
  22142. }
  22143. function addRemoveNode(remove, index, depth) {
  22144. if (depth === 0) {
  22145. remove.push(index);
  22146. }
  22147. }
  22148. function addRemoveNestedIcu(remove, index, depth) {
  22149. if (depth === 0) {
  22150. remove.push(~index); // remove ICU at `index`
  22151. remove.push(index); // remove ICU comment at `index`
  22152. }
  22153. }
  22154. function addUpdateIcuSwitch(update, icuExpression, index) {
  22155. update.push(toMaskBit(icuExpression.mainBinding), 2, -1 - icuExpression.mainBinding, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 2 /* I18nUpdateOpCode.IcuSwitch */);
  22156. }
  22157. function addUpdateIcuUpdate(update, bindingMask, index) {
  22158. update.push(bindingMask, 1, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 3 /* I18nUpdateOpCode.IcuUpdate */);
  22159. }
  22160. function addCreateNodeAndAppend(create, marker, text, appendToParentIdx, createAtIdx) {
  22161. if (marker !== null) {
  22162. create.push(marker);
  22163. }
  22164. create.push(text, createAtIdx, icuCreateOpCode(0 /* IcuCreateOpCode.AppendChild */, appendToParentIdx, createAtIdx));
  22165. }
  22166. function addCreateAttribute(create, newIndex, attr) {
  22167. create.push(newIndex << 1 /* IcuCreateOpCode.SHIFT_REF */ | 1 /* IcuCreateOpCode.Attr */, attr.name, attr.value);
  22168. }
  22169. // i18nPostprocess consts
  22170. const ROOT_TEMPLATE_ID = 0;
  22171. const PP_MULTI_VALUE_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]/;
  22172. const PP_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]|(�\/?\*\d+:\d+�)/g;
  22173. const PP_ICU_VARS_REGEXP = /({\s*)(VAR_(PLURAL|SELECT)(_\d+)?)(\s*,)/g;
  22174. const PP_ICU_PLACEHOLDERS_REGEXP = /{([A-Z0-9_]+)}/g;
  22175. const PP_ICUS_REGEXP = /�I18N_EXP_(ICU(_\d+)?)�/g;
  22176. const PP_CLOSE_TEMPLATE_REGEXP = /\/\*/;
  22177. const PP_TEMPLATE_ID_REGEXP = /\d+\:(\d+)/;
  22178. /**
  22179. * Handles message string post-processing for internationalization.
  22180. *
  22181. * Handles message string post-processing by transforming it from intermediate
  22182. * format (that might contain some markers that we need to replace) to the final
  22183. * form, consumable by i18nStart instruction. Post processing steps include:
  22184. *
  22185. * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�])
  22186. * 2. Replace all ICU vars (like "VAR_PLURAL")
  22187. * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
  22188. * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�)
  22189. * in case multiple ICUs have the same placeholder name
  22190. *
  22191. * @param message Raw translation string for post processing
  22192. * @param replacements Set of replacements that should be applied
  22193. *
  22194. * @returns Transformed string that can be consumed by i18nStart instruction
  22195. *
  22196. * @codeGenApi
  22197. */
  22198. function i18nPostprocess(message, replacements = {}) {
  22199. /**
  22200. * Step 1: resolve all multi-value placeholders like [�#5�|�*1:1��#2:1�|�#4:1�]
  22201. *
  22202. * Note: due to the way we process nested templates (BFS), multi-value placeholders are typically
  22203. * grouped by templates, for example: [�#5�|�#6�|�#1:1�|�#3:2�] where �#5� and �#6� belong to root
  22204. * template, �#1:1� belong to nested template with index 1 and �#1:2� - nested template with index
  22205. * 3. However in real templates the order might be different: i.e. �#1:1� and/or �#3:2� may go in
  22206. * front of �#6�. The post processing step restores the right order by keeping track of the
  22207. * template id stack and looks for placeholders that belong to the currently active template.
  22208. */
  22209. let result = message;
  22210. if (PP_MULTI_VALUE_PLACEHOLDERS_REGEXP.test(message)) {
  22211. const matches = {};
  22212. const templateIdsStack = [ROOT_TEMPLATE_ID];
  22213. result = result.replace(PP_PLACEHOLDERS_REGEXP, (m, phs, tmpl) => {
  22214. const content = phs || tmpl;
  22215. const placeholders = matches[content] || [];
  22216. if (!placeholders.length) {
  22217. content.split('|').forEach((placeholder) => {
  22218. const match = placeholder.match(PP_TEMPLATE_ID_REGEXP);
  22219. const templateId = match ? parseInt(match[1], 10) : ROOT_TEMPLATE_ID;
  22220. const isCloseTemplateTag = PP_CLOSE_TEMPLATE_REGEXP.test(placeholder);
  22221. placeholders.push([templateId, isCloseTemplateTag, placeholder]);
  22222. });
  22223. matches[content] = placeholders;
  22224. }
  22225. if (!placeholders.length) {
  22226. throw new Error(`i18n postprocess: unmatched placeholder - ${content}`);
  22227. }
  22228. const currentTemplateId = templateIdsStack[templateIdsStack.length - 1];
  22229. let idx = 0;
  22230. // find placeholder index that matches current template id
  22231. for (let i = 0; i < placeholders.length; i++) {
  22232. if (placeholders[i][0] === currentTemplateId) {
  22233. idx = i;
  22234. break;
  22235. }
  22236. }
  22237. // update template id stack based on the current tag extracted
  22238. const [templateId, isCloseTemplateTag, placeholder] = placeholders[idx];
  22239. if (isCloseTemplateTag) {
  22240. templateIdsStack.pop();
  22241. }
  22242. else if (currentTemplateId !== templateId) {
  22243. templateIdsStack.push(templateId);
  22244. }
  22245. // remove processed tag from the list
  22246. placeholders.splice(idx, 1);
  22247. return placeholder;
  22248. });
  22249. }
  22250. // return current result if no replacements specified
  22251. if (!Object.keys(replacements).length) {
  22252. return result;
  22253. }
  22254. /**
  22255. * Step 2: replace all ICU vars (like "VAR_PLURAL")
  22256. */
  22257. result = result.replace(PP_ICU_VARS_REGEXP, (match, start, key, _type, _idx, end) => {
  22258. return replacements.hasOwnProperty(key) ? `${start}${replacements[key]}${end}` : match;
  22259. });
  22260. /**
  22261. * Step 3: replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
  22262. */
  22263. result = result.replace(PP_ICU_PLACEHOLDERS_REGEXP, (match, key) => {
  22264. return replacements.hasOwnProperty(key) ? replacements[key] : match;
  22265. });
  22266. /**
  22267. * Step 4: replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�) in case
  22268. * multiple ICUs have the same placeholder name
  22269. */
  22270. result = result.replace(PP_ICUS_REGEXP, (match, key) => {
  22271. if (replacements.hasOwnProperty(key)) {
  22272. const list = replacements[key];
  22273. if (!list.length) {
  22274. throw new Error(`i18n postprocess: unmatched ICU - ${match} with key: ${key}`);
  22275. }
  22276. return list.shift();
  22277. }
  22278. return match;
  22279. });
  22280. return result;
  22281. }
  22282. /**
  22283. * Marks a block of text as translatable.
  22284. *
  22285. * The instructions `i18nStart` and `i18nEnd` mark the translation block in the template.
  22286. * The translation `message` is the value which is locale specific. The translation string may
  22287. * contain placeholders which associate inner elements and sub-templates within the translation.
  22288. *
  22289. * The translation `message` placeholders are:
  22290. * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be
  22291. * interpolated into. The placeholder `index` points to the expression binding index. An optional
  22292. * `block` that matches the sub-template in which it was declared.
  22293. * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning
  22294. * and end of DOM element that were embedded in the original translation block. The placeholder
  22295. * `index` points to the element index in the template instructions set. An optional `block` that
  22296. * matches the sub-template in which it was declared.
  22297. * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be
  22298. * split up and translated separately in each angular template function. The `index` points to the
  22299. * `template` instruction index. A `block` that matches the sub-template in which it was declared.
  22300. *
  22301. * @param index A unique index of the translation in the static block.
  22302. * @param messageIndex An index of the translation message from the `def.consts` array.
  22303. * @param subTemplateIndex Optional sub-template index in the `message`.
  22304. *
  22305. * @codeGenApi
  22306. */
  22307. function ɵɵi18nStart(index, messageIndex, subTemplateIndex = -1) {
  22308. const tView = getTView();
  22309. const lView = getLView();
  22310. const adjustedIndex = HEADER_OFFSET + index;
  22311. ngDevMode && assertDefined(tView, `tView should be defined`);
  22312. const message = getConstant(tView.consts, messageIndex);
  22313. const parentTNode = getCurrentParentTNode();
  22314. if (tView.firstCreatePass) {
  22315. i18nStartFirstCreatePass(tView, parentTNode === null ? 0 : parentTNode.index, lView, adjustedIndex, message, subTemplateIndex);
  22316. }
  22317. // Set a flag that this LView has i18n blocks.
  22318. // The flag is later used to determine whether this component should
  22319. // be hydrated (currently hydration is not supported for i18n blocks).
  22320. if (tView.type === 2 /* TViewType.Embedded */) {
  22321. // Annotate host component's LView (not embedded view's LView),
  22322. // since hydration can be skipped on per-component basis only.
  22323. const componentLView = lView[DECLARATION_COMPONENT_VIEW];
  22324. componentLView[FLAGS] |= 32 /* LViewFlags.HasI18n */;
  22325. }
  22326. else {
  22327. lView[FLAGS] |= 32 /* LViewFlags.HasI18n */;
  22328. }
  22329. const tI18n = tView.data[adjustedIndex];
  22330. const sameViewParentTNode = parentTNode === lView[T_HOST] ? null : parentTNode;
  22331. const parentRNode = getClosestRElement(tView, sameViewParentTNode, lView);
  22332. // If `parentTNode` is an `ElementContainer` than it has `<!--ng-container--->`.
  22333. // When we do inserts we have to make sure to insert in front of `<!--ng-container--->`.
  22334. const insertInFrontOf = parentTNode && (parentTNode.type & 8 /* TNodeType.ElementContainer */) ?
  22335. lView[parentTNode.index] :
  22336. null;
  22337. applyCreateOpCodes(lView, tI18n.create, parentRNode, insertInFrontOf);
  22338. setInI18nBlock(true);
  22339. }
  22340. /**
  22341. * Translates a translation block marked by `i18nStart` and `i18nEnd`. It inserts the text/ICU nodes
  22342. * into the render tree, moves the placeholder nodes and removes the deleted nodes.
  22343. *
  22344. * @codeGenApi
  22345. */
  22346. function ɵɵi18nEnd() {
  22347. setInI18nBlock(false);
  22348. }
  22349. /**
  22350. *
  22351. * Use this instruction to create a translation block that doesn't contain any placeholder.
  22352. * It calls both {@link i18nStart} and {@link i18nEnd} in one instruction.
  22353. *
  22354. * The translation `message` is the value which is locale specific. The translation string may
  22355. * contain placeholders which associate inner elements and sub-templates within the translation.
  22356. *
  22357. * The translation `message` placeholders are:
  22358. * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be
  22359. * interpolated into. The placeholder `index` points to the expression binding index. An optional
  22360. * `block` that matches the sub-template in which it was declared.
  22361. * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning
  22362. * and end of DOM element that were embedded in the original translation block. The placeholder
  22363. * `index` points to the element index in the template instructions set. An optional `block` that
  22364. * matches the sub-template in which it was declared.
  22365. * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be
  22366. * split up and translated separately in each angular template function. The `index` points to the
  22367. * `template` instruction index. A `block` that matches the sub-template in which it was declared.
  22368. *
  22369. * @param index A unique index of the translation in the static block.
  22370. * @param messageIndex An index of the translation message from the `def.consts` array.
  22371. * @param subTemplateIndex Optional sub-template index in the `message`.
  22372. *
  22373. * @codeGenApi
  22374. */
  22375. function ɵɵi18n(index, messageIndex, subTemplateIndex) {
  22376. ɵɵi18nStart(index, messageIndex, subTemplateIndex);
  22377. ɵɵi18nEnd();
  22378. }
  22379. /**
  22380. * Marks a list of attributes as translatable.
  22381. *
  22382. * @param index A unique index in the static block
  22383. * @param values
  22384. *
  22385. * @codeGenApi
  22386. */
  22387. function ɵɵi18nAttributes(index, attrsIndex) {
  22388. const tView = getTView();
  22389. ngDevMode && assertDefined(tView, `tView should be defined`);
  22390. const attrs = getConstant(tView.consts, attrsIndex);
  22391. i18nAttributesFirstPass(tView, index + HEADER_OFFSET, attrs);
  22392. }
  22393. /**
  22394. * Stores the values of the bindings during each update cycle in order to determine if we need to
  22395. * update the translated nodes.
  22396. *
  22397. * @param value The binding's value
  22398. * @returns This function returns itself so that it may be chained
  22399. * (e.g. `i18nExp(ctx.name)(ctx.title)`)
  22400. *
  22401. * @codeGenApi
  22402. */
  22403. function ɵɵi18nExp(value) {
  22404. const lView = getLView();
  22405. setMaskBit(bindingUpdated(lView, nextBindingIndex(), value));
  22406. return ɵɵi18nExp;
  22407. }
  22408. /**
  22409. * Updates a translation block or an i18n attribute when the bindings have changed.
  22410. *
  22411. * @param index Index of either {@link i18nStart} (translation block) or {@link i18nAttributes}
  22412. * (i18n attribute) on which it should update the content.
  22413. *
  22414. * @codeGenApi
  22415. */
  22416. function ɵɵi18nApply(index) {
  22417. applyI18n(getTView(), getLView(), index + HEADER_OFFSET);
  22418. }
  22419. /**
  22420. * Handles message string post-processing for internationalization.
  22421. *
  22422. * Handles message string post-processing by transforming it from intermediate
  22423. * format (that might contain some markers that we need to replace) to the final
  22424. * form, consumable by i18nStart instruction. Post processing steps include:
  22425. *
  22426. * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�])
  22427. * 2. Replace all ICU vars (like "VAR_PLURAL")
  22428. * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
  22429. * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�)
  22430. * in case multiple ICUs have the same placeholder name
  22431. *
  22432. * @param message Raw translation string for post processing
  22433. * @param replacements Set of replacements that should be applied
  22434. *
  22435. * @returns Transformed string that can be consumed by i18nStart instruction
  22436. *
  22437. * @codeGenApi
  22438. */
  22439. function ɵɵi18nPostprocess(message, replacements = {}) {
  22440. return i18nPostprocess(message, replacements);
  22441. }
  22442. /**
  22443. * Creates runtime data structures for `{#defer}` blocks.
  22444. *
  22445. * @param index The index of the defer block in the data array
  22446. * @param deferredDepsFn Function that contains dependencies for this defer block
  22447. *
  22448. * @codeGenApi
  22449. */
  22450. function ɵɵdefer(index, deferredDepsFn) {
  22451. // TODO: implement runtime logic.
  22452. }
  22453. /*
  22454. * This file re-exports all symbols contained in this directory.
  22455. *
  22456. * Why is this file not `index.ts`?
  22457. *
  22458. * There seems to be an inconsistent path resolution of an `index.ts` file
  22459. * when only the parent directory is referenced. This could be due to the
  22460. * node module resolution configuration differing from rollup and/or typescript.
  22461. *
  22462. * With commit
  22463. * https://github.com/angular/angular/commit/d5e3f2c64bd13ce83e7c70788b7fc514ca4a9918
  22464. * the `instructions.ts` file was moved to `instructions/instructions.ts` and an
  22465. * `index.ts` file was used to re-export everything. Having had file names that were
  22466. * importing from `instructions' directly (not the from the sub file or the `index.ts`
  22467. * file) caused strange CI issues. `index.ts` had to be renamed to `all.ts` for this
  22468. * to work.
  22469. *
  22470. * Jira Issue = FW-1184
  22471. */
  22472. /**
  22473. * Resolves the providers which are defined in the DirectiveDef.
  22474. *
  22475. * When inserting the tokens and the factories in their respective arrays, we can assume that
  22476. * this method is called first for the component (if any), and then for other directives on the same
  22477. * node.
  22478. * As a consequence,the providers are always processed in that order:
  22479. * 1) The view providers of the component
  22480. * 2) The providers of the component
  22481. * 3) The providers of the other directives
  22482. * This matches the structure of the injectables arrays of a view (for each node).
  22483. * So the tokens and the factories can be pushed at the end of the arrays, except
  22484. * in one case for multi providers.
  22485. *
  22486. * @param def the directive definition
  22487. * @param providers: Array of `providers`.
  22488. * @param viewProviders: Array of `viewProviders`.
  22489. */
  22490. function providersResolver(def, providers, viewProviders) {
  22491. const tView = getTView();
  22492. if (tView.firstCreatePass) {
  22493. const isComponent = isComponentDef(def);
  22494. // The list of view providers is processed first, and the flags are updated
  22495. resolveProvider(viewProviders, tView.data, tView.blueprint, isComponent, true);
  22496. // Then, the list of providers is processed, and the flags are updated
  22497. resolveProvider(providers, tView.data, tView.blueprint, isComponent, false);
  22498. }
  22499. }
  22500. /**
  22501. * Resolves a provider and publishes it to the DI system.
  22502. */
  22503. function resolveProvider(provider, tInjectables, lInjectablesBlueprint, isComponent, isViewProvider) {
  22504. provider = resolveForwardRef(provider);
  22505. if (Array.isArray(provider)) {
  22506. // Recursively call `resolveProvider`
  22507. // Recursion is OK in this case because this code will not be in hot-path once we implement
  22508. // cloning of the initial state.
  22509. for (let i = 0; i < provider.length; i++) {
  22510. resolveProvider(provider[i], tInjectables, lInjectablesBlueprint, isComponent, isViewProvider);
  22511. }
  22512. }
  22513. else {
  22514. const tView = getTView();
  22515. const lView = getLView();
  22516. const tNode = getCurrentTNode();
  22517. let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide);
  22518. const providerFactory = providerToFactory(provider);
  22519. if (ngDevMode) {
  22520. const injector = new NodeInjector(tNode, lView);
  22521. runInInjectorProfilerContext(injector, token, () => {
  22522. emitProviderConfiguredEvent(provider, isViewProvider);
  22523. });
  22524. }
  22525. const beginIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
  22526. const endIndex = tNode.directiveStart;
  22527. const cptViewProvidersCount = tNode.providerIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */;
  22528. if (isTypeProvider(provider) || !provider.multi) {
  22529. // Single provider case: the factory is created and pushed immediately
  22530. const factory = new NodeInjectorFactory(providerFactory, isViewProvider, ɵɵdirectiveInject);
  22531. const existingFactoryIndex = indexOf(token, tInjectables, isViewProvider ? beginIndex : beginIndex + cptViewProvidersCount, endIndex);
  22532. if (existingFactoryIndex === -1) {
  22533. diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token);
  22534. registerDestroyHooksIfSupported(tView, provider, tInjectables.length);
  22535. tInjectables.push(token);
  22536. tNode.directiveStart++;
  22537. tNode.directiveEnd++;
  22538. if (isViewProvider) {
  22539. tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */;
  22540. }
  22541. lInjectablesBlueprint.push(factory);
  22542. lView.push(factory);
  22543. }
  22544. else {
  22545. lInjectablesBlueprint[existingFactoryIndex] = factory;
  22546. lView[existingFactoryIndex] = factory;
  22547. }
  22548. }
  22549. else {
  22550. // Multi provider case:
  22551. // We create a multi factory which is going to aggregate all the values.
  22552. // Since the output of such a factory depends on content or view injection,
  22553. // we create two of them, which are linked together.
  22554. //
  22555. // The first one (for view providers) is always in the first block of the injectables array,
  22556. // and the second one (for providers) is always in the second block.
  22557. // This is important because view providers have higher priority. When a multi token
  22558. // is being looked up, the view providers should be found first.
  22559. // Note that it is not possible to have a multi factory in the third block (directive block).
  22560. //
  22561. // The algorithm to process multi providers is as follows:
  22562. // 1) If the multi provider comes from the `viewProviders` of the component:
  22563. // a) If the special view providers factory doesn't exist, it is created and pushed.
  22564. // b) Else, the multi provider is added to the existing multi factory.
  22565. // 2) If the multi provider comes from the `providers` of the component or of another
  22566. // directive:
  22567. // a) If the multi factory doesn't exist, it is created and provider pushed into it.
  22568. // It is also linked to the multi factory for view providers, if it exists.
  22569. // b) Else, the multi provider is added to the existing multi factory.
  22570. const existingProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex + cptViewProvidersCount, endIndex);
  22571. const existingViewProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex, beginIndex + cptViewProvidersCount);
  22572. const doesProvidersFactoryExist = existingProvidersFactoryIndex >= 0 &&
  22573. lInjectablesBlueprint[existingProvidersFactoryIndex];
  22574. const doesViewProvidersFactoryExist = existingViewProvidersFactoryIndex >= 0 &&
  22575. lInjectablesBlueprint[existingViewProvidersFactoryIndex];
  22576. if (isViewProvider && !doesViewProvidersFactoryExist ||
  22577. !isViewProvider && !doesProvidersFactoryExist) {
  22578. // Cases 1.a and 2.a
  22579. diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token);
  22580. const factory = multiFactory(isViewProvider ? multiViewProvidersFactoryResolver : multiProvidersFactoryResolver, lInjectablesBlueprint.length, isViewProvider, isComponent, providerFactory);
  22581. if (!isViewProvider && doesViewProvidersFactoryExist) {
  22582. lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory;
  22583. }
  22584. registerDestroyHooksIfSupported(tView, provider, tInjectables.length, 0);
  22585. tInjectables.push(token);
  22586. tNode.directiveStart++;
  22587. tNode.directiveEnd++;
  22588. if (isViewProvider) {
  22589. tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */;
  22590. }
  22591. lInjectablesBlueprint.push(factory);
  22592. lView.push(factory);
  22593. }
  22594. else {
  22595. // Cases 1.b and 2.b
  22596. const indexInFactory = multiFactoryAdd(lInjectablesBlueprint[isViewProvider ? existingViewProvidersFactoryIndex :
  22597. existingProvidersFactoryIndex], providerFactory, !isViewProvider && isComponent);
  22598. registerDestroyHooksIfSupported(tView, provider, existingProvidersFactoryIndex > -1 ? existingProvidersFactoryIndex :
  22599. existingViewProvidersFactoryIndex, indexInFactory);
  22600. }
  22601. if (!isViewProvider && isComponent && doesViewProvidersFactoryExist) {
  22602. lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders++;
  22603. }
  22604. }
  22605. }
  22606. }
  22607. /**
  22608. * Registers the `ngOnDestroy` hook of a provider, if the provider supports destroy hooks.
  22609. * @param tView `TView` in which to register the hook.
  22610. * @param provider Provider whose hook should be registered.
  22611. * @param contextIndex Index under which to find the context for the hook when it's being invoked.
  22612. * @param indexInFactory Only required for `multi` providers. Index of the provider in the multi
  22613. * provider factory.
  22614. */
  22615. function registerDestroyHooksIfSupported(tView, provider, contextIndex, indexInFactory) {
  22616. const providerIsTypeProvider = isTypeProvider(provider);
  22617. const providerIsClassProvider = isClassProvider(provider);
  22618. if (providerIsTypeProvider || providerIsClassProvider) {
  22619. // Resolve forward references as `useClass` can hold a forward reference.
  22620. const classToken = providerIsClassProvider ? resolveForwardRef(provider.useClass) : provider;
  22621. const prototype = classToken.prototype;
  22622. const ngOnDestroy = prototype.ngOnDestroy;
  22623. if (ngOnDestroy) {
  22624. const hooks = tView.destroyHooks || (tView.destroyHooks = []);
  22625. if (!providerIsTypeProvider && provider.multi) {
  22626. ngDevMode &&
  22627. assertDefined(indexInFactory, 'indexInFactory when registering multi factory destroy hook');
  22628. const existingCallbacksIndex = hooks.indexOf(contextIndex);
  22629. if (existingCallbacksIndex === -1) {
  22630. hooks.push(contextIndex, [indexInFactory, ngOnDestroy]);
  22631. }
  22632. else {
  22633. hooks[existingCallbacksIndex + 1].push(indexInFactory, ngOnDestroy);
  22634. }
  22635. }
  22636. else {
  22637. hooks.push(contextIndex, ngOnDestroy);
  22638. }
  22639. }
  22640. }
  22641. }
  22642. /**
  22643. * Add a factory in a multi factory.
  22644. * @returns Index at which the factory was inserted.
  22645. */
  22646. function multiFactoryAdd(multiFactory, factory, isComponentProvider) {
  22647. if (isComponentProvider) {
  22648. multiFactory.componentProviders++;
  22649. }
  22650. return multiFactory.multi.push(factory) - 1;
  22651. }
  22652. /**
  22653. * Returns the index of item in the array, but only in the begin to end range.
  22654. */
  22655. function indexOf(item, arr, begin, end) {
  22656. for (let i = begin; i < end; i++) {
  22657. if (arr[i] === item)
  22658. return i;
  22659. }
  22660. return -1;
  22661. }
  22662. /**
  22663. * Use this with `multi` `providers`.
  22664. */
  22665. function multiProvidersFactoryResolver(_, tData, lData, tNode) {
  22666. return multiResolve(this.multi, []);
  22667. }
  22668. /**
  22669. * Use this with `multi` `viewProviders`.
  22670. *
  22671. * This factory knows how to concatenate itself with the existing `multi` `providers`.
  22672. */
  22673. function multiViewProvidersFactoryResolver(_, tData, lView, tNode) {
  22674. const factories = this.multi;
  22675. let result;
  22676. if (this.providerFactory) {
  22677. const componentCount = this.providerFactory.componentProviders;
  22678. const multiProviders = getNodeInjectable(lView, lView[TVIEW], this.providerFactory.index, tNode);
  22679. // Copy the section of the array which contains `multi` `providers` from the component
  22680. result = multiProviders.slice(0, componentCount);
  22681. // Insert the `viewProvider` instances.
  22682. multiResolve(factories, result);
  22683. // Copy the section of the array which contains `multi` `providers` from other directives
  22684. for (let i = componentCount; i < multiProviders.length; i++) {
  22685. result.push(multiProviders[i]);
  22686. }
  22687. }
  22688. else {
  22689. result = [];
  22690. // Insert the `viewProvider` instances.
  22691. multiResolve(factories, result);
  22692. }
  22693. return result;
  22694. }
  22695. /**
  22696. * Maps an array of factories into an array of values.
  22697. */
  22698. function multiResolve(factories, result) {
  22699. for (let i = 0; i < factories.length; i++) {
  22700. const factory = factories[i];
  22701. result.push(factory());
  22702. }
  22703. return result;
  22704. }
  22705. /**
  22706. * Creates a multi factory.
  22707. */
  22708. function multiFactory(factoryFn, index, isViewProvider, isComponent, f) {
  22709. const factory = new NodeInjectorFactory(factoryFn, isViewProvider, ɵɵdirectiveInject);
  22710. factory.multi = [];
  22711. factory.index = index;
  22712. factory.componentProviders = 0;
  22713. multiFactoryAdd(factory, f, isComponent && !isViewProvider);
  22714. return factory;
  22715. }
  22716. /**
  22717. * This feature resolves the providers of a directive (or component),
  22718. * and publish them into the DI system, making it visible to others for injection.
  22719. *
  22720. * For example:
  22721. * ```ts
  22722. * class ComponentWithProviders {
  22723. * constructor(private greeter: GreeterDE) {}
  22724. *
  22725. * static ɵcmp = defineComponent({
  22726. * type: ComponentWithProviders,
  22727. * selectors: [['component-with-providers']],
  22728. * factory: () => new ComponentWithProviders(directiveInject(GreeterDE as any)),
  22729. * decls: 1,
  22730. * vars: 1,
  22731. * template: function(fs: RenderFlags, ctx: ComponentWithProviders) {
  22732. * if (fs & RenderFlags.Create) {
  22733. * ɵɵtext(0);
  22734. * }
  22735. * if (fs & RenderFlags.Update) {
  22736. * ɵɵtextInterpolate(ctx.greeter.greet());
  22737. * }
  22738. * },
  22739. * features: [ɵɵProvidersFeature([GreeterDE])]
  22740. * });
  22741. * }
  22742. * ```
  22743. *
  22744. * @param definition
  22745. *
  22746. * @codeGenApi
  22747. */
  22748. function ɵɵProvidersFeature(providers, viewProviders = []) {
  22749. return (definition) => {
  22750. definition.providersResolver =
  22751. (def, processProvidersFn) => {
  22752. return providersResolver(def, //
  22753. processProvidersFn ? processProvidersFn(providers) : providers, //
  22754. viewProviders);
  22755. };
  22756. };
  22757. }
  22758. /**
  22759. * Represents an instance of an `NgModule` created by an `NgModuleFactory`.
  22760. * Provides access to the `NgModule` instance and related objects.
  22761. *
  22762. * @publicApi
  22763. */
  22764. class NgModuleRef$1 {
  22765. }
  22766. /**
  22767. * @publicApi
  22768. *
  22769. * @deprecated
  22770. * This class was mostly used as a part of ViewEngine-based JIT API and is no longer needed in Ivy
  22771. * JIT mode. See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes)
  22772. * for additional context. Angular provides APIs that accept NgModule classes directly (such as
  22773. * [PlatformRef.bootstrapModule](api/core/PlatformRef#bootstrapModule) and
  22774. * [createNgModule](api/core/createNgModule)), consider switching to those APIs instead of
  22775. * using factory-based ones.
  22776. */
  22777. class NgModuleFactory$1 {
  22778. }
  22779. /**
  22780. * Returns a new NgModuleRef instance based on the NgModule class and parent injector provided.
  22781. *
  22782. * @param ngModule NgModule class.
  22783. * @param parentInjector Optional injector instance to use as a parent for the module injector. If
  22784. * not provided, `NullInjector` will be used instead.
  22785. * @returns NgModuleRef that represents an NgModule instance.
  22786. *
  22787. * @publicApi
  22788. */
  22789. function createNgModule(ngModule, parentInjector) {
  22790. return new NgModuleRef(ngModule, parentInjector ?? null, []);
  22791. }
  22792. /**
  22793. * The `createNgModule` function alias for backwards-compatibility.
  22794. * Please avoid using it directly and use `createNgModule` instead.
  22795. *
  22796. * @deprecated Use `createNgModule` instead.
  22797. */
  22798. const createNgModuleRef = createNgModule;
  22799. class NgModuleRef extends NgModuleRef$1 {
  22800. constructor(ngModuleType, _parent, additionalProviders) {
  22801. super();
  22802. this._parent = _parent;
  22803. // tslint:disable-next-line:require-internal-with-underscore
  22804. this._bootstrapComponents = [];
  22805. this.destroyCbs = [];
  22806. // When bootstrapping a module we have a dependency graph that looks like this:
  22807. // ApplicationRef -> ComponentFactoryResolver -> NgModuleRef. The problem is that if the
  22808. // module being resolved tries to inject the ComponentFactoryResolver, it'll create a
  22809. // circular dependency which will result in a runtime error, because the injector doesn't
  22810. // exist yet. We work around the issue by creating the ComponentFactoryResolver ourselves
  22811. // and providing it, rather than letting the injector resolve it.
  22812. this.componentFactoryResolver = new ComponentFactoryResolver(this);
  22813. const ngModuleDef = getNgModuleDef(ngModuleType);
  22814. ngDevMode &&
  22815. assertDefined(ngModuleDef, `NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`);
  22816. this._bootstrapComponents = maybeUnwrapFn$1(ngModuleDef.bootstrap);
  22817. this._r3Injector = createInjectorWithoutInjectorInstances(ngModuleType, _parent, [
  22818. { provide: NgModuleRef$1, useValue: this }, {
  22819. provide: ComponentFactoryResolver$1,
  22820. useValue: this.componentFactoryResolver
  22821. },
  22822. ...additionalProviders
  22823. ], stringify(ngModuleType), new Set(['environment']));
  22824. // We need to resolve the injector types separately from the injector creation, because
  22825. // the module might be trying to use this ref in its constructor for DI which will cause a
  22826. // circular error that will eventually error out, because the injector isn't created yet.
  22827. this._r3Injector.resolveInjectorInitializers();
  22828. this.instance = this._r3Injector.get(ngModuleType);
  22829. }
  22830. get injector() {
  22831. return this._r3Injector;
  22832. }
  22833. destroy() {
  22834. ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
  22835. const injector = this._r3Injector;
  22836. !injector.destroyed && injector.destroy();
  22837. this.destroyCbs.forEach(fn => fn());
  22838. this.destroyCbs = null;
  22839. }
  22840. onDestroy(callback) {
  22841. ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
  22842. this.destroyCbs.push(callback);
  22843. }
  22844. }
  22845. class NgModuleFactory extends NgModuleFactory$1 {
  22846. constructor(moduleType) {
  22847. super();
  22848. this.moduleType = moduleType;
  22849. }
  22850. create(parentInjector) {
  22851. return new NgModuleRef(this.moduleType, parentInjector, []);
  22852. }
  22853. }
  22854. function createNgModuleRefWithProviders(moduleType, parentInjector, additionalProviders) {
  22855. return new NgModuleRef(moduleType, parentInjector, additionalProviders);
  22856. }
  22857. class EnvironmentNgModuleRefAdapter extends NgModuleRef$1 {
  22858. constructor(config) {
  22859. super();
  22860. this.componentFactoryResolver = new ComponentFactoryResolver(this);
  22861. this.instance = null;
  22862. const injector = new R3Injector([
  22863. ...config.providers,
  22864. { provide: NgModuleRef$1, useValue: this },
  22865. { provide: ComponentFactoryResolver$1, useValue: this.componentFactoryResolver },
  22866. ], config.parent || getNullInjector(), config.debugName, new Set(['environment']));
  22867. this.injector = injector;
  22868. if (config.runEnvironmentInitializers) {
  22869. injector.resolveInjectorInitializers();
  22870. }
  22871. }
  22872. destroy() {
  22873. this.injector.destroy();
  22874. }
  22875. onDestroy(callback) {
  22876. this.injector.onDestroy(callback);
  22877. }
  22878. }
  22879. /**
  22880. * Create a new environment injector.
  22881. *
  22882. * Learn more about environment injectors in
  22883. * [this guide](guide/standalone-components#environment-injectors).
  22884. *
  22885. * @param providers An array of providers.
  22886. * @param parent A parent environment injector.
  22887. * @param debugName An optional name for this injector instance, which will be used in error
  22888. * messages.
  22889. *
  22890. * @publicApi
  22891. */
  22892. function createEnvironmentInjector(providers, parent, debugName = null) {
  22893. const adapter = new EnvironmentNgModuleRefAdapter({ providers, parent, debugName, runEnvironmentInitializers: true });
  22894. return adapter.injector;
  22895. }
  22896. /**
  22897. * A service used by the framework to create instances of standalone injectors. Those injectors are
  22898. * created on demand in case of dynamic component instantiation and contain ambient providers
  22899. * collected from the imports graph rooted at a given standalone component.
  22900. */
  22901. class StandaloneService {
  22902. constructor(_injector) {
  22903. this._injector = _injector;
  22904. this.cachedInjectors = new Map();
  22905. }
  22906. getOrCreateStandaloneInjector(componentDef) {
  22907. if (!componentDef.standalone) {
  22908. return null;
  22909. }
  22910. if (!this.cachedInjectors.has(componentDef)) {
  22911. const providers = internalImportProvidersFrom(false, componentDef.type);
  22912. const standaloneInjector = providers.length > 0 ?
  22913. createEnvironmentInjector([providers], this._injector, `Standalone[${componentDef.type.name}]`) :
  22914. null;
  22915. this.cachedInjectors.set(componentDef, standaloneInjector);
  22916. }
  22917. return this.cachedInjectors.get(componentDef);
  22918. }
  22919. ngOnDestroy() {
  22920. try {
  22921. for (const injector of this.cachedInjectors.values()) {
  22922. if (injector !== null) {
  22923. injector.destroy();
  22924. }
  22925. }
  22926. }
  22927. finally {
  22928. this.cachedInjectors.clear();
  22929. }
  22930. }
  22931. /** @nocollapse */
  22932. static { this.ɵprov = ɵɵdefineInjectable({
  22933. token: StandaloneService,
  22934. providedIn: 'environment',
  22935. factory: () => new StandaloneService(ɵɵinject(EnvironmentInjector)),
  22936. }); }
  22937. }
  22938. /**
  22939. * A feature that acts as a setup code for the {@link StandaloneService}.
  22940. *
  22941. * The most important responsibility of this feature is to expose the "getStandaloneInjector"
  22942. * function (an entry points to a standalone injector creation) on a component definition object. We
  22943. * go through the features infrastructure to make sure that the standalone injector creation logic
  22944. * is tree-shakable and not included in applications that don't use standalone components.
  22945. *
  22946. * @codeGenApi
  22947. */
  22948. function ɵɵStandaloneFeature(definition) {
  22949. definition.getStandaloneInjector = (parentInjector) => {
  22950. return parentInjector.get(StandaloneService).getOrCreateStandaloneInjector(definition);
  22951. };
  22952. }
  22953. /**
  22954. * Retrieves the component instance associated with a given DOM element.
  22955. *
  22956. * @usageNotes
  22957. * Given the following DOM structure:
  22958. *
  22959. * ```html
  22960. * <app-root>
  22961. * <div>
  22962. * <child-comp></child-comp>
  22963. * </div>
  22964. * </app-root>
  22965. * ```
  22966. *
  22967. * Calling `getComponent` on `<child-comp>` will return the instance of `ChildComponent`
  22968. * associated with this DOM element.
  22969. *
  22970. * Calling the function on `<app-root>` will return the `MyApp` instance.
  22971. *
  22972. *
  22973. * @param element DOM element from which the component should be retrieved.
  22974. * @returns Component instance associated with the element or `null` if there
  22975. * is no component associated with it.
  22976. *
  22977. * @publicApi
  22978. * @globalApi ng
  22979. */
  22980. function getComponent(element) {
  22981. ngDevMode && assertDomElement(element);
  22982. const context = getLContext(element);
  22983. if (context === null)
  22984. return null;
  22985. if (context.component === undefined) {
  22986. const lView = context.lView;
  22987. if (lView === null) {
  22988. return null;
  22989. }
  22990. context.component = getComponentAtNodeIndex(context.nodeIndex, lView);
  22991. }
  22992. return context.component;
  22993. }
  22994. /**
  22995. * If inside an embedded view (e.g. `*ngIf` or `*ngFor`), retrieves the context of the embedded
  22996. * view that the element is part of. Otherwise retrieves the instance of the component whose view
  22997. * owns the element (in this case, the result is the same as calling `getOwningComponent`).
  22998. *
  22999. * @param element Element for which to get the surrounding component instance.
  23000. * @returns Instance of the component that is around the element or null if the element isn't
  23001. * inside any component.
  23002. *
  23003. * @publicApi
  23004. * @globalApi ng
  23005. */
  23006. function getContext(element) {
  23007. assertDomElement(element);
  23008. const context = getLContext(element);
  23009. const lView = context ? context.lView : null;
  23010. return lView === null ? null : lView[CONTEXT];
  23011. }
  23012. /**
  23013. * Retrieves the component instance whose view contains the DOM element.
  23014. *
  23015. * For example, if `<child-comp>` is used in the template of `<app-comp>`
  23016. * (i.e. a `ViewChild` of `<app-comp>`), calling `getOwningComponent` on `<child-comp>`
  23017. * would return `<app-comp>`.
  23018. *
  23019. * @param elementOrDir DOM element, component or directive instance
  23020. * for which to retrieve the root components.
  23021. * @returns Component instance whose view owns the DOM element or null if the element is not
  23022. * part of a component view.
  23023. *
  23024. * @publicApi
  23025. * @globalApi ng
  23026. */
  23027. function getOwningComponent(elementOrDir) {
  23028. const context = getLContext(elementOrDir);
  23029. let lView = context ? context.lView : null;
  23030. if (lView === null)
  23031. return null;
  23032. let parent;
  23033. while (lView[TVIEW].type === 2 /* TViewType.Embedded */ && (parent = getLViewParent(lView))) {
  23034. lView = parent;
  23035. }
  23036. return lView[FLAGS] & 512 /* LViewFlags.IsRoot */ ? null : lView[CONTEXT];
  23037. }
  23038. /**
  23039. * Retrieves all root components associated with a DOM element, directive or component instance.
  23040. * Root components are those which have been bootstrapped by Angular.
  23041. *
  23042. * @param elementOrDir DOM element, component or directive instance
  23043. * for which to retrieve the root components.
  23044. * @returns Root components associated with the target object.
  23045. *
  23046. * @publicApi
  23047. * @globalApi ng
  23048. */
  23049. function getRootComponents(elementOrDir) {
  23050. const lView = readPatchedLView(elementOrDir);
  23051. return lView !== null ? [getRootContext(lView)] : [];
  23052. }
  23053. /**
  23054. * Retrieves an `Injector` associated with an element, component or directive instance.
  23055. *
  23056. * @param elementOrDir DOM element, component or directive instance for which to
  23057. * retrieve the injector.
  23058. * @returns Injector associated with the element, component or directive instance.
  23059. *
  23060. * @publicApi
  23061. * @globalApi ng
  23062. */
  23063. function getInjector(elementOrDir) {
  23064. const context = getLContext(elementOrDir);
  23065. const lView = context ? context.lView : null;
  23066. if (lView === null)
  23067. return Injector.NULL;
  23068. const tNode = lView[TVIEW].data[context.nodeIndex];
  23069. return new NodeInjector(tNode, lView);
  23070. }
  23071. /**
  23072. * Retrieve a set of injection tokens at a given DOM node.
  23073. *
  23074. * @param element Element for which the injection tokens should be retrieved.
  23075. */
  23076. function getInjectionTokens(element) {
  23077. const context = getLContext(element);
  23078. const lView = context ? context.lView : null;
  23079. if (lView === null)
  23080. return [];
  23081. const tView = lView[TVIEW];
  23082. const tNode = tView.data[context.nodeIndex];
  23083. const providerTokens = [];
  23084. const startIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
  23085. const endIndex = tNode.directiveEnd;
  23086. for (let i = startIndex; i < endIndex; i++) {
  23087. let value = tView.data[i];
  23088. if (isDirectiveDefHack(value)) {
  23089. // The fact that we sometimes store Type and sometimes DirectiveDef in this location is a
  23090. // design flaw. We should always store same type so that we can be monomorphic. The issue
  23091. // is that for Components/Directives we store the def instead the type. The correct behavior
  23092. // is that we should always be storing injectable type in this location.
  23093. value = value.type;
  23094. }
  23095. providerTokens.push(value);
  23096. }
  23097. return providerTokens;
  23098. }
  23099. /**
  23100. * Retrieves directive instances associated with a given DOM node. Does not include
  23101. * component instances.
  23102. *
  23103. * @usageNotes
  23104. * Given the following DOM structure:
  23105. *
  23106. * ```html
  23107. * <app-root>
  23108. * <button my-button></button>
  23109. * <my-comp></my-comp>
  23110. * </app-root>
  23111. * ```
  23112. *
  23113. * Calling `getDirectives` on `<button>` will return an array with an instance of the `MyButton`
  23114. * directive that is associated with the DOM node.
  23115. *
  23116. * Calling `getDirectives` on `<my-comp>` will return an empty array.
  23117. *
  23118. * @param node DOM node for which to get the directives.
  23119. * @returns Array of directives associated with the node.
  23120. *
  23121. * @publicApi
  23122. * @globalApi ng
  23123. */
  23124. function getDirectives(node) {
  23125. // Skip text nodes because we can't have directives associated with them.
  23126. if (node instanceof Text) {
  23127. return [];
  23128. }
  23129. const context = getLContext(node);
  23130. const lView = context ? context.lView : null;
  23131. if (lView === null) {
  23132. return [];
  23133. }
  23134. const tView = lView[TVIEW];
  23135. const nodeIndex = context.nodeIndex;
  23136. if (!tView?.data[nodeIndex]) {
  23137. return [];
  23138. }
  23139. if (context.directives === undefined) {
  23140. context.directives = getDirectivesAtNodeIndex(nodeIndex, lView);
  23141. }
  23142. // The `directives` in this case are a named array called `LComponentView`. Clone the
  23143. // result so we don't expose an internal data structure in the user's console.
  23144. return context.directives === null ? [] : [...context.directives];
  23145. }
  23146. /**
  23147. * Returns the debug (partial) metadata for a particular directive or component instance.
  23148. * The function accepts an instance of a directive or component and returns the corresponding
  23149. * metadata.
  23150. *
  23151. * @param directiveOrComponentInstance Instance of a directive or component
  23152. * @returns metadata of the passed directive or component
  23153. *
  23154. * @publicApi
  23155. * @globalApi ng
  23156. */
  23157. function getDirectiveMetadata(directiveOrComponentInstance) {
  23158. const { constructor } = directiveOrComponentInstance;
  23159. if (!constructor) {
  23160. throw new Error('Unable to find the instance constructor');
  23161. }
  23162. // In case a component inherits from a directive, we may have component and directive metadata
  23163. // To ensure we don't get the metadata of the directive, we want to call `getComponentDef` first.
  23164. const componentDef = getComponentDef$1(constructor);
  23165. if (componentDef) {
  23166. return {
  23167. inputs: componentDef.inputs,
  23168. outputs: componentDef.outputs,
  23169. encapsulation: componentDef.encapsulation,
  23170. changeDetection: componentDef.onPush ? ChangeDetectionStrategy.OnPush :
  23171. ChangeDetectionStrategy.Default
  23172. };
  23173. }
  23174. const directiveDef = getDirectiveDef(constructor);
  23175. if (directiveDef) {
  23176. return { inputs: directiveDef.inputs, outputs: directiveDef.outputs };
  23177. }
  23178. return null;
  23179. }
  23180. /**
  23181. * Retrieve map of local references.
  23182. *
  23183. * The references are retrieved as a map of local reference name to element or directive instance.
  23184. *
  23185. * @param target DOM element, component or directive instance for which to retrieve
  23186. * the local references.
  23187. */
  23188. function getLocalRefs(target) {
  23189. const context = getLContext(target);
  23190. if (context === null)
  23191. return {};
  23192. if (context.localRefs === undefined) {
  23193. const lView = context.lView;
  23194. if (lView === null) {
  23195. return {};
  23196. }
  23197. context.localRefs = discoverLocalRefs(lView, context.nodeIndex);
  23198. }
  23199. return context.localRefs || {};
  23200. }
  23201. /**
  23202. * Retrieves the host element of a component or directive instance.
  23203. * The host element is the DOM element that matched the selector of the directive.
  23204. *
  23205. * @param componentOrDirective Component or directive instance for which the host
  23206. * element should be retrieved.
  23207. * @returns Host element of the target.
  23208. *
  23209. * @publicApi
  23210. * @globalApi ng
  23211. */
  23212. function getHostElement(componentOrDirective) {
  23213. return getLContext(componentOrDirective).native;
  23214. }
  23215. /**
  23216. * Retrieves the rendered text for a given component.
  23217. *
  23218. * This function retrieves the host element of a component and
  23219. * and then returns the `textContent` for that element. This implies
  23220. * that the text returned will include re-projected content of
  23221. * the component as well.
  23222. *
  23223. * @param component The component to return the content text for.
  23224. */
  23225. function getRenderedText(component) {
  23226. const hostElement = getHostElement(component);
  23227. return hostElement.textContent || '';
  23228. }
  23229. /**
  23230. * Retrieves a list of event listeners associated with a DOM element. The list does include host
  23231. * listeners, but it does not include event listeners defined outside of the Angular context
  23232. * (e.g. through `addEventListener`).
  23233. *
  23234. * @usageNotes
  23235. * Given the following DOM structure:
  23236. *
  23237. * ```html
  23238. * <app-root>
  23239. * <div (click)="doSomething()"></div>
  23240. * </app-root>
  23241. * ```
  23242. *
  23243. * Calling `getListeners` on `<div>` will return an object that looks as follows:
  23244. *
  23245. * ```ts
  23246. * {
  23247. * name: 'click',
  23248. * element: <div>,
  23249. * callback: () => doSomething(),
  23250. * useCapture: false
  23251. * }
  23252. * ```
  23253. *
  23254. * @param element Element for which the DOM listeners should be retrieved.
  23255. * @returns Array of event listeners on the DOM element.
  23256. *
  23257. * @publicApi
  23258. * @globalApi ng
  23259. */
  23260. function getListeners(element) {
  23261. ngDevMode && assertDomElement(element);
  23262. const lContext = getLContext(element);
  23263. const lView = lContext === null ? null : lContext.lView;
  23264. if (lView === null)
  23265. return [];
  23266. const tView = lView[TVIEW];
  23267. const lCleanup = lView[CLEANUP];
  23268. const tCleanup = tView.cleanup;
  23269. const listeners = [];
  23270. if (tCleanup && lCleanup) {
  23271. for (let i = 0; i < tCleanup.length;) {
  23272. const firstParam = tCleanup[i++];
  23273. const secondParam = tCleanup[i++];
  23274. if (typeof firstParam === 'string') {
  23275. const name = firstParam;
  23276. const listenerElement = unwrapRNode(lView[secondParam]);
  23277. const callback = lCleanup[tCleanup[i++]];
  23278. const useCaptureOrIndx = tCleanup[i++];
  23279. // if useCaptureOrIndx is boolean then report it as is.
  23280. // if useCaptureOrIndx is positive number then it in unsubscribe method
  23281. // if useCaptureOrIndx is negative number then it is a Subscription
  23282. const type = (typeof useCaptureOrIndx === 'boolean' || useCaptureOrIndx >= 0) ? 'dom' : 'output';
  23283. const useCapture = typeof useCaptureOrIndx === 'boolean' ? useCaptureOrIndx : false;
  23284. if (element == listenerElement) {
  23285. listeners.push({ element, name, callback, useCapture, type });
  23286. }
  23287. }
  23288. }
  23289. }
  23290. listeners.sort(sortListeners);
  23291. return listeners;
  23292. }
  23293. function sortListeners(a, b) {
  23294. if (a.name == b.name)
  23295. return 0;
  23296. return a.name < b.name ? -1 : 1;
  23297. }
  23298. /**
  23299. * This function should not exist because it is megamorphic and only mostly correct.
  23300. *
  23301. * See call site for more info.
  23302. */
  23303. function isDirectiveDefHack(obj) {
  23304. return obj.type !== undefined && obj.declaredInputs !== undefined &&
  23305. obj.findHostDirectiveDefs !== undefined;
  23306. }
  23307. /**
  23308. * Retrieve the component `LView` from component/element.
  23309. *
  23310. * NOTE: `LView` is a private and should not be leaked outside.
  23311. * Don't export this method to `ng.*` on window.
  23312. *
  23313. * @param target DOM element or component instance for which to retrieve the LView.
  23314. */
  23315. function getComponentLView(target) {
  23316. const lContext = getLContext(target);
  23317. const nodeIndx = lContext.nodeIndex;
  23318. const lView = lContext.lView;
  23319. ngDevMode && assertLView(lView);
  23320. const componentLView = lView[nodeIndx];
  23321. ngDevMode && assertLView(componentLView);
  23322. return componentLView;
  23323. }
  23324. /** Asserts that a value is a DOM Element. */
  23325. function assertDomElement(value) {
  23326. if (typeof Element !== 'undefined' && !(value instanceof Element)) {
  23327. throw new Error('Expecting instance of DOM Element');
  23328. }
  23329. }
  23330. /**
  23331. * Adds decorator, constructor, and property metadata to a given type via static metadata fields
  23332. * on the type.
  23333. *
  23334. * These metadata fields can later be read with Angular's `ReflectionCapabilities` API.
  23335. *
  23336. * Calls to `setClassMetadata` can be guarded by ngDevMode, resulting in the metadata assignments
  23337. * being tree-shaken away during production builds.
  23338. */
  23339. function setClassMetadata(type, decorators, ctorParameters, propDecorators) {
  23340. return noSideEffects(() => {
  23341. const clazz = type;
  23342. if (decorators !== null) {
  23343. if (clazz.hasOwnProperty('decorators') && clazz.decorators !== undefined) {
  23344. clazz.decorators.push(...decorators);
  23345. }
  23346. else {
  23347. clazz.decorators = decorators;
  23348. }
  23349. }
  23350. if (ctorParameters !== null) {
  23351. // Rather than merging, clobber the existing parameters. If other projects exist which
  23352. // use tsickle-style annotations and reflect over them in the same way, this could
  23353. // cause issues, but that is vanishingly unlikely.
  23354. clazz.ctorParameters = ctorParameters;
  23355. }
  23356. if (propDecorators !== null) {
  23357. // The property decorator objects are merged as it is possible different fields have
  23358. // different decorator types. Decorators on individual fields are not merged, as it's
  23359. // also incredibly unlikely that a field will be decorated both with an Angular
  23360. // decorator and a non-Angular decorator that's also been downleveled.
  23361. if (clazz.hasOwnProperty('propDecorators') && clazz.propDecorators !== undefined) {
  23362. clazz.propDecorators = { ...clazz.propDecorators, ...propDecorators };
  23363. }
  23364. else {
  23365. clazz.propDecorators = propDecorators;
  23366. }
  23367. }
  23368. });
  23369. }
  23370. /**
  23371. * Bindings for pure functions are stored after regular bindings.
  23372. *
  23373. * |-------decls------|---------vars---------| |----- hostVars (dir1) ------|
  23374. * ------------------------------------------------------------------------------------------
  23375. * | nodes/refs/pipes | bindings | fn slots | injector | dir1 | host bindings | host slots |
  23376. * ------------------------------------------------------------------------------------------
  23377. * ^ ^
  23378. * TView.bindingStartIndex TView.expandoStartIndex
  23379. *
  23380. * Pure function instructions are given an offset from the binding root. Adding the offset to the
  23381. * binding root gives the first index where the bindings are stored. In component views, the binding
  23382. * root is the bindingStartIndex. In host bindings, the binding root is the expandoStartIndex +
  23383. * any directive instances + any hostVars in directives evaluated before it.
  23384. *
  23385. * See VIEW_DATA.md for more information about host binding resolution.
  23386. */
  23387. /**
  23388. * If the value hasn't been saved, calls the pure function to store and return the
  23389. * value. If it has been saved, returns the saved value.
  23390. *
  23391. * @param slotOffset the offset from binding root to the reserved slot
  23392. * @param pureFn Function that returns a value
  23393. * @param thisArg Optional calling context of pureFn
  23394. * @returns value
  23395. *
  23396. * @codeGenApi
  23397. */
  23398. function ɵɵpureFunction0(slotOffset, pureFn, thisArg) {
  23399. const bindingIndex = getBindingRoot() + slotOffset;
  23400. const lView = getLView();
  23401. return lView[bindingIndex] === NO_CHANGE ?
  23402. updateBinding(lView, bindingIndex, thisArg ? pureFn.call(thisArg) : pureFn()) :
  23403. getBinding(lView, bindingIndex);
  23404. }
  23405. /**
  23406. * If the value of the provided exp has changed, calls the pure function to return
  23407. * an updated value. Or if the value has not changed, returns cached value.
  23408. *
  23409. * @param slotOffset the offset from binding root to the reserved slot
  23410. * @param pureFn Function that returns an updated value
  23411. * @param exp Updated expression value
  23412. * @param thisArg Optional calling context of pureFn
  23413. * @returns Updated or cached value
  23414. *
  23415. * @codeGenApi
  23416. */
  23417. function ɵɵpureFunction1(slotOffset, pureFn, exp, thisArg) {
  23418. return pureFunction1Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp, thisArg);
  23419. }
  23420. /**
  23421. * If the value of any provided exp has changed, calls the pure function to return
  23422. * an updated value. Or if no values have changed, returns cached value.
  23423. *
  23424. * @param slotOffset the offset from binding root to the reserved slot
  23425. * @param pureFn
  23426. * @param exp1
  23427. * @param exp2
  23428. * @param thisArg Optional calling context of pureFn
  23429. * @returns Updated or cached value
  23430. *
  23431. * @codeGenApi
  23432. */
  23433. function ɵɵpureFunction2(slotOffset, pureFn, exp1, exp2, thisArg) {
  23434. return pureFunction2Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, thisArg);
  23435. }
  23436. /**
  23437. * If the value of any provided exp has changed, calls the pure function to return
  23438. * an updated value. Or if no values have changed, returns cached value.
  23439. *
  23440. * @param slotOffset the offset from binding root to the reserved slot
  23441. * @param pureFn
  23442. * @param exp1
  23443. * @param exp2
  23444. * @param exp3
  23445. * @param thisArg Optional calling context of pureFn
  23446. * @returns Updated or cached value
  23447. *
  23448. * @codeGenApi
  23449. */
  23450. function ɵɵpureFunction3(slotOffset, pureFn, exp1, exp2, exp3, thisArg) {
  23451. return pureFunction3Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, thisArg);
  23452. }
  23453. /**
  23454. * If the value of any provided exp has changed, calls the pure function to return
  23455. * an updated value. Or if no values have changed, returns cached value.
  23456. *
  23457. * @param slotOffset the offset from binding root to the reserved slot
  23458. * @param pureFn
  23459. * @param exp1
  23460. * @param exp2
  23461. * @param exp3
  23462. * @param exp4
  23463. * @param thisArg Optional calling context of pureFn
  23464. * @returns Updated or cached value
  23465. *
  23466. * @codeGenApi
  23467. */
  23468. function ɵɵpureFunction4(slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) {
  23469. return pureFunction4Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg);
  23470. }
  23471. /**
  23472. * If the value of any provided exp has changed, calls the pure function to return
  23473. * an updated value. Or if no values have changed, returns cached value.
  23474. *
  23475. * @param slotOffset the offset from binding root to the reserved slot
  23476. * @param pureFn
  23477. * @param exp1
  23478. * @param exp2
  23479. * @param exp3
  23480. * @param exp4
  23481. * @param exp5
  23482. * @param thisArg Optional calling context of pureFn
  23483. * @returns Updated or cached value
  23484. *
  23485. * @codeGenApi
  23486. */
  23487. function ɵɵpureFunction5(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, thisArg) {
  23488. const bindingIndex = getBindingRoot() + slotOffset;
  23489. const lView = getLView();
  23490. const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23491. return bindingUpdated(lView, bindingIndex + 4, exp5) || different ?
  23492. updateBinding(lView, bindingIndex + 5, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) :
  23493. pureFn(exp1, exp2, exp3, exp4, exp5)) :
  23494. getBinding(lView, bindingIndex + 5);
  23495. }
  23496. /**
  23497. * If the value of any provided exp has changed, calls the pure function to return
  23498. * an updated value. Or if no values have changed, returns cached value.
  23499. *
  23500. * @param slotOffset the offset from binding root to the reserved slot
  23501. * @param pureFn
  23502. * @param exp1
  23503. * @param exp2
  23504. * @param exp3
  23505. * @param exp4
  23506. * @param exp5
  23507. * @param exp6
  23508. * @param thisArg Optional calling context of pureFn
  23509. * @returns Updated or cached value
  23510. *
  23511. * @codeGenApi
  23512. */
  23513. function ɵɵpureFunction6(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, thisArg) {
  23514. const bindingIndex = getBindingRoot() + slotOffset;
  23515. const lView = getLView();
  23516. const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23517. return bindingUpdated2(lView, bindingIndex + 4, exp5, exp6) || different ?
  23518. updateBinding(lView, bindingIndex + 6, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) :
  23519. pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) :
  23520. getBinding(lView, bindingIndex + 6);
  23521. }
  23522. /**
  23523. * If the value of any provided exp has changed, calls the pure function to return
  23524. * an updated value. Or if no values have changed, returns cached value.
  23525. *
  23526. * @param slotOffset the offset from binding root to the reserved slot
  23527. * @param pureFn
  23528. * @param exp1
  23529. * @param exp2
  23530. * @param exp3
  23531. * @param exp4
  23532. * @param exp5
  23533. * @param exp6
  23534. * @param exp7
  23535. * @param thisArg Optional calling context of pureFn
  23536. * @returns Updated or cached value
  23537. *
  23538. * @codeGenApi
  23539. */
  23540. function ɵɵpureFunction7(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, thisArg) {
  23541. const bindingIndex = getBindingRoot() + slotOffset;
  23542. const lView = getLView();
  23543. let different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23544. return bindingUpdated3(lView, bindingIndex + 4, exp5, exp6, exp7) || different ?
  23545. updateBinding(lView, bindingIndex + 7, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) :
  23546. pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) :
  23547. getBinding(lView, bindingIndex + 7);
  23548. }
  23549. /**
  23550. * If the value of any provided exp has changed, calls the pure function to return
  23551. * an updated value. Or if no values have changed, returns cached value.
  23552. *
  23553. * @param slotOffset the offset from binding root to the reserved slot
  23554. * @param pureFn
  23555. * @param exp1
  23556. * @param exp2
  23557. * @param exp3
  23558. * @param exp4
  23559. * @param exp5
  23560. * @param exp6
  23561. * @param exp7
  23562. * @param exp8
  23563. * @param thisArg Optional calling context of pureFn
  23564. * @returns Updated or cached value
  23565. *
  23566. * @codeGenApi
  23567. */
  23568. function ɵɵpureFunction8(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8, thisArg) {
  23569. const bindingIndex = getBindingRoot() + slotOffset;
  23570. const lView = getLView();
  23571. const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23572. return bindingUpdated4(lView, bindingIndex + 4, exp5, exp6, exp7, exp8) || different ?
  23573. updateBinding(lView, bindingIndex + 8, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) :
  23574. pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) :
  23575. getBinding(lView, bindingIndex + 8);
  23576. }
  23577. /**
  23578. * pureFunction instruction that can support any number of bindings.
  23579. *
  23580. * If the value of any provided exp has changed, calls the pure function to return
  23581. * an updated value. Or if no values have changed, returns cached value.
  23582. *
  23583. * @param slotOffset the offset from binding root to the reserved slot
  23584. * @param pureFn A pure function that takes binding values and builds an object or array
  23585. * containing those values.
  23586. * @param exps An array of binding values
  23587. * @param thisArg Optional calling context of pureFn
  23588. * @returns Updated or cached value
  23589. *
  23590. * @codeGenApi
  23591. */
  23592. function ɵɵpureFunctionV(slotOffset, pureFn, exps, thisArg) {
  23593. return pureFunctionVInternal(getLView(), getBindingRoot(), slotOffset, pureFn, exps, thisArg);
  23594. }
  23595. /**
  23596. * Results of a pure function invocation are stored in LView in a dedicated slot that is initialized
  23597. * to NO_CHANGE. In rare situations a pure pipe might throw an exception on the very first
  23598. * invocation and not produce any valid results. In this case LView would keep holding the NO_CHANGE
  23599. * value. The NO_CHANGE is not something that we can use in expressions / bindings thus we convert
  23600. * it to `undefined`.
  23601. */
  23602. function getPureFunctionReturnValue(lView, returnValueIndex) {
  23603. ngDevMode && assertIndexInRange(lView, returnValueIndex);
  23604. const lastReturnValue = lView[returnValueIndex];
  23605. return lastReturnValue === NO_CHANGE ? undefined : lastReturnValue;
  23606. }
  23607. /**
  23608. * If the value of the provided exp has changed, calls the pure function to return
  23609. * an updated value. Or if the value has not changed, returns cached value.
  23610. *
  23611. * @param lView LView in which the function is being executed.
  23612. * @param bindingRoot Binding root index.
  23613. * @param slotOffset the offset from binding root to the reserved slot
  23614. * @param pureFn Function that returns an updated value
  23615. * @param exp Updated expression value
  23616. * @param thisArg Optional calling context of pureFn
  23617. * @returns Updated or cached value
  23618. */
  23619. function pureFunction1Internal(lView, bindingRoot, slotOffset, pureFn, exp, thisArg) {
  23620. const bindingIndex = bindingRoot + slotOffset;
  23621. return bindingUpdated(lView, bindingIndex, exp) ?
  23622. updateBinding(lView, bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) :
  23623. getPureFunctionReturnValue(lView, bindingIndex + 1);
  23624. }
  23625. /**
  23626. * If the value of any provided exp has changed, calls the pure function to return
  23627. * an updated value. Or if no values have changed, returns cached value.
  23628. *
  23629. * @param lView LView in which the function is being executed.
  23630. * @param bindingRoot Binding root index.
  23631. * @param slotOffset the offset from binding root to the reserved slot
  23632. * @param pureFn
  23633. * @param exp1
  23634. * @param exp2
  23635. * @param thisArg Optional calling context of pureFn
  23636. * @returns Updated or cached value
  23637. */
  23638. function pureFunction2Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, thisArg) {
  23639. const bindingIndex = bindingRoot + slotOffset;
  23640. return bindingUpdated2(lView, bindingIndex, exp1, exp2) ?
  23641. updateBinding(lView, bindingIndex + 2, thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) :
  23642. getPureFunctionReturnValue(lView, bindingIndex + 2);
  23643. }
  23644. /**
  23645. * If the value of any provided exp has changed, calls the pure function to return
  23646. * an updated value. Or if no values have changed, returns cached value.
  23647. *
  23648. * @param lView LView in which the function is being executed.
  23649. * @param bindingRoot Binding root index.
  23650. * @param slotOffset the offset from binding root to the reserved slot
  23651. * @param pureFn
  23652. * @param exp1
  23653. * @param exp2
  23654. * @param exp3
  23655. * @param thisArg Optional calling context of pureFn
  23656. * @returns Updated or cached value
  23657. */
  23658. function pureFunction3Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, thisArg) {
  23659. const bindingIndex = bindingRoot + slotOffset;
  23660. return bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) ?
  23661. updateBinding(lView, bindingIndex + 3, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) :
  23662. getPureFunctionReturnValue(lView, bindingIndex + 3);
  23663. }
  23664. /**
  23665. * If the value of any provided exp has changed, calls the pure function to return
  23666. * an updated value. Or if no values have changed, returns cached value.
  23667. *
  23668. * @param lView LView in which the function is being executed.
  23669. * @param bindingRoot Binding root index.
  23670. * @param slotOffset the offset from binding root to the reserved slot
  23671. * @param pureFn
  23672. * @param exp1
  23673. * @param exp2
  23674. * @param exp3
  23675. * @param exp4
  23676. * @param thisArg Optional calling context of pureFn
  23677. * @returns Updated or cached value
  23678. *
  23679. */
  23680. function pureFunction4Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) {
  23681. const bindingIndex = bindingRoot + slotOffset;
  23682. return bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) ?
  23683. updateBinding(lView, bindingIndex + 4, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) :
  23684. getPureFunctionReturnValue(lView, bindingIndex + 4);
  23685. }
  23686. /**
  23687. * pureFunction instruction that can support any number of bindings.
  23688. *
  23689. * If the value of any provided exp has changed, calls the pure function to return
  23690. * an updated value. Or if no values have changed, returns cached value.
  23691. *
  23692. * @param lView LView in which the function is being executed.
  23693. * @param bindingRoot Binding root index.
  23694. * @param slotOffset the offset from binding root to the reserved slot
  23695. * @param pureFn A pure function that takes binding values and builds an object or array
  23696. * containing those values.
  23697. * @param exps An array of binding values
  23698. * @param thisArg Optional calling context of pureFn
  23699. * @returns Updated or cached value
  23700. */
  23701. function pureFunctionVInternal(lView, bindingRoot, slotOffset, pureFn, exps, thisArg) {
  23702. let bindingIndex = bindingRoot + slotOffset;
  23703. let different = false;
  23704. for (let i = 0; i < exps.length; i++) {
  23705. bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true);
  23706. }
  23707. return different ? updateBinding(lView, bindingIndex, pureFn.apply(thisArg, exps)) :
  23708. getPureFunctionReturnValue(lView, bindingIndex);
  23709. }
  23710. /**
  23711. * Create a pipe.
  23712. *
  23713. * @param index Pipe index where the pipe will be stored.
  23714. * @param pipeName The name of the pipe
  23715. * @returns T the instance of the pipe.
  23716. *
  23717. * @codeGenApi
  23718. */
  23719. function ɵɵpipe(index, pipeName) {
  23720. const tView = getTView();
  23721. let pipeDef;
  23722. const adjustedIndex = index + HEADER_OFFSET;
  23723. if (tView.firstCreatePass) {
  23724. // The `getPipeDef` throws if a pipe with a given name is not found
  23725. // (so we use non-null assertion below).
  23726. pipeDef = getPipeDef(pipeName, tView.pipeRegistry);
  23727. tView.data[adjustedIndex] = pipeDef;
  23728. if (pipeDef.onDestroy) {
  23729. (tView.destroyHooks ??= []).push(adjustedIndex, pipeDef.onDestroy);
  23730. }
  23731. }
  23732. else {
  23733. pipeDef = tView.data[adjustedIndex];
  23734. }
  23735. const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true));
  23736. let previousInjectorProfilerContext;
  23737. if (ngDevMode) {
  23738. previousInjectorProfilerContext = setInjectorProfilerContext({
  23739. injector: new NodeInjector(getCurrentTNode(), getLView()),
  23740. token: pipeDef.type
  23741. });
  23742. }
  23743. const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject);
  23744. try {
  23745. // DI for pipes is supposed to behave like directives when placed on a component
  23746. // host node, which means that we have to disable access to `viewProviders`.
  23747. const previousIncludeViewProviders = setIncludeViewProviders(false);
  23748. const pipeInstance = pipeFactory();
  23749. setIncludeViewProviders(previousIncludeViewProviders);
  23750. store(tView, getLView(), adjustedIndex, pipeInstance);
  23751. return pipeInstance;
  23752. }
  23753. finally {
  23754. // we have to restore the injector implementation in finally, just in case the creation of the
  23755. // pipe throws an error.
  23756. setInjectImplementation(previousInjectImplementation);
  23757. ngDevMode && setInjectorProfilerContext(previousInjectorProfilerContext);
  23758. }
  23759. }
  23760. /**
  23761. * Searches the pipe registry for a pipe with the given name. If one is found,
  23762. * returns the pipe. Otherwise, an error is thrown because the pipe cannot be resolved.
  23763. *
  23764. * @param name Name of pipe to resolve
  23765. * @param registry Full list of available pipes
  23766. * @returns Matching PipeDef
  23767. */
  23768. function getPipeDef(name, registry) {
  23769. if (registry) {
  23770. if (ngDevMode) {
  23771. const pipes = registry.filter(pipe => pipe.name === name);
  23772. // TODO: Throw an error in the next major
  23773. if (pipes.length > 1) {
  23774. console.warn(formatRuntimeError(313 /* RuntimeErrorCode.MULTIPLE_MATCHING_PIPES */, getMultipleMatchingPipesMessage(name)));
  23775. }
  23776. }
  23777. for (let i = registry.length - 1; i >= 0; i--) {
  23778. const pipeDef = registry[i];
  23779. if (name === pipeDef.name) {
  23780. return pipeDef;
  23781. }
  23782. }
  23783. }
  23784. if (ngDevMode) {
  23785. throw new RuntimeError(-302 /* RuntimeErrorCode.PIPE_NOT_FOUND */, getPipeNotFoundErrorMessage(name));
  23786. }
  23787. }
  23788. /**
  23789. * Generates a helpful error message for the user when multiple pipes match the name.
  23790. *
  23791. * @param name Name of the pipe
  23792. * @returns The error message
  23793. */
  23794. function getMultipleMatchingPipesMessage(name) {
  23795. const lView = getLView();
  23796. const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
  23797. const context = declarationLView[CONTEXT];
  23798. const hostIsStandalone = isHostComponentStandalone(lView);
  23799. const componentInfoMessage = context ? ` in the '${context.constructor.name}' component` : '';
  23800. const verifyMessage = `check ${hostIsStandalone ? '\'@Component.imports\' of this component' :
  23801. 'the imports of this module'}`;
  23802. const errorMessage = `Multiple pipes match the name \`${name}\`${componentInfoMessage}. ${verifyMessage}`;
  23803. return errorMessage;
  23804. }
  23805. /**
  23806. * Generates a helpful error message for the user when a pipe is not found.
  23807. *
  23808. * @param name Name of the missing pipe
  23809. * @returns The error message
  23810. */
  23811. function getPipeNotFoundErrorMessage(name) {
  23812. const lView = getLView();
  23813. const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
  23814. const context = declarationLView[CONTEXT];
  23815. const hostIsStandalone = isHostComponentStandalone(lView);
  23816. const componentInfoMessage = context ? ` in the '${context.constructor.name}' component` : '';
  23817. const verifyMessage = `Verify that it is ${hostIsStandalone ? 'included in the \'@Component.imports\' of this component' :
  23818. 'declared or imported in this module'}`;
  23819. const errorMessage = `The pipe '${name}' could not be found${componentInfoMessage}. ${verifyMessage}`;
  23820. return errorMessage;
  23821. }
  23822. /**
  23823. * Invokes a pipe with 1 arguments.
  23824. *
  23825. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23826. * the pipe only when an input to the pipe changes.
  23827. *
  23828. * @param index Pipe index where the pipe was stored on creation.
  23829. * @param slotOffset the offset in the reserved slot space
  23830. * @param v1 1st argument to {@link PipeTransform#transform}.
  23831. *
  23832. * @codeGenApi
  23833. */
  23834. function ɵɵpipeBind1(index, slotOffset, v1) {
  23835. const adjustedIndex = index + HEADER_OFFSET;
  23836. const lView = getLView();
  23837. const pipeInstance = load(lView, adjustedIndex);
  23838. return isPure(lView, adjustedIndex) ?
  23839. pureFunction1Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) :
  23840. pipeInstance.transform(v1);
  23841. }
  23842. /**
  23843. * Invokes a pipe with 2 arguments.
  23844. *
  23845. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23846. * the pipe only when an input to the pipe changes.
  23847. *
  23848. * @param index Pipe index where the pipe was stored on creation.
  23849. * @param slotOffset the offset in the reserved slot space
  23850. * @param v1 1st argument to {@link PipeTransform#transform}.
  23851. * @param v2 2nd argument to {@link PipeTransform#transform}.
  23852. *
  23853. * @codeGenApi
  23854. */
  23855. function ɵɵpipeBind2(index, slotOffset, v1, v2) {
  23856. const adjustedIndex = index + HEADER_OFFSET;
  23857. const lView = getLView();
  23858. const pipeInstance = load(lView, adjustedIndex);
  23859. return isPure(lView, adjustedIndex) ?
  23860. pureFunction2Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
  23861. pipeInstance.transform(v1, v2);
  23862. }
  23863. /**
  23864. * Invokes a pipe with 3 arguments.
  23865. *
  23866. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23867. * the pipe only when an input to the pipe changes.
  23868. *
  23869. * @param index Pipe index where the pipe was stored on creation.
  23870. * @param slotOffset the offset in the reserved slot space
  23871. * @param v1 1st argument to {@link PipeTransform#transform}.
  23872. * @param v2 2nd argument to {@link PipeTransform#transform}.
  23873. * @param v3 4rd argument to {@link PipeTransform#transform}.
  23874. *
  23875. * @codeGenApi
  23876. */
  23877. function ɵɵpipeBind3(index, slotOffset, v1, v2, v3) {
  23878. const adjustedIndex = index + HEADER_OFFSET;
  23879. const lView = getLView();
  23880. const pipeInstance = load(lView, adjustedIndex);
  23881. return isPure(lView, adjustedIndex) ?
  23882. pureFunction3Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) :
  23883. pipeInstance.transform(v1, v2, v3);
  23884. }
  23885. /**
  23886. * Invokes a pipe with 4 arguments.
  23887. *
  23888. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23889. * the pipe only when an input to the pipe changes.
  23890. *
  23891. * @param index Pipe index where the pipe was stored on creation.
  23892. * @param slotOffset the offset in the reserved slot space
  23893. * @param v1 1st argument to {@link PipeTransform#transform}.
  23894. * @param v2 2nd argument to {@link PipeTransform#transform}.
  23895. * @param v3 3rd argument to {@link PipeTransform#transform}.
  23896. * @param v4 4th argument to {@link PipeTransform#transform}.
  23897. *
  23898. * @codeGenApi
  23899. */
  23900. function ɵɵpipeBind4(index, slotOffset, v1, v2, v3, v4) {
  23901. const adjustedIndex = index + HEADER_OFFSET;
  23902. const lView = getLView();
  23903. const pipeInstance = load(lView, adjustedIndex);
  23904. return isPure(lView, adjustedIndex) ? pureFunction4Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
  23905. pipeInstance.transform(v1, v2, v3, v4);
  23906. }
  23907. /**
  23908. * Invokes a pipe with variable number of arguments.
  23909. *
  23910. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23911. * the pipe only when an input to the pipe changes.
  23912. *
  23913. * @param index Pipe index where the pipe was stored on creation.
  23914. * @param slotOffset the offset in the reserved slot space
  23915. * @param values Array of arguments to pass to {@link PipeTransform#transform} method.
  23916. *
  23917. * @codeGenApi
  23918. */
  23919. function ɵɵpipeBindV(index, slotOffset, values) {
  23920. const adjustedIndex = index + HEADER_OFFSET;
  23921. const lView = getLView();
  23922. const pipeInstance = load(lView, adjustedIndex);
  23923. return isPure(lView, adjustedIndex) ?
  23924. pureFunctionVInternal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) :
  23925. pipeInstance.transform.apply(pipeInstance, values);
  23926. }
  23927. function isPure(lView, index) {
  23928. return lView[TVIEW].data[index].pure;
  23929. }
  23930. function symbolIterator() {
  23931. // @ts-expect-error accessing a private member
  23932. return this._results[Symbol.iterator]();
  23933. }
  23934. /**
  23935. * An unmodifiable list of items that Angular keeps up to date when the state
  23936. * of the application changes.
  23937. *
  23938. * The type of object that {@link ViewChildren}, {@link ContentChildren}, and {@link QueryList}
  23939. * provide.
  23940. *
  23941. * Implements an iterable interface, therefore it can be used in both ES6
  23942. * javascript `for (var i of items)` loops as well as in Angular templates with
  23943. * `*ngFor="let i of myList"`.
  23944. *
  23945. * Changes can be observed by subscribing to the changes `Observable`.
  23946. *
  23947. * NOTE: In the future this class will implement an `Observable` interface.
  23948. *
  23949. * @usageNotes
  23950. * ### Example
  23951. * ```typescript
  23952. * @Component({...})
  23953. * class Container {
  23954. * @ViewChildren(Item) items:QueryList<Item>;
  23955. * }
  23956. * ```
  23957. *
  23958. * @publicApi
  23959. */
  23960. class QueryList {
  23961. static { Symbol.iterator; }
  23962. /**
  23963. * Returns `Observable` of `QueryList` notifying the subscriber of changes.
  23964. */
  23965. get changes() {
  23966. return this._changes || (this._changes = new EventEmitter());
  23967. }
  23968. /**
  23969. * @param emitDistinctChangesOnly Whether `QueryList.changes` should fire only when actual change
  23970. * has occurred. Or if it should fire when query is recomputed. (recomputing could resolve in
  23971. * the same result)
  23972. */
  23973. constructor(_emitDistinctChangesOnly = false) {
  23974. this._emitDistinctChangesOnly = _emitDistinctChangesOnly;
  23975. this.dirty = true;
  23976. this._results = [];
  23977. this._changesDetected = false;
  23978. this._changes = null;
  23979. this.length = 0;
  23980. this.first = undefined;
  23981. this.last = undefined;
  23982. // This function should be declared on the prototype, but doing so there will cause the class
  23983. // declaration to have side-effects and become not tree-shakable. For this reason we do it in
  23984. // the constructor.
  23985. // [Symbol.iterator](): Iterator<T> { ... }
  23986. const proto = QueryList.prototype;
  23987. if (!proto[Symbol.iterator])
  23988. proto[Symbol.iterator] = symbolIterator;
  23989. }
  23990. /**
  23991. * Returns the QueryList entry at `index`.
  23992. */
  23993. get(index) {
  23994. return this._results[index];
  23995. }
  23996. /**
  23997. * See
  23998. * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
  23999. */
  24000. map(fn) {
  24001. return this._results.map(fn);
  24002. }
  24003. filter(fn) {
  24004. return this._results.filter(fn);
  24005. }
  24006. /**
  24007. * See
  24008. * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
  24009. */
  24010. find(fn) {
  24011. return this._results.find(fn);
  24012. }
  24013. /**
  24014. * See
  24015. * [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
  24016. */
  24017. reduce(fn, init) {
  24018. return this._results.reduce(fn, init);
  24019. }
  24020. /**
  24021. * See
  24022. * [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
  24023. */
  24024. forEach(fn) {
  24025. this._results.forEach(fn);
  24026. }
  24027. /**
  24028. * See
  24029. * [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
  24030. */
  24031. some(fn) {
  24032. return this._results.some(fn);
  24033. }
  24034. /**
  24035. * Returns a copy of the internal results list as an Array.
  24036. */
  24037. toArray() {
  24038. return this._results.slice();
  24039. }
  24040. toString() {
  24041. return this._results.toString();
  24042. }
  24043. /**
  24044. * Updates the stored data of the query list, and resets the `dirty` flag to `false`, so that
  24045. * on change detection, it will not notify of changes to the queries, unless a new change
  24046. * occurs.
  24047. *
  24048. * @param resultsTree The query results to store
  24049. * @param identityAccessor Optional function for extracting stable object identity from a value
  24050. * in the array. This function is executed for each element of the query result list while
  24051. * comparing current query list with the new one (provided as a first argument of the `reset`
  24052. * function) to detect if the lists are different. If the function is not provided, elements
  24053. * are compared as is (without any pre-processing).
  24054. */
  24055. reset(resultsTree, identityAccessor) {
  24056. // Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of
  24057. // QueryList (but not for QueryList itself.)
  24058. const self = this;
  24059. self.dirty = false;
  24060. const newResultFlat = flatten$1(resultsTree);
  24061. if (this._changesDetected = !arrayEquals(self._results, newResultFlat, identityAccessor)) {
  24062. self._results = newResultFlat;
  24063. self.length = newResultFlat.length;
  24064. self.last = newResultFlat[this.length - 1];
  24065. self.first = newResultFlat[0];
  24066. }
  24067. }
  24068. /**
  24069. * Triggers a change event by emitting on the `changes` {@link EventEmitter}.
  24070. */
  24071. notifyOnChanges() {
  24072. if (this._changes && (this._changesDetected || !this._emitDistinctChangesOnly))
  24073. this._changes.emit(this);
  24074. }
  24075. /** internal */
  24076. setDirty() {
  24077. this.dirty = true;
  24078. }
  24079. /** internal */
  24080. destroy() {
  24081. this.changes.complete();
  24082. this.changes.unsubscribe();
  24083. }
  24084. }
  24085. function createAndRenderEmbeddedLView(declarationLView, templateTNode, context, options) {
  24086. const embeddedTView = templateTNode.tView;
  24087. ngDevMode && assertDefined(embeddedTView, 'TView must be defined for a template node.');
  24088. ngDevMode && assertTNodeForLView(templateTNode, declarationLView);
  24089. // Embedded views follow the change detection strategy of the view they're declared in.
  24090. const isSignalView = declarationLView[FLAGS] & 4096 /* LViewFlags.SignalView */;
  24091. const viewFlags = isSignalView ? 4096 /* LViewFlags.SignalView */ : 16 /* LViewFlags.CheckAlways */;
  24092. const embeddedLView = createLView(declarationLView, embeddedTView, context, viewFlags, null, templateTNode, null, null, null, options?.injector ?? null, options?.hydrationInfo ?? null);
  24093. const declarationLContainer = declarationLView[templateTNode.index];
  24094. ngDevMode && assertLContainer(declarationLContainer);
  24095. embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
  24096. const declarationViewLQueries = declarationLView[QUERIES];
  24097. if (declarationViewLQueries !== null) {
  24098. embeddedLView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView);
  24099. }
  24100. // execute creation mode of a view
  24101. renderView(embeddedTView, embeddedLView, context);
  24102. return embeddedLView;
  24103. }
  24104. function getLViewFromLContainer(lContainer, index) {
  24105. const adjustedIndex = CONTAINER_HEADER_OFFSET + index;
  24106. // avoid reading past the array boundaries
  24107. if (adjustedIndex < lContainer.length) {
  24108. const lView = lContainer[adjustedIndex];
  24109. ngDevMode && assertLView(lView);
  24110. return lView;
  24111. }
  24112. return undefined;
  24113. }
  24114. function addLViewToLContainer(lContainer, lView, index, addToDOM = true) {
  24115. const tView = lView[TVIEW];
  24116. // insert to the view tree so the new view can be change-detected
  24117. insertView(tView, lView, lContainer, index);
  24118. // insert to the view to the DOM tree
  24119. if (addToDOM) {
  24120. const beforeNode = getBeforeNodeForView(index, lContainer);
  24121. const renderer = lView[RENDERER];
  24122. const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
  24123. if (parentRNode !== null) {
  24124. addViewToDOM(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
  24125. }
  24126. }
  24127. }
  24128. function removeLViewFromLContainer(lContainer, index) {
  24129. const lView = detachView(lContainer, index);
  24130. if (lView !== undefined) {
  24131. destroyLView(lView[TVIEW], lView);
  24132. }
  24133. return lView;
  24134. }
  24135. /**
  24136. * Represents an embedded template that can be used to instantiate embedded views.
  24137. * To instantiate embedded views based on a template, use the `ViewContainerRef`
  24138. * method `createEmbeddedView()`.
  24139. *
  24140. * Access a `TemplateRef` instance by placing a directive on an `<ng-template>`
  24141. * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view
  24142. * is injected into the constructor of the directive,
  24143. * using the `TemplateRef` token.
  24144. *
  24145. * You can also use a `Query` to find a `TemplateRef` associated with
  24146. * a component or a directive.
  24147. *
  24148. * @see {@link ViewContainerRef}
  24149. * @see [Navigate the Component Tree with DI](guide/dependency-injection-navtree)
  24150. *
  24151. * @publicApi
  24152. */
  24153. class TemplateRef {
  24154. /**
  24155. * @internal
  24156. * @nocollapse
  24157. */
  24158. static { this.__NG_ELEMENT_ID__ = injectTemplateRef; }
  24159. }
  24160. const ViewEngineTemplateRef = TemplateRef;
  24161. // TODO(alxhub): combine interface and implementation. Currently this is challenging since something
  24162. // in g3 depends on them being separate.
  24163. const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef {
  24164. constructor(_declarationLView, _declarationTContainer, elementRef) {
  24165. super();
  24166. this._declarationLView = _declarationLView;
  24167. this._declarationTContainer = _declarationTContainer;
  24168. this.elementRef = elementRef;
  24169. }
  24170. /**
  24171. * Returns an `ssrId` associated with a TView, which was used to
  24172. * create this instance of the `TemplateRef`.
  24173. *
  24174. * @internal
  24175. */
  24176. get ssrId() {
  24177. return this._declarationTContainer.tView?.ssrId || null;
  24178. }
  24179. createEmbeddedView(context, injector) {
  24180. return this.createEmbeddedViewImpl(context, injector);
  24181. }
  24182. /**
  24183. * @internal
  24184. */
  24185. createEmbeddedViewImpl(context, injector, hydrationInfo) {
  24186. const embeddedLView = createAndRenderEmbeddedLView(this._declarationLView, this._declarationTContainer, context, { injector, hydrationInfo });
  24187. return new ViewRef(embeddedLView);
  24188. }
  24189. };
  24190. /**
  24191. * Creates a TemplateRef given a node.
  24192. *
  24193. * @returns The TemplateRef instance to use
  24194. */
  24195. function injectTemplateRef() {
  24196. return createTemplateRef(getCurrentTNode(), getLView());
  24197. }
  24198. /**
  24199. * Creates a TemplateRef and stores it on the injector.
  24200. *
  24201. * @param hostTNode The node on which a TemplateRef is requested
  24202. * @param hostLView The `LView` to which the node belongs
  24203. * @returns The TemplateRef instance or null if we can't create a TemplateRef on a given node type
  24204. */
  24205. function createTemplateRef(hostTNode, hostLView) {
  24206. if (hostTNode.type & 4 /* TNodeType.Container */) {
  24207. ngDevMode && assertDefined(hostTNode.tView, 'TView must be allocated');
  24208. return new R3TemplateRef(hostLView, hostTNode, createElementRef(hostTNode, hostLView));
  24209. }
  24210. return null;
  24211. }
  24212. /**
  24213. * Removes all dehydrated views from a given LContainer:
  24214. * both in internal data structure, as well as removing
  24215. * corresponding DOM nodes that belong to that dehydrated view.
  24216. */
  24217. function removeDehydratedViews(lContainer) {
  24218. const views = lContainer[DEHYDRATED_VIEWS] ?? [];
  24219. const parentLView = lContainer[PARENT];
  24220. const renderer = parentLView[RENDERER];
  24221. for (const view of views) {
  24222. removeDehydratedView(view, renderer);
  24223. ngDevMode && ngDevMode.dehydratedViewsRemoved++;
  24224. }
  24225. // Reset the value to an empty array to indicate that no
  24226. // further processing of dehydrated views is needed for
  24227. // this view container (i.e. do not trigger the lookup process
  24228. // once again in case a `ViewContainerRef` is created later).
  24229. lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
  24230. }
  24231. /**
  24232. * Helper function to remove all nodes from a dehydrated view.
  24233. */
  24234. function removeDehydratedView(dehydratedView, renderer) {
  24235. let nodesRemoved = 0;
  24236. let currentRNode = dehydratedView.firstChild;
  24237. if (currentRNode) {
  24238. const numNodes = dehydratedView.data[NUM_ROOT_NODES];
  24239. while (nodesRemoved < numNodes) {
  24240. ngDevMode && validateSiblingNodeExists(currentRNode);
  24241. const nextSibling = currentRNode.nextSibling;
  24242. nativeRemoveNode(renderer, currentRNode, false);
  24243. currentRNode = nextSibling;
  24244. nodesRemoved++;
  24245. }
  24246. }
  24247. }
  24248. /**
  24249. * Walks over all views within this LContainer invokes dehydrated views
  24250. * cleanup function for each one.
  24251. */
  24252. function cleanupLContainer(lContainer) {
  24253. removeDehydratedViews(lContainer);
  24254. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  24255. cleanupLView(lContainer[i]);
  24256. }
  24257. }
  24258. /**
  24259. * Walks over `LContainer`s and components registered within
  24260. * this LView and invokes dehydrated views cleanup function for each one.
  24261. */
  24262. function cleanupLView(lView) {
  24263. const tView = lView[TVIEW];
  24264. for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
  24265. if (isLContainer(lView[i])) {
  24266. const lContainer = lView[i];
  24267. cleanupLContainer(lContainer);
  24268. }
  24269. else if (Array.isArray(lView[i])) {
  24270. // This is a component, enter the `cleanupLView` recursively.
  24271. cleanupLView(lView[i]);
  24272. }
  24273. }
  24274. }
  24275. /**
  24276. * Walks over all views registered within the ApplicationRef and removes
  24277. * all dehydrated views from all `LContainer`s along the way.
  24278. */
  24279. function cleanupDehydratedViews(appRef) {
  24280. const viewRefs = appRef._views;
  24281. for (const viewRef of viewRefs) {
  24282. const lNode = getLNodeForHydration(viewRef);
  24283. // An `lView` might be `null` if a `ViewRef` represents
  24284. // an embedded view (not a component view).
  24285. if (lNode !== null && lNode[HOST] !== null) {
  24286. if (isLView(lNode)) {
  24287. cleanupLView(lNode);
  24288. }
  24289. else {
  24290. // Cleanup in the root component view
  24291. const componentLView = lNode[HOST];
  24292. cleanupLView(componentLView);
  24293. // Cleanup in all views within this view container
  24294. cleanupLContainer(lNode);
  24295. }
  24296. ngDevMode && ngDevMode.dehydratedViewsCleanupRuns++;
  24297. }
  24298. }
  24299. }
  24300. /**
  24301. * Given a current DOM node and a serialized information about the views
  24302. * in a container, walks over the DOM structure, collecting the list of
  24303. * dehydrated views.
  24304. */
  24305. function locateDehydratedViewsInContainer(currentRNode, serializedViews) {
  24306. const dehydratedViews = [];
  24307. for (const serializedView of serializedViews) {
  24308. // Repeats a view multiple times as needed, based on the serialized information
  24309. // (for example, for *ngFor-produced views).
  24310. for (let i = 0; i < (serializedView[MULTIPLIER] ?? 1); i++) {
  24311. const view = {
  24312. data: serializedView,
  24313. firstChild: null,
  24314. };
  24315. if (serializedView[NUM_ROOT_NODES] > 0) {
  24316. // Keep reference to the first node in this view,
  24317. // so it can be accessed while invoking template instructions.
  24318. view.firstChild = currentRNode;
  24319. // Move over to the next node after this view, which can
  24320. // either be a first node of the next view or an anchor comment
  24321. // node after the last view in a container.
  24322. currentRNode = siblingAfter(serializedView[NUM_ROOT_NODES], currentRNode);
  24323. }
  24324. dehydratedViews.push(view);
  24325. }
  24326. }
  24327. return [currentRNode, dehydratedViews];
  24328. }
  24329. /**
  24330. * Reference to a function that searches for a matching dehydrated views
  24331. * stored on a given lContainer.
  24332. * Returns `null` by default, when hydration is not enabled.
  24333. */
  24334. let _findMatchingDehydratedViewImpl = (lContainer, template) => null;
  24335. /**
  24336. * Retrieves the next dehydrated view from the LContainer and verifies that
  24337. * it matches a given template id (from the TView that was used to create this
  24338. * instance of a view). If the id doesn't match, that means that we are in an
  24339. * unexpected state and can not complete the reconciliation process. Thus,
  24340. * all dehydrated views from this LContainer are removed (including corresponding
  24341. * DOM nodes) and the rendering is performed as if there were no dehydrated views
  24342. * in this container.
  24343. */
  24344. function findMatchingDehydratedViewImpl(lContainer, template) {
  24345. const views = lContainer[DEHYDRATED_VIEWS];
  24346. if (!template || views === null || views.length === 0) {
  24347. return null;
  24348. }
  24349. const view = views[0];
  24350. // Verify whether the first dehydrated view in the container matches
  24351. // the template id passed to this function (that originated from a TView
  24352. // that was used to create an instance of an embedded or component views.
  24353. if (view.data[TEMPLATE_ID] === template) {
  24354. // If the template id matches - extract the first view and return it.
  24355. return views.shift();
  24356. }
  24357. else {
  24358. // Otherwise, we are at the state when reconciliation can not be completed,
  24359. // thus we remove all dehydrated views within this container (remove them
  24360. // from internal data structures as well as delete associated elements from
  24361. // the DOM tree).
  24362. removeDehydratedViews(lContainer);
  24363. return null;
  24364. }
  24365. }
  24366. function enableFindMatchingDehydratedViewImpl() {
  24367. _findMatchingDehydratedViewImpl = findMatchingDehydratedViewImpl;
  24368. }
  24369. function findMatchingDehydratedView(lContainer, template) {
  24370. return _findMatchingDehydratedViewImpl(lContainer, template);
  24371. }
  24372. /**
  24373. * Represents a container where one or more views can be attached to a component.
  24374. *
  24375. * Can contain *host views* (created by instantiating a
  24376. * component with the `createComponent()` method), and *embedded views*
  24377. * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
  24378. *
  24379. * A view container instance can contain other view containers,
  24380. * creating a [view hierarchy](guide/glossary#view-hierarchy).
  24381. *
  24382. * @usageNotes
  24383. *
  24384. * The example below demonstrates how the `createComponent` function can be used
  24385. * to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
  24386. * so that it gets included into change detection cycles.
  24387. *
  24388. * Note: the example uses standalone components, but the function can also be used for
  24389. * non-standalone components (declared in an NgModule) as well.
  24390. *
  24391. * ```typescript
  24392. * @Component({
  24393. * standalone: true,
  24394. * selector: 'dynamic',
  24395. * template: `<span>This is a content of a dynamic component.</span>`,
  24396. * })
  24397. * class DynamicComponent {
  24398. * vcr = inject(ViewContainerRef);
  24399. * }
  24400. *
  24401. * @Component({
  24402. * standalone: true,
  24403. * selector: 'app',
  24404. * template: `<main>Hi! This is the main content.</main>`,
  24405. * })
  24406. * class AppComponent {
  24407. * vcr = inject(ViewContainerRef);
  24408. *
  24409. * ngAfterViewInit() {
  24410. * const compRef = this.vcr.createComponent(DynamicComponent);
  24411. * compRef.changeDetectorRef.detectChanges();
  24412. * }
  24413. * }
  24414. * ```
  24415. *
  24416. * @see {@link ComponentRef}
  24417. * @see {@link EmbeddedViewRef}
  24418. *
  24419. * @publicApi
  24420. */
  24421. class ViewContainerRef {
  24422. /**
  24423. * @internal
  24424. * @nocollapse
  24425. */
  24426. static { this.__NG_ELEMENT_ID__ = injectViewContainerRef; }
  24427. }
  24428. /**
  24429. * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
  24430. * already exists, retrieves the existing ViewContainerRef.
  24431. *
  24432. * @returns The ViewContainerRef instance to use
  24433. */
  24434. function injectViewContainerRef() {
  24435. const previousTNode = getCurrentTNode();
  24436. return createContainerRef(previousTNode, getLView());
  24437. }
  24438. const VE_ViewContainerRef = ViewContainerRef;
  24439. // TODO(alxhub): cleaning up this indirection triggers a subtle bug in Closure in g3. Once the fix
  24440. // for that lands, this can be cleaned up.
  24441. const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
  24442. constructor(_lContainer, _hostTNode, _hostLView) {
  24443. super();
  24444. this._lContainer = _lContainer;
  24445. this._hostTNode = _hostTNode;
  24446. this._hostLView = _hostLView;
  24447. }
  24448. get element() {
  24449. return createElementRef(this._hostTNode, this._hostLView);
  24450. }
  24451. get injector() {
  24452. return new NodeInjector(this._hostTNode, this._hostLView);
  24453. }
  24454. /** @deprecated No replacement */
  24455. get parentInjector() {
  24456. const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostLView);
  24457. if (hasParentInjector(parentLocation)) {
  24458. const parentView = getParentInjectorView(parentLocation, this._hostLView);
  24459. const injectorIndex = getParentInjectorIndex(parentLocation);
  24460. ngDevMode && assertNodeInjector(parentView, injectorIndex);
  24461. const parentTNode = parentView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
  24462. return new NodeInjector(parentTNode, parentView);
  24463. }
  24464. else {
  24465. return new NodeInjector(null, this._hostLView);
  24466. }
  24467. }
  24468. clear() {
  24469. while (this.length > 0) {
  24470. this.remove(this.length - 1);
  24471. }
  24472. }
  24473. get(index) {
  24474. const viewRefs = getViewRefs(this._lContainer);
  24475. return viewRefs !== null && viewRefs[index] || null;
  24476. }
  24477. get length() {
  24478. return this._lContainer.length - CONTAINER_HEADER_OFFSET;
  24479. }
  24480. createEmbeddedView(templateRef, context, indexOrOptions) {
  24481. let index;
  24482. let injector;
  24483. if (typeof indexOrOptions === 'number') {
  24484. index = indexOrOptions;
  24485. }
  24486. else if (indexOrOptions != null) {
  24487. index = indexOrOptions.index;
  24488. injector = indexOrOptions.injector;
  24489. }
  24490. const hydrationInfo = findMatchingDehydratedView(this._lContainer, templateRef.ssrId);
  24491. const viewRef = templateRef.createEmbeddedViewImpl(context || {}, injector, hydrationInfo);
  24492. // If there is a matching dehydrated view, but the host TNode is located in the skip
  24493. // hydration block, this means that the content was detached (as a part of the skip
  24494. // hydration logic) and it needs to be appended into the DOM.
  24495. const skipDomInsertion = !!hydrationInfo && !hasInSkipHydrationBlockFlag(this._hostTNode);
  24496. this.insertImpl(viewRef, index, skipDomInsertion);
  24497. return viewRef;
  24498. }
  24499. createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) {
  24500. const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType);
  24501. let index;
  24502. // This function supports 2 signatures and we need to handle options correctly for both:
  24503. // 1. When first argument is a Component type. This signature also requires extra
  24504. // options to be provided as object (more ergonomic option).
  24505. // 2. First argument is a Component factory. In this case extra options are represented as
  24506. // positional arguments. This signature is less ergonomic and will be deprecated.
  24507. if (isComponentFactory) {
  24508. if (ngDevMode) {
  24509. assertEqual(typeof indexOrOptions !== 'object', true, 'It looks like Component factory was provided as the first argument ' +
  24510. 'and an options object as the second argument. This combination of arguments ' +
  24511. 'is incompatible. You can either change the first argument to provide Component ' +
  24512. 'type or change the second argument to be a number (representing an index at ' +
  24513. 'which to insert the new component\'s host view into this container)');
  24514. }
  24515. index = indexOrOptions;
  24516. }
  24517. else {
  24518. if (ngDevMode) {
  24519. assertDefined(getComponentDef$1(componentFactoryOrType), `Provided Component class doesn't contain Component definition. ` +
  24520. `Please check whether provided class has @Component decorator.`);
  24521. assertEqual(typeof indexOrOptions !== 'number', true, 'It looks like Component type was provided as the first argument ' +
  24522. 'and a number (representing an index at which to insert the new component\'s ' +
  24523. 'host view into this container as the second argument. This combination of arguments ' +
  24524. 'is incompatible. Please use an object as the second argument instead.');
  24525. }
  24526. const options = (indexOrOptions || {});
  24527. if (ngDevMode && options.environmentInjector && options.ngModuleRef) {
  24528. throwError(`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`);
  24529. }
  24530. index = options.index;
  24531. injector = options.injector;
  24532. projectableNodes = options.projectableNodes;
  24533. environmentInjector = options.environmentInjector || options.ngModuleRef;
  24534. }
  24535. const componentFactory = isComponentFactory ?
  24536. componentFactoryOrType :
  24537. new ComponentFactory(getComponentDef$1(componentFactoryOrType));
  24538. const contextInjector = injector || this.parentInjector;
  24539. // If an `NgModuleRef` is not provided explicitly, try retrieving it from the DI tree.
  24540. if (!environmentInjector && componentFactory.ngModule == null) {
  24541. // For the `ComponentFactory` case, entering this logic is very unlikely, since we expect that
  24542. // an instance of a `ComponentFactory`, resolved via `ComponentFactoryResolver` would have an
  24543. // `ngModule` field. This is possible in some test scenarios and potentially in some JIT-based
  24544. // use-cases. For the `ComponentFactory` case we preserve backwards-compatibility and try
  24545. // using a provided injector first, then fall back to the parent injector of this
  24546. // `ViewContainerRef` instance.
  24547. //
  24548. // For the factory-less case, it's critical to establish a connection with the module
  24549. // injector tree (by retrieving an instance of an `NgModuleRef` and accessing its injector),
  24550. // so that a component can use DI tokens provided in MgModules. For this reason, we can not
  24551. // rely on the provided injector, since it might be detached from the DI tree (for example, if
  24552. // it was created via `Injector.create` without specifying a parent injector, or if an
  24553. // injector is retrieved from an `NgModuleRef` created via `createNgModule` using an
  24554. // NgModule outside of a module tree). Instead, we always use `ViewContainerRef`'s parent
  24555. // injector, which is normally connected to the DI tree, which includes module injector
  24556. // subtree.
  24557. const _injector = isComponentFactory ? contextInjector : this.parentInjector;
  24558. // DO NOT REFACTOR. The code here used to have a `injector.get(NgModuleRef, null) ||
  24559. // undefined` expression which seems to cause internal google apps to fail. This is documented
  24560. // in the following internal bug issue: go/b/142967802
  24561. const result = _injector.get(EnvironmentInjector, null);
  24562. if (result) {
  24563. environmentInjector = result;
  24564. }
  24565. }
  24566. const componentDef = getComponentDef$1(componentFactory.componentType ?? {});
  24567. const dehydratedView = findMatchingDehydratedView(this._lContainer, componentDef?.id ?? null);
  24568. const rNode = dehydratedView?.firstChild ?? null;
  24569. const componentRef = componentFactory.create(contextInjector, projectableNodes, rNode, environmentInjector);
  24570. // If there is a matching dehydrated view, but the host TNode is located in the skip
  24571. // hydration block, this means that the content was detached (as a part of the skip
  24572. // hydration logic) and it needs to be appended into the DOM.
  24573. const skipDomInsertion = !!dehydratedView && !hasInSkipHydrationBlockFlag(this._hostTNode);
  24574. this.insertImpl(componentRef.hostView, index, skipDomInsertion);
  24575. return componentRef;
  24576. }
  24577. insert(viewRef, index) {
  24578. return this.insertImpl(viewRef, index, false);
  24579. }
  24580. insertImpl(viewRef, index, skipDomInsertion) {
  24581. const lView = viewRef._lView;
  24582. const tView = lView[TVIEW];
  24583. if (ngDevMode && viewRef.destroyed) {
  24584. throw new Error('Cannot insert a destroyed View in a ViewContainer!');
  24585. }
  24586. if (viewAttachedToContainer(lView)) {
  24587. // If view is already attached, detach it first so we clean up references appropriately.
  24588. const prevIdx = this.indexOf(viewRef);
  24589. // A view might be attached either to this or a different container. The `prevIdx` for
  24590. // those cases will be:
  24591. // equal to -1 for views attached to this ViewContainerRef
  24592. // >= 0 for views attached to a different ViewContainerRef
  24593. if (prevIdx !== -1) {
  24594. this.detach(prevIdx);
  24595. }
  24596. else {
  24597. const prevLContainer = lView[PARENT];
  24598. ngDevMode &&
  24599. assertEqual(isLContainer(prevLContainer), true, 'An attached view should have its PARENT point to a container.');
  24600. // We need to re-create a R3ViewContainerRef instance since those are not stored on
  24601. // LView (nor anywhere else).
  24602. const prevVCRef = new R3ViewContainerRef(prevLContainer, prevLContainer[T_HOST], prevLContainer[PARENT]);
  24603. prevVCRef.detach(prevVCRef.indexOf(viewRef));
  24604. }
  24605. }
  24606. // Logical operation of adding `LView` to `LContainer`
  24607. const adjustedIdx = this._adjustIndex(index);
  24608. const lContainer = this._lContainer;
  24609. addLViewToLContainer(lContainer, lView, adjustedIdx, !skipDomInsertion);
  24610. viewRef.attachToViewContainerRef();
  24611. addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
  24612. return viewRef;
  24613. }
  24614. move(viewRef, newIndex) {
  24615. if (ngDevMode && viewRef.destroyed) {
  24616. throw new Error('Cannot move a destroyed View in a ViewContainer!');
  24617. }
  24618. return this.insert(viewRef, newIndex);
  24619. }
  24620. indexOf(viewRef) {
  24621. const viewRefsArr = getViewRefs(this._lContainer);
  24622. return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
  24623. }
  24624. remove(index) {
  24625. const adjustedIdx = this._adjustIndex(index, -1);
  24626. const detachedView = detachView(this._lContainer, adjustedIdx);
  24627. if (detachedView) {
  24628. // Before destroying the view, remove it from the container's array of `ViewRef`s.
  24629. // This ensures the view container length is updated before calling
  24630. // `destroyLView`, which could recursively call view container methods that
  24631. // rely on an accurate container length.
  24632. // (e.g. a method on this view container being called by a child directive's OnDestroy
  24633. // lifecycle hook)
  24634. removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx);
  24635. destroyLView(detachedView[TVIEW], detachedView);
  24636. }
  24637. }
  24638. detach(index) {
  24639. const adjustedIdx = this._adjustIndex(index, -1);
  24640. const view = detachView(this._lContainer, adjustedIdx);
  24641. const wasDetached = view && removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx) != null;
  24642. return wasDetached ? new ViewRef(view) : null;
  24643. }
  24644. _adjustIndex(index, shift = 0) {
  24645. if (index == null) {
  24646. return this.length + shift;
  24647. }
  24648. if (ngDevMode) {
  24649. assertGreaterThan(index, -1, `ViewRef index must be positive, got ${index}`);
  24650. // +1 because it's legal to insert at the end.
  24651. assertLessThan(index, this.length + 1 + shift, 'index');
  24652. }
  24653. return index;
  24654. }
  24655. };
  24656. function getViewRefs(lContainer) {
  24657. return lContainer[VIEW_REFS];
  24658. }
  24659. function getOrCreateViewRefs(lContainer) {
  24660. return (lContainer[VIEW_REFS] || (lContainer[VIEW_REFS] = []));
  24661. }
  24662. /**
  24663. * Creates a ViewContainerRef and stores it on the injector.
  24664. *
  24665. * @param hostTNode The node that is requesting a ViewContainerRef
  24666. * @param hostLView The view to which the node belongs
  24667. * @returns The ViewContainerRef instance to use
  24668. */
  24669. function createContainerRef(hostTNode, hostLView) {
  24670. ngDevMode && assertTNodeType(hostTNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
  24671. let lContainer;
  24672. const slotValue = hostLView[hostTNode.index];
  24673. if (isLContainer(slotValue)) {
  24674. // If the host is a container, we don't need to create a new LContainer
  24675. lContainer = slotValue;
  24676. }
  24677. else {
  24678. // An LContainer anchor can not be `null`, but we set it here temporarily
  24679. // and update to the actual value later in this function (see
  24680. // `_locateOrCreateAnchorNode`).
  24681. lContainer = createLContainer(slotValue, hostLView, null, hostTNode);
  24682. hostLView[hostTNode.index] = lContainer;
  24683. addToViewTree(hostLView, lContainer);
  24684. }
  24685. _locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue);
  24686. return new R3ViewContainerRef(lContainer, hostTNode, hostLView);
  24687. }
  24688. /**
  24689. * Creates and inserts a comment node that acts as an anchor for a view container.
  24690. *
  24691. * If the host is a regular element, we have to insert a comment node manually which will
  24692. * be used as an anchor when inserting elements. In this specific case we use low-level DOM
  24693. * manipulation to insert it.
  24694. */
  24695. function insertAnchorNode(hostLView, hostTNode) {
  24696. const renderer = hostLView[RENDERER];
  24697. ngDevMode && ngDevMode.rendererCreateComment++;
  24698. const commentNode = renderer.createComment(ngDevMode ? 'container' : '');
  24699. const hostNative = getNativeByTNode(hostTNode, hostLView);
  24700. const parentOfHostNative = nativeParentNode(renderer, hostNative);
  24701. nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
  24702. return commentNode;
  24703. }
  24704. let _locateOrCreateAnchorNode = createAnchorNode;
  24705. /**
  24706. * Regular creation mode: an anchor is created and
  24707. * assigned to the `lContainer[NATIVE]` slot.
  24708. */
  24709. function createAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
  24710. // We already have a native element (anchor) set, return.
  24711. if (lContainer[NATIVE])
  24712. return;
  24713. let commentNode;
  24714. // If the host is an element container, the native host element is guaranteed to be a
  24715. // comment and we can reuse that comment as anchor element for the new LContainer.
  24716. // The comment node in question is already part of the DOM structure so we don't need to append
  24717. // it again.
  24718. if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
  24719. commentNode = unwrapRNode(slotValue);
  24720. }
  24721. else {
  24722. commentNode = insertAnchorNode(hostLView, hostTNode);
  24723. }
  24724. lContainer[NATIVE] = commentNode;
  24725. }
  24726. /**
  24727. * Hydration logic that looks up:
  24728. * - an anchor node in the DOM and stores the node in `lContainer[NATIVE]`
  24729. * - all dehydrated views in this container and puts them into `lContainer[DEHYDRATED_VIEWS]`
  24730. */
  24731. function locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
  24732. // We already have a native element (anchor) set and the process
  24733. // of finding dehydrated views happened (so the `lContainer[DEHYDRATED_VIEWS]`
  24734. // is not null), exit early.
  24735. if (lContainer[NATIVE] && lContainer[DEHYDRATED_VIEWS])
  24736. return;
  24737. const hydrationInfo = hostLView[HYDRATION];
  24738. const noOffsetIndex = hostTNode.index - HEADER_OFFSET;
  24739. // TODO(akushnir): this should really be a single condition, refactor the code
  24740. // to use `hasInSkipHydrationBlockFlag` logic inside `isInSkipHydrationBlock`.
  24741. const skipHydration = isInSkipHydrationBlock(hostTNode) || hasInSkipHydrationBlockFlag(hostTNode);
  24742. const isNodeCreationMode = !hydrationInfo || skipHydration || isDisconnectedNode(hydrationInfo, noOffsetIndex);
  24743. // Regular creation mode.
  24744. if (isNodeCreationMode) {
  24745. return createAnchorNode(lContainer, hostLView, hostTNode, slotValue);
  24746. }
  24747. // Hydration mode, looking up an anchor node and dehydrated views in DOM.
  24748. const currentRNode = getSegmentHead(hydrationInfo, noOffsetIndex);
  24749. const serializedViews = hydrationInfo.data[CONTAINERS]?.[noOffsetIndex];
  24750. ngDevMode &&
  24751. assertDefined(serializedViews, 'Unexpected state: no hydration info available for a given TNode, ' +
  24752. 'which represents a view container.');
  24753. const [commentNode, dehydratedViews] = locateDehydratedViewsInContainer(currentRNode, serializedViews);
  24754. if (ngDevMode) {
  24755. validateMatchingNode(commentNode, Node.COMMENT_NODE, null, hostLView, hostTNode, true);
  24756. // Do not throw in case this node is already claimed (thus `false` as a second
  24757. // argument). If this container is created based on an `<ng-template>`, the comment
  24758. // node would be already claimed from the `template` instruction. If an element acts
  24759. // as an anchor (e.g. <div #vcRef>), a separate comment node would be created/located,
  24760. // so we need to claim it here.
  24761. markRNodeAsClaimedByHydration(commentNode, false);
  24762. }
  24763. lContainer[NATIVE] = commentNode;
  24764. lContainer[DEHYDRATED_VIEWS] = dehydratedViews;
  24765. }
  24766. function enableLocateOrCreateContainerRefImpl() {
  24767. _locateOrCreateAnchorNode = locateOrCreateAnchorNode;
  24768. }
  24769. class LQuery_ {
  24770. constructor(queryList) {
  24771. this.queryList = queryList;
  24772. this.matches = null;
  24773. }
  24774. clone() {
  24775. return new LQuery_(this.queryList);
  24776. }
  24777. setDirty() {
  24778. this.queryList.setDirty();
  24779. }
  24780. }
  24781. class LQueries_ {
  24782. constructor(queries = []) {
  24783. this.queries = queries;
  24784. }
  24785. createEmbeddedView(tView) {
  24786. const tQueries = tView.queries;
  24787. if (tQueries !== null) {
  24788. const noOfInheritedQueries = tView.contentQueries !== null ? tView.contentQueries[0] : tQueries.length;
  24789. const viewLQueries = [];
  24790. // An embedded view has queries propagated from a declaration view at the beginning of the
  24791. // TQueries collection and up until a first content query declared in the embedded view. Only
  24792. // propagated LQueries are created at this point (LQuery corresponding to declared content
  24793. // queries will be instantiated from the content query instructions for each directive).
  24794. for (let i = 0; i < noOfInheritedQueries; i++) {
  24795. const tQuery = tQueries.getByIndex(i);
  24796. const parentLQuery = this.queries[tQuery.indexInDeclarationView];
  24797. viewLQueries.push(parentLQuery.clone());
  24798. }
  24799. return new LQueries_(viewLQueries);
  24800. }
  24801. return null;
  24802. }
  24803. insertView(tView) {
  24804. this.dirtyQueriesWithMatches(tView);
  24805. }
  24806. detachView(tView) {
  24807. this.dirtyQueriesWithMatches(tView);
  24808. }
  24809. dirtyQueriesWithMatches(tView) {
  24810. for (let i = 0; i < this.queries.length; i++) {
  24811. if (getTQuery(tView, i).matches !== null) {
  24812. this.queries[i].setDirty();
  24813. }
  24814. }
  24815. }
  24816. }
  24817. class TQueryMetadata_ {
  24818. constructor(predicate, flags, read = null) {
  24819. this.predicate = predicate;
  24820. this.flags = flags;
  24821. this.read = read;
  24822. }
  24823. }
  24824. class TQueries_ {
  24825. constructor(queries = []) {
  24826. this.queries = queries;
  24827. }
  24828. elementStart(tView, tNode) {
  24829. ngDevMode &&
  24830. assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only');
  24831. for (let i = 0; i < this.queries.length; i++) {
  24832. this.queries[i].elementStart(tView, tNode);
  24833. }
  24834. }
  24835. elementEnd(tNode) {
  24836. for (let i = 0; i < this.queries.length; i++) {
  24837. this.queries[i].elementEnd(tNode);
  24838. }
  24839. }
  24840. embeddedTView(tNode) {
  24841. let queriesForTemplateRef = null;
  24842. for (let i = 0; i < this.length; i++) {
  24843. const childQueryIndex = queriesForTemplateRef !== null ? queriesForTemplateRef.length : 0;
  24844. const tqueryClone = this.getByIndex(i).embeddedTView(tNode, childQueryIndex);
  24845. if (tqueryClone) {
  24846. tqueryClone.indexInDeclarationView = i;
  24847. if (queriesForTemplateRef !== null) {
  24848. queriesForTemplateRef.push(tqueryClone);
  24849. }
  24850. else {
  24851. queriesForTemplateRef = [tqueryClone];
  24852. }
  24853. }
  24854. }
  24855. return queriesForTemplateRef !== null ? new TQueries_(queriesForTemplateRef) : null;
  24856. }
  24857. template(tView, tNode) {
  24858. ngDevMode &&
  24859. assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only');
  24860. for (let i = 0; i < this.queries.length; i++) {
  24861. this.queries[i].template(tView, tNode);
  24862. }
  24863. }
  24864. getByIndex(index) {
  24865. ngDevMode && assertIndexInRange(this.queries, index);
  24866. return this.queries[index];
  24867. }
  24868. get length() {
  24869. return this.queries.length;
  24870. }
  24871. track(tquery) {
  24872. this.queries.push(tquery);
  24873. }
  24874. }
  24875. class TQuery_ {
  24876. constructor(metadata, nodeIndex = -1) {
  24877. this.metadata = metadata;
  24878. this.matches = null;
  24879. this.indexInDeclarationView = -1;
  24880. this.crossesNgTemplate = false;
  24881. /**
  24882. * A flag indicating if a given query still applies to nodes it is crossing. We use this flag
  24883. * (alongside with _declarationNodeIndex) to know when to stop applying content queries to
  24884. * elements in a template.
  24885. */
  24886. this._appliesToNextNode = true;
  24887. this._declarationNodeIndex = nodeIndex;
  24888. }
  24889. elementStart(tView, tNode) {
  24890. if (this.isApplyingToNode(tNode)) {
  24891. this.matchTNode(tView, tNode);
  24892. }
  24893. }
  24894. elementEnd(tNode) {
  24895. if (this._declarationNodeIndex === tNode.index) {
  24896. this._appliesToNextNode = false;
  24897. }
  24898. }
  24899. template(tView, tNode) {
  24900. this.elementStart(tView, tNode);
  24901. }
  24902. embeddedTView(tNode, childQueryIndex) {
  24903. if (this.isApplyingToNode(tNode)) {
  24904. this.crossesNgTemplate = true;
  24905. // A marker indicating a `<ng-template>` element (a placeholder for query results from
  24906. // embedded views created based on this `<ng-template>`).
  24907. this.addMatch(-tNode.index, childQueryIndex);
  24908. return new TQuery_(this.metadata);
  24909. }
  24910. return null;
  24911. }
  24912. isApplyingToNode(tNode) {
  24913. if (this._appliesToNextNode &&
  24914. (this.metadata.flags & 1 /* QueryFlags.descendants */) !== 1 /* QueryFlags.descendants */) {
  24915. const declarationNodeIdx = this._declarationNodeIndex;
  24916. let parent = tNode.parent;
  24917. // Determine if a given TNode is a "direct" child of a node on which a content query was
  24918. // declared (only direct children of query's host node can match with the descendants: false
  24919. // option). There are 3 main use-case / conditions to consider here:
  24920. // - <needs-target><i #target></i></needs-target>: here <i #target> parent node is a query
  24921. // host node;
  24922. // - <needs-target><ng-template [ngIf]="true"><i #target></i></ng-template></needs-target>:
  24923. // here <i #target> parent node is null;
  24924. // - <needs-target><ng-container><i #target></i></ng-container></needs-target>: here we need
  24925. // to go past `<ng-container>` to determine <i #target> parent node (but we shouldn't traverse
  24926. // up past the query's host node!).
  24927. while (parent !== null && (parent.type & 8 /* TNodeType.ElementContainer */) &&
  24928. parent.index !== declarationNodeIdx) {
  24929. parent = parent.parent;
  24930. }
  24931. return declarationNodeIdx === (parent !== null ? parent.index : -1);
  24932. }
  24933. return this._appliesToNextNode;
  24934. }
  24935. matchTNode(tView, tNode) {
  24936. const predicate = this.metadata.predicate;
  24937. if (Array.isArray(predicate)) {
  24938. for (let i = 0; i < predicate.length; i++) {
  24939. const name = predicate[i];
  24940. this.matchTNodeWithReadOption(tView, tNode, getIdxOfMatchingSelector(tNode, name));
  24941. // Also try matching the name to a provider since strings can be used as DI tokens too.
  24942. this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, name, false, false));
  24943. }
  24944. }
  24945. else {
  24946. if (predicate === TemplateRef) {
  24947. if (tNode.type & 4 /* TNodeType.Container */) {
  24948. this.matchTNodeWithReadOption(tView, tNode, -1);
  24949. }
  24950. }
  24951. else {
  24952. this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, predicate, false, false));
  24953. }
  24954. }
  24955. }
  24956. matchTNodeWithReadOption(tView, tNode, nodeMatchIdx) {
  24957. if (nodeMatchIdx !== null) {
  24958. const read = this.metadata.read;
  24959. if (read !== null) {
  24960. if (read === ElementRef || read === ViewContainerRef ||
  24961. read === TemplateRef && (tNode.type & 4 /* TNodeType.Container */)) {
  24962. this.addMatch(tNode.index, -2);
  24963. }
  24964. else {
  24965. const directiveOrProviderIdx = locateDirectiveOrProvider(tNode, tView, read, false, false);
  24966. if (directiveOrProviderIdx !== null) {
  24967. this.addMatch(tNode.index, directiveOrProviderIdx);
  24968. }
  24969. }
  24970. }
  24971. else {
  24972. this.addMatch(tNode.index, nodeMatchIdx);
  24973. }
  24974. }
  24975. }
  24976. addMatch(tNodeIdx, matchIdx) {
  24977. if (this.matches === null) {
  24978. this.matches = [tNodeIdx, matchIdx];
  24979. }
  24980. else {
  24981. this.matches.push(tNodeIdx, matchIdx);
  24982. }
  24983. }
  24984. }
  24985. /**
  24986. * Iterates over local names for a given node and returns directive index
  24987. * (or -1 if a local name points to an element).
  24988. *
  24989. * @param tNode static data of a node to check
  24990. * @param selector selector to match
  24991. * @returns directive index, -1 or null if a selector didn't match any of the local names
  24992. */
  24993. function getIdxOfMatchingSelector(tNode, selector) {
  24994. const localNames = tNode.localNames;
  24995. if (localNames !== null) {
  24996. for (let i = 0; i < localNames.length; i += 2) {
  24997. if (localNames[i] === selector) {
  24998. return localNames[i + 1];
  24999. }
  25000. }
  25001. }
  25002. return null;
  25003. }
  25004. function createResultByTNodeType(tNode, currentView) {
  25005. if (tNode.type & (3 /* TNodeType.AnyRNode */ | 8 /* TNodeType.ElementContainer */)) {
  25006. return createElementRef(tNode, currentView);
  25007. }
  25008. else if (tNode.type & 4 /* TNodeType.Container */) {
  25009. return createTemplateRef(tNode, currentView);
  25010. }
  25011. return null;
  25012. }
  25013. function createResultForNode(lView, tNode, matchingIdx, read) {
  25014. if (matchingIdx === -1) {
  25015. // if read token and / or strategy is not specified, detect it using appropriate tNode type
  25016. return createResultByTNodeType(tNode, lView);
  25017. }
  25018. else if (matchingIdx === -2) {
  25019. // read a special token from a node injector
  25020. return createSpecialToken(lView, tNode, read);
  25021. }
  25022. else {
  25023. // read a token
  25024. return getNodeInjectable(lView, lView[TVIEW], matchingIdx, tNode);
  25025. }
  25026. }
  25027. function createSpecialToken(lView, tNode, read) {
  25028. if (read === ElementRef) {
  25029. return createElementRef(tNode, lView);
  25030. }
  25031. else if (read === TemplateRef) {
  25032. return createTemplateRef(tNode, lView);
  25033. }
  25034. else if (read === ViewContainerRef) {
  25035. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
  25036. return createContainerRef(tNode, lView);
  25037. }
  25038. else {
  25039. ngDevMode &&
  25040. throwError(`Special token to read should be one of ElementRef, TemplateRef or ViewContainerRef but got ${stringify(read)}.`);
  25041. }
  25042. }
  25043. /**
  25044. * A helper function that creates query results for a given view. This function is meant to do the
  25045. * processing once and only once for a given view instance (a set of results for a given view
  25046. * doesn't change).
  25047. */
  25048. function materializeViewResults(tView, lView, tQuery, queryIndex) {
  25049. const lQuery = lView[QUERIES].queries[queryIndex];
  25050. if (lQuery.matches === null) {
  25051. const tViewData = tView.data;
  25052. const tQueryMatches = tQuery.matches;
  25053. const result = [];
  25054. for (let i = 0; i < tQueryMatches.length; i += 2) {
  25055. const matchedNodeIdx = tQueryMatches[i];
  25056. if (matchedNodeIdx < 0) {
  25057. // we at the <ng-template> marker which might have results in views created based on this
  25058. // <ng-template> - those results will be in separate views though, so here we just leave
  25059. // null as a placeholder
  25060. result.push(null);
  25061. }
  25062. else {
  25063. ngDevMode && assertIndexInRange(tViewData, matchedNodeIdx);
  25064. const tNode = tViewData[matchedNodeIdx];
  25065. result.push(createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read));
  25066. }
  25067. }
  25068. lQuery.matches = result;
  25069. }
  25070. return lQuery.matches;
  25071. }
  25072. /**
  25073. * A helper function that collects (already materialized) query results from a tree of views,
  25074. * starting with a provided LView.
  25075. */
  25076. function collectQueryResults(tView, lView, queryIndex, result) {
  25077. const tQuery = tView.queries.getByIndex(queryIndex);
  25078. const tQueryMatches = tQuery.matches;
  25079. if (tQueryMatches !== null) {
  25080. const lViewResults = materializeViewResults(tView, lView, tQuery, queryIndex);
  25081. for (let i = 0; i < tQueryMatches.length; i += 2) {
  25082. const tNodeIdx = tQueryMatches[i];
  25083. if (tNodeIdx > 0) {
  25084. result.push(lViewResults[i / 2]);
  25085. }
  25086. else {
  25087. const childQueryIndex = tQueryMatches[i + 1];
  25088. const declarationLContainer = lView[-tNodeIdx];
  25089. ngDevMode && assertLContainer(declarationLContainer);
  25090. // collect matches for views inserted in this container
  25091. for (let i = CONTAINER_HEADER_OFFSET; i < declarationLContainer.length; i++) {
  25092. const embeddedLView = declarationLContainer[i];
  25093. if (embeddedLView[DECLARATION_LCONTAINER] === embeddedLView[PARENT]) {
  25094. collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result);
  25095. }
  25096. }
  25097. // collect matches for views created from this declaration container and inserted into
  25098. // different containers
  25099. if (declarationLContainer[MOVED_VIEWS] !== null) {
  25100. const embeddedLViews = declarationLContainer[MOVED_VIEWS];
  25101. for (let i = 0; i < embeddedLViews.length; i++) {
  25102. const embeddedLView = embeddedLViews[i];
  25103. collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result);
  25104. }
  25105. }
  25106. }
  25107. }
  25108. }
  25109. return result;
  25110. }
  25111. /**
  25112. * Refreshes a query by combining matches from all active views and removing matches from deleted
  25113. * views.
  25114. *
  25115. * @returns `true` if a query got dirty during change detection or if this is a static query
  25116. * resolving in creation mode, `false` otherwise.
  25117. *
  25118. * @codeGenApi
  25119. */
  25120. function ɵɵqueryRefresh(queryList) {
  25121. const lView = getLView();
  25122. const tView = getTView();
  25123. const queryIndex = getCurrentQueryIndex();
  25124. setCurrentQueryIndex(queryIndex + 1);
  25125. const tQuery = getTQuery(tView, queryIndex);
  25126. if (queryList.dirty &&
  25127. (isCreationMode(lView) ===
  25128. ((tQuery.metadata.flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */))) {
  25129. if (tQuery.matches === null) {
  25130. queryList.reset([]);
  25131. }
  25132. else {
  25133. const result = tQuery.crossesNgTemplate ?
  25134. collectQueryResults(tView, lView, queryIndex, []) :
  25135. materializeViewResults(tView, lView, tQuery, queryIndex);
  25136. queryList.reset(result, unwrapElementRef);
  25137. queryList.notifyOnChanges();
  25138. }
  25139. return true;
  25140. }
  25141. return false;
  25142. }
  25143. /**
  25144. * Creates new QueryList, stores the reference in LView and returns QueryList.
  25145. *
  25146. * @param predicate The type for which the query will search
  25147. * @param flags Flags associated with the query
  25148. * @param read What to save in the query
  25149. *
  25150. * @codeGenApi
  25151. */
  25152. function ɵɵviewQuery(predicate, flags, read) {
  25153. ngDevMode && assertNumber(flags, 'Expecting flags');
  25154. const tView = getTView();
  25155. if (tView.firstCreatePass) {
  25156. createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1);
  25157. if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) {
  25158. tView.staticViewQueries = true;
  25159. }
  25160. }
  25161. createLQuery(tView, getLView(), flags);
  25162. }
  25163. /**
  25164. * Registers a QueryList, associated with a content query, for later refresh (part of a view
  25165. * refresh).
  25166. *
  25167. * @param directiveIndex Current directive index
  25168. * @param predicate The type for which the query will search
  25169. * @param flags Flags associated with the query
  25170. * @param read What to save in the query
  25171. * @returns QueryList<T>
  25172. *
  25173. * @codeGenApi
  25174. */
  25175. function ɵɵcontentQuery(directiveIndex, predicate, flags, read) {
  25176. ngDevMode && assertNumber(flags, 'Expecting flags');
  25177. const tView = getTView();
  25178. if (tView.firstCreatePass) {
  25179. const tNode = getCurrentTNode();
  25180. createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index);
  25181. saveContentQueryAndDirectiveIndex(tView, directiveIndex);
  25182. if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) {
  25183. tView.staticContentQueries = true;
  25184. }
  25185. }
  25186. createLQuery(tView, getLView(), flags);
  25187. }
  25188. /**
  25189. * Loads a QueryList corresponding to the current view or content query.
  25190. *
  25191. * @codeGenApi
  25192. */
  25193. function ɵɵloadQuery() {
  25194. return loadQueryInternal(getLView(), getCurrentQueryIndex());
  25195. }
  25196. function loadQueryInternal(lView, queryIndex) {
  25197. ngDevMode &&
  25198. assertDefined(lView[QUERIES], 'LQueries should be defined when trying to load a query');
  25199. ngDevMode && assertIndexInRange(lView[QUERIES].queries, queryIndex);
  25200. return lView[QUERIES].queries[queryIndex].queryList;
  25201. }
  25202. function createLQuery(tView, lView, flags) {
  25203. const queryList = new QueryList((flags & 4 /* QueryFlags.emitDistinctChangesOnly */) === 4 /* QueryFlags.emitDistinctChangesOnly */);
  25204. storeCleanupWithContext(tView, lView, queryList, queryList.destroy);
  25205. if (lView[QUERIES] === null)
  25206. lView[QUERIES] = new LQueries_();
  25207. lView[QUERIES].queries.push(new LQuery_(queryList));
  25208. }
  25209. function createTQuery(tView, metadata, nodeIndex) {
  25210. if (tView.queries === null)
  25211. tView.queries = new TQueries_();
  25212. tView.queries.track(new TQuery_(metadata, nodeIndex));
  25213. }
  25214. function saveContentQueryAndDirectiveIndex(tView, directiveIndex) {
  25215. const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
  25216. const lastSavedDirectiveIndex = tViewContentQueries.length ? tViewContentQueries[tViewContentQueries.length - 1] : -1;
  25217. if (directiveIndex !== lastSavedDirectiveIndex) {
  25218. tViewContentQueries.push(tView.queries.length - 1, directiveIndex);
  25219. }
  25220. }
  25221. function getTQuery(tView, index) {
  25222. ngDevMode && assertDefined(tView.queries, 'TQueries must be defined to retrieve a TQuery');
  25223. return tView.queries.getByIndex(index);
  25224. }
  25225. /**
  25226. * Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the
  25227. * `<ng-template>` element.
  25228. *
  25229. * @codeGenApi
  25230. */
  25231. function ɵɵtemplateRefExtractor(tNode, lView) {
  25232. return createTemplateRef(tNode, lView);
  25233. }
  25234. /**
  25235. * A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
  25236. *
  25237. * This should be kept up to date with the public exports of @angular/core.
  25238. */
  25239. const angularCoreEnv = (() => ({
  25240. 'ɵɵattribute': ɵɵattribute,
  25241. 'ɵɵattributeInterpolate1': ɵɵattributeInterpolate1,
  25242. 'ɵɵattributeInterpolate2': ɵɵattributeInterpolate2,
  25243. 'ɵɵattributeInterpolate3': ɵɵattributeInterpolate3,
  25244. 'ɵɵattributeInterpolate4': ɵɵattributeInterpolate4,
  25245. 'ɵɵattributeInterpolate5': ɵɵattributeInterpolate5,
  25246. 'ɵɵattributeInterpolate6': ɵɵattributeInterpolate6,
  25247. 'ɵɵattributeInterpolate7': ɵɵattributeInterpolate7,
  25248. 'ɵɵattributeInterpolate8': ɵɵattributeInterpolate8,
  25249. 'ɵɵattributeInterpolateV': ɵɵattributeInterpolateV,
  25250. 'ɵɵdefineComponent': ɵɵdefineComponent,
  25251. 'ɵɵdefineDirective': ɵɵdefineDirective,
  25252. 'ɵɵdefineInjectable': ɵɵdefineInjectable,
  25253. 'ɵɵdefineInjector': ɵɵdefineInjector,
  25254. 'ɵɵdefineNgModule': ɵɵdefineNgModule,
  25255. 'ɵɵdefinePipe': ɵɵdefinePipe,
  25256. 'ɵɵdirectiveInject': ɵɵdirectiveInject,
  25257. 'ɵɵgetInheritedFactory': ɵɵgetInheritedFactory,
  25258. 'ɵɵinject': ɵɵinject,
  25259. 'ɵɵinjectAttribute': ɵɵinjectAttribute,
  25260. 'ɵɵinvalidFactory': ɵɵinvalidFactory,
  25261. 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep,
  25262. 'ɵɵtemplateRefExtractor': ɵɵtemplateRefExtractor,
  25263. 'ɵɵresetView': ɵɵresetView,
  25264. 'ɵɵHostDirectivesFeature': ɵɵHostDirectivesFeature,
  25265. 'ɵɵNgOnChangesFeature': ɵɵNgOnChangesFeature,
  25266. 'ɵɵProvidersFeature': ɵɵProvidersFeature,
  25267. 'ɵɵCopyDefinitionFeature': ɵɵCopyDefinitionFeature,
  25268. 'ɵɵInheritDefinitionFeature': ɵɵInheritDefinitionFeature,
  25269. 'ɵɵInputTransformsFeature': ɵɵInputTransformsFeature,
  25270. 'ɵɵStandaloneFeature': ɵɵStandaloneFeature,
  25271. 'ɵɵnextContext': ɵɵnextContext,
  25272. 'ɵɵnamespaceHTML': ɵɵnamespaceHTML,
  25273. 'ɵɵnamespaceMathML': ɵɵnamespaceMathML,
  25274. 'ɵɵnamespaceSVG': ɵɵnamespaceSVG,
  25275. 'ɵɵenableBindings': ɵɵenableBindings,
  25276. 'ɵɵdisableBindings': ɵɵdisableBindings,
  25277. 'ɵɵelementStart': ɵɵelementStart,
  25278. 'ɵɵelementEnd': ɵɵelementEnd,
  25279. 'ɵɵelement': ɵɵelement,
  25280. 'ɵɵelementContainerStart': ɵɵelementContainerStart,
  25281. 'ɵɵelementContainerEnd': ɵɵelementContainerEnd,
  25282. 'ɵɵelementContainer': ɵɵelementContainer,
  25283. 'ɵɵpureFunction0': ɵɵpureFunction0,
  25284. 'ɵɵpureFunction1': ɵɵpureFunction1,
  25285. 'ɵɵpureFunction2': ɵɵpureFunction2,
  25286. 'ɵɵpureFunction3': ɵɵpureFunction3,
  25287. 'ɵɵpureFunction4': ɵɵpureFunction4,
  25288. 'ɵɵpureFunction5': ɵɵpureFunction5,
  25289. 'ɵɵpureFunction6': ɵɵpureFunction6,
  25290. 'ɵɵpureFunction7': ɵɵpureFunction7,
  25291. 'ɵɵpureFunction8': ɵɵpureFunction8,
  25292. 'ɵɵpureFunctionV': ɵɵpureFunctionV,
  25293. 'ɵɵgetCurrentView': ɵɵgetCurrentView,
  25294. 'ɵɵrestoreView': ɵɵrestoreView,
  25295. 'ɵɵlistener': ɵɵlistener,
  25296. 'ɵɵprojection': ɵɵprojection,
  25297. 'ɵɵsyntheticHostProperty': ɵɵsyntheticHostProperty,
  25298. 'ɵɵsyntheticHostListener': ɵɵsyntheticHostListener,
  25299. 'ɵɵpipeBind1': ɵɵpipeBind1,
  25300. 'ɵɵpipeBind2': ɵɵpipeBind2,
  25301. 'ɵɵpipeBind3': ɵɵpipeBind3,
  25302. 'ɵɵpipeBind4': ɵɵpipeBind4,
  25303. 'ɵɵpipeBindV': ɵɵpipeBindV,
  25304. 'ɵɵprojectionDef': ɵɵprojectionDef,
  25305. 'ɵɵhostProperty': ɵɵhostProperty,
  25306. 'ɵɵproperty': ɵɵproperty,
  25307. 'ɵɵpropertyInterpolate': ɵɵpropertyInterpolate,
  25308. 'ɵɵpropertyInterpolate1': ɵɵpropertyInterpolate1,
  25309. 'ɵɵpropertyInterpolate2': ɵɵpropertyInterpolate2,
  25310. 'ɵɵpropertyInterpolate3': ɵɵpropertyInterpolate3,
  25311. 'ɵɵpropertyInterpolate4': ɵɵpropertyInterpolate4,
  25312. 'ɵɵpropertyInterpolate5': ɵɵpropertyInterpolate5,
  25313. 'ɵɵpropertyInterpolate6': ɵɵpropertyInterpolate6,
  25314. 'ɵɵpropertyInterpolate7': ɵɵpropertyInterpolate7,
  25315. 'ɵɵpropertyInterpolate8': ɵɵpropertyInterpolate8,
  25316. 'ɵɵpropertyInterpolateV': ɵɵpropertyInterpolateV,
  25317. 'ɵɵpipe': ɵɵpipe,
  25318. 'ɵɵqueryRefresh': ɵɵqueryRefresh,
  25319. 'ɵɵviewQuery': ɵɵviewQuery,
  25320. 'ɵɵloadQuery': ɵɵloadQuery,
  25321. 'ɵɵcontentQuery': ɵɵcontentQuery,
  25322. 'ɵɵreference': ɵɵreference,
  25323. 'ɵɵclassMap': ɵɵclassMap,
  25324. 'ɵɵclassMapInterpolate1': ɵɵclassMapInterpolate1,
  25325. 'ɵɵclassMapInterpolate2': ɵɵclassMapInterpolate2,
  25326. 'ɵɵclassMapInterpolate3': ɵɵclassMapInterpolate3,
  25327. 'ɵɵclassMapInterpolate4': ɵɵclassMapInterpolate4,
  25328. 'ɵɵclassMapInterpolate5': ɵɵclassMapInterpolate5,
  25329. 'ɵɵclassMapInterpolate6': ɵɵclassMapInterpolate6,
  25330. 'ɵɵclassMapInterpolate7': ɵɵclassMapInterpolate7,
  25331. 'ɵɵclassMapInterpolate8': ɵɵclassMapInterpolate8,
  25332. 'ɵɵclassMapInterpolateV': ɵɵclassMapInterpolateV,
  25333. 'ɵɵstyleMap': ɵɵstyleMap,
  25334. 'ɵɵstyleMapInterpolate1': ɵɵstyleMapInterpolate1,
  25335. 'ɵɵstyleMapInterpolate2': ɵɵstyleMapInterpolate2,
  25336. 'ɵɵstyleMapInterpolate3': ɵɵstyleMapInterpolate3,
  25337. 'ɵɵstyleMapInterpolate4': ɵɵstyleMapInterpolate4,
  25338. 'ɵɵstyleMapInterpolate5': ɵɵstyleMapInterpolate5,
  25339. 'ɵɵstyleMapInterpolate6': ɵɵstyleMapInterpolate6,
  25340. 'ɵɵstyleMapInterpolate7': ɵɵstyleMapInterpolate7,
  25341. 'ɵɵstyleMapInterpolate8': ɵɵstyleMapInterpolate8,
  25342. 'ɵɵstyleMapInterpolateV': ɵɵstyleMapInterpolateV,
  25343. 'ɵɵstyleProp': ɵɵstyleProp,
  25344. 'ɵɵstylePropInterpolate1': ɵɵstylePropInterpolate1,
  25345. 'ɵɵstylePropInterpolate2': ɵɵstylePropInterpolate2,
  25346. 'ɵɵstylePropInterpolate3': ɵɵstylePropInterpolate3,
  25347. 'ɵɵstylePropInterpolate4': ɵɵstylePropInterpolate4,
  25348. 'ɵɵstylePropInterpolate5': ɵɵstylePropInterpolate5,
  25349. 'ɵɵstylePropInterpolate6': ɵɵstylePropInterpolate6,
  25350. 'ɵɵstylePropInterpolate7': ɵɵstylePropInterpolate7,
  25351. 'ɵɵstylePropInterpolate8': ɵɵstylePropInterpolate8,
  25352. 'ɵɵstylePropInterpolateV': ɵɵstylePropInterpolateV,
  25353. 'ɵɵclassProp': ɵɵclassProp,
  25354. 'ɵɵadvance': ɵɵadvance,
  25355. 'ɵɵtemplate': ɵɵtemplate,
  25356. 'ɵɵdefer': ɵɵdefer,
  25357. 'ɵɵtext': ɵɵtext,
  25358. 'ɵɵtextInterpolate': ɵɵtextInterpolate,
  25359. 'ɵɵtextInterpolate1': ɵɵtextInterpolate1,
  25360. 'ɵɵtextInterpolate2': ɵɵtextInterpolate2,
  25361. 'ɵɵtextInterpolate3': ɵɵtextInterpolate3,
  25362. 'ɵɵtextInterpolate4': ɵɵtextInterpolate4,
  25363. 'ɵɵtextInterpolate5': ɵɵtextInterpolate5,
  25364. 'ɵɵtextInterpolate6': ɵɵtextInterpolate6,
  25365. 'ɵɵtextInterpolate7': ɵɵtextInterpolate7,
  25366. 'ɵɵtextInterpolate8': ɵɵtextInterpolate8,
  25367. 'ɵɵtextInterpolateV': ɵɵtextInterpolateV,
  25368. 'ɵɵi18n': ɵɵi18n,
  25369. 'ɵɵi18nAttributes': ɵɵi18nAttributes,
  25370. 'ɵɵi18nExp': ɵɵi18nExp,
  25371. 'ɵɵi18nStart': ɵɵi18nStart,
  25372. 'ɵɵi18nEnd': ɵɵi18nEnd,
  25373. 'ɵɵi18nApply': ɵɵi18nApply,
  25374. 'ɵɵi18nPostprocess': ɵɵi18nPostprocess,
  25375. 'ɵɵresolveWindow': ɵɵresolveWindow,
  25376. 'ɵɵresolveDocument': ɵɵresolveDocument,
  25377. 'ɵɵresolveBody': ɵɵresolveBody,
  25378. 'ɵɵsetComponentScope': ɵɵsetComponentScope,
  25379. 'ɵɵsetNgModuleScope': ɵɵsetNgModuleScope,
  25380. 'ɵɵregisterNgModuleType': registerNgModuleType,
  25381. 'ɵɵsanitizeHtml': ɵɵsanitizeHtml,
  25382. 'ɵɵsanitizeStyle': ɵɵsanitizeStyle,
  25383. 'ɵɵsanitizeResourceUrl': ɵɵsanitizeResourceUrl,
  25384. 'ɵɵsanitizeScript': ɵɵsanitizeScript,
  25385. 'ɵɵsanitizeUrl': ɵɵsanitizeUrl,
  25386. 'ɵɵsanitizeUrlOrResourceUrl': ɵɵsanitizeUrlOrResourceUrl,
  25387. 'ɵɵtrustConstantHtml': ɵɵtrustConstantHtml,
  25388. 'ɵɵtrustConstantResourceUrl': ɵɵtrustConstantResourceUrl,
  25389. 'ɵɵvalidateIframeAttribute': ɵɵvalidateIframeAttribute,
  25390. 'forwardRef': forwardRef,
  25391. 'resolveForwardRef': resolveForwardRef,
  25392. }))();
  25393. function patchModuleCompilation() {
  25394. // Does nothing, but exists as a target for patching.
  25395. }
  25396. function isModuleWithProviders$1(value) {
  25397. return value.ngModule !== undefined;
  25398. }
  25399. function isNgModule$1(value) {
  25400. return !!getNgModuleDef(value);
  25401. }
  25402. function isPipe(value) {
  25403. return !!getPipeDef$1(value);
  25404. }
  25405. function isDirective(value) {
  25406. return !!getDirectiveDef(value);
  25407. }
  25408. function isComponent(value) {
  25409. return !!getComponentDef$1(value);
  25410. }
  25411. const moduleQueue = [];
  25412. /**
  25413. * Enqueues moduleDef to be checked later to see if scope can be set on its
  25414. * component declarations.
  25415. */
  25416. function enqueueModuleForDelayedScoping(moduleType, ngModule) {
  25417. moduleQueue.push({ moduleType, ngModule });
  25418. }
  25419. let flushingModuleQueue = false;
  25420. /**
  25421. * Loops over queued module definitions, if a given module definition has all of its
  25422. * declarations resolved, it dequeues that module definition and sets the scope on
  25423. * its declarations.
  25424. */
  25425. function flushModuleScopingQueueAsMuchAsPossible() {
  25426. if (!flushingModuleQueue) {
  25427. flushingModuleQueue = true;
  25428. try {
  25429. for (let i = moduleQueue.length - 1; i >= 0; i--) {
  25430. const { moduleType, ngModule } = moduleQueue[i];
  25431. if (ngModule.declarations && ngModule.declarations.every(isResolvedDeclaration)) {
  25432. // dequeue
  25433. moduleQueue.splice(i, 1);
  25434. setScopeOnDeclaredComponents(moduleType, ngModule);
  25435. }
  25436. }
  25437. }
  25438. finally {
  25439. flushingModuleQueue = false;
  25440. }
  25441. }
  25442. }
  25443. /**
  25444. * Returns truthy if a declaration has resolved. If the declaration happens to be
  25445. * an array of declarations, it will recurse to check each declaration in that array
  25446. * (which may also be arrays).
  25447. */
  25448. function isResolvedDeclaration(declaration) {
  25449. if (Array.isArray(declaration)) {
  25450. return declaration.every(isResolvedDeclaration);
  25451. }
  25452. return !!resolveForwardRef(declaration);
  25453. }
  25454. /**
  25455. * Compiles a module in JIT mode.
  25456. *
  25457. * This function automatically gets called when a class has a `@NgModule` decorator.
  25458. */
  25459. function compileNgModule(moduleType, ngModule = {}) {
  25460. patchModuleCompilation();
  25461. compileNgModuleDefs(moduleType, ngModule);
  25462. if (ngModule.id !== undefined) {
  25463. registerNgModuleType(moduleType, ngModule.id);
  25464. }
  25465. // Because we don't know if all declarations have resolved yet at the moment the
  25466. // NgModule decorator is executing, we're enqueueing the setting of module scope
  25467. // on its declarations to be run at a later time when all declarations for the module,
  25468. // including forward refs, have resolved.
  25469. enqueueModuleForDelayedScoping(moduleType, ngModule);
  25470. }
  25471. /**
  25472. * Compiles and adds the `ɵmod`, `ɵfac` and `ɵinj` properties to the module class.
  25473. *
  25474. * It's possible to compile a module via this API which will allow duplicate declarations in its
  25475. * root.
  25476. */
  25477. function compileNgModuleDefs(moduleType, ngModule, allowDuplicateDeclarationsInRoot = false) {
  25478. ngDevMode && assertDefined(moduleType, 'Required value moduleType');
  25479. ngDevMode && assertDefined(ngModule, 'Required value ngModule');
  25480. const declarations = flatten$1(ngModule.declarations || EMPTY_ARRAY);
  25481. let ngModuleDef = null;
  25482. Object.defineProperty(moduleType, NG_MOD_DEF, {
  25483. configurable: true,
  25484. get: () => {
  25485. if (ngModuleDef === null) {
  25486. if (ngDevMode && ngModule.imports && ngModule.imports.indexOf(moduleType) > -1) {
  25487. // We need to assert this immediately, because allowing it to continue will cause it to
  25488. // go into an infinite loop before we've reached the point where we throw all the errors.
  25489. throw new Error(`'${stringifyForError(moduleType)}' module can't import itself`);
  25490. }
  25491. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
  25492. ngModuleDef = compiler.compileNgModule(angularCoreEnv, `ng:///${moduleType.name}/ɵmod.js`, {
  25493. type: moduleType,
  25494. bootstrap: flatten$1(ngModule.bootstrap || EMPTY_ARRAY).map(resolveForwardRef),
  25495. declarations: declarations.map(resolveForwardRef),
  25496. imports: flatten$1(ngModule.imports || EMPTY_ARRAY)
  25497. .map(resolveForwardRef)
  25498. .map(expandModuleWithProviders),
  25499. exports: flatten$1(ngModule.exports || EMPTY_ARRAY)
  25500. .map(resolveForwardRef)
  25501. .map(expandModuleWithProviders),
  25502. schemas: ngModule.schemas ? flatten$1(ngModule.schemas) : null,
  25503. id: ngModule.id || null,
  25504. });
  25505. // Set `schemas` on ngModuleDef to an empty array in JIT mode to indicate that runtime
  25506. // should verify that there are no unknown elements in a template. In AOT mode, that check
  25507. // happens at compile time and `schemas` information is not present on Component and Module
  25508. // defs after compilation (so the check doesn't happen the second time at runtime).
  25509. if (!ngModuleDef.schemas) {
  25510. ngModuleDef.schemas = [];
  25511. }
  25512. }
  25513. return ngModuleDef;
  25514. }
  25515. });
  25516. let ngFactoryDef = null;
  25517. Object.defineProperty(moduleType, NG_FACTORY_DEF, {
  25518. get: () => {
  25519. if (ngFactoryDef === null) {
  25520. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
  25521. ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${moduleType.name}/ɵfac.js`, {
  25522. name: moduleType.name,
  25523. type: moduleType,
  25524. deps: reflectDependencies(moduleType),
  25525. target: compiler.FactoryTarget.NgModule,
  25526. typeArgumentCount: 0,
  25527. });
  25528. }
  25529. return ngFactoryDef;
  25530. },
  25531. // Make the property configurable in dev mode to allow overriding in tests
  25532. configurable: !!ngDevMode,
  25533. });
  25534. let ngInjectorDef = null;
  25535. Object.defineProperty(moduleType, NG_INJ_DEF, {
  25536. get: () => {
  25537. if (ngInjectorDef === null) {
  25538. ngDevMode && verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot);
  25539. const meta = {
  25540. name: moduleType.name,
  25541. type: moduleType,
  25542. providers: ngModule.providers || EMPTY_ARRAY,
  25543. imports: [
  25544. (ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef),
  25545. (ngModule.exports || EMPTY_ARRAY).map(resolveForwardRef),
  25546. ],
  25547. };
  25548. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
  25549. ngInjectorDef =
  25550. compiler.compileInjector(angularCoreEnv, `ng:///${moduleType.name}/ɵinj.js`, meta);
  25551. }
  25552. return ngInjectorDef;
  25553. },
  25554. // Make the property configurable in dev mode to allow overriding in tests
  25555. configurable: !!ngDevMode,
  25556. });
  25557. }
  25558. function generateStandaloneInDeclarationsError(type, location) {
  25559. const prefix = `Unexpected "${stringifyForError(type)}" found in the "declarations" array of the`;
  25560. const suffix = `"${stringifyForError(type)}" is marked as standalone and can't be declared ` +
  25561. 'in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?';
  25562. return `${prefix} ${location}, ${suffix}`;
  25563. }
  25564. function verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot, importingModule) {
  25565. if (verifiedNgModule.get(moduleType))
  25566. return;
  25567. // skip verifications of standalone components, directives, and pipes
  25568. if (isStandalone(moduleType))
  25569. return;
  25570. verifiedNgModule.set(moduleType, true);
  25571. moduleType = resolveForwardRef(moduleType);
  25572. let ngModuleDef;
  25573. if (importingModule) {
  25574. ngModuleDef = getNgModuleDef(moduleType);
  25575. if (!ngModuleDef) {
  25576. throw new Error(`Unexpected value '${moduleType.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
  25577. }
  25578. }
  25579. else {
  25580. ngModuleDef = getNgModuleDef(moduleType, true);
  25581. }
  25582. const errors = [];
  25583. const declarations = maybeUnwrapFn$1(ngModuleDef.declarations);
  25584. const imports = maybeUnwrapFn$1(ngModuleDef.imports);
  25585. flatten$1(imports).map(unwrapModuleWithProvidersImports).forEach(modOrStandaloneCmpt => {
  25586. verifySemanticsOfNgModuleImport(modOrStandaloneCmpt, moduleType);
  25587. verifySemanticsOfNgModuleDef(modOrStandaloneCmpt, false, moduleType);
  25588. });
  25589. const exports = maybeUnwrapFn$1(ngModuleDef.exports);
  25590. declarations.forEach(verifyDeclarationsHaveDefinitions);
  25591. declarations.forEach(verifyDirectivesHaveSelector);
  25592. declarations.forEach((declarationType) => verifyNotStandalone(declarationType, moduleType));
  25593. const combinedDeclarations = [
  25594. ...declarations.map(resolveForwardRef),
  25595. ...flatten$1(imports.map(computeCombinedExports)).map(resolveForwardRef),
  25596. ];
  25597. exports.forEach(verifyExportsAreDeclaredOrReExported);
  25598. declarations.forEach(decl => verifyDeclarationIsUnique(decl, allowDuplicateDeclarationsInRoot));
  25599. const ngModule = getAnnotation(moduleType, 'NgModule');
  25600. if (ngModule) {
  25601. ngModule.imports &&
  25602. flatten$1(ngModule.imports).map(unwrapModuleWithProvidersImports).forEach(mod => {
  25603. verifySemanticsOfNgModuleImport(mod, moduleType);
  25604. verifySemanticsOfNgModuleDef(mod, false, moduleType);
  25605. });
  25606. ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyCorrectBootstrapType);
  25607. ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyComponentIsPartOfNgModule);
  25608. }
  25609. // Throw Error if any errors were detected.
  25610. if (errors.length) {
  25611. throw new Error(errors.join('\n'));
  25612. }
  25613. ////////////////////////////////////////////////////////////////////////////////////////////////
  25614. function verifyDeclarationsHaveDefinitions(type) {
  25615. type = resolveForwardRef(type);
  25616. const def = getComponentDef$1(type) || getDirectiveDef(type) || getPipeDef$1(type);
  25617. if (!def) {
  25618. errors.push(`Unexpected value '${stringifyForError(type)}' declared by the module '${stringifyForError(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`);
  25619. }
  25620. }
  25621. function verifyDirectivesHaveSelector(type) {
  25622. type = resolveForwardRef(type);
  25623. const def = getDirectiveDef(type);
  25624. if (!getComponentDef$1(type) && def && def.selectors.length == 0) {
  25625. errors.push(`Directive ${stringifyForError(type)} has no selector, please add it!`);
  25626. }
  25627. }
  25628. function verifyNotStandalone(type, moduleType) {
  25629. type = resolveForwardRef(type);
  25630. const def = getComponentDef$1(type) || getDirectiveDef(type) || getPipeDef$1(type);
  25631. if (def?.standalone) {
  25632. const location = `"${stringifyForError(moduleType)}" NgModule`;
  25633. errors.push(generateStandaloneInDeclarationsError(type, location));
  25634. }
  25635. }
  25636. function verifyExportsAreDeclaredOrReExported(type) {
  25637. type = resolveForwardRef(type);
  25638. const kind = getComponentDef$1(type) && 'component' || getDirectiveDef(type) && 'directive' ||
  25639. getPipeDef$1(type) && 'pipe';
  25640. if (kind) {
  25641. // only checked if we are declared as Component, Directive, or Pipe
  25642. // Modules don't need to be declared or imported.
  25643. if (combinedDeclarations.lastIndexOf(type) === -1) {
  25644. // We are exporting something which we don't explicitly declare or import.
  25645. errors.push(`Can't export ${kind} ${stringifyForError(type)} from ${stringifyForError(moduleType)} as it was neither declared nor imported!`);
  25646. }
  25647. }
  25648. }
  25649. function verifyDeclarationIsUnique(type, suppressErrors) {
  25650. type = resolveForwardRef(type);
  25651. const existingModule = ownerNgModule.get(type);
  25652. if (existingModule && existingModule !== moduleType) {
  25653. if (!suppressErrors) {
  25654. const modules = [existingModule, moduleType].map(stringifyForError).sort();
  25655. errors.push(`Type ${stringifyForError(type)} is part of the declarations of 2 modules: ${modules[0]} and ${modules[1]}! ` +
  25656. `Please consider moving ${stringifyForError(type)} to a higher module that imports ${modules[0]} and ${modules[1]}. ` +
  25657. `You can also create a new NgModule that exports and includes ${stringifyForError(type)} then import that NgModule in ${modules[0]} and ${modules[1]}.`);
  25658. }
  25659. }
  25660. else {
  25661. // Mark type as having owner.
  25662. ownerNgModule.set(type, moduleType);
  25663. }
  25664. }
  25665. function verifyComponentIsPartOfNgModule(type) {
  25666. type = resolveForwardRef(type);
  25667. const existingModule = ownerNgModule.get(type);
  25668. if (!existingModule && !isStandalone(type)) {
  25669. errors.push(`Component ${stringifyForError(type)} is not part of any NgModule or the module has not been imported into your module.`);
  25670. }
  25671. }
  25672. function verifyCorrectBootstrapType(type) {
  25673. type = resolveForwardRef(type);
  25674. if (!getComponentDef$1(type)) {
  25675. errors.push(`${stringifyForError(type)} cannot be used as an entry component.`);
  25676. }
  25677. if (isStandalone(type)) {
  25678. // Note: this error should be the same as the
  25679. // `NGMODULE_BOOTSTRAP_IS_STANDALONE` one in AOT compiler.
  25680. errors.push(`The \`${stringifyForError(type)}\` class is a standalone component, which can ` +
  25681. `not be used in the \`@NgModule.bootstrap\` array. Use the \`bootstrapApplication\` ` +
  25682. `function for bootstrap instead.`);
  25683. }
  25684. }
  25685. function verifySemanticsOfNgModuleImport(type, importingModule) {
  25686. type = resolveForwardRef(type);
  25687. const directiveDef = getComponentDef$1(type) || getDirectiveDef(type);
  25688. if (directiveDef !== null && !directiveDef.standalone) {
  25689. throw new Error(`Unexpected directive '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
  25690. }
  25691. const pipeDef = getPipeDef$1(type);
  25692. if (pipeDef !== null && !pipeDef.standalone) {
  25693. throw new Error(`Unexpected pipe '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
  25694. }
  25695. }
  25696. }
  25697. function unwrapModuleWithProvidersImports(typeOrWithProviders) {
  25698. typeOrWithProviders = resolveForwardRef(typeOrWithProviders);
  25699. return typeOrWithProviders.ngModule || typeOrWithProviders;
  25700. }
  25701. function getAnnotation(type, name) {
  25702. let annotation = null;
  25703. collect(type.__annotations__);
  25704. collect(type.decorators);
  25705. return annotation;
  25706. function collect(annotations) {
  25707. if (annotations) {
  25708. annotations.forEach(readAnnotation);
  25709. }
  25710. }
  25711. function readAnnotation(decorator) {
  25712. if (!annotation) {
  25713. const proto = Object.getPrototypeOf(decorator);
  25714. if (proto.ngMetadataName == name) {
  25715. annotation = decorator;
  25716. }
  25717. else if (decorator.type) {
  25718. const proto = Object.getPrototypeOf(decorator.type);
  25719. if (proto.ngMetadataName == name) {
  25720. annotation = decorator.args[0];
  25721. }
  25722. }
  25723. }
  25724. }
  25725. }
  25726. /**
  25727. * Keep track of compiled components. This is needed because in tests we often want to compile the
  25728. * same component with more than one NgModule. This would cause an error unless we reset which
  25729. * NgModule the component belongs to. We keep the list of compiled components here so that the
  25730. * TestBed can reset it later.
  25731. */
  25732. let ownerNgModule = new WeakMap();
  25733. let verifiedNgModule = new WeakMap();
  25734. function resetCompiledComponents() {
  25735. ownerNgModule = new WeakMap();
  25736. verifiedNgModule = new WeakMap();
  25737. moduleQueue.length = 0;
  25738. GENERATED_COMP_IDS.clear();
  25739. }
  25740. /**
  25741. * Computes the combined declarations of explicit declarations, as well as declarations inherited by
  25742. * traversing the exports of imported modules.
  25743. * @param type
  25744. */
  25745. function computeCombinedExports(type) {
  25746. type = resolveForwardRef(type);
  25747. const ngModuleDef = getNgModuleDef(type);
  25748. // a standalone component, directive or pipe
  25749. if (ngModuleDef === null) {
  25750. return [type];
  25751. }
  25752. return flatten$1(maybeUnwrapFn$1(ngModuleDef.exports).map((type) => {
  25753. const ngModuleDef = getNgModuleDef(type);
  25754. if (ngModuleDef) {
  25755. verifySemanticsOfNgModuleDef(type, false);
  25756. return computeCombinedExports(type);
  25757. }
  25758. else {
  25759. return type;
  25760. }
  25761. }));
  25762. }
  25763. /**
  25764. * Some declared components may be compiled asynchronously, and thus may not have their
  25765. * ɵcmp set yet. If this is the case, then a reference to the module is written into
  25766. * the `ngSelectorScope` property of the declared type.
  25767. */
  25768. function setScopeOnDeclaredComponents(moduleType, ngModule) {
  25769. const declarations = flatten$1(ngModule.declarations || EMPTY_ARRAY);
  25770. const transitiveScopes = transitiveScopesFor(moduleType);
  25771. declarations.forEach(declaration => {
  25772. declaration = resolveForwardRef(declaration);
  25773. if (declaration.hasOwnProperty(NG_COMP_DEF)) {
  25774. // A `ɵcmp` field exists - go ahead and patch the component directly.
  25775. const component = declaration;
  25776. const componentDef = getComponentDef$1(component);
  25777. patchComponentDefWithScope(componentDef, transitiveScopes);
  25778. }
  25779. else if (!declaration.hasOwnProperty(NG_DIR_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) {
  25780. // Set `ngSelectorScope` for future reference when the component compilation finishes.
  25781. declaration.ngSelectorScope = moduleType;
  25782. }
  25783. });
  25784. }
  25785. /**
  25786. * Patch the definition of a component with directives and pipes from the compilation scope of
  25787. * a given module.
  25788. */
  25789. function patchComponentDefWithScope(componentDef, transitiveScopes) {
  25790. componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives)
  25791. .map(dir => dir.hasOwnProperty(NG_COMP_DEF) ? getComponentDef$1(dir) : getDirectiveDef(dir))
  25792. .filter(def => !!def);
  25793. componentDef.pipeDefs = () => Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef$1(pipe));
  25794. componentDef.schemas = transitiveScopes.schemas;
  25795. // Since we avoid Components/Directives/Pipes recompiling in case there are no overrides, we
  25796. // may face a problem where previously compiled defs available to a given Component/Directive
  25797. // are cached in TView and may become stale (in case any of these defs gets recompiled). In
  25798. // order to avoid this problem, we force fresh TView to be created.
  25799. componentDef.tView = null;
  25800. }
  25801. /**
  25802. * Compute the pair of transitive scopes (compilation scope and exported scope) for a given type
  25803. * (either a NgModule or a standalone component / directive / pipe).
  25804. */
  25805. function transitiveScopesFor(type) {
  25806. if (isNgModule$1(type)) {
  25807. return transitiveScopesForNgModule(type);
  25808. }
  25809. else if (isStandalone(type)) {
  25810. const directiveDef = getComponentDef$1(type) || getDirectiveDef(type);
  25811. if (directiveDef !== null) {
  25812. return {
  25813. schemas: null,
  25814. compilation: {
  25815. directives: new Set(),
  25816. pipes: new Set(),
  25817. },
  25818. exported: {
  25819. directives: new Set([type]),
  25820. pipes: new Set(),
  25821. },
  25822. };
  25823. }
  25824. const pipeDef = getPipeDef$1(type);
  25825. if (pipeDef !== null) {
  25826. return {
  25827. schemas: null,
  25828. compilation: {
  25829. directives: new Set(),
  25830. pipes: new Set(),
  25831. },
  25832. exported: {
  25833. directives: new Set(),
  25834. pipes: new Set([type]),
  25835. },
  25836. };
  25837. }
  25838. }
  25839. // TODO: change the error message to be more user-facing and take standalone into account
  25840. throw new Error(`${type.name} does not have a module def (ɵmod property)`);
  25841. }
  25842. /**
  25843. * Compute the pair of transitive scopes (compilation scope and exported scope) for a given module.
  25844. *
  25845. * This operation is memoized and the result is cached on the module's definition. This function can
  25846. * be called on modules with components that have not fully compiled yet, but the result should not
  25847. * be used until they have.
  25848. *
  25849. * @param moduleType module that transitive scope should be calculated for.
  25850. */
  25851. function transitiveScopesForNgModule(moduleType) {
  25852. const def = getNgModuleDef(moduleType, true);
  25853. if (def.transitiveCompileScopes !== null) {
  25854. return def.transitiveCompileScopes;
  25855. }
  25856. const scopes = {
  25857. schemas: def.schemas || null,
  25858. compilation: {
  25859. directives: new Set(),
  25860. pipes: new Set(),
  25861. },
  25862. exported: {
  25863. directives: new Set(),
  25864. pipes: new Set(),
  25865. },
  25866. };
  25867. maybeUnwrapFn$1(def.imports).forEach((imported) => {
  25868. // When this module imports another, the imported module's exported directives and pipes are
  25869. // added to the compilation scope of this module.
  25870. const importedScope = transitiveScopesFor(imported);
  25871. importedScope.exported.directives.forEach(entry => scopes.compilation.directives.add(entry));
  25872. importedScope.exported.pipes.forEach(entry => scopes.compilation.pipes.add(entry));
  25873. });
  25874. maybeUnwrapFn$1(def.declarations).forEach(declared => {
  25875. const declaredWithDefs = declared;
  25876. if (getPipeDef$1(declaredWithDefs)) {
  25877. scopes.compilation.pipes.add(declared);
  25878. }
  25879. else {
  25880. // Either declared has a ɵcmp or ɵdir, or it's a component which hasn't
  25881. // had its template compiled yet. In either case, it gets added to the compilation's
  25882. // directives.
  25883. scopes.compilation.directives.add(declared);
  25884. }
  25885. });
  25886. maybeUnwrapFn$1(def.exports).forEach((exported) => {
  25887. const exportedType = exported;
  25888. // Either the type is a module, a pipe, or a component/directive (which may not have a
  25889. // ɵcmp as it might be compiled asynchronously).
  25890. if (isNgModule$1(exportedType)) {
  25891. // When this module exports another, the exported module's exported directives and pipes are
  25892. // added to both the compilation and exported scopes of this module.
  25893. const exportedScope = transitiveScopesFor(exportedType);
  25894. exportedScope.exported.directives.forEach(entry => {
  25895. scopes.compilation.directives.add(entry);
  25896. scopes.exported.directives.add(entry);
  25897. });
  25898. exportedScope.exported.pipes.forEach(entry => {
  25899. scopes.compilation.pipes.add(entry);
  25900. scopes.exported.pipes.add(entry);
  25901. });
  25902. }
  25903. else if (getPipeDef$1(exportedType)) {
  25904. scopes.exported.pipes.add(exportedType);
  25905. }
  25906. else {
  25907. scopes.exported.directives.add(exportedType);
  25908. }
  25909. });
  25910. def.transitiveCompileScopes = scopes;
  25911. return scopes;
  25912. }
  25913. function expandModuleWithProviders(value) {
  25914. if (isModuleWithProviders$1(value)) {
  25915. return value.ngModule;
  25916. }
  25917. return value;
  25918. }
  25919. let _nextReferenceId = 0;
  25920. class MetadataOverrider {
  25921. constructor() {
  25922. this._references = new Map();
  25923. }
  25924. /**
  25925. * Creates a new instance for the given metadata class
  25926. * based on an old instance and overrides.
  25927. */
  25928. overrideMetadata(metadataClass, oldMetadata, override) {
  25929. const props = {};
  25930. if (oldMetadata) {
  25931. _valueProps(oldMetadata).forEach((prop) => props[prop] = oldMetadata[prop]);
  25932. }
  25933. if (override.set) {
  25934. if (override.remove || override.add) {
  25935. throw new Error(`Cannot set and add/remove ${ɵstringify(metadataClass)} at the same time!`);
  25936. }
  25937. setMetadata(props, override.set);
  25938. }
  25939. if (override.remove) {
  25940. removeMetadata(props, override.remove, this._references);
  25941. }
  25942. if (override.add) {
  25943. addMetadata(props, override.add);
  25944. }
  25945. return new metadataClass(props);
  25946. }
  25947. }
  25948. function removeMetadata(metadata, remove, references) {
  25949. const removeObjects = new Set();
  25950. for (const prop in remove) {
  25951. const removeValue = remove[prop];
  25952. if (Array.isArray(removeValue)) {
  25953. removeValue.forEach((value) => {
  25954. removeObjects.add(_propHashKey(prop, value, references));
  25955. });
  25956. }
  25957. else {
  25958. removeObjects.add(_propHashKey(prop, removeValue, references));
  25959. }
  25960. }
  25961. for (const prop in metadata) {
  25962. const propValue = metadata[prop];
  25963. if (Array.isArray(propValue)) {
  25964. metadata[prop] = propValue.filter((value) => !removeObjects.has(_propHashKey(prop, value, references)));
  25965. }
  25966. else {
  25967. if (removeObjects.has(_propHashKey(prop, propValue, references))) {
  25968. metadata[prop] = undefined;
  25969. }
  25970. }
  25971. }
  25972. }
  25973. function addMetadata(metadata, add) {
  25974. for (const prop in add) {
  25975. const addValue = add[prop];
  25976. const propValue = metadata[prop];
  25977. if (propValue != null && Array.isArray(propValue)) {
  25978. metadata[prop] = propValue.concat(addValue);
  25979. }
  25980. else {
  25981. metadata[prop] = addValue;
  25982. }
  25983. }
  25984. }
  25985. function setMetadata(metadata, set) {
  25986. for (const prop in set) {
  25987. metadata[prop] = set[prop];
  25988. }
  25989. }
  25990. function _propHashKey(propName, propValue, references) {
  25991. let nextObjectId = 0;
  25992. const objectIds = new Map();
  25993. const replacer = (key, value) => {
  25994. if (value !== null && typeof value === 'object') {
  25995. if (objectIds.has(value)) {
  25996. return objectIds.get(value);
  25997. }
  25998. // Record an id for this object such that any later references use the object's id instead
  25999. // of the object itself, in order to break cyclic pointers in objects.
  26000. objectIds.set(value, `ɵobj#${nextObjectId++}`);
  26001. // The first time an object is seen the object itself is serialized.
  26002. return value;
  26003. }
  26004. else if (typeof value === 'function') {
  26005. value = _serializeReference(value, references);
  26006. }
  26007. return value;
  26008. };
  26009. return `${propName}:${JSON.stringify(propValue, replacer)}`;
  26010. }
  26011. function _serializeReference(ref, references) {
  26012. let id = references.get(ref);
  26013. if (!id) {
  26014. id = `${ɵstringify(ref)}${_nextReferenceId++}`;
  26015. references.set(ref, id);
  26016. }
  26017. return id;
  26018. }
  26019. function _valueProps(obj) {
  26020. const props = [];
  26021. // regular public props
  26022. Object.keys(obj).forEach((prop) => {
  26023. if (!prop.startsWith('_')) {
  26024. props.push(prop);
  26025. }
  26026. });
  26027. // getters
  26028. let proto = obj;
  26029. while (proto = Object.getPrototypeOf(proto)) {
  26030. Object.keys(proto).forEach((protoProp) => {
  26031. const desc = Object.getOwnPropertyDescriptor(proto, protoProp);
  26032. if (!protoProp.startsWith('_') && desc && 'get' in desc) {
  26033. props.push(protoProp);
  26034. }
  26035. });
  26036. }
  26037. return props;
  26038. }
  26039. const reflection = new ɵReflectionCapabilities();
  26040. /**
  26041. * Allows to override ivy metadata for tests (via the `TestBed`).
  26042. */
  26043. class OverrideResolver {
  26044. constructor() {
  26045. this.overrides = new Map();
  26046. this.resolved = new Map();
  26047. }
  26048. addOverride(type, override) {
  26049. const overrides = this.overrides.get(type) || [];
  26050. overrides.push(override);
  26051. this.overrides.set(type, overrides);
  26052. this.resolved.delete(type);
  26053. }
  26054. setOverrides(overrides) {
  26055. this.overrides.clear();
  26056. overrides.forEach(([type, override]) => {
  26057. this.addOverride(type, override);
  26058. });
  26059. }
  26060. getAnnotation(type) {
  26061. const annotations = reflection.annotations(type);
  26062. // Try to find the nearest known Type annotation and make sure that this annotation is an
  26063. // instance of the type we are looking for, so we can use it for resolution. Note: there might
  26064. // be multiple known annotations found due to the fact that Components can extend Directives (so
  26065. // both Directive and Component annotations would be present), so we always check if the known
  26066. // annotation has the right type.
  26067. for (let i = annotations.length - 1; i >= 0; i--) {
  26068. const annotation = annotations[i];
  26069. const isKnownType = annotation instanceof Directive || annotation instanceof Component ||
  26070. annotation instanceof Pipe || annotation instanceof NgModule;
  26071. if (isKnownType) {
  26072. return annotation instanceof this.type ? annotation : null;
  26073. }
  26074. }
  26075. return null;
  26076. }
  26077. resolve(type) {
  26078. let resolved = this.resolved.get(type) || null;
  26079. if (!resolved) {
  26080. resolved = this.getAnnotation(type);
  26081. if (resolved) {
  26082. const overrides = this.overrides.get(type);
  26083. if (overrides) {
  26084. const overrider = new MetadataOverrider();
  26085. overrides.forEach(override => {
  26086. resolved = overrider.overrideMetadata(this.type, resolved, override);
  26087. });
  26088. }
  26089. }
  26090. this.resolved.set(type, resolved);
  26091. }
  26092. return resolved;
  26093. }
  26094. }
  26095. class DirectiveResolver extends OverrideResolver {
  26096. get type() {
  26097. return Directive;
  26098. }
  26099. }
  26100. class ComponentResolver extends OverrideResolver {
  26101. get type() {
  26102. return Component;
  26103. }
  26104. }
  26105. class PipeResolver extends OverrideResolver {
  26106. get type() {
  26107. return Pipe;
  26108. }
  26109. }
  26110. class NgModuleResolver extends OverrideResolver {
  26111. get type() {
  26112. return NgModule;
  26113. }
  26114. }
  26115. var TestingModuleOverride;
  26116. (function (TestingModuleOverride) {
  26117. TestingModuleOverride[TestingModuleOverride["DECLARATION"] = 0] = "DECLARATION";
  26118. TestingModuleOverride[TestingModuleOverride["OVERRIDE_TEMPLATE"] = 1] = "OVERRIDE_TEMPLATE";
  26119. })(TestingModuleOverride || (TestingModuleOverride = {}));
  26120. function isTestingModuleOverride(value) {
  26121. return value === TestingModuleOverride.DECLARATION ||
  26122. value === TestingModuleOverride.OVERRIDE_TEMPLATE;
  26123. }
  26124. function assertNoStandaloneComponents(types, resolver, location) {
  26125. types.forEach(type => {
  26126. const component = resolver.resolve(type);
  26127. if (component && component.standalone) {
  26128. throw new Error(generateStandaloneInDeclarationsError(type, location));
  26129. }
  26130. });
  26131. }
  26132. class TestBedCompiler {
  26133. constructor(platform, additionalModuleTypes) {
  26134. this.platform = platform;
  26135. this.additionalModuleTypes = additionalModuleTypes;
  26136. this.originalComponentResolutionQueue = null;
  26137. // Testing module configuration
  26138. this.declarations = [];
  26139. this.imports = [];
  26140. this.providers = [];
  26141. this.schemas = [];
  26142. // Queues of components/directives/pipes that should be recompiled.
  26143. this.pendingComponents = new Set();
  26144. this.pendingDirectives = new Set();
  26145. this.pendingPipes = new Set();
  26146. // Keep track of all components and directives, so we can patch Providers onto defs later.
  26147. this.seenComponents = new Set();
  26148. this.seenDirectives = new Set();
  26149. // Keep track of overridden modules, so that we can collect all affected ones in the module tree.
  26150. this.overriddenModules = new Set();
  26151. // Store resolved styles for Components that have template overrides present and `styleUrls`
  26152. // defined at the same time.
  26153. this.existingComponentStyles = new Map();
  26154. this.resolvers = initResolvers();
  26155. this.componentToModuleScope = new Map();
  26156. // Map that keeps initial version of component/directive/pipe defs in case
  26157. // we compile a Type again, thus overriding respective static fields. This is
  26158. // required to make sure we restore defs to their initial states between test runs.
  26159. // Note: one class may have multiple defs (for example: ɵmod and ɵinj in case of an
  26160. // NgModule), store all of them in a map.
  26161. this.initialNgDefs = new Map();
  26162. // Array that keeps cleanup operations for initial versions of component/directive/pipe/module
  26163. // defs in case TestBed makes changes to the originals.
  26164. this.defCleanupOps = [];
  26165. this._injector = null;
  26166. this.compilerProviders = null;
  26167. this.providerOverrides = [];
  26168. this.rootProviderOverrides = [];
  26169. // Overrides for injectables with `{providedIn: SomeModule}` need to be tracked and added to that
  26170. // module's provider list.
  26171. this.providerOverridesByModule = new Map();
  26172. this.providerOverridesByToken = new Map();
  26173. this.scopesWithOverriddenProviders = new Set();
  26174. this.testModuleRef = null;
  26175. class DynamicTestModule {
  26176. }
  26177. this.testModuleType = DynamicTestModule;
  26178. }
  26179. setCompilerProviders(providers) {
  26180. this.compilerProviders = providers;
  26181. this._injector = null;
  26182. }
  26183. configureTestingModule(moduleDef) {
  26184. // Enqueue any compilation tasks for the directly declared component.
  26185. if (moduleDef.declarations !== undefined) {
  26186. // Verify that there are no standalone components
  26187. assertNoStandaloneComponents(moduleDef.declarations, this.resolvers.component, '"TestBed.configureTestingModule" call');
  26188. this.queueTypeArray(moduleDef.declarations, TestingModuleOverride.DECLARATION);
  26189. this.declarations.push(...moduleDef.declarations);
  26190. }
  26191. // Enqueue any compilation tasks for imported modules.
  26192. if (moduleDef.imports !== undefined) {
  26193. this.queueTypesFromModulesArray(moduleDef.imports);
  26194. this.imports.push(...moduleDef.imports);
  26195. }
  26196. if (moduleDef.providers !== undefined) {
  26197. this.providers.push(...moduleDef.providers);
  26198. }
  26199. if (moduleDef.schemas !== undefined) {
  26200. this.schemas.push(...moduleDef.schemas);
  26201. }
  26202. }
  26203. overrideModule(ngModule, override) {
  26204. this.overriddenModules.add(ngModule);
  26205. // Compile the module right away.
  26206. this.resolvers.module.addOverride(ngModule, override);
  26207. const metadata = this.resolvers.module.resolve(ngModule);
  26208. if (metadata === null) {
  26209. throw invalidTypeError(ngModule.name, 'NgModule');
  26210. }
  26211. this.recompileNgModule(ngModule, metadata);
  26212. // At this point, the module has a valid module def (ɵmod), but the override may have introduced
  26213. // new declarations or imported modules. Ingest any possible new types and add them to the
  26214. // current queue.
  26215. this.queueTypesFromModulesArray([ngModule]);
  26216. }
  26217. overrideComponent(component, override) {
  26218. this.verifyNoStandaloneFlagOverrides(component, override);
  26219. this.resolvers.component.addOverride(component, override);
  26220. this.pendingComponents.add(component);
  26221. }
  26222. overrideDirective(directive, override) {
  26223. this.verifyNoStandaloneFlagOverrides(directive, override);
  26224. this.resolvers.directive.addOverride(directive, override);
  26225. this.pendingDirectives.add(directive);
  26226. }
  26227. overridePipe(pipe, override) {
  26228. this.verifyNoStandaloneFlagOverrides(pipe, override);
  26229. this.resolvers.pipe.addOverride(pipe, override);
  26230. this.pendingPipes.add(pipe);
  26231. }
  26232. verifyNoStandaloneFlagOverrides(type, override) {
  26233. if (override.add?.hasOwnProperty('standalone') || override.set?.hasOwnProperty('standalone') ||
  26234. override.remove?.hasOwnProperty('standalone')) {
  26235. throw new Error(`An override for the ${type.name} class has the \`standalone\` flag. ` +
  26236. `Changing the \`standalone\` flag via TestBed overrides is not supported.`);
  26237. }
  26238. }
  26239. overrideProvider(token, provider) {
  26240. let providerDef;
  26241. if (provider.useFactory !== undefined) {
  26242. providerDef = {
  26243. provide: token,
  26244. useFactory: provider.useFactory,
  26245. deps: provider.deps || [],
  26246. multi: provider.multi
  26247. };
  26248. }
  26249. else if (provider.useValue !== undefined) {
  26250. providerDef = { provide: token, useValue: provider.useValue, multi: provider.multi };
  26251. }
  26252. else {
  26253. providerDef = { provide: token };
  26254. }
  26255. const injectableDef = typeof token !== 'string' ? ɵgetInjectableDef(token) : null;
  26256. const providedIn = injectableDef === null ? null : resolveForwardRef$1(injectableDef.providedIn);
  26257. const overridesBucket = providedIn === 'root' ? this.rootProviderOverrides : this.providerOverrides;
  26258. overridesBucket.push(providerDef);
  26259. // Keep overrides grouped by token as well for fast lookups using token
  26260. this.providerOverridesByToken.set(token, providerDef);
  26261. if (injectableDef !== null && providedIn !== null && typeof providedIn !== 'string') {
  26262. const existingOverrides = this.providerOverridesByModule.get(providedIn);
  26263. if (existingOverrides !== undefined) {
  26264. existingOverrides.push(providerDef);
  26265. }
  26266. else {
  26267. this.providerOverridesByModule.set(providedIn, [providerDef]);
  26268. }
  26269. }
  26270. }
  26271. overrideTemplateUsingTestingModule(type, template) {
  26272. const def = type[ɵNG_COMP_DEF];
  26273. const hasStyleUrls = () => {
  26274. const metadata = this.resolvers.component.resolve(type);
  26275. return !!metadata.styleUrls && metadata.styleUrls.length > 0;
  26276. };
  26277. const overrideStyleUrls = !!def && !isComponentDefPendingResolution(type) && hasStyleUrls();
  26278. // In Ivy, compiling a component does not require knowing the module providing the
  26279. // component's scope, so overrideTemplateUsingTestingModule can be implemented purely via
  26280. // overrideComponent. Important: overriding template requires full Component re-compilation,
  26281. // which may fail in case styleUrls are also present (thus Component is considered as required
  26282. // resolution). In order to avoid this, we preemptively set styleUrls to an empty array,
  26283. // preserve current styles available on Component def and restore styles back once compilation
  26284. // is complete.
  26285. const override = overrideStyleUrls ? { template, styles: [], styleUrls: [] } : { template };
  26286. this.overrideComponent(type, { set: override });
  26287. if (overrideStyleUrls && def.styles && def.styles.length > 0) {
  26288. this.existingComponentStyles.set(type, def.styles);
  26289. }
  26290. // Set the component's scope to be the testing module.
  26291. this.componentToModuleScope.set(type, TestingModuleOverride.OVERRIDE_TEMPLATE);
  26292. }
  26293. async compileComponents() {
  26294. this.clearComponentResolutionQueue();
  26295. // Run compilers for all queued types.
  26296. let needsAsyncResources = this.compileTypesSync();
  26297. // compileComponents() should not be async unless it needs to be.
  26298. if (needsAsyncResources) {
  26299. let resourceLoader;
  26300. let resolver = (url) => {
  26301. if (!resourceLoader) {
  26302. resourceLoader = this.injector.get(ResourceLoader);
  26303. }
  26304. return Promise.resolve(resourceLoader.get(url));
  26305. };
  26306. await resolveComponentResources(resolver);
  26307. }
  26308. }
  26309. finalize() {
  26310. // One last compile
  26311. this.compileTypesSync();
  26312. // Create the testing module itself.
  26313. this.compileTestModule();
  26314. this.applyTransitiveScopes();
  26315. this.applyProviderOverrides();
  26316. // Patch previously stored `styles` Component values (taken from ɵcmp), in case these
  26317. // Components have `styleUrls` fields defined and template override was requested.
  26318. this.patchComponentsWithExistingStyles();
  26319. // Clear the componentToModuleScope map, so that future compilations don't reset the scope of
  26320. // every component.
  26321. this.componentToModuleScope.clear();
  26322. const parentInjector = this.platform.injector;
  26323. this.testModuleRef = new ɵRender3NgModuleRef(this.testModuleType, parentInjector, []);
  26324. // ApplicationInitStatus.runInitializers() is marked @internal to core.
  26325. // Cast it to any before accessing it.
  26326. this.testModuleRef.injector.get(ApplicationInitStatus).runInitializers();
  26327. // Set locale ID after running app initializers, since locale information might be updated while
  26328. // running initializers. This is also consistent with the execution order while bootstrapping an
  26329. // app (see `packages/core/src/application_ref.ts` file).
  26330. const localeId = this.testModuleRef.injector.get(LOCALE_ID$1, ɵDEFAULT_LOCALE_ID);
  26331. ɵsetLocaleId(localeId);
  26332. return this.testModuleRef;
  26333. }
  26334. /**
  26335. * @internal
  26336. */
  26337. _compileNgModuleSync(moduleType) {
  26338. this.queueTypesFromModulesArray([moduleType]);
  26339. this.compileTypesSync();
  26340. this.applyProviderOverrides();
  26341. this.applyProviderOverridesInScope(moduleType);
  26342. this.applyTransitiveScopes();
  26343. }
  26344. /**
  26345. * @internal
  26346. */
  26347. async _compileNgModuleAsync(moduleType) {
  26348. this.queueTypesFromModulesArray([moduleType]);
  26349. await this.compileComponents();
  26350. this.applyProviderOverrides();
  26351. this.applyProviderOverridesInScope(moduleType);
  26352. this.applyTransitiveScopes();
  26353. }
  26354. /**
  26355. * @internal
  26356. */
  26357. _getModuleResolver() {
  26358. return this.resolvers.module;
  26359. }
  26360. /**
  26361. * @internal
  26362. */
  26363. _getComponentFactories(moduleType) {
  26364. return maybeUnwrapFn(moduleType.ɵmod.declarations).reduce((factories, declaration) => {
  26365. const componentDef = declaration.ɵcmp;
  26366. componentDef && factories.push(new ɵRender3ComponentFactory(componentDef, this.testModuleRef));
  26367. return factories;
  26368. }, []);
  26369. }
  26370. compileTypesSync() {
  26371. // Compile all queued components, directives, pipes.
  26372. let needsAsyncResources = false;
  26373. this.pendingComponents.forEach(declaration => {
  26374. needsAsyncResources = needsAsyncResources || isComponentDefPendingResolution(declaration);
  26375. const metadata = this.resolvers.component.resolve(declaration);
  26376. if (metadata === null) {
  26377. throw invalidTypeError(declaration.name, 'Component');
  26378. }
  26379. this.maybeStoreNgDef(ɵNG_COMP_DEF, declaration);
  26380. ɵcompileComponent(declaration, metadata);
  26381. });
  26382. this.pendingComponents.clear();
  26383. this.pendingDirectives.forEach(declaration => {
  26384. const metadata = this.resolvers.directive.resolve(declaration);
  26385. if (metadata === null) {
  26386. throw invalidTypeError(declaration.name, 'Directive');
  26387. }
  26388. this.maybeStoreNgDef(ɵNG_DIR_DEF, declaration);
  26389. ɵcompileDirective(declaration, metadata);
  26390. });
  26391. this.pendingDirectives.clear();
  26392. this.pendingPipes.forEach(declaration => {
  26393. const metadata = this.resolvers.pipe.resolve(declaration);
  26394. if (metadata === null) {
  26395. throw invalidTypeError(declaration.name, 'Pipe');
  26396. }
  26397. this.maybeStoreNgDef(ɵNG_PIPE_DEF, declaration);
  26398. ɵcompilePipe(declaration, metadata);
  26399. });
  26400. this.pendingPipes.clear();
  26401. return needsAsyncResources;
  26402. }
  26403. applyTransitiveScopes() {
  26404. if (this.overriddenModules.size > 0) {
  26405. // Module overrides (via `TestBed.overrideModule`) might affect scopes that were previously
  26406. // calculated and stored in `transitiveCompileScopes`. If module overrides are present,
  26407. // collect all affected modules and reset scopes to force their re-calculation.
  26408. const testingModuleDef = this.testModuleType[ɵNG_MOD_DEF];
  26409. const affectedModules = this.collectModulesAffectedByOverrides(testingModuleDef.imports);
  26410. if (affectedModules.size > 0) {
  26411. affectedModules.forEach(moduleType => {
  26412. this.storeFieldOfDefOnType(moduleType, ɵNG_MOD_DEF, 'transitiveCompileScopes');
  26413. moduleType[ɵNG_MOD_DEF].transitiveCompileScopes = null;
  26414. });
  26415. }
  26416. }
  26417. const moduleToScope = new Map();
  26418. const getScopeOfModule = (moduleType) => {
  26419. if (!moduleToScope.has(moduleType)) {
  26420. const isTestingModule = isTestingModuleOverride(moduleType);
  26421. const realType = isTestingModule ? this.testModuleType : moduleType;
  26422. moduleToScope.set(moduleType, ɵtransitiveScopesFor(realType));
  26423. }
  26424. return moduleToScope.get(moduleType);
  26425. };
  26426. this.componentToModuleScope.forEach((moduleType, componentType) => {
  26427. const moduleScope = getScopeOfModule(moduleType);
  26428. this.storeFieldOfDefOnType(componentType, ɵNG_COMP_DEF, 'directiveDefs');
  26429. this.storeFieldOfDefOnType(componentType, ɵNG_COMP_DEF, 'pipeDefs');
  26430. // `tView` that is stored on component def contains information about directives and pipes
  26431. // that are in the scope of this component. Patching component scope will cause `tView` to be
  26432. // changed. Store original `tView` before patching scope, so the `tView` (including scope
  26433. // information) is restored back to its previous/original state before running next test.
  26434. this.storeFieldOfDefOnType(componentType, ɵNG_COMP_DEF, 'tView');
  26435. ɵpatchComponentDefWithScope(componentType.ɵcmp, moduleScope);
  26436. });
  26437. this.componentToModuleScope.clear();
  26438. }
  26439. applyProviderOverrides() {
  26440. const maybeApplyOverrides = (field) => (type) => {
  26441. const resolver = field === ɵNG_COMP_DEF ? this.resolvers.component : this.resolvers.directive;
  26442. const metadata = resolver.resolve(type);
  26443. if (this.hasProviderOverrides(metadata.providers)) {
  26444. this.patchDefWithProviderOverrides(type, field);
  26445. }
  26446. };
  26447. this.seenComponents.forEach(maybeApplyOverrides(ɵNG_COMP_DEF));
  26448. this.seenDirectives.forEach(maybeApplyOverrides(ɵNG_DIR_DEF));
  26449. this.seenComponents.clear();
  26450. this.seenDirectives.clear();
  26451. }
  26452. /**
  26453. * Applies provider overrides to a given type (either an NgModule or a standalone component)
  26454. * and all imported NgModules and standalone components recursively.
  26455. */
  26456. applyProviderOverridesInScope(type) {
  26457. const hasScope = isStandaloneComponent(type) || isNgModule(type);
  26458. // The function can be re-entered recursively while inspecting dependencies
  26459. // of an NgModule or a standalone component. Exit early if we come across a
  26460. // type that can not have a scope (directive or pipe) or the type is already
  26461. // processed earlier.
  26462. if (!hasScope || this.scopesWithOverriddenProviders.has(type)) {
  26463. return;
  26464. }
  26465. this.scopesWithOverriddenProviders.add(type);
  26466. // NOTE: the line below triggers JIT compilation of the module injector,
  26467. // which also invokes verification of the NgModule semantics, which produces
  26468. // detailed error messages. The fact that the code relies on this line being
  26469. // present here is suspicious and should be refactored in a way that the line
  26470. // below can be moved (for ex. after an early exit check below).
  26471. const injectorDef = type[ɵNG_INJ_DEF];
  26472. // No provider overrides, exit early.
  26473. if (this.providerOverridesByToken.size === 0)
  26474. return;
  26475. if (isStandaloneComponent(type)) {
  26476. // Visit all component dependencies and override providers there.
  26477. const def = getComponentDef(type);
  26478. const dependencies = maybeUnwrapFn(def.dependencies ?? []);
  26479. for (const dependency of dependencies) {
  26480. this.applyProviderOverridesInScope(dependency);
  26481. }
  26482. }
  26483. else {
  26484. const providers = [
  26485. ...injectorDef.providers,
  26486. ...(this.providerOverridesByModule.get(type) || [])
  26487. ];
  26488. if (this.hasProviderOverrides(providers)) {
  26489. this.maybeStoreNgDef(ɵNG_INJ_DEF, type);
  26490. this.storeFieldOfDefOnType(type, ɵNG_INJ_DEF, 'providers');
  26491. injectorDef.providers = this.getOverriddenProviders(providers);
  26492. }
  26493. // Apply provider overrides to imported modules recursively
  26494. const moduleDef = type[ɵNG_MOD_DEF];
  26495. const imports = maybeUnwrapFn(moduleDef.imports);
  26496. for (const importedModule of imports) {
  26497. this.applyProviderOverridesInScope(importedModule);
  26498. }
  26499. // Also override the providers on any ModuleWithProviders imports since those don't appear in
  26500. // the moduleDef.
  26501. for (const importedModule of flatten(injectorDef.imports)) {
  26502. if (isModuleWithProviders(importedModule)) {
  26503. this.defCleanupOps.push({
  26504. object: importedModule,
  26505. fieldName: 'providers',
  26506. originalValue: importedModule.providers
  26507. });
  26508. importedModule.providers = this.getOverriddenProviders(importedModule.providers);
  26509. }
  26510. }
  26511. }
  26512. }
  26513. patchComponentsWithExistingStyles() {
  26514. this.existingComponentStyles.forEach((styles, type) => type[ɵNG_COMP_DEF].styles = styles);
  26515. this.existingComponentStyles.clear();
  26516. }
  26517. queueTypeArray(arr, moduleType) {
  26518. for (const value of arr) {
  26519. if (Array.isArray(value)) {
  26520. this.queueTypeArray(value, moduleType);
  26521. }
  26522. else {
  26523. this.queueType(value, moduleType);
  26524. }
  26525. }
  26526. }
  26527. recompileNgModule(ngModule, metadata) {
  26528. // Cache the initial ngModuleDef as it will be overwritten.
  26529. this.maybeStoreNgDef(ɵNG_MOD_DEF, ngModule);
  26530. this.maybeStoreNgDef(ɵNG_INJ_DEF, ngModule);
  26531. ɵcompileNgModuleDefs(ngModule, metadata);
  26532. }
  26533. queueType(type, moduleType) {
  26534. const component = this.resolvers.component.resolve(type);
  26535. if (component) {
  26536. // Check whether a give Type has respective NG def (ɵcmp) and compile if def is
  26537. // missing. That might happen in case a class without any Angular decorators extends another
  26538. // class where Component/Directive/Pipe decorator is defined.
  26539. if (isComponentDefPendingResolution(type) || !type.hasOwnProperty(ɵNG_COMP_DEF)) {
  26540. this.pendingComponents.add(type);
  26541. }
  26542. this.seenComponents.add(type);
  26543. // Keep track of the module which declares this component, so later the component's scope
  26544. // can be set correctly. If the component has already been recorded here, then one of several
  26545. // cases is true:
  26546. // * the module containing the component was imported multiple times (common).
  26547. // * the component is declared in multiple modules (which is an error).
  26548. // * the component was in 'declarations' of the testing module, and also in an imported module
  26549. // in which case the module scope will be TestingModuleOverride.DECLARATION.
  26550. // * overrideTemplateUsingTestingModule was called for the component in which case the module
  26551. // scope will be TestingModuleOverride.OVERRIDE_TEMPLATE.
  26552. //
  26553. // If the component was previously in the testing module's 'declarations' (meaning the
  26554. // current value is TestingModuleOverride.DECLARATION), then `moduleType` is the component's
  26555. // real module, which was imported. This pattern is understood to mean that the component
  26556. // should use its original scope, but that the testing module should also contain the
  26557. // component in its scope.
  26558. //
  26559. // Note: standalone components have no associated NgModule, so the `moduleType` can be `null`.
  26560. if (moduleType !== null &&
  26561. (!this.componentToModuleScope.has(type) ||
  26562. this.componentToModuleScope.get(type) === TestingModuleOverride.DECLARATION)) {
  26563. this.componentToModuleScope.set(type, moduleType);
  26564. }
  26565. return;
  26566. }
  26567. const directive = this.resolvers.directive.resolve(type);
  26568. if (directive) {
  26569. if (!type.hasOwnProperty(ɵNG_DIR_DEF)) {
  26570. this.pendingDirectives.add(type);
  26571. }
  26572. this.seenDirectives.add(type);
  26573. return;
  26574. }
  26575. const pipe = this.resolvers.pipe.resolve(type);
  26576. if (pipe && !type.hasOwnProperty(ɵNG_PIPE_DEF)) {
  26577. this.pendingPipes.add(type);
  26578. return;
  26579. }
  26580. }
  26581. queueTypesFromModulesArray(arr) {
  26582. // Because we may encounter the same NgModule or a standalone Component while processing
  26583. // the dependencies of an NgModule or a standalone Component, we cache them in this set so we
  26584. // can skip ones that have already been seen encountered. In some test setups, this caching
  26585. // resulted in 10X runtime improvement.
  26586. const processedDefs = new Set();
  26587. const queueTypesFromModulesArrayRecur = (arr) => {
  26588. for (const value of arr) {
  26589. if (Array.isArray(value)) {
  26590. queueTypesFromModulesArrayRecur(value);
  26591. }
  26592. else if (hasNgModuleDef(value)) {
  26593. const def = value.ɵmod;
  26594. if (processedDefs.has(def)) {
  26595. continue;
  26596. }
  26597. processedDefs.add(def);
  26598. // Look through declarations, imports, and exports, and queue
  26599. // everything found there.
  26600. this.queueTypeArray(maybeUnwrapFn(def.declarations), value);
  26601. queueTypesFromModulesArrayRecur(maybeUnwrapFn(def.imports));
  26602. queueTypesFromModulesArrayRecur(maybeUnwrapFn(def.exports));
  26603. }
  26604. else if (isModuleWithProviders(value)) {
  26605. queueTypesFromModulesArrayRecur([value.ngModule]);
  26606. }
  26607. else if (isStandaloneComponent(value)) {
  26608. this.queueType(value, null);
  26609. const def = getComponentDef(value);
  26610. if (processedDefs.has(def)) {
  26611. continue;
  26612. }
  26613. processedDefs.add(def);
  26614. const dependencies = maybeUnwrapFn(def.dependencies ?? []);
  26615. dependencies.forEach((dependency) => {
  26616. // Note: in AOT, the `dependencies` might also contain regular
  26617. // (NgModule-based) Component, Directive and Pipes, so we handle
  26618. // them separately and proceed with recursive process for standalone
  26619. // Components and NgModules only.
  26620. if (isStandaloneComponent(dependency) || hasNgModuleDef(dependency)) {
  26621. queueTypesFromModulesArrayRecur([dependency]);
  26622. }
  26623. else {
  26624. this.queueType(dependency, null);
  26625. }
  26626. });
  26627. }
  26628. }
  26629. };
  26630. queueTypesFromModulesArrayRecur(arr);
  26631. }
  26632. // When module overrides (via `TestBed.overrideModule`) are present, it might affect all modules
  26633. // that import (even transitively) an overridden one. For all affected modules we need to
  26634. // recalculate their scopes for a given test run and restore original scopes at the end. The goal
  26635. // of this function is to collect all affected modules in a set for further processing. Example:
  26636. // if we have the following module hierarchy: A -> B -> C (where `->` means `imports`) and module
  26637. // `C` is overridden, we consider `A` and `B` as affected, since their scopes might become
  26638. // invalidated with the override.
  26639. collectModulesAffectedByOverrides(arr) {
  26640. const seenModules = new Set();
  26641. const affectedModules = new Set();
  26642. const calcAffectedModulesRecur = (arr, path) => {
  26643. for (const value of arr) {
  26644. if (Array.isArray(value)) {
  26645. // If the value is an array, just flatten it (by invoking this function recursively),
  26646. // keeping "path" the same.
  26647. calcAffectedModulesRecur(value, path);
  26648. }
  26649. else if (hasNgModuleDef(value)) {
  26650. if (seenModules.has(value)) {
  26651. // If we've seen this module before and it's included into "affected modules" list, mark
  26652. // the whole path that leads to that module as affected, but do not descend into its
  26653. // imports, since we already examined them before.
  26654. if (affectedModules.has(value)) {
  26655. path.forEach(item => affectedModules.add(item));
  26656. }
  26657. continue;
  26658. }
  26659. seenModules.add(value);
  26660. if (this.overriddenModules.has(value)) {
  26661. path.forEach(item => affectedModules.add(item));
  26662. }
  26663. // Examine module imports recursively to look for overridden modules.
  26664. const moduleDef = value[ɵNG_MOD_DEF];
  26665. calcAffectedModulesRecur(maybeUnwrapFn(moduleDef.imports), path.concat(value));
  26666. }
  26667. }
  26668. };
  26669. calcAffectedModulesRecur(arr, []);
  26670. return affectedModules;
  26671. }
  26672. /**
  26673. * Preserve an original def (such as ɵmod, ɵinj, etc) before applying an override.
  26674. * Note: one class may have multiple defs (for example: ɵmod and ɵinj in case of
  26675. * an NgModule). If there is a def in a set already, don't override it, since
  26676. * an original one should be restored at the end of a test.
  26677. */
  26678. maybeStoreNgDef(prop, type) {
  26679. if (!this.initialNgDefs.has(type)) {
  26680. this.initialNgDefs.set(type, new Map());
  26681. }
  26682. const currentDefs = this.initialNgDefs.get(type);
  26683. if (!currentDefs.has(prop)) {
  26684. const currentDef = Object.getOwnPropertyDescriptor(type, prop);
  26685. currentDefs.set(prop, currentDef);
  26686. }
  26687. }
  26688. storeFieldOfDefOnType(type, defField, fieldName) {
  26689. const def = type[defField];
  26690. const originalValue = def[fieldName];
  26691. this.defCleanupOps.push({ object: def, fieldName, originalValue });
  26692. }
  26693. /**
  26694. * Clears current components resolution queue, but stores the state of the queue, so we can
  26695. * restore it later. Clearing the queue is required before we try to compile components (via
  26696. * `TestBed.compileComponents`), so that component defs are in sync with the resolution queue.
  26697. */
  26698. clearComponentResolutionQueue() {
  26699. if (this.originalComponentResolutionQueue === null) {
  26700. this.originalComponentResolutionQueue = new Map();
  26701. }
  26702. clearResolutionOfComponentResourcesQueue().forEach((value, key) => this.originalComponentResolutionQueue.set(key, value));
  26703. }
  26704. /*
  26705. * Restores component resolution queue to the previously saved state. This operation is performed
  26706. * as a part of restoring the state after completion of the current set of tests (that might
  26707. * potentially mutate the state).
  26708. */
  26709. restoreComponentResolutionQueue() {
  26710. if (this.originalComponentResolutionQueue !== null) {
  26711. restoreComponentResolutionQueue(this.originalComponentResolutionQueue);
  26712. this.originalComponentResolutionQueue = null;
  26713. }
  26714. }
  26715. restoreOriginalState() {
  26716. // Process cleanup ops in reverse order so the field's original value is restored correctly (in
  26717. // case there were multiple overrides for the same field).
  26718. forEachRight(this.defCleanupOps, (op) => {
  26719. op.object[op.fieldName] = op.originalValue;
  26720. });
  26721. // Restore initial component/directive/pipe defs
  26722. this.initialNgDefs.forEach((defs, type) => {
  26723. defs.forEach((descriptor, prop) => {
  26724. if (!descriptor) {
  26725. // Delete operations are generally undesirable since they have performance
  26726. // implications on objects they were applied to. In this particular case, situations
  26727. // where this code is invoked should be quite rare to cause any noticeable impact,
  26728. // since it's applied only to some test cases (for example when class with no
  26729. // annotations extends some @Component) when we need to clear 'ɵcmp' field on a given
  26730. // class to restore its original state (before applying overrides and running tests).
  26731. delete type[prop];
  26732. }
  26733. else {
  26734. Object.defineProperty(type, prop, descriptor);
  26735. }
  26736. });
  26737. });
  26738. this.initialNgDefs.clear();
  26739. this.scopesWithOverriddenProviders.clear();
  26740. this.restoreComponentResolutionQueue();
  26741. // Restore the locale ID to the default value, this shouldn't be necessary but we never know
  26742. ɵsetLocaleId(ɵDEFAULT_LOCALE_ID);
  26743. }
  26744. compileTestModule() {
  26745. class RootScopeModule {
  26746. }
  26747. ɵcompileNgModuleDefs(RootScopeModule, {
  26748. providers: [...this.rootProviderOverrides],
  26749. });
  26750. const providers = [
  26751. provideZoneChangeDetection(),
  26752. { provide: Compiler, useFactory: () => new R3TestCompiler(this) },
  26753. ...this.providers,
  26754. ...this.providerOverrides,
  26755. ];
  26756. const imports = [RootScopeModule, this.additionalModuleTypes, this.imports || []];
  26757. // clang-format off
  26758. ɵcompileNgModuleDefs(this.testModuleType, {
  26759. declarations: this.declarations,
  26760. imports,
  26761. schemas: this.schemas,
  26762. providers,
  26763. }, /* allowDuplicateDeclarationsInRoot */ true);
  26764. // clang-format on
  26765. this.applyProviderOverridesInScope(this.testModuleType);
  26766. }
  26767. get injector() {
  26768. if (this._injector !== null) {
  26769. return this._injector;
  26770. }
  26771. const providers = [];
  26772. const compilerOptions = this.platform.injector.get(COMPILER_OPTIONS);
  26773. compilerOptions.forEach(opts => {
  26774. if (opts.providers) {
  26775. providers.push(opts.providers);
  26776. }
  26777. });
  26778. if (this.compilerProviders !== null) {
  26779. providers.push(...this.compilerProviders);
  26780. }
  26781. this._injector = Injector$1.create({ providers, parent: this.platform.injector });
  26782. return this._injector;
  26783. }
  26784. // get overrides for a specific provider (if any)
  26785. getSingleProviderOverrides(provider) {
  26786. const token = getProviderToken(provider);
  26787. return this.providerOverridesByToken.get(token) || null;
  26788. }
  26789. getProviderOverrides(providers) {
  26790. if (!providers || !providers.length || this.providerOverridesByToken.size === 0)
  26791. return [];
  26792. // There are two flattening operations here. The inner flattenProviders() operates on the
  26793. // metadata's providers and applies a mapping function which retrieves overrides for each
  26794. // incoming provider. The outer flatten() then flattens the produced overrides array. If this is
  26795. // not done, the array can contain other empty arrays (e.g. `[[], []]`) which leak into the
  26796. // providers array and contaminate any error messages that might be generated.
  26797. return flatten(flattenProviders(providers, (provider) => this.getSingleProviderOverrides(provider) || []));
  26798. }
  26799. getOverriddenProviders(providers) {
  26800. if (!providers || !providers.length || this.providerOverridesByToken.size === 0)
  26801. return [];
  26802. const flattenedProviders = flattenProviders(providers);
  26803. const overrides = this.getProviderOverrides(flattenedProviders);
  26804. const overriddenProviders = [...flattenedProviders, ...overrides];
  26805. const final = [];
  26806. const seenOverriddenProviders = new Set();
  26807. // We iterate through the list of providers in reverse order to make sure provider overrides
  26808. // take precedence over the values defined in provider list. We also filter out all providers
  26809. // that have overrides, keeping overridden values only. This is needed, since presence of a
  26810. // provider with `ngOnDestroy` hook will cause this hook to be registered and invoked later.
  26811. forEachRight(overriddenProviders, (provider) => {
  26812. const token = getProviderToken(provider);
  26813. if (this.providerOverridesByToken.has(token)) {
  26814. if (!seenOverriddenProviders.has(token)) {
  26815. seenOverriddenProviders.add(token);
  26816. // Treat all overridden providers as `{multi: false}` (even if it's a multi-provider) to
  26817. // make sure that provided override takes highest precedence and is not combined with
  26818. // other instances of the same multi provider.
  26819. final.unshift({ ...provider, multi: false });
  26820. }
  26821. }
  26822. else {
  26823. final.unshift(provider);
  26824. }
  26825. });
  26826. return final;
  26827. }
  26828. hasProviderOverrides(providers) {
  26829. return this.getProviderOverrides(providers).length > 0;
  26830. }
  26831. patchDefWithProviderOverrides(declaration, field) {
  26832. const def = declaration[field];
  26833. if (def && def.providersResolver) {
  26834. this.maybeStoreNgDef(field, declaration);
  26835. const resolver = def.providersResolver;
  26836. const processProvidersFn = (providers) => this.getOverriddenProviders(providers);
  26837. this.storeFieldOfDefOnType(declaration, field, 'providersResolver');
  26838. def.providersResolver = (ngDef) => resolver(ngDef, processProvidersFn);
  26839. }
  26840. }
  26841. }
  26842. function initResolvers() {
  26843. return {
  26844. module: new NgModuleResolver(),
  26845. component: new ComponentResolver(),
  26846. directive: new DirectiveResolver(),
  26847. pipe: new PipeResolver()
  26848. };
  26849. }
  26850. function isStandaloneComponent(value) {
  26851. const def = getComponentDef(value);
  26852. return !!def?.standalone;
  26853. }
  26854. function getComponentDef(value) {
  26855. return value.ɵcmp ?? null;
  26856. }
  26857. function hasNgModuleDef(value) {
  26858. return value.hasOwnProperty('ɵmod');
  26859. }
  26860. function isNgModule(value) {
  26861. return hasNgModuleDef(value);
  26862. }
  26863. function maybeUnwrapFn(maybeFn) {
  26864. return maybeFn instanceof Function ? maybeFn() : maybeFn;
  26865. }
  26866. function flatten(values) {
  26867. const out = [];
  26868. values.forEach(value => {
  26869. if (Array.isArray(value)) {
  26870. out.push(...flatten(value));
  26871. }
  26872. else {
  26873. out.push(value);
  26874. }
  26875. });
  26876. return out;
  26877. }
  26878. function identityFn(value) {
  26879. return value;
  26880. }
  26881. function flattenProviders(providers, mapFn = identityFn) {
  26882. const out = [];
  26883. for (let provider of providers) {
  26884. if (ɵisEnvironmentProviders(provider)) {
  26885. provider = provider.ɵproviders;
  26886. }
  26887. if (Array.isArray(provider)) {
  26888. out.push(...flattenProviders(provider, mapFn));
  26889. }
  26890. else {
  26891. out.push(mapFn(provider));
  26892. }
  26893. }
  26894. return out;
  26895. }
  26896. function getProviderField(provider, field) {
  26897. return provider && typeof provider === 'object' && provider[field];
  26898. }
  26899. function getProviderToken(provider) {
  26900. return getProviderField(provider, 'provide') || provider;
  26901. }
  26902. function isModuleWithProviders(value) {
  26903. return value.hasOwnProperty('ngModule');
  26904. }
  26905. function forEachRight(values, fn) {
  26906. for (let idx = values.length - 1; idx >= 0; idx--) {
  26907. fn(values[idx], idx);
  26908. }
  26909. }
  26910. function invalidTypeError(name, expectedType) {
  26911. return new Error(`${name} class doesn't have @${expectedType} decorator or is missing metadata.`);
  26912. }
  26913. class R3TestCompiler {
  26914. constructor(testBed) {
  26915. this.testBed = testBed;
  26916. }
  26917. compileModuleSync(moduleType) {
  26918. this.testBed._compileNgModuleSync(moduleType);
  26919. return new ɵNgModuleFactory(moduleType);
  26920. }
  26921. async compileModuleAsync(moduleType) {
  26922. await this.testBed._compileNgModuleAsync(moduleType);
  26923. return new ɵNgModuleFactory(moduleType);
  26924. }
  26925. compileModuleAndAllComponentsSync(moduleType) {
  26926. const ngModuleFactory = this.compileModuleSync(moduleType);
  26927. const componentFactories = this.testBed._getComponentFactories(moduleType);
  26928. return new ModuleWithComponentFactories(ngModuleFactory, componentFactories);
  26929. }
  26930. async compileModuleAndAllComponentsAsync(moduleType) {
  26931. const ngModuleFactory = await this.compileModuleAsync(moduleType);
  26932. const componentFactories = this.testBed._getComponentFactories(moduleType);
  26933. return new ModuleWithComponentFactories(ngModuleFactory, componentFactories);
  26934. }
  26935. clearCache() { }
  26936. clearCacheFor(type) { }
  26937. getModuleId(moduleType) {
  26938. const meta = this.testBed._getModuleResolver().resolve(moduleType);
  26939. return meta && meta.id || undefined;
  26940. }
  26941. }
  26942. // The formatter and CI disagree on how this import statement should be formatted. Both try to keep
  26943. let _nextRootElementId = 0;
  26944. /**
  26945. * Returns a singleton of the `TestBed` class.
  26946. *
  26947. * @publicApi
  26948. */
  26949. function getTestBed() {
  26950. return TestBedImpl.INSTANCE;
  26951. }
  26952. /**
  26953. * @description
  26954. * Configures and initializes environment for unit testing and provides methods for
  26955. * creating components and services in unit tests.
  26956. *
  26957. * TestBed is the primary api for writing unit tests for Angular applications and libraries.
  26958. */
  26959. class TestBedImpl {
  26960. constructor() {
  26961. // Properties
  26962. this.platform = null;
  26963. this.ngModule = null;
  26964. this._compiler = null;
  26965. this._testModuleRef = null;
  26966. this._activeFixtures = [];
  26967. /**
  26968. * Internal-only flag to indicate whether a module
  26969. * scoping queue has been checked and flushed already.
  26970. * @nodoc
  26971. */
  26972. this.globalCompilationChecked = false;
  26973. }
  26974. static { this._INSTANCE = null; }
  26975. static get INSTANCE() {
  26976. return TestBedImpl._INSTANCE = TestBedImpl._INSTANCE || new TestBedImpl();
  26977. }
  26978. /**
  26979. * Initialize the environment for testing with a compiler factory, a PlatformRef, and an
  26980. * angular module. These are common to every test in the suite.
  26981. *
  26982. * This may only be called once, to set up the common providers for the current test
  26983. * suite on the current platform. If you absolutely need to change the providers,
  26984. * first use `resetTestEnvironment`.
  26985. *
  26986. * Test modules and platforms for individual platforms are available from
  26987. * '@angular/<platform_name>/testing'.
  26988. *
  26989. * @publicApi
  26990. */
  26991. static initTestEnvironment(ngModule, platform, options) {
  26992. const testBed = TestBedImpl.INSTANCE;
  26993. testBed.initTestEnvironment(ngModule, platform, options);
  26994. return testBed;
  26995. }
  26996. /**
  26997. * Reset the providers for the test injector.
  26998. *
  26999. * @publicApi
  27000. */
  27001. static resetTestEnvironment() {
  27002. TestBedImpl.INSTANCE.resetTestEnvironment();
  27003. }
  27004. static configureCompiler(config) {
  27005. return TestBedImpl.INSTANCE.configureCompiler(config);
  27006. }
  27007. /**
  27008. * Allows overriding default providers, directives, pipes, modules of the test injector,
  27009. * which are defined in test_injector.js
  27010. */
  27011. static configureTestingModule(moduleDef) {
  27012. return TestBedImpl.INSTANCE.configureTestingModule(moduleDef);
  27013. }
  27014. /**
  27015. * Compile components with a `templateUrl` for the test's NgModule.
  27016. * It is necessary to call this function
  27017. * as fetching urls is asynchronous.
  27018. */
  27019. static compileComponents() {
  27020. return TestBedImpl.INSTANCE.compileComponents();
  27021. }
  27022. static overrideModule(ngModule, override) {
  27023. return TestBedImpl.INSTANCE.overrideModule(ngModule, override);
  27024. }
  27025. static overrideComponent(component, override) {
  27026. return TestBedImpl.INSTANCE.overrideComponent(component, override);
  27027. }
  27028. static overrideDirective(directive, override) {
  27029. return TestBedImpl.INSTANCE.overrideDirective(directive, override);
  27030. }
  27031. static overridePipe(pipe, override) {
  27032. return TestBedImpl.INSTANCE.overridePipe(pipe, override);
  27033. }
  27034. static overrideTemplate(component, template) {
  27035. return TestBedImpl.INSTANCE.overrideTemplate(component, template);
  27036. }
  27037. /**
  27038. * Overrides the template of the given component, compiling the template
  27039. * in the context of the TestingModule.
  27040. *
  27041. * Note: This works for JIT and AOTed components as well.
  27042. */
  27043. static overrideTemplateUsingTestingModule(component, template) {
  27044. return TestBedImpl.INSTANCE.overrideTemplateUsingTestingModule(component, template);
  27045. }
  27046. static overrideProvider(token, provider) {
  27047. return TestBedImpl.INSTANCE.overrideProvider(token, provider);
  27048. }
  27049. static inject(token, notFoundValue, flags) {
  27050. return TestBedImpl.INSTANCE.inject(token, notFoundValue, ɵconvertToBitFlags(flags));
  27051. }
  27052. /** @deprecated from v9.0.0 use TestBed.inject */
  27053. static get(token, notFoundValue = Injector$1.THROW_IF_NOT_FOUND, flags = InjectFlags$1.Default) {
  27054. return TestBedImpl.INSTANCE.inject(token, notFoundValue, flags);
  27055. }
  27056. /**
  27057. * Runs the given function in the `EnvironmentInjector` context of `TestBed`.
  27058. *
  27059. * @see {@link EnvironmentInjector#runInContext}
  27060. */
  27061. static runInInjectionContext(fn) {
  27062. return TestBedImpl.INSTANCE.runInInjectionContext(fn);
  27063. }
  27064. static createComponent(component) {
  27065. return TestBedImpl.INSTANCE.createComponent(component);
  27066. }
  27067. static resetTestingModule() {
  27068. return TestBedImpl.INSTANCE.resetTestingModule();
  27069. }
  27070. static execute(tokens, fn, context) {
  27071. return TestBedImpl.INSTANCE.execute(tokens, fn, context);
  27072. }
  27073. static get platform() {
  27074. return TestBedImpl.INSTANCE.platform;
  27075. }
  27076. static get ngModule() {
  27077. return TestBedImpl.INSTANCE.ngModule;
  27078. }
  27079. /**
  27080. * Initialize the environment for testing with a compiler factory, a PlatformRef, and an
  27081. * angular module. These are common to every test in the suite.
  27082. *
  27083. * This may only be called once, to set up the common providers for the current test
  27084. * suite on the current platform. If you absolutely need to change the providers,
  27085. * first use `resetTestEnvironment`.
  27086. *
  27087. * Test modules and platforms for individual platforms are available from
  27088. * '@angular/<platform_name>/testing'.
  27089. *
  27090. * @publicApi
  27091. */
  27092. initTestEnvironment(ngModule, platform, options) {
  27093. if (this.platform || this.ngModule) {
  27094. throw new Error('Cannot set base providers because it has already been called');
  27095. }
  27096. TestBedImpl._environmentTeardownOptions = options?.teardown;
  27097. TestBedImpl._environmentErrorOnUnknownElementsOption = options?.errorOnUnknownElements;
  27098. TestBedImpl._environmentErrorOnUnknownPropertiesOption = options?.errorOnUnknownProperties;
  27099. this.platform = platform;
  27100. this.ngModule = ngModule;
  27101. this._compiler = new TestBedCompiler(this.platform, this.ngModule);
  27102. // TestBed does not have an API which can reliably detect the start of a test, and thus could be
  27103. // used to track the state of the NgModule registry and reset it correctly. Instead, when we
  27104. // know we're in a testing scenario, we disable the check for duplicate NgModule registration
  27105. // completely.
  27106. ɵsetAllowDuplicateNgModuleIdsForTest(true);
  27107. }
  27108. /**
  27109. * Reset the providers for the test injector.
  27110. *
  27111. * @publicApi
  27112. */
  27113. resetTestEnvironment() {
  27114. this.resetTestingModule();
  27115. this._compiler = null;
  27116. this.platform = null;
  27117. this.ngModule = null;
  27118. TestBedImpl._environmentTeardownOptions = undefined;
  27119. ɵsetAllowDuplicateNgModuleIdsForTest(false);
  27120. }
  27121. resetTestingModule() {
  27122. this.checkGlobalCompilationFinished();
  27123. ɵresetCompiledComponents();
  27124. if (this._compiler !== null) {
  27125. this.compiler.restoreOriginalState();
  27126. }
  27127. this._compiler = new TestBedCompiler(this.platform, this.ngModule);
  27128. // Restore the previous value of the "error on unknown elements" option
  27129. ɵsetUnknownElementStrictMode$1(this._previousErrorOnUnknownElementsOption ?? THROW_ON_UNKNOWN_ELEMENTS_DEFAULT);
  27130. // Restore the previous value of the "error on unknown properties" option
  27131. ɵsetUnknownPropertyStrictMode$1(this._previousErrorOnUnknownPropertiesOption ?? THROW_ON_UNKNOWN_PROPERTIES_DEFAULT);
  27132. // We have to chain a couple of try/finally blocks, because each step can
  27133. // throw errors and we don't want it to interrupt the next step and we also
  27134. // want an error to be thrown at the end.
  27135. try {
  27136. this.destroyActiveFixtures();
  27137. }
  27138. finally {
  27139. try {
  27140. if (this.shouldTearDownTestingModule()) {
  27141. this.tearDownTestingModule();
  27142. }
  27143. }
  27144. finally {
  27145. this._testModuleRef = null;
  27146. this._instanceTeardownOptions = undefined;
  27147. this._instanceErrorOnUnknownElementsOption = undefined;
  27148. this._instanceErrorOnUnknownPropertiesOption = undefined;
  27149. }
  27150. }
  27151. return this;
  27152. }
  27153. configureCompiler(config) {
  27154. if (config.useJit != null) {
  27155. throw new Error('JIT compiler is not configurable via TestBed APIs.');
  27156. }
  27157. if (config.providers !== undefined) {
  27158. this.compiler.setCompilerProviders(config.providers);
  27159. }
  27160. return this;
  27161. }
  27162. configureTestingModule(moduleDef) {
  27163. this.assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
  27164. // Trigger module scoping queue flush before executing other TestBed operations in a test.
  27165. // This is needed for the first test invocation to ensure that globally declared modules have
  27166. // their components scoped properly. See the `checkGlobalCompilationFinished` function
  27167. // description for additional info.
  27168. this.checkGlobalCompilationFinished();
  27169. // Always re-assign the options, even if they're undefined.
  27170. // This ensures that we don't carry them between tests.
  27171. this._instanceTeardownOptions = moduleDef.teardown;
  27172. this._instanceErrorOnUnknownElementsOption = moduleDef.errorOnUnknownElements;
  27173. this._instanceErrorOnUnknownPropertiesOption = moduleDef.errorOnUnknownProperties;
  27174. // Store the current value of the strict mode option,
  27175. // so we can restore it later
  27176. this._previousErrorOnUnknownElementsOption = ɵgetUnknownElementStrictMode$1();
  27177. ɵsetUnknownElementStrictMode$1(this.shouldThrowErrorOnUnknownElements());
  27178. this._previousErrorOnUnknownPropertiesOption = ɵgetUnknownPropertyStrictMode$1();
  27179. ɵsetUnknownPropertyStrictMode$1(this.shouldThrowErrorOnUnknownProperties());
  27180. this.compiler.configureTestingModule(moduleDef);
  27181. return this;
  27182. }
  27183. compileComponents() {
  27184. return this.compiler.compileComponents();
  27185. }
  27186. inject(token, notFoundValue, flags) {
  27187. if (token === TestBed) {
  27188. return this;
  27189. }
  27190. const UNDEFINED = {};
  27191. const result = this.testModuleRef.injector.get(token, UNDEFINED, ɵconvertToBitFlags(flags));
  27192. return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) :
  27193. result;
  27194. }
  27195. /** @deprecated from v9.0.0 use TestBed.inject */
  27196. get(token, notFoundValue = Injector$1.THROW_IF_NOT_FOUND, flags = InjectFlags$1.Default) {
  27197. return this.inject(token, notFoundValue, flags);
  27198. }
  27199. runInInjectionContext(fn) {
  27200. return this.inject(EnvironmentInjector$1).runInContext(fn);
  27201. }
  27202. execute(tokens, fn, context) {
  27203. const params = tokens.map(t => this.inject(t));
  27204. return fn.apply(context, params);
  27205. }
  27206. overrideModule(ngModule, override) {
  27207. this.assertNotInstantiated('overrideModule', 'override module metadata');
  27208. this.compiler.overrideModule(ngModule, override);
  27209. return this;
  27210. }
  27211. overrideComponent(component, override) {
  27212. this.assertNotInstantiated('overrideComponent', 'override component metadata');
  27213. this.compiler.overrideComponent(component, override);
  27214. return this;
  27215. }
  27216. overrideTemplateUsingTestingModule(component, template) {
  27217. this.assertNotInstantiated('TestBed.overrideTemplateUsingTestingModule', 'Cannot override template when the test module has already been instantiated');
  27218. this.compiler.overrideTemplateUsingTestingModule(component, template);
  27219. return this;
  27220. }
  27221. overrideDirective(directive, override) {
  27222. this.assertNotInstantiated('overrideDirective', 'override directive metadata');
  27223. this.compiler.overrideDirective(directive, override);
  27224. return this;
  27225. }
  27226. overridePipe(pipe, override) {
  27227. this.assertNotInstantiated('overridePipe', 'override pipe metadata');
  27228. this.compiler.overridePipe(pipe, override);
  27229. return this;
  27230. }
  27231. /**
  27232. * Overwrites all providers for the given token with the given provider definition.
  27233. */
  27234. overrideProvider(token, provider) {
  27235. this.assertNotInstantiated('overrideProvider', 'override provider');
  27236. this.compiler.overrideProvider(token, provider);
  27237. return this;
  27238. }
  27239. overrideTemplate(component, template) {
  27240. return this.overrideComponent(component, { set: { template, templateUrl: null } });
  27241. }
  27242. createComponent(type) {
  27243. const testComponentRenderer = this.inject(TestComponentRenderer);
  27244. const rootElId = `root${_nextRootElementId++}`;
  27245. testComponentRenderer.insertRootElement(rootElId);
  27246. const componentDef = type.ɵcmp;
  27247. if (!componentDef) {
  27248. throw new Error(`It looks like '${ɵstringify(type)}' has not been compiled.`);
  27249. }
  27250. const noNgZone = this.inject(ComponentFixtureNoNgZone, false);
  27251. const autoDetect = this.inject(ComponentFixtureAutoDetect, false);
  27252. const ngZone = noNgZone ? null : this.inject(NgZone$1, null);
  27253. const componentFactory = new ɵRender3ComponentFactory(componentDef);
  27254. const initComponent = () => {
  27255. const componentRef = componentFactory.create(Injector$1.NULL, [], `#${rootElId}`, this.testModuleRef);
  27256. return new ComponentFixture(componentRef, ngZone, autoDetect);
  27257. };
  27258. const fixture = ngZone ? ngZone.run(initComponent) : initComponent();
  27259. this._activeFixtures.push(fixture);
  27260. return fixture;
  27261. }
  27262. /**
  27263. * @internal strip this from published d.ts files due to
  27264. * https://github.com/microsoft/TypeScript/issues/36216
  27265. */
  27266. get compiler() {
  27267. if (this._compiler === null) {
  27268. throw new Error(`Need to call TestBed.initTestEnvironment() first`);
  27269. }
  27270. return this._compiler;
  27271. }
  27272. /**
  27273. * @internal strip this from published d.ts files due to
  27274. * https://github.com/microsoft/TypeScript/issues/36216
  27275. */
  27276. get testModuleRef() {
  27277. if (this._testModuleRef === null) {
  27278. this._testModuleRef = this.compiler.finalize();
  27279. }
  27280. return this._testModuleRef;
  27281. }
  27282. assertNotInstantiated(methodName, methodDescription) {
  27283. if (this._testModuleRef !== null) {
  27284. throw new Error(`Cannot ${methodDescription} when the test module has already been instantiated. ` +
  27285. `Make sure you are not using \`inject\` before \`${methodName}\`.`);
  27286. }
  27287. }
  27288. /**
  27289. * Check whether the module scoping queue should be flushed, and flush it if needed.
  27290. *
  27291. * When the TestBed is reset, it clears the JIT module compilation queue, cancelling any
  27292. * in-progress module compilation. This creates a potential hazard - the very first time the
  27293. * TestBed is initialized (or if it's reset without being initialized), there may be pending
  27294. * compilations of modules declared in global scope. These compilations should be finished.
  27295. *
  27296. * To ensure that globally declared modules have their components scoped properly, this function
  27297. * is called whenever TestBed is initialized or reset. The _first_ time that this happens, prior
  27298. * to any other operations, the scoping queue is flushed.
  27299. */
  27300. checkGlobalCompilationFinished() {
  27301. // Checking _testNgModuleRef is null should not be necessary, but is left in as an additional
  27302. // guard that compilations queued in tests (after instantiation) are never flushed accidentally.
  27303. if (!this.globalCompilationChecked && this._testModuleRef === null) {
  27304. ɵflushModuleScopingQueueAsMuchAsPossible();
  27305. }
  27306. this.globalCompilationChecked = true;
  27307. }
  27308. destroyActiveFixtures() {
  27309. let errorCount = 0;
  27310. this._activeFixtures.forEach((fixture) => {
  27311. try {
  27312. fixture.destroy();
  27313. }
  27314. catch (e) {
  27315. errorCount++;
  27316. console.error('Error during cleanup of component', {
  27317. component: fixture.componentInstance,
  27318. stacktrace: e,
  27319. });
  27320. }
  27321. });
  27322. this._activeFixtures = [];
  27323. if (errorCount > 0 && this.shouldRethrowTeardownErrors()) {
  27324. throw Error(`${errorCount} ${(errorCount === 1 ? 'component' : 'components')} ` +
  27325. `threw errors during cleanup`);
  27326. }
  27327. }
  27328. shouldRethrowTeardownErrors() {
  27329. const instanceOptions = this._instanceTeardownOptions;
  27330. const environmentOptions = TestBedImpl._environmentTeardownOptions;
  27331. // If the new teardown behavior hasn't been configured, preserve the old behavior.
  27332. if (!instanceOptions && !environmentOptions) {
  27333. return TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT;
  27334. }
  27335. // Otherwise use the configured behavior or default to rethrowing.
  27336. return instanceOptions?.rethrowErrors ?? environmentOptions?.rethrowErrors ??
  27337. this.shouldTearDownTestingModule();
  27338. }
  27339. shouldThrowErrorOnUnknownElements() {
  27340. // Check if a configuration has been provided to throw when an unknown element is found
  27341. return this._instanceErrorOnUnknownElementsOption ??
  27342. TestBedImpl._environmentErrorOnUnknownElementsOption ?? THROW_ON_UNKNOWN_ELEMENTS_DEFAULT;
  27343. }
  27344. shouldThrowErrorOnUnknownProperties() {
  27345. // Check if a configuration has been provided to throw when an unknown property is found
  27346. return this._instanceErrorOnUnknownPropertiesOption ??
  27347. TestBedImpl._environmentErrorOnUnknownPropertiesOption ??
  27348. THROW_ON_UNKNOWN_PROPERTIES_DEFAULT;
  27349. }
  27350. shouldTearDownTestingModule() {
  27351. return this._instanceTeardownOptions?.destroyAfterEach ??
  27352. TestBedImpl._environmentTeardownOptions?.destroyAfterEach ??
  27353. TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT;
  27354. }
  27355. tearDownTestingModule() {
  27356. // If the module ref has already been destroyed, we won't be able to get a test renderer.
  27357. if (this._testModuleRef === null) {
  27358. return;
  27359. }
  27360. // Resolve the renderer ahead of time, because we want to remove the root elements as the very
  27361. // last step, but the injector will be destroyed as a part of the module ref destruction.
  27362. const testRenderer = this.inject(TestComponentRenderer);
  27363. try {
  27364. this._testModuleRef.destroy();
  27365. }
  27366. catch (e) {
  27367. if (this.shouldRethrowTeardownErrors()) {
  27368. throw e;
  27369. }
  27370. else {
  27371. console.error('Error during cleanup of a testing module', {
  27372. component: this._testModuleRef.instance,
  27373. stacktrace: e,
  27374. });
  27375. }
  27376. }
  27377. finally {
  27378. testRenderer.removeAllRootElements?.();
  27379. }
  27380. }
  27381. }
  27382. /**
  27383. * @description
  27384. * Configures and initializes environment for unit testing and provides methods for
  27385. * creating components and services in unit tests.
  27386. *
  27387. * `TestBed` is the primary api for writing unit tests for Angular applications and libraries.
  27388. *
  27389. * @publicApi
  27390. */
  27391. const TestBed = TestBedImpl;
  27392. /**
  27393. * Allows injecting dependencies in `beforeEach()` and `it()`. Note: this function
  27394. * (imported from the `@angular/core/testing` package) can **only** be used to inject dependencies
  27395. * in tests. To inject dependencies in your application code, use the [`inject`](api/core/inject)
  27396. * function from the `@angular/core` package instead.
  27397. *
  27398. * Example:
  27399. *
  27400. * ```
  27401. * beforeEach(inject([Dependency, AClass], (dep, object) => {
  27402. * // some code that uses `dep` and `object`
  27403. * // ...
  27404. * }));
  27405. *
  27406. * it('...', inject([AClass], (object) => {
  27407. * object.doSomething();
  27408. * expect(...);
  27409. * })
  27410. * ```
  27411. *
  27412. * @publicApi
  27413. */
  27414. function inject(tokens, fn) {
  27415. const testBed = TestBedImpl.INSTANCE;
  27416. // Not using an arrow function to preserve context passed from call site
  27417. return function () {
  27418. return testBed.execute(tokens, fn, this);
  27419. };
  27420. }
  27421. /**
  27422. * @publicApi
  27423. */
  27424. class InjectSetupWrapper {
  27425. constructor(_moduleDef) {
  27426. this._moduleDef = _moduleDef;
  27427. }
  27428. _addModule() {
  27429. const moduleDef = this._moduleDef();
  27430. if (moduleDef) {
  27431. TestBedImpl.configureTestingModule(moduleDef);
  27432. }
  27433. }
  27434. inject(tokens, fn) {
  27435. const self = this;
  27436. // Not using an arrow function to preserve context passed from call site
  27437. return function () {
  27438. self._addModule();
  27439. return inject(tokens, fn).call(this);
  27440. };
  27441. }
  27442. }
  27443. function withModule(moduleDef, fn) {
  27444. if (fn) {
  27445. // Not using an arrow function to preserve context passed from call site
  27446. return function () {
  27447. const testBed = TestBedImpl.INSTANCE;
  27448. if (moduleDef) {
  27449. testBed.configureTestingModule(moduleDef);
  27450. }
  27451. return fn.apply(this);
  27452. };
  27453. }
  27454. return new InjectSetupWrapper(() => moduleDef);
  27455. }
  27456. /**
  27457. * Public Test Library for unit testing Angular applications. Assumes that you are running
  27458. * with Jasmine, Mocha, or a similar framework which exports a beforeEach function and
  27459. * allows tests to be asynchronous by either returning a promise or using a 'done' parameter.
  27460. */
  27461. // Reset the test providers and the fake async zone before each test.
  27462. // We keep a guard because somehow this file can make it into a bundle and be executed
  27463. // beforeEach is only defined when executing the tests
  27464. globalThis.beforeEach?.(getCleanupHook(false));
  27465. // We provide both a `beforeEach` and `afterEach`, because the updated behavior for
  27466. // tearing down the module is supposed to run after the test so that we can associate
  27467. // teardown errors with the correct test.
  27468. // We keep a guard because somehow this file can make it into a bundle and be executed
  27469. // afterEach is only defined when executing the tests
  27470. globalThis.afterEach?.(getCleanupHook(true));
  27471. function getCleanupHook(expectedTeardownValue) {
  27472. return () => {
  27473. const testBed = TestBedImpl.INSTANCE;
  27474. if (testBed.shouldTearDownTestingModule() === expectedTeardownValue) {
  27475. testBed.resetTestingModule();
  27476. resetFakeAsyncZone();
  27477. }
  27478. };
  27479. }
  27480. /**
  27481. * This API should be removed. But doing so seems to break `google3` and so it requires a bit of
  27482. * investigation.
  27483. *
  27484. * A work around is to mark it as `@codeGenApi` for now and investigate later.
  27485. *
  27486. * @codeGenApi
  27487. */
  27488. // TODO(iminar): Remove this code in a safe way.
  27489. const __core_private_testing_placeholder__ = '';
  27490. /**
  27491. * @module
  27492. * @description
  27493. * Entry point for all public APIs of the core/testing package.
  27494. */
  27495. /// <reference types="jasmine" />
  27496. // This file only reexports content of the `src` folder. Keep it that way.
  27497. // This file is not used to build this module. It is only used during editing
  27498. /**
  27499. * Generated bundle index. Do not edit.
  27500. */
  27501. export { ComponentFixture, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, InjectSetupWrapper, TestBed, TestComponentRenderer, __core_private_testing_placeholder__, async, discardPeriodicTasks, fakeAsync, flush, flushMicrotasks, getTestBed, inject, resetFakeAsyncZone, tick, waitForAsync, withModule, MetadataOverrider as ɵMetadataOverrider };
  27502. //# sourceMappingURL=testing.mjs.map