core.mjs 1.3 MB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423104241042510426104271042810429104301043110432104331043410435104361043710438104391044010441104421044310444104451044610447104481044910450104511045210453104541045510456104571045810459104601046110462104631046410465104661046710468104691047010471104721047310474104751047610477104781047910480104811048210483104841048510486104871048810489104901049110492104931049410495104961049710498104991050010501105021050310504105051050610507105081050910510105111051210513105141051510516105171051810519105201052110522105231052410525105261052710528105291053010531105321053310534105351053610537105381053910540105411054210543105441054510546105471054810549105501055110552105531055410555105561055710558105591056010561105621056310564105651056610567105681056910570105711057210573105741057510576105771057810579105801058110582105831058410585105861058710588105891059010591105921059310594105951059610597105981059910600106011060210603106041060510606106071060810609106101061110612106131061410615106161061710618106191062010621106221062310624106251062610627106281062910630106311063210633106341063510636106371063810639106401064110642106431064410645106461064710648106491065010651106521065310654106551065610657106581065910660106611066210663106641066510666106671066810669106701067110672106731067410675106761067710678106791068010681106821068310684106851068610687106881068910690106911069210693106941069510696106971069810699107001070110702107031070410705107061070710708107091071010711107121071310714107151071610717107181071910720107211072210723107241072510726107271072810729107301073110732107331073410735107361073710738107391074010741107421074310744107451074610747107481074910750107511075210753107541075510756107571075810759107601076110762107631076410765107661076710768107691077010771107721077310774107751077610777107781077910780107811078210783107841078510786107871078810789107901079110792107931079410795107961079710798107991080010801108021080310804108051080610807108081080910810108111081210813108141081510816108171081810819108201082110822108231082410825108261082710828108291083010831108321083310834108351083610837108381083910840108411084210843108441084510846108471084810849108501085110852108531085410855108561085710858108591086010861108621086310864108651086610867108681086910870108711087210873108741087510876108771087810879108801088110882108831088410885108861088710888108891089010891108921089310894108951089610897108981089910900109011090210903109041090510906109071090810909109101091110912109131091410915109161091710918109191092010921109221092310924109251092610927109281092910930109311093210933109341093510936109371093810939109401094110942109431094410945109461094710948109491095010951109521095310954109551095610957109581095910960109611096210963109641096510966109671096810969109701097110972109731097410975109761097710978109791098010981109821098310984109851098610987109881098910990109911099210993109941099510996109971099810999110001100111002110031100411005110061100711008110091101011011110121101311014110151101611017110181101911020110211102211023110241102511026110271102811029110301103111032110331103411035110361103711038110391104011041110421104311044110451104611047110481104911050110511105211053110541105511056110571105811059110601106111062110631106411065110661106711068110691107011071110721107311074110751107611077110781107911080110811108211083110841108511086110871108811089110901109111092110931109411095110961109711098110991110011101111021110311104111051110611107111081110911110111111111211113111141111511116111171111811119111201112111122111231112411125111261112711128111291113011131111321113311134111351113611137111381113911140111411114211143111441114511146111471114811149111501115111152111531115411155111561115711158111591116011161111621116311164111651116611167111681116911170111711117211173111741117511176111771117811179111801118111182111831118411185111861118711188111891119011191111921119311194111951119611197111981119911200112011120211203112041120511206112071120811209112101121111212112131121411215112161121711218112191122011221112221122311224112251122611227112281122911230112311123211233112341123511236112371123811239112401124111242112431124411245112461124711248112491125011251112521125311254112551125611257112581125911260112611126211263112641126511266112671126811269112701127111272112731127411275112761127711278112791128011281112821128311284112851128611287112881128911290112911129211293112941129511296112971129811299113001130111302113031130411305113061130711308113091131011311113121131311314113151131611317113181131911320113211132211323113241132511326113271132811329113301133111332113331133411335113361133711338113391134011341113421134311344113451134611347113481134911350113511135211353113541135511356113571135811359113601136111362113631136411365113661136711368113691137011371113721137311374113751137611377113781137911380113811138211383113841138511386113871138811389113901139111392113931139411395113961139711398113991140011401114021140311404114051140611407114081140911410114111141211413114141141511416114171141811419114201142111422114231142411425114261142711428114291143011431114321143311434114351143611437114381143911440114411144211443114441144511446114471144811449114501145111452114531145411455114561145711458114591146011461114621146311464114651146611467114681146911470114711147211473114741147511476114771147811479114801148111482114831148411485114861148711488114891149011491114921149311494114951149611497114981149911500115011150211503115041150511506115071150811509115101151111512115131151411515115161151711518115191152011521115221152311524115251152611527115281152911530115311153211533115341153511536115371153811539115401154111542115431154411545115461154711548115491155011551115521155311554115551155611557115581155911560115611156211563115641156511566115671156811569115701157111572115731157411575115761157711578115791158011581115821158311584115851158611587115881158911590115911159211593115941159511596115971159811599116001160111602116031160411605116061160711608116091161011611116121161311614116151161611617116181161911620116211162211623116241162511626116271162811629116301163111632116331163411635116361163711638116391164011641116421164311644116451164611647116481164911650116511165211653116541165511656116571165811659116601166111662116631166411665116661166711668116691167011671116721167311674116751167611677116781167911680116811168211683116841168511686116871168811689116901169111692116931169411695116961169711698116991170011701117021170311704117051170611707117081170911710117111171211713117141171511716117171171811719117201172111722117231172411725117261172711728117291173011731117321173311734117351173611737117381173911740117411174211743117441174511746117471174811749117501175111752117531175411755117561175711758117591176011761117621176311764117651176611767117681176911770117711177211773117741177511776117771177811779117801178111782117831178411785117861178711788117891179011791117921179311794117951179611797117981179911800118011180211803118041180511806118071180811809118101181111812118131181411815118161181711818118191182011821118221182311824118251182611827118281182911830118311183211833118341183511836118371183811839118401184111842118431184411845118461184711848118491185011851118521185311854118551185611857118581185911860118611186211863118641186511866118671186811869118701187111872118731187411875118761187711878118791188011881118821188311884118851188611887118881188911890118911189211893118941189511896118971189811899119001190111902119031190411905119061190711908119091191011911119121191311914119151191611917119181191911920119211192211923119241192511926119271192811929119301193111932119331193411935119361193711938119391194011941119421194311944119451194611947119481194911950119511195211953119541195511956119571195811959119601196111962119631196411965119661196711968119691197011971119721197311974119751197611977119781197911980119811198211983119841198511986119871198811989119901199111992119931199411995119961199711998119991200012001120021200312004120051200612007120081200912010120111201212013120141201512016120171201812019120201202112022120231202412025120261202712028120291203012031120321203312034120351203612037120381203912040120411204212043120441204512046120471204812049120501205112052120531205412055120561205712058120591206012061120621206312064120651206612067120681206912070120711207212073120741207512076120771207812079120801208112082120831208412085120861208712088120891209012091120921209312094120951209612097120981209912100121011210212103121041210512106121071210812109121101211112112121131211412115121161211712118121191212012121121221212312124121251212612127121281212912130121311213212133121341213512136121371213812139121401214112142121431214412145121461214712148121491215012151121521215312154121551215612157121581215912160121611216212163121641216512166121671216812169121701217112172121731217412175121761217712178121791218012181121821218312184121851218612187121881218912190121911219212193121941219512196121971219812199122001220112202122031220412205122061220712208122091221012211122121221312214122151221612217122181221912220122211222212223122241222512226122271222812229122301223112232122331223412235122361223712238122391224012241122421224312244122451224612247122481224912250122511225212253122541225512256122571225812259122601226112262122631226412265122661226712268122691227012271122721227312274122751227612277122781227912280122811228212283122841228512286122871228812289122901229112292122931229412295122961229712298122991230012301123021230312304123051230612307123081230912310123111231212313123141231512316123171231812319123201232112322123231232412325123261232712328123291233012331123321233312334123351233612337123381233912340123411234212343123441234512346123471234812349123501235112352123531235412355123561235712358123591236012361123621236312364123651236612367123681236912370123711237212373123741237512376123771237812379123801238112382123831238412385123861238712388123891239012391123921239312394123951239612397123981239912400124011240212403124041240512406124071240812409124101241112412124131241412415124161241712418124191242012421124221242312424124251242612427124281242912430124311243212433124341243512436124371243812439124401244112442124431244412445124461244712448124491245012451124521245312454124551245612457124581245912460124611246212463124641246512466124671246812469124701247112472124731247412475124761247712478124791248012481124821248312484124851248612487124881248912490124911249212493124941249512496124971249812499125001250112502125031250412505125061250712508125091251012511125121251312514125151251612517125181251912520125211252212523125241252512526125271252812529125301253112532125331253412535125361253712538125391254012541125421254312544125451254612547125481254912550125511255212553125541255512556125571255812559125601256112562125631256412565125661256712568125691257012571125721257312574125751257612577125781257912580125811258212583125841258512586125871258812589125901259112592125931259412595125961259712598125991260012601126021260312604126051260612607126081260912610126111261212613126141261512616126171261812619126201262112622126231262412625126261262712628126291263012631126321263312634126351263612637126381263912640126411264212643126441264512646126471264812649126501265112652126531265412655126561265712658126591266012661126621266312664126651266612667126681266912670126711267212673126741267512676126771267812679126801268112682126831268412685126861268712688126891269012691126921269312694126951269612697126981269912700127011270212703127041270512706127071270812709127101271112712127131271412715127161271712718127191272012721127221272312724127251272612727127281272912730127311273212733127341273512736127371273812739127401274112742127431274412745127461274712748127491275012751127521275312754127551275612757127581275912760127611276212763127641276512766127671276812769127701277112772127731277412775127761277712778127791278012781127821278312784127851278612787127881278912790127911279212793127941279512796127971279812799128001280112802128031280412805128061280712808128091281012811128121281312814128151281612817128181281912820128211282212823128241282512826128271282812829128301283112832128331283412835128361283712838128391284012841128421284312844128451284612847128481284912850128511285212853128541285512856128571285812859128601286112862128631286412865128661286712868128691287012871128721287312874128751287612877128781287912880128811288212883128841288512886128871288812889128901289112892128931289412895128961289712898128991290012901129021290312904129051290612907129081290912910129111291212913129141291512916129171291812919129201292112922129231292412925129261292712928129291293012931129321293312934129351293612937129381293912940129411294212943129441294512946129471294812949129501295112952129531295412955129561295712958129591296012961129621296312964129651296612967129681296912970129711297212973129741297512976129771297812979129801298112982129831298412985129861298712988129891299012991129921299312994129951299612997129981299913000130011300213003130041300513006130071300813009130101301113012130131301413015130161301713018130191302013021130221302313024130251302613027130281302913030130311303213033130341303513036130371303813039130401304113042130431304413045130461304713048130491305013051130521305313054130551305613057130581305913060130611306213063130641306513066130671306813069130701307113072130731307413075130761307713078130791308013081130821308313084130851308613087130881308913090130911309213093130941309513096130971309813099131001310113102131031310413105131061310713108131091311013111131121311313114131151311613117131181311913120131211312213123131241312513126131271312813129131301313113132131331313413135131361313713138131391314013141131421314313144131451314613147131481314913150131511315213153131541315513156131571315813159131601316113162131631316413165131661316713168131691317013171131721317313174131751317613177131781317913180131811318213183131841318513186131871318813189131901319113192131931319413195131961319713198131991320013201132021320313204132051320613207132081320913210132111321213213132141321513216132171321813219132201322113222132231322413225132261322713228132291323013231132321323313234132351323613237132381323913240132411324213243132441324513246132471324813249132501325113252132531325413255132561325713258132591326013261132621326313264132651326613267132681326913270132711327213273132741327513276132771327813279132801328113282132831328413285132861328713288132891329013291132921329313294132951329613297132981329913300133011330213303133041330513306133071330813309133101331113312133131331413315133161331713318133191332013321133221332313324133251332613327133281332913330133311333213333133341333513336133371333813339133401334113342133431334413345133461334713348133491335013351133521335313354133551335613357133581335913360133611336213363133641336513366133671336813369133701337113372133731337413375133761337713378133791338013381133821338313384133851338613387133881338913390133911339213393133941339513396133971339813399134001340113402134031340413405134061340713408134091341013411134121341313414134151341613417134181341913420134211342213423134241342513426134271342813429134301343113432134331343413435134361343713438134391344013441134421344313444134451344613447134481344913450134511345213453134541345513456134571345813459134601346113462134631346413465134661346713468134691347013471134721347313474134751347613477134781347913480134811348213483134841348513486134871348813489134901349113492134931349413495134961349713498134991350013501135021350313504135051350613507135081350913510135111351213513135141351513516135171351813519135201352113522135231352413525135261352713528135291353013531135321353313534135351353613537135381353913540135411354213543135441354513546135471354813549135501355113552135531355413555135561355713558135591356013561135621356313564135651356613567135681356913570135711357213573135741357513576135771357813579135801358113582135831358413585135861358713588135891359013591135921359313594135951359613597135981359913600136011360213603136041360513606136071360813609136101361113612136131361413615136161361713618136191362013621136221362313624136251362613627136281362913630136311363213633136341363513636136371363813639136401364113642136431364413645136461364713648136491365013651136521365313654136551365613657136581365913660136611366213663136641366513666136671366813669136701367113672136731367413675136761367713678136791368013681136821368313684136851368613687136881368913690136911369213693136941369513696136971369813699137001370113702137031370413705137061370713708137091371013711137121371313714137151371613717137181371913720137211372213723137241372513726137271372813729137301373113732137331373413735137361373713738137391374013741137421374313744137451374613747137481374913750137511375213753137541375513756137571375813759137601376113762137631376413765137661376713768137691377013771137721377313774137751377613777137781377913780137811378213783137841378513786137871378813789137901379113792137931379413795137961379713798137991380013801138021380313804138051380613807138081380913810138111381213813138141381513816138171381813819138201382113822138231382413825138261382713828138291383013831138321383313834138351383613837138381383913840138411384213843138441384513846138471384813849138501385113852138531385413855138561385713858138591386013861138621386313864138651386613867138681386913870138711387213873138741387513876138771387813879138801388113882138831388413885138861388713888138891389013891138921389313894138951389613897138981389913900139011390213903139041390513906139071390813909139101391113912139131391413915139161391713918139191392013921139221392313924139251392613927139281392913930139311393213933139341393513936139371393813939139401394113942139431394413945139461394713948139491395013951139521395313954139551395613957139581395913960139611396213963139641396513966139671396813969139701397113972139731397413975139761397713978139791398013981139821398313984139851398613987139881398913990139911399213993139941399513996139971399813999140001400114002140031400414005140061400714008140091401014011140121401314014140151401614017140181401914020140211402214023140241402514026140271402814029140301403114032140331403414035140361403714038140391404014041140421404314044140451404614047140481404914050140511405214053140541405514056140571405814059140601406114062140631406414065140661406714068140691407014071140721407314074140751407614077140781407914080140811408214083140841408514086140871408814089140901409114092140931409414095140961409714098140991410014101141021410314104141051410614107141081410914110141111411214113141141411514116141171411814119141201412114122141231412414125141261412714128141291413014131141321413314134141351413614137141381413914140141411414214143141441414514146141471414814149141501415114152141531415414155141561415714158141591416014161141621416314164141651416614167141681416914170141711417214173141741417514176141771417814179141801418114182141831418414185141861418714188141891419014191141921419314194141951419614197141981419914200142011420214203142041420514206142071420814209142101421114212142131421414215142161421714218142191422014221142221422314224142251422614227142281422914230142311423214233142341423514236142371423814239142401424114242142431424414245142461424714248142491425014251142521425314254142551425614257142581425914260142611426214263142641426514266142671426814269142701427114272142731427414275142761427714278142791428014281142821428314284142851428614287142881428914290142911429214293142941429514296142971429814299143001430114302143031430414305143061430714308143091431014311143121431314314143151431614317143181431914320143211432214323143241432514326143271432814329143301433114332143331433414335143361433714338143391434014341143421434314344143451434614347143481434914350143511435214353143541435514356143571435814359143601436114362143631436414365143661436714368143691437014371143721437314374143751437614377143781437914380143811438214383143841438514386143871438814389143901439114392143931439414395143961439714398143991440014401144021440314404144051440614407144081440914410144111441214413144141441514416144171441814419144201442114422144231442414425144261442714428144291443014431144321443314434144351443614437144381443914440144411444214443144441444514446144471444814449144501445114452144531445414455144561445714458144591446014461144621446314464144651446614467144681446914470144711447214473144741447514476144771447814479144801448114482144831448414485144861448714488144891449014491144921449314494144951449614497144981449914500145011450214503145041450514506145071450814509145101451114512145131451414515145161451714518145191452014521145221452314524145251452614527145281452914530145311453214533145341453514536145371453814539145401454114542145431454414545145461454714548145491455014551145521455314554145551455614557145581455914560145611456214563145641456514566145671456814569145701457114572145731457414575145761457714578145791458014581145821458314584145851458614587145881458914590145911459214593145941459514596145971459814599146001460114602146031460414605146061460714608146091461014611146121461314614146151461614617146181461914620146211462214623146241462514626146271462814629146301463114632146331463414635146361463714638146391464014641146421464314644146451464614647146481464914650146511465214653146541465514656146571465814659146601466114662146631466414665146661466714668146691467014671146721467314674146751467614677146781467914680146811468214683146841468514686146871468814689146901469114692146931469414695146961469714698146991470014701147021470314704147051470614707147081470914710147111471214713147141471514716147171471814719147201472114722147231472414725147261472714728147291473014731147321473314734147351473614737147381473914740147411474214743147441474514746147471474814749147501475114752147531475414755147561475714758147591476014761147621476314764147651476614767147681476914770147711477214773147741477514776147771477814779147801478114782147831478414785147861478714788147891479014791147921479314794147951479614797147981479914800148011480214803148041480514806148071480814809148101481114812148131481414815148161481714818148191482014821148221482314824148251482614827148281482914830148311483214833148341483514836148371483814839148401484114842148431484414845148461484714848148491485014851148521485314854148551485614857148581485914860148611486214863148641486514866148671486814869148701487114872148731487414875148761487714878148791488014881148821488314884148851488614887148881488914890148911489214893148941489514896148971489814899149001490114902149031490414905149061490714908149091491014911149121491314914149151491614917149181491914920149211492214923149241492514926149271492814929149301493114932149331493414935149361493714938149391494014941149421494314944149451494614947149481494914950149511495214953149541495514956149571495814959149601496114962149631496414965149661496714968149691497014971149721497314974149751497614977149781497914980149811498214983149841498514986149871498814989149901499114992149931499414995149961499714998149991500015001150021500315004150051500615007150081500915010150111501215013150141501515016150171501815019150201502115022150231502415025150261502715028150291503015031150321503315034150351503615037150381503915040150411504215043150441504515046150471504815049150501505115052150531505415055150561505715058150591506015061150621506315064150651506615067150681506915070150711507215073150741507515076150771507815079150801508115082150831508415085150861508715088150891509015091150921509315094150951509615097150981509915100151011510215103151041510515106151071510815109151101511115112151131511415115151161511715118151191512015121151221512315124151251512615127151281512915130151311513215133151341513515136151371513815139151401514115142151431514415145151461514715148151491515015151151521515315154151551515615157151581515915160151611516215163151641516515166151671516815169151701517115172151731517415175151761517715178151791518015181151821518315184151851518615187151881518915190151911519215193151941519515196151971519815199152001520115202152031520415205152061520715208152091521015211152121521315214152151521615217152181521915220152211522215223152241522515226152271522815229152301523115232152331523415235152361523715238152391524015241152421524315244152451524615247152481524915250152511525215253152541525515256152571525815259152601526115262152631526415265152661526715268152691527015271152721527315274152751527615277152781527915280152811528215283152841528515286152871528815289152901529115292152931529415295152961529715298152991530015301153021530315304153051530615307153081530915310153111531215313153141531515316153171531815319153201532115322153231532415325153261532715328153291533015331153321533315334153351533615337153381533915340153411534215343153441534515346153471534815349153501535115352153531535415355153561535715358153591536015361153621536315364153651536615367153681536915370153711537215373153741537515376153771537815379153801538115382153831538415385153861538715388153891539015391153921539315394153951539615397153981539915400154011540215403154041540515406154071540815409154101541115412154131541415415154161541715418154191542015421154221542315424154251542615427154281542915430154311543215433154341543515436154371543815439154401544115442154431544415445154461544715448154491545015451154521545315454154551545615457154581545915460154611546215463154641546515466154671546815469154701547115472154731547415475154761547715478154791548015481154821548315484154851548615487154881548915490154911549215493154941549515496154971549815499155001550115502155031550415505155061550715508155091551015511155121551315514155151551615517155181551915520155211552215523155241552515526155271552815529155301553115532155331553415535155361553715538155391554015541155421554315544155451554615547155481554915550155511555215553155541555515556155571555815559155601556115562155631556415565155661556715568155691557015571155721557315574155751557615577155781557915580155811558215583155841558515586155871558815589155901559115592155931559415595155961559715598155991560015601156021560315604156051560615607156081560915610156111561215613156141561515616156171561815619156201562115622156231562415625156261562715628156291563015631156321563315634156351563615637156381563915640156411564215643156441564515646156471564815649156501565115652156531565415655156561565715658156591566015661156621566315664156651566615667156681566915670156711567215673156741567515676156771567815679156801568115682156831568415685156861568715688156891569015691156921569315694156951569615697156981569915700157011570215703157041570515706157071570815709157101571115712157131571415715157161571715718157191572015721157221572315724157251572615727157281572915730157311573215733157341573515736157371573815739157401574115742157431574415745157461574715748157491575015751157521575315754157551575615757157581575915760157611576215763157641576515766157671576815769157701577115772157731577415775157761577715778157791578015781157821578315784157851578615787157881578915790157911579215793157941579515796157971579815799158001580115802158031580415805158061580715808158091581015811158121581315814158151581615817158181581915820158211582215823158241582515826158271582815829158301583115832158331583415835158361583715838158391584015841158421584315844158451584615847158481584915850158511585215853158541585515856158571585815859158601586115862158631586415865158661586715868158691587015871158721587315874158751587615877158781587915880158811588215883158841588515886158871588815889158901589115892158931589415895158961589715898158991590015901159021590315904159051590615907159081590915910159111591215913159141591515916159171591815919159201592115922159231592415925159261592715928159291593015931159321593315934159351593615937159381593915940159411594215943159441594515946159471594815949159501595115952159531595415955159561595715958159591596015961159621596315964159651596615967159681596915970159711597215973159741597515976159771597815979159801598115982159831598415985159861598715988159891599015991159921599315994159951599615997159981599916000160011600216003160041600516006160071600816009160101601116012160131601416015160161601716018160191602016021160221602316024160251602616027160281602916030160311603216033160341603516036160371603816039160401604116042160431604416045160461604716048160491605016051160521605316054160551605616057160581605916060160611606216063160641606516066160671606816069160701607116072160731607416075160761607716078160791608016081160821608316084160851608616087160881608916090160911609216093160941609516096160971609816099161001610116102161031610416105161061610716108161091611016111161121611316114161151611616117161181611916120161211612216123161241612516126161271612816129161301613116132161331613416135161361613716138161391614016141161421614316144161451614616147161481614916150161511615216153161541615516156161571615816159161601616116162161631616416165161661616716168161691617016171161721617316174161751617616177161781617916180161811618216183161841618516186161871618816189161901619116192161931619416195161961619716198161991620016201162021620316204162051620616207162081620916210162111621216213162141621516216162171621816219162201622116222162231622416225162261622716228162291623016231162321623316234162351623616237162381623916240162411624216243162441624516246162471624816249162501625116252162531625416255162561625716258162591626016261162621626316264162651626616267162681626916270162711627216273162741627516276162771627816279162801628116282162831628416285162861628716288162891629016291162921629316294162951629616297162981629916300163011630216303163041630516306163071630816309163101631116312163131631416315163161631716318163191632016321163221632316324163251632616327163281632916330163311633216333163341633516336163371633816339163401634116342163431634416345163461634716348163491635016351163521635316354163551635616357163581635916360163611636216363163641636516366163671636816369163701637116372163731637416375163761637716378163791638016381163821638316384163851638616387163881638916390163911639216393163941639516396163971639816399164001640116402164031640416405164061640716408164091641016411164121641316414164151641616417164181641916420164211642216423164241642516426164271642816429164301643116432164331643416435164361643716438164391644016441164421644316444164451644616447164481644916450164511645216453164541645516456164571645816459164601646116462164631646416465164661646716468164691647016471164721647316474164751647616477164781647916480164811648216483164841648516486164871648816489164901649116492164931649416495164961649716498164991650016501165021650316504165051650616507165081650916510165111651216513165141651516516165171651816519165201652116522165231652416525165261652716528165291653016531165321653316534165351653616537165381653916540165411654216543165441654516546165471654816549165501655116552165531655416555165561655716558165591656016561165621656316564165651656616567165681656916570165711657216573165741657516576165771657816579165801658116582165831658416585165861658716588165891659016591165921659316594165951659616597165981659916600166011660216603166041660516606166071660816609166101661116612166131661416615166161661716618166191662016621166221662316624166251662616627166281662916630166311663216633166341663516636166371663816639166401664116642166431664416645166461664716648166491665016651166521665316654166551665616657166581665916660166611666216663166641666516666166671666816669166701667116672166731667416675166761667716678166791668016681166821668316684166851668616687166881668916690166911669216693166941669516696166971669816699167001670116702167031670416705167061670716708167091671016711167121671316714167151671616717167181671916720167211672216723167241672516726167271672816729167301673116732167331673416735167361673716738167391674016741167421674316744167451674616747167481674916750167511675216753167541675516756167571675816759167601676116762167631676416765167661676716768167691677016771167721677316774167751677616777167781677916780167811678216783167841678516786167871678816789167901679116792167931679416795167961679716798167991680016801168021680316804168051680616807168081680916810168111681216813168141681516816168171681816819168201682116822168231682416825168261682716828168291683016831168321683316834168351683616837168381683916840168411684216843168441684516846168471684816849168501685116852168531685416855168561685716858168591686016861168621686316864168651686616867168681686916870168711687216873168741687516876168771687816879168801688116882168831688416885168861688716888168891689016891168921689316894168951689616897168981689916900169011690216903169041690516906169071690816909169101691116912169131691416915169161691716918169191692016921169221692316924169251692616927169281692916930169311693216933169341693516936169371693816939169401694116942169431694416945169461694716948169491695016951169521695316954169551695616957169581695916960169611696216963169641696516966169671696816969169701697116972169731697416975169761697716978169791698016981169821698316984169851698616987169881698916990169911699216993169941699516996169971699816999170001700117002170031700417005170061700717008170091701017011170121701317014170151701617017170181701917020170211702217023170241702517026170271702817029170301703117032170331703417035170361703717038170391704017041170421704317044170451704617047170481704917050170511705217053170541705517056170571705817059170601706117062170631706417065170661706717068170691707017071170721707317074170751707617077170781707917080170811708217083170841708517086170871708817089170901709117092170931709417095170961709717098170991710017101171021710317104171051710617107171081710917110171111711217113171141711517116171171711817119171201712117122171231712417125171261712717128171291713017131171321713317134171351713617137171381713917140171411714217143171441714517146171471714817149171501715117152171531715417155171561715717158171591716017161171621716317164171651716617167171681716917170171711717217173171741717517176171771717817179171801718117182171831718417185171861718717188171891719017191171921719317194171951719617197171981719917200172011720217203172041720517206172071720817209172101721117212172131721417215172161721717218172191722017221172221722317224172251722617227172281722917230172311723217233172341723517236172371723817239172401724117242172431724417245172461724717248172491725017251172521725317254172551725617257172581725917260172611726217263172641726517266172671726817269172701727117272172731727417275172761727717278172791728017281172821728317284172851728617287172881728917290172911729217293172941729517296172971729817299173001730117302173031730417305173061730717308173091731017311173121731317314173151731617317173181731917320173211732217323173241732517326173271732817329173301733117332173331733417335173361733717338173391734017341173421734317344173451734617347173481734917350173511735217353173541735517356173571735817359173601736117362173631736417365173661736717368173691737017371173721737317374173751737617377173781737917380173811738217383173841738517386173871738817389173901739117392173931739417395173961739717398173991740017401174021740317404174051740617407174081740917410174111741217413174141741517416174171741817419174201742117422174231742417425174261742717428174291743017431174321743317434174351743617437174381743917440174411744217443174441744517446174471744817449174501745117452174531745417455174561745717458174591746017461174621746317464174651746617467174681746917470174711747217473174741747517476174771747817479174801748117482174831748417485174861748717488174891749017491174921749317494174951749617497174981749917500175011750217503175041750517506175071750817509175101751117512175131751417515175161751717518175191752017521175221752317524175251752617527175281752917530175311753217533175341753517536175371753817539175401754117542175431754417545175461754717548175491755017551175521755317554175551755617557175581755917560175611756217563175641756517566175671756817569175701757117572175731757417575175761757717578175791758017581175821758317584175851758617587175881758917590175911759217593175941759517596175971759817599176001760117602176031760417605176061760717608176091761017611176121761317614176151761617617176181761917620176211762217623176241762517626176271762817629176301763117632176331763417635176361763717638176391764017641176421764317644176451764617647176481764917650176511765217653176541765517656176571765817659176601766117662176631766417665176661766717668176691767017671176721767317674176751767617677176781767917680176811768217683176841768517686176871768817689176901769117692176931769417695176961769717698176991770017701177021770317704177051770617707177081770917710177111771217713177141771517716177171771817719177201772117722177231772417725177261772717728177291773017731177321773317734177351773617737177381773917740177411774217743177441774517746177471774817749177501775117752177531775417755177561775717758177591776017761177621776317764177651776617767177681776917770177711777217773177741777517776177771777817779177801778117782177831778417785177861778717788177891779017791177921779317794177951779617797177981779917800178011780217803178041780517806178071780817809178101781117812178131781417815178161781717818178191782017821178221782317824178251782617827178281782917830178311783217833178341783517836178371783817839178401784117842178431784417845178461784717848178491785017851178521785317854178551785617857178581785917860178611786217863178641786517866178671786817869178701787117872178731787417875178761787717878178791788017881178821788317884178851788617887178881788917890178911789217893178941789517896178971789817899179001790117902179031790417905179061790717908179091791017911179121791317914179151791617917179181791917920179211792217923179241792517926179271792817929179301793117932179331793417935179361793717938179391794017941179421794317944179451794617947179481794917950179511795217953179541795517956179571795817959179601796117962179631796417965179661796717968179691797017971179721797317974179751797617977179781797917980179811798217983179841798517986179871798817989179901799117992179931799417995179961799717998179991800018001180021800318004180051800618007180081800918010180111801218013180141801518016180171801818019180201802118022180231802418025180261802718028180291803018031180321803318034180351803618037180381803918040180411804218043180441804518046180471804818049180501805118052180531805418055180561805718058180591806018061180621806318064180651806618067180681806918070180711807218073180741807518076180771807818079180801808118082180831808418085180861808718088180891809018091180921809318094180951809618097180981809918100181011810218103181041810518106181071810818109181101811118112181131811418115181161811718118181191812018121181221812318124181251812618127181281812918130181311813218133181341813518136181371813818139181401814118142181431814418145181461814718148181491815018151181521815318154181551815618157181581815918160181611816218163181641816518166181671816818169181701817118172181731817418175181761817718178181791818018181181821818318184181851818618187181881818918190181911819218193181941819518196181971819818199182001820118202182031820418205182061820718208182091821018211182121821318214182151821618217182181821918220182211822218223182241822518226182271822818229182301823118232182331823418235182361823718238182391824018241182421824318244182451824618247182481824918250182511825218253182541825518256182571825818259182601826118262182631826418265182661826718268182691827018271182721827318274182751827618277182781827918280182811828218283182841828518286182871828818289182901829118292182931829418295182961829718298182991830018301183021830318304183051830618307183081830918310183111831218313183141831518316183171831818319183201832118322183231832418325183261832718328183291833018331183321833318334183351833618337183381833918340183411834218343183441834518346183471834818349183501835118352183531835418355183561835718358183591836018361183621836318364183651836618367183681836918370183711837218373183741837518376183771837818379183801838118382183831838418385183861838718388183891839018391183921839318394183951839618397183981839918400184011840218403184041840518406184071840818409184101841118412184131841418415184161841718418184191842018421184221842318424184251842618427184281842918430184311843218433184341843518436184371843818439184401844118442184431844418445184461844718448184491845018451184521845318454184551845618457184581845918460184611846218463184641846518466184671846818469184701847118472184731847418475184761847718478184791848018481184821848318484184851848618487184881848918490184911849218493184941849518496184971849818499185001850118502185031850418505185061850718508185091851018511185121851318514185151851618517185181851918520185211852218523185241852518526185271852818529185301853118532185331853418535185361853718538185391854018541185421854318544185451854618547185481854918550185511855218553185541855518556185571855818559185601856118562185631856418565185661856718568185691857018571185721857318574185751857618577185781857918580185811858218583185841858518586185871858818589185901859118592185931859418595185961859718598185991860018601186021860318604186051860618607186081860918610186111861218613186141861518616186171861818619186201862118622186231862418625186261862718628186291863018631186321863318634186351863618637186381863918640186411864218643186441864518646186471864818649186501865118652186531865418655186561865718658186591866018661186621866318664186651866618667186681866918670186711867218673186741867518676186771867818679186801868118682186831868418685186861868718688186891869018691186921869318694186951869618697186981869918700187011870218703187041870518706187071870818709187101871118712187131871418715187161871718718187191872018721187221872318724187251872618727187281872918730187311873218733187341873518736187371873818739187401874118742187431874418745187461874718748187491875018751187521875318754187551875618757187581875918760187611876218763187641876518766187671876818769187701877118772187731877418775187761877718778187791878018781187821878318784187851878618787187881878918790187911879218793187941879518796187971879818799188001880118802188031880418805188061880718808188091881018811188121881318814188151881618817188181881918820188211882218823188241882518826188271882818829188301883118832188331883418835188361883718838188391884018841188421884318844188451884618847188481884918850188511885218853188541885518856188571885818859188601886118862188631886418865188661886718868188691887018871188721887318874188751887618877188781887918880188811888218883188841888518886188871888818889188901889118892188931889418895188961889718898188991890018901189021890318904189051890618907189081890918910189111891218913189141891518916189171891818919189201892118922189231892418925189261892718928189291893018931189321893318934189351893618937189381893918940189411894218943189441894518946189471894818949189501895118952189531895418955189561895718958189591896018961189621896318964189651896618967189681896918970189711897218973189741897518976189771897818979189801898118982189831898418985189861898718988189891899018991189921899318994189951899618997189981899919000190011900219003190041900519006190071900819009190101901119012190131901419015190161901719018190191902019021190221902319024190251902619027190281902919030190311903219033190341903519036190371903819039190401904119042190431904419045190461904719048190491905019051190521905319054190551905619057190581905919060190611906219063190641906519066190671906819069190701907119072190731907419075190761907719078190791908019081190821908319084190851908619087190881908919090190911909219093190941909519096190971909819099191001910119102191031910419105191061910719108191091911019111191121911319114191151911619117191181911919120191211912219123191241912519126191271912819129191301913119132191331913419135191361913719138191391914019141191421914319144191451914619147191481914919150191511915219153191541915519156191571915819159191601916119162191631916419165191661916719168191691917019171191721917319174191751917619177191781917919180191811918219183191841918519186191871918819189191901919119192191931919419195191961919719198191991920019201192021920319204192051920619207192081920919210192111921219213192141921519216192171921819219192201922119222192231922419225192261922719228192291923019231192321923319234192351923619237192381923919240192411924219243192441924519246192471924819249192501925119252192531925419255192561925719258192591926019261192621926319264192651926619267192681926919270192711927219273192741927519276192771927819279192801928119282192831928419285192861928719288192891929019291192921929319294192951929619297192981929919300193011930219303193041930519306193071930819309193101931119312193131931419315193161931719318193191932019321193221932319324193251932619327193281932919330193311933219333193341933519336193371933819339193401934119342193431934419345193461934719348193491935019351193521935319354193551935619357193581935919360193611936219363193641936519366193671936819369193701937119372193731937419375193761937719378193791938019381193821938319384193851938619387193881938919390193911939219393193941939519396193971939819399194001940119402194031940419405194061940719408194091941019411194121941319414194151941619417194181941919420194211942219423194241942519426194271942819429194301943119432194331943419435194361943719438194391944019441194421944319444194451944619447194481944919450194511945219453194541945519456194571945819459194601946119462194631946419465194661946719468194691947019471194721947319474194751947619477194781947919480194811948219483194841948519486194871948819489194901949119492194931949419495194961949719498194991950019501195021950319504195051950619507195081950919510195111951219513195141951519516195171951819519195201952119522195231952419525195261952719528195291953019531195321953319534195351953619537195381953919540195411954219543195441954519546195471954819549195501955119552195531955419555195561955719558195591956019561195621956319564195651956619567195681956919570195711957219573195741957519576195771957819579195801958119582195831958419585195861958719588195891959019591195921959319594195951959619597195981959919600196011960219603196041960519606196071960819609196101961119612196131961419615196161961719618196191962019621196221962319624196251962619627196281962919630196311963219633196341963519636196371963819639196401964119642196431964419645196461964719648196491965019651196521965319654196551965619657196581965919660196611966219663196641966519666196671966819669196701967119672196731967419675196761967719678196791968019681196821968319684196851968619687196881968919690196911969219693196941969519696196971969819699197001970119702197031970419705197061970719708197091971019711197121971319714197151971619717197181971919720197211972219723197241972519726197271972819729197301973119732197331973419735197361973719738197391974019741197421974319744197451974619747197481974919750197511975219753197541975519756197571975819759197601976119762197631976419765197661976719768197691977019771197721977319774197751977619777197781977919780197811978219783197841978519786197871978819789197901979119792197931979419795197961979719798197991980019801198021980319804198051980619807198081980919810198111981219813198141981519816198171981819819198201982119822198231982419825198261982719828198291983019831198321983319834198351983619837198381983919840198411984219843198441984519846198471984819849198501985119852198531985419855198561985719858198591986019861198621986319864198651986619867198681986919870198711987219873198741987519876198771987819879198801988119882198831988419885198861988719888198891989019891198921989319894198951989619897198981989919900199011990219903199041990519906199071990819909199101991119912199131991419915199161991719918199191992019921199221992319924199251992619927199281992919930199311993219933199341993519936199371993819939199401994119942199431994419945199461994719948199491995019951199521995319954199551995619957199581995919960199611996219963199641996519966199671996819969199701997119972199731997419975199761997719978199791998019981199821998319984199851998619987199881998919990199911999219993199941999519996199971999819999200002000120002200032000420005200062000720008200092001020011200122001320014200152001620017200182001920020200212002220023200242002520026200272002820029200302003120032200332003420035200362003720038200392004020041200422004320044200452004620047200482004920050200512005220053200542005520056200572005820059200602006120062200632006420065200662006720068200692007020071200722007320074200752007620077200782007920080200812008220083200842008520086200872008820089200902009120092200932009420095200962009720098200992010020101201022010320104201052010620107201082010920110201112011220113201142011520116201172011820119201202012120122201232012420125201262012720128201292013020131201322013320134201352013620137201382013920140201412014220143201442014520146201472014820149201502015120152201532015420155201562015720158201592016020161201622016320164201652016620167201682016920170201712017220173201742017520176201772017820179201802018120182201832018420185201862018720188201892019020191201922019320194201952019620197201982019920200202012020220203202042020520206202072020820209202102021120212202132021420215202162021720218202192022020221202222022320224202252022620227202282022920230202312023220233202342023520236202372023820239202402024120242202432024420245202462024720248202492025020251202522025320254202552025620257202582025920260202612026220263202642026520266202672026820269202702027120272202732027420275202762027720278202792028020281202822028320284202852028620287202882028920290202912029220293202942029520296202972029820299203002030120302203032030420305203062030720308203092031020311203122031320314203152031620317203182031920320203212032220323203242032520326203272032820329203302033120332203332033420335203362033720338203392034020341203422034320344203452034620347203482034920350203512035220353203542035520356203572035820359203602036120362203632036420365203662036720368203692037020371203722037320374203752037620377203782037920380203812038220383203842038520386203872038820389203902039120392203932039420395203962039720398203992040020401204022040320404204052040620407204082040920410204112041220413204142041520416204172041820419204202042120422204232042420425204262042720428204292043020431204322043320434204352043620437204382043920440204412044220443204442044520446204472044820449204502045120452204532045420455204562045720458204592046020461204622046320464204652046620467204682046920470204712047220473204742047520476204772047820479204802048120482204832048420485204862048720488204892049020491204922049320494204952049620497204982049920500205012050220503205042050520506205072050820509205102051120512205132051420515205162051720518205192052020521205222052320524205252052620527205282052920530205312053220533205342053520536205372053820539205402054120542205432054420545205462054720548205492055020551205522055320554205552055620557205582055920560205612056220563205642056520566205672056820569205702057120572205732057420575205762057720578205792058020581205822058320584205852058620587205882058920590205912059220593205942059520596205972059820599206002060120602206032060420605206062060720608206092061020611206122061320614206152061620617206182061920620206212062220623206242062520626206272062820629206302063120632206332063420635206362063720638206392064020641206422064320644206452064620647206482064920650206512065220653206542065520656206572065820659206602066120662206632066420665206662066720668206692067020671206722067320674206752067620677206782067920680206812068220683206842068520686206872068820689206902069120692206932069420695206962069720698206992070020701207022070320704207052070620707207082070920710207112071220713207142071520716207172071820719207202072120722207232072420725207262072720728207292073020731207322073320734207352073620737207382073920740207412074220743207442074520746207472074820749207502075120752207532075420755207562075720758207592076020761207622076320764207652076620767207682076920770207712077220773207742077520776207772077820779207802078120782207832078420785207862078720788207892079020791207922079320794207952079620797207982079920800208012080220803208042080520806208072080820809208102081120812208132081420815208162081720818208192082020821208222082320824208252082620827208282082920830208312083220833208342083520836208372083820839208402084120842208432084420845208462084720848208492085020851208522085320854208552085620857208582085920860208612086220863208642086520866208672086820869208702087120872208732087420875208762087720878208792088020881208822088320884208852088620887208882088920890208912089220893208942089520896208972089820899209002090120902209032090420905209062090720908209092091020911209122091320914209152091620917209182091920920209212092220923209242092520926209272092820929209302093120932209332093420935209362093720938209392094020941209422094320944209452094620947209482094920950209512095220953209542095520956209572095820959209602096120962209632096420965209662096720968209692097020971209722097320974209752097620977209782097920980209812098220983209842098520986209872098820989209902099120992209932099420995209962099720998209992100021001210022100321004210052100621007210082100921010210112101221013210142101521016210172101821019210202102121022210232102421025210262102721028210292103021031210322103321034210352103621037210382103921040210412104221043210442104521046210472104821049210502105121052210532105421055210562105721058210592106021061210622106321064210652106621067210682106921070210712107221073210742107521076210772107821079210802108121082210832108421085210862108721088210892109021091210922109321094210952109621097210982109921100211012110221103211042110521106211072110821109211102111121112211132111421115211162111721118211192112021121211222112321124211252112621127211282112921130211312113221133211342113521136211372113821139211402114121142211432114421145211462114721148211492115021151211522115321154211552115621157211582115921160211612116221163211642116521166211672116821169211702117121172211732117421175211762117721178211792118021181211822118321184211852118621187211882118921190211912119221193211942119521196211972119821199212002120121202212032120421205212062120721208212092121021211212122121321214212152121621217212182121921220212212122221223212242122521226212272122821229212302123121232212332123421235212362123721238212392124021241212422124321244212452124621247212482124921250212512125221253212542125521256212572125821259212602126121262212632126421265212662126721268212692127021271212722127321274212752127621277212782127921280212812128221283212842128521286212872128821289212902129121292212932129421295212962129721298212992130021301213022130321304213052130621307213082130921310213112131221313213142131521316213172131821319213202132121322213232132421325213262132721328213292133021331213322133321334213352133621337213382133921340213412134221343213442134521346213472134821349213502135121352213532135421355213562135721358213592136021361213622136321364213652136621367213682136921370213712137221373213742137521376213772137821379213802138121382213832138421385213862138721388213892139021391213922139321394213952139621397213982139921400214012140221403214042140521406214072140821409214102141121412214132141421415214162141721418214192142021421214222142321424214252142621427214282142921430214312143221433214342143521436214372143821439214402144121442214432144421445214462144721448214492145021451214522145321454214552145621457214582145921460214612146221463214642146521466214672146821469214702147121472214732147421475214762147721478214792148021481214822148321484214852148621487214882148921490214912149221493214942149521496214972149821499215002150121502215032150421505215062150721508215092151021511215122151321514215152151621517215182151921520215212152221523215242152521526215272152821529215302153121532215332153421535215362153721538215392154021541215422154321544215452154621547215482154921550215512155221553215542155521556215572155821559215602156121562215632156421565215662156721568215692157021571215722157321574215752157621577215782157921580215812158221583215842158521586215872158821589215902159121592215932159421595215962159721598215992160021601216022160321604216052160621607216082160921610216112161221613216142161521616216172161821619216202162121622216232162421625216262162721628216292163021631216322163321634216352163621637216382163921640216412164221643216442164521646216472164821649216502165121652216532165421655216562165721658216592166021661216622166321664216652166621667216682166921670216712167221673216742167521676216772167821679216802168121682216832168421685216862168721688216892169021691216922169321694216952169621697216982169921700217012170221703217042170521706217072170821709217102171121712217132171421715217162171721718217192172021721217222172321724217252172621727217282172921730217312173221733217342173521736217372173821739217402174121742217432174421745217462174721748217492175021751217522175321754217552175621757217582175921760217612176221763217642176521766217672176821769217702177121772217732177421775217762177721778217792178021781217822178321784217852178621787217882178921790217912179221793217942179521796217972179821799218002180121802218032180421805218062180721808218092181021811218122181321814218152181621817218182181921820218212182221823218242182521826218272182821829218302183121832218332183421835218362183721838218392184021841218422184321844218452184621847218482184921850218512185221853218542185521856218572185821859218602186121862218632186421865218662186721868218692187021871218722187321874218752187621877218782187921880218812188221883218842188521886218872188821889218902189121892218932189421895218962189721898218992190021901219022190321904219052190621907219082190921910219112191221913219142191521916219172191821919219202192121922219232192421925219262192721928219292193021931219322193321934219352193621937219382193921940219412194221943219442194521946219472194821949219502195121952219532195421955219562195721958219592196021961219622196321964219652196621967219682196921970219712197221973219742197521976219772197821979219802198121982219832198421985219862198721988219892199021991219922199321994219952199621997219982199922000220012200222003220042200522006220072200822009220102201122012220132201422015220162201722018220192202022021220222202322024220252202622027220282202922030220312203222033220342203522036220372203822039220402204122042220432204422045220462204722048220492205022051220522205322054220552205622057220582205922060220612206222063220642206522066220672206822069220702207122072220732207422075220762207722078220792208022081220822208322084220852208622087220882208922090220912209222093220942209522096220972209822099221002210122102221032210422105221062210722108221092211022111221122211322114221152211622117221182211922120221212212222123221242212522126221272212822129221302213122132221332213422135221362213722138221392214022141221422214322144221452214622147221482214922150221512215222153221542215522156221572215822159221602216122162221632216422165221662216722168221692217022171221722217322174221752217622177221782217922180221812218222183221842218522186221872218822189221902219122192221932219422195221962219722198221992220022201222022220322204222052220622207222082220922210222112221222213222142221522216222172221822219222202222122222222232222422225222262222722228222292223022231222322223322234222352223622237222382223922240222412224222243222442224522246222472224822249222502225122252222532225422255222562225722258222592226022261222622226322264222652226622267222682226922270222712227222273222742227522276222772227822279222802228122282222832228422285222862228722288222892229022291222922229322294222952229622297222982229922300223012230222303223042230522306223072230822309223102231122312223132231422315223162231722318223192232022321223222232322324223252232622327223282232922330223312233222333223342233522336223372233822339223402234122342223432234422345223462234722348223492235022351223522235322354223552235622357223582235922360223612236222363223642236522366223672236822369223702237122372223732237422375223762237722378223792238022381223822238322384223852238622387223882238922390223912239222393223942239522396223972239822399224002240122402224032240422405224062240722408224092241022411224122241322414224152241622417224182241922420224212242222423224242242522426224272242822429224302243122432224332243422435224362243722438224392244022441224422244322444224452244622447224482244922450224512245222453224542245522456224572245822459224602246122462224632246422465224662246722468224692247022471224722247322474224752247622477224782247922480224812248222483224842248522486224872248822489224902249122492224932249422495224962249722498224992250022501225022250322504225052250622507225082250922510225112251222513225142251522516225172251822519225202252122522225232252422525225262252722528225292253022531225322253322534225352253622537225382253922540225412254222543225442254522546225472254822549225502255122552225532255422555225562255722558225592256022561225622256322564225652256622567225682256922570225712257222573225742257522576225772257822579225802258122582225832258422585225862258722588225892259022591225922259322594225952259622597225982259922600226012260222603226042260522606226072260822609226102261122612226132261422615226162261722618226192262022621226222262322624226252262622627226282262922630226312263222633226342263522636226372263822639226402264122642226432264422645226462264722648226492265022651226522265322654226552265622657226582265922660226612266222663226642266522666226672266822669226702267122672226732267422675226762267722678226792268022681226822268322684226852268622687226882268922690226912269222693226942269522696226972269822699227002270122702227032270422705227062270722708227092271022711227122271322714227152271622717227182271922720227212272222723227242272522726227272272822729227302273122732227332273422735227362273722738227392274022741227422274322744227452274622747227482274922750227512275222753227542275522756227572275822759227602276122762227632276422765227662276722768227692277022771227722277322774227752277622777227782277922780227812278222783227842278522786227872278822789227902279122792227932279422795227962279722798227992280022801228022280322804228052280622807228082280922810228112281222813228142281522816228172281822819228202282122822228232282422825228262282722828228292283022831228322283322834228352283622837228382283922840228412284222843228442284522846228472284822849228502285122852228532285422855228562285722858228592286022861228622286322864228652286622867228682286922870228712287222873228742287522876228772287822879228802288122882228832288422885228862288722888228892289022891228922289322894228952289622897228982289922900229012290222903229042290522906229072290822909229102291122912229132291422915229162291722918229192292022921229222292322924229252292622927229282292922930229312293222933229342293522936229372293822939229402294122942229432294422945229462294722948229492295022951229522295322954229552295622957229582295922960229612296222963229642296522966229672296822969229702297122972229732297422975229762297722978229792298022981229822298322984229852298622987229882298922990229912299222993229942299522996229972299822999230002300123002230032300423005230062300723008230092301023011230122301323014230152301623017230182301923020230212302223023230242302523026230272302823029230302303123032230332303423035230362303723038230392304023041230422304323044230452304623047230482304923050230512305223053230542305523056230572305823059230602306123062230632306423065230662306723068230692307023071230722307323074230752307623077230782307923080230812308223083230842308523086230872308823089230902309123092230932309423095230962309723098230992310023101231022310323104231052310623107231082310923110231112311223113231142311523116231172311823119231202312123122231232312423125231262312723128231292313023131231322313323134231352313623137231382313923140231412314223143231442314523146231472314823149231502315123152231532315423155231562315723158231592316023161231622316323164231652316623167231682316923170231712317223173231742317523176231772317823179231802318123182231832318423185231862318723188231892319023191231922319323194231952319623197231982319923200232012320223203232042320523206232072320823209232102321123212232132321423215232162321723218232192322023221232222322323224232252322623227232282322923230232312323223233232342323523236232372323823239232402324123242232432324423245232462324723248232492325023251232522325323254232552325623257232582325923260232612326223263232642326523266232672326823269232702327123272232732327423275232762327723278232792328023281232822328323284232852328623287232882328923290232912329223293232942329523296232972329823299233002330123302233032330423305233062330723308233092331023311233122331323314233152331623317233182331923320233212332223323233242332523326233272332823329233302333123332233332333423335233362333723338233392334023341233422334323344233452334623347233482334923350233512335223353233542335523356233572335823359233602336123362233632336423365233662336723368233692337023371233722337323374233752337623377233782337923380233812338223383233842338523386233872338823389233902339123392233932339423395233962339723398233992340023401234022340323404234052340623407234082340923410234112341223413234142341523416234172341823419234202342123422234232342423425234262342723428234292343023431234322343323434234352343623437234382343923440234412344223443234442344523446234472344823449234502345123452234532345423455234562345723458234592346023461234622346323464234652346623467234682346923470234712347223473234742347523476234772347823479234802348123482234832348423485234862348723488234892349023491234922349323494234952349623497234982349923500235012350223503235042350523506235072350823509235102351123512235132351423515235162351723518235192352023521235222352323524235252352623527235282352923530235312353223533235342353523536235372353823539235402354123542235432354423545235462354723548235492355023551235522355323554235552355623557235582355923560235612356223563235642356523566235672356823569235702357123572235732357423575235762357723578235792358023581235822358323584235852358623587235882358923590235912359223593235942359523596235972359823599236002360123602236032360423605236062360723608236092361023611236122361323614236152361623617236182361923620236212362223623236242362523626236272362823629236302363123632236332363423635236362363723638236392364023641236422364323644236452364623647236482364923650236512365223653236542365523656236572365823659236602366123662236632366423665236662366723668236692367023671236722367323674236752367623677236782367923680236812368223683236842368523686236872368823689236902369123692236932369423695236962369723698236992370023701237022370323704237052370623707237082370923710237112371223713237142371523716237172371823719237202372123722237232372423725237262372723728237292373023731237322373323734237352373623737237382373923740237412374223743237442374523746237472374823749237502375123752237532375423755237562375723758237592376023761237622376323764237652376623767237682376923770237712377223773237742377523776237772377823779237802378123782237832378423785237862378723788237892379023791237922379323794237952379623797237982379923800238012380223803238042380523806238072380823809238102381123812238132381423815238162381723818238192382023821238222382323824238252382623827238282382923830238312383223833238342383523836238372383823839238402384123842238432384423845238462384723848238492385023851238522385323854238552385623857238582385923860238612386223863238642386523866238672386823869238702387123872238732387423875238762387723878238792388023881238822388323884238852388623887238882388923890238912389223893238942389523896238972389823899239002390123902239032390423905239062390723908239092391023911239122391323914239152391623917239182391923920239212392223923239242392523926239272392823929239302393123932239332393423935239362393723938239392394023941239422394323944239452394623947239482394923950239512395223953239542395523956239572395823959239602396123962239632396423965239662396723968239692397023971239722397323974239752397623977239782397923980239812398223983239842398523986239872398823989239902399123992239932399423995239962399723998239992400024001240022400324004240052400624007240082400924010240112401224013240142401524016240172401824019240202402124022240232402424025240262402724028240292403024031240322403324034240352403624037240382403924040240412404224043240442404524046240472404824049240502405124052240532405424055240562405724058240592406024061240622406324064240652406624067240682406924070240712407224073240742407524076240772407824079240802408124082240832408424085240862408724088240892409024091240922409324094240952409624097240982409924100241012410224103241042410524106241072410824109241102411124112241132411424115241162411724118241192412024121241222412324124241252412624127241282412924130241312413224133241342413524136241372413824139241402414124142241432414424145241462414724148241492415024151241522415324154241552415624157241582415924160241612416224163241642416524166241672416824169241702417124172241732417424175241762417724178241792418024181241822418324184241852418624187241882418924190241912419224193241942419524196241972419824199242002420124202242032420424205242062420724208242092421024211242122421324214242152421624217242182421924220242212422224223242242422524226242272422824229242302423124232242332423424235242362423724238242392424024241242422424324244242452424624247242482424924250242512425224253242542425524256242572425824259242602426124262242632426424265242662426724268242692427024271242722427324274242752427624277242782427924280242812428224283242842428524286242872428824289242902429124292242932429424295242962429724298242992430024301243022430324304243052430624307243082430924310243112431224313243142431524316243172431824319243202432124322243232432424325243262432724328243292433024331243322433324334243352433624337243382433924340243412434224343243442434524346243472434824349243502435124352243532435424355243562435724358243592436024361243622436324364243652436624367243682436924370243712437224373243742437524376243772437824379243802438124382243832438424385243862438724388243892439024391243922439324394243952439624397243982439924400244012440224403244042440524406244072440824409244102441124412244132441424415244162441724418244192442024421244222442324424244252442624427244282442924430244312443224433244342443524436244372443824439244402444124442244432444424445244462444724448244492445024451244522445324454244552445624457244582445924460244612446224463244642446524466244672446824469244702447124472244732447424475244762447724478244792448024481244822448324484244852448624487244882448924490244912449224493244942449524496244972449824499245002450124502245032450424505245062450724508245092451024511245122451324514245152451624517245182451924520245212452224523245242452524526245272452824529245302453124532245332453424535245362453724538245392454024541245422454324544245452454624547245482454924550245512455224553245542455524556245572455824559245602456124562245632456424565245662456724568245692457024571245722457324574245752457624577245782457924580245812458224583245842458524586245872458824589245902459124592245932459424595245962459724598245992460024601246022460324604246052460624607246082460924610246112461224613246142461524616246172461824619246202462124622246232462424625246262462724628246292463024631246322463324634246352463624637246382463924640246412464224643246442464524646246472464824649246502465124652246532465424655246562465724658246592466024661246622466324664246652466624667246682466924670246712467224673246742467524676246772467824679246802468124682246832468424685246862468724688246892469024691246922469324694246952469624697246982469924700247012470224703247042470524706247072470824709247102471124712247132471424715247162471724718247192472024721247222472324724247252472624727247282472924730247312473224733247342473524736247372473824739247402474124742247432474424745247462474724748247492475024751247522475324754247552475624757247582475924760247612476224763247642476524766247672476824769247702477124772247732477424775247762477724778247792478024781247822478324784247852478624787247882478924790247912479224793247942479524796247972479824799248002480124802248032480424805248062480724808248092481024811248122481324814248152481624817248182481924820248212482224823248242482524826248272482824829248302483124832248332483424835248362483724838248392484024841248422484324844248452484624847248482484924850248512485224853248542485524856248572485824859248602486124862248632486424865248662486724868248692487024871248722487324874248752487624877248782487924880248812488224883248842488524886248872488824889248902489124892248932489424895248962489724898248992490024901249022490324904249052490624907249082490924910249112491224913249142491524916249172491824919249202492124922249232492424925249262492724928249292493024931249322493324934249352493624937249382493924940249412494224943249442494524946249472494824949249502495124952249532495424955249562495724958249592496024961249622496324964249652496624967249682496924970249712497224973249742497524976249772497824979249802498124982249832498424985249862498724988249892499024991249922499324994249952499624997249982499925000250012500225003250042500525006250072500825009250102501125012250132501425015250162501725018250192502025021250222502325024250252502625027250282502925030250312503225033250342503525036250372503825039250402504125042250432504425045250462504725048250492505025051250522505325054250552505625057250582505925060250612506225063250642506525066250672506825069250702507125072250732507425075250762507725078250792508025081250822508325084250852508625087250882508925090250912509225093250942509525096250972509825099251002510125102251032510425105251062510725108251092511025111251122511325114251152511625117251182511925120251212512225123251242512525126251272512825129251302513125132251332513425135251362513725138251392514025141251422514325144251452514625147251482514925150251512515225153251542515525156251572515825159251602516125162251632516425165251662516725168251692517025171251722517325174251752517625177251782517925180251812518225183251842518525186251872518825189251902519125192251932519425195251962519725198251992520025201252022520325204252052520625207252082520925210252112521225213252142521525216252172521825219252202522125222252232522425225252262522725228252292523025231252322523325234252352523625237252382523925240252412524225243252442524525246252472524825249252502525125252252532525425255252562525725258252592526025261252622526325264252652526625267252682526925270252712527225273252742527525276252772527825279252802528125282252832528425285252862528725288252892529025291252922529325294252952529625297252982529925300253012530225303253042530525306253072530825309253102531125312253132531425315253162531725318253192532025321253222532325324253252532625327253282532925330253312533225333253342533525336253372533825339253402534125342253432534425345253462534725348253492535025351253522535325354253552535625357253582535925360253612536225363253642536525366253672536825369253702537125372253732537425375253762537725378253792538025381253822538325384253852538625387253882538925390253912539225393253942539525396253972539825399254002540125402254032540425405254062540725408254092541025411254122541325414254152541625417254182541925420254212542225423254242542525426254272542825429254302543125432254332543425435254362543725438254392544025441254422544325444254452544625447254482544925450254512545225453254542545525456254572545825459254602546125462254632546425465254662546725468254692547025471254722547325474254752547625477254782547925480254812548225483254842548525486254872548825489254902549125492254932549425495254962549725498254992550025501255022550325504255052550625507255082550925510255112551225513255142551525516255172551825519255202552125522255232552425525255262552725528255292553025531255322553325534255352553625537255382553925540255412554225543255442554525546255472554825549255502555125552255532555425555255562555725558255592556025561255622556325564255652556625567255682556925570255712557225573255742557525576255772557825579255802558125582255832558425585255862558725588255892559025591255922559325594255952559625597255982559925600256012560225603256042560525606256072560825609256102561125612256132561425615256162561725618256192562025621256222562325624256252562625627256282562925630256312563225633256342563525636256372563825639256402564125642256432564425645256462564725648256492565025651256522565325654256552565625657256582565925660256612566225663256642566525666256672566825669256702567125672256732567425675256762567725678256792568025681256822568325684256852568625687256882568925690256912569225693256942569525696256972569825699257002570125702257032570425705257062570725708257092571025711257122571325714257152571625717257182571925720257212572225723257242572525726257272572825729257302573125732257332573425735257362573725738257392574025741257422574325744257452574625747257482574925750257512575225753257542575525756257572575825759257602576125762257632576425765257662576725768257692577025771257722577325774257752577625777257782577925780257812578225783257842578525786257872578825789257902579125792257932579425795257962579725798257992580025801258022580325804258052580625807258082580925810258112581225813258142581525816258172581825819258202582125822258232582425825258262582725828258292583025831258322583325834258352583625837258382583925840258412584225843258442584525846258472584825849258502585125852258532585425855258562585725858258592586025861258622586325864258652586625867258682586925870258712587225873258742587525876258772587825879258802588125882258832588425885258862588725888258892589025891258922589325894258952589625897258982589925900259012590225903259042590525906259072590825909259102591125912259132591425915259162591725918259192592025921259222592325924259252592625927259282592925930259312593225933259342593525936259372593825939259402594125942259432594425945259462594725948259492595025951259522595325954259552595625957259582595925960259612596225963259642596525966259672596825969259702597125972259732597425975259762597725978259792598025981259822598325984259852598625987259882598925990259912599225993259942599525996259972599825999260002600126002260032600426005260062600726008260092601026011260122601326014260152601626017260182601926020260212602226023260242602526026260272602826029260302603126032260332603426035260362603726038260392604026041260422604326044260452604626047260482604926050260512605226053260542605526056260572605826059260602606126062260632606426065260662606726068260692607026071260722607326074260752607626077260782607926080260812608226083260842608526086260872608826089260902609126092260932609426095260962609726098260992610026101261022610326104261052610626107261082610926110261112611226113261142611526116261172611826119261202612126122261232612426125261262612726128261292613026131261322613326134261352613626137261382613926140261412614226143261442614526146261472614826149261502615126152261532615426155261562615726158261592616026161261622616326164261652616626167261682616926170261712617226173261742617526176261772617826179261802618126182261832618426185261862618726188261892619026191261922619326194261952619626197261982619926200262012620226203262042620526206262072620826209262102621126212262132621426215262162621726218262192622026221262222622326224262252622626227262282622926230262312623226233262342623526236262372623826239262402624126242262432624426245262462624726248262492625026251262522625326254262552625626257262582625926260262612626226263262642626526266262672626826269262702627126272262732627426275262762627726278262792628026281262822628326284262852628626287262882628926290262912629226293262942629526296262972629826299263002630126302263032630426305263062630726308263092631026311263122631326314263152631626317263182631926320263212632226323263242632526326263272632826329263302633126332263332633426335263362633726338263392634026341263422634326344263452634626347263482634926350263512635226353263542635526356263572635826359263602636126362263632636426365263662636726368263692637026371263722637326374263752637626377263782637926380263812638226383263842638526386263872638826389263902639126392263932639426395263962639726398263992640026401264022640326404264052640626407264082640926410264112641226413264142641526416264172641826419264202642126422264232642426425264262642726428264292643026431264322643326434264352643626437264382643926440264412644226443264442644526446264472644826449264502645126452264532645426455264562645726458264592646026461264622646326464264652646626467264682646926470264712647226473264742647526476264772647826479264802648126482264832648426485264862648726488264892649026491264922649326494264952649626497264982649926500265012650226503265042650526506265072650826509265102651126512265132651426515265162651726518265192652026521265222652326524265252652626527265282652926530265312653226533265342653526536265372653826539265402654126542265432654426545265462654726548265492655026551265522655326554265552655626557265582655926560265612656226563265642656526566265672656826569265702657126572265732657426575265762657726578265792658026581265822658326584265852658626587265882658926590265912659226593265942659526596265972659826599266002660126602266032660426605266062660726608266092661026611266122661326614266152661626617266182661926620266212662226623266242662526626266272662826629266302663126632266332663426635266362663726638266392664026641266422664326644266452664626647266482664926650266512665226653266542665526656266572665826659266602666126662266632666426665266662666726668266692667026671266722667326674266752667626677266782667926680266812668226683266842668526686266872668826689266902669126692266932669426695266962669726698266992670026701267022670326704267052670626707267082670926710267112671226713267142671526716267172671826719267202672126722267232672426725267262672726728267292673026731267322673326734267352673626737267382673926740267412674226743267442674526746267472674826749267502675126752267532675426755267562675726758267592676026761267622676326764267652676626767267682676926770267712677226773267742677526776267772677826779267802678126782267832678426785267862678726788267892679026791267922679326794267952679626797267982679926800268012680226803268042680526806268072680826809268102681126812268132681426815268162681726818268192682026821268222682326824268252682626827268282682926830268312683226833268342683526836268372683826839268402684126842268432684426845268462684726848268492685026851268522685326854268552685626857268582685926860268612686226863268642686526866268672686826869268702687126872268732687426875268762687726878268792688026881268822688326884268852688626887268882688926890268912689226893268942689526896268972689826899269002690126902269032690426905269062690726908269092691026911269122691326914269152691626917269182691926920269212692226923269242692526926269272692826929269302693126932269332693426935269362693726938269392694026941269422694326944269452694626947269482694926950269512695226953269542695526956269572695826959269602696126962269632696426965269662696726968269692697026971269722697326974269752697626977269782697926980269812698226983269842698526986269872698826989269902699126992269932699426995269962699726998269992700027001270022700327004270052700627007270082700927010270112701227013270142701527016270172701827019270202702127022270232702427025270262702727028270292703027031270322703327034270352703627037270382703927040270412704227043270442704527046270472704827049270502705127052270532705427055270562705727058270592706027061270622706327064270652706627067270682706927070270712707227073270742707527076270772707827079270802708127082270832708427085270862708727088270892709027091270922709327094270952709627097270982709927100271012710227103271042710527106271072710827109271102711127112271132711427115271162711727118271192712027121271222712327124271252712627127271282712927130271312713227133271342713527136271372713827139271402714127142271432714427145271462714727148271492715027151271522715327154271552715627157271582715927160271612716227163271642716527166271672716827169271702717127172271732717427175271762717727178271792718027181271822718327184271852718627187271882718927190271912719227193271942719527196271972719827199272002720127202272032720427205272062720727208272092721027211272122721327214272152721627217272182721927220272212722227223272242722527226272272722827229272302723127232272332723427235272362723727238272392724027241272422724327244272452724627247272482724927250272512725227253272542725527256272572725827259272602726127262272632726427265272662726727268272692727027271272722727327274272752727627277272782727927280272812728227283272842728527286272872728827289272902729127292272932729427295272962729727298272992730027301273022730327304273052730627307273082730927310273112731227313273142731527316273172731827319273202732127322273232732427325273262732727328273292733027331273322733327334273352733627337273382733927340273412734227343273442734527346273472734827349273502735127352273532735427355273562735727358273592736027361273622736327364273652736627367273682736927370273712737227373273742737527376273772737827379273802738127382273832738427385273862738727388273892739027391273922739327394273952739627397273982739927400274012740227403274042740527406274072740827409274102741127412274132741427415274162741727418274192742027421274222742327424274252742627427274282742927430274312743227433274342743527436274372743827439274402744127442274432744427445274462744727448274492745027451274522745327454274552745627457274582745927460274612746227463274642746527466274672746827469274702747127472274732747427475274762747727478274792748027481274822748327484274852748627487274882748927490274912749227493274942749527496274972749827499275002750127502275032750427505275062750727508275092751027511275122751327514275152751627517275182751927520275212752227523275242752527526275272752827529275302753127532275332753427535275362753727538275392754027541275422754327544275452754627547275482754927550275512755227553275542755527556275572755827559275602756127562275632756427565275662756727568275692757027571275722757327574275752757627577275782757927580275812758227583275842758527586275872758827589275902759127592275932759427595275962759727598275992760027601276022760327604276052760627607276082760927610276112761227613276142761527616276172761827619276202762127622276232762427625276262762727628276292763027631276322763327634276352763627637276382763927640276412764227643276442764527646276472764827649276502765127652276532765427655276562765727658276592766027661276622766327664276652766627667276682766927670276712767227673276742767527676276772767827679276802768127682276832768427685276862768727688276892769027691276922769327694276952769627697276982769927700277012770227703277042770527706277072770827709277102771127712277132771427715277162771727718277192772027721277222772327724277252772627727277282772927730277312773227733277342773527736277372773827739277402774127742277432774427745277462774727748277492775027751277522775327754277552775627757277582775927760277612776227763277642776527766277672776827769277702777127772277732777427775277762777727778277792778027781277822778327784277852778627787277882778927790277912779227793277942779527796277972779827799278002780127802278032780427805278062780727808278092781027811278122781327814278152781627817278182781927820278212782227823278242782527826278272782827829278302783127832278332783427835278362783727838278392784027841278422784327844278452784627847278482784927850278512785227853278542785527856278572785827859278602786127862278632786427865278662786727868278692787027871278722787327874278752787627877278782787927880278812788227883278842788527886278872788827889278902789127892278932789427895278962789727898278992790027901279022790327904279052790627907279082790927910279112791227913279142791527916279172791827919279202792127922279232792427925279262792727928279292793027931279322793327934279352793627937279382793927940279412794227943279442794527946279472794827949279502795127952279532795427955279562795727958279592796027961279622796327964279652796627967279682796927970279712797227973279742797527976279772797827979279802798127982279832798427985279862798727988279892799027991279922799327994279952799627997279982799928000280012800228003280042800528006280072800828009280102801128012280132801428015280162801728018280192802028021280222802328024280252802628027280282802928030280312803228033280342803528036280372803828039280402804128042280432804428045280462804728048280492805028051280522805328054280552805628057280582805928060280612806228063280642806528066280672806828069280702807128072280732807428075280762807728078280792808028081280822808328084280852808628087280882808928090280912809228093280942809528096280972809828099281002810128102281032810428105281062810728108281092811028111281122811328114281152811628117281182811928120281212812228123281242812528126281272812828129281302813128132281332813428135281362813728138281392814028141281422814328144281452814628147281482814928150281512815228153281542815528156281572815828159281602816128162281632816428165281662816728168281692817028171281722817328174281752817628177281782817928180281812818228183281842818528186281872818828189281902819128192281932819428195281962819728198281992820028201282022820328204282052820628207282082820928210282112821228213282142821528216282172821828219282202822128222282232822428225282262822728228282292823028231282322823328234282352823628237282382823928240282412824228243282442824528246282472824828249282502825128252282532825428255282562825728258282592826028261282622826328264282652826628267282682826928270282712827228273282742827528276282772827828279282802828128282282832828428285282862828728288282892829028291282922829328294282952829628297282982829928300283012830228303283042830528306283072830828309283102831128312283132831428315283162831728318283192832028321283222832328324283252832628327283282832928330283312833228333283342833528336283372833828339283402834128342283432834428345283462834728348283492835028351283522835328354283552835628357283582835928360283612836228363283642836528366283672836828369283702837128372283732837428375283762837728378283792838028381283822838328384283852838628387283882838928390283912839228393283942839528396283972839828399284002840128402284032840428405284062840728408284092841028411284122841328414284152841628417284182841928420284212842228423284242842528426284272842828429284302843128432284332843428435284362843728438284392844028441284422844328444284452844628447284482844928450284512845228453284542845528456284572845828459284602846128462284632846428465284662846728468284692847028471284722847328474284752847628477284782847928480284812848228483284842848528486284872848828489284902849128492284932849428495284962849728498284992850028501285022850328504285052850628507285082850928510285112851228513285142851528516285172851828519285202852128522285232852428525285262852728528285292853028531285322853328534285352853628537285382853928540285412854228543285442854528546285472854828549285502855128552285532855428555285562855728558285592856028561285622856328564285652856628567285682856928570285712857228573285742857528576285772857828579285802858128582285832858428585285862858728588285892859028591285922859328594285952859628597285982859928600286012860228603286042860528606286072860828609286102861128612286132861428615286162861728618286192862028621286222862328624286252862628627286282862928630286312863228633286342863528636286372863828639286402864128642286432864428645286462864728648286492865028651286522865328654286552865628657286582865928660286612866228663286642866528666286672866828669286702867128672286732867428675286762867728678286792868028681286822868328684286852868628687286882868928690286912869228693286942869528696286972869828699287002870128702287032870428705287062870728708287092871028711287122871328714287152871628717287182871928720287212872228723287242872528726287272872828729287302873128732287332873428735287362873728738287392874028741287422874328744287452874628747287482874928750287512875228753287542875528756287572875828759287602876128762287632876428765287662876728768287692877028771287722877328774287752877628777287782877928780287812878228783287842878528786287872878828789287902879128792287932879428795287962879728798287992880028801288022880328804288052880628807288082880928810288112881228813288142881528816288172881828819288202882128822288232882428825288262882728828288292883028831288322883328834288352883628837288382883928840288412884228843288442884528846288472884828849288502885128852288532885428855288562885728858288592886028861288622886328864288652886628867288682886928870288712887228873288742887528876288772887828879288802888128882288832888428885288862888728888288892889028891288922889328894288952889628897288982889928900289012890228903289042890528906289072890828909289102891128912289132891428915289162891728918289192892028921289222892328924289252892628927289282892928930289312893228933289342893528936289372893828939289402894128942289432894428945289462894728948289492895028951289522895328954289552895628957289582895928960289612896228963289642896528966289672896828969289702897128972289732897428975289762897728978289792898028981289822898328984289852898628987289882898928990289912899228993289942899528996289972899828999290002900129002290032900429005290062900729008290092901029011290122901329014290152901629017290182901929020290212902229023290242902529026290272902829029290302903129032290332903429035290362903729038290392904029041290422904329044290452904629047290482904929050290512905229053290542905529056290572905829059290602906129062290632906429065290662906729068290692907029071290722907329074290752907629077290782907929080290812908229083290842908529086290872908829089290902909129092290932909429095290962909729098290992910029101291022910329104291052910629107291082910929110291112911229113291142911529116291172911829119291202912129122291232912429125291262912729128291292913029131291322913329134291352913629137291382913929140291412914229143291442914529146291472914829149291502915129152291532915429155291562915729158291592916029161291622916329164291652916629167291682916929170291712917229173291742917529176291772917829179291802918129182291832918429185291862918729188291892919029191291922919329194291952919629197291982919929200292012920229203292042920529206292072920829209292102921129212292132921429215292162921729218292192922029221292222922329224292252922629227292282922929230292312923229233292342923529236292372923829239292402924129242292432924429245292462924729248292492925029251292522925329254292552925629257292582925929260292612926229263292642926529266292672926829269292702927129272292732927429275292762927729278292792928029281292822928329284292852928629287292882928929290292912929229293292942929529296292972929829299293002930129302293032930429305293062930729308293092931029311293122931329314293152931629317293182931929320293212932229323293242932529326293272932829329293302933129332293332933429335293362933729338293392934029341293422934329344293452934629347293482934929350293512935229353293542935529356293572935829359293602936129362293632936429365293662936729368293692937029371293722937329374293752937629377293782937929380293812938229383293842938529386293872938829389293902939129392293932939429395293962939729398293992940029401294022940329404294052940629407294082940929410294112941229413294142941529416294172941829419294202942129422294232942429425294262942729428294292943029431294322943329434294352943629437294382943929440294412944229443294442944529446294472944829449294502945129452294532945429455294562945729458294592946029461294622946329464294652946629467294682946929470294712947229473294742947529476294772947829479294802948129482294832948429485294862948729488294892949029491294922949329494294952949629497294982949929500295012950229503295042950529506295072950829509295102951129512295132951429515295162951729518295192952029521295222952329524295252952629527295282952929530295312953229533295342953529536295372953829539295402954129542295432954429545295462954729548295492955029551295522955329554295552955629557295582955929560295612956229563295642956529566295672956829569295702957129572295732957429575295762957729578295792958029581295822958329584295852958629587295882958929590295912959229593295942959529596295972959829599296002960129602296032960429605296062960729608296092961029611296122961329614296152961629617296182961929620296212962229623296242962529626296272962829629296302963129632296332963429635296362963729638296392964029641296422964329644296452964629647296482964929650296512965229653296542965529656296572965829659296602966129662296632966429665296662966729668296692967029671296722967329674296752967629677296782967929680296812968229683296842968529686296872968829689296902969129692296932969429695296962969729698296992970029701297022970329704297052970629707297082970929710297112971229713297142971529716297172971829719297202972129722297232972429725297262972729728297292973029731297322973329734297352973629737297382973929740297412974229743297442974529746297472974829749297502975129752297532975429755297562975729758297592976029761297622976329764297652976629767297682976929770297712977229773297742977529776297772977829779297802978129782297832978429785297862978729788297892979029791297922979329794297952979629797297982979929800298012980229803298042980529806298072980829809298102981129812298132981429815298162981729818298192982029821298222982329824298252982629827298282982929830298312983229833298342983529836298372983829839298402984129842298432984429845298462984729848298492985029851298522985329854298552985629857298582985929860298612986229863298642986529866298672986829869298702987129872298732987429875298762987729878298792988029881298822988329884298852988629887298882988929890298912989229893298942989529896298972989829899299002990129902299032990429905299062990729908299092991029911299122991329914299152991629917299182991929920299212992229923299242992529926299272992829929299302993129932299332993429935299362993729938299392994029941299422994329944299452994629947299482994929950299512995229953299542995529956299572995829959299602996129962299632996429965299662996729968299692997029971299722997329974299752997629977299782997929980299812998229983299842998529986299872998829989299902999129992299932999429995299962999729998299993000030001300023000330004300053000630007300083000930010300113001230013300143001530016300173001830019300203002130022300233002430025300263002730028300293003030031300323003330034300353003630037300383003930040300413004230043300443004530046300473004830049300503005130052300533005430055300563005730058300593006030061300623006330064300653006630067300683006930070300713007230073300743007530076300773007830079300803008130082300833008430085300863008730088300893009030091300923009330094300953009630097300983009930100301013010230103301043010530106301073010830109301103011130112301133011430115301163011730118301193012030121301223012330124301253012630127301283012930130301313013230133301343013530136301373013830139301403014130142301433014430145301463014730148301493015030151301523015330154301553015630157301583015930160301613016230163301643016530166301673016830169301703017130172301733017430175301763017730178301793018030181301823018330184301853018630187301883018930190301913019230193301943019530196301973019830199302003020130202302033020430205302063020730208302093021030211302123021330214302153021630217302183021930220302213022230223302243022530226302273022830229302303023130232302333023430235302363023730238302393024030241302423024330244302453024630247302483024930250302513025230253302543025530256302573025830259302603026130262302633026430265302663026730268302693027030271302723027330274302753027630277302783027930280302813028230283302843028530286302873028830289302903029130292302933029430295302963029730298302993030030301303023030330304303053030630307303083030930310303113031230313303143031530316303173031830319303203032130322303233032430325303263032730328303293033030331303323033330334303353033630337303383033930340303413034230343303443034530346303473034830349303503035130352303533035430355303563035730358303593036030361303623036330364303653036630367303683036930370303713037230373303743037530376303773037830379303803038130382303833038430385303863038730388303893039030391303923039330394303953039630397303983039930400304013040230403304043040530406304073040830409304103041130412304133041430415304163041730418304193042030421304223042330424304253042630427304283042930430304313043230433304343043530436304373043830439304403044130442304433044430445304463044730448304493045030451304523045330454304553045630457304583045930460304613046230463304643046530466304673046830469304703047130472304733047430475304763047730478304793048030481304823048330484304853048630487304883048930490304913049230493304943049530496304973049830499305003050130502305033050430505305063050730508305093051030511305123051330514305153051630517305183051930520305213052230523305243052530526305273052830529305303053130532305333053430535305363053730538305393054030541305423054330544305453054630547305483054930550305513055230553305543055530556305573055830559305603056130562305633056430565305663056730568305693057030571305723057330574305753057630577305783057930580305813058230583305843058530586305873058830589305903059130592305933059430595305963059730598305993060030601306023060330604306053060630607306083060930610306113061230613306143061530616306173061830619306203062130622306233062430625306263062730628306293063030631306323063330634306353063630637306383063930640306413064230643306443064530646306473064830649306503065130652306533065430655306563065730658306593066030661306623066330664306653066630667306683066930670306713067230673306743067530676306773067830679306803068130682306833068430685306863068730688306893069030691306923069330694306953069630697306983069930700307013070230703307043070530706307073070830709307103071130712307133071430715307163071730718307193072030721307223072330724307253072630727307283072930730307313073230733307343073530736307373073830739307403074130742307433074430745307463074730748307493075030751307523075330754307553075630757307583075930760307613076230763307643076530766307673076830769307703077130772307733077430775307763077730778307793078030781307823078330784307853078630787307883078930790307913079230793307943079530796307973079830799308003080130802308033080430805308063080730808308093081030811308123081330814308153081630817308183081930820308213082230823308243082530826308273082830829308303083130832308333083430835308363083730838308393084030841308423084330844308453084630847308483084930850308513085230853308543085530856308573085830859308603086130862308633086430865308663086730868308693087030871308723087330874308753087630877308783087930880308813088230883308843088530886308873088830889308903089130892308933089430895308963089730898308993090030901309023090330904309053090630907309083090930910309113091230913309143091530916309173091830919309203092130922309233092430925309263092730928309293093030931309323093330934309353093630937309383093930940309413094230943309443094530946309473094830949309503095130952309533095430955309563095730958309593096030961309623096330964309653096630967309683096930970309713097230973309743097530976309773097830979309803098130982309833098430985309863098730988309893099030991309923099330994309953099630997309983099931000310013100231003310043100531006310073100831009310103101131012310133101431015310163101731018310193102031021310223102331024310253102631027310283102931030310313103231033310343103531036310373103831039310403104131042310433104431045310463104731048310493105031051310523105331054310553105631057310583105931060310613106231063310643106531066310673106831069310703107131072310733107431075310763107731078310793108031081310823108331084310853108631087310883108931090310913109231093310943109531096310973109831099311003110131102311033110431105311063110731108311093111031111311123111331114311153111631117311183111931120311213112231123311243112531126311273112831129311303113131132311333113431135311363113731138311393114031141311423114331144311453114631147311483114931150311513115231153311543115531156311573115831159311603116131162311633116431165311663116731168311693117031171311723117331174311753117631177311783117931180311813118231183311843118531186311873118831189311903119131192311933119431195311963119731198311993120031201312023120331204312053120631207312083120931210312113121231213312143121531216312173121831219312203122131222312233122431225312263122731228312293123031231312323123331234312353123631237312383123931240312413124231243312443124531246312473124831249312503125131252312533125431255312563125731258312593126031261312623126331264312653126631267312683126931270312713127231273312743127531276312773127831279312803128131282312833128431285312863128731288312893129031291312923129331294312953129631297312983129931300313013130231303313043130531306313073130831309313103131131312313133131431315313163131731318313193132031321313223132331324313253132631327313283132931330313313133231333313343133531336313373133831339313403134131342313433134431345313463134731348313493135031351313523135331354313553135631357313583135931360313613136231363313643136531366313673136831369313703137131372313733137431375313763137731378313793138031381313823138331384313853138631387313883138931390313913139231393313943139531396313973139831399314003140131402314033140431405314063140731408314093141031411314123141331414314153141631417314183141931420314213142231423314243142531426314273142831429314303143131432314333143431435314363143731438314393144031441314423144331444314453144631447314483144931450314513145231453314543145531456314573145831459314603146131462314633146431465314663146731468314693147031471314723147331474314753147631477314783147931480314813148231483314843148531486314873148831489314903149131492314933149431495314963149731498314993150031501315023150331504315053150631507315083150931510315113151231513315143151531516315173151831519315203152131522315233152431525315263152731528315293153031531315323153331534315353153631537315383153931540315413154231543315443154531546315473154831549315503155131552315533155431555315563155731558315593156031561315623156331564315653156631567315683156931570315713157231573315743157531576315773157831579315803158131582315833158431585315863158731588315893159031591315923159331594315953159631597315983159931600316013160231603316043160531606316073160831609316103161131612316133161431615316163161731618316193162031621316223162331624316253162631627316283162931630316313163231633316343163531636316373163831639316403164131642316433164431645316463164731648316493165031651316523165331654316553165631657316583165931660316613166231663316643166531666316673166831669316703167131672316733167431675316763167731678316793168031681316823168331684316853168631687316883168931690316913169231693316943169531696316973169831699317003170131702317033170431705317063170731708317093171031711317123171331714317153171631717317183171931720317213172231723317243172531726317273172831729317303173131732317333173431735317363173731738317393174031741317423174331744317453174631747317483174931750317513175231753317543175531756317573175831759317603176131762317633176431765317663176731768317693177031771317723177331774317753177631777317783177931780317813178231783317843178531786
  1. /**
  2. * @license Angular v16.2.9
  3. * (c) 2010-2022 Google LLC. https://angular.io/
  4. * License: MIT
  5. */
  6. import { Subject, Subscription, Observable, merge as merge$1, BehaviorSubject, of } from 'rxjs';
  7. import { share, switchMap, distinctUntilChanged, first } from 'rxjs/operators';
  8. function getClosureSafeProperty(objWithPropertyToExtract) {
  9. for (let key in objWithPropertyToExtract) {
  10. if (objWithPropertyToExtract[key] === getClosureSafeProperty) {
  11. return key;
  12. }
  13. }
  14. throw Error('Could not find renamed property on target object.');
  15. }
  16. /**
  17. * Sets properties on a target object from a source object, but only if
  18. * the property doesn't already exist on the target object.
  19. * @param target The target to set properties on
  20. * @param source The source of the property keys and values to set
  21. */
  22. function fillProperties(target, source) {
  23. for (const key in source) {
  24. if (source.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
  25. target[key] = source[key];
  26. }
  27. }
  28. }
  29. function stringify(token) {
  30. if (typeof token === 'string') {
  31. return token;
  32. }
  33. if (Array.isArray(token)) {
  34. return '[' + token.map(stringify).join(', ') + ']';
  35. }
  36. if (token == null) {
  37. return '' + token;
  38. }
  39. if (token.overriddenName) {
  40. return `${token.overriddenName}`;
  41. }
  42. if (token.name) {
  43. return `${token.name}`;
  44. }
  45. const res = token.toString();
  46. if (res == null) {
  47. return '' + res;
  48. }
  49. const newLineIndex = res.indexOf('\n');
  50. return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
  51. }
  52. /**
  53. * Concatenates two strings with separator, allocating new strings only when necessary.
  54. *
  55. * @param before before string.
  56. * @param separator separator string.
  57. * @param after after string.
  58. * @returns concatenated string.
  59. */
  60. function concatStringsWithSpace(before, after) {
  61. return (before == null || before === '') ?
  62. (after === null ? '' : after) :
  63. ((after == null || after === '') ? before : before + ' ' + after);
  64. }
  65. const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty });
  66. /**
  67. * Allows to refer to references which are not yet defined.
  68. *
  69. * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
  70. * DI is declared, but not yet defined. It is also used when the `token` which we use when creating
  71. * a query is not yet defined.
  72. *
  73. * `forwardRef` is also used to break circularities in standalone components imports.
  74. *
  75. * @usageNotes
  76. * ### Circular dependency example
  77. * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
  78. *
  79. * ### Circular standalone reference import example
  80. * ```ts
  81. * @Component({
  82. * standalone: true,
  83. * imports: [ChildComponent],
  84. * selector: 'app-parent',
  85. * template: `<app-child [hideParent]="hideParent"></app-child>`,
  86. * })
  87. * export class ParentComponent {
  88. * @Input() hideParent: boolean;
  89. * }
  90. *
  91. *
  92. * @Component({
  93. * standalone: true,
  94. * imports: [CommonModule, forwardRef(() => ParentComponent)],
  95. * selector: 'app-child',
  96. * template: `<app-parent *ngIf="!hideParent"></app-parent>`,
  97. * })
  98. * export class ChildComponent {
  99. * @Input() hideParent: boolean;
  100. * }
  101. * ```
  102. *
  103. * @publicApi
  104. */
  105. function forwardRef(forwardRefFn) {
  106. forwardRefFn.__forward_ref__ = forwardRef;
  107. forwardRefFn.toString = function () {
  108. return stringify(this());
  109. };
  110. return forwardRefFn;
  111. }
  112. /**
  113. * Lazily retrieves the reference value from a forwardRef.
  114. *
  115. * Acts as the identity function when given a non-forward-ref value.
  116. *
  117. * @usageNotes
  118. * ### Example
  119. *
  120. * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
  121. *
  122. * @see {@link forwardRef}
  123. * @publicApi
  124. */
  125. function resolveForwardRef(type) {
  126. return isForwardRef(type) ? type() : type;
  127. }
  128. /** Checks whether a function is wrapped by a `forwardRef`. */
  129. function isForwardRef(fn) {
  130. return typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
  131. fn.__forward_ref__ === forwardRef;
  132. }
  133. function isEnvironmentProviders(value) {
  134. return value && !!value.ɵproviders;
  135. }
  136. /**
  137. * Base URL for the error details page.
  138. *
  139. * Keep this constant in sync across:
  140. * - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts
  141. * - packages/core/src/error_details_base_url.ts
  142. */
  143. const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
  144. /**
  145. * URL for the XSS security documentation.
  146. */
  147. const XSS_SECURITY_URL = 'https://g.co/ng/security#xss';
  148. /**
  149. * Class that represents a runtime error.
  150. * Formats and outputs the error message in a consistent way.
  151. *
  152. * Example:
  153. * ```
  154. * throw new RuntimeError(
  155. * RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED,
  156. * ngDevMode && 'Injector has already been destroyed.');
  157. * ```
  158. *
  159. * Note: the `message` argument contains a descriptive error message as a string in development
  160. * mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the
  161. * `message` argument becomes `false`, thus we account for it in the typings and the runtime
  162. * logic.
  163. */
  164. class RuntimeError extends Error {
  165. constructor(code, message) {
  166. super(formatRuntimeError(code, message));
  167. this.code = code;
  168. }
  169. }
  170. /**
  171. * Called to format a runtime error.
  172. * See additional info on the `message` argument type in the `RuntimeError` class description.
  173. */
  174. function formatRuntimeError(code, message) {
  175. // Error code might be a negative number, which is a special marker that instructs the logic to
  176. // generate a link to the error details page on angular.io.
  177. // We also prepend `0` to non-compile-time errors.
  178. const fullCode = `NG0${Math.abs(code)}`;
  179. let errorMessage = `${fullCode}${message ? ': ' + message : ''}`;
  180. if (ngDevMode && code < 0) {
  181. const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/);
  182. const separator = addPeriodSeparator ? '.' : '';
  183. errorMessage =
  184. `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
  185. }
  186. return errorMessage;
  187. }
  188. /**
  189. * Used for stringify render output in Ivy.
  190. * Important! This function is very performance-sensitive and we should
  191. * be extra careful not to introduce megamorphic reads in it.
  192. * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations.
  193. */
  194. function renderStringify(value) {
  195. if (typeof value === 'string')
  196. return value;
  197. if (value == null)
  198. return '';
  199. // Use `String` so that it invokes the `toString` method of the value. Note that this
  200. // appears to be faster than calling `value.toString` (see `render_stringify` benchmark).
  201. return String(value);
  202. }
  203. /**
  204. * Used to stringify a value so that it can be displayed in an error message.
  205. * Important! This function contains a megamorphic read and should only be
  206. * used for error messages.
  207. */
  208. function stringifyForError(value) {
  209. if (typeof value === 'function')
  210. return value.name || value.toString();
  211. if (typeof value === 'object' && value != null && typeof value.type === 'function') {
  212. return value.type.name || value.type.toString();
  213. }
  214. return renderStringify(value);
  215. }
  216. /** Called when directives inject each other (creating a circular dependency) */
  217. function throwCyclicDependencyError(token, path) {
  218. const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
  219. throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, `Circular dependency in DI detected for ${token}${depPath}`);
  220. }
  221. function throwMixedMultiProviderError() {
  222. throw new Error(`Cannot mix multi providers and regular providers`);
  223. }
  224. function throwInvalidProviderError(ngModuleType, providers, provider) {
  225. if (ngModuleType && providers) {
  226. const providerDetail = providers.map(v => v == provider ? '?' + provider + '?' : '...');
  227. throw new Error(`Invalid provider for the NgModule '${stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`);
  228. }
  229. else if (isEnvironmentProviders(provider)) {
  230. if (provider.ɵfromNgModule) {
  231. 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.`);
  232. }
  233. else {
  234. 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.`);
  235. }
  236. }
  237. else {
  238. throw new Error('Invalid provider');
  239. }
  240. }
  241. /** Throws an error when a token is not found in DI. */
  242. function throwProviderNotFoundError(token, injectorName) {
  243. const injectorDetails = injectorName ? ` in ${injectorName}` : '';
  244. throw new RuntimeError(-201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */, ngDevMode && `No provider for ${stringifyForError(token)} found${injectorDetails}`);
  245. }
  246. // The functions in this file verify that the assumptions we are making
  247. function assertNumber(actual, msg) {
  248. if (!(typeof actual === 'number')) {
  249. throwError(msg, typeof actual, 'number', '===');
  250. }
  251. }
  252. function assertNumberInRange(actual, minInclusive, maxInclusive) {
  253. assertNumber(actual, 'Expected a number');
  254. assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to');
  255. assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to');
  256. }
  257. function assertString(actual, msg) {
  258. if (!(typeof actual === 'string')) {
  259. throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
  260. }
  261. }
  262. function assertFunction(actual, msg) {
  263. if (!(typeof actual === 'function')) {
  264. throwError(msg, actual === null ? 'null' : typeof actual, 'function', '===');
  265. }
  266. }
  267. function assertEqual(actual, expected, msg) {
  268. if (!(actual == expected)) {
  269. throwError(msg, actual, expected, '==');
  270. }
  271. }
  272. function assertNotEqual(actual, expected, msg) {
  273. if (!(actual != expected)) {
  274. throwError(msg, actual, expected, '!=');
  275. }
  276. }
  277. function assertSame(actual, expected, msg) {
  278. if (!(actual === expected)) {
  279. throwError(msg, actual, expected, '===');
  280. }
  281. }
  282. function assertNotSame(actual, expected, msg) {
  283. if (!(actual !== expected)) {
  284. throwError(msg, actual, expected, '!==');
  285. }
  286. }
  287. function assertLessThan(actual, expected, msg) {
  288. if (!(actual < expected)) {
  289. throwError(msg, actual, expected, '<');
  290. }
  291. }
  292. function assertLessThanOrEqual(actual, expected, msg) {
  293. if (!(actual <= expected)) {
  294. throwError(msg, actual, expected, '<=');
  295. }
  296. }
  297. function assertGreaterThan(actual, expected, msg) {
  298. if (!(actual > expected)) {
  299. throwError(msg, actual, expected, '>');
  300. }
  301. }
  302. function assertGreaterThanOrEqual(actual, expected, msg) {
  303. if (!(actual >= expected)) {
  304. throwError(msg, actual, expected, '>=');
  305. }
  306. }
  307. function assertNotDefined(actual, msg) {
  308. if (actual != null) {
  309. throwError(msg, actual, null, '==');
  310. }
  311. }
  312. function assertDefined(actual, msg) {
  313. if (actual == null) {
  314. throwError(msg, actual, null, '!=');
  315. }
  316. }
  317. function throwError(msg, actual, expected, comparison) {
  318. throw new Error(`ASSERTION ERROR: ${msg}` +
  319. (comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
  320. }
  321. function assertDomNode(node) {
  322. if (!(node instanceof Node)) {
  323. throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`);
  324. }
  325. }
  326. function assertIndexInRange(arr, index) {
  327. assertDefined(arr, 'Array must be defined.');
  328. const maxLen = arr.length;
  329. if (index < 0 || index >= maxLen) {
  330. throwError(`Index expected to be less than ${maxLen} but got ${index}`);
  331. }
  332. }
  333. function assertOneOf(value, ...validValues) {
  334. if (validValues.indexOf(value) !== -1)
  335. return true;
  336. throwError(`Expected value to be one of ${JSON.stringify(validValues)} but was ${JSON.stringify(value)}.`);
  337. }
  338. /**
  339. * Construct an injectable definition which defines how a token will be constructed by the DI
  340. * system, and in which injectors (if any) it will be available.
  341. *
  342. * This should be assigned to a static `ɵprov` field on a type, which will then be an
  343. * `InjectableType`.
  344. *
  345. * Options:
  346. * * `providedIn` determines which injectors will include the injectable, by either associating it
  347. * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
  348. * provided in the `'root'` injector, which will be the application-level injector in most apps.
  349. * * `factory` gives the zero argument function which will create an instance of the injectable.
  350. * The factory can call [`inject`](api/core/inject) to access the `Injector` and request injection
  351. * of dependencies.
  352. *
  353. * @codeGenApi
  354. * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm.
  355. */
  356. function ɵɵdefineInjectable(opts) {
  357. return {
  358. token: opts.token,
  359. providedIn: opts.providedIn || null,
  360. factory: opts.factory,
  361. value: undefined,
  362. };
  363. }
  364. /**
  365. * @deprecated in v8, delete after v10. This API should be used only by generated code, and that
  366. * code should now use ɵɵdefineInjectable instead.
  367. * @publicApi
  368. */
  369. const defineInjectable = ɵɵdefineInjectable;
  370. /**
  371. * Construct an `InjectorDef` which configures an injector.
  372. *
  373. * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
  374. * `InjectorType`.
  375. *
  376. * Options:
  377. *
  378. * * `providers`: an optional array of providers to add to the injector. Each provider must
  379. * either have a factory or point to a type which has a `ɵprov` static property (the
  380. * type must be an `InjectableType`).
  381. * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
  382. * whose providers will also be added to the injector. Locally provided types will override
  383. * providers from imports.
  384. *
  385. * @codeGenApi
  386. */
  387. function ɵɵdefineInjector(options) {
  388. return { providers: options.providers || [], imports: options.imports || [] };
  389. }
  390. /**
  391. * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
  392. * inherited value.
  393. *
  394. * @param type A type which may have its own (non-inherited) `ɵprov`.
  395. */
  396. function getInjectableDef(type) {
  397. return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF);
  398. }
  399. function isInjectable(type) {
  400. return getInjectableDef(type) !== null;
  401. }
  402. /**
  403. * Return definition only if it is defined directly on `type` and is not inherited from a base
  404. * class of `type`.
  405. */
  406. function getOwnDefinition(type, field) {
  407. return type.hasOwnProperty(field) ? type[field] : null;
  408. }
  409. /**
  410. * Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors.
  411. *
  412. * @param type A type which may have `ɵprov`, via inheritance.
  413. *
  414. * @deprecated Will be removed in a future version of Angular, where an error will occur in the
  415. * scenario if we find the `ɵprov` on an ancestor only.
  416. */
  417. function getInheritedInjectableDef(type) {
  418. const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF]);
  419. if (def) {
  420. ngDevMode &&
  421. console.warn(`DEPRECATED: DI is instantiating a token "${type.name}" that inherits its @Injectable decorator but does not provide one itself.\n` +
  422. `This will become an error in a future version of Angular. Please add @Injectable() to the "${type.name}" class.`);
  423. return def;
  424. }
  425. else {
  426. return null;
  427. }
  428. }
  429. /**
  430. * Read the injector def type in a way which is immune to accidentally reading inherited value.
  431. *
  432. * @param type type which may have an injector def (`ɵinj`)
  433. */
  434. function getInjectorDef(type) {
  435. return type && (type.hasOwnProperty(NG_INJ_DEF) || type.hasOwnProperty(NG_INJECTOR_DEF)) ?
  436. type[NG_INJ_DEF] :
  437. null;
  438. }
  439. const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty });
  440. const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty });
  441. // We need to keep these around so we can read off old defs if new defs are unavailable
  442. const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty });
  443. const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty });
  444. /**
  445. * Injection flags for DI.
  446. *
  447. * @publicApi
  448. * @deprecated use an options object for [`inject`](api/core/inject) instead.
  449. */
  450. var InjectFlags;
  451. (function (InjectFlags) {
  452. // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer
  453. // writes exports of it into ngfactory files.
  454. /** Check self and check parent injector if needed */
  455. InjectFlags[InjectFlags["Default"] = 0] = "Default";
  456. /**
  457. * Specifies that an injector should retrieve a dependency from any injector until reaching the
  458. * host element of the current component. (Only used with Element Injector)
  459. */
  460. InjectFlags[InjectFlags["Host"] = 1] = "Host";
  461. /** Don't ascend to ancestors of the node requesting injection. */
  462. InjectFlags[InjectFlags["Self"] = 2] = "Self";
  463. /** Skip the node that is requesting injection. */
  464. InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf";
  465. /** Inject `defaultValue` instead if token not found. */
  466. InjectFlags[InjectFlags["Optional"] = 8] = "Optional";
  467. })(InjectFlags || (InjectFlags = {}));
  468. /**
  469. * Current implementation of inject.
  470. *
  471. * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
  472. * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
  473. * way for two reasons:
  474. * 1. `Injector` should not depend on ivy logic.
  475. * 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
  476. */
  477. let _injectImplementation;
  478. function getInjectImplementation() {
  479. return _injectImplementation;
  480. }
  481. /**
  482. * Sets the current inject implementation.
  483. */
  484. function setInjectImplementation(impl) {
  485. const previous = _injectImplementation;
  486. _injectImplementation = impl;
  487. return previous;
  488. }
  489. /**
  490. * Injects `root` tokens in limp mode.
  491. *
  492. * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
  493. * `"root"`. This is known as the limp mode injection. In such case the value is stored in the
  494. * injectable definition.
  495. */
  496. function injectRootLimpMode(token, notFoundValue, flags) {
  497. const injectableDef = getInjectableDef(token);
  498. if (injectableDef && injectableDef.providedIn == 'root') {
  499. return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
  500. injectableDef.value;
  501. }
  502. if (flags & InjectFlags.Optional)
  503. return null;
  504. if (notFoundValue !== undefined)
  505. return notFoundValue;
  506. throwProviderNotFoundError(stringify(token), 'Injector');
  507. }
  508. /**
  509. * Assert that `_injectImplementation` is not `fn`.
  510. *
  511. * This is useful, to prevent infinite recursion.
  512. *
  513. * @param fn Function which it should not equal to
  514. */
  515. function assertInjectImplementationNotEqual(fn) {
  516. ngDevMode &&
  517. assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion');
  518. }
  519. const _global = globalThis;
  520. function ngDevModeResetPerfCounters() {
  521. const locationString = typeof location !== 'undefined' ? location.toString() : '';
  522. const newCounters = {
  523. namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1,
  524. firstCreatePass: 0,
  525. tNode: 0,
  526. tView: 0,
  527. rendererCreateTextNode: 0,
  528. rendererSetText: 0,
  529. rendererCreateElement: 0,
  530. rendererAddEventListener: 0,
  531. rendererSetAttribute: 0,
  532. rendererRemoveAttribute: 0,
  533. rendererSetProperty: 0,
  534. rendererSetClassName: 0,
  535. rendererAddClass: 0,
  536. rendererRemoveClass: 0,
  537. rendererSetStyle: 0,
  538. rendererRemoveStyle: 0,
  539. rendererDestroy: 0,
  540. rendererDestroyNode: 0,
  541. rendererMoveNode: 0,
  542. rendererRemoveNode: 0,
  543. rendererAppendChild: 0,
  544. rendererInsertBefore: 0,
  545. rendererCreateComment: 0,
  546. hydratedNodes: 0,
  547. hydratedComponents: 0,
  548. dehydratedViewsRemoved: 0,
  549. dehydratedViewsCleanupRuns: 0,
  550. componentsSkippedHydration: 0,
  551. };
  552. // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
  553. const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
  554. _global['ngDevMode'] = allowNgDevModeTrue && newCounters;
  555. return newCounters;
  556. }
  557. /**
  558. * This function checks to see if the `ngDevMode` has been set. If yes,
  559. * then we honor it, otherwise we default to dev mode with additional checks.
  560. *
  561. * The idea is that unless we are doing production build where we explicitly
  562. * set `ngDevMode == false` we should be helping the developer by providing
  563. * as much early warning and errors as possible.
  564. *
  565. * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
  566. * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
  567. * is defined for the entire instruction set.
  568. *
  569. * When checking `ngDevMode` on toplevel, always init it before referencing it
  570. * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
  571. * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
  572. *
  573. * Details on possible values for `ngDevMode` can be found on its docstring.
  574. *
  575. * NOTE:
  576. * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`.
  577. */
  578. function initNgDevMode() {
  579. // The below checks are to ensure that calling `initNgDevMode` multiple times does not
  580. // reset the counters.
  581. // If the `ngDevMode` is not an object, then it means we have not created the perf counters
  582. // yet.
  583. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  584. if (typeof ngDevMode !== 'object') {
  585. ngDevModeResetPerfCounters();
  586. }
  587. return typeof ngDevMode !== 'undefined' && !!ngDevMode;
  588. }
  589. return false;
  590. }
  591. let _injectorProfilerContext;
  592. function getInjectorProfilerContext() {
  593. !ngDevMode && throwError('getInjectorProfilerContext should never be called in production mode');
  594. return _injectorProfilerContext;
  595. }
  596. function setInjectorProfilerContext(context) {
  597. !ngDevMode && throwError('setInjectorProfilerContext should never be called in production mode');
  598. const previous = _injectorProfilerContext;
  599. _injectorProfilerContext = context;
  600. return previous;
  601. }
  602. let injectorProfilerCallback = null;
  603. /**
  604. * Sets the callback function which will be invoked during certain DI events within the
  605. * runtime (for example: injecting services, creating injectable instances, configuring providers)
  606. *
  607. * Warning: this function is *INTERNAL* and should not be relied upon in application's code.
  608. * The contract of the function might be changed in any release and/or the function can be removed
  609. * completely.
  610. *
  611. * @param profiler function provided by the caller or null value to disable profiling.
  612. */
  613. const setInjectorProfiler = (injectorProfiler) => {
  614. !ngDevMode && throwError('setInjectorProfiler should never be called in production mode');
  615. injectorProfilerCallback = injectorProfiler;
  616. };
  617. /**
  618. * Injector profiler function which emits on DI events executed by the runtime.
  619. *
  620. * @param event InjectorProfilerEvent corresponding to the DI event being emitted
  621. */
  622. function injectorProfiler(event) {
  623. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  624. if (injectorProfilerCallback != null /* both `null` and `undefined` */) {
  625. injectorProfilerCallback(event);
  626. }
  627. }
  628. /**
  629. * Emits an InjectorProfilerEventType.ProviderConfigured to the injector profiler. The data in the
  630. * emitted event includes the raw provider, as well as the token that provider is providing.
  631. *
  632. * @param provider A provider object
  633. */
  634. function emitProviderConfiguredEvent(provider, isViewProvider = false) {
  635. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  636. injectorProfiler({
  637. type: 2 /* InjectorProfilerEventType.ProviderConfigured */,
  638. context: getInjectorProfilerContext(),
  639. providerRecord: {
  640. token: typeof provider === 'function' ? provider : resolveForwardRef(provider.provide),
  641. provider,
  642. isViewProvider
  643. }
  644. });
  645. }
  646. /**
  647. * Emits an event to the injector profiler with the instance that was created. Note that
  648. * the injector associated with this emission can be accessed by using getDebugInjectContext()
  649. *
  650. * @param instance an object created by an injector
  651. */
  652. function emitInstanceCreatedByInjectorEvent(instance) {
  653. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  654. injectorProfiler({
  655. type: 1 /* InjectorProfilerEventType.InstanceCreatedByInjector */,
  656. context: getInjectorProfilerContext(),
  657. instance: { value: instance }
  658. });
  659. }
  660. /**
  661. * @param token DI token associated with injected service
  662. * @param value the instance of the injected service (i.e the result of `inject(token)`)
  663. * @param flags the flags that the token was injected with
  664. */
  665. function emitInjectEvent(token, value, flags) {
  666. !ngDevMode && throwError('Injector profiler should never be called in production mode');
  667. injectorProfiler({
  668. type: 0 /* InjectorProfilerEventType.Inject */,
  669. context: getInjectorProfilerContext(),
  670. service: { token, value, flags }
  671. });
  672. }
  673. function runInInjectorProfilerContext(injector, token, callback) {
  674. !ngDevMode &&
  675. throwError('runInInjectorProfilerContext should never be called in production mode');
  676. const prevInjectContext = setInjectorProfilerContext({ injector, token });
  677. try {
  678. callback();
  679. }
  680. finally {
  681. setInjectorProfilerContext(prevInjectContext);
  682. }
  683. }
  684. const _THROW_IF_NOT_FOUND = {};
  685. const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
  686. /*
  687. * Name of a property (that we patch onto DI decorator), which is used as an annotation of which
  688. * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators
  689. * in the code, thus making them tree-shakable.
  690. */
  691. const DI_DECORATOR_FLAG = '__NG_DI_FLAG__';
  692. const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
  693. const NG_TOKEN_PATH = 'ngTokenPath';
  694. const NEW_LINE = /\n/gm;
  695. const NO_NEW_LINE = 'ɵ';
  696. const SOURCE = '__source';
  697. /**
  698. * Current injector value used by `inject`.
  699. * - `undefined`: it is an error to call `inject`
  700. * - `null`: `inject` can be called but there is no injector (limp-mode).
  701. * - Injector instance: Use the injector for resolution.
  702. */
  703. let _currentInjector = undefined;
  704. function getCurrentInjector() {
  705. return _currentInjector;
  706. }
  707. function setCurrentInjector(injector) {
  708. const former = _currentInjector;
  709. _currentInjector = injector;
  710. return former;
  711. }
  712. function injectInjectorOnly(token, flags = InjectFlags.Default) {
  713. if (_currentInjector === undefined) {
  714. throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
  715. `inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`runInInjectionContext\`.`);
  716. }
  717. else if (_currentInjector === null) {
  718. return injectRootLimpMode(token, undefined, flags);
  719. }
  720. else {
  721. const value = _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
  722. ngDevMode && emitInjectEvent(token, value, flags);
  723. return value;
  724. }
  725. }
  726. function ɵɵinject(token, flags = InjectFlags.Default) {
  727. return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
  728. }
  729. /**
  730. * Throws an error indicating that a factory function could not be generated by the compiler for a
  731. * particular class.
  732. *
  733. * The name of the class is not mentioned here, but will be in the generated factory function name
  734. * and thus in the stack trace.
  735. *
  736. * @codeGenApi
  737. */
  738. function ɵɵinvalidFactoryDep(index) {
  739. throw new RuntimeError(202 /* RuntimeErrorCode.INVALID_FACTORY_DEPENDENCY */, ngDevMode &&
  740. `This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid.
  741. 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.
  742. 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.`);
  743. }
  744. /**
  745. * Injects a token from the currently active injector.
  746. * `inject` is only supported in an [injection context](/guide/dependency-injection-context). It can
  747. * be used during:
  748. * - Construction (via the `constructor`) of a class being instantiated by the DI system, such
  749. * as an `@Injectable` or `@Component`.
  750. * - In the initializer for fields of such classes.
  751. * - In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
  752. * - In the `factory` function specified for an `InjectionToken`.
  753. * - In a stackframe of a function call in a DI context
  754. *
  755. * @param token A token that represents a dependency that should be injected.
  756. * @param flags Optional flags that control how injection is executed.
  757. * The flags correspond to injection strategies that can be specified with
  758. * parameter decorators `@Host`, `@Self`, `@SkipSelf`, and `@Optional`.
  759. * @returns the injected value if operation is successful, `null` otherwise.
  760. * @throws if called outside of a supported context.
  761. *
  762. * @usageNotes
  763. * In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a
  764. * field initializer:
  765. *
  766. * ```typescript
  767. * @Injectable({providedIn: 'root'})
  768. * export class Car {
  769. * radio: Radio|undefined;
  770. * // OK: field initializer
  771. * spareTyre = inject(Tyre);
  772. *
  773. * constructor() {
  774. * // OK: constructor body
  775. * this.radio = inject(Radio);
  776. * }
  777. * }
  778. * ```
  779. *
  780. * It is also legal to call `inject` from a provider's factory:
  781. *
  782. * ```typescript
  783. * providers: [
  784. * {provide: Car, useFactory: () => {
  785. * // OK: a class factory
  786. * const engine = inject(Engine);
  787. * return new Car(engine);
  788. * }}
  789. * ]
  790. * ```
  791. *
  792. * Calls to the `inject()` function outside of the class creation context will result in error. Most
  793. * notably, calls to `inject()` are disallowed after a class instance was created, in methods
  794. * (including lifecycle hooks):
  795. *
  796. * ```typescript
  797. * @Component({ ... })
  798. * export class CarComponent {
  799. * ngOnInit() {
  800. * // ERROR: too late, the component instance was already created
  801. * const engine = inject(Engine);
  802. * engine.start();
  803. * }
  804. * }
  805. * ```
  806. *
  807. * @publicApi
  808. */
  809. function inject(token, flags = InjectFlags.Default) {
  810. return ɵɵinject(token, convertToBitFlags(flags));
  811. }
  812. // Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
  813. function convertToBitFlags(flags) {
  814. if (typeof flags === 'undefined' || typeof flags === 'number') {
  815. return flags;
  816. }
  817. // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
  818. // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
  819. // `InjectOptions` to `InjectFlags`.
  820. return (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
  821. (flags.optional && 8 /* InternalInjectFlags.Optional */) |
  822. (flags.host && 1 /* InternalInjectFlags.Host */) |
  823. (flags.self && 2 /* InternalInjectFlags.Self */) |
  824. (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
  825. }
  826. function injectArgs(types) {
  827. const args = [];
  828. for (let i = 0; i < types.length; i++) {
  829. const arg = resolveForwardRef(types[i]);
  830. if (Array.isArray(arg)) {
  831. if (arg.length === 0) {
  832. throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && 'Arguments array must have arguments.');
  833. }
  834. let type = undefined;
  835. let flags = InjectFlags.Default;
  836. for (let j = 0; j < arg.length; j++) {
  837. const meta = arg[j];
  838. const flag = getInjectFlag(meta);
  839. if (typeof flag === 'number') {
  840. // Special case when we handle @Inject decorator.
  841. if (flag === -1 /* DecoratorFlags.Inject */) {
  842. type = meta.token;
  843. }
  844. else {
  845. flags |= flag;
  846. }
  847. }
  848. else {
  849. type = meta;
  850. }
  851. }
  852. args.push(ɵɵinject(type, flags));
  853. }
  854. else {
  855. args.push(ɵɵinject(arg));
  856. }
  857. }
  858. return args;
  859. }
  860. /**
  861. * Attaches a given InjectFlag to a given decorator using monkey-patching.
  862. * Since DI decorators can be used in providers `deps` array (when provider is configured using
  863. * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we
  864. * attach the flag to make it available both as a static property and as a field on decorator
  865. * instance.
  866. *
  867. * @param decorator Provided DI decorator.
  868. * @param flag InjectFlag that should be applied.
  869. */
  870. function attachInjectFlag(decorator, flag) {
  871. decorator[DI_DECORATOR_FLAG] = flag;
  872. decorator.prototype[DI_DECORATOR_FLAG] = flag;
  873. return decorator;
  874. }
  875. /**
  876. * Reads monkey-patched property that contains InjectFlag attached to a decorator.
  877. *
  878. * @param token Token that may contain monkey-patched DI flags property.
  879. */
  880. function getInjectFlag(token) {
  881. return token[DI_DECORATOR_FLAG];
  882. }
  883. function catchInjectorError(e, token, injectorErrorName, source) {
  884. const tokenPath = e[NG_TEMP_TOKEN_PATH];
  885. if (token[SOURCE]) {
  886. tokenPath.unshift(token[SOURCE]);
  887. }
  888. e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
  889. e[NG_TOKEN_PATH] = tokenPath;
  890. e[NG_TEMP_TOKEN_PATH] = null;
  891. throw e;
  892. }
  893. function formatError(text, obj, injectorErrorName, source = null) {
  894. text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.slice(2) : text;
  895. let context = stringify(obj);
  896. if (Array.isArray(obj)) {
  897. context = obj.map(stringify).join(' -> ');
  898. }
  899. else if (typeof obj === 'object') {
  900. let parts = [];
  901. for (let key in obj) {
  902. if (obj.hasOwnProperty(key)) {
  903. let value = obj[key];
  904. parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value)));
  905. }
  906. }
  907. context = `{${parts.join(', ')}}`;
  908. }
  909. return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
  910. }
  911. /**
  912. * Convince closure compiler that the wrapped function has no side-effects.
  913. *
  914. * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to
  915. * allow us to execute a function but have closure compiler mark the call as no-side-effects.
  916. * It is important that the return value for the `noSideEffects` function be assigned
  917. * to something which is retained otherwise the call to `noSideEffects` will be removed by closure
  918. * compiler.
  919. */
  920. function noSideEffects(fn) {
  921. return { toString: fn }.toString();
  922. }
  923. /**
  924. * The strategy that the default change detector uses to detect changes.
  925. * When set, takes effect the next time change detection is triggered.
  926. *
  927. * @see {@link ChangeDetectorRef#usage-notes Change detection usage}
  928. *
  929. * @publicApi
  930. */
  931. var ChangeDetectionStrategy;
  932. (function (ChangeDetectionStrategy) {
  933. /**
  934. * Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
  935. * until reactivated by setting the strategy to `Default` (`CheckAlways`).
  936. * Change detection can still be explicitly invoked.
  937. * This strategy applies to all child directives and cannot be overridden.
  938. */
  939. ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
  940. /**
  941. * Use the default `CheckAlways` strategy, in which change detection is automatic until
  942. * explicitly deactivated.
  943. */
  944. ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
  945. })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
  946. /**
  947. * Defines the CSS styles encapsulation policies for the {@link Component} decorator's
  948. * `encapsulation` option.
  949. *
  950. * See {@link Component#encapsulation encapsulation}.
  951. *
  952. * @usageNotes
  953. * ### Example
  954. *
  955. * {@example core/ts/metadata/encapsulation.ts region='longform'}
  956. *
  957. * @publicApi
  958. */
  959. var ViewEncapsulation$1;
  960. (function (ViewEncapsulation) {
  961. // TODO: consider making `ViewEncapsulation` a `const enum` instead. See
  962. // https://github.com/angular/angular/issues/44119 for additional information.
  963. /**
  964. * Emulates a native Shadow DOM encapsulation behavior by adding a specific attribute to the
  965. * component's host element and applying the same attribute to all the CSS selectors provided
  966. * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls}.
  967. *
  968. * This is the default option.
  969. */
  970. ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
  971. // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
  972. /**
  973. * Doesn't provide any sort of CSS style encapsulation, meaning that all the styles provided
  974. * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls} are applicable
  975. * to any HTML element of the application regardless of their host Component.
  976. */
  977. ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
  978. /**
  979. * Uses the browser's native Shadow DOM API to encapsulate CSS styles, meaning that it creates
  980. * a ShadowRoot for the component's host element which is then used to encapsulate
  981. * all the Component's styling.
  982. */
  983. ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
  984. })(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
  985. /**
  986. * This file contains reuseable "empty" symbols that can be used as default return values
  987. * in different parts of the rendering code. Because the same symbols are returned, this
  988. * allows for identity checks against these values to be consistently used by the framework
  989. * code.
  990. */
  991. const EMPTY_OBJ = {};
  992. const EMPTY_ARRAY = [];
  993. // freezing the values prevents any code from accidentally inserting new values in
  994. if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
  995. // These property accesses can be ignored because ngDevMode will be set to false
  996. // when optimizing code and the whole if statement will be dropped.
  997. // tslint:disable-next-line:no-toplevel-property-access
  998. Object.freeze(EMPTY_OBJ);
  999. // tslint:disable-next-line:no-toplevel-property-access
  1000. Object.freeze(EMPTY_ARRAY);
  1001. }
  1002. const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty });
  1003. const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty });
  1004. const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty });
  1005. const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty });
  1006. const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty });
  1007. /**
  1008. * If a directive is diPublic, bloomAdd sets a property on the type with this constant as
  1009. * the key and the directive's unique ID as the value. This allows us to map directives to their
  1010. * bloom filter bit for DI.
  1011. */
  1012. // TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
  1013. const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty });
  1014. /**
  1015. * The `NG_ENV_ID` field on a DI token indicates special processing in the `EnvironmentInjector`:
  1016. * getting such tokens from the `EnvironmentInjector` will bypass the standard DI resolution
  1017. * strategy and instead will return implementation produced by the `NG_ENV_ID` factory function.
  1018. *
  1019. * This particular retrieval of DI tokens is mostly done to eliminate circular dependencies and
  1020. * improve tree-shaking.
  1021. */
  1022. const NG_ENV_ID = getClosureSafeProperty({ __NG_ENV_ID__: getClosureSafeProperty });
  1023. /**
  1024. * Returns an index of `classToSearch` in `className` taking token boundaries into account.
  1025. *
  1026. * `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`)
  1027. *
  1028. * @param className A string containing classes (whitespace separated)
  1029. * @param classToSearch A class name to locate
  1030. * @param startingIndex Starting location of search
  1031. * @returns an index of the located class (or -1 if not found)
  1032. */
  1033. function classIndexOf(className, classToSearch, startingIndex) {
  1034. ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.');
  1035. let end = className.length;
  1036. while (true) {
  1037. const foundIndex = className.indexOf(classToSearch, startingIndex);
  1038. if (foundIndex === -1)
  1039. return foundIndex;
  1040. if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) {
  1041. // Ensure that it has leading whitespace
  1042. const length = classToSearch.length;
  1043. if (foundIndex + length === end ||
  1044. className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) {
  1045. // Ensure that it has trailing whitespace
  1046. return foundIndex;
  1047. }
  1048. }
  1049. // False positive, keep searching from where we left off.
  1050. startingIndex = foundIndex + 1;
  1051. }
  1052. }
  1053. /**
  1054. * Assigns all attribute values to the provided element via the inferred renderer.
  1055. *
  1056. * This function accepts two forms of attribute entries:
  1057. *
  1058. * default: (key, value):
  1059. * attrs = [key1, value1, key2, value2]
  1060. *
  1061. * namespaced: (NAMESPACE_MARKER, uri, name, value)
  1062. * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
  1063. *
  1064. * The `attrs` array can contain a mix of both the default and namespaced entries.
  1065. * The "default" values are set without a marker, but if the function comes across
  1066. * a marker value then it will attempt to set a namespaced value. If the marker is
  1067. * not of a namespaced value then the function will quit and return the index value
  1068. * where it stopped during the iteration of the attrs array.
  1069. *
  1070. * See [AttributeMarker] to understand what the namespace marker value is.
  1071. *
  1072. * Note that this instruction does not support assigning style and class values to
  1073. * an element. See `elementStart` and `elementHostAttrs` to learn how styling values
  1074. * are applied to an element.
  1075. * @param renderer The renderer to be used
  1076. * @param native The element that the attributes will be assigned to
  1077. * @param attrs The attribute array of values that will be assigned to the element
  1078. * @returns the index value that was last accessed in the attributes array
  1079. */
  1080. function setUpAttributes(renderer, native, attrs) {
  1081. let i = 0;
  1082. while (i < attrs.length) {
  1083. const value = attrs[i];
  1084. if (typeof value === 'number') {
  1085. // only namespaces are supported. Other value types (such as style/class
  1086. // entries) are not supported in this function.
  1087. if (value !== 0 /* AttributeMarker.NamespaceURI */) {
  1088. break;
  1089. }
  1090. // we just landed on the marker value ... therefore
  1091. // we should skip to the next entry
  1092. i++;
  1093. const namespaceURI = attrs[i++];
  1094. const attrName = attrs[i++];
  1095. const attrVal = attrs[i++];
  1096. ngDevMode && ngDevMode.rendererSetAttribute++;
  1097. renderer.setAttribute(native, attrName, attrVal, namespaceURI);
  1098. }
  1099. else {
  1100. // attrName is string;
  1101. const attrName = value;
  1102. const attrVal = attrs[++i];
  1103. // Standard attributes
  1104. ngDevMode && ngDevMode.rendererSetAttribute++;
  1105. if (isAnimationProp(attrName)) {
  1106. renderer.setProperty(native, attrName, attrVal);
  1107. }
  1108. else {
  1109. renderer.setAttribute(native, attrName, attrVal);
  1110. }
  1111. i++;
  1112. }
  1113. }
  1114. // another piece of code may iterate over the same attributes array. Therefore
  1115. // it may be helpful to return the exact spot where the attributes array exited
  1116. // whether by running into an unsupported marker or if all the static values were
  1117. // iterated over.
  1118. return i;
  1119. }
  1120. /**
  1121. * Test whether the given value is a marker that indicates that the following
  1122. * attribute values in a `TAttributes` array are only the names of attributes,
  1123. * and not name-value pairs.
  1124. * @param marker The attribute marker to test.
  1125. * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`).
  1126. */
  1127. function isNameOnlyAttributeMarker(marker) {
  1128. return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ ||
  1129. marker === 6 /* AttributeMarker.I18n */;
  1130. }
  1131. function isAnimationProp(name) {
  1132. // Perf note: accessing charCodeAt to check for the first character of a string is faster as
  1133. // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
  1134. // charCodeAt doesn't allocate memory to return a substring.
  1135. return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */;
  1136. }
  1137. /**
  1138. * Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process.
  1139. *
  1140. * This merge function keeps the order of attrs same.
  1141. *
  1142. * @param dst Location of where the merged `TAttributes` should end up.
  1143. * @param src `TAttributes` which should be appended to `dst`
  1144. */
  1145. function mergeHostAttrs(dst, src) {
  1146. if (src === null || src.length === 0) {
  1147. // do nothing
  1148. }
  1149. else if (dst === null || dst.length === 0) {
  1150. // We have source, but dst is empty, just make a copy.
  1151. dst = src.slice();
  1152. }
  1153. else {
  1154. let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */;
  1155. for (let i = 0; i < src.length; i++) {
  1156. const item = src[i];
  1157. if (typeof item === 'number') {
  1158. srcMarker = item;
  1159. }
  1160. else {
  1161. if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) {
  1162. // Case where we need to consume `key1`, `key2`, `value` items.
  1163. }
  1164. else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ ||
  1165. srcMarker === 2 /* AttributeMarker.Styles */) {
  1166. // Case where we have to consume `key1` and `value` only.
  1167. mergeHostAttribute(dst, srcMarker, item, null, src[++i]);
  1168. }
  1169. else {
  1170. // Case where we have to consume `key1` only.
  1171. mergeHostAttribute(dst, srcMarker, item, null, null);
  1172. }
  1173. }
  1174. }
  1175. }
  1176. return dst;
  1177. }
  1178. /**
  1179. * Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account.
  1180. *
  1181. * @param dst `TAttributes` to append to.
  1182. * @param marker Region where the `key`/`value` should be added.
  1183. * @param key1 Key to add to `TAttributes`
  1184. * @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`)
  1185. * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class.
  1186. */
  1187. function mergeHostAttribute(dst, marker, key1, key2, value) {
  1188. let i = 0;
  1189. // Assume that new markers will be inserted at the end.
  1190. let markerInsertPosition = dst.length;
  1191. // scan until correct type.
  1192. if (marker === -1 /* AttributeMarker.ImplicitAttributes */) {
  1193. markerInsertPosition = -1;
  1194. }
  1195. else {
  1196. while (i < dst.length) {
  1197. const dstValue = dst[i++];
  1198. if (typeof dstValue === 'number') {
  1199. if (dstValue === marker) {
  1200. markerInsertPosition = -1;
  1201. break;
  1202. }
  1203. else if (dstValue > marker) {
  1204. // We need to save this as we want the markers to be inserted in specific order.
  1205. markerInsertPosition = i - 1;
  1206. break;
  1207. }
  1208. }
  1209. }
  1210. }
  1211. // search until you find place of insertion
  1212. while (i < dst.length) {
  1213. const item = dst[i];
  1214. if (typeof item === 'number') {
  1215. // since `i` started as the index after the marker, we did not find it if we are at the next
  1216. // marker
  1217. break;
  1218. }
  1219. else if (item === key1) {
  1220. // We already have same token
  1221. if (key2 === null) {
  1222. if (value !== null) {
  1223. dst[i + 1] = value;
  1224. }
  1225. return;
  1226. }
  1227. else if (key2 === dst[i + 1]) {
  1228. dst[i + 2] = value;
  1229. return;
  1230. }
  1231. }
  1232. // Increment counter.
  1233. i++;
  1234. if (key2 !== null)
  1235. i++;
  1236. if (value !== null)
  1237. i++;
  1238. }
  1239. // insert at location.
  1240. if (markerInsertPosition !== -1) {
  1241. dst.splice(markerInsertPosition, 0, marker);
  1242. i = markerInsertPosition + 1;
  1243. }
  1244. dst.splice(i++, 0, key1);
  1245. if (key2 !== null) {
  1246. dst.splice(i++, 0, key2);
  1247. }
  1248. if (value !== null) {
  1249. dst.splice(i++, 0, value);
  1250. }
  1251. }
  1252. const NG_TEMPLATE_SELECTOR = 'ng-template';
  1253. /**
  1254. * Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive)
  1255. *
  1256. * @param attrs `TAttributes` to search through.
  1257. * @param cssClassToMatch class to match (lowercase)
  1258. * @param isProjectionMode Whether or not class matching should look into the attribute `class` in
  1259. * addition to the `AttributeMarker.Classes`.
  1260. */
  1261. function isCssClassMatching(attrs, cssClassToMatch, isProjectionMode) {
  1262. // TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
  1263. // It is strange to me that sometimes the class information comes in form of `class` attribute
  1264. // and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
  1265. // if that is the right behavior.
  1266. ngDevMode &&
  1267. assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
  1268. let i = 0;
  1269. // Indicates whether we are processing value from the implicit
  1270. // attribute section (i.e. before the first marker in the array).
  1271. let isImplicitAttrsSection = true;
  1272. while (i < attrs.length) {
  1273. let item = attrs[i++];
  1274. if (typeof item === 'string' && isImplicitAttrsSection) {
  1275. const value = attrs[i++];
  1276. if (isProjectionMode && item === 'class') {
  1277. // We found a `class` attribute in the implicit attribute section,
  1278. // check if it matches the value of the `cssClassToMatch` argument.
  1279. if (classIndexOf(value.toLowerCase(), cssClassToMatch, 0) !== -1) {
  1280. return true;
  1281. }
  1282. }
  1283. }
  1284. else if (item === 1 /* AttributeMarker.Classes */) {
  1285. // We found the classes section. Start searching for the class.
  1286. while (i < attrs.length && typeof (item = attrs[i++]) == 'string') {
  1287. // while we have strings
  1288. if (item.toLowerCase() === cssClassToMatch)
  1289. return true;
  1290. }
  1291. return false;
  1292. }
  1293. else if (typeof item === 'number') {
  1294. // We've came across a first marker, which indicates
  1295. // that the implicit attribute section is over.
  1296. isImplicitAttrsSection = false;
  1297. }
  1298. }
  1299. return false;
  1300. }
  1301. /**
  1302. * Checks whether the `tNode` represents an inline template (e.g. `*ngFor`).
  1303. *
  1304. * @param tNode current TNode
  1305. */
  1306. function isInlineTemplate(tNode) {
  1307. return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR;
  1308. }
  1309. /**
  1310. * Function that checks whether a given tNode matches tag-based selector and has a valid type.
  1311. *
  1312. * Matching can be performed in 2 modes: projection mode (when we project nodes) and regular
  1313. * directive matching mode:
  1314. * - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is
  1315. * different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a
  1316. * tag name was extracted from * syntax so we would match the same directive twice);
  1317. * - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing
  1318. * (applicable to TNodeType.Container only).
  1319. */
  1320. function hasTagAndTypeMatch(tNode, currentSelector, isProjectionMode) {
  1321. const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value;
  1322. return currentSelector === tagNameToCompare;
  1323. }
  1324. /**
  1325. * A utility function to match an Ivy node static data against a simple CSS selector
  1326. *
  1327. * @param node static data of the node to match
  1328. * @param selector The selector to try matching against the node.
  1329. * @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing
  1330. * directive matching.
  1331. * @returns true if node matches the selector.
  1332. */
  1333. function isNodeMatchingSelector(tNode, selector, isProjectionMode) {
  1334. ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
  1335. let mode = 4 /* SelectorFlags.ELEMENT */;
  1336. const nodeAttrs = tNode.attrs || [];
  1337. // Find the index of first attribute that has no value, only a name.
  1338. const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs);
  1339. // When processing ":not" selectors, we skip to the next ":not" if the
  1340. // current one doesn't match
  1341. let skipToNextSelector = false;
  1342. for (let i = 0; i < selector.length; i++) {
  1343. const current = selector[i];
  1344. if (typeof current === 'number') {
  1345. // If we finish processing a :not selector and it hasn't failed, return false
  1346. if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) {
  1347. return false;
  1348. }
  1349. // If we are skipping to the next :not() and this mode flag is positive,
  1350. // it's a part of the current :not() selector, and we should keep skipping
  1351. if (skipToNextSelector && isPositive(current))
  1352. continue;
  1353. skipToNextSelector = false;
  1354. mode = current | (mode & 1 /* SelectorFlags.NOT */);
  1355. continue;
  1356. }
  1357. if (skipToNextSelector)
  1358. continue;
  1359. if (mode & 4 /* SelectorFlags.ELEMENT */) {
  1360. mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */;
  1361. if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) ||
  1362. current === '' && selector.length === 1) {
  1363. if (isPositive(mode))
  1364. return false;
  1365. skipToNextSelector = true;
  1366. }
  1367. }
  1368. else {
  1369. const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i];
  1370. // special case for matching against classes when a tNode has been instantiated with
  1371. // class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
  1372. if ((mode & 8 /* SelectorFlags.CLASS */) && tNode.attrs !== null) {
  1373. if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) {
  1374. if (isPositive(mode))
  1375. return false;
  1376. skipToNextSelector = true;
  1377. }
  1378. continue;
  1379. }
  1380. const attrName = (mode & 8 /* SelectorFlags.CLASS */) ? 'class' : current;
  1381. const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
  1382. if (attrIndexInNode === -1) {
  1383. if (isPositive(mode))
  1384. return false;
  1385. skipToNextSelector = true;
  1386. continue;
  1387. }
  1388. if (selectorAttrValue !== '') {
  1389. let nodeAttrValue;
  1390. if (attrIndexInNode > nameOnlyMarkerIdx) {
  1391. nodeAttrValue = '';
  1392. }
  1393. else {
  1394. ngDevMode &&
  1395. assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes');
  1396. // we lowercase the attribute value to be able to match
  1397. // selectors without case-sensitivity
  1398. // (selectors are already in lowercase when generated)
  1399. nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase();
  1400. }
  1401. const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null;
  1402. if (compareAgainstClassName &&
  1403. classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 ||
  1404. mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) {
  1405. if (isPositive(mode))
  1406. return false;
  1407. skipToNextSelector = true;
  1408. }
  1409. }
  1410. }
  1411. }
  1412. return isPositive(mode) || skipToNextSelector;
  1413. }
  1414. function isPositive(mode) {
  1415. return (mode & 1 /* SelectorFlags.NOT */) === 0;
  1416. }
  1417. /**
  1418. * Examines the attribute's definition array for a node to find the index of the
  1419. * attribute that matches the given `name`.
  1420. *
  1421. * NOTE: This will not match namespaced attributes.
  1422. *
  1423. * Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`.
  1424. * The following table summarizes which types of attributes we attempt to match:
  1425. *
  1426. * ===========================================================================================================
  1427. * Modes | Normal Attributes | Bindings Attributes | Template Attributes | I18n
  1428. * Attributes
  1429. * ===========================================================================================================
  1430. * Inline + Projection | YES | YES | NO | YES
  1431. * -----------------------------------------------------------------------------------------------------------
  1432. * Inline + Directive | NO | NO | YES | NO
  1433. * -----------------------------------------------------------------------------------------------------------
  1434. * Non-inline + Projection | YES | YES | NO | YES
  1435. * -----------------------------------------------------------------------------------------------------------
  1436. * Non-inline + Directive | YES | YES | NO | YES
  1437. * ===========================================================================================================
  1438. *
  1439. * @param name the name of the attribute to find
  1440. * @param attrs the attribute array to examine
  1441. * @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`)
  1442. * rather than a manually expanded template node (e.g `<ng-template>`).
  1443. * @param isProjectionMode true if we are matching against content projection otherwise we are
  1444. * matching against directives.
  1445. */
  1446. function findAttrIndexInNode(name, attrs, isInlineTemplate, isProjectionMode) {
  1447. if (attrs === null)
  1448. return -1;
  1449. let i = 0;
  1450. if (isProjectionMode || !isInlineTemplate) {
  1451. let bindingsMode = false;
  1452. while (i < attrs.length) {
  1453. const maybeAttrName = attrs[i];
  1454. if (maybeAttrName === name) {
  1455. return i;
  1456. }
  1457. else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) {
  1458. bindingsMode = true;
  1459. }
  1460. else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) {
  1461. let value = attrs[++i];
  1462. // We should skip classes here because we have a separate mechanism for
  1463. // matching classes in projection mode.
  1464. while (typeof value === 'string') {
  1465. value = attrs[++i];
  1466. }
  1467. continue;
  1468. }
  1469. else if (maybeAttrName === 4 /* AttributeMarker.Template */) {
  1470. // We do not care about Template attributes in this scenario.
  1471. break;
  1472. }
  1473. else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) {
  1474. // Skip the whole namespaced attribute and value. This is by design.
  1475. i += 4;
  1476. continue;
  1477. }
  1478. // In binding mode there are only names, rather than name-value pairs.
  1479. i += bindingsMode ? 1 : 2;
  1480. }
  1481. // We did not match the attribute
  1482. return -1;
  1483. }
  1484. else {
  1485. return matchTemplateAttribute(attrs, name);
  1486. }
  1487. }
  1488. function isNodeMatchingSelectorList(tNode, selector, isProjectionMode = false) {
  1489. for (let i = 0; i < selector.length; i++) {
  1490. if (isNodeMatchingSelector(tNode, selector[i], isProjectionMode)) {
  1491. return true;
  1492. }
  1493. }
  1494. return false;
  1495. }
  1496. function getProjectAsAttrValue(tNode) {
  1497. const nodeAttrs = tNode.attrs;
  1498. if (nodeAttrs != null) {
  1499. const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */);
  1500. // only check for ngProjectAs in attribute names, don't accidentally match attribute's value
  1501. // (attribute names are stored at even indexes)
  1502. if ((ngProjectAsAttrIdx & 1) === 0) {
  1503. return nodeAttrs[ngProjectAsAttrIdx + 1];
  1504. }
  1505. }
  1506. return null;
  1507. }
  1508. function getNameOnlyMarkerIndex(nodeAttrs) {
  1509. for (let i = 0; i < nodeAttrs.length; i++) {
  1510. const nodeAttr = nodeAttrs[i];
  1511. if (isNameOnlyAttributeMarker(nodeAttr)) {
  1512. return i;
  1513. }
  1514. }
  1515. return nodeAttrs.length;
  1516. }
  1517. function matchTemplateAttribute(attrs, name) {
  1518. let i = attrs.indexOf(4 /* AttributeMarker.Template */);
  1519. if (i > -1) {
  1520. i++;
  1521. while (i < attrs.length) {
  1522. const attr = attrs[i];
  1523. // Return in case we checked all template attrs and are switching to the next section in the
  1524. // attrs array (that starts with a number that represents an attribute marker).
  1525. if (typeof attr === 'number')
  1526. return -1;
  1527. if (attr === name)
  1528. return i;
  1529. i++;
  1530. }
  1531. }
  1532. return -1;
  1533. }
  1534. /**
  1535. * Checks whether a selector is inside a CssSelectorList
  1536. * @param selector Selector to be checked.
  1537. * @param list List in which to look for the selector.
  1538. */
  1539. function isSelectorInSelectorList(selector, list) {
  1540. selectorListLoop: for (let i = 0; i < list.length; i++) {
  1541. const currentSelectorInList = list[i];
  1542. if (selector.length !== currentSelectorInList.length) {
  1543. continue;
  1544. }
  1545. for (let j = 0; j < selector.length; j++) {
  1546. if (selector[j] !== currentSelectorInList[j]) {
  1547. continue selectorListLoop;
  1548. }
  1549. }
  1550. return true;
  1551. }
  1552. return false;
  1553. }
  1554. function maybeWrapInNotSelector(isNegativeMode, chunk) {
  1555. return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
  1556. }
  1557. function stringifyCSSSelector(selector) {
  1558. let result = selector[0];
  1559. let i = 1;
  1560. let mode = 2 /* SelectorFlags.ATTRIBUTE */;
  1561. let currentChunk = '';
  1562. let isNegativeMode = false;
  1563. while (i < selector.length) {
  1564. let valueOrMarker = selector[i];
  1565. if (typeof valueOrMarker === 'string') {
  1566. if (mode & 2 /* SelectorFlags.ATTRIBUTE */) {
  1567. const attrValue = selector[++i];
  1568. currentChunk +=
  1569. '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
  1570. }
  1571. else if (mode & 8 /* SelectorFlags.CLASS */) {
  1572. currentChunk += '.' + valueOrMarker;
  1573. }
  1574. else if (mode & 4 /* SelectorFlags.ELEMENT */) {
  1575. currentChunk += ' ' + valueOrMarker;
  1576. }
  1577. }
  1578. else {
  1579. //
  1580. // Append current chunk to the final result in case we come across SelectorFlag, which
  1581. // indicates that the previous section of a selector is over. We need to accumulate content
  1582. // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
  1583. // ```
  1584. // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
  1585. // ```
  1586. // should be transformed to `.classA :not(.classB .classC)`.
  1587. //
  1588. // Note: for negative selector part, we accumulate content between flags until we find the
  1589. // next negative flag. This is needed to support a case where `:not()` rule contains more than
  1590. // one chunk, e.g. the following selector:
  1591. // ```
  1592. // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
  1593. // ```
  1594. // should be stringified to `:not(p.foo) :not(.bar)`
  1595. //
  1596. if (currentChunk !== '' && !isPositive(valueOrMarker)) {
  1597. result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
  1598. currentChunk = '';
  1599. }
  1600. mode = valueOrMarker;
  1601. // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
  1602. // mode is maintained for remaining chunks of a selector.
  1603. isNegativeMode = isNegativeMode || !isPositive(mode);
  1604. }
  1605. i++;
  1606. }
  1607. if (currentChunk !== '') {
  1608. result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
  1609. }
  1610. return result;
  1611. }
  1612. /**
  1613. * Generates string representation of CSS selector in parsed form.
  1614. *
  1615. * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
  1616. * additional parsing at runtime (for example, for directive matching). However in some cases (for
  1617. * example, while bootstrapping a component), a string version of the selector is required to query
  1618. * for the host element on the page. This function takes the parsed form of a selector and returns
  1619. * its string representation.
  1620. *
  1621. * @param selectorList selector in parsed form
  1622. * @returns string representation of a given selector
  1623. */
  1624. function stringifyCSSSelectorList(selectorList) {
  1625. return selectorList.map(stringifyCSSSelector).join(',');
  1626. }
  1627. /**
  1628. * Extracts attributes and classes information from a given CSS selector.
  1629. *
  1630. * This function is used while creating a component dynamically. In this case, the host element
  1631. * (that is created dynamically) should contain attributes and classes specified in component's CSS
  1632. * selector.
  1633. *
  1634. * @param selector CSS selector in parsed form (in a form of array)
  1635. * @returns object with `attrs` and `classes` fields that contain extracted information
  1636. */
  1637. function extractAttrsAndClassesFromSelector(selector) {
  1638. const attrs = [];
  1639. const classes = [];
  1640. let i = 1;
  1641. let mode = 2 /* SelectorFlags.ATTRIBUTE */;
  1642. while (i < selector.length) {
  1643. let valueOrMarker = selector[i];
  1644. if (typeof valueOrMarker === 'string') {
  1645. if (mode === 2 /* SelectorFlags.ATTRIBUTE */) {
  1646. if (valueOrMarker !== '') {
  1647. attrs.push(valueOrMarker, selector[++i]);
  1648. }
  1649. }
  1650. else if (mode === 8 /* SelectorFlags.CLASS */) {
  1651. classes.push(valueOrMarker);
  1652. }
  1653. }
  1654. else {
  1655. // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
  1656. // mode is maintained for remaining chunks of a selector. Since attributes and classes are
  1657. // extracted only for "positive" part of the selector, we can stop here.
  1658. if (!isPositive(mode))
  1659. break;
  1660. mode = valueOrMarker;
  1661. }
  1662. i++;
  1663. }
  1664. return { attrs, classes };
  1665. }
  1666. /**
  1667. * Create a component definition object.
  1668. *
  1669. *
  1670. * # Example
  1671. * ```
  1672. * class MyComponent {
  1673. * // Generated by Angular Template Compiler
  1674. * // [Symbol] syntax will not be supported by TypeScript until v2.7
  1675. * static ɵcmp = defineComponent({
  1676. * ...
  1677. * });
  1678. * }
  1679. * ```
  1680. * @codeGenApi
  1681. */
  1682. function ɵɵdefineComponent(componentDefinition) {
  1683. return noSideEffects(() => {
  1684. // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent.
  1685. // See the `initNgDevMode` docstring for more information.
  1686. (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
  1687. const baseDef = getNgDirectiveDef(componentDefinition);
  1688. const def = {
  1689. ...baseDef,
  1690. decls: componentDefinition.decls,
  1691. vars: componentDefinition.vars,
  1692. template: componentDefinition.template,
  1693. consts: componentDefinition.consts || null,
  1694. ngContentSelectors: componentDefinition.ngContentSelectors,
  1695. onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
  1696. directiveDefs: null,
  1697. pipeDefs: null,
  1698. dependencies: baseDef.standalone && componentDefinition.dependencies || null,
  1699. getStandaloneInjector: null,
  1700. signals: componentDefinition.signals ?? false,
  1701. data: componentDefinition.data || {},
  1702. encapsulation: componentDefinition.encapsulation || ViewEncapsulation$1.Emulated,
  1703. styles: componentDefinition.styles || EMPTY_ARRAY,
  1704. _: null,
  1705. schemas: componentDefinition.schemas || null,
  1706. tView: null,
  1707. id: '',
  1708. };
  1709. initFeatures(def);
  1710. const dependencies = componentDefinition.dependencies;
  1711. def.directiveDefs = extractDefListOrFactory(dependencies, /* pipeDef */ false);
  1712. def.pipeDefs = extractDefListOrFactory(dependencies, /* pipeDef */ true);
  1713. def.id = getComponentId(def);
  1714. return def;
  1715. });
  1716. }
  1717. /**
  1718. * Generated next to NgModules to monkey-patch directive and pipe references onto a component's
  1719. * definition, when generating a direct reference in the component file would otherwise create an
  1720. * import cycle.
  1721. *
  1722. * See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details.
  1723. *
  1724. * @codeGenApi
  1725. */
  1726. function ɵɵsetComponentScope(type, directives, pipes) {
  1727. const def = type.ɵcmp;
  1728. def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */ false);
  1729. def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */ true);
  1730. }
  1731. function extractDirectiveDef(type) {
  1732. return getComponentDef(type) || getDirectiveDef(type);
  1733. }
  1734. function nonNull(value) {
  1735. return value !== null;
  1736. }
  1737. /**
  1738. * @codeGenApi
  1739. */
  1740. function ɵɵdefineNgModule(def) {
  1741. return noSideEffects(() => {
  1742. const res = {
  1743. type: def.type,
  1744. bootstrap: def.bootstrap || EMPTY_ARRAY,
  1745. declarations: def.declarations || EMPTY_ARRAY,
  1746. imports: def.imports || EMPTY_ARRAY,
  1747. exports: def.exports || EMPTY_ARRAY,
  1748. transitiveCompileScopes: null,
  1749. schemas: def.schemas || null,
  1750. id: def.id || null,
  1751. };
  1752. return res;
  1753. });
  1754. }
  1755. /**
  1756. * Adds the module metadata that is necessary to compute the module's transitive scope to an
  1757. * existing module definition.
  1758. *
  1759. * Scope metadata of modules is not used in production builds, so calls to this function can be
  1760. * marked pure to tree-shake it from the bundle, allowing for all referenced declarations
  1761. * to become eligible for tree-shaking as well.
  1762. *
  1763. * @codeGenApi
  1764. */
  1765. function ɵɵsetNgModuleScope(type, scope) {
  1766. return noSideEffects(() => {
  1767. const ngModuleDef = getNgModuleDef(type, true);
  1768. ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
  1769. ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
  1770. ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
  1771. });
  1772. }
  1773. /**
  1774. * Inverts an inputs or outputs lookup such that the keys, which were the
  1775. * minified keys, are part of the values, and the values are parsed so that
  1776. * the publicName of the property is the new key
  1777. *
  1778. * e.g. for
  1779. *
  1780. * ```
  1781. * class Comp {
  1782. * @Input()
  1783. * propName1: string;
  1784. *
  1785. * @Input('publicName2')
  1786. * declaredPropName2: number;
  1787. * }
  1788. * ```
  1789. *
  1790. * will be serialized as
  1791. *
  1792. * ```
  1793. * {
  1794. * propName1: 'propName1',
  1795. * declaredPropName2: ['publicName2', 'declaredPropName2'],
  1796. * }
  1797. * ```
  1798. *
  1799. * which is than translated by the minifier as:
  1800. *
  1801. * ```
  1802. * {
  1803. * minifiedPropName1: 'propName1',
  1804. * minifiedPropName2: ['publicName2', 'declaredPropName2'],
  1805. * }
  1806. * ```
  1807. *
  1808. * becomes: (public name => minifiedName)
  1809. *
  1810. * ```
  1811. * {
  1812. * 'propName1': 'minifiedPropName1',
  1813. * 'publicName2': 'minifiedPropName2',
  1814. * }
  1815. * ```
  1816. *
  1817. * Optionally the function can take `secondary` which will result in: (public name => declared name)
  1818. *
  1819. * ```
  1820. * {
  1821. * 'propName1': 'propName1',
  1822. * 'publicName2': 'declaredPropName2',
  1823. * }
  1824. * ```
  1825. *
  1826. */
  1827. function invertObject(obj, secondary) {
  1828. if (obj == null)
  1829. return EMPTY_OBJ;
  1830. const newLookup = {};
  1831. for (const minifiedKey in obj) {
  1832. if (obj.hasOwnProperty(minifiedKey)) {
  1833. let publicName = obj[minifiedKey];
  1834. let declaredName = publicName;
  1835. if (Array.isArray(publicName)) {
  1836. declaredName = publicName[1];
  1837. publicName = publicName[0];
  1838. }
  1839. newLookup[publicName] = minifiedKey;
  1840. if (secondary) {
  1841. (secondary[publicName] = declaredName);
  1842. }
  1843. }
  1844. }
  1845. return newLookup;
  1846. }
  1847. /**
  1848. * Create a directive definition object.
  1849. *
  1850. * # Example
  1851. * ```ts
  1852. * class MyDirective {
  1853. * // Generated by Angular Template Compiler
  1854. * // [Symbol] syntax will not be supported by TypeScript until v2.7
  1855. * static ɵdir = ɵɵdefineDirective({
  1856. * ...
  1857. * });
  1858. * }
  1859. * ```
  1860. *
  1861. * @codeGenApi
  1862. */
  1863. function ɵɵdefineDirective(directiveDefinition) {
  1864. return noSideEffects(() => {
  1865. const def = getNgDirectiveDef(directiveDefinition);
  1866. initFeatures(def);
  1867. return def;
  1868. });
  1869. }
  1870. /**
  1871. * Create a pipe definition object.
  1872. *
  1873. * # Example
  1874. * ```
  1875. * class MyPipe implements PipeTransform {
  1876. * // Generated by Angular Template Compiler
  1877. * static ɵpipe = definePipe({
  1878. * ...
  1879. * });
  1880. * }
  1881. * ```
  1882. * @param pipeDef Pipe definition generated by the compiler
  1883. *
  1884. * @codeGenApi
  1885. */
  1886. function ɵɵdefinePipe(pipeDef) {
  1887. return {
  1888. type: pipeDef.type,
  1889. name: pipeDef.name,
  1890. factory: null,
  1891. pure: pipeDef.pure !== false,
  1892. standalone: pipeDef.standalone === true,
  1893. onDestroy: pipeDef.type.prototype.ngOnDestroy || null
  1894. };
  1895. }
  1896. /**
  1897. * The following getter methods retrieve the definition from the type. Currently the retrieval
  1898. * honors inheritance, but in the future we may change the rule to require that definitions are
  1899. * explicit. This would require some sort of migration strategy.
  1900. */
  1901. function getComponentDef(type) {
  1902. return type[NG_COMP_DEF] || null;
  1903. }
  1904. function getDirectiveDef(type) {
  1905. return type[NG_DIR_DEF] || null;
  1906. }
  1907. function getPipeDef$1(type) {
  1908. return type[NG_PIPE_DEF] || null;
  1909. }
  1910. /**
  1911. * Checks whether a given Component, Directive or Pipe is marked as standalone.
  1912. * This will return false if passed anything other than a Component, Directive, or Pipe class
  1913. * See [this guide](/guide/standalone-components) for additional information:
  1914. *
  1915. * @param type A reference to a Component, Directive or Pipe.
  1916. * @publicApi
  1917. */
  1918. function isStandalone(type) {
  1919. const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef$1(type);
  1920. return def !== null ? def.standalone : false;
  1921. }
  1922. function getNgModuleDef(type, throwNotFound) {
  1923. const ngModuleDef = type[NG_MOD_DEF] || null;
  1924. if (!ngModuleDef && throwNotFound === true) {
  1925. throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`);
  1926. }
  1927. return ngModuleDef;
  1928. }
  1929. function getNgDirectiveDef(directiveDefinition) {
  1930. const declaredInputs = {};
  1931. return {
  1932. type: directiveDefinition.type,
  1933. providersResolver: null,
  1934. factory: null,
  1935. hostBindings: directiveDefinition.hostBindings || null,
  1936. hostVars: directiveDefinition.hostVars || 0,
  1937. hostAttrs: directiveDefinition.hostAttrs || null,
  1938. contentQueries: directiveDefinition.contentQueries || null,
  1939. declaredInputs,
  1940. inputTransforms: null,
  1941. inputConfig: directiveDefinition.inputs || EMPTY_OBJ,
  1942. exportAs: directiveDefinition.exportAs || null,
  1943. standalone: directiveDefinition.standalone === true,
  1944. signals: directiveDefinition.signals === true,
  1945. selectors: directiveDefinition.selectors || EMPTY_ARRAY,
  1946. viewQuery: directiveDefinition.viewQuery || null,
  1947. features: directiveDefinition.features || null,
  1948. setInput: null,
  1949. findHostDirectiveDefs: null,
  1950. hostDirectives: null,
  1951. inputs: invertObject(directiveDefinition.inputs, declaredInputs),
  1952. outputs: invertObject(directiveDefinition.outputs),
  1953. };
  1954. }
  1955. function initFeatures(definition) {
  1956. definition.features?.forEach((fn) => fn(definition));
  1957. }
  1958. function extractDefListOrFactory(dependencies, pipeDef) {
  1959. if (!dependencies) {
  1960. return null;
  1961. }
  1962. const defExtractor = pipeDef ? getPipeDef$1 : extractDirectiveDef;
  1963. return () => (typeof dependencies === 'function' ? dependencies() : dependencies)
  1964. .map(dep => defExtractor(dep))
  1965. .filter(nonNull);
  1966. }
  1967. /**
  1968. * A map that contains the generated component IDs and type.
  1969. */
  1970. const GENERATED_COMP_IDS = new Map();
  1971. /**
  1972. * A method can returns a component ID from the component definition using a variant of DJB2 hash
  1973. * algorithm.
  1974. */
  1975. function getComponentId(componentDef) {
  1976. let hash = 0;
  1977. // We cannot rely solely on the component selector as the same selector can be used in different
  1978. // modules.
  1979. //
  1980. // `componentDef.style` is not used, due to it causing inconsistencies. Ex: when server
  1981. // component styles has no sourcemaps and browsers do.
  1982. //
  1983. // Example:
  1984. // https://github.com/angular/components/blob/d9f82c8f95309e77a6d82fd574c65871e91354c2/src/material/core/option/option.ts#L248
  1985. // https://github.com/angular/components/blob/285f46dc2b4c5b127d356cb7c4714b221f03ce50/src/material/legacy-core/option/option.ts#L32
  1986. const hashSelectors = [
  1987. componentDef.selectors,
  1988. componentDef.ngContentSelectors,
  1989. componentDef.hostVars,
  1990. componentDef.hostAttrs,
  1991. componentDef.consts,
  1992. componentDef.vars,
  1993. componentDef.decls,
  1994. componentDef.encapsulation,
  1995. componentDef.standalone,
  1996. componentDef.signals,
  1997. componentDef.exportAs,
  1998. JSON.stringify(componentDef.inputs),
  1999. JSON.stringify(componentDef.outputs),
  2000. // We cannot use 'componentDef.type.name' as the name of the symbol will change and will not
  2001. // match in the server and browser bundles.
  2002. Object.getOwnPropertyNames(componentDef.type.prototype),
  2003. !!componentDef.contentQueries,
  2004. !!componentDef.viewQuery,
  2005. ].join('|');
  2006. for (const char of hashSelectors) {
  2007. hash = Math.imul(31, hash) + char.charCodeAt(0) << 0;
  2008. }
  2009. // Force positive number hash.
  2010. // 2147483647 = equivalent of Integer.MAX_VALUE.
  2011. hash += 2147483647 + 1;
  2012. const compId = 'c' + hash;
  2013. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  2014. if (GENERATED_COMP_IDS.has(compId)) {
  2015. const previousCompDefType = GENERATED_COMP_IDS.get(compId);
  2016. if (previousCompDefType !== componentDef.type) {
  2017. console.warn(formatRuntimeError(-912 /* RuntimeErrorCode.COMPONENT_ID_COLLISION */, `Component ID generation collision detected. Components '${previousCompDefType.name}' and '${componentDef.type.name}' with selector '${stringifyCSSSelectorList(componentDef
  2018. .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.`));
  2019. }
  2020. }
  2021. else {
  2022. GENERATED_COMP_IDS.set(compId, componentDef.type);
  2023. }
  2024. }
  2025. return compId;
  2026. }
  2027. // Below are constants for LView indices to help us look up LView members
  2028. // without having to remember the specific indices.
  2029. // Uglify will inline these when minifying so there shouldn't be a cost.
  2030. const HOST = 0;
  2031. const TVIEW = 1;
  2032. const FLAGS = 2;
  2033. const PARENT = 3;
  2034. const NEXT = 4;
  2035. const DESCENDANT_VIEWS_TO_REFRESH = 5;
  2036. const T_HOST = 6;
  2037. const CLEANUP = 7;
  2038. const CONTEXT = 8;
  2039. const INJECTOR$1 = 9;
  2040. const ENVIRONMENT = 10;
  2041. const RENDERER = 11;
  2042. const CHILD_HEAD = 12;
  2043. const CHILD_TAIL = 13;
  2044. // FIXME(misko): Investigate if the three declarations aren't all same thing.
  2045. const DECLARATION_VIEW = 14;
  2046. const DECLARATION_COMPONENT_VIEW = 15;
  2047. const DECLARATION_LCONTAINER = 16;
  2048. const PREORDER_HOOK_FLAGS = 17;
  2049. const QUERIES = 18;
  2050. const ID = 19;
  2051. const EMBEDDED_VIEW_INJECTOR = 20;
  2052. const ON_DESTROY_HOOKS = 21;
  2053. const HYDRATION = 22;
  2054. const REACTIVE_TEMPLATE_CONSUMER = 23;
  2055. const REACTIVE_HOST_BINDING_CONSUMER = 24;
  2056. /**
  2057. * Size of LView's header. Necessary to adjust for it when setting slots.
  2058. *
  2059. * IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
  2060. * instruction index into `LView` index. All other indexes should be in the `LView` index space and
  2061. * there should be no need to refer to `HEADER_OFFSET` anywhere else.
  2062. */
  2063. const HEADER_OFFSET = 25;
  2064. // Note: This hack is necessary so we don't erroneously get a circular dependency
  2065. // failure based on types.
  2066. const unusedValueExportToPlacateAjd$4 = 1;
  2067. /**
  2068. * Special location which allows easy identification of type. If we have an array which was
  2069. * retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is
  2070. * `LContainer`.
  2071. */
  2072. const TYPE = 1;
  2073. /**
  2074. * Below are constants for LContainer indices to help us look up LContainer members
  2075. * without having to remember the specific indices.
  2076. * Uglify will inline these when minifying so there shouldn't be a cost.
  2077. */
  2078. /**
  2079. * Flag to signify that this `LContainer` may have transplanted views which need to be change
  2080. * detected. (see: `LView[DECLARATION_COMPONENT_VIEW])`.
  2081. *
  2082. * This flag, once set, is never unset for the `LContainer`. This means that when unset we can skip
  2083. * a lot of work in `refreshEmbeddedViews`. But when set we still need to verify
  2084. * that the `MOVED_VIEWS` are transplanted and on-push.
  2085. */
  2086. const HAS_TRANSPLANTED_VIEWS = 2;
  2087. // PARENT, NEXT, DESCENDANT_VIEWS_TO_REFRESH are indices 3, 4, and 5
  2088. // As we already have these constants in LView, we don't need to re-create them.
  2089. // T_HOST is index 6
  2090. // We already have this constants in LView, we don't need to re-create it.
  2091. const NATIVE = 7;
  2092. const VIEW_REFS = 8;
  2093. const MOVED_VIEWS = 9;
  2094. const DEHYDRATED_VIEWS = 10;
  2095. /**
  2096. * Size of LContainer's header. Represents the index after which all views in the
  2097. * container will be inserted. We need to keep a record of current views so we know
  2098. * which views are already in the DOM (and don't need to be re-added) and so we can
  2099. * remove views from the DOM when they are no longer required.
  2100. */
  2101. const CONTAINER_HEADER_OFFSET = 11;
  2102. // Note: This hack is necessary so we don't erroneously get a circular dependency
  2103. // failure based on types.
  2104. const unusedValueExportToPlacateAjd$3 = 1;
  2105. /**
  2106. * True if `value` is `LView`.
  2107. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  2108. */
  2109. function isLView(value) {
  2110. return Array.isArray(value) && typeof value[TYPE] === 'object';
  2111. }
  2112. /**
  2113. * True if `value` is `LContainer`.
  2114. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  2115. */
  2116. function isLContainer(value) {
  2117. return Array.isArray(value) && value[TYPE] === true;
  2118. }
  2119. function isContentQueryHost(tNode) {
  2120. return (tNode.flags & 4 /* TNodeFlags.hasContentQuery */) !== 0;
  2121. }
  2122. function isComponentHost(tNode) {
  2123. return tNode.componentOffset > -1;
  2124. }
  2125. function isDirectiveHost(tNode) {
  2126. return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */;
  2127. }
  2128. function isComponentDef(def) {
  2129. return !!def.template;
  2130. }
  2131. function isRootView(target) {
  2132. return (target[FLAGS] & 512 /* LViewFlags.IsRoot */) !== 0;
  2133. }
  2134. function isProjectionTNode(tNode) {
  2135. return (tNode.type & 16 /* TNodeType.Projection */) === 16 /* TNodeType.Projection */;
  2136. }
  2137. function hasI18n(lView) {
  2138. return (lView[FLAGS] & 32 /* LViewFlags.HasI18n */) === 32 /* LViewFlags.HasI18n */;
  2139. }
  2140. // [Assert functions do not constraint type when they are guarded by a truthy
  2141. // expression.](https://github.com/microsoft/TypeScript/issues/37295)
  2142. function assertTNodeForLView(tNode, lView) {
  2143. assertTNodeForTView(tNode, lView[TVIEW]);
  2144. }
  2145. function assertTNodeForTView(tNode, tView) {
  2146. assertTNode(tNode);
  2147. const tData = tView.data;
  2148. for (let i = HEADER_OFFSET; i < tData.length; i++) {
  2149. if (tData[i] === tNode) {
  2150. return;
  2151. }
  2152. }
  2153. throwError('This TNode does not belong to this TView.');
  2154. }
  2155. function assertTNode(tNode) {
  2156. assertDefined(tNode, 'TNode must be defined');
  2157. if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) {
  2158. throwError('Not of type TNode, got: ' + tNode);
  2159. }
  2160. }
  2161. function assertTIcu(tIcu) {
  2162. assertDefined(tIcu, 'Expected TIcu to be defined');
  2163. if (!(typeof tIcu.currentCaseLViewIndex === 'number')) {
  2164. throwError('Object is not of TIcu type.');
  2165. }
  2166. }
  2167. function assertComponentType(actual, msg = 'Type passed in is not ComponentType, it does not have \'ɵcmp\' property.') {
  2168. if (!getComponentDef(actual)) {
  2169. throwError(msg);
  2170. }
  2171. }
  2172. function assertNgModuleType(actual, msg = 'Type passed in is not NgModuleType, it does not have \'ɵmod\' property.') {
  2173. if (!getNgModuleDef(actual)) {
  2174. throwError(msg);
  2175. }
  2176. }
  2177. function assertCurrentTNodeIsParent(isParent) {
  2178. assertEqual(isParent, true, 'currentTNode should be a parent');
  2179. }
  2180. function assertHasParent(tNode) {
  2181. assertDefined(tNode, 'currentTNode should exist!');
  2182. assertDefined(tNode.parent, 'currentTNode should have a parent');
  2183. }
  2184. function assertLContainer(value) {
  2185. assertDefined(value, 'LContainer must be defined');
  2186. assertEqual(isLContainer(value), true, 'Expecting LContainer');
  2187. }
  2188. function assertLViewOrUndefined(value) {
  2189. value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
  2190. }
  2191. function assertLView(value) {
  2192. assertDefined(value, 'LView must be defined');
  2193. assertEqual(isLView(value), true, 'Expecting LView');
  2194. }
  2195. function assertFirstCreatePass(tView, errMessage) {
  2196. assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.');
  2197. }
  2198. function assertFirstUpdatePass(tView, errMessage) {
  2199. assertEqual(tView.firstUpdatePass, true, errMessage || 'Should only be called in first update pass.');
  2200. }
  2201. /**
  2202. * This is a basic sanity check that an object is probably a directive def. DirectiveDef is
  2203. * an interface, so we can't do a direct instanceof check.
  2204. */
  2205. function assertDirectiveDef(obj) {
  2206. if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
  2207. throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
  2208. }
  2209. }
  2210. function assertIndexInDeclRange(lView, index) {
  2211. const tView = lView[1];
  2212. assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index);
  2213. }
  2214. function assertIndexInExpandoRange(lView, index) {
  2215. const tView = lView[1];
  2216. assertBetween(tView.expandoStartIndex, lView.length, index);
  2217. }
  2218. function assertBetween(lower, upper, index) {
  2219. if (!(lower <= index && index < upper)) {
  2220. throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`);
  2221. }
  2222. }
  2223. function assertProjectionSlots(lView, errMessage) {
  2224. assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.');
  2225. assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, errMessage ||
  2226. 'Components with projection nodes (<ng-content>) must have projection slots defined.');
  2227. }
  2228. function assertParentView(lView, errMessage) {
  2229. assertDefined(lView, errMessage || 'Component views should always have a parent view (component\'s host view)');
  2230. }
  2231. /**
  2232. * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a
  2233. * NodeInjector data structure.
  2234. *
  2235. * @param lView `LView` which should be checked.
  2236. * @param injectorIndex index into the `LView` where the `NodeInjector` is expected.
  2237. */
  2238. function assertNodeInjector(lView, injectorIndex) {
  2239. assertIndexInExpandoRange(lView, injectorIndex);
  2240. assertIndexInExpandoRange(lView, injectorIndex + 8 /* NodeInjectorOffset.PARENT */);
  2241. assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter');
  2242. assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter');
  2243. assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter');
  2244. assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter');
  2245. assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter');
  2246. assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter');
  2247. assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter');
  2248. assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter');
  2249. assertNumber(lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */], 'injectorIndex should point to parent injector');
  2250. }
  2251. function getFactoryDef(type, throwNotFound) {
  2252. const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF);
  2253. if (!hasFactoryDef && throwNotFound === true && ngDevMode) {
  2254. throw new Error(`Type ${stringify(type)} does not have 'ɵfac' property.`);
  2255. }
  2256. return hasFactoryDef ? type[NG_FACTORY_DEF] : null;
  2257. }
  2258. /**
  2259. * Symbol used to tell `Signal`s apart from other functions.
  2260. *
  2261. * This can be used to auto-unwrap signals in various cases, or to auto-wrap non-signal values.
  2262. */
  2263. const SIGNAL = /* @__PURE__ */ Symbol('SIGNAL');
  2264. /**
  2265. * Checks if the given `value` is a reactive `Signal`.
  2266. *
  2267. * @developerPreview
  2268. */
  2269. function isSignal(value) {
  2270. return typeof value === 'function' && value[SIGNAL] !== undefined;
  2271. }
  2272. /**
  2273. * The default equality function used for `signal` and `computed`, which treats objects and arrays
  2274. * as never equal, and all other primitive values using identity semantics.
  2275. *
  2276. * This allows signals to hold non-primitive values (arrays, objects, other collections) and still
  2277. * propagate change notification upon explicit mutation without identity change.
  2278. *
  2279. * @developerPreview
  2280. */
  2281. function defaultEquals(a, b) {
  2282. // `Object.is` compares two values using identity semantics which is desired behavior for
  2283. // primitive values. If `Object.is` determines two values to be equal we need to make sure that
  2284. // those don't represent objects (we want to make sure that 2 objects are always considered
  2285. // "unequal"). The null check is needed for the special case of JavaScript reporting null values
  2286. // as objects (`typeof null === 'object'`).
  2287. return (a === null || typeof a !== 'object') && Object.is(a, b);
  2288. }
  2289. // Required as the signals library is in a separate package, so we need to explicitly ensure the
  2290. /**
  2291. * The currently active consumer `ReactiveNode`, if running code in a reactive context.
  2292. *
  2293. * Change this via `setActiveConsumer`.
  2294. */
  2295. let activeConsumer = null;
  2296. let inNotificationPhase = false;
  2297. function setActiveConsumer(consumer) {
  2298. const prev = activeConsumer;
  2299. activeConsumer = consumer;
  2300. return prev;
  2301. }
  2302. const REACTIVE_NODE = {
  2303. version: 0,
  2304. dirty: false,
  2305. producerNode: undefined,
  2306. producerLastReadVersion: undefined,
  2307. producerIndexOfThis: undefined,
  2308. nextProducerIndex: 0,
  2309. liveConsumerNode: undefined,
  2310. liveConsumerIndexOfThis: undefined,
  2311. consumerAllowSignalWrites: false,
  2312. consumerIsAlwaysLive: false,
  2313. producerMustRecompute: () => false,
  2314. producerRecomputeValue: () => { },
  2315. consumerMarkedDirty: () => { },
  2316. };
  2317. /**
  2318. * Called by implementations when a producer's signal is read.
  2319. */
  2320. function producerAccessed(node) {
  2321. if (inNotificationPhase) {
  2322. throw new Error(typeof ngDevMode !== 'undefined' && ngDevMode ?
  2323. `Assertion error: signal read during notification phase` :
  2324. '');
  2325. }
  2326. if (activeConsumer === null) {
  2327. // Accessed outside of a reactive context, so nothing to record.
  2328. return;
  2329. }
  2330. // This producer is the `idx`th dependency of `activeConsumer`.
  2331. const idx = activeConsumer.nextProducerIndex++;
  2332. assertConsumerNode(activeConsumer);
  2333. if (idx < activeConsumer.producerNode.length && activeConsumer.producerNode[idx] !== node) {
  2334. // There's been a change in producers since the last execution of `activeConsumer`.
  2335. // `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
  2336. // replaced with `this`.
  2337. //
  2338. // If `activeConsumer` isn't live, then this is a no-op, since we can replace the producer in
  2339. // `activeConsumer.producerNode` directly. However, if `activeConsumer` is live, then we need
  2340. // to remove it from the stale producer's `liveConsumer`s.
  2341. if (consumerIsLive(activeConsumer)) {
  2342. const staleProducer = activeConsumer.producerNode[idx];
  2343. producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx]);
  2344. // At this point, the only record of `staleProducer` is the reference at
  2345. // `activeConsumer.producerNode[idx]` which will be overwritten below.
  2346. }
  2347. }
  2348. if (activeConsumer.producerNode[idx] !== node) {
  2349. // We're a new dependency of the consumer (at `idx`).
  2350. activeConsumer.producerNode[idx] = node;
  2351. // If the active consumer is live, then add it as a live consumer. If not, then use 0 as a
  2352. // placeholder value.
  2353. activeConsumer.producerIndexOfThis[idx] =
  2354. consumerIsLive(activeConsumer) ? producerAddLiveConsumer(node, activeConsumer, idx) : 0;
  2355. }
  2356. activeConsumer.producerLastReadVersion[idx] = node.version;
  2357. }
  2358. /**
  2359. * Ensure this producer's `version` is up-to-date.
  2360. */
  2361. function producerUpdateValueVersion(node) {
  2362. if (consumerIsLive(node) && !node.dirty) {
  2363. // A live consumer will be marked dirty by producers, so a clean state means that its version
  2364. // is guaranteed to be up-to-date.
  2365. return;
  2366. }
  2367. if (!node.producerMustRecompute(node) && !consumerPollProducersForChange(node)) {
  2368. // None of our producers report a change since the last time they were read, so no
  2369. // recomputation of our value is necessary, and we can consider ourselves clean.
  2370. node.dirty = false;
  2371. return;
  2372. }
  2373. node.producerRecomputeValue(node);
  2374. // After recomputing the value, we're no longer dirty.
  2375. node.dirty = false;
  2376. }
  2377. /**
  2378. * Propagate a dirty notification to live consumers of this producer.
  2379. */
  2380. function producerNotifyConsumers(node) {
  2381. if (node.liveConsumerNode === undefined) {
  2382. return;
  2383. }
  2384. // Prevent signal reads when we're updating the graph
  2385. const prev = inNotificationPhase;
  2386. inNotificationPhase = true;
  2387. try {
  2388. for (const consumer of node.liveConsumerNode) {
  2389. if (!consumer.dirty) {
  2390. consumerMarkDirty(consumer);
  2391. }
  2392. }
  2393. }
  2394. finally {
  2395. inNotificationPhase = prev;
  2396. }
  2397. }
  2398. /**
  2399. * Whether this `ReactiveNode` in its producer capacity is currently allowed to initiate updates,
  2400. * based on the current consumer context.
  2401. */
  2402. function producerUpdatesAllowed() {
  2403. return activeConsumer?.consumerAllowSignalWrites !== false;
  2404. }
  2405. function consumerMarkDirty(node) {
  2406. node.dirty = true;
  2407. producerNotifyConsumers(node);
  2408. node.consumerMarkedDirty?.(node);
  2409. }
  2410. /**
  2411. * Prepare this consumer to run a computation in its reactive context.
  2412. *
  2413. * Must be called by subclasses which represent reactive computations, before those computations
  2414. * begin.
  2415. */
  2416. function consumerBeforeComputation(node) {
  2417. node && (node.nextProducerIndex = 0);
  2418. return setActiveConsumer(node);
  2419. }
  2420. /**
  2421. * Finalize this consumer's state after a reactive computation has run.
  2422. *
  2423. * Must be called by subclasses which represent reactive computations, after those computations
  2424. * have finished.
  2425. */
  2426. function consumerAfterComputation(node, prevConsumer) {
  2427. setActiveConsumer(prevConsumer);
  2428. if (!node || node.producerNode === undefined || node.producerIndexOfThis === undefined ||
  2429. node.producerLastReadVersion === undefined) {
  2430. return;
  2431. }
  2432. if (consumerIsLive(node)) {
  2433. // For live consumers, we need to remove the producer -> consumer edge for any stale producers
  2434. // which weren't dependencies after the recomputation.
  2435. for (let i = node.nextProducerIndex; i < node.producerNode.length; i++) {
  2436. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  2437. }
  2438. }
  2439. // Truncate the producer tracking arrays.
  2440. // Perf note: this is essentially truncating the length to `node.nextProducerIndex`, but
  2441. // benchmarking has shown that individual pop operations are faster.
  2442. while (node.producerNode.length > node.nextProducerIndex) {
  2443. node.producerNode.pop();
  2444. node.producerLastReadVersion.pop();
  2445. node.producerIndexOfThis.pop();
  2446. }
  2447. }
  2448. /**
  2449. * Determine whether this consumer has any dependencies which have changed since the last time
  2450. * they were read.
  2451. */
  2452. function consumerPollProducersForChange(node) {
  2453. assertConsumerNode(node);
  2454. // Poll producers for change.
  2455. for (let i = 0; i < node.producerNode.length; i++) {
  2456. const producer = node.producerNode[i];
  2457. const seenVersion = node.producerLastReadVersion[i];
  2458. // First check the versions. A mismatch means that the producer's value is known to have
  2459. // changed since the last time we read it.
  2460. if (seenVersion !== producer.version) {
  2461. return true;
  2462. }
  2463. // The producer's version is the same as the last time we read it, but it might itself be
  2464. // stale. Force the producer to recompute its version (calculating a new value if necessary).
  2465. producerUpdateValueVersion(producer);
  2466. // Now when we do this check, `producer.version` is guaranteed to be up to date, so if the
  2467. // versions still match then it has not changed since the last time we read it.
  2468. if (seenVersion !== producer.version) {
  2469. return true;
  2470. }
  2471. }
  2472. return false;
  2473. }
  2474. /**
  2475. * Disconnect this consumer from the graph.
  2476. */
  2477. function consumerDestroy(node) {
  2478. assertConsumerNode(node);
  2479. if (consumerIsLive(node)) {
  2480. // Drop all connections from the graph to this node.
  2481. for (let i = 0; i < node.producerNode.length; i++) {
  2482. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  2483. }
  2484. }
  2485. // Truncate all the arrays to drop all connection from this node to the graph.
  2486. node.producerNode.length = node.producerLastReadVersion.length = node.producerIndexOfThis.length =
  2487. 0;
  2488. if (node.liveConsumerNode) {
  2489. node.liveConsumerNode.length = node.liveConsumerIndexOfThis.length = 0;
  2490. }
  2491. }
  2492. /**
  2493. * Add `consumer` as a live consumer of this node.
  2494. *
  2495. * Note that this operation is potentially transitive. If this node becomes live, then it becomes
  2496. * a live consumer of all of its current producers.
  2497. */
  2498. function producerAddLiveConsumer(node, consumer, indexOfThis) {
  2499. assertProducerNode(node);
  2500. assertConsumerNode(node);
  2501. if (node.liveConsumerNode.length === 0) {
  2502. // When going from 0 to 1 live consumers, we become a live consumer to our producers.
  2503. for (let i = 0; i < node.producerNode.length; i++) {
  2504. node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i);
  2505. }
  2506. }
  2507. node.liveConsumerIndexOfThis.push(indexOfThis);
  2508. return node.liveConsumerNode.push(consumer) - 1;
  2509. }
  2510. /**
  2511. * Remove the live consumer at `idx`.
  2512. */
  2513. function producerRemoveLiveConsumerAtIndex(node, idx) {
  2514. assertProducerNode(node);
  2515. assertConsumerNode(node);
  2516. if (typeof ngDevMode !== 'undefined' && ngDevMode && idx >= node.liveConsumerNode.length) {
  2517. throw new Error(`Assertion error: active consumer index ${idx} is out of bounds of ${node.liveConsumerNode.length} consumers)`);
  2518. }
  2519. if (node.liveConsumerNode.length === 1) {
  2520. // When removing the last live consumer, we will no longer be live. We need to remove
  2521. // ourselves from our producers' tracking (which may cause consumer-producers to lose
  2522. // liveness as well).
  2523. for (let i = 0; i < node.producerNode.length; i++) {
  2524. producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i]);
  2525. }
  2526. }
  2527. // Move the last value of `liveConsumers` into `idx`. Note that if there's only a single
  2528. // live consumer, this is a no-op.
  2529. const lastIdx = node.liveConsumerNode.length - 1;
  2530. node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx];
  2531. node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx];
  2532. // Truncate the array.
  2533. node.liveConsumerNode.length--;
  2534. node.liveConsumerIndexOfThis.length--;
  2535. // If the index is still valid, then we need to fix the index pointer from the producer to this
  2536. // consumer, and update it from `lastIdx` to `idx` (accounting for the move above).
  2537. if (idx < node.liveConsumerNode.length) {
  2538. const idxProducer = node.liveConsumerIndexOfThis[idx];
  2539. const consumer = node.liveConsumerNode[idx];
  2540. assertConsumerNode(consumer);
  2541. consumer.producerIndexOfThis[idxProducer] = idx;
  2542. }
  2543. }
  2544. function consumerIsLive(node) {
  2545. return node.consumerIsAlwaysLive || (node?.liveConsumerNode?.length ?? 0) > 0;
  2546. }
  2547. function assertConsumerNode(node) {
  2548. node.producerNode ??= [];
  2549. node.producerIndexOfThis ??= [];
  2550. node.producerLastReadVersion ??= [];
  2551. }
  2552. function assertProducerNode(node) {
  2553. node.liveConsumerNode ??= [];
  2554. node.liveConsumerIndexOfThis ??= [];
  2555. }
  2556. /**
  2557. * Create a computed `Signal` which derives a reactive value from an expression.
  2558. *
  2559. * @developerPreview
  2560. */
  2561. function computed(computation, options) {
  2562. const node = Object.create(COMPUTED_NODE);
  2563. node.computation = computation;
  2564. options?.equal && (node.equal = options.equal);
  2565. const computed = () => {
  2566. // Check if the value needs updating before returning it.
  2567. producerUpdateValueVersion(node);
  2568. // Record that someone looked at this signal.
  2569. producerAccessed(node);
  2570. if (node.value === ERRORED) {
  2571. throw node.error;
  2572. }
  2573. return node.value;
  2574. };
  2575. computed[SIGNAL] = node;
  2576. return computed;
  2577. }
  2578. /**
  2579. * A dedicated symbol used before a computed value has been calculated for the first time.
  2580. * Explicitly typed as `any` so we can use it as signal's value.
  2581. */
  2582. const UNSET = /* @__PURE__ */ Symbol('UNSET');
  2583. /**
  2584. * A dedicated symbol used in place of a computed signal value to indicate that a given computation
  2585. * is in progress. Used to detect cycles in computation chains.
  2586. * Explicitly typed as `any` so we can use it as signal's value.
  2587. */
  2588. const COMPUTING = /* @__PURE__ */ Symbol('COMPUTING');
  2589. /**
  2590. * A dedicated symbol used in place of a computed signal value to indicate that a given computation
  2591. * failed. The thrown error is cached until the computation gets dirty again.
  2592. * Explicitly typed as `any` so we can use it as signal's value.
  2593. */
  2594. const ERRORED = /* @__PURE__ */ Symbol('ERRORED');
  2595. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  2596. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  2597. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  2598. const COMPUTED_NODE = /* @__PURE__ */ (() => {
  2599. return {
  2600. ...REACTIVE_NODE,
  2601. value: UNSET,
  2602. dirty: true,
  2603. error: null,
  2604. equal: defaultEquals,
  2605. producerMustRecompute(node) {
  2606. // Force a recomputation if there's no current value, or if the current value is in the
  2607. // process of being calculated (which should throw an error).
  2608. return node.value === UNSET || node.value === COMPUTING;
  2609. },
  2610. producerRecomputeValue(node) {
  2611. if (node.value === COMPUTING) {
  2612. // Our computation somehow led to a cyclic read of itself.
  2613. throw new Error('Detected cycle in computations.');
  2614. }
  2615. const oldValue = node.value;
  2616. node.value = COMPUTING;
  2617. const prevConsumer = consumerBeforeComputation(node);
  2618. let newValue;
  2619. try {
  2620. newValue = node.computation();
  2621. }
  2622. catch (err) {
  2623. newValue = ERRORED;
  2624. node.error = err;
  2625. }
  2626. finally {
  2627. consumerAfterComputation(node, prevConsumer);
  2628. }
  2629. if (oldValue !== UNSET && oldValue !== ERRORED && newValue !== ERRORED &&
  2630. node.equal(oldValue, newValue)) {
  2631. // No change to `valueVersion` - old and new values are
  2632. // semantically equivalent.
  2633. node.value = oldValue;
  2634. return;
  2635. }
  2636. node.value = newValue;
  2637. node.version++;
  2638. },
  2639. };
  2640. })();
  2641. function defaultThrowError() {
  2642. throw new Error();
  2643. }
  2644. let throwInvalidWriteToSignalErrorFn = defaultThrowError;
  2645. function throwInvalidWriteToSignalError() {
  2646. throwInvalidWriteToSignalErrorFn();
  2647. }
  2648. function setThrowInvalidWriteToSignalError(fn) {
  2649. throwInvalidWriteToSignalErrorFn = fn;
  2650. }
  2651. /**
  2652. * If set, called after `WritableSignal`s are updated.
  2653. *
  2654. * This hook can be used to achieve various effects, such as running effects synchronously as part
  2655. * of setting a signal.
  2656. */
  2657. let postSignalSetFn = null;
  2658. /**
  2659. * Create a `Signal` that can be set or updated directly.
  2660. *
  2661. * @developerPreview
  2662. */
  2663. function signal(initialValue, options) {
  2664. const node = Object.create(SIGNAL_NODE);
  2665. node.value = initialValue;
  2666. options?.equal && (node.equal = options.equal);
  2667. function signalFn() {
  2668. producerAccessed(node);
  2669. return node.value;
  2670. }
  2671. signalFn.set = signalSetFn;
  2672. signalFn.update = signalUpdateFn;
  2673. signalFn.mutate = signalMutateFn;
  2674. signalFn.asReadonly = signalAsReadonlyFn;
  2675. signalFn[SIGNAL] = node;
  2676. return signalFn;
  2677. }
  2678. function setPostSignalSetFn(fn) {
  2679. const prev = postSignalSetFn;
  2680. postSignalSetFn = fn;
  2681. return prev;
  2682. }
  2683. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  2684. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  2685. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  2686. const SIGNAL_NODE = /* @__PURE__ */ (() => {
  2687. return {
  2688. ...REACTIVE_NODE,
  2689. equal: defaultEquals,
  2690. readonlyFn: undefined,
  2691. };
  2692. })();
  2693. function signalValueChanged(node) {
  2694. node.version++;
  2695. producerNotifyConsumers(node);
  2696. postSignalSetFn?.();
  2697. }
  2698. function signalSetFn(newValue) {
  2699. const node = this[SIGNAL];
  2700. if (!producerUpdatesAllowed()) {
  2701. throwInvalidWriteToSignalError();
  2702. }
  2703. if (!node.equal(node.value, newValue)) {
  2704. node.value = newValue;
  2705. signalValueChanged(node);
  2706. }
  2707. }
  2708. function signalUpdateFn(updater) {
  2709. if (!producerUpdatesAllowed()) {
  2710. throwInvalidWriteToSignalError();
  2711. }
  2712. signalSetFn.call(this, updater(this[SIGNAL].value));
  2713. }
  2714. function signalMutateFn(mutator) {
  2715. const node = this[SIGNAL];
  2716. if (!producerUpdatesAllowed()) {
  2717. throwInvalidWriteToSignalError();
  2718. }
  2719. // Mutate bypasses equality checks as it's by definition changing the value.
  2720. mutator(node.value);
  2721. signalValueChanged(node);
  2722. }
  2723. function signalAsReadonlyFn() {
  2724. const node = this[SIGNAL];
  2725. if (node.readonlyFn === undefined) {
  2726. const readonlyFn = () => this();
  2727. readonlyFn[SIGNAL] = node;
  2728. node.readonlyFn = readonlyFn;
  2729. }
  2730. return node.readonlyFn;
  2731. }
  2732. /**
  2733. * Execute an arbitrary function in a non-reactive (non-tracking) context. The executed function
  2734. * can, optionally, return a value.
  2735. *
  2736. * @developerPreview
  2737. */
  2738. function untracked(nonReactiveReadsFn) {
  2739. const prevConsumer = setActiveConsumer(null);
  2740. // We are not trying to catch any particular errors here, just making sure that the consumers
  2741. // stack is restored in case of errors.
  2742. try {
  2743. return nonReactiveReadsFn();
  2744. }
  2745. finally {
  2746. setActiveConsumer(prevConsumer);
  2747. }
  2748. }
  2749. function watch(fn, schedule, allowSignalWrites) {
  2750. const node = Object.create(WATCH_NODE);
  2751. if (allowSignalWrites) {
  2752. node.consumerAllowSignalWrites = true;
  2753. }
  2754. node.fn = fn;
  2755. node.schedule = schedule;
  2756. const registerOnCleanup = (cleanupFn) => {
  2757. node.cleanupFn = cleanupFn;
  2758. };
  2759. const run = () => {
  2760. node.dirty = false;
  2761. if (node.hasRun && !consumerPollProducersForChange(node)) {
  2762. return;
  2763. }
  2764. node.hasRun = true;
  2765. const prevConsumer = consumerBeforeComputation(node);
  2766. try {
  2767. node.cleanupFn();
  2768. node.cleanupFn = NOOP_CLEANUP_FN;
  2769. node.fn(registerOnCleanup);
  2770. }
  2771. finally {
  2772. consumerAfterComputation(node, prevConsumer);
  2773. }
  2774. };
  2775. node.ref = {
  2776. notify: () => consumerMarkDirty(node),
  2777. run,
  2778. cleanup: () => node.cleanupFn(),
  2779. };
  2780. return node.ref;
  2781. }
  2782. const NOOP_CLEANUP_FN = () => { };
  2783. // Note: Using an IIFE here to ensure that the spread assignment is not considered
  2784. // a side-effect, ending up preserving `COMPUTED_NODE` and `REACTIVE_NODE`.
  2785. // TODO: remove when https://github.com/evanw/esbuild/issues/3392 is resolved.
  2786. const WATCH_NODE = /* @__PURE__ */ (() => {
  2787. return {
  2788. ...REACTIVE_NODE,
  2789. consumerIsAlwaysLive: true,
  2790. consumerAllowSignalWrites: false,
  2791. consumerMarkedDirty: (node) => {
  2792. node.schedule(node.ref);
  2793. },
  2794. hasRun: false,
  2795. cleanupFn: NOOP_CLEANUP_FN,
  2796. };
  2797. })();
  2798. function setAlternateWeakRefImpl(impl) {
  2799. // TODO: remove this function
  2800. }
  2801. /**
  2802. * Represents a basic change from a previous to a new value for a single
  2803. * property on a directive instance. Passed as a value in a
  2804. * {@link SimpleChanges} object to the `ngOnChanges` hook.
  2805. *
  2806. * @see {@link OnChanges}
  2807. *
  2808. * @publicApi
  2809. */
  2810. class SimpleChange {
  2811. constructor(previousValue, currentValue, firstChange) {
  2812. this.previousValue = previousValue;
  2813. this.currentValue = currentValue;
  2814. this.firstChange = firstChange;
  2815. }
  2816. /**
  2817. * Check whether the new value is the first value assigned.
  2818. */
  2819. isFirstChange() {
  2820. return this.firstChange;
  2821. }
  2822. }
  2823. /**
  2824. * The NgOnChangesFeature decorates a component with support for the ngOnChanges
  2825. * lifecycle hook, so it should be included in any component that implements
  2826. * that hook.
  2827. *
  2828. * If the component or directive uses inheritance, the NgOnChangesFeature MUST
  2829. * be included as a feature AFTER {@link InheritDefinitionFeature}, otherwise
  2830. * inherited properties will not be propagated to the ngOnChanges lifecycle
  2831. * hook.
  2832. *
  2833. * Example usage:
  2834. *
  2835. * ```
  2836. * static ɵcmp = defineComponent({
  2837. * ...
  2838. * inputs: {name: 'publicName'},
  2839. * features: [NgOnChangesFeature]
  2840. * });
  2841. * ```
  2842. *
  2843. * @codeGenApi
  2844. */
  2845. function ɵɵNgOnChangesFeature() {
  2846. return NgOnChangesFeatureImpl;
  2847. }
  2848. function NgOnChangesFeatureImpl(definition) {
  2849. if (definition.type.prototype.ngOnChanges) {
  2850. definition.setInput = ngOnChangesSetInput;
  2851. }
  2852. return rememberChangeHistoryAndInvokeOnChangesHook;
  2853. }
  2854. // This option ensures that the ngOnChanges lifecycle hook will be inherited
  2855. // from superclasses (in InheritDefinitionFeature).
  2856. /** @nocollapse */
  2857. // tslint:disable-next-line:no-toplevel-property-access
  2858. ɵɵNgOnChangesFeature.ngInherit = true;
  2859. /**
  2860. * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate
  2861. * `ngOnChanges`.
  2862. *
  2863. * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are
  2864. * found it invokes `ngOnChanges` on the component instance.
  2865. *
  2866. * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`,
  2867. * it is guaranteed to be called with component instance.
  2868. */
  2869. function rememberChangeHistoryAndInvokeOnChangesHook() {
  2870. const simpleChangesStore = getSimpleChangesStore(this);
  2871. const current = simpleChangesStore?.current;
  2872. if (current) {
  2873. const previous = simpleChangesStore.previous;
  2874. if (previous === EMPTY_OBJ) {
  2875. simpleChangesStore.previous = current;
  2876. }
  2877. else {
  2878. // New changes are copied to the previous store, so that we don't lose history for inputs
  2879. // which were not changed this time
  2880. for (let key in current) {
  2881. previous[key] = current[key];
  2882. }
  2883. }
  2884. simpleChangesStore.current = null;
  2885. this.ngOnChanges(current);
  2886. }
  2887. }
  2888. function ngOnChangesSetInput(instance, value, publicName, privateName) {
  2889. const declaredName = this.declaredInputs[publicName];
  2890. ngDevMode && assertString(declaredName, 'Name of input in ngOnChanges has to be a string');
  2891. const simpleChangesStore = getSimpleChangesStore(instance) ||
  2892. setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null });
  2893. const current = simpleChangesStore.current || (simpleChangesStore.current = {});
  2894. const previous = simpleChangesStore.previous;
  2895. const previousChange = previous[declaredName];
  2896. current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ);
  2897. instance[privateName] = value;
  2898. }
  2899. const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__';
  2900. function getSimpleChangesStore(instance) {
  2901. return instance[SIMPLE_CHANGES_STORE] || null;
  2902. }
  2903. function setSimpleChangesStore(instance, store) {
  2904. return instance[SIMPLE_CHANGES_STORE] = store;
  2905. }
  2906. let profilerCallback = null;
  2907. /**
  2908. * Sets the callback function which will be invoked before and after performing certain actions at
  2909. * runtime (for example, before and after running change detection).
  2910. *
  2911. * Warning: this function is *INTERNAL* and should not be relied upon in application's code.
  2912. * The contract of the function might be changed in any release and/or the function can be removed
  2913. * completely.
  2914. *
  2915. * @param profiler function provided by the caller or null value to disable profiling.
  2916. */
  2917. const setProfiler = (profiler) => {
  2918. profilerCallback = profiler;
  2919. };
  2920. /**
  2921. * Profiler function which wraps user code executed by the runtime.
  2922. *
  2923. * @param event ProfilerEvent corresponding to the execution context
  2924. * @param instance component instance
  2925. * @param hookOrListener lifecycle hook function or output listener. The value depends on the
  2926. * execution context
  2927. * @returns
  2928. */
  2929. const profiler = function (event, instance, hookOrListener) {
  2930. if (profilerCallback != null /* both `null` and `undefined` */) {
  2931. profilerCallback(event, instance, hookOrListener);
  2932. }
  2933. };
  2934. const SVG_NAMESPACE = 'svg';
  2935. const MATH_ML_NAMESPACE = 'math';
  2936. /**
  2937. * For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
  2938. * in same location in `LView`. This is because we don't want to pre-allocate space for it
  2939. * because the storage is sparse. This file contains utilities for dealing with such data types.
  2940. *
  2941. * How do we know what is stored at a given location in `LView`.
  2942. * - `Array.isArray(value) === false` => `RNode` (The normal storage value)
  2943. * - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value.
  2944. * - `typeof value[TYPE] === 'object'` => `LView`
  2945. * - This happens when we have a component at a given location
  2946. * - `typeof value[TYPE] === true` => `LContainer`
  2947. * - This happens when we have `LContainer` binding at a given location.
  2948. *
  2949. *
  2950. * NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.
  2951. */
  2952. /**
  2953. * Returns `RNode`.
  2954. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  2955. */
  2956. function unwrapRNode(value) {
  2957. while (Array.isArray(value)) {
  2958. value = value[HOST];
  2959. }
  2960. return value;
  2961. }
  2962. /**
  2963. * Returns `LView` or `null` if not found.
  2964. * @param value wrapped value of `RNode`, `LView`, `LContainer`
  2965. */
  2966. function unwrapLView(value) {
  2967. while (Array.isArray(value)) {
  2968. // This check is same as `isLView()` but we don't call at as we don't want to call
  2969. // `Array.isArray()` twice and give JITer more work for inlining.
  2970. if (typeof value[TYPE] === 'object')
  2971. return value;
  2972. value = value[HOST];
  2973. }
  2974. return null;
  2975. }
  2976. /**
  2977. * Retrieves an element value from the provided `viewData`, by unwrapping
  2978. * from any containers, component views, or style contexts.
  2979. */
  2980. function getNativeByIndex(index, lView) {
  2981. ngDevMode && assertIndexInRange(lView, index);
  2982. ngDevMode && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Expected to be past HEADER_OFFSET');
  2983. return unwrapRNode(lView[index]);
  2984. }
  2985. /**
  2986. * Retrieve an `RNode` for a given `TNode` and `LView`.
  2987. *
  2988. * This function guarantees in dev mode to retrieve a non-null `RNode`.
  2989. *
  2990. * @param tNode
  2991. * @param lView
  2992. */
  2993. function getNativeByTNode(tNode, lView) {
  2994. ngDevMode && assertTNodeForLView(tNode, lView);
  2995. ngDevMode && assertIndexInRange(lView, tNode.index);
  2996. const node = unwrapRNode(lView[tNode.index]);
  2997. return node;
  2998. }
  2999. /**
  3000. * Retrieve an `RNode` or `null` for a given `TNode` and `LView`.
  3001. *
  3002. * Some `TNode`s don't have associated `RNode`s. For example `Projection`
  3003. *
  3004. * @param tNode
  3005. * @param lView
  3006. */
  3007. function getNativeByTNodeOrNull(tNode, lView) {
  3008. const index = tNode === null ? -1 : tNode.index;
  3009. if (index !== -1) {
  3010. ngDevMode && assertTNodeForLView(tNode, lView);
  3011. const node = unwrapRNode(lView[index]);
  3012. return node;
  3013. }
  3014. return null;
  3015. }
  3016. // fixme(misko): The return Type should be `TNode|null`
  3017. function getTNode(tView, index) {
  3018. ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
  3019. ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode');
  3020. const tNode = tView.data[index];
  3021. ngDevMode && tNode !== null && assertTNode(tNode);
  3022. return tNode;
  3023. }
  3024. /** Retrieves a value from any `LView` or `TData`. */
  3025. function load(view, index) {
  3026. ngDevMode && assertIndexInRange(view, index);
  3027. return view[index];
  3028. }
  3029. function getComponentLViewByIndex(nodeIndex, hostView) {
  3030. // Could be an LView or an LContainer. If LContainer, unwrap to find LView.
  3031. ngDevMode && assertIndexInRange(hostView, nodeIndex);
  3032. const slotValue = hostView[nodeIndex];
  3033. const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
  3034. return lView;
  3035. }
  3036. /** Checks whether a given view is in creation mode */
  3037. function isCreationMode(view) {
  3038. return (view[FLAGS] & 4 /* LViewFlags.CreationMode */) === 4 /* LViewFlags.CreationMode */;
  3039. }
  3040. /**
  3041. * Returns a boolean for whether the view is attached to the change detection tree.
  3042. *
  3043. * Note: This determines whether a view should be checked, not whether it's inserted
  3044. * into a container. For that, you'll want `viewAttachedToContainer` below.
  3045. */
  3046. function viewAttachedToChangeDetector(view) {
  3047. return (view[FLAGS] & 128 /* LViewFlags.Attached */) === 128 /* LViewFlags.Attached */;
  3048. }
  3049. /** Returns a boolean for whether the view is attached to a container. */
  3050. function viewAttachedToContainer(view) {
  3051. return isLContainer(view[PARENT]);
  3052. }
  3053. function getConstant(consts, index) {
  3054. if (index === null || index === undefined)
  3055. return null;
  3056. ngDevMode && assertIndexInRange(consts, index);
  3057. return consts[index];
  3058. }
  3059. /**
  3060. * Resets the pre-order hook flags of the view.
  3061. * @param lView the LView on which the flags are reset
  3062. */
  3063. function resetPreOrderHookFlags(lView) {
  3064. lView[PREORDER_HOOK_FLAGS] = 0;
  3065. }
  3066. /**
  3067. * Adds the `RefreshView` flag from the lView and updates DESCENDANT_VIEWS_TO_REFRESH counters of
  3068. * parents.
  3069. */
  3070. function markViewForRefresh(lView) {
  3071. if ((lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) === 0) {
  3072. lView[FLAGS] |= 1024 /* LViewFlags.RefreshView */;
  3073. updateViewsToRefresh(lView, 1);
  3074. }
  3075. }
  3076. /**
  3077. * Removes the `RefreshView` flag from the lView and updates DESCENDANT_VIEWS_TO_REFRESH counters of
  3078. * parents.
  3079. */
  3080. function clearViewRefreshFlag(lView) {
  3081. if (lView[FLAGS] & 1024 /* LViewFlags.RefreshView */) {
  3082. lView[FLAGS] &= ~1024 /* LViewFlags.RefreshView */;
  3083. updateViewsToRefresh(lView, -1);
  3084. }
  3085. }
  3086. /**
  3087. * Updates the `DESCENDANT_VIEWS_TO_REFRESH` counter on the parents of the `LView` as well as the
  3088. * parents above that whose
  3089. * 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh
  3090. * or
  3091. * 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh
  3092. */
  3093. function updateViewsToRefresh(lView, amount) {
  3094. let parent = lView[PARENT];
  3095. if (parent === null) {
  3096. return;
  3097. }
  3098. parent[DESCENDANT_VIEWS_TO_REFRESH] += amount;
  3099. let viewOrContainer = parent;
  3100. parent = parent[PARENT];
  3101. while (parent !== null &&
  3102. ((amount === 1 && viewOrContainer[DESCENDANT_VIEWS_TO_REFRESH] === 1) ||
  3103. (amount === -1 && viewOrContainer[DESCENDANT_VIEWS_TO_REFRESH] === 0))) {
  3104. parent[DESCENDANT_VIEWS_TO_REFRESH] += amount;
  3105. viewOrContainer = parent;
  3106. parent = parent[PARENT];
  3107. }
  3108. }
  3109. /**
  3110. * Stores a LView-specific destroy callback.
  3111. */
  3112. function storeLViewOnDestroy(lView, onDestroyCallback) {
  3113. if ((lView[FLAGS] & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */) {
  3114. throw new RuntimeError(911 /* RuntimeErrorCode.VIEW_ALREADY_DESTROYED */, ngDevMode && 'View has already been destroyed.');
  3115. }
  3116. if (lView[ON_DESTROY_HOOKS] === null) {
  3117. lView[ON_DESTROY_HOOKS] = [];
  3118. }
  3119. lView[ON_DESTROY_HOOKS].push(onDestroyCallback);
  3120. }
  3121. /**
  3122. * Removes previously registered LView-specific destroy callback.
  3123. */
  3124. function removeLViewOnDestroy(lView, onDestroyCallback) {
  3125. if (lView[ON_DESTROY_HOOKS] === null)
  3126. return;
  3127. const destroyCBIdx = lView[ON_DESTROY_HOOKS].indexOf(onDestroyCallback);
  3128. if (destroyCBIdx !== -1) {
  3129. lView[ON_DESTROY_HOOKS].splice(destroyCBIdx, 1);
  3130. }
  3131. }
  3132. const instructionState = {
  3133. lFrame: createLFrame(null),
  3134. bindingsEnabled: true,
  3135. skipHydrationRootTNode: null,
  3136. };
  3137. /**
  3138. * In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
  3139. *
  3140. * Necessary to support ChangeDetectorRef.checkNoChanges().
  3141. *
  3142. * The `checkNoChanges` function is invoked only in ngDevMode=true and verifies that no unintended
  3143. * changes exist in the change detector or its children.
  3144. */
  3145. let _isInCheckNoChangesMode = false;
  3146. /**
  3147. * Returns true if the instruction state stack is empty.
  3148. *
  3149. * Intended to be called from tests only (tree shaken otherwise).
  3150. */
  3151. function specOnlyIsInstructionStateEmpty() {
  3152. return instructionState.lFrame.parent === null;
  3153. }
  3154. function getElementDepthCount() {
  3155. return instructionState.lFrame.elementDepthCount;
  3156. }
  3157. function increaseElementDepthCount() {
  3158. instructionState.lFrame.elementDepthCount++;
  3159. }
  3160. function decreaseElementDepthCount() {
  3161. instructionState.lFrame.elementDepthCount--;
  3162. }
  3163. function getBindingsEnabled() {
  3164. return instructionState.bindingsEnabled;
  3165. }
  3166. /**
  3167. * Returns true if currently inside a skip hydration block.
  3168. * @returns boolean
  3169. */
  3170. function isInSkipHydrationBlock$1() {
  3171. return instructionState.skipHydrationRootTNode !== null;
  3172. }
  3173. /**
  3174. * Returns true if this is the root TNode of the skip hydration block.
  3175. * @param tNode the current TNode
  3176. * @returns boolean
  3177. */
  3178. function isSkipHydrationRootTNode(tNode) {
  3179. return instructionState.skipHydrationRootTNode === tNode;
  3180. }
  3181. /**
  3182. * Enables directive matching on elements.
  3183. *
  3184. * * Example:
  3185. * ```
  3186. * <my-comp my-directive>
  3187. * Should match component / directive.
  3188. * </my-comp>
  3189. * <div ngNonBindable>
  3190. * <!-- ɵɵdisableBindings() -->
  3191. * <my-comp my-directive>
  3192. * Should not match component / directive because we are in ngNonBindable.
  3193. * </my-comp>
  3194. * <!-- ɵɵenableBindings() -->
  3195. * </div>
  3196. * ```
  3197. *
  3198. * @codeGenApi
  3199. */
  3200. function ɵɵenableBindings() {
  3201. instructionState.bindingsEnabled = true;
  3202. }
  3203. /**
  3204. * Sets a flag to specify that the TNode is in a skip hydration block.
  3205. * @param tNode the current TNode
  3206. */
  3207. function enterSkipHydrationBlock(tNode) {
  3208. instructionState.skipHydrationRootTNode = tNode;
  3209. }
  3210. /**
  3211. * Disables directive matching on element.
  3212. *
  3213. * * Example:
  3214. * ```
  3215. * <my-comp my-directive>
  3216. * Should match component / directive.
  3217. * </my-comp>
  3218. * <div ngNonBindable>
  3219. * <!-- ɵɵdisableBindings() -->
  3220. * <my-comp my-directive>
  3221. * Should not match component / directive because we are in ngNonBindable.
  3222. * </my-comp>
  3223. * <!-- ɵɵenableBindings() -->
  3224. * </div>
  3225. * ```
  3226. *
  3227. * @codeGenApi
  3228. */
  3229. function ɵɵdisableBindings() {
  3230. instructionState.bindingsEnabled = false;
  3231. }
  3232. /**
  3233. * Clears the root skip hydration node when leaving a skip hydration block.
  3234. */
  3235. function leaveSkipHydrationBlock() {
  3236. instructionState.skipHydrationRootTNode = null;
  3237. }
  3238. /**
  3239. * Return the current `LView`.
  3240. */
  3241. function getLView() {
  3242. return instructionState.lFrame.lView;
  3243. }
  3244. /**
  3245. * Return the current `TView`.
  3246. */
  3247. function getTView() {
  3248. return instructionState.lFrame.tView;
  3249. }
  3250. /**
  3251. * Restores `contextViewData` to the given OpaqueViewState instance.
  3252. *
  3253. * Used in conjunction with the getCurrentView() instruction to save a snapshot
  3254. * of the current view and restore it when listeners are invoked. This allows
  3255. * walking the declaration view tree in listeners to get vars from parent views.
  3256. *
  3257. * @param viewToRestore The OpaqueViewState instance to restore.
  3258. * @returns Context of the restored OpaqueViewState instance.
  3259. *
  3260. * @codeGenApi
  3261. */
  3262. function ɵɵrestoreView(viewToRestore) {
  3263. instructionState.lFrame.contextLView = viewToRestore;
  3264. return viewToRestore[CONTEXT];
  3265. }
  3266. /**
  3267. * Clears the view set in `ɵɵrestoreView` from memory. Returns the passed in
  3268. * value so that it can be used as a return value of an instruction.
  3269. *
  3270. * @codeGenApi
  3271. */
  3272. function ɵɵresetView(value) {
  3273. instructionState.lFrame.contextLView = null;
  3274. return value;
  3275. }
  3276. function getCurrentTNode() {
  3277. let currentTNode = getCurrentTNodePlaceholderOk();
  3278. while (currentTNode !== null && currentTNode.type === 64 /* TNodeType.Placeholder */) {
  3279. currentTNode = currentTNode.parent;
  3280. }
  3281. return currentTNode;
  3282. }
  3283. function getCurrentTNodePlaceholderOk() {
  3284. return instructionState.lFrame.currentTNode;
  3285. }
  3286. function getCurrentParentTNode() {
  3287. const lFrame = instructionState.lFrame;
  3288. const currentTNode = lFrame.currentTNode;
  3289. return lFrame.isParent ? currentTNode : currentTNode.parent;
  3290. }
  3291. function setCurrentTNode(tNode, isParent) {
  3292. ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView);
  3293. const lFrame = instructionState.lFrame;
  3294. lFrame.currentTNode = tNode;
  3295. lFrame.isParent = isParent;
  3296. }
  3297. function isCurrentTNodeParent() {
  3298. return instructionState.lFrame.isParent;
  3299. }
  3300. function setCurrentTNodeAsNotParent() {
  3301. instructionState.lFrame.isParent = false;
  3302. }
  3303. function getContextLView() {
  3304. const contextLView = instructionState.lFrame.contextLView;
  3305. ngDevMode && assertDefined(contextLView, 'contextLView must be defined.');
  3306. return contextLView;
  3307. }
  3308. function isInCheckNoChangesMode() {
  3309. !ngDevMode && throwError('Must never be called in production mode');
  3310. return _isInCheckNoChangesMode;
  3311. }
  3312. function setIsInCheckNoChangesMode(mode) {
  3313. !ngDevMode && throwError('Must never be called in production mode');
  3314. _isInCheckNoChangesMode = mode;
  3315. }
  3316. // top level variables should not be exported for performance reasons (PERF_NOTES.md)
  3317. function getBindingRoot() {
  3318. const lFrame = instructionState.lFrame;
  3319. let index = lFrame.bindingRootIndex;
  3320. if (index === -1) {
  3321. index = lFrame.bindingRootIndex = lFrame.tView.bindingStartIndex;
  3322. }
  3323. return index;
  3324. }
  3325. function getBindingIndex() {
  3326. return instructionState.lFrame.bindingIndex;
  3327. }
  3328. function setBindingIndex(value) {
  3329. return instructionState.lFrame.bindingIndex = value;
  3330. }
  3331. function nextBindingIndex() {
  3332. return instructionState.lFrame.bindingIndex++;
  3333. }
  3334. function incrementBindingIndex(count) {
  3335. const lFrame = instructionState.lFrame;
  3336. const index = lFrame.bindingIndex;
  3337. lFrame.bindingIndex = lFrame.bindingIndex + count;
  3338. return index;
  3339. }
  3340. function isInI18nBlock() {
  3341. return instructionState.lFrame.inI18n;
  3342. }
  3343. function setInI18nBlock(isInI18nBlock) {
  3344. instructionState.lFrame.inI18n = isInI18nBlock;
  3345. }
  3346. /**
  3347. * Set a new binding root index so that host template functions can execute.
  3348. *
  3349. * Bindings inside the host template are 0 index. But because we don't know ahead of time
  3350. * how many host bindings we have we can't pre-compute them. For this reason they are all
  3351. * 0 index and we just shift the root so that they match next available location in the LView.
  3352. *
  3353. * @param bindingRootIndex Root index for `hostBindings`
  3354. * @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive
  3355. * whose `hostBindings` are being processed.
  3356. */
  3357. function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) {
  3358. const lFrame = instructionState.lFrame;
  3359. lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex;
  3360. setCurrentDirectiveIndex(currentDirectiveIndex);
  3361. }
  3362. /**
  3363. * When host binding is executing this points to the directive index.
  3364. * `TView.data[getCurrentDirectiveIndex()]` is `DirectiveDef`
  3365. * `LView[getCurrentDirectiveIndex()]` is directive instance.
  3366. */
  3367. function getCurrentDirectiveIndex() {
  3368. return instructionState.lFrame.currentDirectiveIndex;
  3369. }
  3370. /**
  3371. * Sets an index of a directive whose `hostBindings` are being processed.
  3372. *
  3373. * @param currentDirectiveIndex `TData` index where current directive instance can be found.
  3374. */
  3375. function setCurrentDirectiveIndex(currentDirectiveIndex) {
  3376. instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex;
  3377. }
  3378. /**
  3379. * Retrieve the current `DirectiveDef` which is active when `hostBindings` instruction is being
  3380. * executed.
  3381. *
  3382. * @param tData Current `TData` where the `DirectiveDef` will be looked up at.
  3383. */
  3384. function getCurrentDirectiveDef(tData) {
  3385. const currentDirectiveIndex = instructionState.lFrame.currentDirectiveIndex;
  3386. return currentDirectiveIndex === -1 ? null : tData[currentDirectiveIndex];
  3387. }
  3388. function getCurrentQueryIndex() {
  3389. return instructionState.lFrame.currentQueryIndex;
  3390. }
  3391. function setCurrentQueryIndex(value) {
  3392. instructionState.lFrame.currentQueryIndex = value;
  3393. }
  3394. /**
  3395. * Returns a `TNode` of the location where the current `LView` is declared at.
  3396. *
  3397. * @param lView an `LView` that we want to find parent `TNode` for.
  3398. */
  3399. function getDeclarationTNode(lView) {
  3400. const tView = lView[TVIEW];
  3401. // Return the declaration parent for embedded views
  3402. if (tView.type === 2 /* TViewType.Embedded */) {
  3403. ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
  3404. return tView.declTNode;
  3405. }
  3406. // Components don't have `TView.declTNode` because each instance of component could be
  3407. // inserted in different location, hence `TView.declTNode` is meaningless.
  3408. // Falling back to `T_HOST` in case we cross component boundary.
  3409. if (tView.type === 1 /* TViewType.Component */) {
  3410. return lView[T_HOST];
  3411. }
  3412. // Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode.
  3413. return null;
  3414. }
  3415. /**
  3416. * This is a light weight version of the `enterView` which is needed by the DI system.
  3417. *
  3418. * @param lView `LView` location of the DI context.
  3419. * @param tNode `TNode` for DI context
  3420. * @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration
  3421. * tree from `tNode` until we find parent declared `TElementNode`.
  3422. * @returns `true` if we have successfully entered DI associated with `tNode` (or with declared
  3423. * `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated
  3424. * `NodeInjector` can be found and we should instead use `ModuleInjector`.
  3425. * - If `true` than this call must be fallowed by `leaveDI`
  3426. * - If `false` than this call failed and we should NOT call `leaveDI`
  3427. */
  3428. function enterDI(lView, tNode, flags) {
  3429. ngDevMode && assertLViewOrUndefined(lView);
  3430. if (flags & InjectFlags.SkipSelf) {
  3431. ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]);
  3432. let parentTNode = tNode;
  3433. let parentLView = lView;
  3434. while (true) {
  3435. ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined');
  3436. parentTNode = parentTNode.parent;
  3437. if (parentTNode === null && !(flags & InjectFlags.Host)) {
  3438. parentTNode = getDeclarationTNode(parentLView);
  3439. if (parentTNode === null)
  3440. break;
  3441. // In this case, a parent exists and is definitely an element. So it will definitely
  3442. // have an existing lView as the declaration view, which is why we can assume it's defined.
  3443. ngDevMode && assertDefined(parentLView, 'Parent LView should be defined');
  3444. parentLView = parentLView[DECLARATION_VIEW];
  3445. // In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives
  3446. // We want to skip those and look only at Elements and ElementContainers to ensure
  3447. // we're looking at true parent nodes, and not content or other types.
  3448. if (parentTNode.type & (2 /* TNodeType.Element */ | 8 /* TNodeType.ElementContainer */)) {
  3449. break;
  3450. }
  3451. }
  3452. else {
  3453. break;
  3454. }
  3455. }
  3456. if (parentTNode === null) {
  3457. // If we failed to find a parent TNode this means that we should use module injector.
  3458. return false;
  3459. }
  3460. else {
  3461. tNode = parentTNode;
  3462. lView = parentLView;
  3463. }
  3464. }
  3465. ngDevMode && assertTNodeForLView(tNode, lView);
  3466. const lFrame = instructionState.lFrame = allocLFrame();
  3467. lFrame.currentTNode = tNode;
  3468. lFrame.lView = lView;
  3469. return true;
  3470. }
  3471. /**
  3472. * Swap the current lView with a new lView.
  3473. *
  3474. * For performance reasons we store the lView in the top level of the module.
  3475. * This way we minimize the number of properties to read. Whenever a new view
  3476. * is entered we have to store the lView for later, and when the view is
  3477. * exited the state has to be restored
  3478. *
  3479. * @param newView New lView to become active
  3480. * @returns the previously active lView;
  3481. */
  3482. function enterView(newView) {
  3483. ngDevMode && assertNotEqual(newView[0], newView[1], '????');
  3484. ngDevMode && assertLViewOrUndefined(newView);
  3485. const newLFrame = allocLFrame();
  3486. if (ngDevMode) {
  3487. assertEqual(newLFrame.isParent, true, 'Expected clean LFrame');
  3488. assertEqual(newLFrame.lView, null, 'Expected clean LFrame');
  3489. assertEqual(newLFrame.tView, null, 'Expected clean LFrame');
  3490. assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame');
  3491. assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
  3492. assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
  3493. assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
  3494. assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
  3495. assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
  3496. }
  3497. const tView = newView[TVIEW];
  3498. instructionState.lFrame = newLFrame;
  3499. ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView);
  3500. newLFrame.currentTNode = tView.firstChild;
  3501. newLFrame.lView = newView;
  3502. newLFrame.tView = tView;
  3503. newLFrame.contextLView = newView;
  3504. newLFrame.bindingIndex = tView.bindingStartIndex;
  3505. newLFrame.inI18n = false;
  3506. }
  3507. /**
  3508. * Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
  3509. */
  3510. function allocLFrame() {
  3511. const currentLFrame = instructionState.lFrame;
  3512. const childLFrame = currentLFrame === null ? null : currentLFrame.child;
  3513. const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
  3514. return newLFrame;
  3515. }
  3516. function createLFrame(parent) {
  3517. const lFrame = {
  3518. currentTNode: null,
  3519. isParent: true,
  3520. lView: null,
  3521. tView: null,
  3522. selectedIndex: -1,
  3523. contextLView: null,
  3524. elementDepthCount: 0,
  3525. currentNamespace: null,
  3526. currentDirectiveIndex: -1,
  3527. bindingRootIndex: -1,
  3528. bindingIndex: -1,
  3529. currentQueryIndex: 0,
  3530. parent: parent,
  3531. child: null,
  3532. inI18n: false,
  3533. };
  3534. parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
  3535. return lFrame;
  3536. }
  3537. /**
  3538. * A lightweight version of leave which is used with DI.
  3539. *
  3540. * This function only resets `currentTNode` and `LView` as those are the only properties
  3541. * used with DI (`enterDI()`).
  3542. *
  3543. * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where
  3544. * as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`.
  3545. */
  3546. function leaveViewLight() {
  3547. const oldLFrame = instructionState.lFrame;
  3548. instructionState.lFrame = oldLFrame.parent;
  3549. oldLFrame.currentTNode = null;
  3550. oldLFrame.lView = null;
  3551. return oldLFrame;
  3552. }
  3553. /**
  3554. * This is a lightweight version of the `leaveView` which is needed by the DI system.
  3555. *
  3556. * NOTE: this function is an alias so that we can change the type of the function to have `void`
  3557. * return type.
  3558. */
  3559. const leaveDI = leaveViewLight;
  3560. /**
  3561. * Leave the current `LView`
  3562. *
  3563. * This pops the `LFrame` with the associated `LView` from the stack.
  3564. *
  3565. * IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is
  3566. * because for performance reasons we don't release `LFrame` but rather keep it for next use.
  3567. */
  3568. function leaveView() {
  3569. const oldLFrame = leaveViewLight();
  3570. oldLFrame.isParent = true;
  3571. oldLFrame.tView = null;
  3572. oldLFrame.selectedIndex = -1;
  3573. oldLFrame.contextLView = null;
  3574. oldLFrame.elementDepthCount = 0;
  3575. oldLFrame.currentDirectiveIndex = -1;
  3576. oldLFrame.currentNamespace = null;
  3577. oldLFrame.bindingRootIndex = -1;
  3578. oldLFrame.bindingIndex = -1;
  3579. oldLFrame.currentQueryIndex = 0;
  3580. }
  3581. function nextContextImpl(level) {
  3582. const contextLView = instructionState.lFrame.contextLView =
  3583. walkUpViews(level, instructionState.lFrame.contextLView);
  3584. return contextLView[CONTEXT];
  3585. }
  3586. function walkUpViews(nestingLevel, currentView) {
  3587. while (nestingLevel > 0) {
  3588. ngDevMode &&
  3589. assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.');
  3590. currentView = currentView[DECLARATION_VIEW];
  3591. nestingLevel--;
  3592. }
  3593. return currentView;
  3594. }
  3595. /**
  3596. * Gets the currently selected element index.
  3597. *
  3598. * Used with {@link property} instruction (and more in the future) to identify the index in the
  3599. * current `LView` to act on.
  3600. */
  3601. function getSelectedIndex() {
  3602. return instructionState.lFrame.selectedIndex;
  3603. }
  3604. /**
  3605. * Sets the most recent index passed to {@link select}
  3606. *
  3607. * Used with {@link property} instruction (and more in the future) to identify the index in the
  3608. * current `LView` to act on.
  3609. *
  3610. * (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
  3611. * run if and when the provided `index` value is different from the current selected index value.)
  3612. */
  3613. function setSelectedIndex(index) {
  3614. ngDevMode && index !== -1 &&
  3615. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).');
  3616. ngDevMode &&
  3617. assertLessThan(index, instructionState.lFrame.lView.length, 'Can\'t set index passed end of LView');
  3618. instructionState.lFrame.selectedIndex = index;
  3619. }
  3620. /**
  3621. * Gets the `tNode` that represents currently selected element.
  3622. */
  3623. function getSelectedTNode() {
  3624. const lFrame = instructionState.lFrame;
  3625. return getTNode(lFrame.tView, lFrame.selectedIndex);
  3626. }
  3627. /**
  3628. * Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state.
  3629. *
  3630. * @codeGenApi
  3631. */
  3632. function ɵɵnamespaceSVG() {
  3633. instructionState.lFrame.currentNamespace = SVG_NAMESPACE;
  3634. }
  3635. /**
  3636. * Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state.
  3637. *
  3638. * @codeGenApi
  3639. */
  3640. function ɵɵnamespaceMathML() {
  3641. instructionState.lFrame.currentNamespace = MATH_ML_NAMESPACE;
  3642. }
  3643. /**
  3644. * Sets the namespace used to create elements to `null`, which forces element creation to use
  3645. * `createElement` rather than `createElementNS`.
  3646. *
  3647. * @codeGenApi
  3648. */
  3649. function ɵɵnamespaceHTML() {
  3650. namespaceHTMLInternal();
  3651. }
  3652. /**
  3653. * Sets the namespace used to create elements to `null`, which forces element creation to use
  3654. * `createElement` rather than `createElementNS`.
  3655. */
  3656. function namespaceHTMLInternal() {
  3657. instructionState.lFrame.currentNamespace = null;
  3658. }
  3659. function getNamespace$1() {
  3660. return instructionState.lFrame.currentNamespace;
  3661. }
  3662. let _wasLastNodeCreated = true;
  3663. /**
  3664. * Retrieves a global flag that indicates whether the most recent DOM node
  3665. * was created or hydrated.
  3666. */
  3667. function wasLastNodeCreated() {
  3668. return _wasLastNodeCreated;
  3669. }
  3670. /**
  3671. * Sets a global flag to indicate whether the most recent DOM node
  3672. * was created or hydrated.
  3673. */
  3674. function lastNodeWasCreated(flag) {
  3675. _wasLastNodeCreated = flag;
  3676. }
  3677. /**
  3678. * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`.
  3679. *
  3680. * Must be run *only* on the first template pass.
  3681. *
  3682. * Sets up the pre-order hooks on the provided `tView`,
  3683. * see {@link HookData} for details about the data structure.
  3684. *
  3685. * @param directiveIndex The index of the directive in LView
  3686. * @param directiveDef The definition containing the hooks to setup in tView
  3687. * @param tView The current TView
  3688. */
  3689. function registerPreOrderHooks(directiveIndex, directiveDef, tView) {
  3690. ngDevMode && assertFirstCreatePass(tView);
  3691. const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype;
  3692. if (ngOnChanges) {
  3693. const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef);
  3694. (tView.preOrderHooks ??= []).push(directiveIndex, wrappedOnChanges);
  3695. (tView.preOrderCheckHooks ??= []).push(directiveIndex, wrappedOnChanges);
  3696. }
  3697. if (ngOnInit) {
  3698. (tView.preOrderHooks ??= []).push(0 - directiveIndex, ngOnInit);
  3699. }
  3700. if (ngDoCheck) {
  3701. (tView.preOrderHooks ??= []).push(directiveIndex, ngDoCheck);
  3702. (tView.preOrderCheckHooks ??= []).push(directiveIndex, ngDoCheck);
  3703. }
  3704. }
  3705. /**
  3706. *
  3707. * Loops through the directives on the provided `tNode` and queues hooks to be
  3708. * run that are not initialization hooks.
  3709. *
  3710. * Should be executed during `elementEnd()` and similar to
  3711. * preserve hook execution order. Content, view, and destroy hooks for projected
  3712. * components and directives must be called *before* their hosts.
  3713. *
  3714. * Sets up the content, view, and destroy hooks on the provided `tView`,
  3715. * see {@link HookData} for details about the data structure.
  3716. *
  3717. * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up
  3718. * separately at `elementStart`.
  3719. *
  3720. * @param tView The current TView
  3721. * @param tNode The TNode whose directives are to be searched for hooks to queue
  3722. */
  3723. function registerPostOrderHooks(tView, tNode) {
  3724. ngDevMode && assertFirstCreatePass(tView);
  3725. // It's necessary to loop through the directives at elementEnd() (rather than processing in
  3726. // directiveCreate) so we can preserve the current hook order. Content, view, and destroy
  3727. // hooks for projected components and directives must be called *before* their hosts.
  3728. for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) {
  3729. const directiveDef = tView.data[i];
  3730. ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef');
  3731. const lifecycleHooks = directiveDef.type.prototype;
  3732. const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy } = lifecycleHooks;
  3733. if (ngAfterContentInit) {
  3734. (tView.contentHooks ??= []).push(-i, ngAfterContentInit);
  3735. }
  3736. if (ngAfterContentChecked) {
  3737. (tView.contentHooks ??= []).push(i, ngAfterContentChecked);
  3738. (tView.contentCheckHooks ??= []).push(i, ngAfterContentChecked);
  3739. }
  3740. if (ngAfterViewInit) {
  3741. (tView.viewHooks ??= []).push(-i, ngAfterViewInit);
  3742. }
  3743. if (ngAfterViewChecked) {
  3744. (tView.viewHooks ??= []).push(i, ngAfterViewChecked);
  3745. (tView.viewCheckHooks ??= []).push(i, ngAfterViewChecked);
  3746. }
  3747. if (ngOnDestroy != null) {
  3748. (tView.destroyHooks ??= []).push(i, ngOnDestroy);
  3749. }
  3750. }
  3751. }
  3752. /**
  3753. * Executing hooks requires complex logic as we need to deal with 2 constraints.
  3754. *
  3755. * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
  3756. * once, across many change detection cycles. This must be true even if some hooks throw, or if
  3757. * some recursively trigger a change detection cycle.
  3758. * To solve that, it is required to track the state of the execution of these init hooks.
  3759. * This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
  3760. * and the index within that phase. They can be seen as a cursor in the following structure:
  3761. * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
  3762. * They are stored as flags in LView[FLAGS].
  3763. *
  3764. * 2. Pre-order hooks can be executed in batches, because of the select instruction.
  3765. * To be able to pause and resume their execution, we also need some state about the hook's array
  3766. * that is being processed:
  3767. * - the index of the next hook to be executed
  3768. * - the number of init hooks already found in the processed part of the array
  3769. * They are stored as flags in LView[PREORDER_HOOK_FLAGS].
  3770. */
  3771. /**
  3772. * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were
  3773. * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read
  3774. * / write of the init-hooks related flags.
  3775. * @param lView The LView where hooks are defined
  3776. * @param hooks Hooks to be run
  3777. * @param nodeIndex 3 cases depending on the value:
  3778. * - undefined: all hooks from the array should be executed (post-order case)
  3779. * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
  3780. * flushing the remaining hooks)
  3781. * - number: execute hooks only from the saved index until that node index exclusive (pre-order
  3782. * case, when executing select(number))
  3783. */
  3784. function executeCheckHooks(lView, hooks, nodeIndex) {
  3785. callHooks(lView, hooks, 3 /* InitPhaseState.InitPhaseCompleted */, nodeIndex);
  3786. }
  3787. /**
  3788. * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked,
  3789. * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed.
  3790. * @param lView The LView where hooks are defined
  3791. * @param hooks Hooks to be run
  3792. * @param initPhase A phase for which hooks should be run
  3793. * @param nodeIndex 3 cases depending on the value:
  3794. * - undefined: all hooks from the array should be executed (post-order case)
  3795. * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
  3796. * flushing the remaining hooks)
  3797. * - number: execute hooks only from the saved index until that node index exclusive (pre-order
  3798. * case, when executing select(number))
  3799. */
  3800. function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) {
  3801. ngDevMode &&
  3802. assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once');
  3803. if ((lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
  3804. callHooks(lView, hooks, initPhase, nodeIndex);
  3805. }
  3806. }
  3807. function incrementInitPhaseFlags(lView, initPhase) {
  3808. ngDevMode &&
  3809. assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.');
  3810. let flags = lView[FLAGS];
  3811. if ((flags & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
  3812. flags &= 8191 /* LViewFlags.IndexWithinInitPhaseReset */;
  3813. flags += 1 /* LViewFlags.InitPhaseStateIncrementer */;
  3814. lView[FLAGS] = flags;
  3815. }
  3816. }
  3817. /**
  3818. * Calls lifecycle hooks with their contexts, skipping init hooks if it's not
  3819. * the first LView pass
  3820. *
  3821. * @param currentView The current view
  3822. * @param arr The array in which the hooks are found
  3823. * @param initPhaseState the current state of the init phase
  3824. * @param currentNodeIndex 3 cases depending on the value:
  3825. * - undefined: all hooks from the array should be executed (post-order case)
  3826. * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
  3827. * flushing the remaining hooks)
  3828. * - number: execute hooks only from the saved index until that node index exclusive (pre-order
  3829. * case, when executing select(number))
  3830. */
  3831. function callHooks(currentView, arr, initPhase, currentNodeIndex) {
  3832. ngDevMode &&
  3833. assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.');
  3834. const startIndex = currentNodeIndex !== undefined ?
  3835. (currentView[PREORDER_HOOK_FLAGS] & 65535 /* PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask */) :
  3836. 0;
  3837. const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1;
  3838. const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1
  3839. let lastNodeIndexFound = 0;
  3840. for (let i = startIndex; i < max; i++) {
  3841. const hook = arr[i + 1];
  3842. if (typeof hook === 'number') {
  3843. lastNodeIndexFound = arr[i];
  3844. if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) {
  3845. break;
  3846. }
  3847. }
  3848. else {
  3849. const isInitHook = arr[i] < 0;
  3850. if (isInitHook) {
  3851. currentView[PREORDER_HOOK_FLAGS] += 65536 /* PreOrderHookFlags.NumberOfInitHooksCalledIncrementer */;
  3852. }
  3853. if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) {
  3854. callHook(currentView, initPhase, arr, i);
  3855. currentView[PREORDER_HOOK_FLAGS] =
  3856. (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* PreOrderHookFlags.NumberOfInitHooksCalledMask */) + i +
  3857. 2;
  3858. }
  3859. i++;
  3860. }
  3861. }
  3862. }
  3863. /**
  3864. * Executes a single lifecycle hook, making sure that:
  3865. * - it is called in the non-reactive context;
  3866. * - profiling data are registered.
  3867. */
  3868. function callHookInternal(directive, hook) {
  3869. profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook);
  3870. const prevConsumer = setActiveConsumer(null);
  3871. try {
  3872. hook.call(directive);
  3873. }
  3874. finally {
  3875. setActiveConsumer(prevConsumer);
  3876. profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook);
  3877. }
  3878. }
  3879. /**
  3880. * Execute one hook against the current `LView`.
  3881. *
  3882. * @param currentView The current view
  3883. * @param initPhaseState the current state of the init phase
  3884. * @param arr The array in which the hooks are found
  3885. * @param i The current index within the hook data array
  3886. */
  3887. function callHook(currentView, initPhase, arr, i) {
  3888. const isInitHook = arr[i] < 0;
  3889. const hook = arr[i + 1];
  3890. const directiveIndex = isInitHook ? -arr[i] : arr[i];
  3891. const directive = currentView[directiveIndex];
  3892. if (isInitHook) {
  3893. const indexWithintInitPhase = currentView[FLAGS] >> 13 /* LViewFlags.IndexWithinInitPhaseShift */;
  3894. // The init phase state must be always checked here as it may have been recursively updated.
  3895. if (indexWithintInitPhase <
  3896. (currentView[PREORDER_HOOK_FLAGS] >> 16 /* PreOrderHookFlags.NumberOfInitHooksCalledShift */) &&
  3897. (currentView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
  3898. currentView[FLAGS] += 8192 /* LViewFlags.IndexWithinInitPhaseIncrementer */;
  3899. callHookInternal(directive, hook);
  3900. }
  3901. }
  3902. else {
  3903. callHookInternal(directive, hook);
  3904. }
  3905. }
  3906. const NO_PARENT_INJECTOR = -1;
  3907. /**
  3908. * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in
  3909. * `TView.data`. This allows us to store information about the current node's tokens (which
  3910. * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be
  3911. * shared, so they live in `LView`).
  3912. *
  3913. * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter
  3914. * determines whether a directive is available on the associated node or not. This prevents us
  3915. * from searching the directives array at this level unless it's probable the directive is in it.
  3916. *
  3917. * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
  3918. *
  3919. * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed
  3920. * using interfaces as they were previously. The start index of each `LInjector` and `TInjector`
  3921. * will differ based on where it is flattened into the main array, so it's not possible to know
  3922. * the indices ahead of time and save their types here. The interfaces are still included here
  3923. * for documentation purposes.
  3924. *
  3925. * export interface LInjector extends Array<any> {
  3926. *
  3927. * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
  3928. * [0]: number;
  3929. *
  3930. * // Cumulative bloom for directive IDs 32-63
  3931. * [1]: number;
  3932. *
  3933. * // Cumulative bloom for directive IDs 64-95
  3934. * [2]: number;
  3935. *
  3936. * // Cumulative bloom for directive IDs 96-127
  3937. * [3]: number;
  3938. *
  3939. * // Cumulative bloom for directive IDs 128-159
  3940. * [4]: number;
  3941. *
  3942. * // Cumulative bloom for directive IDs 160 - 191
  3943. * [5]: number;
  3944. *
  3945. * // Cumulative bloom for directive IDs 192 - 223
  3946. * [6]: number;
  3947. *
  3948. * // Cumulative bloom for directive IDs 224 - 255
  3949. * [7]: number;
  3950. *
  3951. * // We need to store a reference to the injector's parent so DI can keep looking up
  3952. * // the injector tree until it finds the dependency it's looking for.
  3953. * [PARENT_INJECTOR]: number;
  3954. * }
  3955. *
  3956. * export interface TInjector extends Array<any> {
  3957. *
  3958. * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
  3959. * [0]: number;
  3960. *
  3961. * // Shared node bloom for directive IDs 32-63
  3962. * [1]: number;
  3963. *
  3964. * // Shared node bloom for directive IDs 64-95
  3965. * [2]: number;
  3966. *
  3967. * // Shared node bloom for directive IDs 96-127
  3968. * [3]: number;
  3969. *
  3970. * // Shared node bloom for directive IDs 128-159
  3971. * [4]: number;
  3972. *
  3973. * // Shared node bloom for directive IDs 160 - 191
  3974. * [5]: number;
  3975. *
  3976. * // Shared node bloom for directive IDs 192 - 223
  3977. * [6]: number;
  3978. *
  3979. * // Shared node bloom for directive IDs 224 - 255
  3980. * [7]: number;
  3981. *
  3982. * // Necessary to find directive indices for a particular node.
  3983. * [TNODE]: TElementNode|TElementContainerNode|TContainerNode;
  3984. * }
  3985. */
  3986. /**
  3987. * Factory for creating instances of injectors in the NodeInjector.
  3988. *
  3989. * This factory is complicated by the fact that it can resolve `multi` factories as well.
  3990. *
  3991. * NOTE: Some of the fields are optional which means that this class has two hidden classes.
  3992. * - One without `multi` support (most common)
  3993. * - One with `multi` values, (rare).
  3994. *
  3995. * Since VMs can cache up to 4 inline hidden classes this is OK.
  3996. *
  3997. * - Single factory: Only `resolving` and `factory` is defined.
  3998. * - `providers` factory: `componentProviders` is a number and `index = -1`.
  3999. * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`.
  4000. */
  4001. class NodeInjectorFactory {
  4002. constructor(
  4003. /**
  4004. * Factory to invoke in order to create a new instance.
  4005. */
  4006. factory,
  4007. /**
  4008. * Set to `true` if the token is declared in `viewProviders` (or if it is component).
  4009. */
  4010. isViewProvider, injectImplementation) {
  4011. this.factory = factory;
  4012. /**
  4013. * Marker set to true during factory invocation to see if we get into recursive loop.
  4014. * Recursive loop causes an error to be displayed.
  4015. */
  4016. this.resolving = false;
  4017. ngDevMode && assertDefined(factory, 'Factory not specified');
  4018. ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.');
  4019. this.canSeeViewProviders = isViewProvider;
  4020. this.injectImpl = injectImplementation;
  4021. }
  4022. }
  4023. function isFactory(obj) {
  4024. return obj instanceof NodeInjectorFactory;
  4025. }
  4026. // Note: This hack is necessary so we don't erroneously get a circular dependency
  4027. // failure based on types.
  4028. const unusedValueExportToPlacateAjd$2 = 1;
  4029. /**
  4030. * Converts `TNodeType` into human readable text.
  4031. * Make sure this matches with `TNodeType`
  4032. */
  4033. function toTNodeTypeAsString(tNodeType) {
  4034. let text = '';
  4035. (tNodeType & 1 /* TNodeType.Text */) && (text += '|Text');
  4036. (tNodeType & 2 /* TNodeType.Element */) && (text += '|Element');
  4037. (tNodeType & 4 /* TNodeType.Container */) && (text += '|Container');
  4038. (tNodeType & 8 /* TNodeType.ElementContainer */) && (text += '|ElementContainer');
  4039. (tNodeType & 16 /* TNodeType.Projection */) && (text += '|Projection');
  4040. (tNodeType & 32 /* TNodeType.Icu */) && (text += '|IcuContainer');
  4041. (tNodeType & 64 /* TNodeType.Placeholder */) && (text += '|Placeholder');
  4042. return text.length > 0 ? text.substring(1) : text;
  4043. }
  4044. // Note: This hack is necessary so we don't erroneously get a circular dependency
  4045. // failure based on types.
  4046. const unusedValueExportToPlacateAjd$1 = 1;
  4047. /**
  4048. * Returns `true` if the `TNode` has a directive which has `@Input()` for `class` binding.
  4049. *
  4050. * ```
  4051. * <div my-dir [class]="exp"></div>
  4052. * ```
  4053. * and
  4054. * ```
  4055. * @Directive({
  4056. * })
  4057. * class MyDirective {
  4058. * @Input()
  4059. * class: string;
  4060. * }
  4061. * ```
  4062. *
  4063. * In the above case it is necessary to write the reconciled styling information into the
  4064. * directive's input.
  4065. *
  4066. * @param tNode
  4067. */
  4068. function hasClassInput(tNode) {
  4069. return (tNode.flags & 8 /* TNodeFlags.hasClassInput */) !== 0;
  4070. }
  4071. /**
  4072. * Returns `true` if the `TNode` has a directive which has `@Input()` for `style` binding.
  4073. *
  4074. * ```
  4075. * <div my-dir [style]="exp"></div>
  4076. * ```
  4077. * and
  4078. * ```
  4079. * @Directive({
  4080. * })
  4081. * class MyDirective {
  4082. * @Input()
  4083. * class: string;
  4084. * }
  4085. * ```
  4086. *
  4087. * In the above case it is necessary to write the reconciled styling information into the
  4088. * directive's input.
  4089. *
  4090. * @param tNode
  4091. */
  4092. function hasStyleInput(tNode) {
  4093. return (tNode.flags & 16 /* TNodeFlags.hasStyleInput */) !== 0;
  4094. }
  4095. function assertTNodeType(tNode, expectedTypes, message) {
  4096. assertDefined(tNode, 'should be called with a TNode');
  4097. if ((tNode.type & expectedTypes) === 0) {
  4098. throwError(message ||
  4099. `Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${toTNodeTypeAsString(tNode.type)}.`);
  4100. }
  4101. }
  4102. function assertPureTNodeType(type) {
  4103. if (!(type === 2 /* TNodeType.Element */ || //
  4104. type === 1 /* TNodeType.Text */ || //
  4105. type === 4 /* TNodeType.Container */ || //
  4106. type === 8 /* TNodeType.ElementContainer */ || //
  4107. type === 32 /* TNodeType.Icu */ || //
  4108. type === 16 /* TNodeType.Projection */ || //
  4109. type === 64 /* TNodeType.Placeholder */)) {
  4110. throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`);
  4111. }
  4112. }
  4113. /// Parent Injector Utils ///////////////////////////////////////////////////////////////
  4114. function hasParentInjector(parentLocation) {
  4115. return parentLocation !== NO_PARENT_INJECTOR;
  4116. }
  4117. function getParentInjectorIndex(parentLocation) {
  4118. ngDevMode && assertNumber(parentLocation, 'Number expected');
  4119. ngDevMode && assertNotEqual(parentLocation, -1, 'Not a valid state.');
  4120. const parentInjectorIndex = parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */;
  4121. ngDevMode &&
  4122. assertGreaterThan(parentInjectorIndex, HEADER_OFFSET, 'Parent injector must be pointing past HEADER_OFFSET.');
  4123. return parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */;
  4124. }
  4125. function getParentInjectorViewOffset(parentLocation) {
  4126. return parentLocation >> 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */;
  4127. }
  4128. /**
  4129. * Unwraps a parent injector location number to find the view offset from the current injector,
  4130. * then walks up the declaration view tree until the view is found that contains the parent
  4131. * injector.
  4132. *
  4133. * @param location The location of the parent injector, which contains the view offset
  4134. * @param startView The LView instance from which to start walking up the view tree
  4135. * @returns The LView instance that contains the parent injector
  4136. */
  4137. function getParentInjectorView(location, startView) {
  4138. let viewOffset = getParentInjectorViewOffset(location);
  4139. let parentView = startView;
  4140. // For most cases, the parent injector can be found on the host node (e.g. for component
  4141. // or container), but we must keep the loop here to support the rarer case of deeply nested
  4142. // <ng-template> tags or inline views, where the parent injector might live many views
  4143. // above the child injector.
  4144. while (viewOffset > 0) {
  4145. parentView = parentView[DECLARATION_VIEW];
  4146. viewOffset--;
  4147. }
  4148. return parentView;
  4149. }
  4150. /**
  4151. * Defines if the call to `inject` should include `viewProviders` in its resolution.
  4152. *
  4153. * This is set to true when we try to instantiate a component. This value is reset in
  4154. * `getNodeInjectable` to a value which matches the declaration location of the token about to be
  4155. * instantiated. This is done so that if we are injecting a token which was declared outside of
  4156. * `viewProviders` we don't accidentally pull `viewProviders` in.
  4157. *
  4158. * Example:
  4159. *
  4160. * ```
  4161. * @Injectable()
  4162. * class MyService {
  4163. * constructor(public value: String) {}
  4164. * }
  4165. *
  4166. * @Component({
  4167. * providers: [
  4168. * MyService,
  4169. * {provide: String, value: 'providers' }
  4170. * ]
  4171. * viewProviders: [
  4172. * {provide: String, value: 'viewProviders'}
  4173. * ]
  4174. * })
  4175. * class MyComponent {
  4176. * constructor(myService: MyService, value: String) {
  4177. * // We expect that Component can see into `viewProviders`.
  4178. * expect(value).toEqual('viewProviders');
  4179. * // `MyService` was not declared in `viewProviders` hence it can't see it.
  4180. * expect(myService.value).toEqual('providers');
  4181. * }
  4182. * }
  4183. *
  4184. * ```
  4185. */
  4186. let includeViewProviders = true;
  4187. function setIncludeViewProviders(v) {
  4188. const oldValue = includeViewProviders;
  4189. includeViewProviders = v;
  4190. return oldValue;
  4191. }
  4192. /**
  4193. * The number of slots in each bloom filter (used by DI). The larger this number, the fewer
  4194. * directives that will share slots, and thus, the fewer false positives when checking for
  4195. * the existence of a directive.
  4196. */
  4197. const BLOOM_SIZE = 256;
  4198. const BLOOM_MASK = BLOOM_SIZE - 1;
  4199. /**
  4200. * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits,
  4201. * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash
  4202. * number.
  4203. */
  4204. const BLOOM_BUCKET_BITS = 5;
  4205. /** Counter used to generate unique IDs for directives. */
  4206. let nextNgElementId = 0;
  4207. /** Value used when something wasn't found by an injector. */
  4208. const NOT_FOUND = {};
  4209. /**
  4210. * Registers this directive as present in its node's injector by flipping the directive's
  4211. * corresponding bit in the injector's bloom filter.
  4212. *
  4213. * @param injectorIndex The index of the node injector where this token should be registered
  4214. * @param tView The TView for the injector's bloom filters
  4215. * @param type The directive token to register
  4216. */
  4217. function bloomAdd(injectorIndex, tView, type) {
  4218. ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true');
  4219. let id;
  4220. if (typeof type === 'string') {
  4221. id = type.charCodeAt(0) || 0;
  4222. }
  4223. else if (type.hasOwnProperty(NG_ELEMENT_ID)) {
  4224. id = type[NG_ELEMENT_ID];
  4225. }
  4226. // Set a unique ID on the directive type, so if something tries to inject the directive,
  4227. // we can easily retrieve the ID and hash it into the bloom bit that should be checked.
  4228. if (id == null) {
  4229. id = type[NG_ELEMENT_ID] = nextNgElementId++;
  4230. }
  4231. // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
  4232. // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
  4233. const bloomHash = id & BLOOM_MASK;
  4234. // Create a mask that targets the specific bit associated with the directive.
  4235. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
  4236. // to bit positions 0 - 31 in a 32 bit integer.
  4237. const mask = 1 << bloomHash;
  4238. // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`.
  4239. // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask
  4240. // should be written to.
  4241. tView.data[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask;
  4242. }
  4243. /**
  4244. * Creates (or gets an existing) injector for a given element or container.
  4245. *
  4246. * @param tNode for which an injector should be retrieved / created.
  4247. * @param lView View where the node is stored
  4248. * @returns Node injector
  4249. */
  4250. function getOrCreateNodeInjectorForNode(tNode, lView) {
  4251. const existingInjectorIndex = getInjectorIndex(tNode, lView);
  4252. if (existingInjectorIndex !== -1) {
  4253. return existingInjectorIndex;
  4254. }
  4255. const tView = lView[TVIEW];
  4256. if (tView.firstCreatePass) {
  4257. tNode.injectorIndex = lView.length;
  4258. insertBloom(tView.data, tNode); // foundation for node bloom
  4259. insertBloom(lView, null); // foundation for cumulative bloom
  4260. insertBloom(tView.blueprint, null);
  4261. }
  4262. const parentLoc = getParentInjectorLocation(tNode, lView);
  4263. const injectorIndex = tNode.injectorIndex;
  4264. // If a parent injector can't be found, its location is set to -1.
  4265. // In that case, we don't need to set up a cumulative bloom
  4266. if (hasParentInjector(parentLoc)) {
  4267. const parentIndex = getParentInjectorIndex(parentLoc);
  4268. const parentLView = getParentInjectorView(parentLoc, lView);
  4269. const parentData = parentLView[TVIEW].data;
  4270. // Creates a cumulative bloom filter that merges the parent's bloom filter
  4271. // and its own cumulative bloom (which contains tokens for all ancestors)
  4272. for (let i = 0; i < 8 /* NodeInjectorOffset.BLOOM_SIZE */; i++) {
  4273. lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i];
  4274. }
  4275. }
  4276. lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */] = parentLoc;
  4277. return injectorIndex;
  4278. }
  4279. function insertBloom(arr, footer) {
  4280. arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer);
  4281. }
  4282. function getInjectorIndex(tNode, lView) {
  4283. if (tNode.injectorIndex === -1 ||
  4284. // If the injector index is the same as its parent's injector index, then the index has been
  4285. // copied down from the parent node. No injector has been created yet on this node.
  4286. (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) ||
  4287. // After the first template pass, the injector index might exist but the parent values
  4288. // might not have been calculated yet for this instance
  4289. lView[tNode.injectorIndex + 8 /* NodeInjectorOffset.PARENT */] === null) {
  4290. return -1;
  4291. }
  4292. else {
  4293. ngDevMode && assertIndexInRange(lView, tNode.injectorIndex);
  4294. return tNode.injectorIndex;
  4295. }
  4296. }
  4297. /**
  4298. * Finds the index of the parent injector, with a view offset if applicable. Used to set the
  4299. * parent injector initially.
  4300. *
  4301. * @returns Returns a number that is the combination of the number of LViews that we have to go up
  4302. * to find the LView containing the parent inject AND the index of the injector within that LView.
  4303. */
  4304. function getParentInjectorLocation(tNode, lView) {
  4305. if (tNode.parent && tNode.parent.injectorIndex !== -1) {
  4306. // If we have a parent `TNode` and there is an injector associated with it we are done, because
  4307. // the parent injector is within the current `LView`.
  4308. return tNode.parent.injectorIndex; // ViewOffset is 0
  4309. }
  4310. // When parent injector location is computed it may be outside of the current view. (ie it could
  4311. // be pointing to a declared parent location). This variable stores number of declaration parents
  4312. // we need to walk up in order to find the parent injector location.
  4313. let declarationViewOffset = 0;
  4314. let parentTNode = null;
  4315. let lViewCursor = lView;
  4316. // The parent injector is not in the current `LView`. We will have to walk the declared parent
  4317. // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent
  4318. // `NodeInjector`.
  4319. while (lViewCursor !== null) {
  4320. parentTNode = getTNodeFromLView(lViewCursor);
  4321. if (parentTNode === null) {
  4322. // If we have no parent, than we are done.
  4323. return NO_PARENT_INJECTOR;
  4324. }
  4325. ngDevMode && parentTNode && assertTNodeForLView(parentTNode, lViewCursor[DECLARATION_VIEW]);
  4326. // Every iteration of the loop requires that we go to the declared parent.
  4327. declarationViewOffset++;
  4328. lViewCursor = lViewCursor[DECLARATION_VIEW];
  4329. if (parentTNode.injectorIndex !== -1) {
  4330. // We found a NodeInjector which points to something.
  4331. return (parentTNode.injectorIndex |
  4332. (declarationViewOffset << 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */));
  4333. }
  4334. }
  4335. return NO_PARENT_INJECTOR;
  4336. }
  4337. /**
  4338. * Makes a type or an injection token public to the DI system by adding it to an
  4339. * injector's bloom filter.
  4340. *
  4341. * @param di The node injector in which a directive will be added
  4342. * @param token The type or the injection token to be made public
  4343. */
  4344. function diPublicInInjector(injectorIndex, tView, token) {
  4345. bloomAdd(injectorIndex, tView, token);
  4346. }
  4347. /**
  4348. * Inject static attribute value into directive constructor.
  4349. *
  4350. * This method is used with `factory` functions which are generated as part of
  4351. * `defineDirective` or `defineComponent`. The method retrieves the static value
  4352. * of an attribute. (Dynamic attributes are not supported since they are not resolved
  4353. * at the time of injection and can change over time.)
  4354. *
  4355. * # Example
  4356. * Given:
  4357. * ```
  4358. * @Component(...)
  4359. * class MyComponent {
  4360. * constructor(@Attribute('title') title: string) { ... }
  4361. * }
  4362. * ```
  4363. * When instantiated with
  4364. * ```
  4365. * <my-component title="Hello"></my-component>
  4366. * ```
  4367. *
  4368. * Then factory method generated is:
  4369. * ```
  4370. * MyComponent.ɵcmp = defineComponent({
  4371. * factory: () => new MyComponent(injectAttribute('title'))
  4372. * ...
  4373. * })
  4374. * ```
  4375. *
  4376. * @publicApi
  4377. */
  4378. function injectAttributeImpl(tNode, attrNameToInject) {
  4379. ngDevMode && assertTNodeType(tNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
  4380. ngDevMode && assertDefined(tNode, 'expecting tNode');
  4381. if (attrNameToInject === 'class') {
  4382. return tNode.classes;
  4383. }
  4384. if (attrNameToInject === 'style') {
  4385. return tNode.styles;
  4386. }
  4387. const attrs = tNode.attrs;
  4388. if (attrs) {
  4389. const attrsLength = attrs.length;
  4390. let i = 0;
  4391. while (i < attrsLength) {
  4392. const value = attrs[i];
  4393. // If we hit a `Bindings` or `Template` marker then we are done.
  4394. if (isNameOnlyAttributeMarker(value))
  4395. break;
  4396. // Skip namespaced attributes
  4397. if (value === 0 /* AttributeMarker.NamespaceURI */) {
  4398. // we skip the next two values
  4399. // as namespaced attributes looks like
  4400. // [..., AttributeMarker.NamespaceURI, 'http://someuri.com/test', 'test:exist',
  4401. // 'existValue', ...]
  4402. i = i + 2;
  4403. }
  4404. else if (typeof value === 'number') {
  4405. // Skip to the first value of the marked attribute.
  4406. i++;
  4407. while (i < attrsLength && typeof attrs[i] === 'string') {
  4408. i++;
  4409. }
  4410. }
  4411. else if (value === attrNameToInject) {
  4412. return attrs[i + 1];
  4413. }
  4414. else {
  4415. i = i + 2;
  4416. }
  4417. }
  4418. }
  4419. return null;
  4420. }
  4421. function notFoundValueOrThrow(notFoundValue, token, flags) {
  4422. if ((flags & InjectFlags.Optional) || notFoundValue !== undefined) {
  4423. return notFoundValue;
  4424. }
  4425. else {
  4426. throwProviderNotFoundError(token, 'NodeInjector');
  4427. }
  4428. }
  4429. /**
  4430. * Returns the value associated to the given token from the ModuleInjector or throws exception
  4431. *
  4432. * @param lView The `LView` that contains the `tNode`
  4433. * @param token The token to look for
  4434. * @param flags Injection flags
  4435. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  4436. * @returns the value from the injector or throws an exception
  4437. */
  4438. function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) {
  4439. if ((flags & InjectFlags.Optional) && notFoundValue === undefined) {
  4440. // This must be set or the NullInjector will throw for optional deps
  4441. notFoundValue = null;
  4442. }
  4443. if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) {
  4444. const moduleInjector = lView[INJECTOR$1];
  4445. // switch to `injectInjectorOnly` implementation for module injector, since module injector
  4446. // should not have access to Component/Directive DI scope (that may happen through
  4447. // `directiveInject` implementation)
  4448. const previousInjectImplementation = setInjectImplementation(undefined);
  4449. try {
  4450. if (moduleInjector) {
  4451. return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional);
  4452. }
  4453. else {
  4454. return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional);
  4455. }
  4456. }
  4457. finally {
  4458. setInjectImplementation(previousInjectImplementation);
  4459. }
  4460. }
  4461. return notFoundValueOrThrow(notFoundValue, token, flags);
  4462. }
  4463. /**
  4464. * Returns the value associated to the given token from the NodeInjectors => ModuleInjector.
  4465. *
  4466. * Look for the injector providing the token by walking up the node injector tree and then
  4467. * the module injector tree.
  4468. *
  4469. * This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom
  4470. * filter. `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`)
  4471. *
  4472. * @param tNode The Node where the search for the injector should start
  4473. * @param lView The `LView` that contains the `tNode`
  4474. * @param token The token to look for
  4475. * @param flags Injection flags
  4476. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  4477. * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
  4478. */
  4479. function getOrCreateInjectable(tNode, lView, token, flags = InjectFlags.Default, notFoundValue) {
  4480. if (tNode !== null) {
  4481. // If the view or any of its ancestors have an embedded
  4482. // view injector, we have to look it up there first.
  4483. if (lView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */ &&
  4484. // The token must be present on the current node injector when the `Self`
  4485. // flag is set, so the lookup on embedded view injector(s) can be skipped.
  4486. !(flags & InjectFlags.Self)) {
  4487. const embeddedInjectorValue = lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, NOT_FOUND);
  4488. if (embeddedInjectorValue !== NOT_FOUND) {
  4489. return embeddedInjectorValue;
  4490. }
  4491. }
  4492. // Otherwise try the node injector.
  4493. const value = lookupTokenUsingNodeInjector(tNode, lView, token, flags, NOT_FOUND);
  4494. if (value !== NOT_FOUND) {
  4495. return value;
  4496. }
  4497. }
  4498. // Finally, fall back to the module injector.
  4499. return lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
  4500. }
  4501. /**
  4502. * Returns the value associated to the given token from the node injector.
  4503. *
  4504. * @param tNode The Node where the search for the injector should start
  4505. * @param lView The `LView` that contains the `tNode`
  4506. * @param token The token to look for
  4507. * @param flags Injection flags
  4508. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  4509. * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
  4510. */
  4511. function lookupTokenUsingNodeInjector(tNode, lView, token, flags, notFoundValue) {
  4512. const bloomHash = bloomHashBitOrFactory(token);
  4513. // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
  4514. // so just call the factory function to create it.
  4515. if (typeof bloomHash === 'function') {
  4516. if (!enterDI(lView, tNode, flags)) {
  4517. // Failed to enter DI, try module injector instead. If a token is injected with the @Host
  4518. // flag, the module injector is not searched for that token in Ivy.
  4519. return (flags & InjectFlags.Host) ?
  4520. notFoundValueOrThrow(notFoundValue, token, flags) :
  4521. lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
  4522. }
  4523. try {
  4524. let value;
  4525. if (ngDevMode) {
  4526. runInInjectorProfilerContext(new NodeInjector(getCurrentTNode(), getLView()), token, () => {
  4527. value = bloomHash(flags);
  4528. if (value != null) {
  4529. emitInstanceCreatedByInjectorEvent(value);
  4530. }
  4531. });
  4532. }
  4533. else {
  4534. value = bloomHash(flags);
  4535. }
  4536. if (value == null && !(flags & InjectFlags.Optional)) {
  4537. throwProviderNotFoundError(token);
  4538. }
  4539. else {
  4540. return value;
  4541. }
  4542. }
  4543. finally {
  4544. leaveDI();
  4545. }
  4546. }
  4547. else if (typeof bloomHash === 'number') {
  4548. // A reference to the previous injector TView that was found while climbing the element
  4549. // injector tree. This is used to know if viewProviders can be accessed on the current
  4550. // injector.
  4551. let previousTView = null;
  4552. let injectorIndex = getInjectorIndex(tNode, lView);
  4553. let parentLocation = NO_PARENT_INJECTOR;
  4554. let hostTElementNode = flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null;
  4555. // If we should skip this injector, or if there is no injector on this node, start by
  4556. // searching the parent injector.
  4557. if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
  4558. parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
  4559. lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */];
  4560. if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) {
  4561. injectorIndex = -1;
  4562. }
  4563. else {
  4564. previousTView = lView[TVIEW];
  4565. injectorIndex = getParentInjectorIndex(parentLocation);
  4566. lView = getParentInjectorView(parentLocation, lView);
  4567. }
  4568. }
  4569. // Traverse up the injector tree until we find a potential match or until we know there
  4570. // *isn't* a match.
  4571. while (injectorIndex !== -1) {
  4572. ngDevMode && assertNodeInjector(lView, injectorIndex);
  4573. // Check the current injector. If it matches, see if it contains token.
  4574. const tView = lView[TVIEW];
  4575. ngDevMode &&
  4576. assertTNodeForLView(tView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */], lView);
  4577. if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
  4578. // At this point, we have an injector which *may* contain the token, so we step through
  4579. // the providers and directives associated with the injector's corresponding node to get
  4580. // the instance.
  4581. const instance = searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode);
  4582. if (instance !== NOT_FOUND) {
  4583. return instance;
  4584. }
  4585. }
  4586. parentLocation = lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */];
  4587. if (parentLocation !== NO_PARENT_INJECTOR &&
  4588. shouldSearchParent(flags, lView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */] === hostTElementNode) &&
  4589. bloomHasToken(bloomHash, injectorIndex, lView)) {
  4590. // The def wasn't found anywhere on this node, so it was a false positive.
  4591. // Traverse up the tree and continue searching.
  4592. previousTView = tView;
  4593. injectorIndex = getParentInjectorIndex(parentLocation);
  4594. lView = getParentInjectorView(parentLocation, lView);
  4595. }
  4596. else {
  4597. // If we should not search parent OR If the ancestor bloom filter value does not have the
  4598. // bit corresponding to the directive we can give up on traversing up to find the specific
  4599. // injector.
  4600. injectorIndex = -1;
  4601. }
  4602. }
  4603. }
  4604. return notFoundValue;
  4605. }
  4606. function searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode) {
  4607. const currentTView = lView[TVIEW];
  4608. const tNode = currentTView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
  4609. // First, we need to determine if view providers can be accessed by the starting element.
  4610. // There are two possibilities
  4611. const canAccessViewProviders = previousTView == null ?
  4612. // 1) This is the first invocation `previousTView == null` which means that we are at the
  4613. // `TNode` of where injector is starting to look. In such a case the only time we are allowed
  4614. // to look into the ViewProviders is if:
  4615. // - we are on a component
  4616. // - AND the injector set `includeViewProviders` to true (implying that the token can see
  4617. // ViewProviders because it is the Component or a Service which itself was declared in
  4618. // ViewProviders)
  4619. (isComponentHost(tNode) && includeViewProviders) :
  4620. // 2) `previousTView != null` which means that we are now walking across the parent nodes.
  4621. // In such a case we are only allowed to look into the ViewProviders if:
  4622. // - We just crossed from child View to Parent View `previousTView != currentTView`
  4623. // - AND the parent TNode is an Element.
  4624. // This means that we just came from the Component's View and therefore are allowed to see
  4625. // into the ViewProviders.
  4626. (previousTView != currentTView && ((tNode.type & 3 /* TNodeType.AnyRNode */) !== 0));
  4627. // This special case happens when there is a @host on the inject and when we are searching
  4628. // on the host element node.
  4629. const isHostSpecialCase = (flags & InjectFlags.Host) && hostTElementNode === tNode;
  4630. const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase);
  4631. if (injectableIdx !== null) {
  4632. return getNodeInjectable(lView, currentTView, injectableIdx, tNode);
  4633. }
  4634. else {
  4635. return NOT_FOUND;
  4636. }
  4637. }
  4638. /**
  4639. * Searches for the given token among the node's directives and providers.
  4640. *
  4641. * @param tNode TNode on which directives are present.
  4642. * @param tView The tView we are currently processing
  4643. * @param token Provider token or type of a directive to look for.
  4644. * @param canAccessViewProviders Whether view providers should be considered.
  4645. * @param isHostSpecialCase Whether the host special case applies.
  4646. * @returns Index of a found directive or provider, or null when none found.
  4647. */
  4648. function locateDirectiveOrProvider(tNode, tView, token, canAccessViewProviders, isHostSpecialCase) {
  4649. const nodeProviderIndexes = tNode.providerIndexes;
  4650. const tInjectables = tView.data;
  4651. const injectablesStart = nodeProviderIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
  4652. const directivesStart = tNode.directiveStart;
  4653. const directiveEnd = tNode.directiveEnd;
  4654. const cptViewProvidersCount = nodeProviderIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */;
  4655. const startingIndex = canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
  4656. // When the host special case applies, only the viewProviders and the component are visible
  4657. const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd;
  4658. for (let i = startingIndex; i < endIndex; i++) {
  4659. const providerTokenOrDef = tInjectables[i];
  4660. if (i < directivesStart && token === providerTokenOrDef ||
  4661. i >= directivesStart && providerTokenOrDef.type === token) {
  4662. return i;
  4663. }
  4664. }
  4665. if (isHostSpecialCase) {
  4666. const dirDef = tInjectables[directivesStart];
  4667. if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
  4668. return directivesStart;
  4669. }
  4670. }
  4671. return null;
  4672. }
  4673. /**
  4674. * Retrieve or instantiate the injectable from the `LView` at particular `index`.
  4675. *
  4676. * This function checks to see if the value has already been instantiated and if so returns the
  4677. * cached `injectable`. Otherwise if it detects that the value is still a factory it
  4678. * instantiates the `injectable` and caches the value.
  4679. */
  4680. function getNodeInjectable(lView, tView, index, tNode) {
  4681. let value = lView[index];
  4682. const tData = tView.data;
  4683. if (isFactory(value)) {
  4684. const factory = value;
  4685. if (factory.resolving) {
  4686. throwCyclicDependencyError(stringifyForError(tData[index]));
  4687. }
  4688. const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
  4689. factory.resolving = true;
  4690. let prevInjectContext;
  4691. if (ngDevMode) {
  4692. // tData indexes mirror the concrete instances in its corresponding LView.
  4693. // lView[index] here is either the injectable instace itself or a factory,
  4694. // therefore tData[index] is the constructor of that injectable or a
  4695. // definition object that contains the constructor in a `.type` field.
  4696. const token = tData[index].type || tData[index];
  4697. const injector = new NodeInjector(tNode, lView);
  4698. prevInjectContext = setInjectorProfilerContext({ injector, token });
  4699. }
  4700. const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null;
  4701. const success = enterDI(lView, tNode, InjectFlags.Default);
  4702. ngDevMode &&
  4703. assertEqual(success, true, 'Because flags do not contain \`SkipSelf\' we expect this to always succeed.');
  4704. try {
  4705. value = lView[index] = factory.factory(undefined, tData, lView, tNode);
  4706. ngDevMode && emitInstanceCreatedByInjectorEvent(value);
  4707. // This code path is hit for both directives and providers.
  4708. // For perf reasons, we want to avoid searching for hooks on providers.
  4709. // It does no harm to try (the hooks just won't exist), but the extra
  4710. // checks are unnecessary and this is a hot path. So we check to see
  4711. // if the index of the dependency is in the directive range for this
  4712. // tNode. If it's not, we know it's a provider and skip hook registration.
  4713. if (tView.firstCreatePass && index >= tNode.directiveStart) {
  4714. ngDevMode && assertDirectiveDef(tData[index]);
  4715. registerPreOrderHooks(index, tData[index], tView);
  4716. }
  4717. }
  4718. finally {
  4719. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  4720. previousInjectImplementation !== null &&
  4721. setInjectImplementation(previousInjectImplementation);
  4722. setIncludeViewProviders(previousIncludeViewProviders);
  4723. factory.resolving = false;
  4724. leaveDI();
  4725. }
  4726. }
  4727. return value;
  4728. }
  4729. /**
  4730. * Returns the bit in an injector's bloom filter that should be used to determine whether or not
  4731. * the directive might be provided by the injector.
  4732. *
  4733. * When a directive is public, it is added to the bloom filter and given a unique ID that can be
  4734. * retrieved on the Type. When the directive isn't public or the token is not a directive `null`
  4735. * is returned as the node injector can not possibly provide that token.
  4736. *
  4737. * @param token the injection token
  4738. * @returns the matching bit to check in the bloom filter or `null` if the token is not known.
  4739. * When the returned value is negative then it represents special values such as `Injector`.
  4740. */
  4741. function bloomHashBitOrFactory(token) {
  4742. ngDevMode && assertDefined(token, 'token must be defined');
  4743. if (typeof token === 'string') {
  4744. return token.charCodeAt(0) || 0;
  4745. }
  4746. const tokenId =
  4747. // First check with `hasOwnProperty` so we don't get an inherited ID.
  4748. token.hasOwnProperty(NG_ELEMENT_ID) ? token[NG_ELEMENT_ID] : undefined;
  4749. // Negative token IDs are used for special objects such as `Injector`
  4750. if (typeof tokenId === 'number') {
  4751. if (tokenId >= 0) {
  4752. return tokenId & BLOOM_MASK;
  4753. }
  4754. else {
  4755. ngDevMode &&
  4756. assertEqual(tokenId, -1 /* InjectorMarkers.Injector */, 'Expecting to get Special Injector Id');
  4757. return createNodeInjector;
  4758. }
  4759. }
  4760. else {
  4761. return tokenId;
  4762. }
  4763. }
  4764. function bloomHasToken(bloomHash, injectorIndex, injectorView) {
  4765. // Create a mask that targets the specific bit associated with the directive we're looking for.
  4766. // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
  4767. // to bit positions 0 - 31 in a 32 bit integer.
  4768. const mask = 1 << bloomHash;
  4769. // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of
  4770. // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset
  4771. // that should be used.
  4772. const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)];
  4773. // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
  4774. // this injector is a potential match.
  4775. return !!(value & mask);
  4776. }
  4777. /** Returns true if flags prevent parent injector from being searched for tokens */
  4778. function shouldSearchParent(flags, isFirstHostTNode) {
  4779. return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
  4780. }
  4781. function getNodeInjectorLView(nodeInjector) {
  4782. return nodeInjector._lView;
  4783. }
  4784. function getNodeInjectorTNode(nodeInjector) {
  4785. return nodeInjector._tNode;
  4786. }
  4787. class NodeInjector {
  4788. constructor(_tNode, _lView) {
  4789. this._tNode = _tNode;
  4790. this._lView = _lView;
  4791. }
  4792. get(token, notFoundValue, flags) {
  4793. return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue);
  4794. }
  4795. }
  4796. /** Creates a `NodeInjector` for the current node. */
  4797. function createNodeInjector() {
  4798. return new NodeInjector(getCurrentTNode(), getLView());
  4799. }
  4800. /**
  4801. * @codeGenApi
  4802. */
  4803. function ɵɵgetInheritedFactory(type) {
  4804. return noSideEffects(() => {
  4805. const ownConstructor = type.prototype.constructor;
  4806. const ownFactory = ownConstructor[NG_FACTORY_DEF] || getFactoryOf(ownConstructor);
  4807. const objectPrototype = Object.prototype;
  4808. let parent = Object.getPrototypeOf(type.prototype).constructor;
  4809. // Go up the prototype until we hit `Object`.
  4810. while (parent && parent !== objectPrototype) {
  4811. const factory = parent[NG_FACTORY_DEF] || getFactoryOf(parent);
  4812. // If we hit something that has a factory and the factory isn't the same as the type,
  4813. // we've found the inherited factory. Note the check that the factory isn't the type's
  4814. // own factory is redundant in most cases, but if the user has custom decorators on the
  4815. // class, this lookup will start one level down in the prototype chain, causing us to
  4816. // find the own factory first and potentially triggering an infinite loop downstream.
  4817. if (factory && factory !== ownFactory) {
  4818. return factory;
  4819. }
  4820. parent = Object.getPrototypeOf(parent);
  4821. }
  4822. // There is no factory defined. Either this was improper usage of inheritance
  4823. // (no Angular decorator on the superclass) or there is no constructor at all
  4824. // in the inheritance chain. Since the two cases cannot be distinguished, the
  4825. // latter has to be assumed.
  4826. return (t) => new t();
  4827. });
  4828. }
  4829. function getFactoryOf(type) {
  4830. if (isForwardRef(type)) {
  4831. return () => {
  4832. const factory = getFactoryOf(resolveForwardRef(type));
  4833. return factory && factory();
  4834. };
  4835. }
  4836. return getFactoryDef(type);
  4837. }
  4838. /**
  4839. * Returns a value from the closest embedded or node injector.
  4840. *
  4841. * @param tNode The Node where the search for the injector should start
  4842. * @param lView The `LView` that contains the `tNode`
  4843. * @param token The token to look for
  4844. * @param flags Injection flags
  4845. * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
  4846. * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
  4847. */
  4848. function lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, notFoundValue) {
  4849. let currentTNode = tNode;
  4850. let currentLView = lView;
  4851. // When an LView with an embedded view injector is inserted, it'll likely be interlaced with
  4852. // nodes who may have injectors (e.g. node injector -> embedded view injector -> node injector).
  4853. // Since the bloom filters for the node injectors have already been constructed and we don't
  4854. // have a way of extracting the records from an injector, the only way to maintain the correct
  4855. // hierarchy when resolving the value is to walk it node-by-node while attempting to resolve
  4856. // the token at each level.
  4857. while (currentTNode !== null && currentLView !== null &&
  4858. (currentLView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */) &&
  4859. !(currentLView[FLAGS] & 512 /* LViewFlags.IsRoot */)) {
  4860. ngDevMode && assertTNodeForLView(currentTNode, currentLView);
  4861. // Note that this lookup on the node injector is using the `Self` flag, because
  4862. // we don't want the node injector to look at any parent injectors since we
  4863. // may hit the embedded view injector first.
  4864. const nodeInjectorValue = lookupTokenUsingNodeInjector(currentTNode, currentLView, token, flags | InjectFlags.Self, NOT_FOUND);
  4865. if (nodeInjectorValue !== NOT_FOUND) {
  4866. return nodeInjectorValue;
  4867. }
  4868. // Has an explicit type due to a TS bug: https://github.com/microsoft/TypeScript/issues/33191
  4869. let parentTNode = currentTNode.parent;
  4870. // `TNode.parent` includes the parent within the current view only. If it doesn't exist,
  4871. // it means that we've hit the view boundary and we need to go up to the next view.
  4872. if (!parentTNode) {
  4873. // Before we go to the next LView, check if the token exists on the current embedded injector.
  4874. const embeddedViewInjector = currentLView[EMBEDDED_VIEW_INJECTOR];
  4875. if (embeddedViewInjector) {
  4876. const embeddedViewInjectorValue = embeddedViewInjector.get(token, NOT_FOUND, flags);
  4877. if (embeddedViewInjectorValue !== NOT_FOUND) {
  4878. return embeddedViewInjectorValue;
  4879. }
  4880. }
  4881. // Otherwise keep going up the tree.
  4882. parentTNode = getTNodeFromLView(currentLView);
  4883. currentLView = currentLView[DECLARATION_VIEW];
  4884. }
  4885. currentTNode = parentTNode;
  4886. }
  4887. return notFoundValue;
  4888. }
  4889. /** Gets the TNode associated with an LView inside of the declaration view. */
  4890. function getTNodeFromLView(lView) {
  4891. const tView = lView[TVIEW];
  4892. const tViewType = tView.type;
  4893. // The parent pointer differs based on `TView.type`.
  4894. if (tViewType === 2 /* TViewType.Embedded */) {
  4895. ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
  4896. return tView.declTNode;
  4897. }
  4898. else if (tViewType === 1 /* TViewType.Component */) {
  4899. // Components don't have `TView.declTNode` because each instance of component could be
  4900. // inserted in different location, hence `TView.declTNode` is meaningless.
  4901. return lView[T_HOST];
  4902. }
  4903. return null;
  4904. }
  4905. /**
  4906. * Facade for the attribute injection from DI.
  4907. *
  4908. * @codeGenApi
  4909. */
  4910. function ɵɵinjectAttribute(attrNameToInject) {
  4911. return injectAttributeImpl(getCurrentTNode(), attrNameToInject);
  4912. }
  4913. const ANNOTATIONS = '__annotations__';
  4914. const PARAMETERS = '__parameters__';
  4915. const PROP_METADATA = '__prop__metadata__';
  4916. /**
  4917. * @suppress {globalThis}
  4918. */
  4919. function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) {
  4920. return noSideEffects(() => {
  4921. const metaCtor = makeMetadataCtor(props);
  4922. function DecoratorFactory(...args) {
  4923. if (this instanceof DecoratorFactory) {
  4924. metaCtor.call(this, ...args);
  4925. return this;
  4926. }
  4927. const annotationInstance = new DecoratorFactory(...args);
  4928. return function TypeDecorator(cls) {
  4929. if (typeFn)
  4930. typeFn(cls, ...args);
  4931. // Use of Object.defineProperty is important since it creates non-enumerable property which
  4932. // prevents the property is copied during subclassing.
  4933. const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
  4934. cls[ANNOTATIONS] :
  4935. Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS];
  4936. annotations.push(annotationInstance);
  4937. if (additionalProcessing)
  4938. additionalProcessing(cls);
  4939. return cls;
  4940. };
  4941. }
  4942. if (parentClass) {
  4943. DecoratorFactory.prototype = Object.create(parentClass.prototype);
  4944. }
  4945. DecoratorFactory.prototype.ngMetadataName = name;
  4946. DecoratorFactory.annotationCls = DecoratorFactory;
  4947. return DecoratorFactory;
  4948. });
  4949. }
  4950. function makeMetadataCtor(props) {
  4951. return function ctor(...args) {
  4952. if (props) {
  4953. const values = props(...args);
  4954. for (const propName in values) {
  4955. this[propName] = values[propName];
  4956. }
  4957. }
  4958. };
  4959. }
  4960. function makeParamDecorator(name, props, parentClass) {
  4961. return noSideEffects(() => {
  4962. const metaCtor = makeMetadataCtor(props);
  4963. function ParamDecoratorFactory(...args) {
  4964. if (this instanceof ParamDecoratorFactory) {
  4965. metaCtor.apply(this, args);
  4966. return this;
  4967. }
  4968. const annotationInstance = new ParamDecoratorFactory(...args);
  4969. ParamDecorator.annotation = annotationInstance;
  4970. return ParamDecorator;
  4971. function ParamDecorator(cls, unusedKey, index) {
  4972. // Use of Object.defineProperty is important since it creates non-enumerable property which
  4973. // prevents the property is copied during subclassing.
  4974. const parameters = cls.hasOwnProperty(PARAMETERS) ?
  4975. cls[PARAMETERS] :
  4976. Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS];
  4977. // there might be gaps if some in between parameters do not have annotations.
  4978. // we pad with nulls.
  4979. while (parameters.length <= index) {
  4980. parameters.push(null);
  4981. }
  4982. (parameters[index] = parameters[index] || []).push(annotationInstance);
  4983. return cls;
  4984. }
  4985. }
  4986. if (parentClass) {
  4987. ParamDecoratorFactory.prototype = Object.create(parentClass.prototype);
  4988. }
  4989. ParamDecoratorFactory.prototype.ngMetadataName = name;
  4990. ParamDecoratorFactory.annotationCls = ParamDecoratorFactory;
  4991. return ParamDecoratorFactory;
  4992. });
  4993. }
  4994. function makePropDecorator(name, props, parentClass, additionalProcessing) {
  4995. return noSideEffects(() => {
  4996. const metaCtor = makeMetadataCtor(props);
  4997. function PropDecoratorFactory(...args) {
  4998. if (this instanceof PropDecoratorFactory) {
  4999. metaCtor.apply(this, args);
  5000. return this;
  5001. }
  5002. const decoratorInstance = new PropDecoratorFactory(...args);
  5003. function PropDecorator(target, name) {
  5004. // target is undefined with standard decorators. This case is not supported and will throw
  5005. // if this decorator is used in JIT mode with standard decorators.
  5006. if (target === undefined) {
  5007. throw new Error('Standard Angular field decorators are not supported in JIT mode.');
  5008. }
  5009. const constructor = target.constructor;
  5010. // Use of Object.defineProperty is important because it creates a non-enumerable property
  5011. // which prevents the property from being copied during subclassing.
  5012. const meta = constructor.hasOwnProperty(PROP_METADATA) ?
  5013. constructor[PROP_METADATA] :
  5014. Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA];
  5015. meta[name] = meta.hasOwnProperty(name) && meta[name] || [];
  5016. meta[name].unshift(decoratorInstance);
  5017. if (additionalProcessing)
  5018. additionalProcessing(target, name, ...args);
  5019. }
  5020. return PropDecorator;
  5021. }
  5022. if (parentClass) {
  5023. PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
  5024. }
  5025. PropDecoratorFactory.prototype.ngMetadataName = name;
  5026. PropDecoratorFactory.annotationCls = PropDecoratorFactory;
  5027. return PropDecoratorFactory;
  5028. });
  5029. }
  5030. /**
  5031. * Attribute decorator and metadata.
  5032. *
  5033. * @Annotation
  5034. * @publicApi
  5035. */
  5036. const Attribute = makeParamDecorator('Attribute', (attributeName) => ({ attributeName, __NG_ELEMENT_ID__: () => ɵɵinjectAttribute(attributeName) }));
  5037. // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
  5038. // explicitly set.
  5039. const emitDistinctChangesOnlyDefaultValue = true;
  5040. /**
  5041. * Base class for query metadata.
  5042. *
  5043. * @see {@link ContentChildren}
  5044. * @see {@link ContentChild}
  5045. * @see {@link ViewChildren}
  5046. * @see {@link ViewChild}
  5047. *
  5048. * @publicApi
  5049. */
  5050. class Query {
  5051. }
  5052. /**
  5053. * ContentChildren decorator and metadata.
  5054. *
  5055. *
  5056. * @Annotation
  5057. * @publicApi
  5058. */
  5059. const ContentChildren = makePropDecorator('ContentChildren', (selector, data = {}) => ({
  5060. selector,
  5061. first: false,
  5062. isViewQuery: false,
  5063. descendants: false,
  5064. emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue,
  5065. ...data
  5066. }), Query);
  5067. /**
  5068. * ContentChild decorator and metadata.
  5069. *
  5070. *
  5071. * @Annotation
  5072. *
  5073. * @publicApi
  5074. */
  5075. const ContentChild = makePropDecorator('ContentChild', (selector, data = {}) => ({ selector, first: true, isViewQuery: false, descendants: true, ...data }), Query);
  5076. /**
  5077. * ViewChildren decorator and metadata.
  5078. *
  5079. * @Annotation
  5080. * @publicApi
  5081. */
  5082. const ViewChildren = makePropDecorator('ViewChildren', (selector, data = {}) => ({
  5083. selector,
  5084. first: false,
  5085. isViewQuery: true,
  5086. descendants: true,
  5087. emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue,
  5088. ...data
  5089. }), Query);
  5090. /**
  5091. * ViewChild decorator and metadata.
  5092. *
  5093. * @Annotation
  5094. * @publicApi
  5095. */
  5096. const ViewChild = makePropDecorator('ViewChild', (selector, data) => ({ selector, first: true, isViewQuery: true, descendants: true, ...data }), Query);
  5097. var FactoryTarget;
  5098. (function (FactoryTarget) {
  5099. FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive";
  5100. FactoryTarget[FactoryTarget["Component"] = 1] = "Component";
  5101. FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable";
  5102. FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe";
  5103. FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule";
  5104. })(FactoryTarget || (FactoryTarget = {}));
  5105. var R3TemplateDependencyKind;
  5106. (function (R3TemplateDependencyKind) {
  5107. R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive";
  5108. R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe";
  5109. R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule";
  5110. })(R3TemplateDependencyKind || (R3TemplateDependencyKind = {}));
  5111. var ViewEncapsulation;
  5112. (function (ViewEncapsulation) {
  5113. ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
  5114. // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
  5115. ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
  5116. ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
  5117. })(ViewEncapsulation || (ViewEncapsulation = {}));
  5118. function getCompilerFacade(request) {
  5119. const globalNg = _global['ng'];
  5120. if (globalNg && globalNg.ɵcompilerFacade) {
  5121. return globalNg.ɵcompilerFacade;
  5122. }
  5123. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  5124. // Log the type as an error so that a developer can easily navigate to the type from the
  5125. // console.
  5126. console.error(`JIT compilation failed for ${request.kind}`, request.type);
  5127. let message = `The ${request.kind} '${request
  5128. .type.name}' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.\n\n`;
  5129. if (request.usage === 1 /* JitCompilerUsage.PartialDeclaration */) {
  5130. message += `The ${request.kind} is part of a library that has been partially compiled.\n`;
  5131. message +=
  5132. `However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.\n`;
  5133. message += '\n';
  5134. message +=
  5135. `Ideally, the library is processed using the Angular Linker to become fully AOT compiled.\n`;
  5136. }
  5137. else {
  5138. message +=
  5139. `JIT compilation is discouraged for production use-cases! Consider using AOT mode instead.\n`;
  5140. }
  5141. message +=
  5142. `Alternatively, the JIT compiler should be loaded by bootstrapping using '@angular/platform-browser-dynamic' or '@angular/platform-server',\n`;
  5143. message +=
  5144. `or manually provide the compiler with 'import "@angular/compiler";' before bootstrapping.`;
  5145. throw new Error(message);
  5146. }
  5147. else {
  5148. throw new Error('JIT compiler unavailable');
  5149. }
  5150. }
  5151. /**
  5152. * @description
  5153. *
  5154. * Represents a type that a Component or other object is instances of.
  5155. *
  5156. * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by
  5157. * the `MyCustomComponent` constructor function.
  5158. *
  5159. * @publicApi
  5160. */
  5161. const Type = Function;
  5162. function isType(v) {
  5163. return typeof v === 'function';
  5164. }
  5165. /**
  5166. * Determines if the contents of two arrays is identical
  5167. *
  5168. * @param a first array
  5169. * @param b second array
  5170. * @param identityAccessor Optional function for extracting stable object identity from a value in
  5171. * the array.
  5172. */
  5173. function arrayEquals(a, b, identityAccessor) {
  5174. if (a.length !== b.length)
  5175. return false;
  5176. for (let i = 0; i < a.length; i++) {
  5177. let valueA = a[i];
  5178. let valueB = b[i];
  5179. if (identityAccessor) {
  5180. valueA = identityAccessor(valueA);
  5181. valueB = identityAccessor(valueB);
  5182. }
  5183. if (valueB !== valueA) {
  5184. return false;
  5185. }
  5186. }
  5187. return true;
  5188. }
  5189. /**
  5190. * Flattens an array.
  5191. */
  5192. function flatten(list) {
  5193. return list.flat(Number.POSITIVE_INFINITY);
  5194. }
  5195. function deepForEach(input, fn) {
  5196. input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value));
  5197. }
  5198. function addToArray(arr, index, value) {
  5199. // perf: array.push is faster than array.splice!
  5200. if (index >= arr.length) {
  5201. arr.push(value);
  5202. }
  5203. else {
  5204. arr.splice(index, 0, value);
  5205. }
  5206. }
  5207. function removeFromArray(arr, index) {
  5208. // perf: array.pop is faster than array.splice!
  5209. if (index >= arr.length - 1) {
  5210. return arr.pop();
  5211. }
  5212. else {
  5213. return arr.splice(index, 1)[0];
  5214. }
  5215. }
  5216. function newArray(size, value) {
  5217. const list = [];
  5218. for (let i = 0; i < size; i++) {
  5219. list.push(value);
  5220. }
  5221. return list;
  5222. }
  5223. /**
  5224. * Remove item from array (Same as `Array.splice()` but faster.)
  5225. *
  5226. * `Array.splice()` is not as fast because it has to allocate an array for the elements which were
  5227. * removed. This causes memory pressure and slows down code when most of the time we don't
  5228. * care about the deleted items array.
  5229. *
  5230. * https://jsperf.com/fast-array-splice (About 20x faster)
  5231. *
  5232. * @param array Array to splice
  5233. * @param index Index of element in array to remove.
  5234. * @param count Number of items to remove.
  5235. */
  5236. function arraySplice(array, index, count) {
  5237. const length = array.length - count;
  5238. while (index < length) {
  5239. array[index] = array[index + count];
  5240. index++;
  5241. }
  5242. while (count--) {
  5243. array.pop(); // shrink the array
  5244. }
  5245. }
  5246. /**
  5247. * Same as `Array.splice(index, 0, value)` but faster.
  5248. *
  5249. * `Array.splice()` is not fast because it has to allocate an array for the elements which were
  5250. * removed. This causes memory pressure and slows down code when most of the time we don't
  5251. * care about the deleted items array.
  5252. *
  5253. * @param array Array to splice.
  5254. * @param index Index in array where the `value` should be added.
  5255. * @param value Value to add to array.
  5256. */
  5257. function arrayInsert(array, index, value) {
  5258. ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.');
  5259. let end = array.length;
  5260. while (end > index) {
  5261. const previousEnd = end - 1;
  5262. array[end] = array[previousEnd];
  5263. end = previousEnd;
  5264. }
  5265. array[index] = value;
  5266. }
  5267. /**
  5268. * Same as `Array.splice2(index, 0, value1, value2)` but faster.
  5269. *
  5270. * `Array.splice()` is not fast because it has to allocate an array for the elements which were
  5271. * removed. This causes memory pressure and slows down code when most of the time we don't
  5272. * care about the deleted items array.
  5273. *
  5274. * @param array Array to splice.
  5275. * @param index Index in array where the `value` should be added.
  5276. * @param value1 Value to add to array.
  5277. * @param value2 Value to add to array.
  5278. */
  5279. function arrayInsert2(array, index, value1, value2) {
  5280. ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.');
  5281. let end = array.length;
  5282. if (end == index) {
  5283. // inserting at the end.
  5284. array.push(value1, value2);
  5285. }
  5286. else if (end === 1) {
  5287. // corner case when we have less items in array than we have items to insert.
  5288. array.push(value2, array[0]);
  5289. array[0] = value1;
  5290. }
  5291. else {
  5292. end--;
  5293. array.push(array[end - 1], array[end]);
  5294. while (end > index) {
  5295. const previousEnd = end - 2;
  5296. array[end] = array[previousEnd];
  5297. end--;
  5298. }
  5299. array[index] = value1;
  5300. array[index + 1] = value2;
  5301. }
  5302. }
  5303. /**
  5304. * Get an index of an `value` in a sorted `array`.
  5305. *
  5306. * NOTE:
  5307. * - This uses binary search algorithm for fast removals.
  5308. *
  5309. * @param array A sorted array to binary search.
  5310. * @param value The value to look for.
  5311. * @returns index of the value.
  5312. * - positive index if value found.
  5313. * - negative index if value not found. (`~index` to get the value where it should have been
  5314. * located)
  5315. */
  5316. function arrayIndexOfSorted(array, value) {
  5317. return _arrayIndexOfSorted(array, value, 0);
  5318. }
  5319. /**
  5320. * Set a `value` for a `key`.
  5321. *
  5322. * @param keyValueArray to modify.
  5323. * @param key The key to locate or create.
  5324. * @param value The value to set for a `key`.
  5325. * @returns index (always even) of where the value vas set.
  5326. */
  5327. function keyValueArraySet(keyValueArray, key, value) {
  5328. let index = keyValueArrayIndexOf(keyValueArray, key);
  5329. if (index >= 0) {
  5330. // if we found it set it.
  5331. keyValueArray[index | 1] = value;
  5332. }
  5333. else {
  5334. index = ~index;
  5335. arrayInsert2(keyValueArray, index, key, value);
  5336. }
  5337. return index;
  5338. }
  5339. /**
  5340. * Retrieve a `value` for a `key` (on `undefined` if not found.)
  5341. *
  5342. * @param keyValueArray to search.
  5343. * @param key The key to locate.
  5344. * @return The `value` stored at the `key` location or `undefined if not found.
  5345. */
  5346. function keyValueArrayGet(keyValueArray, key) {
  5347. const index = keyValueArrayIndexOf(keyValueArray, key);
  5348. if (index >= 0) {
  5349. // if we found it retrieve it.
  5350. return keyValueArray[index | 1];
  5351. }
  5352. return undefined;
  5353. }
  5354. /**
  5355. * Retrieve a `key` index value in the array or `-1` if not found.
  5356. *
  5357. * @param keyValueArray to search.
  5358. * @param key The key to locate.
  5359. * @returns index of where the key is (or should have been.)
  5360. * - positive (even) index if key found.
  5361. * - negative index if key not found. (`~index` (even) to get the index where it should have
  5362. * been inserted.)
  5363. */
  5364. function keyValueArrayIndexOf(keyValueArray, key) {
  5365. return _arrayIndexOfSorted(keyValueArray, key, 1);
  5366. }
  5367. /**
  5368. * Delete a `key` (and `value`) from the `KeyValueArray`.
  5369. *
  5370. * @param keyValueArray to modify.
  5371. * @param key The key to locate or delete (if exist).
  5372. * @returns index of where the key was (or should have been.)
  5373. * - positive (even) index if key found and deleted.
  5374. * - negative index if key not found. (`~index` (even) to get the index where it should have
  5375. * been.)
  5376. */
  5377. function keyValueArrayDelete(keyValueArray, key) {
  5378. const index = keyValueArrayIndexOf(keyValueArray, key);
  5379. if (index >= 0) {
  5380. // if we found it remove it.
  5381. arraySplice(keyValueArray, index, 2);
  5382. }
  5383. return index;
  5384. }
  5385. /**
  5386. * INTERNAL: Get an index of an `value` in a sorted `array` by grouping search by `shift`.
  5387. *
  5388. * NOTE:
  5389. * - This uses binary search algorithm for fast removals.
  5390. *
  5391. * @param array A sorted array to binary search.
  5392. * @param value The value to look for.
  5393. * @param shift grouping shift.
  5394. * - `0` means look at every location
  5395. * - `1` means only look at every other (even) location (the odd locations are to be ignored as
  5396. * they are values.)
  5397. * @returns index of the value.
  5398. * - positive index if value found.
  5399. * - negative index if value not found. (`~index` to get the value where it should have been
  5400. * inserted)
  5401. */
  5402. function _arrayIndexOfSorted(array, value, shift) {
  5403. ngDevMode && assertEqual(Array.isArray(array), true, 'Expecting an array');
  5404. let start = 0;
  5405. let end = array.length >> shift;
  5406. while (end !== start) {
  5407. const middle = start + ((end - start) >> 1); // find the middle.
  5408. const current = array[middle << shift];
  5409. if (value === current) {
  5410. return (middle << shift);
  5411. }
  5412. else if (current > value) {
  5413. end = middle;
  5414. }
  5415. else {
  5416. start = middle + 1; // We already searched middle so make it non-inclusive by adding 1
  5417. }
  5418. }
  5419. return ~(end << shift);
  5420. }
  5421. /*
  5422. * #########################
  5423. * Attention: These Regular expressions have to hold even if the code is minified!
  5424. * ##########################
  5425. */
  5426. /**
  5427. * Regular expression that detects pass-through constructors for ES5 output. This Regex
  5428. * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also
  5429. * it intends to capture the pattern where existing constructors have been downleveled from
  5430. * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g.
  5431. *
  5432. * ```
  5433. * function MyClass() {
  5434. * var _this = _super.apply(this, arguments) || this;
  5435. * ```
  5436. *
  5437. * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2:
  5438. * ```
  5439. * function MyClass() {
  5440. * var _this = _super.apply(this, __spread(arguments)) || this;
  5441. * ```
  5442. *
  5443. * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2:
  5444. * ```
  5445. * function MyClass() {
  5446. * var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this;
  5447. * ```
  5448. *
  5449. * More details can be found in: https://github.com/angular/angular/issues/38453.
  5450. */
  5451. const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\).*)\)/;
  5452. /** Regular expression that detects ES2015 classes which extend from other classes. */
  5453. const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/;
  5454. /**
  5455. * Regular expression that detects ES2015 classes which extend from other classes and
  5456. * have an explicit constructor defined.
  5457. */
  5458. const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/;
  5459. /**
  5460. * Regular expression that detects ES2015 classes which extend from other classes
  5461. * and inherit a constructor.
  5462. */
  5463. const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/;
  5464. /**
  5465. * Determine whether a stringified type is a class which delegates its constructor
  5466. * to its parent.
  5467. *
  5468. * This is not trivial since compiled code can actually contain a constructor function
  5469. * even if the original source code did not. For instance, when the child class contains
  5470. * an initialized instance property.
  5471. */
  5472. function isDelegateCtor(typeStr) {
  5473. return ES5_DELEGATE_CTOR.test(typeStr) ||
  5474. ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) ||
  5475. (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr));
  5476. }
  5477. class ReflectionCapabilities {
  5478. constructor(reflect) {
  5479. this._reflect = reflect || _global['Reflect'];
  5480. }
  5481. factory(t) {
  5482. return (...args) => new t(...args);
  5483. }
  5484. /** @internal */
  5485. _zipTypesAndAnnotations(paramTypes, paramAnnotations) {
  5486. let result;
  5487. if (typeof paramTypes === 'undefined') {
  5488. result = newArray(paramAnnotations.length);
  5489. }
  5490. else {
  5491. result = newArray(paramTypes.length);
  5492. }
  5493. for (let i = 0; i < result.length; i++) {
  5494. // TS outputs Object for parameters without types, while Traceur omits
  5495. // the annotations. For now we preserve the Traceur behavior to aid
  5496. // migration, but this can be revisited.
  5497. if (typeof paramTypes === 'undefined') {
  5498. result[i] = [];
  5499. }
  5500. else if (paramTypes[i] && paramTypes[i] != Object) {
  5501. result[i] = [paramTypes[i]];
  5502. }
  5503. else {
  5504. result[i] = [];
  5505. }
  5506. if (paramAnnotations && paramAnnotations[i] != null) {
  5507. result[i] = result[i].concat(paramAnnotations[i]);
  5508. }
  5509. }
  5510. return result;
  5511. }
  5512. _ownParameters(type, parentCtor) {
  5513. const typeStr = type.toString();
  5514. // If we have no decorators, we only have function.length as metadata.
  5515. // In that case, to detect whether a child class declared an own constructor or not,
  5516. // we need to look inside of that constructor to check whether it is
  5517. // just calling the parent.
  5518. // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
  5519. // that sets 'design:paramtypes' to []
  5520. // if a class inherits from another class but has no ctor declared itself.
  5521. if (isDelegateCtor(typeStr)) {
  5522. return null;
  5523. }
  5524. // Prefer the direct API.
  5525. if (type.parameters && type.parameters !== parentCtor.parameters) {
  5526. return type.parameters;
  5527. }
  5528. // API of tsickle for lowering decorators to properties on the class.
  5529. const tsickleCtorParams = type.ctorParameters;
  5530. if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) {
  5531. // Newer tsickle uses a function closure
  5532. // Retain the non-function case for compatibility with older tsickle
  5533. const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams;
  5534. const paramTypes = ctorParameters.map((ctorParam) => ctorParam && ctorParam.type);
  5535. const paramAnnotations = ctorParameters.map((ctorParam) => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
  5536. return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
  5537. }
  5538. // API for metadata created by invoking the decorators.
  5539. const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS];
  5540. const paramTypes = this._reflect && this._reflect.getOwnMetadata &&
  5541. this._reflect.getOwnMetadata('design:paramtypes', type);
  5542. if (paramTypes || paramAnnotations) {
  5543. return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
  5544. }
  5545. // If a class has no decorators, at least create metadata
  5546. // based on function.length.
  5547. // Note: We know that this is a real constructor as we checked
  5548. // the content of the constructor above.
  5549. return newArray(type.length);
  5550. }
  5551. parameters(type) {
  5552. // Note: only report metadata if we have at least one class decorator
  5553. // to stay in sync with the static reflector.
  5554. if (!isType(type)) {
  5555. return [];
  5556. }
  5557. const parentCtor = getParentCtor(type);
  5558. let parameters = this._ownParameters(type, parentCtor);
  5559. if (!parameters && parentCtor !== Object) {
  5560. parameters = this.parameters(parentCtor);
  5561. }
  5562. return parameters || [];
  5563. }
  5564. _ownAnnotations(typeOrFunc, parentCtor) {
  5565. // Prefer the direct API.
  5566. if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) {
  5567. let annotations = typeOrFunc.annotations;
  5568. if (typeof annotations === 'function' && annotations.annotations) {
  5569. annotations = annotations.annotations;
  5570. }
  5571. return annotations;
  5572. }
  5573. // API of tsickle for lowering decorators to properties on the class.
  5574. if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) {
  5575. return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators);
  5576. }
  5577. // API for metadata created by invoking the decorators.
  5578. if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
  5579. return typeOrFunc[ANNOTATIONS];
  5580. }
  5581. return null;
  5582. }
  5583. annotations(typeOrFunc) {
  5584. if (!isType(typeOrFunc)) {
  5585. return [];
  5586. }
  5587. const parentCtor = getParentCtor(typeOrFunc);
  5588. const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || [];
  5589. const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : [];
  5590. return parentAnnotations.concat(ownAnnotations);
  5591. }
  5592. _ownPropMetadata(typeOrFunc, parentCtor) {
  5593. // Prefer the direct API.
  5594. if (typeOrFunc.propMetadata &&
  5595. typeOrFunc.propMetadata !== parentCtor.propMetadata) {
  5596. let propMetadata = typeOrFunc.propMetadata;
  5597. if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
  5598. propMetadata = propMetadata.propMetadata;
  5599. }
  5600. return propMetadata;
  5601. }
  5602. // API of tsickle for lowering decorators to properties on the class.
  5603. if (typeOrFunc.propDecorators &&
  5604. typeOrFunc.propDecorators !== parentCtor.propDecorators) {
  5605. const propDecorators = typeOrFunc.propDecorators;
  5606. const propMetadata = {};
  5607. Object.keys(propDecorators).forEach(prop => {
  5608. propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
  5609. });
  5610. return propMetadata;
  5611. }
  5612. // API for metadata created by invoking the decorators.
  5613. if (typeOrFunc.hasOwnProperty(PROP_METADATA)) {
  5614. return typeOrFunc[PROP_METADATA];
  5615. }
  5616. return null;
  5617. }
  5618. propMetadata(typeOrFunc) {
  5619. if (!isType(typeOrFunc)) {
  5620. return {};
  5621. }
  5622. const parentCtor = getParentCtor(typeOrFunc);
  5623. const propMetadata = {};
  5624. if (parentCtor !== Object) {
  5625. const parentPropMetadata = this.propMetadata(parentCtor);
  5626. Object.keys(parentPropMetadata).forEach((propName) => {
  5627. propMetadata[propName] = parentPropMetadata[propName];
  5628. });
  5629. }
  5630. const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor);
  5631. if (ownPropMetadata) {
  5632. Object.keys(ownPropMetadata).forEach((propName) => {
  5633. const decorators = [];
  5634. if (propMetadata.hasOwnProperty(propName)) {
  5635. decorators.push(...propMetadata[propName]);
  5636. }
  5637. decorators.push(...ownPropMetadata[propName]);
  5638. propMetadata[propName] = decorators;
  5639. });
  5640. }
  5641. return propMetadata;
  5642. }
  5643. ownPropMetadata(typeOrFunc) {
  5644. if (!isType(typeOrFunc)) {
  5645. return {};
  5646. }
  5647. return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {};
  5648. }
  5649. hasLifecycleHook(type, lcProperty) {
  5650. return type instanceof Type && lcProperty in type.prototype;
  5651. }
  5652. }
  5653. function convertTsickleDecoratorIntoMetadata(decoratorInvocations) {
  5654. if (!decoratorInvocations) {
  5655. return [];
  5656. }
  5657. return decoratorInvocations.map(decoratorInvocation => {
  5658. const decoratorType = decoratorInvocation.type;
  5659. const annotationCls = decoratorType.annotationCls;
  5660. const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
  5661. return new annotationCls(...annotationArgs);
  5662. });
  5663. }
  5664. function getParentCtor(ctor) {
  5665. const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null;
  5666. const parentCtor = parentProto ? parentProto.constructor : null;
  5667. // Note: We always use `Object` as the null value
  5668. // to simplify checking later on.
  5669. return parentCtor || Object;
  5670. }
  5671. /**
  5672. * Inject decorator and metadata.
  5673. *
  5674. * @Annotation
  5675. * @publicApi
  5676. */
  5677. const Inject = attachInjectFlag(
  5678. // Disable tslint because `DecoratorFlags` is a const enum which gets inlined.
  5679. // tslint:disable-next-line: no-toplevel-property-access
  5680. makeParamDecorator('Inject', (token) => ({ token })), -1 /* DecoratorFlags.Inject */);
  5681. /**
  5682. * Optional decorator and metadata.
  5683. *
  5684. * @Annotation
  5685. * @publicApi
  5686. */
  5687. const Optional =
  5688. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  5689. // tslint:disable-next-line: no-toplevel-property-access
  5690. attachInjectFlag(makeParamDecorator('Optional'), 8 /* InternalInjectFlags.Optional */);
  5691. /**
  5692. * Self decorator and metadata.
  5693. *
  5694. * @Annotation
  5695. * @publicApi
  5696. */
  5697. const Self =
  5698. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  5699. // tslint:disable-next-line: no-toplevel-property-access
  5700. attachInjectFlag(makeParamDecorator('Self'), 2 /* InternalInjectFlags.Self */);
  5701. /**
  5702. * `SkipSelf` decorator and metadata.
  5703. *
  5704. * @Annotation
  5705. * @publicApi
  5706. */
  5707. const SkipSelf =
  5708. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  5709. // tslint:disable-next-line: no-toplevel-property-access
  5710. attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* InternalInjectFlags.SkipSelf */);
  5711. /**
  5712. * Host decorator and metadata.
  5713. *
  5714. * @Annotation
  5715. * @publicApi
  5716. */
  5717. const Host =
  5718. // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
  5719. // tslint:disable-next-line: no-toplevel-property-access
  5720. attachInjectFlag(makeParamDecorator('Host'), 1 /* InternalInjectFlags.Host */);
  5721. let _reflect = null;
  5722. function getReflect() {
  5723. return (_reflect = _reflect || new ReflectionCapabilities());
  5724. }
  5725. function reflectDependencies(type) {
  5726. return convertDependencies(getReflect().parameters(type));
  5727. }
  5728. function convertDependencies(deps) {
  5729. return deps.map(dep => reflectDependency(dep));
  5730. }
  5731. function reflectDependency(dep) {
  5732. const meta = {
  5733. token: null,
  5734. attribute: null,
  5735. host: false,
  5736. optional: false,
  5737. self: false,
  5738. skipSelf: false,
  5739. };
  5740. if (Array.isArray(dep) && dep.length > 0) {
  5741. for (let j = 0; j < dep.length; j++) {
  5742. const param = dep[j];
  5743. if (param === undefined) {
  5744. // param may be undefined if type of dep is not set by ngtsc
  5745. continue;
  5746. }
  5747. const proto = Object.getPrototypeOf(param);
  5748. if (param instanceof Optional || proto.ngMetadataName === 'Optional') {
  5749. meta.optional = true;
  5750. }
  5751. else if (param instanceof SkipSelf || proto.ngMetadataName === 'SkipSelf') {
  5752. meta.skipSelf = true;
  5753. }
  5754. else if (param instanceof Self || proto.ngMetadataName === 'Self') {
  5755. meta.self = true;
  5756. }
  5757. else if (param instanceof Host || proto.ngMetadataName === 'Host') {
  5758. meta.host = true;
  5759. }
  5760. else if (param instanceof Inject) {
  5761. meta.token = param.token;
  5762. }
  5763. else if (param instanceof Attribute) {
  5764. if (param.attributeName === undefined) {
  5765. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Attribute name must be defined.`);
  5766. }
  5767. meta.attribute = param.attributeName;
  5768. }
  5769. else {
  5770. meta.token = param;
  5771. }
  5772. }
  5773. }
  5774. else if (dep === undefined || (Array.isArray(dep) && dep.length === 0)) {
  5775. meta.token = null;
  5776. }
  5777. else {
  5778. meta.token = dep;
  5779. }
  5780. return meta;
  5781. }
  5782. /**
  5783. * Used to resolve resource URLs on `@Component` when used with JIT compilation.
  5784. *
  5785. * Example:
  5786. * ```
  5787. * @Component({
  5788. * selector: 'my-comp',
  5789. * templateUrl: 'my-comp.html', // This requires asynchronous resolution
  5790. * })
  5791. * class MyComponent{
  5792. * }
  5793. *
  5794. * // Calling `renderComponent` will fail because `renderComponent` is a synchronous process
  5795. * // and `MyComponent`'s `@Component.templateUrl` needs to be resolved asynchronously.
  5796. *
  5797. * // Calling `resolveComponentResources()` will resolve `@Component.templateUrl` into
  5798. * // `@Component.template`, which allows `renderComponent` to proceed in a synchronous manner.
  5799. *
  5800. * // Use browser's `fetch()` function as the default resource resolution strategy.
  5801. * resolveComponentResources(fetch).then(() => {
  5802. * // After resolution all URLs have been converted into `template` strings.
  5803. * renderComponent(MyComponent);
  5804. * });
  5805. *
  5806. * ```
  5807. *
  5808. * NOTE: In AOT the resolution happens during compilation, and so there should be no need
  5809. * to call this method outside JIT mode.
  5810. *
  5811. * @param resourceResolver a function which is responsible for returning a `Promise` to the
  5812. * contents of the resolved URL. Browser's `fetch()` method is a good default implementation.
  5813. */
  5814. function resolveComponentResources(resourceResolver) {
  5815. // Store all promises which are fetching the resources.
  5816. const componentResolved = [];
  5817. // Cache so that we don't fetch the same resource more than once.
  5818. const urlMap = new Map();
  5819. function cachedResourceResolve(url) {
  5820. let promise = urlMap.get(url);
  5821. if (!promise) {
  5822. const resp = resourceResolver(url);
  5823. urlMap.set(url, promise = resp.then(unwrapResponse));
  5824. }
  5825. return promise;
  5826. }
  5827. componentResourceResolutionQueue.forEach((component, type) => {
  5828. const promises = [];
  5829. if (component.templateUrl) {
  5830. promises.push(cachedResourceResolve(component.templateUrl).then((template) => {
  5831. component.template = template;
  5832. }));
  5833. }
  5834. const styleUrls = component.styleUrls;
  5835. const styles = component.styles || (component.styles = []);
  5836. const styleOffset = component.styles.length;
  5837. styleUrls && styleUrls.forEach((styleUrl, index) => {
  5838. styles.push(''); // pre-allocate array.
  5839. promises.push(cachedResourceResolve(styleUrl).then((style) => {
  5840. styles[styleOffset + index] = style;
  5841. styleUrls.splice(styleUrls.indexOf(styleUrl), 1);
  5842. if (styleUrls.length == 0) {
  5843. component.styleUrls = undefined;
  5844. }
  5845. }));
  5846. });
  5847. const fullyResolved = Promise.all(promises).then(() => componentDefResolved(type));
  5848. componentResolved.push(fullyResolved);
  5849. });
  5850. clearResolutionOfComponentResourcesQueue();
  5851. return Promise.all(componentResolved).then(() => undefined);
  5852. }
  5853. let componentResourceResolutionQueue = new Map();
  5854. // Track when existing ɵcmp for a Type is waiting on resources.
  5855. const componentDefPendingResolution = new Set();
  5856. function maybeQueueResolutionOfComponentResources(type, metadata) {
  5857. if (componentNeedsResolution(metadata)) {
  5858. componentResourceResolutionQueue.set(type, metadata);
  5859. componentDefPendingResolution.add(type);
  5860. }
  5861. }
  5862. function isComponentDefPendingResolution(type) {
  5863. return componentDefPendingResolution.has(type);
  5864. }
  5865. function componentNeedsResolution(component) {
  5866. return !!((component.templateUrl && !component.hasOwnProperty('template')) ||
  5867. component.styleUrls && component.styleUrls.length);
  5868. }
  5869. function clearResolutionOfComponentResourcesQueue() {
  5870. const old = componentResourceResolutionQueue;
  5871. componentResourceResolutionQueue = new Map();
  5872. return old;
  5873. }
  5874. function restoreComponentResolutionQueue(queue) {
  5875. componentDefPendingResolution.clear();
  5876. queue.forEach((_, type) => componentDefPendingResolution.add(type));
  5877. componentResourceResolutionQueue = queue;
  5878. }
  5879. function isComponentResourceResolutionQueueEmpty() {
  5880. return componentResourceResolutionQueue.size === 0;
  5881. }
  5882. function unwrapResponse(response) {
  5883. return typeof response == 'string' ? response : response.text();
  5884. }
  5885. function componentDefResolved(type) {
  5886. componentDefPendingResolution.delete(type);
  5887. }
  5888. /**
  5889. * Map of module-id to the corresponding NgModule.
  5890. */
  5891. const modules = new Map();
  5892. /**
  5893. * Whether to check for duplicate NgModule registrations.
  5894. *
  5895. * This can be disabled for testing.
  5896. */
  5897. let checkForDuplicateNgModules = true;
  5898. function assertSameOrNotExisting(id, type, incoming) {
  5899. if (type && type !== incoming && checkForDuplicateNgModules) {
  5900. throw new Error(`Duplicate module registered for ${id} - ${stringify(type)} vs ${stringify(type.name)}`);
  5901. }
  5902. }
  5903. /**
  5904. * Adds the given NgModule type to Angular's NgModule registry.
  5905. *
  5906. * This is generated as a side-effect of NgModule compilation. Note that the `id` is passed in
  5907. * explicitly and not read from the NgModule definition. This is for two reasons: it avoids a
  5908. * megamorphic read, and in JIT there's a chicken-and-egg problem where the NgModule may not be
  5909. * fully resolved when it's registered.
  5910. *
  5911. * @codeGenApi
  5912. */
  5913. function registerNgModuleType(ngModuleType, id) {
  5914. const existing = modules.get(id) || null;
  5915. assertSameOrNotExisting(id, existing, ngModuleType);
  5916. modules.set(id, ngModuleType);
  5917. }
  5918. function clearModulesForTest() {
  5919. modules.clear();
  5920. }
  5921. function getRegisteredNgModuleType(id) {
  5922. return modules.get(id);
  5923. }
  5924. /**
  5925. * Control whether the NgModule registration system enforces that each NgModule type registered has
  5926. * a unique id.
  5927. *
  5928. * This is useful for testing as the NgModule registry cannot be properly reset between tests with
  5929. * Angular's current API.
  5930. */
  5931. function setAllowDuplicateNgModuleIdsForTest(allowDuplicates) {
  5932. checkForDuplicateNgModules = !allowDuplicates;
  5933. }
  5934. /**
  5935. * Defines a schema that allows an NgModule to contain the following:
  5936. * - Non-Angular elements named with dash case (`-`).
  5937. * - Element properties named with dash case (`-`).
  5938. * Dash case is the naming convention for custom elements.
  5939. *
  5940. * @publicApi
  5941. */
  5942. const CUSTOM_ELEMENTS_SCHEMA = {
  5943. name: 'custom-elements'
  5944. };
  5945. /**
  5946. * Defines a schema that allows any property on any element.
  5947. *
  5948. * This schema allows you to ignore the errors related to any unknown elements or properties in a
  5949. * template. The usage of this schema is generally discouraged because it prevents useful validation
  5950. * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
  5951. *
  5952. * @publicApi
  5953. */
  5954. const NO_ERRORS_SCHEMA = {
  5955. name: 'no-errors-schema'
  5956. };
  5957. let shouldThrowErrorOnUnknownElement = false;
  5958. /**
  5959. * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
  5960. * instead of just logging the error.
  5961. * (for AOT-compiled ones this check happens at build time).
  5962. */
  5963. function ɵsetUnknownElementStrictMode(shouldThrow) {
  5964. shouldThrowErrorOnUnknownElement = shouldThrow;
  5965. }
  5966. /**
  5967. * Gets the current value of the strict mode.
  5968. */
  5969. function ɵgetUnknownElementStrictMode() {
  5970. return shouldThrowErrorOnUnknownElement;
  5971. }
  5972. let shouldThrowErrorOnUnknownProperty = false;
  5973. /**
  5974. * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
  5975. * instead of just logging the error.
  5976. * (for AOT-compiled ones this check happens at build time).
  5977. */
  5978. function ɵsetUnknownPropertyStrictMode(shouldThrow) {
  5979. shouldThrowErrorOnUnknownProperty = shouldThrow;
  5980. }
  5981. /**
  5982. * Gets the current value of the strict mode.
  5983. */
  5984. function ɵgetUnknownPropertyStrictMode() {
  5985. return shouldThrowErrorOnUnknownProperty;
  5986. }
  5987. /**
  5988. * Validates that the element is known at runtime and produces
  5989. * an error if it's not the case.
  5990. * This check is relevant for JIT-compiled components (for AOT-compiled
  5991. * ones this check happens at build time).
  5992. *
  5993. * The element is considered known if either:
  5994. * - it's a known HTML element
  5995. * - it's a known custom element
  5996. * - the element matches any directive
  5997. * - the element is allowed by one of the schemas
  5998. *
  5999. * @param element Element to validate
  6000. * @param lView An `LView` that represents a current component that is being rendered
  6001. * @param tagName Name of the tag to check
  6002. * @param schemas Array of schemas
  6003. * @param hasDirectives Boolean indicating that the element matches any directive
  6004. */
  6005. function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
  6006. // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
  6007. // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
  6008. // defined as an array (as an empty array in case `schemas` field is not defined) and we should
  6009. // execute the check below.
  6010. if (schemas === null)
  6011. return;
  6012. // If the element matches any directive, it's considered as valid.
  6013. if (!hasDirectives && tagName !== null) {
  6014. // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
  6015. // as a custom element. Note that unknown elements with a dash in their name won't be instances
  6016. // of HTMLUnknownElement in browsers that support web components.
  6017. const isUnknown =
  6018. // Note that we can't check for `typeof HTMLUnknownElement === 'function'` because
  6019. // Domino doesn't expose HTMLUnknownElement globally.
  6020. (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
  6021. element instanceof HTMLUnknownElement) ||
  6022. (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
  6023. !customElements.get(tagName));
  6024. if (isUnknown && !matchingSchemas(schemas, tagName)) {
  6025. const isHostStandalone = isHostComponentStandalone(lView);
  6026. const templateLocation = getTemplateLocationDetails(lView);
  6027. const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
  6028. let message = `'${tagName}' is not a known element${templateLocation}:\n`;
  6029. message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
  6030. 'a part of an @NgModule where this component is declared'}.\n`;
  6031. if (tagName && tagName.indexOf('-') > -1) {
  6032. message +=
  6033. `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
  6034. }
  6035. else {
  6036. message +=
  6037. `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
  6038. }
  6039. if (shouldThrowErrorOnUnknownElement) {
  6040. throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
  6041. }
  6042. else {
  6043. console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
  6044. }
  6045. }
  6046. }
  6047. }
  6048. /**
  6049. * Validates that the property of the element is known at runtime and returns
  6050. * false if it's not the case.
  6051. * This check is relevant for JIT-compiled components (for AOT-compiled
  6052. * ones this check happens at build time).
  6053. *
  6054. * The property is considered known if either:
  6055. * - it's a known property of the element
  6056. * - the element is allowed by one of the schemas
  6057. * - the property is used for animations
  6058. *
  6059. * @param element Element to validate
  6060. * @param propName Name of the property to check
  6061. * @param tagName Name of the tag hosting the property
  6062. * @param schemas Array of schemas
  6063. */
  6064. function isPropertyValid(element, propName, tagName, schemas) {
  6065. // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
  6066. // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
  6067. // defined as an array (as an empty array in case `schemas` field is not defined) and we should
  6068. // execute the check below.
  6069. if (schemas === null)
  6070. return true;
  6071. // The property is considered valid if the element matches the schema, it exists on the element,
  6072. // or it is synthetic.
  6073. if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
  6074. return true;
  6075. }
  6076. // Note: `typeof Node` returns 'function' in most browsers, but is undefined with domino.
  6077. return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
  6078. }
  6079. /**
  6080. * Logs or throws an error that a property is not supported on an element.
  6081. *
  6082. * @param propName Name of the invalid property
  6083. * @param tagName Name of the tag hosting the property
  6084. * @param nodeType Type of the node hosting the property
  6085. * @param lView An `LView` that represents a current component
  6086. */
  6087. function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
  6088. // Special-case a situation when a structural directive is applied to
  6089. // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
  6090. // In this case the compiler generates the `ɵɵtemplate` instruction with
  6091. // the `null` as the tagName. The directive matching logic at runtime relies
  6092. // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
  6093. // a default value of the `tNode.value` is not feasible at this moment.
  6094. if (!tagName && nodeType === 4 /* TNodeType.Container */) {
  6095. tagName = 'ng-template';
  6096. }
  6097. const isHostStandalone = isHostComponentStandalone(lView);
  6098. const templateLocation = getTemplateLocationDetails(lView);
  6099. let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
  6100. const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
  6101. const importLocation = isHostStandalone ?
  6102. 'included in the \'@Component.imports\' of this component' :
  6103. 'a part of an @NgModule where this component is declared';
  6104. if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
  6105. // Most likely this is a control flow directive (such as `*ngIf`) used in
  6106. // a template, but the directive or the `CommonModule` is not imported.
  6107. const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
  6108. message += `\nIf the '${propName}' is an Angular control flow directive, ` +
  6109. `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
  6110. }
  6111. else {
  6112. // May be an Angular component, which is not imported/declared?
  6113. message += `\n1. If '${tagName}' is an Angular component and it has the ` +
  6114. `'${propName}' input, then verify that it is ${importLocation}.`;
  6115. // May be a Web Component?
  6116. if (tagName && tagName.indexOf('-') > -1) {
  6117. message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
  6118. `to the ${schemas} of this component to suppress this message.`;
  6119. message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
  6120. `the ${schemas} of this component.`;
  6121. }
  6122. else {
  6123. // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
  6124. message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
  6125. `the ${schemas} of this component.`;
  6126. }
  6127. }
  6128. reportUnknownPropertyError(message);
  6129. }
  6130. function reportUnknownPropertyError(message) {
  6131. if (shouldThrowErrorOnUnknownProperty) {
  6132. throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
  6133. }
  6134. else {
  6135. console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
  6136. }
  6137. }
  6138. /**
  6139. * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
  6140. * and must **not** be used in production bundles. The function makes megamorphic reads, which might
  6141. * be too slow for production mode and also it relies on the constructor function being available.
  6142. *
  6143. * Gets a reference to the host component def (where a current component is declared).
  6144. *
  6145. * @param lView An `LView` that represents a current component that is being rendered.
  6146. */
  6147. function getDeclarationComponentDef(lView) {
  6148. !ngDevMode && throwError('Must never be called in production mode');
  6149. const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
  6150. const context = declarationLView[CONTEXT];
  6151. // Unable to obtain a context.
  6152. if (!context)
  6153. return null;
  6154. return context.constructor ? getComponentDef(context.constructor) : null;
  6155. }
  6156. /**
  6157. * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
  6158. * and must **not** be used in production bundles. The function makes megamorphic reads, which might
  6159. * be too slow for production mode.
  6160. *
  6161. * Checks if the current component is declared inside of a standalone component template.
  6162. *
  6163. * @param lView An `LView` that represents a current component that is being rendered.
  6164. */
  6165. function isHostComponentStandalone(lView) {
  6166. !ngDevMode && throwError('Must never be called in production mode');
  6167. const componentDef = getDeclarationComponentDef(lView);
  6168. // Treat host component as non-standalone if we can't obtain the def.
  6169. return !!componentDef?.standalone;
  6170. }
  6171. /**
  6172. * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
  6173. * and must **not** be used in production bundles. The function makes megamorphic reads, which might
  6174. * be too slow for production mode.
  6175. *
  6176. * Constructs a string describing the location of the host component template. The function is used
  6177. * in dev mode to produce error messages.
  6178. *
  6179. * @param lView An `LView` that represents a current component that is being rendered.
  6180. */
  6181. function getTemplateLocationDetails(lView) {
  6182. !ngDevMode && throwError('Must never be called in production mode');
  6183. const hostComponentDef = getDeclarationComponentDef(lView);
  6184. const componentClassName = hostComponentDef?.type?.name;
  6185. return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
  6186. }
  6187. /**
  6188. * The set of known control flow directives and their corresponding imports.
  6189. * We use this set to produce a more precises error message with a note
  6190. * that the `CommonModule` should also be included.
  6191. */
  6192. const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
  6193. ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
  6194. ['ngSwitchDefault', 'NgSwitchDefault']
  6195. ]);
  6196. /**
  6197. * Returns true if the tag name is allowed by specified schemas.
  6198. * @param schemas Array of schemas
  6199. * @param tagName Name of the tag
  6200. */
  6201. function matchingSchemas(schemas, tagName) {
  6202. if (schemas !== null) {
  6203. for (let i = 0; i < schemas.length; i++) {
  6204. const schema = schemas[i];
  6205. if (schema === NO_ERRORS_SCHEMA ||
  6206. schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
  6207. return true;
  6208. }
  6209. }
  6210. }
  6211. return false;
  6212. }
  6213. /**
  6214. * The name of an attribute that can be added to the hydration boundary node
  6215. * (component host node) to disable hydration for the content within that boundary.
  6216. */
  6217. const SKIP_HYDRATION_ATTR_NAME = 'ngSkipHydration';
  6218. /**
  6219. * Helper function to check if a given TNode has the 'ngSkipHydration' attribute.
  6220. */
  6221. function hasSkipHydrationAttrOnTNode(tNode) {
  6222. const SKIP_HYDRATION_ATTR_NAME_LOWER_CASE = SKIP_HYDRATION_ATTR_NAME.toLowerCase();
  6223. const attrs = tNode.mergedAttrs;
  6224. if (attrs === null)
  6225. return false;
  6226. // only ever look at the attribute name and skip the values
  6227. for (let i = 0; i < attrs.length; i += 2) {
  6228. const value = attrs[i];
  6229. // This is a marker, which means that the static attributes section is over,
  6230. // so we can exit early.
  6231. if (typeof value === 'number')
  6232. return false;
  6233. if (typeof value === 'string' && value.toLowerCase() === SKIP_HYDRATION_ATTR_NAME_LOWER_CASE) {
  6234. return true;
  6235. }
  6236. }
  6237. return false;
  6238. }
  6239. /**
  6240. * Helper function to check if a given RElement has the 'ngSkipHydration' attribute.
  6241. */
  6242. function hasSkipHydrationAttrOnRElement(rNode) {
  6243. return rNode.hasAttribute(SKIP_HYDRATION_ATTR_NAME);
  6244. }
  6245. /**
  6246. * Checks whether a TNode has a flag to indicate that it's a part of
  6247. * a skip hydration block.
  6248. */
  6249. function hasInSkipHydrationBlockFlag(tNode) {
  6250. return (tNode.flags & 128 /* TNodeFlags.inSkipHydrationBlock */) === 128 /* TNodeFlags.inSkipHydrationBlock */;
  6251. }
  6252. /**
  6253. * Helper function that determines if a given node is within a skip hydration block
  6254. * by navigating up the TNode tree to see if any parent nodes have skip hydration
  6255. * attribute.
  6256. *
  6257. * TODO(akushnir): this function should contain the logic of `hasInSkipHydrationBlockFlag`,
  6258. * there is no need to traverse parent nodes when we have a TNode flag (which would also
  6259. * make this lookup O(1)).
  6260. */
  6261. function isInSkipHydrationBlock(tNode) {
  6262. let currentTNode = tNode.parent;
  6263. while (currentTNode) {
  6264. if (hasSkipHydrationAttrOnTNode(currentTNode)) {
  6265. return true;
  6266. }
  6267. currentTNode = currentTNode.parent;
  6268. }
  6269. return false;
  6270. }
  6271. /**
  6272. * Flags for renderer-specific style modifiers.
  6273. * @publicApi
  6274. */
  6275. var RendererStyleFlags2;
  6276. (function (RendererStyleFlags2) {
  6277. // TODO(misko): This needs to be refactored into a separate file so that it can be imported from
  6278. // `node_manipulation.ts` Currently doing the import cause resolution order to change and fails
  6279. // the tests. The work around is to have hard coded value in `node_manipulation.ts` for now.
  6280. /**
  6281. * Marks a style as important.
  6282. */
  6283. RendererStyleFlags2[RendererStyleFlags2["Important"] = 1] = "Important";
  6284. /**
  6285. * Marks a style as using dash case naming (this-is-dash-case).
  6286. */
  6287. RendererStyleFlags2[RendererStyleFlags2["DashCase"] = 2] = "DashCase";
  6288. })(RendererStyleFlags2 || (RendererStyleFlags2 = {}));
  6289. /**
  6290. * Disallowed strings in the comment.
  6291. *
  6292. * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
  6293. */
  6294. const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
  6295. /**
  6296. * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
  6297. */
  6298. const COMMENT_DELIMITER = /(<|>)/g;
  6299. const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
  6300. /**
  6301. * Escape the content of comment strings so that it can be safely inserted into a comment node.
  6302. *
  6303. * The issue is that HTML does not specify any way to escape comment end text inside the comment.
  6304. * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
  6305. * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
  6306. * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
  6307. *
  6308. * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
  6309. *
  6310. * ```
  6311. * div.innerHTML = div.innerHTML
  6312. * ```
  6313. *
  6314. * One would expect that the above code would be safe to do, but it turns out that because comment
  6315. * text is not escaped, the comment may contain text which will prematurely close the comment
  6316. * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
  6317. * may contain such text and expect them to be safe.)
  6318. *
  6319. * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
  6320. * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
  6321. * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
  6322. * text it will render normally but it will not cause the HTML parser to close/open the comment.
  6323. *
  6324. * @param value text to make safe for comment node by escaping the comment open/close character
  6325. * sequence.
  6326. */
  6327. function escapeCommentText(value) {
  6328. return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
  6329. }
  6330. // Keeps track of the currently-active LViews.
  6331. const TRACKED_LVIEWS = new Map();
  6332. // Used for generating unique IDs for LViews.
  6333. let uniqueIdCounter = 0;
  6334. /** Gets a unique ID that can be assigned to an LView. */
  6335. function getUniqueLViewId() {
  6336. return uniqueIdCounter++;
  6337. }
  6338. /** Starts tracking an LView. */
  6339. function registerLView(lView) {
  6340. ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
  6341. TRACKED_LVIEWS.set(lView[ID], lView);
  6342. }
  6343. /** Gets an LView by its unique ID. */
  6344. function getLViewById(id) {
  6345. ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
  6346. return TRACKED_LVIEWS.get(id) || null;
  6347. }
  6348. /** Stops tracking an LView. */
  6349. function unregisterLView(lView) {
  6350. ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
  6351. TRACKED_LVIEWS.delete(lView[ID]);
  6352. }
  6353. /**
  6354. * The internal view context which is specific to a given DOM element, directive or
  6355. * component instance. Each value in here (besides the LView and element node details)
  6356. * can be present, null or undefined. If undefined then it implies the value has not been
  6357. * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
  6358. *
  6359. * Each value will get filled when the respective value is examined within the getContext
  6360. * function. The component, element and each directive instance will share the same instance
  6361. * of the context.
  6362. */
  6363. class LContext {
  6364. /** Component's parent view data. */
  6365. get lView() {
  6366. return getLViewById(this.lViewId);
  6367. }
  6368. constructor(
  6369. /**
  6370. * ID of the component's parent view data.
  6371. */
  6372. lViewId,
  6373. /**
  6374. * The index instance of the node.
  6375. */
  6376. nodeIndex,
  6377. /**
  6378. * The instance of the DOM node that is attached to the lNode.
  6379. */
  6380. native) {
  6381. this.lViewId = lViewId;
  6382. this.nodeIndex = nodeIndex;
  6383. this.native = native;
  6384. }
  6385. }
  6386. /**
  6387. * Returns the matching `LContext` data for a given DOM node, directive or component instance.
  6388. *
  6389. * This function will examine the provided DOM element, component, or directive instance\'s
  6390. * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
  6391. * value will be that of the newly created `LContext`.
  6392. *
  6393. * If the monkey-patched value is the `LView` instance then the context value for that
  6394. * target will be created and the monkey-patch reference will be updated. Therefore when this
  6395. * function is called it may mutate the provided element\'s, component\'s or any of the associated
  6396. * directive\'s monkey-patch values.
  6397. *
  6398. * If the monkey-patch value is not detected then the code will walk up the DOM until an element
  6399. * is found which contains a monkey-patch reference. When that occurs then the provided element
  6400. * will be updated with a new context (which is then returned). If the monkey-patch value is not
  6401. * detected for a component/directive instance then it will throw an error (all components and
  6402. * directives should be automatically monkey-patched by ivy).
  6403. *
  6404. * @param target Component, Directive or DOM Node.
  6405. */
  6406. function getLContext(target) {
  6407. let mpValue = readPatchedData(target);
  6408. if (mpValue) {
  6409. // only when it's an array is it considered an LView instance
  6410. // ... otherwise it's an already constructed LContext instance
  6411. if (isLView(mpValue)) {
  6412. const lView = mpValue;
  6413. let nodeIndex;
  6414. let component = undefined;
  6415. let directives = undefined;
  6416. if (isComponentInstance(target)) {
  6417. nodeIndex = findViaComponent(lView, target);
  6418. if (nodeIndex == -1) {
  6419. throw new Error('The provided component was not found in the application');
  6420. }
  6421. component = target;
  6422. }
  6423. else if (isDirectiveInstance(target)) {
  6424. nodeIndex = findViaDirective(lView, target);
  6425. if (nodeIndex == -1) {
  6426. throw new Error('The provided directive was not found in the application');
  6427. }
  6428. directives = getDirectivesAtNodeIndex(nodeIndex, lView);
  6429. }
  6430. else {
  6431. nodeIndex = findViaNativeElement(lView, target);
  6432. if (nodeIndex == -1) {
  6433. return null;
  6434. }
  6435. }
  6436. // the goal is not to fill the entire context full of data because the lookups
  6437. // are expensive. Instead, only the target data (the element, component, container, ICU
  6438. // expression or directive details) are filled into the context. If called multiple times
  6439. // with different target values then the missing target data will be filled in.
  6440. const native = unwrapRNode(lView[nodeIndex]);
  6441. const existingCtx = readPatchedData(native);
  6442. const context = (existingCtx && !Array.isArray(existingCtx)) ?
  6443. existingCtx :
  6444. createLContext(lView, nodeIndex, native);
  6445. // only when the component has been discovered then update the monkey-patch
  6446. if (component && context.component === undefined) {
  6447. context.component = component;
  6448. attachPatchData(context.component, context);
  6449. }
  6450. // only when the directives have been discovered then update the monkey-patch
  6451. if (directives && context.directives === undefined) {
  6452. context.directives = directives;
  6453. for (let i = 0; i < directives.length; i++) {
  6454. attachPatchData(directives[i], context);
  6455. }
  6456. }
  6457. attachPatchData(context.native, context);
  6458. mpValue = context;
  6459. }
  6460. }
  6461. else {
  6462. const rElement = target;
  6463. ngDevMode && assertDomNode(rElement);
  6464. // if the context is not found then we need to traverse upwards up the DOM
  6465. // to find the nearest element that has already been monkey patched with data
  6466. let parent = rElement;
  6467. while (parent = parent.parentNode) {
  6468. const parentContext = readPatchedData(parent);
  6469. if (parentContext) {
  6470. const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
  6471. // the edge of the app was also reached here through another means
  6472. // (maybe because the DOM was changed manually).
  6473. if (!lView) {
  6474. return null;
  6475. }
  6476. const index = findViaNativeElement(lView, rElement);
  6477. if (index >= 0) {
  6478. const native = unwrapRNode(lView[index]);
  6479. const context = createLContext(lView, index, native);
  6480. attachPatchData(native, context);
  6481. mpValue = context;
  6482. break;
  6483. }
  6484. }
  6485. }
  6486. }
  6487. return mpValue || null;
  6488. }
  6489. /**
  6490. * Creates an empty instance of a `LContext` context
  6491. */
  6492. function createLContext(lView, nodeIndex, native) {
  6493. return new LContext(lView[ID], nodeIndex, native);
  6494. }
  6495. /**
  6496. * Takes a component instance and returns the view for that component.
  6497. *
  6498. * @param componentInstance
  6499. * @returns The component's view
  6500. */
  6501. function getComponentViewByInstance(componentInstance) {
  6502. let patchedData = readPatchedData(componentInstance);
  6503. let lView;
  6504. if (isLView(patchedData)) {
  6505. const contextLView = patchedData;
  6506. const nodeIndex = findViaComponent(contextLView, componentInstance);
  6507. lView = getComponentLViewByIndex(nodeIndex, contextLView);
  6508. const context = createLContext(contextLView, nodeIndex, lView[HOST]);
  6509. context.component = componentInstance;
  6510. attachPatchData(componentInstance, context);
  6511. attachPatchData(context.native, context);
  6512. }
  6513. else {
  6514. const context = patchedData;
  6515. const contextLView = context.lView;
  6516. ngDevMode && assertLView(contextLView);
  6517. lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
  6518. }
  6519. return lView;
  6520. }
  6521. /**
  6522. * This property will be monkey-patched on elements, components and directives.
  6523. */
  6524. const MONKEY_PATCH_KEY_NAME = '__ngContext__';
  6525. /**
  6526. * Assigns the given data to the given target (which could be a component,
  6527. * directive or DOM node instance) using monkey-patching.
  6528. */
  6529. function attachPatchData(target, data) {
  6530. ngDevMode && assertDefined(target, 'Target expected');
  6531. // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
  6532. // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
  6533. // we can't know when to remove an `LContext`.
  6534. if (isLView(data)) {
  6535. target[MONKEY_PATCH_KEY_NAME] = data[ID];
  6536. registerLView(data);
  6537. }
  6538. else {
  6539. target[MONKEY_PATCH_KEY_NAME] = data;
  6540. }
  6541. }
  6542. /**
  6543. * Returns the monkey-patch value data present on the target (which could be
  6544. * a component, directive or a DOM node).
  6545. */
  6546. function readPatchedData(target) {
  6547. ngDevMode && assertDefined(target, 'Target expected');
  6548. const data = target[MONKEY_PATCH_KEY_NAME];
  6549. return (typeof data === 'number') ? getLViewById(data) : data || null;
  6550. }
  6551. function readPatchedLView(target) {
  6552. const value = readPatchedData(target);
  6553. if (value) {
  6554. return (isLView(value) ? value : value.lView);
  6555. }
  6556. return null;
  6557. }
  6558. function isComponentInstance(instance) {
  6559. return instance && instance.constructor && instance.constructor.ɵcmp;
  6560. }
  6561. function isDirectiveInstance(instance) {
  6562. return instance && instance.constructor && instance.constructor.ɵdir;
  6563. }
  6564. /**
  6565. * Locates the element within the given LView and returns the matching index
  6566. */
  6567. function findViaNativeElement(lView, target) {
  6568. const tView = lView[TVIEW];
  6569. for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
  6570. if (unwrapRNode(lView[i]) === target) {
  6571. return i;
  6572. }
  6573. }
  6574. return -1;
  6575. }
  6576. /**
  6577. * Locates the next tNode (child, sibling or parent).
  6578. */
  6579. function traverseNextElement(tNode) {
  6580. if (tNode.child) {
  6581. return tNode.child;
  6582. }
  6583. else if (tNode.next) {
  6584. return tNode.next;
  6585. }
  6586. else {
  6587. // Let's take the following template: <div><span>text</span></div><component/>
  6588. // After checking the text node, we need to find the next parent that has a "next" TNode,
  6589. // in this case the parent `div`, so that we can find the component.
  6590. while (tNode.parent && !tNode.parent.next) {
  6591. tNode = tNode.parent;
  6592. }
  6593. return tNode.parent && tNode.parent.next;
  6594. }
  6595. }
  6596. /**
  6597. * Locates the component within the given LView and returns the matching index
  6598. */
  6599. function findViaComponent(lView, componentInstance) {
  6600. const componentIndices = lView[TVIEW].components;
  6601. if (componentIndices) {
  6602. for (let i = 0; i < componentIndices.length; i++) {
  6603. const elementComponentIndex = componentIndices[i];
  6604. const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
  6605. if (componentView[CONTEXT] === componentInstance) {
  6606. return elementComponentIndex;
  6607. }
  6608. }
  6609. }
  6610. else {
  6611. const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
  6612. const rootComponent = rootComponentView[CONTEXT];
  6613. if (rootComponent === componentInstance) {
  6614. // we are dealing with the root element here therefore we know that the
  6615. // element is the very first element after the HEADER data in the lView
  6616. return HEADER_OFFSET;
  6617. }
  6618. }
  6619. return -1;
  6620. }
  6621. /**
  6622. * Locates the directive within the given LView and returns the matching index
  6623. */
  6624. function findViaDirective(lView, directiveInstance) {
  6625. // if a directive is monkey patched then it will (by default)
  6626. // have a reference to the LView of the current view. The
  6627. // element bound to the directive being search lives somewhere
  6628. // in the view data. We loop through the nodes and check their
  6629. // list of directives for the instance.
  6630. let tNode = lView[TVIEW].firstChild;
  6631. while (tNode) {
  6632. const directiveIndexStart = tNode.directiveStart;
  6633. const directiveIndexEnd = tNode.directiveEnd;
  6634. for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
  6635. if (lView[i] === directiveInstance) {
  6636. return tNode.index;
  6637. }
  6638. }
  6639. tNode = traverseNextElement(tNode);
  6640. }
  6641. return -1;
  6642. }
  6643. /**
  6644. * Returns a list of directives applied to a node at a specific index. The list includes
  6645. * directives matched by selector and any host directives, but it excludes components.
  6646. * Use `getComponentAtNodeIndex` to find the component applied to a node.
  6647. *
  6648. * @param nodeIndex The node index
  6649. * @param lView The target view data
  6650. */
  6651. function getDirectivesAtNodeIndex(nodeIndex, lView) {
  6652. const tNode = lView[TVIEW].data[nodeIndex];
  6653. if (tNode.directiveStart === 0)
  6654. return EMPTY_ARRAY;
  6655. const results = [];
  6656. for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
  6657. const directiveInstance = lView[i];
  6658. if (!isComponentInstance(directiveInstance)) {
  6659. results.push(directiveInstance);
  6660. }
  6661. }
  6662. return results;
  6663. }
  6664. function getComponentAtNodeIndex(nodeIndex, lView) {
  6665. const tNode = lView[TVIEW].data[nodeIndex];
  6666. const { directiveStart, componentOffset } = tNode;
  6667. return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
  6668. }
  6669. /**
  6670. * Returns a map of local references (local reference name => element or directive instance) that
  6671. * exist on a given element.
  6672. */
  6673. function discoverLocalRefs(lView, nodeIndex) {
  6674. const tNode = lView[TVIEW].data[nodeIndex];
  6675. if (tNode && tNode.localNames) {
  6676. const result = {};
  6677. let localIndex = tNode.index + 1;
  6678. for (let i = 0; i < tNode.localNames.length; i += 2) {
  6679. result[tNode.localNames[i]] = lView[localIndex];
  6680. localIndex++;
  6681. }
  6682. return result;
  6683. }
  6684. return null;
  6685. }
  6686. let _icuContainerIterate;
  6687. /**
  6688. * Iterator which provides ability to visit all of the `TIcuContainerNode` root `RNode`s.
  6689. */
  6690. function icuContainerIterate(tIcuContainerNode, lView) {
  6691. return _icuContainerIterate(tIcuContainerNode, lView);
  6692. }
  6693. /**
  6694. * Ensures that `IcuContainerVisitor`'s implementation is present.
  6695. *
  6696. * This function is invoked when i18n instruction comes across an ICU. The purpose is to allow the
  6697. * bundler to tree shake ICU logic and only load it if ICU instruction is executed.
  6698. */
  6699. function ensureIcuContainerVisitorLoaded(loader) {
  6700. if (_icuContainerIterate === undefined) {
  6701. // Do not inline this function. We want to keep `ensureIcuContainerVisitorLoaded` light, so it
  6702. // can be inlined into call-site.
  6703. _icuContainerIterate = loader();
  6704. }
  6705. }
  6706. /**
  6707. * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of
  6708. * that LContainer, which is an LView
  6709. * @param lView the lView whose parent to get
  6710. */
  6711. function getLViewParent(lView) {
  6712. ngDevMode && assertLView(lView);
  6713. const parent = lView[PARENT];
  6714. return isLContainer(parent) ? parent[PARENT] : parent;
  6715. }
  6716. /**
  6717. * Retrieve the root view from any component or `LView` by walking the parent `LView` until
  6718. * reaching the root `LView`.
  6719. *
  6720. * @param componentOrLView any component or `LView`
  6721. */
  6722. function getRootView(componentOrLView) {
  6723. ngDevMode && assertDefined(componentOrLView, 'component');
  6724. let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView);
  6725. while (lView && !(lView[FLAGS] & 512 /* LViewFlags.IsRoot */)) {
  6726. lView = getLViewParent(lView);
  6727. }
  6728. ngDevMode && assertLView(lView);
  6729. return lView;
  6730. }
  6731. /**
  6732. * Returns the context information associated with the application where the target is situated. It
  6733. * does this by walking the parent views until it gets to the root view, then getting the context
  6734. * off of that.
  6735. *
  6736. * @param viewOrComponent the `LView` or component to get the root context for.
  6737. */
  6738. function getRootContext(viewOrComponent) {
  6739. const rootView = getRootView(viewOrComponent);
  6740. ngDevMode &&
  6741. assertDefined(rootView[CONTEXT], 'Root view has no context. Perhaps it is disconnected?');
  6742. return rootView[CONTEXT];
  6743. }
  6744. /**
  6745. * Gets the first `LContainer` in the LView or `null` if none exists.
  6746. */
  6747. function getFirstLContainer(lView) {
  6748. return getNearestLContainer(lView[CHILD_HEAD]);
  6749. }
  6750. /**
  6751. * Gets the next `LContainer` that is a sibling of the given container.
  6752. */
  6753. function getNextLContainer(container) {
  6754. return getNearestLContainer(container[NEXT]);
  6755. }
  6756. function getNearestLContainer(viewOrContainer) {
  6757. while (viewOrContainer !== null && !isLContainer(viewOrContainer)) {
  6758. viewOrContainer = viewOrContainer[NEXT];
  6759. }
  6760. return viewOrContainer;
  6761. }
  6762. /**
  6763. * NOTE: for performance reasons, the possible actions are inlined within the function instead of
  6764. * being passed as an argument.
  6765. */
  6766. function applyToElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) {
  6767. // If this slot was allocated for a text node dynamically created by i18n, the text node itself
  6768. // won't be created until i18nApply() in the update block, so this node should be skipped.
  6769. // For more info, see "ICU expressions should work inside an ngTemplateOutlet inside an ngFor"
  6770. // in `i18n_spec.ts`.
  6771. if (lNodeToHandle != null) {
  6772. let lContainer;
  6773. let isComponent = false;
  6774. // We are expecting an RNode, but in the case of a component or LContainer the `RNode` is
  6775. // wrapped in an array which needs to be unwrapped. We need to know if it is a component and if
  6776. // it has LContainer so that we can process all of those cases appropriately.
  6777. if (isLContainer(lNodeToHandle)) {
  6778. lContainer = lNodeToHandle;
  6779. }
  6780. else if (isLView(lNodeToHandle)) {
  6781. isComponent = true;
  6782. ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView');
  6783. lNodeToHandle = lNodeToHandle[HOST];
  6784. }
  6785. const rNode = unwrapRNode(lNodeToHandle);
  6786. if (action === 0 /* WalkTNodeTreeAction.Create */ && parent !== null) {
  6787. if (beforeNode == null) {
  6788. nativeAppendChild(renderer, parent, rNode);
  6789. }
  6790. else {
  6791. nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
  6792. }
  6793. }
  6794. else if (action === 1 /* WalkTNodeTreeAction.Insert */ && parent !== null) {
  6795. nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
  6796. }
  6797. else if (action === 2 /* WalkTNodeTreeAction.Detach */) {
  6798. nativeRemoveNode(renderer, rNode, isComponent);
  6799. }
  6800. else if (action === 3 /* WalkTNodeTreeAction.Destroy */) {
  6801. ngDevMode && ngDevMode.rendererDestroyNode++;
  6802. renderer.destroyNode(rNode);
  6803. }
  6804. if (lContainer != null) {
  6805. applyContainer(renderer, action, lContainer, parent, beforeNode);
  6806. }
  6807. }
  6808. }
  6809. function createTextNode(renderer, value) {
  6810. ngDevMode && ngDevMode.rendererCreateTextNode++;
  6811. ngDevMode && ngDevMode.rendererSetText++;
  6812. return renderer.createText(value);
  6813. }
  6814. function updateTextNode(renderer, rNode, value) {
  6815. ngDevMode && ngDevMode.rendererSetText++;
  6816. renderer.setValue(rNode, value);
  6817. }
  6818. function createCommentNode(renderer, value) {
  6819. ngDevMode && ngDevMode.rendererCreateComment++;
  6820. return renderer.createComment(escapeCommentText(value));
  6821. }
  6822. /**
  6823. * Creates a native element from a tag name, using a renderer.
  6824. * @param renderer A renderer to use
  6825. * @param name the tag name
  6826. * @param namespace Optional namespace for element.
  6827. * @returns the element created
  6828. */
  6829. function createElementNode(renderer, name, namespace) {
  6830. ngDevMode && ngDevMode.rendererCreateElement++;
  6831. return renderer.createElement(name, namespace);
  6832. }
  6833. /**
  6834. * Removes all DOM elements associated with a view.
  6835. *
  6836. * Because some root nodes of the view may be containers, we sometimes need
  6837. * to propagate deeply into the nested containers to remove all elements in the
  6838. * views beneath it.
  6839. *
  6840. * @param tView The `TView' of the `LView` from which elements should be added or removed
  6841. * @param lView The view from which elements should be added or removed
  6842. */
  6843. function removeViewFromDOM(tView, lView) {
  6844. const renderer = lView[RENDERER];
  6845. applyView(tView, lView, renderer, 2 /* WalkTNodeTreeAction.Detach */, null, null);
  6846. lView[HOST] = null;
  6847. lView[T_HOST] = null;
  6848. }
  6849. /**
  6850. * Adds all DOM elements associated with a view.
  6851. *
  6852. * Because some root nodes of the view may be containers, we sometimes need
  6853. * to propagate deeply into the nested containers to add all elements in the
  6854. * views beneath it.
  6855. *
  6856. * @param tView The `TView' of the `LView` from which elements should be added or removed
  6857. * @param parentTNode The `TNode` where the `LView` should be attached to.
  6858. * @param renderer Current renderer to use for DOM manipulations.
  6859. * @param lView The view from which elements should be added or removed
  6860. * @param parentNativeNode The parent `RElement` where it should be inserted into.
  6861. * @param beforeNode The node before which elements should be added, if insert mode
  6862. */
  6863. function addViewToDOM(tView, parentTNode, renderer, lView, parentNativeNode, beforeNode) {
  6864. lView[HOST] = parentNativeNode;
  6865. lView[T_HOST] = parentTNode;
  6866. applyView(tView, lView, renderer, 1 /* WalkTNodeTreeAction.Insert */, parentNativeNode, beforeNode);
  6867. }
  6868. /**
  6869. * Detach a `LView` from the DOM by detaching its nodes.
  6870. *
  6871. * @param tView The `TView' of the `LView` to be detached
  6872. * @param lView the `LView` to be detached.
  6873. */
  6874. function detachViewFromDOM(tView, lView) {
  6875. applyView(tView, lView, lView[RENDERER], 2 /* WalkTNodeTreeAction.Detach */, null, null);
  6876. }
  6877. /**
  6878. * Traverses down and up the tree of views and containers to remove listeners and
  6879. * call onDestroy callbacks.
  6880. *
  6881. * Notes:
  6882. * - Because it's used for onDestroy calls, it needs to be bottom-up.
  6883. * - Must process containers instead of their views to avoid splicing
  6884. * when views are destroyed and re-added.
  6885. * - Using a while loop because it's faster than recursion
  6886. * - Destroy only called on movement to sibling or movement to parent (laterally or up)
  6887. *
  6888. * @param rootView The view to destroy
  6889. */
  6890. function destroyViewTree(rootView) {
  6891. // If the view has no children, we can clean it up and return early.
  6892. let lViewOrLContainer = rootView[CHILD_HEAD];
  6893. if (!lViewOrLContainer) {
  6894. return cleanUpView(rootView[TVIEW], rootView);
  6895. }
  6896. while (lViewOrLContainer) {
  6897. let next = null;
  6898. if (isLView(lViewOrLContainer)) {
  6899. // If LView, traverse down to child.
  6900. next = lViewOrLContainer[CHILD_HEAD];
  6901. }
  6902. else {
  6903. ngDevMode && assertLContainer(lViewOrLContainer);
  6904. // If container, traverse down to its first LView.
  6905. const firstView = lViewOrLContainer[CONTAINER_HEADER_OFFSET];
  6906. if (firstView)
  6907. next = firstView;
  6908. }
  6909. if (!next) {
  6910. // Only clean up view when moving to the side or up, as destroy hooks
  6911. // should be called in order from the bottom up.
  6912. while (lViewOrLContainer && !lViewOrLContainer[NEXT] && lViewOrLContainer !== rootView) {
  6913. if (isLView(lViewOrLContainer)) {
  6914. cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
  6915. }
  6916. lViewOrLContainer = lViewOrLContainer[PARENT];
  6917. }
  6918. if (lViewOrLContainer === null)
  6919. lViewOrLContainer = rootView;
  6920. if (isLView(lViewOrLContainer)) {
  6921. cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
  6922. }
  6923. next = lViewOrLContainer && lViewOrLContainer[NEXT];
  6924. }
  6925. lViewOrLContainer = next;
  6926. }
  6927. }
  6928. /**
  6929. * Inserts a view into a container.
  6930. *
  6931. * This adds the view to the container's array of active views in the correct
  6932. * position. It also adds the view's elements to the DOM if the container isn't a
  6933. * root node of another view (in that case, the view's elements will be added when
  6934. * the container's parent view is added later).
  6935. *
  6936. * @param tView The `TView' of the `LView` to insert
  6937. * @param lView The view to insert
  6938. * @param lContainer The container into which the view should be inserted
  6939. * @param index Which index in the container to insert the child view into
  6940. */
  6941. function insertView(tView, lView, lContainer, index) {
  6942. ngDevMode && assertLView(lView);
  6943. ngDevMode && assertLContainer(lContainer);
  6944. const indexInContainer = CONTAINER_HEADER_OFFSET + index;
  6945. const containerLength = lContainer.length;
  6946. if (index > 0) {
  6947. // This is a new view, we need to add it to the children.
  6948. lContainer[indexInContainer - 1][NEXT] = lView;
  6949. }
  6950. if (index < containerLength - CONTAINER_HEADER_OFFSET) {
  6951. lView[NEXT] = lContainer[indexInContainer];
  6952. addToArray(lContainer, CONTAINER_HEADER_OFFSET + index, lView);
  6953. }
  6954. else {
  6955. lContainer.push(lView);
  6956. lView[NEXT] = null;
  6957. }
  6958. lView[PARENT] = lContainer;
  6959. // track views where declaration and insertion points are different
  6960. const declarationLContainer = lView[DECLARATION_LCONTAINER];
  6961. if (declarationLContainer !== null && lContainer !== declarationLContainer) {
  6962. trackMovedView(declarationLContainer, lView);
  6963. }
  6964. // notify query that a new view has been added
  6965. const lQueries = lView[QUERIES];
  6966. if (lQueries !== null) {
  6967. lQueries.insertView(tView);
  6968. }
  6969. // Sets the attached flag
  6970. lView[FLAGS] |= 128 /* LViewFlags.Attached */;
  6971. }
  6972. /**
  6973. * Track views created from the declaration container (TemplateRef) and inserted into a
  6974. * different LContainer.
  6975. */
  6976. function trackMovedView(declarationContainer, lView) {
  6977. ngDevMode && assertDefined(lView, 'LView required');
  6978. ngDevMode && assertLContainer(declarationContainer);
  6979. const movedViews = declarationContainer[MOVED_VIEWS];
  6980. const insertedLContainer = lView[PARENT];
  6981. ngDevMode && assertLContainer(insertedLContainer);
  6982. const insertedComponentLView = insertedLContainer[PARENT][DECLARATION_COMPONENT_VIEW];
  6983. ngDevMode && assertDefined(insertedComponentLView, 'Missing insertedComponentLView');
  6984. const declaredComponentLView = lView[DECLARATION_COMPONENT_VIEW];
  6985. ngDevMode && assertDefined(declaredComponentLView, 'Missing declaredComponentLView');
  6986. if (declaredComponentLView !== insertedComponentLView) {
  6987. // At this point the declaration-component is not same as insertion-component; this means that
  6988. // this is a transplanted view. Mark the declared lView as having transplanted views so that
  6989. // those views can participate in CD.
  6990. declarationContainer[HAS_TRANSPLANTED_VIEWS] = true;
  6991. }
  6992. if (movedViews === null) {
  6993. declarationContainer[MOVED_VIEWS] = [lView];
  6994. }
  6995. else {
  6996. movedViews.push(lView);
  6997. }
  6998. }
  6999. function detachMovedView(declarationContainer, lView) {
  7000. ngDevMode && assertLContainer(declarationContainer);
  7001. ngDevMode &&
  7002. assertDefined(declarationContainer[MOVED_VIEWS], 'A projected view should belong to a non-empty projected views collection');
  7003. const movedViews = declarationContainer[MOVED_VIEWS];
  7004. const declarationViewIndex = movedViews.indexOf(lView);
  7005. const insertionLContainer = lView[PARENT];
  7006. ngDevMode && assertLContainer(insertionLContainer);
  7007. // If the view was marked for refresh but then detached before it was checked (where the flag
  7008. // would be cleared and the counter decremented), we need to update the status here.
  7009. clearViewRefreshFlag(lView);
  7010. movedViews.splice(declarationViewIndex, 1);
  7011. }
  7012. /**
  7013. * Detaches a view from a container.
  7014. *
  7015. * This method removes the view from the container's array of active views. It also
  7016. * removes the view's elements from the DOM.
  7017. *
  7018. * @param lContainer The container from which to detach a view
  7019. * @param removeIndex The index of the view to detach
  7020. * @returns Detached LView instance.
  7021. */
  7022. function detachView(lContainer, removeIndex) {
  7023. if (lContainer.length <= CONTAINER_HEADER_OFFSET)
  7024. return;
  7025. const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex;
  7026. const viewToDetach = lContainer[indexInContainer];
  7027. if (viewToDetach) {
  7028. const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER];
  7029. if (declarationLContainer !== null && declarationLContainer !== lContainer) {
  7030. detachMovedView(declarationLContainer, viewToDetach);
  7031. }
  7032. if (removeIndex > 0) {
  7033. lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT];
  7034. }
  7035. const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex);
  7036. removeViewFromDOM(viewToDetach[TVIEW], viewToDetach);
  7037. // notify query that a view has been removed
  7038. const lQueries = removedLView[QUERIES];
  7039. if (lQueries !== null) {
  7040. lQueries.detachView(removedLView[TVIEW]);
  7041. }
  7042. viewToDetach[PARENT] = null;
  7043. viewToDetach[NEXT] = null;
  7044. // Unsets the attached flag
  7045. viewToDetach[FLAGS] &= ~128 /* LViewFlags.Attached */;
  7046. }
  7047. return viewToDetach;
  7048. }
  7049. /**
  7050. * A standalone function which destroys an LView,
  7051. * conducting clean up (e.g. removing listeners, calling onDestroys).
  7052. *
  7053. * @param tView The `TView' of the `LView` to be destroyed
  7054. * @param lView The view to be destroyed.
  7055. */
  7056. function destroyLView(tView, lView) {
  7057. if (!(lView[FLAGS] & 256 /* LViewFlags.Destroyed */)) {
  7058. const renderer = lView[RENDERER];
  7059. lView[REACTIVE_TEMPLATE_CONSUMER] && consumerDestroy(lView[REACTIVE_TEMPLATE_CONSUMER]);
  7060. lView[REACTIVE_HOST_BINDING_CONSUMER] && consumerDestroy(lView[REACTIVE_HOST_BINDING_CONSUMER]);
  7061. if (renderer.destroyNode) {
  7062. applyView(tView, lView, renderer, 3 /* WalkTNodeTreeAction.Destroy */, null, null);
  7063. }
  7064. destroyViewTree(lView);
  7065. }
  7066. }
  7067. /**
  7068. * Calls onDestroys hooks for all directives and pipes in a given view and then removes all
  7069. * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks
  7070. * can be propagated to @Output listeners.
  7071. *
  7072. * @param tView `TView` for the `LView` to clean up.
  7073. * @param lView The LView to clean up
  7074. */
  7075. function cleanUpView(tView, lView) {
  7076. if (!(lView[FLAGS] & 256 /* LViewFlags.Destroyed */)) {
  7077. // Usually the Attached flag is removed when the view is detached from its parent, however
  7078. // if it's a root view, the flag won't be unset hence why we're also removing on destroy.
  7079. lView[FLAGS] &= ~128 /* LViewFlags.Attached */;
  7080. // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook
  7081. // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If
  7082. // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop.
  7083. // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is
  7084. // really more of an "afterDestroy" hook if you think about it.
  7085. lView[FLAGS] |= 256 /* LViewFlags.Destroyed */;
  7086. executeOnDestroys(tView, lView);
  7087. processCleanups(tView, lView);
  7088. // For component views only, the local renderer is destroyed at clean up time.
  7089. if (lView[TVIEW].type === 1 /* TViewType.Component */) {
  7090. ngDevMode && ngDevMode.rendererDestroy++;
  7091. lView[RENDERER].destroy();
  7092. }
  7093. const declarationContainer = lView[DECLARATION_LCONTAINER];
  7094. // we are dealing with an embedded view that is still inserted into a container
  7095. if (declarationContainer !== null && isLContainer(lView[PARENT])) {
  7096. // and this is a projected view
  7097. if (declarationContainer !== lView[PARENT]) {
  7098. detachMovedView(declarationContainer, lView);
  7099. }
  7100. // For embedded views still attached to a container: remove query result from this view.
  7101. const lQueries = lView[QUERIES];
  7102. if (lQueries !== null) {
  7103. lQueries.detachView(tView);
  7104. }
  7105. }
  7106. // Unregister the view once everything else has been cleaned up.
  7107. unregisterLView(lView);
  7108. }
  7109. }
  7110. /** Removes listeners and unsubscribes from output subscriptions */
  7111. function processCleanups(tView, lView) {
  7112. const tCleanup = tView.cleanup;
  7113. const lCleanup = lView[CLEANUP];
  7114. if (tCleanup !== null) {
  7115. for (let i = 0; i < tCleanup.length - 1; i += 2) {
  7116. if (typeof tCleanup[i] === 'string') {
  7117. // This is a native DOM listener. It will occupy 4 entries in the TCleanup array (hence i +=
  7118. // 2 at the end of this block).
  7119. const targetIdx = tCleanup[i + 3];
  7120. ngDevMode && assertNumber(targetIdx, 'cleanup target must be a number');
  7121. if (targetIdx >= 0) {
  7122. // unregister
  7123. lCleanup[targetIdx]();
  7124. }
  7125. else {
  7126. // Subscription
  7127. lCleanup[-targetIdx].unsubscribe();
  7128. }
  7129. i += 2;
  7130. }
  7131. else {
  7132. // This is a cleanup function that is grouped with the index of its context
  7133. const context = lCleanup[tCleanup[i + 1]];
  7134. tCleanup[i].call(context);
  7135. }
  7136. }
  7137. }
  7138. if (lCleanup !== null) {
  7139. lView[CLEANUP] = null;
  7140. }
  7141. const destroyHooks = lView[ON_DESTROY_HOOKS];
  7142. if (destroyHooks !== null) {
  7143. // Reset the ON_DESTROY_HOOKS array before iterating over it to prevent hooks that unregister
  7144. // themselves from mutating the array during iteration.
  7145. lView[ON_DESTROY_HOOKS] = null;
  7146. for (let i = 0; i < destroyHooks.length; i++) {
  7147. const destroyHooksFn = destroyHooks[i];
  7148. ngDevMode && assertFunction(destroyHooksFn, 'Expecting destroy hook to be a function.');
  7149. destroyHooksFn();
  7150. }
  7151. }
  7152. }
  7153. /** Calls onDestroy hooks for this view */
  7154. function executeOnDestroys(tView, lView) {
  7155. let destroyHooks;
  7156. if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
  7157. for (let i = 0; i < destroyHooks.length; i += 2) {
  7158. const context = lView[destroyHooks[i]];
  7159. // Only call the destroy hook if the context has been requested.
  7160. if (!(context instanceof NodeInjectorFactory)) {
  7161. const toCall = destroyHooks[i + 1];
  7162. if (Array.isArray(toCall)) {
  7163. for (let j = 0; j < toCall.length; j += 2) {
  7164. const callContext = context[toCall[j]];
  7165. const hook = toCall[j + 1];
  7166. profiler(4 /* ProfilerEvent.LifecycleHookStart */, callContext, hook);
  7167. try {
  7168. hook.call(callContext);
  7169. }
  7170. finally {
  7171. profiler(5 /* ProfilerEvent.LifecycleHookEnd */, callContext, hook);
  7172. }
  7173. }
  7174. }
  7175. else {
  7176. profiler(4 /* ProfilerEvent.LifecycleHookStart */, context, toCall);
  7177. try {
  7178. toCall.call(context);
  7179. }
  7180. finally {
  7181. profiler(5 /* ProfilerEvent.LifecycleHookEnd */, context, toCall);
  7182. }
  7183. }
  7184. }
  7185. }
  7186. }
  7187. }
  7188. /**
  7189. * Returns a native element if a node can be inserted into the given parent.
  7190. *
  7191. * There are two reasons why we may not be able to insert a element immediately.
  7192. * - Projection: When creating a child content element of a component, we have to skip the
  7193. * insertion because the content of a component will be projected.
  7194. * `<component><content>delayed due to projection</content></component>`
  7195. * - Parent container is disconnected: This can happen when we are inserting a view into
  7196. * parent container, which itself is disconnected. For example the parent container is part
  7197. * of a View which has not be inserted or is made for projection but has not been inserted
  7198. * into destination.
  7199. *
  7200. * @param tView: Current `TView`.
  7201. * @param tNode: `TNode` for which we wish to retrieve render parent.
  7202. * @param lView: Current `LView`.
  7203. */
  7204. function getParentRElement(tView, tNode, lView) {
  7205. return getClosestRElement(tView, tNode.parent, lView);
  7206. }
  7207. /**
  7208. * Get closest `RElement` or `null` if it can't be found.
  7209. *
  7210. * If `TNode` is `TNodeType.Element` => return `RElement` at `LView[tNode.index]` location.
  7211. * If `TNode` is `TNodeType.ElementContainer|IcuContain` => return the parent (recursively).
  7212. * If `TNode` is `null` then return host `RElement`:
  7213. * - return `null` if projection
  7214. * - return `null` if parent container is disconnected (we have no parent.)
  7215. *
  7216. * @param tView: Current `TView`.
  7217. * @param tNode: `TNode` for which we wish to retrieve `RElement` (or `null` if host element is
  7218. * needed).
  7219. * @param lView: Current `LView`.
  7220. * @returns `null` if the `RElement` can't be determined at this time (no parent / projection)
  7221. */
  7222. function getClosestRElement(tView, tNode, lView) {
  7223. let parentTNode = tNode;
  7224. // Skip over element and ICU containers as those are represented by a comment node and
  7225. // can't be used as a render parent.
  7226. while (parentTNode !== null &&
  7227. (parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */))) {
  7228. tNode = parentTNode;
  7229. parentTNode = tNode.parent;
  7230. }
  7231. // If the parent tNode is null, then we are inserting across views: either into an embedded view
  7232. // or a component view.
  7233. if (parentTNode === null) {
  7234. // We are inserting a root element of the component view into the component host element and
  7235. // it should always be eager.
  7236. return lView[HOST];
  7237. }
  7238. else {
  7239. ngDevMode && assertTNodeType(parentTNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */);
  7240. const { componentOffset } = parentTNode;
  7241. if (componentOffset > -1) {
  7242. ngDevMode && assertTNodeForLView(parentTNode, lView);
  7243. const { encapsulation } = tView.data[parentTNode.directiveStart + componentOffset];
  7244. // We've got a parent which is an element in the current view. We just need to verify if the
  7245. // parent element is not a component. Component's content nodes are not inserted immediately
  7246. // because they will be projected, and so doing insert at this point would be wasteful.
  7247. // Since the projection would then move it to its final destination. Note that we can't
  7248. // make this assumption when using the Shadow DOM, because the native projection placeholders
  7249. // (<content> or <slot>) have to be in place as elements are being inserted.
  7250. if (encapsulation === ViewEncapsulation$1.None ||
  7251. encapsulation === ViewEncapsulation$1.Emulated) {
  7252. return null;
  7253. }
  7254. }
  7255. return getNativeByTNode(parentTNode, lView);
  7256. }
  7257. }
  7258. /**
  7259. * Inserts a native node before another native node for a given parent.
  7260. * This is a utility function that can be used when native nodes were determined.
  7261. */
  7262. function nativeInsertBefore(renderer, parent, child, beforeNode, isMove) {
  7263. ngDevMode && ngDevMode.rendererInsertBefore++;
  7264. renderer.insertBefore(parent, child, beforeNode, isMove);
  7265. }
  7266. function nativeAppendChild(renderer, parent, child) {
  7267. ngDevMode && ngDevMode.rendererAppendChild++;
  7268. ngDevMode && assertDefined(parent, 'parent node must be defined');
  7269. renderer.appendChild(parent, child);
  7270. }
  7271. function nativeAppendOrInsertBefore(renderer, parent, child, beforeNode, isMove) {
  7272. if (beforeNode !== null) {
  7273. nativeInsertBefore(renderer, parent, child, beforeNode, isMove);
  7274. }
  7275. else {
  7276. nativeAppendChild(renderer, parent, child);
  7277. }
  7278. }
  7279. /** Removes a node from the DOM given its native parent. */
  7280. function nativeRemoveChild(renderer, parent, child, isHostElement) {
  7281. renderer.removeChild(parent, child, isHostElement);
  7282. }
  7283. /** Checks if an element is a `<template>` node. */
  7284. function isTemplateNode(node) {
  7285. return node.tagName === 'TEMPLATE' && node.content !== undefined;
  7286. }
  7287. /**
  7288. * Returns a native parent of a given native node.
  7289. */
  7290. function nativeParentNode(renderer, node) {
  7291. return renderer.parentNode(node);
  7292. }
  7293. /**
  7294. * Returns a native sibling of a given native node.
  7295. */
  7296. function nativeNextSibling(renderer, node) {
  7297. return renderer.nextSibling(node);
  7298. }
  7299. /**
  7300. * Find a node in front of which `currentTNode` should be inserted.
  7301. *
  7302. * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
  7303. * takes `TNode.insertBeforeIndex` into account if i18n code has been invoked.
  7304. *
  7305. * @param parentTNode parent `TNode`
  7306. * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
  7307. * @param lView current `LView`
  7308. */
  7309. function getInsertInFrontOfRNode(parentTNode, currentTNode, lView) {
  7310. return _getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView);
  7311. }
  7312. /**
  7313. * Find a node in front of which `currentTNode` should be inserted. (Does not take i18n into
  7314. * account)
  7315. *
  7316. * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
  7317. * does not take `TNode.insertBeforeIndex` into account.
  7318. *
  7319. * @param parentTNode parent `TNode`
  7320. * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
  7321. * @param lView current `LView`
  7322. */
  7323. function getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView) {
  7324. if (parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */)) {
  7325. return getNativeByTNode(parentTNode, lView);
  7326. }
  7327. return null;
  7328. }
  7329. /**
  7330. * Tree shakable boundary for `getInsertInFrontOfRNodeWithI18n` function.
  7331. *
  7332. * This function will only be set if i18n code runs.
  7333. */
  7334. let _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithNoI18n;
  7335. /**
  7336. * Tree shakable boundary for `processI18nInsertBefore` function.
  7337. *
  7338. * This function will only be set if i18n code runs.
  7339. */
  7340. let _processI18nInsertBefore;
  7341. function setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore) {
  7342. _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithI18n;
  7343. _processI18nInsertBefore = processI18nInsertBefore;
  7344. }
  7345. /**
  7346. * Appends the `child` native node (or a collection of nodes) to the `parent`.
  7347. *
  7348. * @param tView The `TView' to be appended
  7349. * @param lView The current LView
  7350. * @param childRNode The native child (or children) that should be appended
  7351. * @param childTNode The TNode of the child element
  7352. */
  7353. function appendChild(tView, lView, childRNode, childTNode) {
  7354. const parentRNode = getParentRElement(tView, childTNode, lView);
  7355. const renderer = lView[RENDERER];
  7356. const parentTNode = childTNode.parent || lView[T_HOST];
  7357. const anchorNode = getInsertInFrontOfRNode(parentTNode, childTNode, lView);
  7358. if (parentRNode != null) {
  7359. if (Array.isArray(childRNode)) {
  7360. for (let i = 0; i < childRNode.length; i++) {
  7361. nativeAppendOrInsertBefore(renderer, parentRNode, childRNode[i], anchorNode, false);
  7362. }
  7363. }
  7364. else {
  7365. nativeAppendOrInsertBefore(renderer, parentRNode, childRNode, anchorNode, false);
  7366. }
  7367. }
  7368. _processI18nInsertBefore !== undefined &&
  7369. _processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRNode);
  7370. }
  7371. /**
  7372. * Returns the first native node for a given LView, starting from the provided TNode.
  7373. *
  7374. * Native nodes are returned in the order in which those appear in the native tree (DOM).
  7375. */
  7376. function getFirstNativeNode(lView, tNode) {
  7377. if (tNode !== null) {
  7378. ngDevMode &&
  7379. assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */ | 16 /* TNodeType.Projection */);
  7380. const tNodeType = tNode.type;
  7381. if (tNodeType & 3 /* TNodeType.AnyRNode */) {
  7382. return getNativeByTNode(tNode, lView);
  7383. }
  7384. else if (tNodeType & 4 /* TNodeType.Container */) {
  7385. return getBeforeNodeForView(-1, lView[tNode.index]);
  7386. }
  7387. else if (tNodeType & 8 /* TNodeType.ElementContainer */) {
  7388. const elIcuContainerChild = tNode.child;
  7389. if (elIcuContainerChild !== null) {
  7390. return getFirstNativeNode(lView, elIcuContainerChild);
  7391. }
  7392. else {
  7393. const rNodeOrLContainer = lView[tNode.index];
  7394. if (isLContainer(rNodeOrLContainer)) {
  7395. return getBeforeNodeForView(-1, rNodeOrLContainer);
  7396. }
  7397. else {
  7398. return unwrapRNode(rNodeOrLContainer);
  7399. }
  7400. }
  7401. }
  7402. else if (tNodeType & 32 /* TNodeType.Icu */) {
  7403. let nextRNode = icuContainerIterate(tNode, lView);
  7404. let rNode = nextRNode();
  7405. // If the ICU container has no nodes, than we use the ICU anchor as the node.
  7406. return rNode || unwrapRNode(lView[tNode.index]);
  7407. }
  7408. else {
  7409. const projectionNodes = getProjectionNodes(lView, tNode);
  7410. if (projectionNodes !== null) {
  7411. if (Array.isArray(projectionNodes)) {
  7412. return projectionNodes[0];
  7413. }
  7414. const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
  7415. ngDevMode && assertParentView(parentView);
  7416. return getFirstNativeNode(parentView, projectionNodes);
  7417. }
  7418. else {
  7419. return getFirstNativeNode(lView, tNode.next);
  7420. }
  7421. }
  7422. }
  7423. return null;
  7424. }
  7425. function getProjectionNodes(lView, tNode) {
  7426. if (tNode !== null) {
  7427. const componentView = lView[DECLARATION_COMPONENT_VIEW];
  7428. const componentHost = componentView[T_HOST];
  7429. const slotIdx = tNode.projection;
  7430. ngDevMode && assertProjectionSlots(lView);
  7431. return componentHost.projection[slotIdx];
  7432. }
  7433. return null;
  7434. }
  7435. function getBeforeNodeForView(viewIndexInContainer, lContainer) {
  7436. const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1;
  7437. if (nextViewIndex < lContainer.length) {
  7438. const lView = lContainer[nextViewIndex];
  7439. const firstTNodeOfView = lView[TVIEW].firstChild;
  7440. if (firstTNodeOfView !== null) {
  7441. return getFirstNativeNode(lView, firstTNodeOfView);
  7442. }
  7443. }
  7444. return lContainer[NATIVE];
  7445. }
  7446. /**
  7447. * Removes a native node itself using a given renderer. To remove the node we are looking up its
  7448. * parent from the native tree as not all platforms / browsers support the equivalent of
  7449. * node.remove().
  7450. *
  7451. * @param renderer A renderer to be used
  7452. * @param rNode The native node that should be removed
  7453. * @param isHostElement A flag indicating if a node to be removed is a host of a component.
  7454. */
  7455. function nativeRemoveNode(renderer, rNode, isHostElement) {
  7456. ngDevMode && ngDevMode.rendererRemoveNode++;
  7457. const nativeParent = nativeParentNode(renderer, rNode);
  7458. if (nativeParent) {
  7459. nativeRemoveChild(renderer, nativeParent, rNode, isHostElement);
  7460. }
  7461. }
  7462. /**
  7463. * Clears the contents of a given RElement.
  7464. *
  7465. * @param rElement the native RElement to be cleared
  7466. */
  7467. function clearElementContents(rElement) {
  7468. rElement.textContent = '';
  7469. }
  7470. /**
  7471. * Performs the operation of `action` on the node. Typically this involves inserting or removing
  7472. * nodes on the LView or projection boundary.
  7473. */
  7474. function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode, isProjection) {
  7475. while (tNode != null) {
  7476. ngDevMode && assertTNodeForLView(tNode, lView);
  7477. ngDevMode &&
  7478. assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
  7479. const rawSlotValue = lView[tNode.index];
  7480. const tNodeType = tNode.type;
  7481. if (isProjection) {
  7482. if (action === 0 /* WalkTNodeTreeAction.Create */) {
  7483. rawSlotValue && attachPatchData(unwrapRNode(rawSlotValue), lView);
  7484. tNode.flags |= 2 /* TNodeFlags.isProjected */;
  7485. }
  7486. }
  7487. if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
  7488. if (tNodeType & 8 /* TNodeType.ElementContainer */) {
  7489. applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false);
  7490. applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
  7491. }
  7492. else if (tNodeType & 32 /* TNodeType.Icu */) {
  7493. const nextRNode = icuContainerIterate(tNode, lView);
  7494. let rNode;
  7495. while (rNode = nextRNode()) {
  7496. applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
  7497. }
  7498. applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
  7499. }
  7500. else if (tNodeType & 16 /* TNodeType.Projection */) {
  7501. applyProjectionRecursive(renderer, action, lView, tNode, parentRElement, beforeNode);
  7502. }
  7503. else {
  7504. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */);
  7505. applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
  7506. }
  7507. }
  7508. tNode = isProjection ? tNode.projectionNext : tNode.next;
  7509. }
  7510. }
  7511. function applyView(tView, lView, renderer, action, parentRElement, beforeNode) {
  7512. applyNodes(renderer, action, tView.firstChild, lView, parentRElement, beforeNode, false);
  7513. }
  7514. /**
  7515. * `applyProjection` performs operation on the projection.
  7516. *
  7517. * Inserting a projection requires us to locate the projected nodes from the parent component. The
  7518. * complication is that those nodes themselves could be re-projected from their parent component.
  7519. *
  7520. * @param tView The `TView` of `LView` which needs to be inserted, detached, destroyed
  7521. * @param lView The `LView` which needs to be inserted, detached, destroyed.
  7522. * @param tProjectionNode node to project
  7523. */
  7524. function applyProjection(tView, lView, tProjectionNode) {
  7525. const renderer = lView[RENDERER];
  7526. const parentRNode = getParentRElement(tView, tProjectionNode, lView);
  7527. const parentTNode = tProjectionNode.parent || lView[T_HOST];
  7528. let beforeNode = getInsertInFrontOfRNode(parentTNode, tProjectionNode, lView);
  7529. applyProjectionRecursive(renderer, 0 /* WalkTNodeTreeAction.Create */, lView, tProjectionNode, parentRNode, beforeNode);
  7530. }
  7531. /**
  7532. * `applyProjectionRecursive` performs operation on the projection specified by `action` (insert,
  7533. * detach, destroy)
  7534. *
  7535. * Inserting a projection requires us to locate the projected nodes from the parent component. The
  7536. * complication is that those nodes themselves could be re-projected from their parent component.
  7537. *
  7538. * @param renderer Render to use
  7539. * @param action action to perform (insert, detach, destroy)
  7540. * @param lView The LView which needs to be inserted, detached, destroyed.
  7541. * @param tProjectionNode node to project
  7542. * @param parentRElement parent DOM element for insertion/removal.
  7543. * @param beforeNode Before which node the insertions should happen.
  7544. */
  7545. function applyProjectionRecursive(renderer, action, lView, tProjectionNode, parentRElement, beforeNode) {
  7546. const componentLView = lView[DECLARATION_COMPONENT_VIEW];
  7547. const componentNode = componentLView[T_HOST];
  7548. ngDevMode &&
  7549. assertEqual(typeof tProjectionNode.projection, 'number', 'expecting projection index');
  7550. const nodeToProjectOrRNodes = componentNode.projection[tProjectionNode.projection];
  7551. if (Array.isArray(nodeToProjectOrRNodes)) {
  7552. // This should not exist, it is a bit of a hack. When we bootstrap a top level node and we
  7553. // need to support passing projectable nodes, so we cheat and put them in the TNode
  7554. // of the Host TView. (Yes we put instance info at the T Level). We can get away with it
  7555. // because we know that that TView is not shared and therefore it will not be a problem.
  7556. // This should be refactored and cleaned up.
  7557. for (let i = 0; i < nodeToProjectOrRNodes.length; i++) {
  7558. const rNode = nodeToProjectOrRNodes[i];
  7559. applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
  7560. }
  7561. }
  7562. else {
  7563. let nodeToProject = nodeToProjectOrRNodes;
  7564. const projectedComponentLView = componentLView[PARENT];
  7565. // If a parent <ng-content> is located within a skip hydration block,
  7566. // annotate an actual node that is being projected with the same flag too.
  7567. if (hasInSkipHydrationBlockFlag(tProjectionNode)) {
  7568. nodeToProject.flags |= 128 /* TNodeFlags.inSkipHydrationBlock */;
  7569. }
  7570. applyNodes(renderer, action, nodeToProject, projectedComponentLView, parentRElement, beforeNode, true);
  7571. }
  7572. }
  7573. /**
  7574. * `applyContainer` performs an operation on the container and its views as specified by
  7575. * `action` (insert, detach, destroy)
  7576. *
  7577. * Inserting a Container is complicated by the fact that the container may have Views which
  7578. * themselves have containers or projections.
  7579. *
  7580. * @param renderer Renderer to use
  7581. * @param action action to perform (insert, detach, destroy)
  7582. * @param lContainer The LContainer which needs to be inserted, detached, destroyed.
  7583. * @param parentRElement parent DOM element for insertion/removal.
  7584. * @param beforeNode Before which node the insertions should happen.
  7585. */
  7586. function applyContainer(renderer, action, lContainer, parentRElement, beforeNode) {
  7587. ngDevMode && assertLContainer(lContainer);
  7588. const anchor = lContainer[NATIVE]; // LContainer has its own before node.
  7589. const native = unwrapRNode(lContainer);
  7590. // An LContainer can be created dynamically on any node by injecting ViewContainerRef.
  7591. // Asking for a ViewContainerRef on an element will result in a creation of a separate anchor
  7592. // node (comment in the DOM) that will be different from the LContainer's host node. In this
  7593. // particular case we need to execute action on 2 nodes:
  7594. // - container's host node (this is done in the executeActionOnElementOrContainer)
  7595. // - container's host node (this is done here)
  7596. if (anchor !== native) {
  7597. // This is very strange to me (Misko). I would expect that the native is same as anchor. I
  7598. // don't see a reason why they should be different, but they are.
  7599. //
  7600. // If they are we need to process the second anchor as well.
  7601. applyToElementOrContainer(action, renderer, parentRElement, anchor, beforeNode);
  7602. }
  7603. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  7604. const lView = lContainer[i];
  7605. applyView(lView[TVIEW], lView, renderer, action, parentRElement, anchor);
  7606. }
  7607. }
  7608. /**
  7609. * Writes class/style to element.
  7610. *
  7611. * @param renderer Renderer to use.
  7612. * @param isClassBased `true` if it should be written to `class` (`false` to write to `style`)
  7613. * @param rNode The Node to write to.
  7614. * @param prop Property to write to. This would be the class/style name.
  7615. * @param value Value to write. If `null`/`undefined`/`false` this is considered a remove (set/add
  7616. * otherwise).
  7617. */
  7618. function applyStyling(renderer, isClassBased, rNode, prop, value) {
  7619. if (isClassBased) {
  7620. // We actually want JS true/false here because any truthy value should add the class
  7621. if (!value) {
  7622. ngDevMode && ngDevMode.rendererRemoveClass++;
  7623. renderer.removeClass(rNode, prop);
  7624. }
  7625. else {
  7626. ngDevMode && ngDevMode.rendererAddClass++;
  7627. renderer.addClass(rNode, prop);
  7628. }
  7629. }
  7630. else {
  7631. let flags = prop.indexOf('-') === -1 ? undefined : RendererStyleFlags2.DashCase;
  7632. if (value == null /** || value === undefined */) {
  7633. ngDevMode && ngDevMode.rendererRemoveStyle++;
  7634. renderer.removeStyle(rNode, prop, flags);
  7635. }
  7636. else {
  7637. // A value is important if it ends with `!important`. The style
  7638. // parser strips any semicolons at the end of the value.
  7639. const isImportant = typeof value === 'string' ? value.endsWith('!important') : false;
  7640. if (isImportant) {
  7641. // !important has to be stripped from the value for it to be valid.
  7642. value = value.slice(0, -10);
  7643. flags |= RendererStyleFlags2.Important;
  7644. }
  7645. ngDevMode && ngDevMode.rendererSetStyle++;
  7646. renderer.setStyle(rNode, prop, value, flags);
  7647. }
  7648. }
  7649. }
  7650. /**
  7651. * Write `cssText` to `RElement`.
  7652. *
  7653. * This function does direct write without any reconciliation. Used for writing initial values, so
  7654. * that static styling values do not pull in the style parser.
  7655. *
  7656. * @param renderer Renderer to use
  7657. * @param element The element which needs to be updated.
  7658. * @param newValue The new class list to write.
  7659. */
  7660. function writeDirectStyle(renderer, element, newValue) {
  7661. ngDevMode && assertString(newValue, '\'newValue\' should be a string');
  7662. renderer.setAttribute(element, 'style', newValue);
  7663. ngDevMode && ngDevMode.rendererSetStyle++;
  7664. }
  7665. /**
  7666. * Write `className` to `RElement`.
  7667. *
  7668. * This function does direct write without any reconciliation. Used for writing initial values, so
  7669. * that static styling values do not pull in the style parser.
  7670. *
  7671. * @param renderer Renderer to use
  7672. * @param element The element which needs to be updated.
  7673. * @param newValue The new class list to write.
  7674. */
  7675. function writeDirectClass(renderer, element, newValue) {
  7676. ngDevMode && assertString(newValue, '\'newValue\' should be a string');
  7677. if (newValue === '') {
  7678. // There are tests in `google3` which expect `element.getAttribute('class')` to be `null`.
  7679. renderer.removeAttribute(element, 'class');
  7680. }
  7681. else {
  7682. renderer.setAttribute(element, 'class', newValue);
  7683. }
  7684. ngDevMode && ngDevMode.rendererSetClassName++;
  7685. }
  7686. /** Sets up the static DOM attributes on an `RNode`. */
  7687. function setupStaticAttributes(renderer, element, tNode) {
  7688. const { mergedAttrs, classes, styles } = tNode;
  7689. if (mergedAttrs !== null) {
  7690. setUpAttributes(renderer, element, mergedAttrs);
  7691. }
  7692. if (classes !== null) {
  7693. writeDirectClass(renderer, element, classes);
  7694. }
  7695. if (styles !== null) {
  7696. writeDirectStyle(renderer, element, styles);
  7697. }
  7698. }
  7699. /**
  7700. * @fileoverview
  7701. * A module to facilitate use of a Trusted Types policy internally within
  7702. * Angular. It lazily constructs the Trusted Types policy, providing helper
  7703. * utilities for promoting strings to Trusted Types. When Trusted Types are not
  7704. * available, strings are used as a fallback.
  7705. * @security All use of this module is security-sensitive and should go through
  7706. * security review.
  7707. */
  7708. /**
  7709. * The Trusted Types policy, or null if Trusted Types are not
  7710. * enabled/supported, or undefined if the policy has not been created yet.
  7711. */
  7712. let policy$1;
  7713. /**
  7714. * Returns the Trusted Types policy, or null if Trusted Types are not
  7715. * enabled/supported. The first call to this function will create the policy.
  7716. */
  7717. function getPolicy$1() {
  7718. if (policy$1 === undefined) {
  7719. policy$1 = null;
  7720. if (_global.trustedTypes) {
  7721. try {
  7722. policy$1 = _global.trustedTypes.createPolicy('angular', {
  7723. createHTML: (s) => s,
  7724. createScript: (s) => s,
  7725. createScriptURL: (s) => s,
  7726. });
  7727. }
  7728. catch {
  7729. // trustedTypes.createPolicy throws if called with a name that is
  7730. // already registered, even in report-only mode. Until the API changes,
  7731. // catch the error not to break the applications functionally. In such
  7732. // cases, the code will fall back to using strings.
  7733. }
  7734. }
  7735. }
  7736. return policy$1;
  7737. }
  7738. /**
  7739. * Unsafely promote a string to a TrustedHTML, falling back to strings when
  7740. * Trusted Types are not available.
  7741. * @security This is a security-sensitive function; any use of this function
  7742. * must go through security review. In particular, it must be assured that the
  7743. * provided string will never cause an XSS vulnerability if used in a context
  7744. * that will be interpreted as HTML by a browser, e.g. when assigning to
  7745. * element.innerHTML.
  7746. */
  7747. function trustedHTMLFromString(html) {
  7748. return getPolicy$1()?.createHTML(html) || html;
  7749. }
  7750. /**
  7751. * Unsafely promote a string to a TrustedScript, falling back to strings when
  7752. * Trusted Types are not available.
  7753. * @security In particular, it must be assured that the provided string will
  7754. * never cause an XSS vulnerability if used in a context that will be
  7755. * interpreted and executed as a script by a browser, e.g. when calling eval.
  7756. */
  7757. function trustedScriptFromString(script) {
  7758. return getPolicy$1()?.createScript(script) || script;
  7759. }
  7760. /**
  7761. * Unsafely promote a string to a TrustedScriptURL, falling back to strings
  7762. * when Trusted Types are not available.
  7763. * @security This is a security-sensitive function; any use of this function
  7764. * must go through security review. In particular, it must be assured that the
  7765. * provided string will never cause an XSS vulnerability if used in a context
  7766. * that will cause a browser to load and execute a resource, e.g. when
  7767. * assigning to script.src.
  7768. */
  7769. function trustedScriptURLFromString(url) {
  7770. return getPolicy$1()?.createScriptURL(url) || url;
  7771. }
  7772. /**
  7773. * Unsafely call the Function constructor with the given string arguments. It
  7774. * is only available in development mode, and should be stripped out of
  7775. * production code.
  7776. * @security This is a security-sensitive function; any use of this function
  7777. * must go through security review. In particular, it must be assured that it
  7778. * is only called from development code, as use in production code can lead to
  7779. * XSS vulnerabilities.
  7780. */
  7781. function newTrustedFunctionForDev(...args) {
  7782. if (typeof ngDevMode === 'undefined') {
  7783. throw new Error('newTrustedFunctionForDev should never be called in production');
  7784. }
  7785. if (!_global.trustedTypes) {
  7786. // In environments that don't support Trusted Types, fall back to the most
  7787. // straightforward implementation:
  7788. return new Function(...args);
  7789. }
  7790. // Chrome currently does not support passing TrustedScript to the Function
  7791. // constructor. The following implements the workaround proposed on the page
  7792. // below, where the Chromium bug is also referenced:
  7793. // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
  7794. const fnArgs = args.slice(0, -1).join(',');
  7795. const fnBody = args[args.length - 1];
  7796. const body = `(function anonymous(${fnArgs}
  7797. ) { ${fnBody}
  7798. })`;
  7799. // Using eval directly confuses the compiler and prevents this module from
  7800. // being stripped out of JS binaries even if not used. The global['eval']
  7801. // indirection fixes that.
  7802. const fn = _global['eval'](trustedScriptFromString(body));
  7803. if (fn.bind === undefined) {
  7804. // Workaround for a browser bug that only exists in Chrome 83, where passing
  7805. // a TrustedScript to eval just returns the TrustedScript back without
  7806. // evaluating it. In that case, fall back to the most straightforward
  7807. // implementation:
  7808. return new Function(...args);
  7809. }
  7810. // To completely mimic the behavior of calling "new Function", two more
  7811. // things need to happen:
  7812. // 1. Stringifying the resulting function should return its source code
  7813. fn.toString = () => body;
  7814. // 2. When calling the resulting function, `this` should refer to `global`
  7815. return fn.bind(_global);
  7816. // When Trusted Types support in Function constructors is widely available,
  7817. // the implementation of this function can be simplified to:
  7818. // return new Function(...args.map(a => trustedScriptFromString(a)));
  7819. }
  7820. /**
  7821. * Validation function invoked at runtime for each binding that might potentially
  7822. * represent a security-sensitive attribute of an <iframe>.
  7823. * See `IFRAME_SECURITY_SENSITIVE_ATTRS` in the
  7824. * `packages/compiler/src/schema/dom_security_schema.ts` script for the full list
  7825. * of such attributes.
  7826. *
  7827. * @codeGenApi
  7828. */
  7829. function ɵɵvalidateIframeAttribute(attrValue, tagName, attrName) {
  7830. const lView = getLView();
  7831. const tNode = getSelectedTNode();
  7832. const element = getNativeByTNode(tNode, lView);
  7833. // Restrict any dynamic bindings of security-sensitive attributes/properties
  7834. // on an <iframe> for security reasons.
  7835. if (tNode.type === 2 /* TNodeType.Element */ && tagName.toLowerCase() === 'iframe') {
  7836. const iframe = element;
  7837. // Unset previously applied `src` and `srcdoc` if we come across a situation when
  7838. // a security-sensitive attribute is set later via an attribute/property binding.
  7839. iframe.src = '';
  7840. iframe.srcdoc = trustedHTMLFromString('');
  7841. // Also remove the <iframe> from the document.
  7842. nativeRemoveNode(lView[RENDERER], iframe);
  7843. const errorMessage = ngDevMode &&
  7844. `Angular has detected that the \`${attrName}\` was applied ` +
  7845. `as a binding to an <iframe>${getTemplateLocationDetails(lView)}. ` +
  7846. `For security reasons, the \`${attrName}\` can be set on an <iframe> ` +
  7847. `as a static attribute only. \n` +
  7848. `To fix this, switch the \`${attrName}\` binding to a static attribute ` +
  7849. `in a template or in host bindings section.`;
  7850. throw new RuntimeError(-910 /* RuntimeErrorCode.UNSAFE_IFRAME_ATTRS */, errorMessage);
  7851. }
  7852. return attrValue;
  7853. }
  7854. /**
  7855. * Most of the use of `document` in Angular is from within the DI system so it is possible to simply
  7856. * inject the `DOCUMENT` token and are done.
  7857. *
  7858. * Ivy is special because it does not rely upon the DI and must get hold of the document some other
  7859. * way.
  7860. *
  7861. * The solution is to define `getDocument()` and `setDocument()` top-level functions for ivy.
  7862. * Wherever ivy needs the global document, it calls `getDocument()` instead.
  7863. *
  7864. * When running ivy outside of a browser environment, it is necessary to call `setDocument()` to
  7865. * tell ivy what the global `document` is.
  7866. *
  7867. * Angular does this for us in each of the standard platforms (`Browser` and `Server`)
  7868. * by calling `setDocument()` when providing the `DOCUMENT` token.
  7869. */
  7870. let DOCUMENT = undefined;
  7871. /**
  7872. * Tell ivy what the `document` is for this platform.
  7873. *
  7874. * It is only necessary to call this if the current platform is not a browser.
  7875. *
  7876. * @param document The object representing the global `document` in this environment.
  7877. */
  7878. function setDocument(document) {
  7879. DOCUMENT = document;
  7880. }
  7881. /**
  7882. * Access the object that represents the `document` for this platform.
  7883. *
  7884. * Ivy calls this whenever it needs to access the `document` object.
  7885. * For example to create the renderer or to do sanitization.
  7886. */
  7887. function getDocument() {
  7888. if (DOCUMENT !== undefined) {
  7889. return DOCUMENT;
  7890. }
  7891. else if (typeof document !== 'undefined') {
  7892. return document;
  7893. }
  7894. throw new RuntimeError(210 /* RuntimeErrorCode.MISSING_DOCUMENT */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
  7895. `The document object is not available in this context. Make sure the DOCUMENT injection token is provided.`);
  7896. // No "document" can be found. This should only happen if we are running ivy outside Angular and
  7897. // the current platform is not a browser. Since this is not a supported scenario at the moment
  7898. // this should not happen in Angular apps.
  7899. // Once we support running ivy outside of Angular we will need to publish `setDocument()` as a
  7900. // public API.
  7901. }
  7902. /**
  7903. * @fileoverview
  7904. * A module to facilitate use of a Trusted Types policy internally within
  7905. * Angular specifically for bypassSecurityTrust* and custom sanitizers. It
  7906. * lazily constructs the Trusted Types policy, providing helper utilities for
  7907. * promoting strings to Trusted Types. When Trusted Types are not available,
  7908. * strings are used as a fallback.
  7909. * @security All use of this module is security-sensitive and should go through
  7910. * security review.
  7911. */
  7912. /**
  7913. * The Trusted Types policy, or null if Trusted Types are not
  7914. * enabled/supported, or undefined if the policy has not been created yet.
  7915. */
  7916. let policy;
  7917. /**
  7918. * Returns the Trusted Types policy, or null if Trusted Types are not
  7919. * enabled/supported. The first call to this function will create the policy.
  7920. */
  7921. function getPolicy() {
  7922. if (policy === undefined) {
  7923. policy = null;
  7924. if (_global.trustedTypes) {
  7925. try {
  7926. policy = _global.trustedTypes
  7927. .createPolicy('angular#unsafe-bypass', {
  7928. createHTML: (s) => s,
  7929. createScript: (s) => s,
  7930. createScriptURL: (s) => s,
  7931. });
  7932. }
  7933. catch {
  7934. // trustedTypes.createPolicy throws if called with a name that is
  7935. // already registered, even in report-only mode. Until the API changes,
  7936. // catch the error not to break the applications functionally. In such
  7937. // cases, the code will fall back to using strings.
  7938. }
  7939. }
  7940. }
  7941. return policy;
  7942. }
  7943. /**
  7944. * Unsafely promote a string to a TrustedHTML, falling back to strings when
  7945. * Trusted Types are not available.
  7946. * @security This is a security-sensitive function; any use of this function
  7947. * must go through security review. In particular, it must be assured that it
  7948. * is only passed strings that come directly from custom sanitizers or the
  7949. * bypassSecurityTrust* functions.
  7950. */
  7951. function trustedHTMLFromStringBypass(html) {
  7952. return getPolicy()?.createHTML(html) || html;
  7953. }
  7954. /**
  7955. * Unsafely promote a string to a TrustedScript, falling back to strings when
  7956. * Trusted Types are not available.
  7957. * @security This is a security-sensitive function; any use of this function
  7958. * must go through security review. In particular, it must be assured that it
  7959. * is only passed strings that come directly from custom sanitizers or the
  7960. * bypassSecurityTrust* functions.
  7961. */
  7962. function trustedScriptFromStringBypass(script) {
  7963. return getPolicy()?.createScript(script) || script;
  7964. }
  7965. /**
  7966. * Unsafely promote a string to a TrustedScriptURL, falling back to strings
  7967. * when Trusted Types are not available.
  7968. * @security This is a security-sensitive function; any use of this function
  7969. * must go through security review. In particular, it must be assured that it
  7970. * is only passed strings that come directly from custom sanitizers or the
  7971. * bypassSecurityTrust* functions.
  7972. */
  7973. function trustedScriptURLFromStringBypass(url) {
  7974. return getPolicy()?.createScriptURL(url) || url;
  7975. }
  7976. class SafeValueImpl {
  7977. constructor(changingThisBreaksApplicationSecurity) {
  7978. this.changingThisBreaksApplicationSecurity = changingThisBreaksApplicationSecurity;
  7979. }
  7980. toString() {
  7981. return `SafeValue must use [property]=binding: ${this.changingThisBreaksApplicationSecurity}` +
  7982. ` (see ${XSS_SECURITY_URL})`;
  7983. }
  7984. }
  7985. class SafeHtmlImpl extends SafeValueImpl {
  7986. getTypeName() {
  7987. return "HTML" /* BypassType.Html */;
  7988. }
  7989. }
  7990. class SafeStyleImpl extends SafeValueImpl {
  7991. getTypeName() {
  7992. return "Style" /* BypassType.Style */;
  7993. }
  7994. }
  7995. class SafeScriptImpl extends SafeValueImpl {
  7996. getTypeName() {
  7997. return "Script" /* BypassType.Script */;
  7998. }
  7999. }
  8000. class SafeUrlImpl extends SafeValueImpl {
  8001. getTypeName() {
  8002. return "URL" /* BypassType.Url */;
  8003. }
  8004. }
  8005. class SafeResourceUrlImpl extends SafeValueImpl {
  8006. getTypeName() {
  8007. return "ResourceURL" /* BypassType.ResourceUrl */;
  8008. }
  8009. }
  8010. function unwrapSafeValue(value) {
  8011. return value instanceof SafeValueImpl ? value.changingThisBreaksApplicationSecurity :
  8012. value;
  8013. }
  8014. function allowSanitizationBypassAndThrow(value, type) {
  8015. const actualType = getSanitizationBypassType(value);
  8016. if (actualType != null && actualType !== type) {
  8017. // Allow ResourceURLs in URL contexts, they are strictly more trusted.
  8018. if (actualType === "ResourceURL" /* BypassType.ResourceUrl */ && type === "URL" /* BypassType.Url */)
  8019. return true;
  8020. throw new Error(`Required a safe ${type}, got a ${actualType} (see ${XSS_SECURITY_URL})`);
  8021. }
  8022. return actualType === type;
  8023. }
  8024. function getSanitizationBypassType(value) {
  8025. return value instanceof SafeValueImpl && value.getTypeName() || null;
  8026. }
  8027. /**
  8028. * Mark `html` string as trusted.
  8029. *
  8030. * This function wraps the trusted string in `String` and brands it in a way which makes it
  8031. * recognizable to {@link htmlSanitizer} to be trusted implicitly.
  8032. *
  8033. * @param trustedHtml `html` string which needs to be implicitly trusted.
  8034. * @returns a `html` which has been branded to be implicitly trusted.
  8035. */
  8036. function bypassSanitizationTrustHtml(trustedHtml) {
  8037. return new SafeHtmlImpl(trustedHtml);
  8038. }
  8039. /**
  8040. * Mark `style` string as trusted.
  8041. *
  8042. * This function wraps the trusted string in `String` and brands it in a way which makes it
  8043. * recognizable to {@link styleSanitizer} to be trusted implicitly.
  8044. *
  8045. * @param trustedStyle `style` string which needs to be implicitly trusted.
  8046. * @returns a `style` hich has been branded to be implicitly trusted.
  8047. */
  8048. function bypassSanitizationTrustStyle(trustedStyle) {
  8049. return new SafeStyleImpl(trustedStyle);
  8050. }
  8051. /**
  8052. * Mark `script` string as trusted.
  8053. *
  8054. * This function wraps the trusted string in `String` and brands it in a way which makes it
  8055. * recognizable to {@link scriptSanitizer} to be trusted implicitly.
  8056. *
  8057. * @param trustedScript `script` string which needs to be implicitly trusted.
  8058. * @returns a `script` which has been branded to be implicitly trusted.
  8059. */
  8060. function bypassSanitizationTrustScript(trustedScript) {
  8061. return new SafeScriptImpl(trustedScript);
  8062. }
  8063. /**
  8064. * Mark `url` string as trusted.
  8065. *
  8066. * This function wraps the trusted string in `String` and brands it in a way which makes it
  8067. * recognizable to {@link urlSanitizer} to be trusted implicitly.
  8068. *
  8069. * @param trustedUrl `url` string which needs to be implicitly trusted.
  8070. * @returns a `url` which has been branded to be implicitly trusted.
  8071. */
  8072. function bypassSanitizationTrustUrl(trustedUrl) {
  8073. return new SafeUrlImpl(trustedUrl);
  8074. }
  8075. /**
  8076. * Mark `url` string as trusted.
  8077. *
  8078. * This function wraps the trusted string in `String` and brands it in a way which makes it
  8079. * recognizable to {@link resourceUrlSanitizer} to be trusted implicitly.
  8080. *
  8081. * @param trustedResourceUrl `url` string which needs to be implicitly trusted.
  8082. * @returns a `url` which has been branded to be implicitly trusted.
  8083. */
  8084. function bypassSanitizationTrustResourceUrl(trustedResourceUrl) {
  8085. return new SafeResourceUrlImpl(trustedResourceUrl);
  8086. }
  8087. /**
  8088. * This helper is used to get hold of an inert tree of DOM elements containing dirty HTML
  8089. * that needs sanitizing.
  8090. * Depending upon browser support we use one of two strategies for doing this.
  8091. * Default: DOMParser strategy
  8092. * Fallback: InertDocument strategy
  8093. */
  8094. function getInertBodyHelper(defaultDoc) {
  8095. const inertDocumentHelper = new InertDocumentHelper(defaultDoc);
  8096. return isDOMParserAvailable() ? new DOMParserHelper(inertDocumentHelper) : inertDocumentHelper;
  8097. }
  8098. /**
  8099. * Uses DOMParser to create and fill an inert body element.
  8100. * This is the default strategy used in browsers that support it.
  8101. */
  8102. class DOMParserHelper {
  8103. constructor(inertDocumentHelper) {
  8104. this.inertDocumentHelper = inertDocumentHelper;
  8105. }
  8106. getInertBodyElement(html) {
  8107. // We add these extra elements to ensure that the rest of the content is parsed as expected
  8108. // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the
  8109. // `<head>` tag. Note that the `<body>` tag is closed implicitly to prevent unclosed tags
  8110. // in `html` from consuming the otherwise explicit `</body>` tag.
  8111. html = '<body><remove></remove>' + html;
  8112. try {
  8113. const body = new window.DOMParser()
  8114. .parseFromString(trustedHTMLFromString(html), 'text/html')
  8115. .body;
  8116. if (body === null) {
  8117. // In some browsers (e.g. Mozilla/5.0 iPad AppleWebKit Mobile) the `body` property only
  8118. // becomes available in the following tick of the JS engine. In that case we fall back to
  8119. // the `inertDocumentHelper` instead.
  8120. return this.inertDocumentHelper.getInertBodyElement(html);
  8121. }
  8122. body.removeChild(body.firstChild);
  8123. return body;
  8124. }
  8125. catch {
  8126. return null;
  8127. }
  8128. }
  8129. }
  8130. /**
  8131. * Use an HTML5 `template` element to create and fill an inert DOM element.
  8132. * This is the fallback strategy if the browser does not support DOMParser.
  8133. */
  8134. class InertDocumentHelper {
  8135. constructor(defaultDoc) {
  8136. this.defaultDoc = defaultDoc;
  8137. this.inertDocument = this.defaultDoc.implementation.createHTMLDocument('sanitization-inert');
  8138. }
  8139. getInertBodyElement(html) {
  8140. const templateEl = this.inertDocument.createElement('template');
  8141. templateEl.innerHTML = trustedHTMLFromString(html);
  8142. return templateEl;
  8143. }
  8144. }
  8145. /**
  8146. * We need to determine whether the DOMParser exists in the global context and
  8147. * supports parsing HTML; HTML parsing support is not as wide as other formats, see
  8148. * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Browser_compatibility.
  8149. *
  8150. * @suppress {uselessCode}
  8151. */
  8152. function isDOMParserAvailable() {
  8153. try {
  8154. return !!new window.DOMParser().parseFromString(trustedHTMLFromString(''), 'text/html');
  8155. }
  8156. catch {
  8157. return false;
  8158. }
  8159. }
  8160. /**
  8161. * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation
  8162. * contexts.
  8163. *
  8164. * This regular expression matches a subset of URLs that will not cause script
  8165. * execution if used in URL context within a HTML document. Specifically, this
  8166. * regular expression matches if:
  8167. * (1) Either a protocol that is not javascript:, and that has valid characters
  8168. * (alphanumeric or [+-.]).
  8169. * (2) or no protocol. A protocol must be followed by a colon. The below
  8170. * allows that by allowing colons only after one of the characters [/?#].
  8171. * A colon after a hash (#) must be in the fragment.
  8172. * Otherwise, a colon after a (?) must be in a query.
  8173. * Otherwise, a colon after a single solidus (/) must be in a path.
  8174. * Otherwise, a colon after a double solidus (//) must be in the authority
  8175. * (before port).
  8176. *
  8177. * The pattern disallows &, used in HTML entity declarations before
  8178. * one of the characters in [/?#]. This disallows HTML entities used in the
  8179. * protocol name, which should never happen, e.g. "h&#116;tp" for "http".
  8180. * It also disallows HTML entities in the first path part of a relative path,
  8181. * e.g. "foo&lt;bar/baz". Our existing escaping functions should not produce
  8182. * that. More importantly, it disallows masking of a colon,
  8183. * e.g. "javascript&#58;...".
  8184. *
  8185. * This regular expression was taken from the Closure sanitization library.
  8186. */
  8187. const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:\/?#]*(?:[\/?#]|$))/i;
  8188. function _sanitizeUrl(url) {
  8189. url = String(url);
  8190. if (url.match(SAFE_URL_PATTERN))
  8191. return url;
  8192. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  8193. console.warn(`WARNING: sanitizing unsafe URL value ${url} (see ${XSS_SECURITY_URL})`);
  8194. }
  8195. return 'unsafe:' + url;
  8196. }
  8197. function tagSet(tags) {
  8198. const res = {};
  8199. for (const t of tags.split(','))
  8200. res[t] = true;
  8201. return res;
  8202. }
  8203. function merge(...sets) {
  8204. const res = {};
  8205. for (const s of sets) {
  8206. for (const v in s) {
  8207. if (s.hasOwnProperty(v))
  8208. res[v] = true;
  8209. }
  8210. }
  8211. return res;
  8212. }
  8213. // Good source of info about elements and attributes
  8214. // https://html.spec.whatwg.org/#semantics
  8215. // https://simon.html5.org/html-elements
  8216. // Safe Void Elements - HTML5
  8217. // https://html.spec.whatwg.org/#void-elements
  8218. const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr');
  8219. // Elements that you can, intentionally, leave open (and which close themselves)
  8220. // https://html.spec.whatwg.org/#optional-tags
  8221. const OPTIONAL_END_TAG_BLOCK_ELEMENTS = tagSet('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr');
  8222. const OPTIONAL_END_TAG_INLINE_ELEMENTS = tagSet('rp,rt');
  8223. const OPTIONAL_END_TAG_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, OPTIONAL_END_TAG_BLOCK_ELEMENTS);
  8224. // Safe Block Elements - HTML5
  8225. const BLOCK_ELEMENTS = merge(OPTIONAL_END_TAG_BLOCK_ELEMENTS, tagSet('address,article,' +
  8226. 'aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
  8227. 'h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul'));
  8228. // Inline Elements - HTML5
  8229. const INLINE_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, tagSet('a,abbr,acronym,audio,b,' +
  8230. 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' +
  8231. 'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video'));
  8232. const VALID_ELEMENTS = merge(VOID_ELEMENTS, BLOCK_ELEMENTS, INLINE_ELEMENTS, OPTIONAL_END_TAG_ELEMENTS);
  8233. // Attributes that have href and hence need to be sanitized
  8234. const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href');
  8235. const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' +
  8236. 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' +
  8237. 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' +
  8238. 'scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,' +
  8239. 'valign,value,vspace,width');
  8240. // Accessibility attributes as per WAI-ARIA 1.1 (W3C Working Draft 14 December 2018)
  8241. const ARIA_ATTRS = tagSet('aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,' +
  8242. 'aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,' +
  8243. 'aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,' +
  8244. 'aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,' +
  8245. 'aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,' +
  8246. 'aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,' +
  8247. 'aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext');
  8248. // NB: This currently consciously doesn't support SVG. SVG sanitization has had several security
  8249. // issues in the past, so it seems safer to leave it out if possible. If support for binding SVG via
  8250. // innerHTML is required, SVG attributes should be added here.
  8251. // NB: Sanitization does not allow <form> elements or other active elements (<button> etc). Those
  8252. // can be sanitized, but they increase security surface area without a legitimate use case, so they
  8253. // are left out here.
  8254. const VALID_ATTRS = merge(URI_ATTRS, HTML_ATTRS, ARIA_ATTRS);
  8255. // Elements whose content should not be traversed/preserved, if the elements themselves are invalid.
  8256. //
  8257. // Typically, `<invalid>Some content</invalid>` would traverse (and in this case preserve)
  8258. // `Some content`, but strip `invalid-element` opening/closing tags. For some elements, though, we
  8259. // don't want to preserve the content, if the elements themselves are going to be removed.
  8260. const SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS = tagSet('script,style,template');
  8261. /**
  8262. * SanitizingHtmlSerializer serializes a DOM fragment, stripping out any unsafe elements and unsafe
  8263. * attributes.
  8264. */
  8265. class SanitizingHtmlSerializer {
  8266. constructor() {
  8267. // Explicitly track if something was stripped, to avoid accidentally warning of sanitization just
  8268. // because characters were re-encoded.
  8269. this.sanitizedSomething = false;
  8270. this.buf = [];
  8271. }
  8272. sanitizeChildren(el) {
  8273. // This cannot use a TreeWalker, as it has to run on Angular's various DOM adapters.
  8274. // However this code never accesses properties off of `document` before deleting its contents
  8275. // again, so it shouldn't be vulnerable to DOM clobbering.
  8276. let current = el.firstChild;
  8277. let traverseContent = true;
  8278. while (current) {
  8279. if (current.nodeType === Node.ELEMENT_NODE) {
  8280. traverseContent = this.startElement(current);
  8281. }
  8282. else if (current.nodeType === Node.TEXT_NODE) {
  8283. this.chars(current.nodeValue);
  8284. }
  8285. else {
  8286. // Strip non-element, non-text nodes.
  8287. this.sanitizedSomething = true;
  8288. }
  8289. if (traverseContent && current.firstChild) {
  8290. current = current.firstChild;
  8291. continue;
  8292. }
  8293. while (current) {
  8294. // Leaving the element. Walk up and to the right, closing tags as we go.
  8295. if (current.nodeType === Node.ELEMENT_NODE) {
  8296. this.endElement(current);
  8297. }
  8298. let next = this.checkClobberedElement(current, current.nextSibling);
  8299. if (next) {
  8300. current = next;
  8301. break;
  8302. }
  8303. current = this.checkClobberedElement(current, current.parentNode);
  8304. }
  8305. }
  8306. return this.buf.join('');
  8307. }
  8308. /**
  8309. * Sanitizes an opening element tag (if valid) and returns whether the element's contents should
  8310. * be traversed. Element content must always be traversed (even if the element itself is not
  8311. * valid/safe), unless the element is one of `SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS`.
  8312. *
  8313. * @param element The element to sanitize.
  8314. * @return True if the element's contents should be traversed.
  8315. */
  8316. startElement(element) {
  8317. const tagName = element.nodeName.toLowerCase();
  8318. if (!VALID_ELEMENTS.hasOwnProperty(tagName)) {
  8319. this.sanitizedSomething = true;
  8320. return !SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS.hasOwnProperty(tagName);
  8321. }
  8322. this.buf.push('<');
  8323. this.buf.push(tagName);
  8324. const elAttrs = element.attributes;
  8325. for (let i = 0; i < elAttrs.length; i++) {
  8326. const elAttr = elAttrs.item(i);
  8327. const attrName = elAttr.name;
  8328. const lower = attrName.toLowerCase();
  8329. if (!VALID_ATTRS.hasOwnProperty(lower)) {
  8330. this.sanitizedSomething = true;
  8331. continue;
  8332. }
  8333. let value = elAttr.value;
  8334. // TODO(martinprobst): Special case image URIs for data:image/...
  8335. if (URI_ATTRS[lower])
  8336. value = _sanitizeUrl(value);
  8337. this.buf.push(' ', attrName, '="', encodeEntities(value), '"');
  8338. }
  8339. this.buf.push('>');
  8340. return true;
  8341. }
  8342. endElement(current) {
  8343. const tagName = current.nodeName.toLowerCase();
  8344. if (VALID_ELEMENTS.hasOwnProperty(tagName) && !VOID_ELEMENTS.hasOwnProperty(tagName)) {
  8345. this.buf.push('</');
  8346. this.buf.push(tagName);
  8347. this.buf.push('>');
  8348. }
  8349. }
  8350. chars(chars) {
  8351. this.buf.push(encodeEntities(chars));
  8352. }
  8353. checkClobberedElement(node, nextNode) {
  8354. if (nextNode &&
  8355. (node.compareDocumentPosition(nextNode) &
  8356. Node.DOCUMENT_POSITION_CONTAINED_BY) === Node.DOCUMENT_POSITION_CONTAINED_BY) {
  8357. throw new Error(`Failed to sanitize html because the element is clobbered: ${node.outerHTML}`);
  8358. }
  8359. return nextNode;
  8360. }
  8361. }
  8362. // Regular Expressions for parsing tags and attributes
  8363. const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
  8364. // ! to ~ is the ASCII range.
  8365. const NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
  8366. /**
  8367. * Escapes all potentially dangerous characters, so that the
  8368. * resulting string can be safely inserted into attribute or
  8369. * element text.
  8370. * @param value
  8371. */
  8372. function encodeEntities(value) {
  8373. return value.replace(/&/g, '&amp;')
  8374. .replace(SURROGATE_PAIR_REGEXP, function (match) {
  8375. const hi = match.charCodeAt(0);
  8376. const low = match.charCodeAt(1);
  8377. return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
  8378. })
  8379. .replace(NON_ALPHANUMERIC_REGEXP, function (match) {
  8380. return '&#' + match.charCodeAt(0) + ';';
  8381. })
  8382. .replace(/</g, '&lt;')
  8383. .replace(/>/g, '&gt;');
  8384. }
  8385. let inertBodyHelper;
  8386. /**
  8387. * Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to
  8388. * the DOM in a browser environment.
  8389. */
  8390. function _sanitizeHtml(defaultDoc, unsafeHtmlInput) {
  8391. let inertBodyElement = null;
  8392. try {
  8393. inertBodyHelper = inertBodyHelper || getInertBodyHelper(defaultDoc);
  8394. // Make sure unsafeHtml is actually a string (TypeScript types are not enforced at runtime).
  8395. let unsafeHtml = unsafeHtmlInput ? String(unsafeHtmlInput) : '';
  8396. inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
  8397. // mXSS protection. Repeatedly parse the document to make sure it stabilizes, so that a browser
  8398. // trying to auto-correct incorrect HTML cannot cause formerly inert HTML to become dangerous.
  8399. let mXSSAttempts = 5;
  8400. let parsedHtml = unsafeHtml;
  8401. do {
  8402. if (mXSSAttempts === 0) {
  8403. throw new Error('Failed to sanitize html because the input is unstable');
  8404. }
  8405. mXSSAttempts--;
  8406. unsafeHtml = parsedHtml;
  8407. parsedHtml = inertBodyElement.innerHTML;
  8408. inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
  8409. } while (unsafeHtml !== parsedHtml);
  8410. const sanitizer = new SanitizingHtmlSerializer();
  8411. const safeHtml = sanitizer.sanitizeChildren(getTemplateContent(inertBodyElement) || inertBodyElement);
  8412. if ((typeof ngDevMode === 'undefined' || ngDevMode) && sanitizer.sanitizedSomething) {
  8413. console.warn(`WARNING: sanitizing HTML stripped some content, see ${XSS_SECURITY_URL}`);
  8414. }
  8415. return trustedHTMLFromString(safeHtml);
  8416. }
  8417. finally {
  8418. // In case anything goes wrong, clear out inertElement to reset the entire DOM structure.
  8419. if (inertBodyElement) {
  8420. const parent = getTemplateContent(inertBodyElement) || inertBodyElement;
  8421. while (parent.firstChild) {
  8422. parent.removeChild(parent.firstChild);
  8423. }
  8424. }
  8425. }
  8426. }
  8427. function getTemplateContent(el) {
  8428. return 'content' in el /** Microsoft/TypeScript#21517 */ && isTemplateElement(el) ?
  8429. el.content :
  8430. null;
  8431. }
  8432. function isTemplateElement(el) {
  8433. return el.nodeType === Node.ELEMENT_NODE && el.nodeName === 'TEMPLATE';
  8434. }
  8435. /**
  8436. * A SecurityContext marks a location that has dangerous security implications, e.g. a DOM property
  8437. * like `innerHTML` that could cause Cross Site Scripting (XSS) security bugs when improperly
  8438. * handled.
  8439. *
  8440. * See DomSanitizer for more details on security in Angular applications.
  8441. *
  8442. * @publicApi
  8443. */
  8444. var SecurityContext;
  8445. (function (SecurityContext) {
  8446. SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
  8447. SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
  8448. SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
  8449. SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
  8450. SecurityContext[SecurityContext["URL"] = 4] = "URL";
  8451. SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
  8452. })(SecurityContext || (SecurityContext = {}));
  8453. /**
  8454. * An `html` sanitizer which converts untrusted `html` **string** into trusted string by removing
  8455. * dangerous content.
  8456. *
  8457. * This method parses the `html` and locates potentially dangerous content (such as urls and
  8458. * javascript) and removes it.
  8459. *
  8460. * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustHtml}.
  8461. *
  8462. * @param unsafeHtml untrusted `html`, typically from the user.
  8463. * @returns `html` string which is safe to display to user, because all of the dangerous javascript
  8464. * and urls have been removed.
  8465. *
  8466. * @codeGenApi
  8467. */
  8468. function ɵɵsanitizeHtml(unsafeHtml) {
  8469. const sanitizer = getSanitizer();
  8470. if (sanitizer) {
  8471. return trustedHTMLFromStringBypass(sanitizer.sanitize(SecurityContext.HTML, unsafeHtml) || '');
  8472. }
  8473. if (allowSanitizationBypassAndThrow(unsafeHtml, "HTML" /* BypassType.Html */)) {
  8474. return trustedHTMLFromStringBypass(unwrapSafeValue(unsafeHtml));
  8475. }
  8476. return _sanitizeHtml(getDocument(), renderStringify(unsafeHtml));
  8477. }
  8478. /**
  8479. * A `style` sanitizer which converts untrusted `style` **string** into trusted string by removing
  8480. * dangerous content.
  8481. *
  8482. * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustStyle}.
  8483. *
  8484. * @param unsafeStyle untrusted `style`, typically from the user.
  8485. * @returns `style` string which is safe to bind to the `style` properties.
  8486. *
  8487. * @codeGenApi
  8488. */
  8489. function ɵɵsanitizeStyle(unsafeStyle) {
  8490. const sanitizer = getSanitizer();
  8491. if (sanitizer) {
  8492. return sanitizer.sanitize(SecurityContext.STYLE, unsafeStyle) || '';
  8493. }
  8494. if (allowSanitizationBypassAndThrow(unsafeStyle, "Style" /* BypassType.Style */)) {
  8495. return unwrapSafeValue(unsafeStyle);
  8496. }
  8497. return renderStringify(unsafeStyle);
  8498. }
  8499. /**
  8500. * A `url` sanitizer which converts untrusted `url` **string** into trusted string by removing
  8501. * dangerous
  8502. * content.
  8503. *
  8504. * This method parses the `url` and locates potentially dangerous content (such as javascript) and
  8505. * removes it.
  8506. *
  8507. * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustUrl}.
  8508. *
  8509. * @param unsafeUrl untrusted `url`, typically from the user.
  8510. * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
  8511. * all of the dangerous javascript has been removed.
  8512. *
  8513. * @codeGenApi
  8514. */
  8515. function ɵɵsanitizeUrl(unsafeUrl) {
  8516. const sanitizer = getSanitizer();
  8517. if (sanitizer) {
  8518. return sanitizer.sanitize(SecurityContext.URL, unsafeUrl) || '';
  8519. }
  8520. if (allowSanitizationBypassAndThrow(unsafeUrl, "URL" /* BypassType.Url */)) {
  8521. return unwrapSafeValue(unsafeUrl);
  8522. }
  8523. return _sanitizeUrl(renderStringify(unsafeUrl));
  8524. }
  8525. /**
  8526. * A `url` sanitizer which only lets trusted `url`s through.
  8527. *
  8528. * This passes only `url`s marked trusted by calling {@link bypassSanitizationTrustResourceUrl}.
  8529. *
  8530. * @param unsafeResourceUrl untrusted `url`, typically from the user.
  8531. * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
  8532. * only trusted `url`s have been allowed to pass.
  8533. *
  8534. * @codeGenApi
  8535. */
  8536. function ɵɵsanitizeResourceUrl(unsafeResourceUrl) {
  8537. const sanitizer = getSanitizer();
  8538. if (sanitizer) {
  8539. return trustedScriptURLFromStringBypass(sanitizer.sanitize(SecurityContext.RESOURCE_URL, unsafeResourceUrl) || '');
  8540. }
  8541. if (allowSanitizationBypassAndThrow(unsafeResourceUrl, "ResourceURL" /* BypassType.ResourceUrl */)) {
  8542. return trustedScriptURLFromStringBypass(unwrapSafeValue(unsafeResourceUrl));
  8543. }
  8544. throw new RuntimeError(904 /* RuntimeErrorCode.UNSAFE_VALUE_IN_RESOURCE_URL */, ngDevMode && `unsafe value used in a resource URL context (see ${XSS_SECURITY_URL})`);
  8545. }
  8546. /**
  8547. * A `script` sanitizer which only lets trusted javascript through.
  8548. *
  8549. * This passes only `script`s marked trusted by calling {@link
  8550. * bypassSanitizationTrustScript}.
  8551. *
  8552. * @param unsafeScript untrusted `script`, typically from the user.
  8553. * @returns `url` string which is safe to bind to the `<script>` element such as `<img src>`,
  8554. * because only trusted `scripts` have been allowed to pass.
  8555. *
  8556. * @codeGenApi
  8557. */
  8558. function ɵɵsanitizeScript(unsafeScript) {
  8559. const sanitizer = getSanitizer();
  8560. if (sanitizer) {
  8561. return trustedScriptFromStringBypass(sanitizer.sanitize(SecurityContext.SCRIPT, unsafeScript) || '');
  8562. }
  8563. if (allowSanitizationBypassAndThrow(unsafeScript, "Script" /* BypassType.Script */)) {
  8564. return trustedScriptFromStringBypass(unwrapSafeValue(unsafeScript));
  8565. }
  8566. throw new RuntimeError(905 /* RuntimeErrorCode.UNSAFE_VALUE_IN_SCRIPT */, ngDevMode && 'unsafe value used in a script context');
  8567. }
  8568. /**
  8569. * A template tag function for promoting the associated constant literal to a
  8570. * TrustedHTML. Interpolation is explicitly not allowed.
  8571. *
  8572. * @param html constant template literal containing trusted HTML.
  8573. * @returns TrustedHTML wrapping `html`.
  8574. *
  8575. * @security This is a security-sensitive function and should only be used to
  8576. * convert constant values of attributes and properties found in
  8577. * application-provided Angular templates to TrustedHTML.
  8578. *
  8579. * @codeGenApi
  8580. */
  8581. function ɵɵtrustConstantHtml(html) {
  8582. // The following runtime check ensures that the function was called as a
  8583. // template tag (e.g. ɵɵtrustConstantHtml`content`), without any interpolation
  8584. // (e.g. not ɵɵtrustConstantHtml`content ${variable}`). A TemplateStringsArray
  8585. // is an array with a `raw` property that is also an array. The associated
  8586. // template literal has no interpolation if and only if the length of the
  8587. // TemplateStringsArray is 1.
  8588. if (ngDevMode && (!Array.isArray(html) || !Array.isArray(html.raw) || html.length !== 1)) {
  8589. throw new Error(`Unexpected interpolation in trusted HTML constant: ${html.join('?')}`);
  8590. }
  8591. return trustedHTMLFromString(html[0]);
  8592. }
  8593. /**
  8594. * A template tag function for promoting the associated constant literal to a
  8595. * TrustedScriptURL. Interpolation is explicitly not allowed.
  8596. *
  8597. * @param url constant template literal containing a trusted script URL.
  8598. * @returns TrustedScriptURL wrapping `url`.
  8599. *
  8600. * @security This is a security-sensitive function and should only be used to
  8601. * convert constant values of attributes and properties found in
  8602. * application-provided Angular templates to TrustedScriptURL.
  8603. *
  8604. * @codeGenApi
  8605. */
  8606. function ɵɵtrustConstantResourceUrl(url) {
  8607. // The following runtime check ensures that the function was called as a
  8608. // template tag (e.g. ɵɵtrustConstantResourceUrl`content`), without any
  8609. // interpolation (e.g. not ɵɵtrustConstantResourceUrl`content ${variable}`). A
  8610. // TemplateStringsArray is an array with a `raw` property that is also an
  8611. // array. The associated template literal has no interpolation if and only if
  8612. // the length of the TemplateStringsArray is 1.
  8613. if (ngDevMode && (!Array.isArray(url) || !Array.isArray(url.raw) || url.length !== 1)) {
  8614. throw new Error(`Unexpected interpolation in trusted URL constant: ${url.join('?')}`);
  8615. }
  8616. return trustedScriptURLFromString(url[0]);
  8617. }
  8618. /**
  8619. * Detects which sanitizer to use for URL property, based on tag name and prop name.
  8620. *
  8621. * The rules are based on the RESOURCE_URL context config from
  8622. * `packages/compiler/src/schema/dom_security_schema.ts`.
  8623. * If tag and prop names don't match Resource URL schema, use URL sanitizer.
  8624. */
  8625. function getUrlSanitizer(tag, prop) {
  8626. if ((prop === 'src' &&
  8627. (tag === 'embed' || tag === 'frame' || tag === 'iframe' || tag === 'media' ||
  8628. tag === 'script')) ||
  8629. (prop === 'href' && (tag === 'base' || tag === 'link'))) {
  8630. return ɵɵsanitizeResourceUrl;
  8631. }
  8632. return ɵɵsanitizeUrl;
  8633. }
  8634. /**
  8635. * Sanitizes URL, selecting sanitizer function based on tag and property names.
  8636. *
  8637. * This function is used in case we can't define security context at compile time, when only prop
  8638. * name is available. This happens when we generate host bindings for Directives/Components. The
  8639. * host element is unknown at compile time, so we defer calculation of specific sanitizer to
  8640. * runtime.
  8641. *
  8642. * @param unsafeUrl untrusted `url`, typically from the user.
  8643. * @param tag target element tag name.
  8644. * @param prop name of the property that contains the value.
  8645. * @returns `url` string which is safe to bind.
  8646. *
  8647. * @codeGenApi
  8648. */
  8649. function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl, tag, prop) {
  8650. return getUrlSanitizer(tag, prop)(unsafeUrl);
  8651. }
  8652. function validateAgainstEventProperties(name) {
  8653. if (name.toLowerCase().startsWith('on')) {
  8654. const errorMessage = `Binding to event property '${name}' is disallowed for security reasons, ` +
  8655. `please use (${name.slice(2)})=...` +
  8656. `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
  8657. ` current module.`;
  8658. throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage);
  8659. }
  8660. }
  8661. function validateAgainstEventAttributes(name) {
  8662. if (name.toLowerCase().startsWith('on')) {
  8663. const errorMessage = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
  8664. `please use (${name.slice(2)})=...`;
  8665. throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage);
  8666. }
  8667. }
  8668. function getSanitizer() {
  8669. const lView = getLView();
  8670. return lView && lView[ENVIRONMENT].sanitizer;
  8671. }
  8672. /**
  8673. * Creates a token that can be used in a DI Provider.
  8674. *
  8675. * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
  8676. * runtime representation) such as when injecting an interface, callable type, array or
  8677. * parameterized type.
  8678. *
  8679. * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
  8680. * the `Injector`. This provides an additional level of type safety.
  8681. *
  8682. * <div class="alert is-helpful">
  8683. *
  8684. * **Important Note**: Ensure that you use the same instance of the `InjectionToken` in both the
  8685. * provider and the injection call. Creating a new instance of `InjectionToken` in different places,
  8686. * even with the same description, will be treated as different tokens by Angular's DI system,
  8687. * leading to a `NullInjectorError`.
  8688. *
  8689. * </div>
  8690. *
  8691. * <code-example format="typescript" language="typescript" path="injection-token/src/main.ts"
  8692. * region="InjectionToken"></code-example>
  8693. *
  8694. * When creating an `InjectionToken`, you can optionally specify a factory function which returns
  8695. * (possibly by creating) a default value of the parameterized type `T`. This sets up the
  8696. * `InjectionToken` using this factory as a provider as if it was defined explicitly in the
  8697. * application's root injector. If the factory function, which takes zero arguments, needs to inject
  8698. * dependencies, it can do so using the [`inject`](api/core/inject) function.
  8699. * As you can see in the Tree-shakable InjectionToken example below.
  8700. *
  8701. * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
  8702. * overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note:
  8703. * this option is now deprecated). As mentioned above, `'root'` is the default value for
  8704. * `providedIn`.
  8705. *
  8706. * The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated.
  8707. *
  8708. * @usageNotes
  8709. * ### Basic Examples
  8710. *
  8711. * ### Plain InjectionToken
  8712. *
  8713. * {@example core/di/ts/injector_spec.ts region='InjectionToken'}
  8714. *
  8715. * ### Tree-shakable InjectionToken
  8716. *
  8717. * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
  8718. *
  8719. * @publicApi
  8720. */
  8721. class InjectionToken {
  8722. /**
  8723. * @param _desc Description for the token,
  8724. * used only for debugging purposes,
  8725. * it should but does not need to be unique
  8726. * @param options Options for the token's usage, as described above
  8727. */
  8728. constructor(_desc, options) {
  8729. this._desc = _desc;
  8730. /** @internal */
  8731. this.ngMetadataName = 'InjectionToken';
  8732. this.ɵprov = undefined;
  8733. if (typeof options == 'number') {
  8734. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  8735. assertLessThan(options, 0, 'Only negative numbers are supported here');
  8736. // This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
  8737. // See `InjectorMarkers`
  8738. this.__NG_ELEMENT_ID__ = options;
  8739. }
  8740. else if (options !== undefined) {
  8741. this.ɵprov = ɵɵdefineInjectable({
  8742. token: this,
  8743. providedIn: options.providedIn || 'root',
  8744. factory: options.factory,
  8745. });
  8746. }
  8747. }
  8748. /**
  8749. * @internal
  8750. */
  8751. get multi() {
  8752. return this;
  8753. }
  8754. toString() {
  8755. return `InjectionToken ${this._desc}`;
  8756. }
  8757. }
  8758. /**
  8759. * A multi-provider token for initialization functions that will run upon construction of an
  8760. * environment injector.
  8761. *
  8762. * @publicApi
  8763. */
  8764. const ENVIRONMENT_INITIALIZER = new InjectionToken('ENVIRONMENT_INITIALIZER');
  8765. /**
  8766. * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
  8767. *
  8768. * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
  8769. * project.
  8770. *
  8771. * @publicApi
  8772. */
  8773. const INJECTOR = new InjectionToken('INJECTOR',
  8774. // Disable tslint because this is const enum which gets inlined not top level prop access.
  8775. // tslint:disable-next-line: no-toplevel-property-access
  8776. -1 /* InjectorMarkers.Injector */);
  8777. const INJECTOR_DEF_TYPES = new InjectionToken('INJECTOR_DEF_TYPES');
  8778. class NullInjector {
  8779. get(token, notFoundValue = THROW_IF_NOT_FOUND) {
  8780. if (notFoundValue === THROW_IF_NOT_FOUND) {
  8781. const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
  8782. error.name = 'NullInjectorError';
  8783. throw error;
  8784. }
  8785. return notFoundValue;
  8786. }
  8787. }
  8788. /**
  8789. * Wrap an array of `Provider`s into `EnvironmentProviders`, preventing them from being accidentally
  8790. * referenced in `@Component` in a component injector.
  8791. */
  8792. function makeEnvironmentProviders(providers) {
  8793. return {
  8794. ɵproviders: providers,
  8795. };
  8796. }
  8797. /**
  8798. * Collects providers from all NgModules and standalone components, including transitively imported
  8799. * ones.
  8800. *
  8801. * Providers extracted via `importProvidersFrom` are only usable in an application injector or
  8802. * another environment injector (such as a route injector). They should not be used in component
  8803. * providers.
  8804. *
  8805. * More information about standalone components can be found in [this
  8806. * guide](guide/standalone-components).
  8807. *
  8808. * @usageNotes
  8809. * The results of the `importProvidersFrom` call can be used in the `bootstrapApplication` call:
  8810. *
  8811. * ```typescript
  8812. * await bootstrapApplication(RootComponent, {
  8813. * providers: [
  8814. * importProvidersFrom(NgModuleOne, NgModuleTwo)
  8815. * ]
  8816. * });
  8817. * ```
  8818. *
  8819. * You can also use the `importProvidersFrom` results in the `providers` field of a route, when a
  8820. * standalone component is used:
  8821. *
  8822. * ```typescript
  8823. * export const ROUTES: Route[] = [
  8824. * {
  8825. * path: 'foo',
  8826. * providers: [
  8827. * importProvidersFrom(NgModuleOne, NgModuleTwo)
  8828. * ],
  8829. * component: YourStandaloneComponent
  8830. * }
  8831. * ];
  8832. * ```
  8833. *
  8834. * @returns Collected providers from the specified list of types.
  8835. * @publicApi
  8836. */
  8837. function importProvidersFrom(...sources) {
  8838. return {
  8839. ɵproviders: internalImportProvidersFrom(true, sources),
  8840. ɵfromNgModule: true,
  8841. };
  8842. }
  8843. function internalImportProvidersFrom(checkForStandaloneCmp, ...sources) {
  8844. const providersOut = [];
  8845. const dedup = new Set(); // already seen types
  8846. let injectorTypesWithProviders;
  8847. const collectProviders = (provider) => {
  8848. providersOut.push(provider);
  8849. };
  8850. deepForEach(sources, source => {
  8851. if ((typeof ngDevMode === 'undefined' || ngDevMode) && checkForStandaloneCmp) {
  8852. const cmpDef = getComponentDef(source);
  8853. if (cmpDef?.standalone) {
  8854. throw new RuntimeError(800 /* RuntimeErrorCode.IMPORT_PROVIDERS_FROM_STANDALONE */, `Importing providers supports NgModule or ModuleWithProviders but got a standalone component "${stringifyForError(source)}"`);
  8855. }
  8856. }
  8857. // Narrow `source` to access the internal type analogue for `ModuleWithProviders`.
  8858. const internalSource = source;
  8859. if (walkProviderTree(internalSource, collectProviders, [], dedup)) {
  8860. injectorTypesWithProviders ||= [];
  8861. injectorTypesWithProviders.push(internalSource);
  8862. }
  8863. });
  8864. // Collect all providers from `ModuleWithProviders` types.
  8865. if (injectorTypesWithProviders !== undefined) {
  8866. processInjectorTypesWithProviders(injectorTypesWithProviders, collectProviders);
  8867. }
  8868. return providersOut;
  8869. }
  8870. /**
  8871. * Collects all providers from the list of `ModuleWithProviders` and appends them to the provided
  8872. * array.
  8873. */
  8874. function processInjectorTypesWithProviders(typesWithProviders, visitor) {
  8875. for (let i = 0; i < typesWithProviders.length; i++) {
  8876. const { ngModule, providers } = typesWithProviders[i];
  8877. deepForEachProvider(providers, provider => {
  8878. ngDevMode && validateProvider(provider, providers || EMPTY_ARRAY, ngModule);
  8879. visitor(provider, ngModule);
  8880. });
  8881. }
  8882. }
  8883. /**
  8884. * The logic visits an `InjectorType`, an `InjectorTypeWithProviders`, or a standalone
  8885. * `ComponentType`, and all of its transitive providers and collects providers.
  8886. *
  8887. * If an `InjectorTypeWithProviders` that declares providers besides the type is specified,
  8888. * the function will return "true" to indicate that the providers of the type definition need
  8889. * to be processed. This allows us to process providers of injector types after all imports of
  8890. * an injector definition are processed. (following View Engine semantics: see FW-1349)
  8891. */
  8892. function walkProviderTree(container, visitor, parents, dedup) {
  8893. container = resolveForwardRef(container);
  8894. if (!container)
  8895. return false;
  8896. // The actual type which had the definition. Usually `container`, but may be an unwrapped type
  8897. // from `InjectorTypeWithProviders`.
  8898. let defType = null;
  8899. let injDef = getInjectorDef(container);
  8900. const cmpDef = !injDef && getComponentDef(container);
  8901. if (!injDef && !cmpDef) {
  8902. // `container` is not an injector type or a component type. It might be:
  8903. // * An `InjectorTypeWithProviders` that wraps an injector type.
  8904. // * A standalone directive or pipe that got pulled in from a standalone component's
  8905. // dependencies.
  8906. // Try to unwrap it as an `InjectorTypeWithProviders` first.
  8907. const ngModule = container.ngModule;
  8908. injDef = getInjectorDef(ngModule);
  8909. if (injDef) {
  8910. defType = ngModule;
  8911. }
  8912. else {
  8913. // Not a component or injector type, so ignore it.
  8914. return false;
  8915. }
  8916. }
  8917. else if (cmpDef && !cmpDef.standalone) {
  8918. return false;
  8919. }
  8920. else {
  8921. defType = container;
  8922. }
  8923. // Check for circular dependencies.
  8924. if (ngDevMode && parents.indexOf(defType) !== -1) {
  8925. const defName = stringify(defType);
  8926. const path = parents.map(stringify);
  8927. throwCyclicDependencyError(defName, path);
  8928. }
  8929. // Check for multiple imports of the same module
  8930. const isDuplicate = dedup.has(defType);
  8931. if (cmpDef) {
  8932. if (isDuplicate) {
  8933. // This component definition has already been processed.
  8934. return false;
  8935. }
  8936. dedup.add(defType);
  8937. if (cmpDef.dependencies) {
  8938. const deps = typeof cmpDef.dependencies === 'function' ? cmpDef.dependencies() : cmpDef.dependencies;
  8939. for (const dep of deps) {
  8940. walkProviderTree(dep, visitor, parents, dedup);
  8941. }
  8942. }
  8943. }
  8944. else if (injDef) {
  8945. // First, include providers from any imports.
  8946. if (injDef.imports != null && !isDuplicate) {
  8947. // Before processing defType's imports, add it to the set of parents. This way, if it ends
  8948. // up deeply importing itself, this can be detected.
  8949. ngDevMode && parents.push(defType);
  8950. // Add it to the set of dedups. This way we can detect multiple imports of the same module
  8951. dedup.add(defType);
  8952. let importTypesWithProviders;
  8953. try {
  8954. deepForEach(injDef.imports, imported => {
  8955. if (walkProviderTree(imported, visitor, parents, dedup)) {
  8956. importTypesWithProviders ||= [];
  8957. // If the processed import is an injector type with providers, we store it in the
  8958. // list of import types with providers, so that we can process those afterwards.
  8959. importTypesWithProviders.push(imported);
  8960. }
  8961. });
  8962. }
  8963. finally {
  8964. // Remove it from the parents set when finished.
  8965. ngDevMode && parents.pop();
  8966. }
  8967. // Imports which are declared with providers (TypeWithProviders) need to be processed
  8968. // after all imported modules are processed. This is similar to how View Engine
  8969. // processes/merges module imports in the metadata resolver. See: FW-1349.
  8970. if (importTypesWithProviders !== undefined) {
  8971. processInjectorTypesWithProviders(importTypesWithProviders, visitor);
  8972. }
  8973. }
  8974. if (!isDuplicate) {
  8975. // Track the InjectorType and add a provider for it.
  8976. // It's important that this is done after the def's imports.
  8977. const factory = getFactoryDef(defType) || (() => new defType());
  8978. // Append extra providers to make more info available for consumers (to retrieve an injector
  8979. // type), as well as internally (to calculate an injection scope correctly and eagerly
  8980. // instantiate a `defType` when an injector is created).
  8981. // Provider to create `defType` using its factory.
  8982. visitor({ provide: defType, useFactory: factory, deps: EMPTY_ARRAY }, defType);
  8983. // Make this `defType` available to an internal logic that calculates injector scope.
  8984. visitor({ provide: INJECTOR_DEF_TYPES, useValue: defType, multi: true }, defType);
  8985. // Provider to eagerly instantiate `defType` via `INJECTOR_INITIALIZER`.
  8986. visitor({ provide: ENVIRONMENT_INITIALIZER, useValue: () => ɵɵinject(defType), multi: true }, defType);
  8987. }
  8988. // Next, include providers listed on the definition itself.
  8989. const defProviders = injDef.providers;
  8990. if (defProviders != null && !isDuplicate) {
  8991. const injectorType = container;
  8992. deepForEachProvider(defProviders, provider => {
  8993. ngDevMode && validateProvider(provider, defProviders, injectorType);
  8994. visitor(provider, injectorType);
  8995. });
  8996. }
  8997. }
  8998. else {
  8999. // Should not happen, but just in case.
  9000. return false;
  9001. }
  9002. return (defType !== container &&
  9003. container.providers !== undefined);
  9004. }
  9005. function validateProvider(provider, providers, containerType) {
  9006. if (isTypeProvider(provider) || isValueProvider(provider) || isFactoryProvider(provider) ||
  9007. isExistingProvider(provider)) {
  9008. return;
  9009. }
  9010. // Here we expect the provider to be a `useClass` provider (by elimination).
  9011. const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide));
  9012. if (!classRef) {
  9013. throwInvalidProviderError(containerType, providers, provider);
  9014. }
  9015. }
  9016. function deepForEachProvider(providers, fn) {
  9017. for (let provider of providers) {
  9018. if (isEnvironmentProviders(provider)) {
  9019. provider = provider.ɵproviders;
  9020. }
  9021. if (Array.isArray(provider)) {
  9022. deepForEachProvider(provider, fn);
  9023. }
  9024. else {
  9025. fn(provider);
  9026. }
  9027. }
  9028. }
  9029. const USE_VALUE$1 = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty });
  9030. function isValueProvider(value) {
  9031. return value !== null && typeof value == 'object' && USE_VALUE$1 in value;
  9032. }
  9033. function isExistingProvider(value) {
  9034. return !!(value && value.useExisting);
  9035. }
  9036. function isFactoryProvider(value) {
  9037. return !!(value && value.useFactory);
  9038. }
  9039. function isTypeProvider(value) {
  9040. return typeof value === 'function';
  9041. }
  9042. function isClassProvider(value) {
  9043. return !!value.useClass;
  9044. }
  9045. /**
  9046. * An internal token whose presence in an injector indicates that the injector should treat itself
  9047. * as a root scoped injector when processing requests for unknown tokens which may indicate
  9048. * they are provided in the root scope.
  9049. */
  9050. const INJECTOR_SCOPE = new InjectionToken('Set Injector scope.');
  9051. /**
  9052. * Marker which indicates that a value has not yet been created from the factory function.
  9053. */
  9054. const NOT_YET = {};
  9055. /**
  9056. * Marker which indicates that the factory function for a token is in the process of being called.
  9057. *
  9058. * If the injector is asked to inject a token with its value set to CIRCULAR, that indicates
  9059. * injection of a dependency has recursively attempted to inject the original token, and there is
  9060. * a circular dependency among the providers.
  9061. */
  9062. const CIRCULAR = {};
  9063. /**
  9064. * A lazily initialized NullInjector.
  9065. */
  9066. let NULL_INJECTOR = undefined;
  9067. function getNullInjector() {
  9068. if (NULL_INJECTOR === undefined) {
  9069. NULL_INJECTOR = new NullInjector();
  9070. }
  9071. return NULL_INJECTOR;
  9072. }
  9073. /**
  9074. * An `Injector` that's part of the environment injector hierarchy, which exists outside of the
  9075. * component tree.
  9076. */
  9077. class EnvironmentInjector {
  9078. }
  9079. class R3Injector extends EnvironmentInjector {
  9080. /**
  9081. * Flag indicating that this injector was previously destroyed.
  9082. */
  9083. get destroyed() {
  9084. return this._destroyed;
  9085. }
  9086. constructor(providers, parent, source, scopes) {
  9087. super();
  9088. this.parent = parent;
  9089. this.source = source;
  9090. this.scopes = scopes;
  9091. /**
  9092. * Map of tokens to records which contain the instances of those tokens.
  9093. * - `null` value implies that we don't have the record. Used by tree-shakable injectors
  9094. * to prevent further searches.
  9095. */
  9096. this.records = new Map();
  9097. /**
  9098. * Set of values instantiated by this injector which contain `ngOnDestroy` lifecycle hooks.
  9099. */
  9100. this._ngOnDestroyHooks = new Set();
  9101. this._onDestroyHooks = [];
  9102. this._destroyed = false;
  9103. // Start off by creating Records for every provider.
  9104. forEachSingleProvider(providers, provider => this.processProvider(provider));
  9105. // Make sure the INJECTOR token provides this injector.
  9106. this.records.set(INJECTOR, makeRecord(undefined, this));
  9107. // And `EnvironmentInjector` if the current injector is supposed to be env-scoped.
  9108. if (scopes.has('environment')) {
  9109. this.records.set(EnvironmentInjector, makeRecord(undefined, this));
  9110. }
  9111. // Detect whether this injector has the APP_ROOT_SCOPE token and thus should provide
  9112. // any injectable scoped to APP_ROOT_SCOPE.
  9113. const record = this.records.get(INJECTOR_SCOPE);
  9114. if (record != null && typeof record.value === 'string') {
  9115. this.scopes.add(record.value);
  9116. }
  9117. this.injectorDefTypes =
  9118. new Set(this.get(INJECTOR_DEF_TYPES.multi, EMPTY_ARRAY, InjectFlags.Self));
  9119. }
  9120. /**
  9121. * Destroy the injector and release references to every instance or provider associated with it.
  9122. *
  9123. * Also calls the `OnDestroy` lifecycle hooks of every instance that was created for which a
  9124. * hook was found.
  9125. */
  9126. destroy() {
  9127. this.assertNotDestroyed();
  9128. // Set destroyed = true first, in case lifecycle hooks re-enter destroy().
  9129. this._destroyed = true;
  9130. try {
  9131. // Call all the lifecycle hooks.
  9132. for (const service of this._ngOnDestroyHooks) {
  9133. service.ngOnDestroy();
  9134. }
  9135. const onDestroyHooks = this._onDestroyHooks;
  9136. // Reset the _onDestroyHooks array before iterating over it to prevent hooks that unregister
  9137. // themselves from mutating the array during iteration.
  9138. this._onDestroyHooks = [];
  9139. for (const hook of onDestroyHooks) {
  9140. hook();
  9141. }
  9142. }
  9143. finally {
  9144. // Release all references.
  9145. this.records.clear();
  9146. this._ngOnDestroyHooks.clear();
  9147. this.injectorDefTypes.clear();
  9148. }
  9149. }
  9150. onDestroy(callback) {
  9151. this.assertNotDestroyed();
  9152. this._onDestroyHooks.push(callback);
  9153. return () => this.removeOnDestroy(callback);
  9154. }
  9155. runInContext(fn) {
  9156. this.assertNotDestroyed();
  9157. const previousInjector = setCurrentInjector(this);
  9158. const previousInjectImplementation = setInjectImplementation(undefined);
  9159. let prevInjectContext;
  9160. if (ngDevMode) {
  9161. prevInjectContext = setInjectorProfilerContext({ injector: this, token: null });
  9162. }
  9163. try {
  9164. return fn();
  9165. }
  9166. finally {
  9167. setCurrentInjector(previousInjector);
  9168. setInjectImplementation(previousInjectImplementation);
  9169. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  9170. }
  9171. }
  9172. get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) {
  9173. this.assertNotDestroyed();
  9174. if (token.hasOwnProperty(NG_ENV_ID)) {
  9175. return token[NG_ENV_ID](this);
  9176. }
  9177. flags = convertToBitFlags(flags);
  9178. // Set the injection context.
  9179. let prevInjectContext;
  9180. if (ngDevMode) {
  9181. prevInjectContext = setInjectorProfilerContext({ injector: this, token: token });
  9182. }
  9183. const previousInjector = setCurrentInjector(this);
  9184. const previousInjectImplementation = setInjectImplementation(undefined);
  9185. try {
  9186. // Check for the SkipSelf flag.
  9187. if (!(flags & InjectFlags.SkipSelf)) {
  9188. // SkipSelf isn't set, check if the record belongs to this injector.
  9189. let record = this.records.get(token);
  9190. if (record === undefined) {
  9191. // No record, but maybe the token is scoped to this injector. Look for an injectable
  9192. // def with a scope matching this injector.
  9193. const def = couldBeInjectableType(token) && getInjectableDef(token);
  9194. if (def && this.injectableDefInScope(def)) {
  9195. // Found an injectable def and it's scoped to this injector. Pretend as if it was here
  9196. // all along.
  9197. record = makeRecord(injectableDefOrInjectorDefFactory(token), NOT_YET);
  9198. }
  9199. else {
  9200. record = null;
  9201. }
  9202. this.records.set(token, record);
  9203. }
  9204. // If a record was found, get the instance for it and return it.
  9205. if (record != null /* NOT null || undefined */) {
  9206. return this.hydrate(token, record);
  9207. }
  9208. }
  9209. // Select the next injector based on the Self flag - if self is set, the next injector is
  9210. // the NullInjector, otherwise it's the parent.
  9211. const nextInjector = !(flags & InjectFlags.Self) ? this.parent : getNullInjector();
  9212. // Set the notFoundValue based on the Optional flag - if optional is set and notFoundValue
  9213. // is undefined, the value is null, otherwise it's the notFoundValue.
  9214. notFoundValue = (flags & InjectFlags.Optional) && notFoundValue === THROW_IF_NOT_FOUND ?
  9215. null :
  9216. notFoundValue;
  9217. return nextInjector.get(token, notFoundValue);
  9218. }
  9219. catch (e) {
  9220. if (e.name === 'NullInjectorError') {
  9221. const path = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
  9222. path.unshift(stringify(token));
  9223. if (previousInjector) {
  9224. // We still have a parent injector, keep throwing
  9225. throw e;
  9226. }
  9227. else {
  9228. // Format & throw the final error message when we don't have any previous injector
  9229. return catchInjectorError(e, token, 'R3InjectorError', this.source);
  9230. }
  9231. }
  9232. else {
  9233. throw e;
  9234. }
  9235. }
  9236. finally {
  9237. // Lastly, restore the previous injection context.
  9238. setInjectImplementation(previousInjectImplementation);
  9239. setCurrentInjector(previousInjector);
  9240. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  9241. }
  9242. }
  9243. /** @internal */
  9244. resolveInjectorInitializers() {
  9245. const previousInjector = setCurrentInjector(this);
  9246. const previousInjectImplementation = setInjectImplementation(undefined);
  9247. let prevInjectContext;
  9248. if (ngDevMode) {
  9249. prevInjectContext = setInjectorProfilerContext({ injector: this, token: null });
  9250. }
  9251. try {
  9252. const initializers = this.get(ENVIRONMENT_INITIALIZER.multi, EMPTY_ARRAY, InjectFlags.Self);
  9253. if (ngDevMode && !Array.isArray(initializers)) {
  9254. throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' +
  9255. `(expected an array, but got ${typeof initializers}). ` +
  9256. 'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' +
  9257. '`multi: true` provider.');
  9258. }
  9259. for (const initializer of initializers) {
  9260. initializer();
  9261. }
  9262. }
  9263. finally {
  9264. setCurrentInjector(previousInjector);
  9265. setInjectImplementation(previousInjectImplementation);
  9266. ngDevMode && setInjectorProfilerContext(prevInjectContext);
  9267. }
  9268. }
  9269. toString() {
  9270. const tokens = [];
  9271. const records = this.records;
  9272. for (const token of records.keys()) {
  9273. tokens.push(stringify(token));
  9274. }
  9275. return `R3Injector[${tokens.join(', ')}]`;
  9276. }
  9277. assertNotDestroyed() {
  9278. if (this._destroyed) {
  9279. throw new RuntimeError(205 /* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED */, ngDevMode && 'Injector has already been destroyed.');
  9280. }
  9281. }
  9282. /**
  9283. * Process a `SingleProvider` and add it.
  9284. */
  9285. processProvider(provider) {
  9286. // Determine the token from the provider. Either it's its own token, or has a {provide: ...}
  9287. // property.
  9288. provider = resolveForwardRef(provider);
  9289. let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider && provider.provide);
  9290. // Construct a `Record` for the provider.
  9291. const record = providerToRecord(provider);
  9292. if (ngDevMode) {
  9293. runInInjectorProfilerContext(this, token, () => {
  9294. // Emit InjectorProfilerEventType.Create if provider is a value provider because
  9295. // these are the only providers that do not go through the value hydration logic
  9296. // where this event would normally be emitted from.
  9297. if (isValueProvider(provider)) {
  9298. emitInstanceCreatedByInjectorEvent(provider.useValue);
  9299. }
  9300. emitProviderConfiguredEvent(provider);
  9301. });
  9302. }
  9303. if (!isTypeProvider(provider) && provider.multi === true) {
  9304. // If the provider indicates that it's a multi-provider, process it specially.
  9305. // First check whether it's been defined already.
  9306. let multiRecord = this.records.get(token);
  9307. if (multiRecord) {
  9308. // It has. Throw a nice error if
  9309. if (ngDevMode && multiRecord.multi === undefined) {
  9310. throwMixedMultiProviderError();
  9311. }
  9312. }
  9313. else {
  9314. multiRecord = makeRecord(undefined, NOT_YET, true);
  9315. multiRecord.factory = () => injectArgs(multiRecord.multi);
  9316. this.records.set(token, multiRecord);
  9317. }
  9318. token = provider;
  9319. multiRecord.multi.push(provider);
  9320. }
  9321. else {
  9322. const existing = this.records.get(token);
  9323. if (ngDevMode && existing && existing.multi !== undefined) {
  9324. throwMixedMultiProviderError();
  9325. }
  9326. }
  9327. this.records.set(token, record);
  9328. }
  9329. hydrate(token, record) {
  9330. if (ngDevMode && record.value === CIRCULAR) {
  9331. throwCyclicDependencyError(stringify(token));
  9332. }
  9333. else if (record.value === NOT_YET) {
  9334. record.value = CIRCULAR;
  9335. if (ngDevMode) {
  9336. runInInjectorProfilerContext(this, token, () => {
  9337. record.value = record.factory();
  9338. emitInstanceCreatedByInjectorEvent(record.value);
  9339. });
  9340. }
  9341. else {
  9342. record.value = record.factory();
  9343. }
  9344. }
  9345. if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) {
  9346. this._ngOnDestroyHooks.add(record.value);
  9347. }
  9348. return record.value;
  9349. }
  9350. injectableDefInScope(def) {
  9351. if (!def.providedIn) {
  9352. return false;
  9353. }
  9354. const providedIn = resolveForwardRef(def.providedIn);
  9355. if (typeof providedIn === 'string') {
  9356. return providedIn === 'any' || (this.scopes.has(providedIn));
  9357. }
  9358. else {
  9359. return this.injectorDefTypes.has(providedIn);
  9360. }
  9361. }
  9362. removeOnDestroy(callback) {
  9363. const destroyCBIdx = this._onDestroyHooks.indexOf(callback);
  9364. if (destroyCBIdx !== -1) {
  9365. this._onDestroyHooks.splice(destroyCBIdx, 1);
  9366. }
  9367. }
  9368. }
  9369. function injectableDefOrInjectorDefFactory(token) {
  9370. // Most tokens will have an injectable def directly on them, which specifies a factory directly.
  9371. const injectableDef = getInjectableDef(token);
  9372. const factory = injectableDef !== null ? injectableDef.factory : getFactoryDef(token);
  9373. if (factory !== null) {
  9374. return factory;
  9375. }
  9376. // InjectionTokens should have an injectable def (ɵprov) and thus should be handled above.
  9377. // If it's missing that, it's an error.
  9378. if (token instanceof InjectionToken) {
  9379. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Token ${stringify(token)} is missing a ɵprov definition.`);
  9380. }
  9381. // Undecorated types can sometimes be created if they have no constructor arguments.
  9382. if (token instanceof Function) {
  9383. return getUndecoratedInjectableFactory(token);
  9384. }
  9385. // There was no way to resolve a factory for this token.
  9386. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && 'unreachable');
  9387. }
  9388. function getUndecoratedInjectableFactory(token) {
  9389. // If the token has parameters then it has dependencies that we cannot resolve implicitly.
  9390. const paramLength = token.length;
  9391. if (paramLength > 0) {
  9392. const args = newArray(paramLength, '?');
  9393. throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).`);
  9394. }
  9395. // The constructor function appears to have no parameters.
  9396. // This might be because it inherits from a super-class. In which case, use an injectable
  9397. // def from an ancestor if there is one.
  9398. // Otherwise this really is a simple class with no dependencies, so return a factory that
  9399. // just instantiates the zero-arg constructor.
  9400. const inheritedInjectableDef = getInheritedInjectableDef(token);
  9401. if (inheritedInjectableDef !== null) {
  9402. return () => inheritedInjectableDef.factory(token);
  9403. }
  9404. else {
  9405. return () => new token();
  9406. }
  9407. }
  9408. function providerToRecord(provider) {
  9409. if (isValueProvider(provider)) {
  9410. return makeRecord(undefined, provider.useValue);
  9411. }
  9412. else {
  9413. const factory = providerToFactory(provider);
  9414. return makeRecord(factory, NOT_YET);
  9415. }
  9416. }
  9417. /**
  9418. * Converts a `SingleProvider` into a factory function.
  9419. *
  9420. * @param provider provider to convert to factory
  9421. */
  9422. function providerToFactory(provider, ngModuleType, providers) {
  9423. let factory = undefined;
  9424. if (ngDevMode && isEnvironmentProviders(provider)) {
  9425. throwInvalidProviderError(undefined, providers, provider);
  9426. }
  9427. if (isTypeProvider(provider)) {
  9428. const unwrappedProvider = resolveForwardRef(provider);
  9429. return getFactoryDef(unwrappedProvider) || injectableDefOrInjectorDefFactory(unwrappedProvider);
  9430. }
  9431. else {
  9432. if (isValueProvider(provider)) {
  9433. factory = () => resolveForwardRef(provider.useValue);
  9434. }
  9435. else if (isFactoryProvider(provider)) {
  9436. factory = () => provider.useFactory(...injectArgs(provider.deps || []));
  9437. }
  9438. else if (isExistingProvider(provider)) {
  9439. factory = () => ɵɵinject(resolveForwardRef(provider.useExisting));
  9440. }
  9441. else {
  9442. const classRef = resolveForwardRef(provider &&
  9443. (provider.useClass || provider.provide));
  9444. if (ngDevMode && !classRef) {
  9445. throwInvalidProviderError(ngModuleType, providers, provider);
  9446. }
  9447. if (hasDeps(provider)) {
  9448. factory = () => new (classRef)(...injectArgs(provider.deps));
  9449. }
  9450. else {
  9451. return getFactoryDef(classRef) || injectableDefOrInjectorDefFactory(classRef);
  9452. }
  9453. }
  9454. }
  9455. return factory;
  9456. }
  9457. function makeRecord(factory, value, multi = false) {
  9458. return {
  9459. factory: factory,
  9460. value: value,
  9461. multi: multi ? [] : undefined,
  9462. };
  9463. }
  9464. function hasDeps(value) {
  9465. return !!value.deps;
  9466. }
  9467. function hasOnDestroy(value) {
  9468. return value !== null && typeof value === 'object' &&
  9469. typeof value.ngOnDestroy === 'function';
  9470. }
  9471. function couldBeInjectableType(value) {
  9472. return (typeof value === 'function') ||
  9473. (typeof value === 'object' && value instanceof InjectionToken);
  9474. }
  9475. function forEachSingleProvider(providers, fn) {
  9476. for (const provider of providers) {
  9477. if (Array.isArray(provider)) {
  9478. forEachSingleProvider(provider, fn);
  9479. }
  9480. else if (provider && isEnvironmentProviders(provider)) {
  9481. forEachSingleProvider(provider.ɵproviders, fn);
  9482. }
  9483. else {
  9484. fn(provider);
  9485. }
  9486. }
  9487. }
  9488. /**
  9489. * A [DI token](guide/glossary#di-token "DI token definition") representing a string ID, used
  9490. * primarily for prefixing application attributes and CSS styles when
  9491. * {@link ViewEncapsulation#Emulated} is being used.
  9492. *
  9493. * The token is needed in cases when multiple applications are bootstrapped on a page
  9494. * (for example, using `bootstrapApplication` calls). In this case, ensure that those applications
  9495. * have different `APP_ID` value setup. For example:
  9496. *
  9497. * ```
  9498. * bootstrapApplication(ComponentA, {
  9499. * providers: [
  9500. * { provide: APP_ID, useValue: 'app-a' },
  9501. * // ... other providers ...
  9502. * ]
  9503. * });
  9504. *
  9505. * bootstrapApplication(ComponentB, {
  9506. * providers: [
  9507. * { provide: APP_ID, useValue: 'app-b' },
  9508. * // ... other providers ...
  9509. * ]
  9510. * });
  9511. * ```
  9512. *
  9513. * By default, when there is only one application bootstrapped, you don't need to provide the
  9514. * `APP_ID` token (the `ng` will be used as an app ID).
  9515. *
  9516. * @publicApi
  9517. */
  9518. const APP_ID = new InjectionToken('AppId', {
  9519. providedIn: 'root',
  9520. factory: () => DEFAULT_APP_ID,
  9521. });
  9522. /** Default value of the `APP_ID` token. */
  9523. const DEFAULT_APP_ID = 'ng';
  9524. /**
  9525. * A function that is executed when a platform is initialized.
  9526. * @publicApi
  9527. */
  9528. const PLATFORM_INITIALIZER = new InjectionToken('Platform Initializer');
  9529. /**
  9530. * A token that indicates an opaque platform ID.
  9531. * @publicApi
  9532. */
  9533. const PLATFORM_ID = new InjectionToken('Platform ID', {
  9534. providedIn: 'platform',
  9535. factory: () => 'unknown', // set a default platform name, when none set explicitly
  9536. });
  9537. /**
  9538. * A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of
  9539. * the application
  9540. * @publicApi
  9541. * @deprecated
  9542. */
  9543. const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL');
  9544. // We keep this token here, rather than the animations package, so that modules that only care
  9545. // about which animations module is loaded (e.g. the CDK) can retrieve it without having to
  9546. // include extra dependencies. See #44970 for more context.
  9547. /**
  9548. * A [DI token](guide/glossary#di-token "DI token definition") that indicates which animations
  9549. * module has been loaded.
  9550. * @publicApi
  9551. */
  9552. const ANIMATION_MODULE_TYPE = new InjectionToken('AnimationModuleType');
  9553. // TODO(crisbeto): link to CSP guide here.
  9554. /**
  9555. * Token used to configure the [Content Security Policy](https://web.dev/strict-csp/) nonce that
  9556. * Angular will apply when inserting inline styles. If not provided, Angular will look up its value
  9557. * from the `ngCspNonce` attribute of the application root node.
  9558. *
  9559. * @publicApi
  9560. */
  9561. const CSP_NONCE = new InjectionToken('CSP nonce', {
  9562. providedIn: 'root',
  9563. factory: () => {
  9564. // Ideally we wouldn't have to use `querySelector` here since we know that the nonce will be on
  9565. // the root node, but because the token value is used in renderers, it has to be available
  9566. // *very* early in the bootstrapping process. This should be a fairly shallow search, because
  9567. // the app won't have been added to the DOM yet. Some approaches that were considered:
  9568. // 1. Find the root node through `ApplicationRef.components[i].location` - normally this would
  9569. // be enough for our purposes, but the token is injected very early so the `components` array
  9570. // isn't populated yet.
  9571. // 2. Find the root `LView` through the current `LView` - renderers are a prerequisite to
  9572. // creating the `LView`. This means that no `LView` will have been entered when this factory is
  9573. // invoked for the root component.
  9574. // 3. Have the token factory return `() => string` which is invoked when a nonce is requested -
  9575. // the slightly later execution does allow us to get an `LView` reference, but the fact that
  9576. // it is a function means that it could be executed at *any* time (including immediately) which
  9577. // may lead to weird bugs.
  9578. // 4. Have the `ComponentFactory` read the attribute and provide it to the injector under the
  9579. // hood - has the same problem as #1 and #2 in that the renderer is used to query for the root
  9580. // node and the nonce value needs to be available when the renderer is created.
  9581. return getDocument().body?.querySelector('[ngCspNonce]')?.getAttribute('ngCspNonce') || null;
  9582. },
  9583. });
  9584. /**
  9585. * Internal token to collect all SSR-related features enabled for this application.
  9586. *
  9587. * Note: the token is in `core` to let other packages register features (the `core`
  9588. * package is imported in other packages).
  9589. */
  9590. const ENABLED_SSR_FEATURES = new InjectionToken((typeof ngDevMode === 'undefined' || ngDevMode) ? 'ENABLED_SSR_FEATURES' : '', {
  9591. providedIn: 'root',
  9592. factory: () => new Set(),
  9593. });
  9594. /**
  9595. * Create a `StateKey<T>` that can be used to store value of type T with `TransferState`.
  9596. *
  9597. * Example:
  9598. *
  9599. * ```
  9600. * const COUNTER_KEY = makeStateKey<number>('counter');
  9601. * let value = 10;
  9602. *
  9603. * transferState.set(COUNTER_KEY, value);
  9604. * ```
  9605. *
  9606. * @publicApi
  9607. */
  9608. function makeStateKey(key) {
  9609. return key;
  9610. }
  9611. function initTransferState() {
  9612. const transferState = new TransferState();
  9613. if (inject(PLATFORM_ID) === 'browser') {
  9614. transferState.store = retrieveTransferredState(getDocument(), inject(APP_ID));
  9615. }
  9616. return transferState;
  9617. }
  9618. /**
  9619. * A key value store that is transferred from the application on the server side to the application
  9620. * on the client side.
  9621. *
  9622. * The `TransferState` is available as an injectable token.
  9623. * On the client, just inject this token using DI and use it, it will be lazily initialized.
  9624. * On the server it's already included if `renderApplication` function is used. Otherwise, import
  9625. * the `ServerTransferStateModule` module to make the `TransferState` available.
  9626. *
  9627. * The values in the store are serialized/deserialized using JSON.stringify/JSON.parse. So only
  9628. * boolean, number, string, null and non-class objects will be serialized and deserialized in a
  9629. * non-lossy manner.
  9630. *
  9631. * @publicApi
  9632. */
  9633. class TransferState {
  9634. constructor() {
  9635. /** @internal */
  9636. this.store = {};
  9637. this.onSerializeCallbacks = {};
  9638. }
  9639. /** @nocollapse */
  9640. static { this.ɵprov =
  9641. /** @pureOrBreakMyCode */ ɵɵdefineInjectable({
  9642. token: TransferState,
  9643. providedIn: 'root',
  9644. factory: initTransferState,
  9645. }); }
  9646. /**
  9647. * Get the value corresponding to a key. Return `defaultValue` if key is not found.
  9648. */
  9649. get(key, defaultValue) {
  9650. return this.store[key] !== undefined ? this.store[key] : defaultValue;
  9651. }
  9652. /**
  9653. * Set the value corresponding to a key.
  9654. */
  9655. set(key, value) {
  9656. this.store[key] = value;
  9657. }
  9658. /**
  9659. * Remove a key from the store.
  9660. */
  9661. remove(key) {
  9662. delete this.store[key];
  9663. }
  9664. /**
  9665. * Test whether a key exists in the store.
  9666. */
  9667. hasKey(key) {
  9668. return this.store.hasOwnProperty(key);
  9669. }
  9670. /**
  9671. * Indicates whether the state is empty.
  9672. */
  9673. get isEmpty() {
  9674. return Object.keys(this.store).length === 0;
  9675. }
  9676. /**
  9677. * Register a callback to provide the value for a key when `toJson` is called.
  9678. */
  9679. onSerialize(key, callback) {
  9680. this.onSerializeCallbacks[key] = callback;
  9681. }
  9682. /**
  9683. * Serialize the current state of the store to JSON.
  9684. */
  9685. toJson() {
  9686. // Call the onSerialize callbacks and put those values into the store.
  9687. for (const key in this.onSerializeCallbacks) {
  9688. if (this.onSerializeCallbacks.hasOwnProperty(key)) {
  9689. try {
  9690. this.store[key] = this.onSerializeCallbacks[key]();
  9691. }
  9692. catch (e) {
  9693. console.warn('Exception in onSerialize callback: ', e);
  9694. }
  9695. }
  9696. }
  9697. // Escape script tag to avoid break out of <script> tag in serialized output.
  9698. // Encoding of `<` is the same behaviour as G3 script_builders.
  9699. return JSON.stringify(this.store).replace(/</g, '\\u003C');
  9700. }
  9701. }
  9702. function retrieveTransferredState(doc, appId) {
  9703. // Locate the script tag with the JSON data transferred from the server.
  9704. // The id of the script tag is set to the Angular appId + 'state'.
  9705. const script = doc.getElementById(appId + '-state');
  9706. if (script?.textContent) {
  9707. try {
  9708. // Avoid using any here as it triggers lint errors in google3 (any is not allowed).
  9709. // Decoding of `<` is done of the box by browsers and node.js, same behaviour as G3
  9710. // script_builders.
  9711. return JSON.parse(script.textContent);
  9712. }
  9713. catch (e) {
  9714. console.warn('Exception while restoring TransferState for app ' + appId, e);
  9715. }
  9716. }
  9717. return {};
  9718. }
  9719. /** Encodes that the node lookup should start from the host node of this component. */
  9720. const REFERENCE_NODE_HOST = 'h';
  9721. /** Encodes that the node lookup should start from the document body node. */
  9722. const REFERENCE_NODE_BODY = 'b';
  9723. /**
  9724. * Describes navigation steps that the runtime logic need to perform,
  9725. * starting from a given (known) element.
  9726. */
  9727. var NodeNavigationStep;
  9728. (function (NodeNavigationStep) {
  9729. NodeNavigationStep["FirstChild"] = "f";
  9730. NodeNavigationStep["NextSibling"] = "n";
  9731. })(NodeNavigationStep || (NodeNavigationStep = {}));
  9732. /**
  9733. * Keys within serialized view data structure to represent various
  9734. * parts. See the `SerializedView` interface below for additional information.
  9735. */
  9736. const ELEMENT_CONTAINERS = 'e';
  9737. const TEMPLATES = 't';
  9738. const CONTAINERS = 'c';
  9739. const MULTIPLIER = 'x';
  9740. const NUM_ROOT_NODES = 'r';
  9741. const TEMPLATE_ID = 'i'; // as it's also an "id"
  9742. const NODES = 'n';
  9743. const DISCONNECTED_NODES = 'd';
  9744. /**
  9745. * The name of the key used in the TransferState collection,
  9746. * where hydration information is located.
  9747. */
  9748. const TRANSFER_STATE_TOKEN_ID = '__ɵnghData__';
  9749. /**
  9750. * Lookup key used to reference DOM hydration data (ngh) in `TransferState`.
  9751. */
  9752. const NGH_DATA_KEY = makeStateKey(TRANSFER_STATE_TOKEN_ID);
  9753. /**
  9754. * The name of the attribute that would be added to host component
  9755. * nodes and contain a reference to a particular slot in transferred
  9756. * state that contains the necessary hydration info for this component.
  9757. */
  9758. const NGH_ATTR_NAME = 'ngh';
  9759. /**
  9760. * Marker used in a comment node to ensure hydration content integrity
  9761. */
  9762. const SSR_CONTENT_INTEGRITY_MARKER = 'nghm';
  9763. /**
  9764. * Reference to a function that reads `ngh` attribute value from a given RNode
  9765. * and retrieves hydration information from the TransferState using that value
  9766. * as an index. Returns `null` by default, when hydration is not enabled.
  9767. *
  9768. * @param rNode Component's host element.
  9769. * @param injector Injector that this component has access to.
  9770. * @param isRootView Specifies whether we trying to read hydration info for the root view.
  9771. */
  9772. let _retrieveHydrationInfoImpl = (rNode, injector, isRootView) => null;
  9773. function retrieveHydrationInfoImpl(rNode, injector, isRootView = false) {
  9774. let nghAttrValue = rNode.getAttribute(NGH_ATTR_NAME);
  9775. if (nghAttrValue == null)
  9776. return null;
  9777. // For cases when a root component also acts as an anchor node for a ViewContainerRef
  9778. // (for example, when ViewContainerRef is injected in a root component), there is a need
  9779. // to serialize information about the component itself, as well as an LContainer that
  9780. // represents this ViewContainerRef. Effectively, we need to serialize 2 pieces of info:
  9781. // (1) hydration info for the root component itself and (2) hydration info for the
  9782. // ViewContainerRef instance (an LContainer). Each piece of information is included into
  9783. // the hydration data (in the TransferState object) separately, thus we end up with 2 ids.
  9784. // Since we only have 1 root element, we encode both bits of info into a single string:
  9785. // ids are separated by the `|` char (e.g. `10|25`, where `10` is the ngh for a component view
  9786. // and 25 is the `ngh` for a root view which holds LContainer).
  9787. const [componentViewNgh, rootViewNgh] = nghAttrValue.split('|');
  9788. nghAttrValue = isRootView ? rootViewNgh : componentViewNgh;
  9789. if (!nghAttrValue)
  9790. return null;
  9791. // We've read one of the ngh ids, keep the remaining one, so that
  9792. // we can set it back on the DOM element.
  9793. const remainingNgh = isRootView ? componentViewNgh : (rootViewNgh ? `|${rootViewNgh}` : '');
  9794. let data = {};
  9795. // An element might have an empty `ngh` attribute value (e.g. `<comp ngh="" />`),
  9796. // which means that no special annotations are required. Do not attempt to read
  9797. // from the TransferState in this case.
  9798. if (nghAttrValue !== '') {
  9799. const transferState = injector.get(TransferState, null, { optional: true });
  9800. if (transferState !== null) {
  9801. const nghData = transferState.get(NGH_DATA_KEY, []);
  9802. // The nghAttrValue is always a number referencing an index
  9803. // in the hydration TransferState data.
  9804. data = nghData[Number(nghAttrValue)];
  9805. // If the `ngh` attribute exists and has a non-empty value,
  9806. // the hydration info *must* be present in the TransferState.
  9807. // If there is no data for some reasons, this is an error.
  9808. ngDevMode && assertDefined(data, 'Unable to retrieve hydration info from the TransferState.');
  9809. }
  9810. }
  9811. const dehydratedView = {
  9812. data,
  9813. firstChild: rNode.firstChild ?? null,
  9814. };
  9815. if (isRootView) {
  9816. // If there is hydration info present for the root view, it means that there was
  9817. // a ViewContainerRef injected in the root component. The root component host element
  9818. // acted as an anchor node in this scenario. As a result, the DOM nodes that represent
  9819. // embedded views in this ViewContainerRef are located as siblings to the host node,
  9820. // i.e. `<app-root /><#VIEW1><#VIEW2>...<!--container-->`. In this case, the current
  9821. // node becomes the first child of this root view and the next sibling is the first
  9822. // element in the DOM segment.
  9823. dehydratedView.firstChild = rNode;
  9824. // We use `0` here, since this is the slot (right after the HEADER_OFFSET)
  9825. // where a component LView or an LContainer is located in a root LView.
  9826. setSegmentHead(dehydratedView, 0, rNode.nextSibling);
  9827. }
  9828. if (remainingNgh) {
  9829. // If we have only used one of the ngh ids, store the remaining one
  9830. // back on this RNode.
  9831. rNode.setAttribute(NGH_ATTR_NAME, remainingNgh);
  9832. }
  9833. else {
  9834. // The `ngh` attribute is cleared from the DOM node now
  9835. // that the data has been retrieved for all indices.
  9836. rNode.removeAttribute(NGH_ATTR_NAME);
  9837. }
  9838. // Note: don't check whether this node was claimed for hydration,
  9839. // because this node might've been previously claimed while processing
  9840. // template instructions.
  9841. ngDevMode && markRNodeAsClaimedByHydration(rNode, /* checkIfAlreadyClaimed */ false);
  9842. ngDevMode && ngDevMode.hydratedComponents++;
  9843. return dehydratedView;
  9844. }
  9845. /**
  9846. * Sets the implementation for the `retrieveHydrationInfo` function.
  9847. */
  9848. function enableRetrieveHydrationInfoImpl() {
  9849. _retrieveHydrationInfoImpl = retrieveHydrationInfoImpl;
  9850. }
  9851. /**
  9852. * Retrieves hydration info by reading the value from the `ngh` attribute
  9853. * and accessing a corresponding slot in TransferState storage.
  9854. */
  9855. function retrieveHydrationInfo(rNode, injector, isRootView = false) {
  9856. return _retrieveHydrationInfoImpl(rNode, injector, isRootView);
  9857. }
  9858. /**
  9859. * Retrieves the necessary object from a given ViewRef to serialize:
  9860. * - an LView for component views
  9861. * - an LContainer for cases when component acts as a ViewContainerRef anchor
  9862. * - `null` in case of an embedded view
  9863. */
  9864. function getLNodeForHydration(viewRef) {
  9865. // Reading an internal field from `ViewRef` instance.
  9866. let lView = viewRef._lView;
  9867. const tView = lView[TVIEW];
  9868. // A registered ViewRef might represent an instance of an
  9869. // embedded view, in which case we do not need to annotate it.
  9870. if (tView.type === 2 /* TViewType.Embedded */) {
  9871. return null;
  9872. }
  9873. // Check if it's a root view and if so, retrieve component's
  9874. // LView from the first slot after the header.
  9875. if (isRootView(lView)) {
  9876. lView = lView[HEADER_OFFSET];
  9877. }
  9878. return lView;
  9879. }
  9880. function getTextNodeContent(node) {
  9881. return node.textContent?.replace(/\s/gm, '');
  9882. }
  9883. /**
  9884. * Restores text nodes and separators into the DOM that were lost during SSR
  9885. * serialization. The hydration process replaces empty text nodes and text
  9886. * nodes that are immediately adjacent to other text nodes with comment nodes
  9887. * that this method filters on to restore those missing nodes that the
  9888. * hydration process is expecting to be present.
  9889. *
  9890. * @param node The app's root HTML Element
  9891. */
  9892. function processTextNodeMarkersBeforeHydration(node) {
  9893. const doc = getDocument();
  9894. const commentNodesIterator = doc.createNodeIterator(node, NodeFilter.SHOW_COMMENT, {
  9895. acceptNode(node) {
  9896. const content = getTextNodeContent(node);
  9897. const isTextNodeMarker = content === "ngetn" /* TextNodeMarker.EmptyNode */ || content === "ngtns" /* TextNodeMarker.Separator */;
  9898. return isTextNodeMarker ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
  9899. }
  9900. });
  9901. let currentNode;
  9902. // We cannot modify the DOM while using the commentIterator,
  9903. // because it throws off the iterator state.
  9904. // So we collect all marker nodes first and then follow up with
  9905. // applying the changes to the DOM: either inserting an empty node
  9906. // or just removing the marker if it was used as a separator.
  9907. const nodes = [];
  9908. while (currentNode = commentNodesIterator.nextNode()) {
  9909. nodes.push(currentNode);
  9910. }
  9911. for (const node of nodes) {
  9912. if (node.textContent === "ngetn" /* TextNodeMarker.EmptyNode */) {
  9913. node.replaceWith(doc.createTextNode(''));
  9914. }
  9915. else {
  9916. node.remove();
  9917. }
  9918. }
  9919. }
  9920. /**
  9921. * Marks a node as "claimed" by hydration process.
  9922. * This is needed to make assessments in tests whether
  9923. * the hydration process handled all nodes.
  9924. */
  9925. function markRNodeAsClaimedByHydration(node, checkIfAlreadyClaimed = true) {
  9926. if (!ngDevMode) {
  9927. throw new Error('Calling `markRNodeAsClaimedByHydration` in prod mode ' +
  9928. 'is not supported and likely a mistake.');
  9929. }
  9930. if (checkIfAlreadyClaimed && isRNodeClaimedForHydration(node)) {
  9931. throw new Error('Trying to claim a node, which was claimed already.');
  9932. }
  9933. node.__claimed = true;
  9934. ngDevMode.hydratedNodes++;
  9935. }
  9936. function isRNodeClaimedForHydration(node) {
  9937. return !!node.__claimed;
  9938. }
  9939. function setSegmentHead(hydrationInfo, index, node) {
  9940. hydrationInfo.segmentHeads ??= {};
  9941. hydrationInfo.segmentHeads[index] = node;
  9942. }
  9943. function getSegmentHead(hydrationInfo, index) {
  9944. return hydrationInfo.segmentHeads?.[index] ?? null;
  9945. }
  9946. /**
  9947. * Returns the size of an <ng-container>, using either the information
  9948. * serialized in `ELEMENT_CONTAINERS` (element container size) or by
  9949. * computing the sum of root nodes in all dehydrated views in a given
  9950. * container (in case this `<ng-container>` was also used as a view
  9951. * container host node, e.g. <ng-container *ngIf>).
  9952. */
  9953. function getNgContainerSize(hydrationInfo, index) {
  9954. const data = hydrationInfo.data;
  9955. let size = data[ELEMENT_CONTAINERS]?.[index] ?? null;
  9956. // If there is no serialized information available in the `ELEMENT_CONTAINERS` slot,
  9957. // check if we have info about view containers at this location (e.g.
  9958. // `<ng-container *ngIf>`) and use container size as a number of root nodes in this
  9959. // element container.
  9960. if (size === null && data[CONTAINERS]?.[index]) {
  9961. size = calcSerializedContainerSize(hydrationInfo, index);
  9962. }
  9963. return size;
  9964. }
  9965. function getSerializedContainerViews(hydrationInfo, index) {
  9966. return hydrationInfo.data[CONTAINERS]?.[index] ?? null;
  9967. }
  9968. /**
  9969. * Computes the size of a serialized container (the number of root nodes)
  9970. * by calculating the sum of root nodes in all dehydrated views in this container.
  9971. */
  9972. function calcSerializedContainerSize(hydrationInfo, index) {
  9973. const views = getSerializedContainerViews(hydrationInfo, index) ?? [];
  9974. let numNodes = 0;
  9975. for (let view of views) {
  9976. numNodes += view[NUM_ROOT_NODES] * (view[MULTIPLIER] ?? 1);
  9977. }
  9978. return numNodes;
  9979. }
  9980. /**
  9981. * Checks whether a node is annotated as "disconnected", i.e. not present
  9982. * in the DOM at serialization time. We should not attempt hydration for
  9983. * such nodes and instead, use a regular "creation mode".
  9984. */
  9985. function isDisconnectedNode$1(hydrationInfo, index) {
  9986. // Check if we are processing disconnected info for the first time.
  9987. if (typeof hydrationInfo.disconnectedNodes === 'undefined') {
  9988. const nodeIds = hydrationInfo.data[DISCONNECTED_NODES];
  9989. hydrationInfo.disconnectedNodes = nodeIds ? (new Set(nodeIds)) : null;
  9990. }
  9991. return !!hydrationInfo.disconnectedNodes?.has(index);
  9992. }
  9993. /**
  9994. * Represents a component created by a `ComponentFactory`.
  9995. * Provides access to the component instance and related objects,
  9996. * and provides the means of destroying the instance.
  9997. *
  9998. * @publicApi
  9999. */
  10000. class ComponentRef$1 {
  10001. }
  10002. /**
  10003. * Base class for a factory that can create a component dynamically.
  10004. * Instantiate a factory for a given type of component with `resolveComponentFactory()`.
  10005. * Use the resulting `ComponentFactory.create()` method to create a component of that type.
  10006. *
  10007. * @see [Dynamic Components](guide/dynamic-component-loader)
  10008. *
  10009. * @publicApi
  10010. *
  10011. * @deprecated Angular no longer requires Component factories. Please use other APIs where
  10012. * Component class can be used directly.
  10013. */
  10014. class ComponentFactory$1 {
  10015. }
  10016. function noComponentFactoryError(component) {
  10017. const error = Error(`No component factory found for ${stringify(component)}.`);
  10018. error[ERROR_COMPONENT] = component;
  10019. return error;
  10020. }
  10021. const ERROR_COMPONENT = 'ngComponent';
  10022. function getComponent$1(error) {
  10023. return error[ERROR_COMPONENT];
  10024. }
  10025. class _NullComponentFactoryResolver {
  10026. resolveComponentFactory(component) {
  10027. throw noComponentFactoryError(component);
  10028. }
  10029. }
  10030. /**
  10031. * A simple registry that maps `Components` to generated `ComponentFactory` classes
  10032. * that can be used to create instances of components.
  10033. * Use to obtain the factory for a given component type,
  10034. * then use the factory's `create()` method to create a component of that type.
  10035. *
  10036. * Note: since v13, dynamic component creation via
  10037. * [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent)
  10038. * does **not** require resolving component factory: component class can be used directly.
  10039. *
  10040. * @publicApi
  10041. *
  10042. * @deprecated Angular no longer requires Component factories. Please use other APIs where
  10043. * Component class can be used directly.
  10044. */
  10045. class ComponentFactoryResolver$1 {
  10046. static { this.NULL = ( /* @__PURE__ */new _NullComponentFactoryResolver()); }
  10047. }
  10048. /**
  10049. * Creates an ElementRef from the most recent node.
  10050. *
  10051. * @returns The ElementRef instance to use
  10052. */
  10053. function injectElementRef() {
  10054. return createElementRef(getCurrentTNode(), getLView());
  10055. }
  10056. /**
  10057. * Creates an ElementRef given a node.
  10058. *
  10059. * @param tNode The node for which you'd like an ElementRef
  10060. * @param lView The view to which the node belongs
  10061. * @returns The ElementRef instance to use
  10062. */
  10063. function createElementRef(tNode, lView) {
  10064. return new ElementRef(getNativeByTNode(tNode, lView));
  10065. }
  10066. /**
  10067. * A wrapper around a native element inside of a View.
  10068. *
  10069. * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
  10070. * element.
  10071. *
  10072. * @security Permitting direct access to the DOM can make your application more vulnerable to
  10073. * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
  10074. * [Security Guide](https://g.co/ng/security).
  10075. *
  10076. * @publicApi
  10077. */
  10078. // Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
  10079. // i.e. users have to ask for what they need. With that, we can build better analysis tools
  10080. // and could do better codegen in the future.
  10081. class ElementRef {
  10082. constructor(nativeElement) {
  10083. this.nativeElement = nativeElement;
  10084. }
  10085. /**
  10086. * @internal
  10087. * @nocollapse
  10088. */
  10089. static { this.__NG_ELEMENT_ID__ = injectElementRef; }
  10090. }
  10091. /**
  10092. * Unwraps `ElementRef` and return the `nativeElement`.
  10093. *
  10094. * @param value value to unwrap
  10095. * @returns `nativeElement` if `ElementRef` otherwise returns value as is.
  10096. */
  10097. function unwrapElementRef(value) {
  10098. return value instanceof ElementRef ? value.nativeElement : value;
  10099. }
  10100. /**
  10101. * Creates and initializes a custom renderer that implements the `Renderer2` base class.
  10102. *
  10103. * @publicApi
  10104. */
  10105. class RendererFactory2 {
  10106. }
  10107. /**
  10108. * Extend this base class to implement custom rendering. By default, Angular
  10109. * renders a template into DOM. You can use custom rendering to intercept
  10110. * rendering calls, or to render to something other than DOM.
  10111. *
  10112. * Create your custom renderer using `RendererFactory2`.
  10113. *
  10114. * Use a custom renderer to bypass Angular's templating and
  10115. * make custom UI changes that can't be expressed declaratively.
  10116. * For example if you need to set a property or an attribute whose name is
  10117. * not statically known, use the `setProperty()` or
  10118. * `setAttribute()` method.
  10119. *
  10120. * @publicApi
  10121. */
  10122. class Renderer2 {
  10123. constructor() {
  10124. /**
  10125. * If null or undefined, the view engine won't call it.
  10126. * This is used as a performance optimization for production mode.
  10127. */
  10128. this.destroyNode = null;
  10129. }
  10130. /**
  10131. * @internal
  10132. * @nocollapse
  10133. */
  10134. static { this.__NG_ELEMENT_ID__ = () => injectRenderer2(); }
  10135. }
  10136. /** Injects a Renderer2 for the current component. */
  10137. function injectRenderer2() {
  10138. // We need the Renderer to be based on the component that it's being injected into, however since
  10139. // DI happens before we've entered its view, `getLView` will return the parent view instead.
  10140. const lView = getLView();
  10141. const tNode = getCurrentTNode();
  10142. const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
  10143. return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
  10144. }
  10145. /**
  10146. * Sanitizer is used by the views to sanitize potentially dangerous values.
  10147. *
  10148. * @publicApi
  10149. */
  10150. class Sanitizer {
  10151. /** @nocollapse */
  10152. static { this.ɵprov = ɵɵdefineInjectable({
  10153. token: Sanitizer,
  10154. providedIn: 'root',
  10155. factory: () => null,
  10156. }); }
  10157. }
  10158. /**
  10159. * @description Represents the version of Angular
  10160. *
  10161. * @publicApi
  10162. */
  10163. class Version {
  10164. constructor(full) {
  10165. this.full = full;
  10166. this.major = full.split('.')[0];
  10167. this.minor = full.split('.')[1];
  10168. this.patch = full.split('.').slice(2).join('.');
  10169. }
  10170. }
  10171. /**
  10172. * @publicApi
  10173. */
  10174. const VERSION = new Version('16.2.9');
  10175. // This default value is when checking the hierarchy for a token.
  10176. //
  10177. // It means both:
  10178. // - the token is not provided by the current injector,
  10179. // - only the element injectors should be checked (ie do not check module injectors
  10180. //
  10181. // mod1
  10182. // /
  10183. // el1 mod2
  10184. // \ /
  10185. // el2
  10186. //
  10187. // When requesting el2.injector.get(token), we should check in the following order and return the
  10188. // first found value:
  10189. // - el2.injector.get(token, default)
  10190. // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
  10191. // - mod2.injector.get(token, default)
  10192. const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
  10193. /**
  10194. * Runs the given function in the [context](guide/dependency-injection-context) of the given
  10195. * `Injector`.
  10196. *
  10197. * Within the function's stack frame, [`inject`](api/core/inject) can be used to inject dependencies
  10198. * from the given `Injector`. Note that `inject` is only usable synchronously, and cannot be used in
  10199. * any asynchronous callbacks or after any `await` points.
  10200. *
  10201. * @param injector the injector which will satisfy calls to [`inject`](api/core/inject) while `fn`
  10202. * is executing
  10203. * @param fn the closure to be run in the context of `injector`
  10204. * @returns the return value of the function, if any
  10205. * @publicApi
  10206. */
  10207. function runInInjectionContext(injector, fn) {
  10208. if (injector instanceof R3Injector) {
  10209. injector.assertNotDestroyed();
  10210. }
  10211. let prevInjectorProfilerContext;
  10212. if (ngDevMode) {
  10213. prevInjectorProfilerContext = setInjectorProfilerContext({ injector, token: null });
  10214. }
  10215. const prevInjector = setCurrentInjector(injector);
  10216. const previousInjectImplementation = setInjectImplementation(undefined);
  10217. try {
  10218. return fn();
  10219. }
  10220. finally {
  10221. setCurrentInjector(prevInjector);
  10222. ngDevMode && setInjectorProfilerContext(prevInjectorProfilerContext);
  10223. setInjectImplementation(previousInjectImplementation);
  10224. }
  10225. }
  10226. /**
  10227. * Asserts that the current stack frame is within an [injection
  10228. * context](guide/dependency-injection-context) and has access to `inject`.
  10229. *
  10230. * @param debugFn a reference to the function making the assertion (used for the error message).
  10231. *
  10232. * @publicApi
  10233. */
  10234. function assertInInjectionContext(debugFn) {
  10235. // Taking a `Function` instead of a string name here prevents the unminified name of the function
  10236. // from being retained in the bundle regardless of minification.
  10237. if (!getInjectImplementation() && !getCurrentInjector()) {
  10238. throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
  10239. (debugFn.name +
  10240. '() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`'));
  10241. }
  10242. }
  10243. /**
  10244. * A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
  10245. *
  10246. * This should be kept up to date with the public exports of @angular/core.
  10247. */
  10248. const angularCoreDiEnv = {
  10249. 'ɵɵdefineInjectable': ɵɵdefineInjectable,
  10250. 'ɵɵdefineInjector': ɵɵdefineInjector,
  10251. 'ɵɵinject': ɵɵinject,
  10252. 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep,
  10253. 'resolveForwardRef': resolveForwardRef,
  10254. };
  10255. /**
  10256. * Compile an Angular injectable according to its `Injectable` metadata, and patch the resulting
  10257. * injectable def (`ɵprov`) onto the injectable type.
  10258. */
  10259. function compileInjectable(type, meta) {
  10260. let ngInjectableDef = null;
  10261. let ngFactoryDef = null;
  10262. // if NG_PROV_DEF is already defined on this class then don't overwrite it
  10263. if (!type.hasOwnProperty(NG_PROV_DEF)) {
  10264. Object.defineProperty(type, NG_PROV_DEF, {
  10265. get: () => {
  10266. if (ngInjectableDef === null) {
  10267. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type });
  10268. ngInjectableDef = compiler.compileInjectable(angularCoreDiEnv, `ng:///${type.name}/ɵprov.js`, getInjectableMetadata(type, meta));
  10269. }
  10270. return ngInjectableDef;
  10271. },
  10272. });
  10273. }
  10274. // if NG_FACTORY_DEF is already defined on this class then don't overwrite it
  10275. if (!type.hasOwnProperty(NG_FACTORY_DEF)) {
  10276. Object.defineProperty(type, NG_FACTORY_DEF, {
  10277. get: () => {
  10278. if (ngFactoryDef === null) {
  10279. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type });
  10280. ngFactoryDef = compiler.compileFactory(angularCoreDiEnv, `ng:///${type.name}/ɵfac.js`, {
  10281. name: type.name,
  10282. type,
  10283. typeArgumentCount: 0,
  10284. deps: reflectDependencies(type),
  10285. target: compiler.FactoryTarget.Injectable
  10286. });
  10287. }
  10288. return ngFactoryDef;
  10289. },
  10290. // Leave this configurable so that the factories from directives or pipes can take precedence.
  10291. configurable: true
  10292. });
  10293. }
  10294. }
  10295. const USE_VALUE = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty });
  10296. function isUseClassProvider(meta) {
  10297. return meta.useClass !== undefined;
  10298. }
  10299. function isUseValueProvider(meta) {
  10300. return USE_VALUE in meta;
  10301. }
  10302. function isUseFactoryProvider(meta) {
  10303. return meta.useFactory !== undefined;
  10304. }
  10305. function isUseExistingProvider(meta) {
  10306. return meta.useExisting !== undefined;
  10307. }
  10308. function getInjectableMetadata(type, srcMeta) {
  10309. // Allow the compilation of a class with a `@Injectable()` decorator without parameters
  10310. const meta = srcMeta || { providedIn: null };
  10311. const compilerMeta = {
  10312. name: type.name,
  10313. type: type,
  10314. typeArgumentCount: 0,
  10315. providedIn: meta.providedIn,
  10316. };
  10317. if ((isUseClassProvider(meta) || isUseFactoryProvider(meta)) && meta.deps !== undefined) {
  10318. compilerMeta.deps = convertDependencies(meta.deps);
  10319. }
  10320. // Check to see if the user explicitly provided a `useXxxx` property.
  10321. if (isUseClassProvider(meta)) {
  10322. compilerMeta.useClass = meta.useClass;
  10323. }
  10324. else if (isUseValueProvider(meta)) {
  10325. compilerMeta.useValue = meta.useValue;
  10326. }
  10327. else if (isUseFactoryProvider(meta)) {
  10328. compilerMeta.useFactory = meta.useFactory;
  10329. }
  10330. else if (isUseExistingProvider(meta)) {
  10331. compilerMeta.useExisting = meta.useExisting;
  10332. }
  10333. return compilerMeta;
  10334. }
  10335. /**
  10336. * Injectable decorator and metadata.
  10337. *
  10338. * @Annotation
  10339. * @publicApi
  10340. */
  10341. const Injectable = makeDecorator('Injectable', undefined, undefined, undefined, (type, meta) => compileInjectable(type, meta));
  10342. /**
  10343. * Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s.
  10344. */
  10345. function createInjector(defType, parent = null, additionalProviders = null, name) {
  10346. const injector = createInjectorWithoutInjectorInstances(defType, parent, additionalProviders, name);
  10347. injector.resolveInjectorInitializers();
  10348. return injector;
  10349. }
  10350. /**
  10351. * Creates a new injector without eagerly resolving its injector types. Can be used in places
  10352. * where resolving the injector types immediately can lead to an infinite loop. The injector types
  10353. * should be resolved at a later point by calling `_resolveInjectorDefTypes`.
  10354. */
  10355. function createInjectorWithoutInjectorInstances(defType, parent = null, additionalProviders = null, name, scopes = new Set()) {
  10356. const providers = [
  10357. additionalProviders || EMPTY_ARRAY,
  10358. importProvidersFrom(defType),
  10359. ];
  10360. name = name || (typeof defType === 'object' ? undefined : stringify(defType));
  10361. return new R3Injector(providers, parent || getNullInjector(), name || null, scopes);
  10362. }
  10363. /**
  10364. * Concrete injectors implement this interface. Injectors are configured
  10365. * with [providers](guide/glossary#provider) that associate
  10366. * dependencies of various types with [injection tokens](guide/glossary#di-token).
  10367. *
  10368. * @see ["DI Providers"](guide/dependency-injection-providers).
  10369. * @see {@link StaticProvider}
  10370. *
  10371. * @usageNotes
  10372. *
  10373. * The following example creates a service injector instance.
  10374. *
  10375. * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
  10376. *
  10377. * ### Usage example
  10378. *
  10379. * {@example core/di/ts/injector_spec.ts region='Injector'}
  10380. *
  10381. * `Injector` returns itself when given `Injector` as a token:
  10382. *
  10383. * {@example core/di/ts/injector_spec.ts region='injectInjector'}
  10384. *
  10385. * @publicApi
  10386. */
  10387. class Injector {
  10388. static { this.THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND; }
  10389. static { this.NULL = ( /* @__PURE__ */new NullInjector()); }
  10390. static create(options, parent) {
  10391. if (Array.isArray(options)) {
  10392. return createInjector({ name: '' }, parent, options, '');
  10393. }
  10394. else {
  10395. const name = options.name ?? '';
  10396. return createInjector({ name }, options.parent, options.providers, name);
  10397. }
  10398. }
  10399. /** @nocollapse */
  10400. static { this.ɵprov = ɵɵdefineInjectable({
  10401. token: Injector,
  10402. providedIn: 'any',
  10403. factory: () => ɵɵinject(INJECTOR),
  10404. }); }
  10405. /**
  10406. * @internal
  10407. * @nocollapse
  10408. */
  10409. static { this.__NG_ELEMENT_ID__ = -1 /* InjectorMarkers.Injector */; }
  10410. }
  10411. /**
  10412. * @module
  10413. * @description
  10414. * The `di` module provides dependency injection container services.
  10415. */
  10416. /**
  10417. * This file should not be necessary because node resolution should just default to `./di/index`!
  10418. *
  10419. * However it does not seem to work and it breaks:
  10420. * - //packages/animations/browser/test:test_web_chromium-local
  10421. * - //packages/compiler-cli/test:extract_i18n
  10422. * - //packages/compiler-cli/test:ngc
  10423. * - //packages/compiler-cli/test:perform_watch
  10424. * - //packages/compiler-cli/test/diagnostics:check_types
  10425. * - //packages/compiler-cli/test/transformers:test
  10426. * - //packages/compiler/test:test
  10427. * - //tools/public_api_guard:core_api
  10428. *
  10429. * Remove this file once the above is solved or wait until `ngc` is deleted and then it should be
  10430. * safe to delete this file.
  10431. */
  10432. const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
  10433. function wrappedError(message, originalError) {
  10434. const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
  10435. const error = Error(msg);
  10436. error[ERROR_ORIGINAL_ERROR] = originalError;
  10437. return error;
  10438. }
  10439. function getOriginalError(error) {
  10440. return error[ERROR_ORIGINAL_ERROR];
  10441. }
  10442. /**
  10443. * Provides a hook for centralized exception handling.
  10444. *
  10445. * The default implementation of `ErrorHandler` prints error messages to the `console`. To
  10446. * intercept error handling, write a custom exception handler that replaces this default as
  10447. * appropriate for your app.
  10448. *
  10449. * @usageNotes
  10450. * ### Example
  10451. *
  10452. * ```
  10453. * class MyErrorHandler implements ErrorHandler {
  10454. * handleError(error) {
  10455. * // do something with the exception
  10456. * }
  10457. * }
  10458. *
  10459. * @NgModule({
  10460. * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
  10461. * })
  10462. * class MyModule {}
  10463. * ```
  10464. *
  10465. * @publicApi
  10466. */
  10467. class ErrorHandler {
  10468. constructor() {
  10469. /**
  10470. * @internal
  10471. */
  10472. this._console = console;
  10473. }
  10474. handleError(error) {
  10475. const originalError = this._findOriginalError(error);
  10476. this._console.error('ERROR', error);
  10477. if (originalError) {
  10478. this._console.error('ORIGINAL ERROR', originalError);
  10479. }
  10480. }
  10481. /** @internal */
  10482. _findOriginalError(error) {
  10483. let e = error && getOriginalError(error);
  10484. while (e && getOriginalError(e)) {
  10485. e = getOriginalError(e);
  10486. }
  10487. return e || null;
  10488. }
  10489. }
  10490. /**
  10491. * `DestroyRef` lets you set callbacks to run for any cleanup or destruction behavior.
  10492. * The scope of this destruction depends on where `DestroyRef` is injected. If `DestroyRef`
  10493. * is injected in a component or directive, the callbacks run when that component or
  10494. * directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.
  10495. *
  10496. * @publicApi
  10497. */
  10498. class DestroyRef {
  10499. /**
  10500. * @internal
  10501. * @nocollapse
  10502. */
  10503. static { this.__NG_ELEMENT_ID__ = injectDestroyRef; }
  10504. /**
  10505. * @internal
  10506. * @nocollapse
  10507. */
  10508. static { this.__NG_ENV_ID__ = (injector) => injector; }
  10509. }
  10510. class NodeInjectorDestroyRef extends DestroyRef {
  10511. constructor(_lView) {
  10512. super();
  10513. this._lView = _lView;
  10514. }
  10515. onDestroy(callback) {
  10516. storeLViewOnDestroy(this._lView, callback);
  10517. return () => removeLViewOnDestroy(this._lView, callback);
  10518. }
  10519. }
  10520. function injectDestroyRef() {
  10521. return new NodeInjectorDestroyRef(getLView());
  10522. }
  10523. /// <reference types="rxjs" />
  10524. class EventEmitter_ extends Subject {
  10525. constructor(isAsync = false) {
  10526. super();
  10527. this.__isAsync = isAsync;
  10528. }
  10529. emit(value) {
  10530. super.next(value);
  10531. }
  10532. subscribe(observerOrNext, error, complete) {
  10533. let nextFn = observerOrNext;
  10534. let errorFn = error || (() => null);
  10535. let completeFn = complete;
  10536. if (observerOrNext && typeof observerOrNext === 'object') {
  10537. const observer = observerOrNext;
  10538. nextFn = observer.next?.bind(observer);
  10539. errorFn = observer.error?.bind(observer);
  10540. completeFn = observer.complete?.bind(observer);
  10541. }
  10542. if (this.__isAsync) {
  10543. errorFn = _wrapInTimeout(errorFn);
  10544. if (nextFn) {
  10545. nextFn = _wrapInTimeout(nextFn);
  10546. }
  10547. if (completeFn) {
  10548. completeFn = _wrapInTimeout(completeFn);
  10549. }
  10550. }
  10551. const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn });
  10552. if (observerOrNext instanceof Subscription) {
  10553. observerOrNext.add(sink);
  10554. }
  10555. return sink;
  10556. }
  10557. }
  10558. function _wrapInTimeout(fn) {
  10559. return (value) => {
  10560. setTimeout(fn, undefined, value);
  10561. };
  10562. }
  10563. /**
  10564. * @publicApi
  10565. */
  10566. const EventEmitter = EventEmitter_;
  10567. function noop(...args) {
  10568. // Do nothing.
  10569. }
  10570. function getNativeRequestAnimationFrame() {
  10571. // Note: the `getNativeRequestAnimationFrame` is used in the `NgZone` class, but we cannot use the
  10572. // `inject` function. The `NgZone` instance may be created manually, and thus the injection
  10573. // context will be unavailable. This might be enough to check whether `requestAnimationFrame` is
  10574. // available because otherwise, we'll fall back to `setTimeout`.
  10575. const isBrowser = typeof _global['requestAnimationFrame'] === 'function';
  10576. // Note: `requestAnimationFrame` is unavailable when the code runs in the Node.js environment. We
  10577. // use `setTimeout` because no changes are required other than checking if the current platform is
  10578. // the browser. `setTimeout` is a well-established API that is available in both environments.
  10579. // `requestAnimationFrame` is used in the browser to coalesce event tasks since event tasks are
  10580. // usually executed within the same rendering frame (but this is more implementation details of
  10581. // browsers).
  10582. let nativeRequestAnimationFrame = _global[isBrowser ? 'requestAnimationFrame' : 'setTimeout'];
  10583. let nativeCancelAnimationFrame = _global[isBrowser ? 'cancelAnimationFrame' : 'clearTimeout'];
  10584. if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
  10585. // Note: zone.js sets original implementations on patched APIs behind the
  10586. // `__zone_symbol__OriginalDelegate` key (see `attachOriginToPatched`). Given the following
  10587. // example: `window.requestAnimationFrame.__zone_symbol__OriginalDelegate`; this would return an
  10588. // unpatched implementation of the `requestAnimationFrame`, which isn't intercepted by the
  10589. // Angular zone. We use the unpatched implementation to avoid another change detection when
  10590. // coalescing tasks.
  10591. const unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__('OriginalDelegate')];
  10592. if (unpatchedRequestAnimationFrame) {
  10593. nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
  10594. }
  10595. const unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__('OriginalDelegate')];
  10596. if (unpatchedCancelAnimationFrame) {
  10597. nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
  10598. }
  10599. }
  10600. return { nativeRequestAnimationFrame, nativeCancelAnimationFrame };
  10601. }
  10602. class AsyncStackTaggingZoneSpec {
  10603. constructor(namePrefix, consoleAsyncStackTaggingImpl = console) {
  10604. this.name = 'asyncStackTagging for ' + namePrefix;
  10605. this.createTask = consoleAsyncStackTaggingImpl?.createTask ?? (() => null);
  10606. }
  10607. onScheduleTask(delegate, _current, target, task) {
  10608. task.consoleTask = this.createTask(`Zone - ${task.source || task.type}`);
  10609. return delegate.scheduleTask(target, task);
  10610. }
  10611. onInvokeTask(delegate, _currentZone, targetZone, task, applyThis, applyArgs) {
  10612. let ret;
  10613. if (task.consoleTask) {
  10614. ret = task.consoleTask.run(() => delegate.invokeTask(targetZone, task, applyThis, applyArgs));
  10615. }
  10616. else {
  10617. ret = delegate.invokeTask(targetZone, task, applyThis, applyArgs);
  10618. }
  10619. return ret;
  10620. }
  10621. }
  10622. /**
  10623. * An injectable service for executing work inside or outside of the Angular zone.
  10624. *
  10625. * The most common use of this service is to optimize performance when starting a work consisting of
  10626. * one or more asynchronous tasks that don't require UI updates or error handling to be handled by
  10627. * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
  10628. * can reenter the Angular zone via {@link #run}.
  10629. *
  10630. * <!-- TODO: add/fix links to:
  10631. * - docs explaining zones and the use of zones in Angular and change-detection
  10632. * - link to runOutsideAngular/run (throughout this file!)
  10633. * -->
  10634. *
  10635. * @usageNotes
  10636. * ### Example
  10637. *
  10638. * ```
  10639. * import {Component, NgZone} from '@angular/core';
  10640. * import {NgIf} from '@angular/common';
  10641. *
  10642. * @Component({
  10643. * selector: 'ng-zone-demo',
  10644. * template: `
  10645. * <h2>Demo: NgZone</h2>
  10646. *
  10647. * <p>Progress: {{progress}}%</p>
  10648. * <p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p>
  10649. *
  10650. * <button (click)="processWithinAngularZone()">Process within Angular zone</button>
  10651. * <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
  10652. * `,
  10653. * })
  10654. * export class NgZoneDemo {
  10655. * progress: number = 0;
  10656. * label: string;
  10657. *
  10658. * constructor(private _ngZone: NgZone) {}
  10659. *
  10660. * // Loop inside the Angular zone
  10661. * // so the UI DOES refresh after each setTimeout cycle
  10662. * processWithinAngularZone() {
  10663. * this.label = 'inside';
  10664. * this.progress = 0;
  10665. * this._increaseProgress(() => console.log('Inside Done!'));
  10666. * }
  10667. *
  10668. * // Loop outside of the Angular zone
  10669. * // so the UI DOES NOT refresh after each setTimeout cycle
  10670. * processOutsideOfAngularZone() {
  10671. * this.label = 'outside';
  10672. * this.progress = 0;
  10673. * this._ngZone.runOutsideAngular(() => {
  10674. * this._increaseProgress(() => {
  10675. * // reenter the Angular zone and display done
  10676. * this._ngZone.run(() => { console.log('Outside Done!'); });
  10677. * });
  10678. * });
  10679. * }
  10680. *
  10681. * _increaseProgress(doneCallback: () => void) {
  10682. * this.progress += 1;
  10683. * console.log(`Current progress: ${this.progress}%`);
  10684. *
  10685. * if (this.progress < 100) {
  10686. * window.setTimeout(() => this._increaseProgress(doneCallback), 10);
  10687. * } else {
  10688. * doneCallback();
  10689. * }
  10690. * }
  10691. * }
  10692. * ```
  10693. *
  10694. * @publicApi
  10695. */
  10696. class NgZone {
  10697. constructor({ enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false }) {
  10698. this.hasPendingMacrotasks = false;
  10699. this.hasPendingMicrotasks = false;
  10700. /**
  10701. * Whether there are no outstanding microtasks or macrotasks.
  10702. */
  10703. this.isStable = true;
  10704. /**
  10705. * Notifies when code enters Angular Zone. This gets fired first on VM Turn.
  10706. */
  10707. this.onUnstable = new EventEmitter(false);
  10708. /**
  10709. * Notifies when there is no more microtasks enqueued in the current VM Turn.
  10710. * This is a hint for Angular to do change detection, which may enqueue more microtasks.
  10711. * For this reason this event can fire multiple times per VM Turn.
  10712. */
  10713. this.onMicrotaskEmpty = new EventEmitter(false);
  10714. /**
  10715. * Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
  10716. * implies we are about to relinquish VM turn.
  10717. * This event gets called just once.
  10718. */
  10719. this.onStable = new EventEmitter(false);
  10720. /**
  10721. * Notifies that an error has been delivered.
  10722. */
  10723. this.onError = new EventEmitter(false);
  10724. if (typeof Zone == 'undefined') {
  10725. throw new RuntimeError(908 /* RuntimeErrorCode.MISSING_ZONEJS */, ngDevMode && `In this configuration Angular requires Zone.js`);
  10726. }
  10727. Zone.assertZonePatched();
  10728. const self = this;
  10729. self._nesting = 0;
  10730. self._outer = self._inner = Zone.current;
  10731. // AsyncStackTaggingZoneSpec provides `linked stack traces` to show
  10732. // where the async operation is scheduled. For more details, refer
  10733. // to this article, https://developer.chrome.com/blog/devtools-better-angular-debugging/
  10734. // And we only import this AsyncStackTaggingZoneSpec in development mode,
  10735. // in the production mode, the AsyncStackTaggingZoneSpec will be tree shaken away.
  10736. if (ngDevMode) {
  10737. self._inner = self._inner.fork(new AsyncStackTaggingZoneSpec('Angular'));
  10738. }
  10739. if (Zone['TaskTrackingZoneSpec']) {
  10740. self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']);
  10741. }
  10742. if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) {
  10743. self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']);
  10744. }
  10745. // if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be
  10746. // coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped.
  10747. self.shouldCoalesceEventChangeDetection =
  10748. !shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
  10749. self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
  10750. self.lastRequestAnimationFrameId = -1;
  10751. self.nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame;
  10752. forkInnerZoneWithAngularBehavior(self);
  10753. }
  10754. /**
  10755. This method checks whether the method call happens within an Angular Zone instance.
  10756. */
  10757. static isInAngularZone() {
  10758. // Zone needs to be checked, because this method might be called even when NoopNgZone is used.
  10759. return typeof Zone !== 'undefined' && Zone.current.get('isAngularZone') === true;
  10760. }
  10761. /**
  10762. Assures that the method is called within the Angular Zone, otherwise throws an error.
  10763. */
  10764. static assertInAngularZone() {
  10765. if (!NgZone.isInAngularZone()) {
  10766. throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to be in Angular Zone, but it is not!');
  10767. }
  10768. }
  10769. /**
  10770. Assures that the method is called outside of the Angular Zone, otherwise throws an error.
  10771. */
  10772. static assertNotInAngularZone() {
  10773. if (NgZone.isInAngularZone()) {
  10774. throw new RuntimeError(909 /* RuntimeErrorCode.UNEXPECTED_ZONE_STATE */, ngDevMode && 'Expected to not be in Angular Zone, but it is!');
  10775. }
  10776. }
  10777. /**
  10778. * Executes the `fn` function synchronously within the Angular zone and returns value returned by
  10779. * the function.
  10780. *
  10781. * Running functions via `run` allows you to reenter Angular zone from a task that was executed
  10782. * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
  10783. *
  10784. * Any future tasks or microtasks scheduled from within this function will continue executing from
  10785. * within the Angular zone.
  10786. *
  10787. * If a synchronous error happens it will be rethrown and not reported via `onError`.
  10788. */
  10789. run(fn, applyThis, applyArgs) {
  10790. return this._inner.run(fn, applyThis, applyArgs);
  10791. }
  10792. /**
  10793. * Executes the `fn` function synchronously within the Angular zone as a task and returns value
  10794. * returned by the function.
  10795. *
  10796. * Running functions via `run` allows you to reenter Angular zone from a task that was executed
  10797. * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
  10798. *
  10799. * Any future tasks or microtasks scheduled from within this function will continue executing from
  10800. * within the Angular zone.
  10801. *
  10802. * If a synchronous error happens it will be rethrown and not reported via `onError`.
  10803. */
  10804. runTask(fn, applyThis, applyArgs, name) {
  10805. const zone = this._inner;
  10806. const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop);
  10807. try {
  10808. return zone.runTask(task, applyThis, applyArgs);
  10809. }
  10810. finally {
  10811. zone.cancelTask(task);
  10812. }
  10813. }
  10814. /**
  10815. * Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not
  10816. * rethrown.
  10817. */
  10818. runGuarded(fn, applyThis, applyArgs) {
  10819. return this._inner.runGuarded(fn, applyThis, applyArgs);
  10820. }
  10821. /**
  10822. * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
  10823. * the function.
  10824. *
  10825. * Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do
  10826. * work that
  10827. * doesn't trigger Angular change-detection or is subject to Angular's error handling.
  10828. *
  10829. * Any future tasks or microtasks scheduled from within this function will continue executing from
  10830. * outside of the Angular zone.
  10831. *
  10832. * Use {@link #run} to reenter the Angular zone and do work that updates the application model.
  10833. */
  10834. runOutsideAngular(fn) {
  10835. return this._outer.run(fn);
  10836. }
  10837. }
  10838. const EMPTY_PAYLOAD = {};
  10839. function checkStable(zone) {
  10840. // TODO: @JiaLiPassion, should check zone.isCheckStableRunning to prevent
  10841. // re-entry. The case is:
  10842. //
  10843. // @Component({...})
  10844. // export class AppComponent {
  10845. // constructor(private ngZone: NgZone) {
  10846. // this.ngZone.onStable.subscribe(() => {
  10847. // this.ngZone.run(() => console.log('stable'););
  10848. // });
  10849. // }
  10850. //
  10851. // The onStable subscriber run another function inside ngZone
  10852. // which causes `checkStable()` re-entry.
  10853. // But this fix causes some issues in g3, so this fix will be
  10854. // launched in another PR.
  10855. if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) {
  10856. try {
  10857. zone._nesting++;
  10858. zone.onMicrotaskEmpty.emit(null);
  10859. }
  10860. finally {
  10861. zone._nesting--;
  10862. if (!zone.hasPendingMicrotasks) {
  10863. try {
  10864. zone.runOutsideAngular(() => zone.onStable.emit(null));
  10865. }
  10866. finally {
  10867. zone.isStable = true;
  10868. }
  10869. }
  10870. }
  10871. }
  10872. }
  10873. function delayChangeDetectionForEvents(zone) {
  10874. /**
  10875. * We also need to check _nesting here
  10876. * Consider the following case with shouldCoalesceRunChangeDetection = true
  10877. *
  10878. * ngZone.run(() => {});
  10879. * ngZone.run(() => {});
  10880. *
  10881. * We want the two `ngZone.run()` only trigger one change detection
  10882. * when shouldCoalesceRunChangeDetection is true.
  10883. * And because in this case, change detection run in async way(requestAnimationFrame),
  10884. * so we also need to check the _nesting here to prevent multiple
  10885. * change detections.
  10886. */
  10887. if (zone.isCheckStableRunning || zone.lastRequestAnimationFrameId !== -1) {
  10888. return;
  10889. }
  10890. zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(_global, () => {
  10891. // This is a work around for https://github.com/angular/angular/issues/36839.
  10892. // The core issue is that when event coalescing is enabled it is possible for microtasks
  10893. // to get flushed too early (As is the case with `Promise.then`) between the
  10894. // coalescing eventTasks.
  10895. //
  10896. // To workaround this we schedule a "fake" eventTask before we process the
  10897. // coalescing eventTasks. The benefit of this is that the "fake" container eventTask
  10898. // will prevent the microtasks queue from getting drained in between the coalescing
  10899. // eventTask execution.
  10900. if (!zone.fakeTopEventTask) {
  10901. zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
  10902. zone.lastRequestAnimationFrameId = -1;
  10903. updateMicroTaskStatus(zone);
  10904. zone.isCheckStableRunning = true;
  10905. checkStable(zone);
  10906. zone.isCheckStableRunning = false;
  10907. }, undefined, () => { }, () => { });
  10908. }
  10909. zone.fakeTopEventTask.invoke();
  10910. });
  10911. updateMicroTaskStatus(zone);
  10912. }
  10913. function forkInnerZoneWithAngularBehavior(zone) {
  10914. const delayChangeDetectionForEventsDelegate = () => {
  10915. delayChangeDetectionForEvents(zone);
  10916. };
  10917. zone._inner = zone._inner.fork({
  10918. name: 'angular',
  10919. properties: { 'isAngularZone': true },
  10920. onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
  10921. if (shouldBeIgnoredByZone(applyArgs)) {
  10922. return delegate.invokeTask(target, task, applyThis, applyArgs);
  10923. }
  10924. try {
  10925. onEnter(zone);
  10926. return delegate.invokeTask(target, task, applyThis, applyArgs);
  10927. }
  10928. finally {
  10929. if ((zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask') ||
  10930. zone.shouldCoalesceRunChangeDetection) {
  10931. delayChangeDetectionForEventsDelegate();
  10932. }
  10933. onLeave(zone);
  10934. }
  10935. },
  10936. onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => {
  10937. try {
  10938. onEnter(zone);
  10939. return delegate.invoke(target, callback, applyThis, applyArgs, source);
  10940. }
  10941. finally {
  10942. if (zone.shouldCoalesceRunChangeDetection) {
  10943. delayChangeDetectionForEventsDelegate();
  10944. }
  10945. onLeave(zone);
  10946. }
  10947. },
  10948. onHasTask: (delegate, current, target, hasTaskState) => {
  10949. delegate.hasTask(target, hasTaskState);
  10950. if (current === target) {
  10951. // We are only interested in hasTask events which originate from our zone
  10952. // (A child hasTask event is not interesting to us)
  10953. if (hasTaskState.change == 'microTask') {
  10954. zone._hasPendingMicrotasks = hasTaskState.microTask;
  10955. updateMicroTaskStatus(zone);
  10956. checkStable(zone);
  10957. }
  10958. else if (hasTaskState.change == 'macroTask') {
  10959. zone.hasPendingMacrotasks = hasTaskState.macroTask;
  10960. }
  10961. }
  10962. },
  10963. onHandleError: (delegate, current, target, error) => {
  10964. delegate.handleError(target, error);
  10965. zone.runOutsideAngular(() => zone.onError.emit(error));
  10966. return false;
  10967. }
  10968. });
  10969. }
  10970. function updateMicroTaskStatus(zone) {
  10971. if (zone._hasPendingMicrotasks ||
  10972. ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
  10973. zone.lastRequestAnimationFrameId !== -1)) {
  10974. zone.hasPendingMicrotasks = true;
  10975. }
  10976. else {
  10977. zone.hasPendingMicrotasks = false;
  10978. }
  10979. }
  10980. function onEnter(zone) {
  10981. zone._nesting++;
  10982. if (zone.isStable) {
  10983. zone.isStable = false;
  10984. zone.onUnstable.emit(null);
  10985. }
  10986. }
  10987. function onLeave(zone) {
  10988. zone._nesting--;
  10989. checkStable(zone);
  10990. }
  10991. /**
  10992. * Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls
  10993. * to framework to perform rendering.
  10994. */
  10995. class NoopNgZone {
  10996. constructor() {
  10997. this.hasPendingMicrotasks = false;
  10998. this.hasPendingMacrotasks = false;
  10999. this.isStable = true;
  11000. this.onUnstable = new EventEmitter();
  11001. this.onMicrotaskEmpty = new EventEmitter();
  11002. this.onStable = new EventEmitter();
  11003. this.onError = new EventEmitter();
  11004. }
  11005. run(fn, applyThis, applyArgs) {
  11006. return fn.apply(applyThis, applyArgs);
  11007. }
  11008. runGuarded(fn, applyThis, applyArgs) {
  11009. return fn.apply(applyThis, applyArgs);
  11010. }
  11011. runOutsideAngular(fn) {
  11012. return fn();
  11013. }
  11014. runTask(fn, applyThis, applyArgs, name) {
  11015. return fn.apply(applyThis, applyArgs);
  11016. }
  11017. }
  11018. /**
  11019. * Token used to drive ApplicationRef.isStable
  11020. *
  11021. * TODO: This should be moved entirely to NgZone (as a breaking change) so it can be tree-shakeable
  11022. * for `NoopNgZone` which is always just an `Observable` of `true`. Additionally, we should consider
  11023. * whether the property on `NgZone` should be `Observable` or `Signal`.
  11024. */
  11025. const ZONE_IS_STABLE_OBSERVABLE = new InjectionToken(ngDevMode ? 'isStable Observable' : '', {
  11026. providedIn: 'root',
  11027. // TODO(atscott): Replace this with a suitable default like `new
  11028. // BehaviorSubject(true).asObservable`. Again, long term this won't exist on ApplicationRef at
  11029. // all but until we can remove it, we need a default value zoneless.
  11030. factory: isStableFactory,
  11031. });
  11032. function isStableFactory() {
  11033. const zone = inject(NgZone);
  11034. let _stable = true;
  11035. const isCurrentlyStable = new Observable((observer) => {
  11036. _stable = zone.isStable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks;
  11037. zone.runOutsideAngular(() => {
  11038. observer.next(_stable);
  11039. observer.complete();
  11040. });
  11041. });
  11042. const isStable = new Observable((observer) => {
  11043. // Create the subscription to onStable outside the Angular Zone so that
  11044. // the callback is run outside the Angular Zone.
  11045. let stableSub;
  11046. zone.runOutsideAngular(() => {
  11047. stableSub = zone.onStable.subscribe(() => {
  11048. NgZone.assertNotInAngularZone();
  11049. // Check whether there are no pending macro/micro tasks in the next tick
  11050. // to allow for NgZone to update the state.
  11051. queueMicrotask(() => {
  11052. if (!_stable && !zone.hasPendingMacrotasks && !zone.hasPendingMicrotasks) {
  11053. _stable = true;
  11054. observer.next(true);
  11055. }
  11056. });
  11057. });
  11058. });
  11059. const unstableSub = zone.onUnstable.subscribe(() => {
  11060. NgZone.assertInAngularZone();
  11061. if (_stable) {
  11062. _stable = false;
  11063. zone.runOutsideAngular(() => {
  11064. observer.next(false);
  11065. });
  11066. }
  11067. });
  11068. return () => {
  11069. stableSub.unsubscribe();
  11070. unstableSub.unsubscribe();
  11071. };
  11072. });
  11073. return merge$1(isCurrentlyStable, isStable.pipe(share()));
  11074. }
  11075. function shouldBeIgnoredByZone(applyArgs) {
  11076. if (!Array.isArray(applyArgs)) {
  11077. return false;
  11078. }
  11079. // We should only ever get 1 arg passed through to invokeTask.
  11080. // Short circuit here incase that behavior changes.
  11081. if (applyArgs.length !== 1) {
  11082. return false;
  11083. }
  11084. // Prevent triggering change detection when the __ignore_ng_zone__ flag is detected.
  11085. return applyArgs[0].data?.['__ignore_ng_zone__'] === true;
  11086. }
  11087. // Public API for Zone
  11088. /**
  11089. *
  11090. * @codeGenApi
  11091. */
  11092. function ɵɵresolveWindow(element) {
  11093. return element.ownerDocument.defaultView;
  11094. }
  11095. /**
  11096. *
  11097. * @codeGenApi
  11098. */
  11099. function ɵɵresolveDocument(element) {
  11100. return element.ownerDocument;
  11101. }
  11102. /**
  11103. *
  11104. * @codeGenApi
  11105. */
  11106. function ɵɵresolveBody(element) {
  11107. return element.ownerDocument.body;
  11108. }
  11109. /**
  11110. * The special delimiter we use to separate property names, prefixes, and suffixes
  11111. * in property binding metadata. See storeBindingMetadata().
  11112. *
  11113. * We intentionally use the Unicode "REPLACEMENT CHARACTER" (U+FFFD) as a delimiter
  11114. * because it is a very uncommon character that is unlikely to be part of a user's
  11115. * property names or interpolation strings. If it is in fact used in a property
  11116. * binding, DebugElement.properties will not return the correct value for that
  11117. * binding. However, there should be no runtime effect for real applications.
  11118. *
  11119. * This character is typically rendered as a question mark inside of a diamond.
  11120. * See https://en.wikipedia.org/wiki/Specials_(Unicode_block)
  11121. *
  11122. */
  11123. const INTERPOLATION_DELIMITER = `�`;
  11124. /**
  11125. * Unwrap a value which might be behind a closure (for forward declaration reasons).
  11126. */
  11127. function maybeUnwrapFn(value) {
  11128. if (value instanceof Function) {
  11129. return value();
  11130. }
  11131. else {
  11132. return value;
  11133. }
  11134. }
  11135. /**
  11136. * Detects whether the code is invoked in a browser.
  11137. * Later on, this check should be replaced with a tree-shakable
  11138. * flag (e.g. `!isServer`).
  11139. */
  11140. function isPlatformBrowser(injector) {
  11141. return (injector ?? inject(Injector)).get(PLATFORM_ID) === 'browser';
  11142. }
  11143. /**
  11144. * Register a callback to be invoked each time the application
  11145. * finishes rendering.
  11146. *
  11147. * Note that the callback will run
  11148. * - in the order it was registered
  11149. * - once per render
  11150. * - on browser platforms only
  11151. *
  11152. * <div class="alert is-important">
  11153. *
  11154. * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.
  11155. * You must use caution when directly reading or writing the DOM and layout.
  11156. *
  11157. * </div>
  11158. *
  11159. * @param callback A callback function to register
  11160. *
  11161. * @usageNotes
  11162. *
  11163. * Use `afterRender` to read or write the DOM after each render.
  11164. *
  11165. * ### Example
  11166. * ```ts
  11167. * @Component({
  11168. * selector: 'my-cmp',
  11169. * template: `<span #content>{{ ... }}</span>`,
  11170. * })
  11171. * export class MyComponent {
  11172. * @ViewChild('content') contentRef: ElementRef;
  11173. *
  11174. * constructor() {
  11175. * afterRender(() => {
  11176. * console.log('content height: ' + this.contentRef.nativeElement.scrollHeight);
  11177. * });
  11178. * }
  11179. * }
  11180. * ```
  11181. *
  11182. * @developerPreview
  11183. */
  11184. function afterRender(callback, options) {
  11185. !options && assertInInjectionContext(afterRender);
  11186. const injector = options?.injector ?? inject(Injector);
  11187. if (!isPlatformBrowser(injector)) {
  11188. return { destroy() { } };
  11189. }
  11190. let destroy;
  11191. const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
  11192. const afterRenderEventManager = injector.get(AfterRenderEventManager);
  11193. // Lazily initialize the handler implementation, if necessary. This is so that it can be
  11194. // tree-shaken if `afterRender` and `afterNextRender` aren't used.
  11195. const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
  11196. const ngZone = injector.get(NgZone);
  11197. const errorHandler = injector.get(ErrorHandler, null, { optional: true });
  11198. const instance = new AfterRenderCallback(ngZone, errorHandler, callback);
  11199. destroy = () => {
  11200. callbackHandler.unregister(instance);
  11201. unregisterFn();
  11202. };
  11203. callbackHandler.register(instance);
  11204. return { destroy };
  11205. }
  11206. /**
  11207. * Register a callback to be invoked the next time the application
  11208. * finishes rendering.
  11209. *
  11210. * Note that the callback will run
  11211. * - in the order it was registered
  11212. * - on browser platforms only
  11213. *
  11214. * <div class="alert is-important">
  11215. *
  11216. * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.
  11217. * You must use caution when directly reading or writing the DOM and layout.
  11218. *
  11219. * </div>
  11220. *
  11221. * @param callback A callback function to register
  11222. *
  11223. * @usageNotes
  11224. *
  11225. * Use `afterNextRender` to read or write the DOM once,
  11226. * for example to initialize a non-Angular library.
  11227. *
  11228. * ### Example
  11229. * ```ts
  11230. * @Component({
  11231. * selector: 'my-chart-cmp',
  11232. * template: `<div #chart>{{ ... }}</div>`,
  11233. * })
  11234. * export class MyChartCmp {
  11235. * @ViewChild('chart') chartRef: ElementRef;
  11236. * chart: MyChart|null;
  11237. *
  11238. * constructor() {
  11239. * afterNextRender(() => {
  11240. * this.chart = new MyChart(this.chartRef.nativeElement);
  11241. * });
  11242. * }
  11243. * }
  11244. * ```
  11245. *
  11246. * @developerPreview
  11247. */
  11248. function afterNextRender(callback, options) {
  11249. !options && assertInInjectionContext(afterNextRender);
  11250. const injector = options?.injector ?? inject(Injector);
  11251. if (!isPlatformBrowser(injector)) {
  11252. return { destroy() { } };
  11253. }
  11254. let destroy;
  11255. const unregisterFn = injector.get(DestroyRef).onDestroy(() => destroy?.());
  11256. const afterRenderEventManager = injector.get(AfterRenderEventManager);
  11257. // Lazily initialize the handler implementation, if necessary. This is so that it can be
  11258. // tree-shaken if `afterRender` and `afterNextRender` aren't used.
  11259. const callbackHandler = afterRenderEventManager.handler ??= new AfterRenderCallbackHandlerImpl();
  11260. const ngZone = injector.get(NgZone);
  11261. const errorHandler = injector.get(ErrorHandler, null, { optional: true });
  11262. const instance = new AfterRenderCallback(ngZone, errorHandler, () => {
  11263. destroy?.();
  11264. callback();
  11265. });
  11266. destroy = () => {
  11267. callbackHandler.unregister(instance);
  11268. unregisterFn();
  11269. };
  11270. callbackHandler.register(instance);
  11271. return { destroy };
  11272. }
  11273. /**
  11274. * A wrapper around a function to be used as an after render callback.
  11275. */
  11276. class AfterRenderCallback {
  11277. constructor(zone, errorHandler, callbackFn) {
  11278. this.zone = zone;
  11279. this.errorHandler = errorHandler;
  11280. this.callbackFn = callbackFn;
  11281. }
  11282. invoke() {
  11283. try {
  11284. this.zone.runOutsideAngular(this.callbackFn);
  11285. }
  11286. catch (err) {
  11287. this.errorHandler?.handleError(err);
  11288. }
  11289. }
  11290. }
  11291. /**
  11292. * Core functionality for `afterRender` and `afterNextRender`. Kept separate from
  11293. * `AfterRenderEventManager` for tree-shaking.
  11294. */
  11295. class AfterRenderCallbackHandlerImpl {
  11296. constructor() {
  11297. this.executingCallbacks = false;
  11298. this.callbacks = new Set();
  11299. this.deferredCallbacks = new Set();
  11300. }
  11301. validateBegin() {
  11302. if (this.executingCallbacks) {
  11303. throw new RuntimeError(102 /* RuntimeErrorCode.RECURSIVE_APPLICATION_RENDER */, ngDevMode &&
  11304. 'A new render operation began before the previous operation ended. ' +
  11305. 'Did you trigger change detection from afterRender or afterNextRender?');
  11306. }
  11307. }
  11308. register(callback) {
  11309. // If we're currently running callbacks, new callbacks should be deferred
  11310. // until the next render operation.
  11311. const target = this.executingCallbacks ? this.deferredCallbacks : this.callbacks;
  11312. target.add(callback);
  11313. }
  11314. unregister(callback) {
  11315. this.callbacks.delete(callback);
  11316. this.deferredCallbacks.delete(callback);
  11317. }
  11318. execute() {
  11319. this.executingCallbacks = true;
  11320. for (const callback of this.callbacks) {
  11321. callback.invoke();
  11322. }
  11323. this.executingCallbacks = false;
  11324. for (const callback of this.deferredCallbacks) {
  11325. this.callbacks.add(callback);
  11326. }
  11327. this.deferredCallbacks.clear();
  11328. }
  11329. destroy() {
  11330. this.callbacks.clear();
  11331. this.deferredCallbacks.clear();
  11332. }
  11333. }
  11334. /**
  11335. * Implements core timing for `afterRender` and `afterNextRender` events.
  11336. * Delegates to an optional `AfterRenderCallbackHandler` for implementation.
  11337. */
  11338. class AfterRenderEventManager {
  11339. constructor() {
  11340. this.renderDepth = 0;
  11341. /* @internal */
  11342. this.handler = null;
  11343. }
  11344. /**
  11345. * Mark the beginning of a render operation (i.e. CD cycle).
  11346. * Throws if called while executing callbacks.
  11347. */
  11348. begin() {
  11349. this.handler?.validateBegin();
  11350. this.renderDepth++;
  11351. }
  11352. /**
  11353. * Mark the end of a render operation. Callbacks will be
  11354. * executed if there are no more pending operations.
  11355. */
  11356. end() {
  11357. ngDevMode && assertGreaterThan(this.renderDepth, 0, 'renderDepth must be greater than 0');
  11358. this.renderDepth--;
  11359. if (this.renderDepth === 0) {
  11360. this.handler?.execute();
  11361. }
  11362. }
  11363. ngOnDestroy() {
  11364. this.handler?.destroy();
  11365. this.handler = null;
  11366. }
  11367. /** @nocollapse */
  11368. static { this.ɵprov = ɵɵdefineInjectable({
  11369. token: AfterRenderEventManager,
  11370. providedIn: 'root',
  11371. factory: () => new AfterRenderEventManager(),
  11372. }); }
  11373. }
  11374. /**
  11375. * Marks current view and all ancestors dirty.
  11376. *
  11377. * Returns the root view because it is found as a byproduct of marking the view tree
  11378. * dirty, and can be used by methods that consume markViewDirty() to easily schedule
  11379. * change detection. Otherwise, such methods would need to traverse up the view tree
  11380. * an additional time to get the root view and schedule a tick on it.
  11381. *
  11382. * @param lView The starting LView to mark dirty
  11383. * @returns the root LView
  11384. */
  11385. function markViewDirty(lView) {
  11386. while (lView) {
  11387. lView[FLAGS] |= 64 /* LViewFlags.Dirty */;
  11388. const parent = getLViewParent(lView);
  11389. // Stop traversing up as soon as you find a root view that wasn't attached to any container
  11390. if (isRootView(lView) && !parent) {
  11391. return lView;
  11392. }
  11393. // continue otherwise
  11394. lView = parent;
  11395. }
  11396. return null;
  11397. }
  11398. /**
  11399. * Internal token that specifies whether DOM reuse logic
  11400. * during hydration is enabled.
  11401. */
  11402. const IS_HYDRATION_DOM_REUSE_ENABLED = new InjectionToken((typeof ngDevMode === 'undefined' || !!ngDevMode) ? 'IS_HYDRATION_DOM_REUSE_ENABLED' : '');
  11403. // By default (in client rendering mode), we remove all the contents
  11404. // of the host element and render an application after that.
  11405. const PRESERVE_HOST_CONTENT_DEFAULT = false;
  11406. /**
  11407. * Internal token that indicates whether host element content should be
  11408. * retained during the bootstrap.
  11409. */
  11410. const PRESERVE_HOST_CONTENT = new InjectionToken((typeof ngDevMode === 'undefined' || !!ngDevMode) ? 'PRESERVE_HOST_CONTENT' : '', {
  11411. providedIn: 'root',
  11412. factory: () => PRESERVE_HOST_CONTENT_DEFAULT,
  11413. });
  11414. function normalizeDebugBindingName(name) {
  11415. // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
  11416. name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
  11417. return `ng-reflect-${name}`;
  11418. }
  11419. const CAMEL_CASE_REGEXP = /([A-Z])/g;
  11420. function camelCaseToDashCase(input) {
  11421. return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
  11422. }
  11423. function normalizeDebugBindingValue(value) {
  11424. try {
  11425. // Limit the size of the value as otherwise the DOM just gets polluted.
  11426. return value != null ? value.toString().slice(0, 30) : value;
  11427. }
  11428. catch (e) {
  11429. return '[ERROR] Exception while trying to serialize the value';
  11430. }
  11431. }
  11432. /**
  11433. * The max length of the string representation of a value in an error message
  11434. */
  11435. const VALUE_STRING_LENGTH_LIMIT = 200;
  11436. /** Verifies that a given type is a Standalone Component. */
  11437. function assertStandaloneComponentType(type) {
  11438. assertComponentDef(type);
  11439. const componentDef = getComponentDef(type);
  11440. if (!componentDef.standalone) {
  11441. throw new RuntimeError(907 /* RuntimeErrorCode.TYPE_IS_NOT_STANDALONE */, `The ${stringifyForError(type)} component is not marked as standalone, ` +
  11442. `but Angular expects to have a standalone component here. ` +
  11443. `Please make sure the ${stringifyForError(type)} component has ` +
  11444. `the \`standalone: true\` flag in the decorator.`);
  11445. }
  11446. }
  11447. /** Verifies whether a given type is a component */
  11448. function assertComponentDef(type) {
  11449. if (!getComponentDef(type)) {
  11450. throw new RuntimeError(906 /* RuntimeErrorCode.MISSING_GENERATED_DEF */, `The ${stringifyForError(type)} is not an Angular component, ` +
  11451. `make sure it has the \`@Component\` decorator.`);
  11452. }
  11453. }
  11454. /** Called when there are multiple component selectors that match a given node */
  11455. function throwMultipleComponentError(tNode, first, second) {
  11456. throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` +
  11457. `${stringifyForError(first)} and ` +
  11458. `${stringifyForError(second)}`);
  11459. }
  11460. /** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */
  11461. function throwErrorIfNoChangesMode(creationMode, oldValue, currValue, propName, lView) {
  11462. const hostComponentDef = getDeclarationComponentDef(lView);
  11463. const componentClassName = hostComponentDef?.type?.name;
  11464. const field = propName ? ` for '${propName}'` : '';
  11465. let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${formatValue(oldValue)}'. Current value: '${formatValue(currValue)}'.${componentClassName ? ` Expression location: ${componentClassName} component` : ''}`;
  11466. if (creationMode) {
  11467. msg +=
  11468. ` It seems like the view has been created after its parent and its children have been dirty checked.` +
  11469. ` Has it been created in a change detection hook?`;
  11470. }
  11471. throw new RuntimeError(-100 /* RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED */, msg);
  11472. }
  11473. function formatValue(value) {
  11474. let strValue = String(value);
  11475. // JSON.stringify will throw on circular references
  11476. try {
  11477. if (Array.isArray(value) || strValue === '[object Object]') {
  11478. strValue = JSON.stringify(value);
  11479. }
  11480. }
  11481. catch (error) {
  11482. }
  11483. return strValue.length > VALUE_STRING_LENGTH_LIMIT ?
  11484. (strValue.substring(0, VALUE_STRING_LENGTH_LIMIT) + '…') :
  11485. strValue;
  11486. }
  11487. function constructDetailsForInterpolation(lView, rootIndex, expressionIndex, meta, changedValue) {
  11488. const [propName, prefix, ...chunks] = meta.split(INTERPOLATION_DELIMITER);
  11489. let oldValue = prefix, newValue = prefix;
  11490. for (let i = 0; i < chunks.length; i++) {
  11491. const slotIdx = rootIndex + i;
  11492. oldValue += `${lView[slotIdx]}${chunks[i]}`;
  11493. newValue += `${slotIdx === expressionIndex ? changedValue : lView[slotIdx]}${chunks[i]}`;
  11494. }
  11495. return { propName, oldValue, newValue };
  11496. }
  11497. /**
  11498. * Constructs an object that contains details for the ExpressionChangedAfterItHasBeenCheckedError:
  11499. * - property name (for property bindings or interpolations)
  11500. * - old and new values, enriched using information from metadata
  11501. *
  11502. * More information on the metadata storage format can be found in `storePropertyBindingMetadata`
  11503. * function description.
  11504. */
  11505. function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValue) {
  11506. const tData = lView[TVIEW].data;
  11507. const metadata = tData[bindingIndex];
  11508. if (typeof metadata === 'string') {
  11509. // metadata for property interpolation
  11510. if (metadata.indexOf(INTERPOLATION_DELIMITER) > -1) {
  11511. return constructDetailsForInterpolation(lView, bindingIndex, bindingIndex, metadata, newValue);
  11512. }
  11513. // metadata for property binding
  11514. return { propName: metadata, oldValue, newValue };
  11515. }
  11516. // metadata is not available for this expression, check if this expression is a part of the
  11517. // property interpolation by going from the current binding index left and look for a string that
  11518. // contains INTERPOLATION_DELIMITER, the layout in tView.data for this case will look like this:
  11519. // [..., 'id�Prefix � and � suffix', null, null, null, ...]
  11520. if (metadata === null) {
  11521. let idx = bindingIndex - 1;
  11522. while (typeof tData[idx] !== 'string' && tData[idx + 1] === null) {
  11523. idx--;
  11524. }
  11525. const meta = tData[idx];
  11526. if (typeof meta === 'string') {
  11527. const matches = meta.match(new RegExp(INTERPOLATION_DELIMITER, 'g'));
  11528. // first interpolation delimiter separates property name from interpolation parts (in case of
  11529. // property interpolations), so we subtract one from total number of found delimiters
  11530. if (matches && (matches.length - 1) > bindingIndex - idx) {
  11531. return constructDetailsForInterpolation(lView, idx, bindingIndex, meta, newValue);
  11532. }
  11533. }
  11534. }
  11535. return { propName: undefined, oldValue, newValue };
  11536. }
  11537. let currentConsumer = null;
  11538. function setLViewForConsumer(node, lView) {
  11539. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  11540. assertEqual(node.lView, null, 'Consumer already associated with a view.');
  11541. node.lView = lView;
  11542. }
  11543. /**
  11544. * Create a new template consumer pointing at the specified LView.
  11545. * Sometimes, a previously created consumer may be reused, in order to save on allocations. In that
  11546. * case, the LView will be updated.
  11547. */
  11548. function getReactiveLViewConsumer(lView, slot) {
  11549. return lView[slot] ?? getOrCreateCurrentLViewConsumer();
  11550. }
  11551. /**
  11552. * Assigns the `currentTemplateContext` to its LView's `REACTIVE_CONSUMER` slot if there are tracked
  11553. * producers.
  11554. *
  11555. * The presence of producers means that a signal was read while the consumer was the active
  11556. * consumer.
  11557. *
  11558. * If no producers are present, we do not assign the current template context. This also means we
  11559. * can just reuse the template context for the next LView.
  11560. */
  11561. function commitLViewConsumerIfHasProducers(lView, slot) {
  11562. const consumer = getOrCreateCurrentLViewConsumer();
  11563. if (!consumer.producerNode?.length) {
  11564. return;
  11565. }
  11566. lView[slot] = currentConsumer;
  11567. consumer.lView = lView;
  11568. currentConsumer = createLViewConsumer();
  11569. }
  11570. const REACTIVE_LVIEW_CONSUMER_NODE = {
  11571. ...REACTIVE_NODE,
  11572. consumerIsAlwaysLive: true,
  11573. consumerMarkedDirty: (node) => {
  11574. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  11575. assertDefined(node.lView, 'Updating a signal during template or host binding execution is not allowed.');
  11576. markViewDirty(node.lView);
  11577. },
  11578. lView: null,
  11579. };
  11580. function createLViewConsumer() {
  11581. return Object.create(REACTIVE_LVIEW_CONSUMER_NODE);
  11582. }
  11583. function getOrCreateCurrentLViewConsumer() {
  11584. currentConsumer ??= createLViewConsumer();
  11585. return currentConsumer;
  11586. }
  11587. /** A special value which designates that a value has not changed. */
  11588. const NO_CHANGE = (typeof ngDevMode === 'undefined' || ngDevMode) ? { __brand__: 'NO_CHANGE' } : {};
  11589. /**
  11590. * Advances to an element for later binding instructions.
  11591. *
  11592. * Used in conjunction with instructions like {@link property} to act on elements with specified
  11593. * indices, for example those created with {@link element} or {@link elementStart}.
  11594. *
  11595. * ```ts
  11596. * (rf: RenderFlags, ctx: any) => {
  11597. * if (rf & 1) {
  11598. * text(0, 'Hello');
  11599. * text(1, 'Goodbye')
  11600. * element(2, 'div');
  11601. * }
  11602. * if (rf & 2) {
  11603. * advance(2); // Advance twice to the <div>.
  11604. * property('title', 'test');
  11605. * }
  11606. * }
  11607. * ```
  11608. * @param delta Number of elements to advance forwards by.
  11609. *
  11610. * @codeGenApi
  11611. */
  11612. function ɵɵadvance(delta) {
  11613. ngDevMode && assertGreaterThan(delta, 0, 'Can only advance forward');
  11614. selectIndexInternal(getTView(), getLView(), getSelectedIndex() + delta, !!ngDevMode && isInCheckNoChangesMode());
  11615. }
  11616. function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
  11617. ngDevMode && assertIndexInDeclRange(lView, index);
  11618. // Flush the initial hooks for elements in the view that have been added up to this point.
  11619. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  11620. if (!checkNoChangesMode) {
  11621. const hooksInitPhaseCompleted = (lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
  11622. if (hooksInitPhaseCompleted) {
  11623. const preOrderCheckHooks = tView.preOrderCheckHooks;
  11624. if (preOrderCheckHooks !== null) {
  11625. executeCheckHooks(lView, preOrderCheckHooks, index);
  11626. }
  11627. }
  11628. else {
  11629. const preOrderHooks = tView.preOrderHooks;
  11630. if (preOrderHooks !== null) {
  11631. executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, index);
  11632. }
  11633. }
  11634. }
  11635. // We must set the selected index *after* running the hooks, because hooks may have side-effects
  11636. // that cause other template functions to run, thus updating the selected index, which is global
  11637. // state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
  11638. // will be altered by the time we leave the `ɵɵadvance` instruction.
  11639. setSelectedIndex(index);
  11640. }
  11641. function ɵɵdirectiveInject(token, flags = InjectFlags.Default) {
  11642. const lView = getLView();
  11643. // Fall back to inject() if view hasn't been created. This situation can happen in tests
  11644. // if inject utilities are used before bootstrapping.
  11645. if (lView === null) {
  11646. // Verify that we will not get into infinite loop.
  11647. ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject);
  11648. return ɵɵinject(token, flags);
  11649. }
  11650. const tNode = getCurrentTNode();
  11651. const value = getOrCreateInjectable(tNode, lView, resolveForwardRef(token), flags);
  11652. ngDevMode && emitInjectEvent(token, value, flags);
  11653. return value;
  11654. }
  11655. /**
  11656. * Throws an error indicating that a factory function could not be generated by the compiler for a
  11657. * particular class.
  11658. *
  11659. * This instruction allows the actual error message to be optimized away when ngDevMode is turned
  11660. * off, saving bytes of generated code while still providing a good experience in dev mode.
  11661. *
  11662. * The name of the class is not mentioned here, but will be in the generated factory function name
  11663. * and thus in the stack trace.
  11664. *
  11665. * @codeGenApi
  11666. */
  11667. function ɵɵinvalidFactory() {
  11668. const msg = ngDevMode ? `This constructor was not compatible with Dependency Injection.` : 'invalid';
  11669. throw new Error(msg);
  11670. }
  11671. /**
  11672. * Invoke `HostBindingsFunction`s for view.
  11673. *
  11674. * This methods executes `TView.hostBindingOpCodes`. It is used to execute the
  11675. * `HostBindingsFunction`s associated with the current `LView`.
  11676. *
  11677. * @param tView Current `TView`.
  11678. * @param lView Current `LView`.
  11679. */
  11680. function processHostBindingOpCodes(tView, lView) {
  11681. const hostBindingOpCodes = tView.hostBindingOpCodes;
  11682. if (hostBindingOpCodes === null)
  11683. return;
  11684. const consumer = getReactiveLViewConsumer(lView, REACTIVE_HOST_BINDING_CONSUMER);
  11685. try {
  11686. for (let i = 0; i < hostBindingOpCodes.length; i++) {
  11687. const opCode = hostBindingOpCodes[i];
  11688. if (opCode < 0) {
  11689. // Negative numbers are element indexes.
  11690. setSelectedIndex(~opCode);
  11691. }
  11692. else {
  11693. // Positive numbers are NumberTuple which store bindingRootIndex and directiveIndex.
  11694. const directiveIdx = opCode;
  11695. const bindingRootIndx = hostBindingOpCodes[++i];
  11696. const hostBindingFn = hostBindingOpCodes[++i];
  11697. setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
  11698. consumer.dirty = false;
  11699. const prevConsumer = consumerBeforeComputation(consumer);
  11700. try {
  11701. const context = lView[directiveIdx];
  11702. hostBindingFn(2 /* RenderFlags.Update */, context);
  11703. }
  11704. finally {
  11705. consumerAfterComputation(consumer, prevConsumer);
  11706. }
  11707. }
  11708. }
  11709. }
  11710. finally {
  11711. if (lView[REACTIVE_HOST_BINDING_CONSUMER] === null) {
  11712. commitLViewConsumerIfHasProducers(lView, REACTIVE_HOST_BINDING_CONSUMER);
  11713. }
  11714. setSelectedIndex(-1);
  11715. }
  11716. }
  11717. function createLView(parentLView, tView, context, flags, host, tHostNode, environment, renderer, injector, embeddedViewInjector, hydrationInfo) {
  11718. const lView = tView.blueprint.slice();
  11719. lView[HOST] = host;
  11720. lView[FLAGS] = flags | 4 /* LViewFlags.CreationMode */ | 128 /* LViewFlags.Attached */ | 8 /* LViewFlags.FirstLViewPass */;
  11721. if (embeddedViewInjector !== null ||
  11722. (parentLView && (parentLView[FLAGS] & 2048 /* LViewFlags.HasEmbeddedViewInjector */))) {
  11723. lView[FLAGS] |= 2048 /* LViewFlags.HasEmbeddedViewInjector */;
  11724. }
  11725. resetPreOrderHookFlags(lView);
  11726. ngDevMode && tView.declTNode && parentLView && assertTNodeForLView(tView.declTNode, parentLView);
  11727. lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
  11728. lView[CONTEXT] = context;
  11729. lView[ENVIRONMENT] = (environment || parentLView && parentLView[ENVIRONMENT]);
  11730. ngDevMode && assertDefined(lView[ENVIRONMENT], 'LViewEnvironment is required');
  11731. lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER]);
  11732. ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required');
  11733. lView[INJECTOR$1] = injector || parentLView && parentLView[INJECTOR$1] || null;
  11734. lView[T_HOST] = tHostNode;
  11735. lView[ID] = getUniqueLViewId();
  11736. lView[HYDRATION] = hydrationInfo;
  11737. lView[EMBEDDED_VIEW_INJECTOR] = embeddedViewInjector;
  11738. ngDevMode &&
  11739. assertEqual(tView.type == 2 /* TViewType.Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView');
  11740. lView[DECLARATION_COMPONENT_VIEW] =
  11741. tView.type == 2 /* TViewType.Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView;
  11742. return lView;
  11743. }
  11744. function getOrCreateTNode(tView, index, type, name, attrs) {
  11745. ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
  11746. // `view_engine_compatibility` for additional context.
  11747. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
  11748. // Keep this function short, so that the VM will inline it.
  11749. ngDevMode && assertPureTNodeType(type);
  11750. let tNode = tView.data[index];
  11751. if (tNode === null) {
  11752. tNode = createTNodeAtIndex(tView, index, type, name, attrs);
  11753. if (isInI18nBlock()) {
  11754. // If we are in i18n block then all elements should be pre declared through `Placeholder`
  11755. // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
  11756. // If the `TNode` was not pre-declared than it means it was not mentioned which means it was
  11757. // removed, so we mark it as detached.
  11758. tNode.flags |= 32 /* TNodeFlags.isDetached */;
  11759. }
  11760. }
  11761. else if (tNode.type & 64 /* TNodeType.Placeholder */) {
  11762. tNode.type = type;
  11763. tNode.value = name;
  11764. tNode.attrs = attrs;
  11765. const parent = getCurrentParentTNode();
  11766. tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex;
  11767. ngDevMode && assertTNodeForTView(tNode, tView);
  11768. ngDevMode && assertEqual(index, tNode.index, 'Expecting same index');
  11769. }
  11770. setCurrentTNode(tNode, true);
  11771. return tNode;
  11772. }
  11773. function createTNodeAtIndex(tView, index, type, name, attrs) {
  11774. const currentTNode = getCurrentTNodePlaceholderOk();
  11775. const isParent = isCurrentTNodeParent();
  11776. const parent = isParent ? currentTNode : currentTNode && currentTNode.parent;
  11777. // Parents cannot cross component boundaries because components will be used in multiple places.
  11778. const tNode = tView.data[index] =
  11779. createTNode(tView, parent, type, index, name, attrs);
  11780. // Assign a pointer to the first child node of a given view. The first node is not always the one
  11781. // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has
  11782. // the index 1 or more, so we can't just check node index.
  11783. if (tView.firstChild === null) {
  11784. tView.firstChild = tNode;
  11785. }
  11786. if (currentTNode !== null) {
  11787. if (isParent) {
  11788. // FIXME(misko): This logic looks unnecessarily complicated. Could we simplify?
  11789. if (currentTNode.child == null && tNode.parent !== null) {
  11790. // We are in the same view, which means we are adding content node to the parent view.
  11791. currentTNode.child = tNode;
  11792. }
  11793. }
  11794. else {
  11795. if (currentTNode.next === null) {
  11796. // In the case of i18n the `currentTNode` may already be linked, in which case we don't want
  11797. // to break the links which i18n created.
  11798. currentTNode.next = tNode;
  11799. tNode.prev = currentTNode;
  11800. }
  11801. }
  11802. }
  11803. return tNode;
  11804. }
  11805. /**
  11806. * When elements are created dynamically after a view blueprint is created (e.g. through
  11807. * i18nApply()), we need to adjust the blueprint for future
  11808. * template passes.
  11809. *
  11810. * @param tView `TView` associated with `LView`
  11811. * @param lView The `LView` containing the blueprint to adjust
  11812. * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
  11813. * @param initialValue Initial value to store in blueprint
  11814. */
  11815. function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
  11816. if (numSlotsToAlloc === 0)
  11817. return -1;
  11818. if (ngDevMode) {
  11819. assertFirstCreatePass(tView);
  11820. assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
  11821. assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
  11822. assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
  11823. assertFirstUpdatePass(tView);
  11824. }
  11825. const allocIdx = lView.length;
  11826. for (let i = 0; i < numSlotsToAlloc; i++) {
  11827. lView.push(initialValue);
  11828. tView.blueprint.push(initialValue);
  11829. tView.data.push(null);
  11830. }
  11831. return allocIdx;
  11832. }
  11833. function executeTemplate(tView, lView, templateFn, rf, context) {
  11834. const consumer = getReactiveLViewConsumer(lView, REACTIVE_TEMPLATE_CONSUMER);
  11835. const prevSelectedIndex = getSelectedIndex();
  11836. const isUpdatePhase = rf & 2 /* RenderFlags.Update */;
  11837. try {
  11838. setSelectedIndex(-1);
  11839. if (isUpdatePhase && lView.length > HEADER_OFFSET) {
  11840. // When we're updating, inherently select 0 so we don't
  11841. // have to generate that instruction for most update blocks.
  11842. selectIndexInternal(tView, lView, HEADER_OFFSET, !!ngDevMode && isInCheckNoChangesMode());
  11843. }
  11844. const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */;
  11845. profiler(preHookType, context);
  11846. const effectiveConsumer = isUpdatePhase ? consumer : null;
  11847. const prevConsumer = consumerBeforeComputation(effectiveConsumer);
  11848. try {
  11849. if (effectiveConsumer !== null) {
  11850. effectiveConsumer.dirty = false;
  11851. }
  11852. templateFn(rf, context);
  11853. }
  11854. finally {
  11855. consumerAfterComputation(effectiveConsumer, prevConsumer);
  11856. }
  11857. }
  11858. finally {
  11859. if (isUpdatePhase && lView[REACTIVE_TEMPLATE_CONSUMER] === null) {
  11860. commitLViewConsumerIfHasProducers(lView, REACTIVE_TEMPLATE_CONSUMER);
  11861. }
  11862. setSelectedIndex(prevSelectedIndex);
  11863. const postHookType = isUpdatePhase ? 3 /* ProfilerEvent.TemplateUpdateEnd */ : 1 /* ProfilerEvent.TemplateCreateEnd */;
  11864. profiler(postHookType, context);
  11865. }
  11866. }
  11867. //////////////////////////
  11868. //// Element
  11869. //////////////////////////
  11870. function executeContentQueries(tView, tNode, lView) {
  11871. if (isContentQueryHost(tNode)) {
  11872. const prevConsumer = setActiveConsumer(null);
  11873. try {
  11874. const start = tNode.directiveStart;
  11875. const end = tNode.directiveEnd;
  11876. for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
  11877. const def = tView.data[directiveIndex];
  11878. if (def.contentQueries) {
  11879. def.contentQueries(1 /* RenderFlags.Create */, lView[directiveIndex], directiveIndex);
  11880. }
  11881. }
  11882. }
  11883. finally {
  11884. setActiveConsumer(prevConsumer);
  11885. }
  11886. }
  11887. }
  11888. /**
  11889. * Creates directive instances.
  11890. */
  11891. function createDirectivesInstances(tView, lView, tNode) {
  11892. if (!getBindingsEnabled())
  11893. return;
  11894. instantiateAllDirectives(tView, lView, tNode, getNativeByTNode(tNode, lView));
  11895. if ((tNode.flags & 64 /* TNodeFlags.hasHostBindings */) === 64 /* TNodeFlags.hasHostBindings */) {
  11896. invokeDirectivesHostBindings(tView, lView, tNode);
  11897. }
  11898. }
  11899. /**
  11900. * Takes a list of local names and indices and pushes the resolved local variable values
  11901. * to LView in the same order as they are loaded in the template with load().
  11902. */
  11903. function saveResolvedLocalsInData(viewData, tNode, localRefExtractor = getNativeByTNode) {
  11904. const localNames = tNode.localNames;
  11905. if (localNames !== null) {
  11906. let localIndex = tNode.index + 1;
  11907. for (let i = 0; i < localNames.length; i += 2) {
  11908. const index = localNames[i + 1];
  11909. const value = index === -1 ?
  11910. localRefExtractor(tNode, viewData) :
  11911. viewData[index];
  11912. viewData[localIndex++] = value;
  11913. }
  11914. }
  11915. }
  11916. /**
  11917. * Gets TView from a template function or creates a new TView
  11918. * if it doesn't already exist.
  11919. *
  11920. * @param def ComponentDef
  11921. * @returns TView
  11922. */
  11923. function getOrCreateComponentTView(def) {
  11924. const tView = def.tView;
  11925. // Create a TView if there isn't one, or recreate it if the first create pass didn't
  11926. // complete successfully since we can't know for sure whether it's in a usable shape.
  11927. if (tView === null || tView.incompleteFirstPass) {
  11928. // Declaration node here is null since this function is called when we dynamically create a
  11929. // component and hence there is no declaration.
  11930. const declTNode = null;
  11931. 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);
  11932. }
  11933. return tView;
  11934. }
  11935. /**
  11936. * Creates a TView instance
  11937. *
  11938. * @param type Type of `TView`.
  11939. * @param declTNode Declaration location of this `TView`.
  11940. * @param templateFn Template function
  11941. * @param decls The number of nodes, local refs, and pipes in this template
  11942. * @param directives Registry of directives for this view
  11943. * @param pipes Registry of pipes for this view
  11944. * @param viewQuery View queries for this view
  11945. * @param schemas Schemas for this view
  11946. * @param consts Constants for this view
  11947. */
  11948. function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory, ssrId) {
  11949. ngDevMode && ngDevMode.tView++;
  11950. const bindingStartIndex = HEADER_OFFSET + decls;
  11951. // This length does not yet contain host bindings from child directives because at this point,
  11952. // we don't know which directives are active on this template. As soon as a directive is matched
  11953. // that has a host binding, we will update the blueprint with that def's hostVars count.
  11954. const initialViewLength = bindingStartIndex + vars;
  11955. const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength);
  11956. const consts = typeof constsOrFactory === 'function' ? constsOrFactory() : constsOrFactory;
  11957. const tView = blueprint[TVIEW] = {
  11958. type: type,
  11959. blueprint: blueprint,
  11960. template: templateFn,
  11961. queries: null,
  11962. viewQuery: viewQuery,
  11963. declTNode: declTNode,
  11964. data: blueprint.slice().fill(null, bindingStartIndex),
  11965. bindingStartIndex: bindingStartIndex,
  11966. expandoStartIndex: initialViewLength,
  11967. hostBindingOpCodes: null,
  11968. firstCreatePass: true,
  11969. firstUpdatePass: true,
  11970. staticViewQueries: false,
  11971. staticContentQueries: false,
  11972. preOrderHooks: null,
  11973. preOrderCheckHooks: null,
  11974. contentHooks: null,
  11975. contentCheckHooks: null,
  11976. viewHooks: null,
  11977. viewCheckHooks: null,
  11978. destroyHooks: null,
  11979. cleanup: null,
  11980. contentQueries: null,
  11981. components: null,
  11982. directiveRegistry: typeof directives === 'function' ? directives() : directives,
  11983. pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
  11984. firstChild: null,
  11985. schemas: schemas,
  11986. consts: consts,
  11987. incompleteFirstPass: false,
  11988. ssrId,
  11989. };
  11990. if (ngDevMode) {
  11991. // For performance reasons it is important that the tView retains the same shape during runtime.
  11992. // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
  11993. // prevent class transitions.
  11994. Object.seal(tView);
  11995. }
  11996. return tView;
  11997. }
  11998. function createViewBlueprint(bindingStartIndex, initialViewLength) {
  11999. const blueprint = [];
  12000. for (let i = 0; i < initialViewLength; i++) {
  12001. blueprint.push(i < bindingStartIndex ? null : NO_CHANGE);
  12002. }
  12003. return blueprint;
  12004. }
  12005. /**
  12006. * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
  12007. *
  12008. * @param renderer the renderer used to locate the element.
  12009. * @param elementOrSelector Render element or CSS selector to locate the element.
  12010. * @param encapsulation View Encapsulation defined for component that requests host element.
  12011. * @param injector Root view injector instance.
  12012. */
  12013. function locateHostElement(renderer, elementOrSelector, encapsulation, injector) {
  12014. // Note: we use default value for the `PRESERVE_HOST_CONTENT` here even though it's a
  12015. // tree-shakable one (providedIn:'root'). This code path can be triggered during dynamic
  12016. // component creation (after calling ViewContainerRef.createComponent) when an injector
  12017. // instance can be provided. The injector instance might be disconnected from the main DI
  12018. // tree, thus the `PRESERVE_HOST_CONTENT` would not be able to instantiate. In this case, the
  12019. // default value will be used.
  12020. const preserveHostContent = injector.get(PRESERVE_HOST_CONTENT, PRESERVE_HOST_CONTENT_DEFAULT);
  12021. // When using native Shadow DOM, do not clear host element to allow native slot
  12022. // projection.
  12023. const preserveContent = preserveHostContent || encapsulation === ViewEncapsulation$1.ShadowDom;
  12024. const rootElement = renderer.selectRootElement(elementOrSelector, preserveContent);
  12025. applyRootElementTransform(rootElement);
  12026. return rootElement;
  12027. }
  12028. /**
  12029. * Applies any root element transformations that are needed. If hydration is enabled,
  12030. * this will process corrupted text nodes.
  12031. *
  12032. * @param rootElement the app root HTML Element
  12033. */
  12034. function applyRootElementTransform(rootElement) {
  12035. _applyRootElementTransformImpl(rootElement);
  12036. }
  12037. /**
  12038. * Reference to a function that applies transformations to the root HTML element
  12039. * of an app. When hydration is enabled, this processes any corrupt text nodes
  12040. * so they are properly hydratable on the client.
  12041. *
  12042. * @param rootElement the app root HTML Element
  12043. */
  12044. let _applyRootElementTransformImpl = (rootElement) => null;
  12045. /**
  12046. * Processes text node markers before hydration begins. This replaces any special comment
  12047. * nodes that were added prior to serialization are swapped out to restore proper text
  12048. * nodes before hydration.
  12049. *
  12050. * @param rootElement the app root HTML Element
  12051. */
  12052. function applyRootElementTransformImpl(rootElement) {
  12053. if (hasSkipHydrationAttrOnRElement(rootElement)) {
  12054. // Handle a situation when the `ngSkipHydration` attribute is applied
  12055. // to the root node of an application. In this case, we should clear
  12056. // the contents and render everything from scratch.
  12057. clearElementContents(rootElement);
  12058. }
  12059. else {
  12060. processTextNodeMarkersBeforeHydration(rootElement);
  12061. }
  12062. }
  12063. /**
  12064. * Sets the implementation for the `applyRootElementTransform` function.
  12065. */
  12066. function enableApplyRootElementTransformImpl() {
  12067. _applyRootElementTransformImpl = applyRootElementTransformImpl;
  12068. }
  12069. /**
  12070. * Saves context for this cleanup function in LView.cleanupInstances.
  12071. *
  12072. * On the first template pass, saves in TView:
  12073. * - Cleanup function
  12074. * - Index of context we just saved in LView.cleanupInstances
  12075. */
  12076. function storeCleanupWithContext(tView, lView, context, cleanupFn) {
  12077. const lCleanup = getOrCreateLViewCleanup(lView);
  12078. // Historically the `storeCleanupWithContext` was used to register both framework-level and
  12079. // user-defined cleanup callbacks, but over time those two types of cleanups were separated.
  12080. // This dev mode checks assures that user-level cleanup callbacks are _not_ stored in data
  12081. // structures reserved for framework-specific hooks.
  12082. ngDevMode &&
  12083. assertDefined(context, 'Cleanup context is mandatory when registering framework-level destroy hooks');
  12084. lCleanup.push(context);
  12085. if (tView.firstCreatePass) {
  12086. getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1);
  12087. }
  12088. else {
  12089. // Make sure that no new framework-level cleanup functions are registered after the first
  12090. // template pass is done (and TView data structures are meant to fully constructed).
  12091. if (ngDevMode) {
  12092. Object.freeze(getOrCreateTViewCleanup(tView));
  12093. }
  12094. }
  12095. }
  12096. function createTNode(tView, tParent, type, index, value, attrs) {
  12097. ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
  12098. // `view_engine_compatibility` for additional context.
  12099. assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
  12100. ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\'');
  12101. ngDevMode && ngDevMode.tNode++;
  12102. ngDevMode && tParent && assertTNodeForTView(tParent, tView);
  12103. let injectorIndex = tParent ? tParent.injectorIndex : -1;
  12104. let flags = 0;
  12105. if (isInSkipHydrationBlock$1()) {
  12106. flags |= 128 /* TNodeFlags.inSkipHydrationBlock */;
  12107. }
  12108. const tNode = {
  12109. type,
  12110. index,
  12111. insertBeforeIndex: null,
  12112. injectorIndex,
  12113. directiveStart: -1,
  12114. directiveEnd: -1,
  12115. directiveStylingLast: -1,
  12116. componentOffset: -1,
  12117. propertyBindings: null,
  12118. flags,
  12119. providerIndexes: 0,
  12120. value: value,
  12121. attrs: attrs,
  12122. mergedAttrs: null,
  12123. localNames: null,
  12124. initialInputs: undefined,
  12125. inputs: null,
  12126. outputs: null,
  12127. tView: null,
  12128. next: null,
  12129. prev: null,
  12130. projectionNext: null,
  12131. child: null,
  12132. parent: tParent,
  12133. projection: null,
  12134. styles: null,
  12135. stylesWithoutHost: null,
  12136. residualStyles: undefined,
  12137. classes: null,
  12138. classesWithoutHost: null,
  12139. residualClasses: undefined,
  12140. classBindings: 0,
  12141. styleBindings: 0,
  12142. };
  12143. if (ngDevMode) {
  12144. // For performance reasons it is important that the tNode retains the same shape during runtime.
  12145. // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
  12146. // prevent class transitions.
  12147. Object.seal(tNode);
  12148. }
  12149. return tNode;
  12150. }
  12151. /**
  12152. * Generates the `PropertyAliases` data structure from the provided input/output mapping.
  12153. * @param aliasMap Input/output mapping from the directive definition.
  12154. * @param directiveIndex Index of the directive.
  12155. * @param propertyAliases Object in which to store the results.
  12156. * @param hostDirectiveAliasMap Object used to alias or filter out properties for host directives.
  12157. * If the mapping is provided, it'll act as an allowlist, as well as a mapping of what public
  12158. * name inputs/outputs should be exposed under.
  12159. */
  12160. function generatePropertyAliases(aliasMap, directiveIndex, propertyAliases, hostDirectiveAliasMap) {
  12161. for (let publicName in aliasMap) {
  12162. if (aliasMap.hasOwnProperty(publicName)) {
  12163. propertyAliases = propertyAliases === null ? {} : propertyAliases;
  12164. const internalName = aliasMap[publicName];
  12165. // If there are no host directive mappings, we want to remap using the alias map from the
  12166. // definition itself. If there is an alias map, it has two functions:
  12167. // 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
  12168. // ones inside the host directive map will be exposed on the host.
  12169. // 2. The public name of the property is aliased using the host directive alias map, rather
  12170. // than the alias map from the definition.
  12171. if (hostDirectiveAliasMap === null) {
  12172. addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName);
  12173. }
  12174. else if (hostDirectiveAliasMap.hasOwnProperty(publicName)) {
  12175. addPropertyAlias(propertyAliases, directiveIndex, hostDirectiveAliasMap[publicName], internalName);
  12176. }
  12177. }
  12178. }
  12179. return propertyAliases;
  12180. }
  12181. function addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName) {
  12182. if (propertyAliases.hasOwnProperty(publicName)) {
  12183. propertyAliases[publicName].push(directiveIndex, internalName);
  12184. }
  12185. else {
  12186. propertyAliases[publicName] = [directiveIndex, internalName];
  12187. }
  12188. }
  12189. /**
  12190. * Initializes data structures required to work with directive inputs and outputs.
  12191. * Initialization is done for all directives matched on a given TNode.
  12192. */
  12193. function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
  12194. ngDevMode && assertFirstCreatePass(tView);
  12195. const start = tNode.directiveStart;
  12196. const end = tNode.directiveEnd;
  12197. const tViewData = tView.data;
  12198. const tNodeAttrs = tNode.attrs;
  12199. const inputsFromAttrs = [];
  12200. let inputsStore = null;
  12201. let outputsStore = null;
  12202. for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
  12203. const directiveDef = tViewData[directiveIndex];
  12204. const aliasData = hostDirectiveDefinitionMap ? hostDirectiveDefinitionMap.get(directiveDef) : null;
  12205. const aliasedInputs = aliasData ? aliasData.inputs : null;
  12206. const aliasedOutputs = aliasData ? aliasData.outputs : null;
  12207. inputsStore =
  12208. generatePropertyAliases(directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
  12209. outputsStore =
  12210. generatePropertyAliases(directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
  12211. // Do not use unbound attributes as inputs to structural directives, since structural
  12212. // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
  12213. // TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which
  12214. // should be set for inline templates.
  12215. const initialInputs = (inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)) ?
  12216. generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs) :
  12217. null;
  12218. inputsFromAttrs.push(initialInputs);
  12219. }
  12220. if (inputsStore !== null) {
  12221. if (inputsStore.hasOwnProperty('class')) {
  12222. tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
  12223. }
  12224. if (inputsStore.hasOwnProperty('style')) {
  12225. tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
  12226. }
  12227. }
  12228. tNode.initialInputs = inputsFromAttrs;
  12229. tNode.inputs = inputsStore;
  12230. tNode.outputs = outputsStore;
  12231. }
  12232. /**
  12233. * Mapping between attributes names that don't correspond to their element property names.
  12234. *
  12235. * Performance note: this function is written as a series of if checks (instead of, say, a property
  12236. * object lookup) for performance reasons - the series of `if` checks seems to be the fastest way of
  12237. * mapping property names. Do NOT change without benchmarking.
  12238. *
  12239. * Note: this mapping has to be kept in sync with the equally named mapping in the template
  12240. * type-checking machinery of ngtsc.
  12241. */
  12242. function mapPropName(name) {
  12243. if (name === 'class')
  12244. return 'className';
  12245. if (name === 'for')
  12246. return 'htmlFor';
  12247. if (name === 'formaction')
  12248. return 'formAction';
  12249. if (name === 'innerHtml')
  12250. return 'innerHTML';
  12251. if (name === 'readonly')
  12252. return 'readOnly';
  12253. if (name === 'tabindex')
  12254. return 'tabIndex';
  12255. return name;
  12256. }
  12257. function elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, nativeOnly) {
  12258. ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
  12259. const element = getNativeByTNode(tNode, lView);
  12260. let inputData = tNode.inputs;
  12261. let dataValue;
  12262. if (!nativeOnly && inputData != null && (dataValue = inputData[propName])) {
  12263. setInputsForProperty(tView, lView, dataValue, propName, value);
  12264. if (isComponentHost(tNode))
  12265. markDirtyIfOnPush(lView, tNode.index);
  12266. if (ngDevMode) {
  12267. setNgReflectProperties(lView, element, tNode.type, dataValue, value);
  12268. }
  12269. }
  12270. else if (tNode.type & 3 /* TNodeType.AnyRNode */) {
  12271. propName = mapPropName(propName);
  12272. if (ngDevMode) {
  12273. validateAgainstEventProperties(propName);
  12274. if (!isPropertyValid(element, propName, tNode.value, tView.schemas)) {
  12275. handleUnknownPropertyError(propName, tNode.value, tNode.type, lView);
  12276. }
  12277. ngDevMode.rendererSetProperty++;
  12278. }
  12279. // It is assumed that the sanitizer is only added when the compiler determines that the
  12280. // property is risky, so sanitization can be done without further checks.
  12281. value = sanitizer != null ? sanitizer(value, tNode.value || '', propName) : value;
  12282. renderer.setProperty(element, propName, value);
  12283. }
  12284. else if (tNode.type & 12 /* TNodeType.AnyContainer */) {
  12285. // If the node is a container and the property didn't
  12286. // match any of the inputs or schemas we should throw.
  12287. if (ngDevMode && !matchingSchemas(tView.schemas, tNode.value)) {
  12288. handleUnknownPropertyError(propName, tNode.value, tNode.type, lView);
  12289. }
  12290. }
  12291. }
  12292. /** If node is an OnPush component, marks its LView dirty. */
  12293. function markDirtyIfOnPush(lView, viewIndex) {
  12294. ngDevMode && assertLView(lView);
  12295. const childComponentLView = getComponentLViewByIndex(viewIndex, lView);
  12296. if (!(childComponentLView[FLAGS] & 16 /* LViewFlags.CheckAlways */)) {
  12297. childComponentLView[FLAGS] |= 64 /* LViewFlags.Dirty */;
  12298. }
  12299. }
  12300. function setNgReflectProperty(lView, element, type, attrName, value) {
  12301. const renderer = lView[RENDERER];
  12302. attrName = normalizeDebugBindingName(attrName);
  12303. const debugValue = normalizeDebugBindingValue(value);
  12304. if (type & 3 /* TNodeType.AnyRNode */) {
  12305. if (value == null) {
  12306. renderer.removeAttribute(element, attrName);
  12307. }
  12308. else {
  12309. renderer.setAttribute(element, attrName, debugValue);
  12310. }
  12311. }
  12312. else {
  12313. const textContent = escapeCommentText(`bindings=${JSON.stringify({ [attrName]: debugValue }, null, 2)}`);
  12314. renderer.setValue(element, textContent);
  12315. }
  12316. }
  12317. function setNgReflectProperties(lView, element, type, dataValue, value) {
  12318. if (type & (3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */)) {
  12319. /**
  12320. * dataValue is an array containing runtime input or output names for the directives:
  12321. * i+0: directive instance index
  12322. * i+1: privateName
  12323. *
  12324. * e.g. [0, 'change', 'change-minified']
  12325. * we want to set the reflected property with the privateName: dataValue[i+1]
  12326. */
  12327. for (let i = 0; i < dataValue.length; i += 2) {
  12328. setNgReflectProperty(lView, element, type, dataValue[i + 1], value);
  12329. }
  12330. }
  12331. }
  12332. /**
  12333. * Resolve the matched directives on a node.
  12334. */
  12335. function resolveDirectives(tView, lView, tNode, localRefs) {
  12336. // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
  12337. // tsickle.
  12338. ngDevMode && assertFirstCreatePass(tView);
  12339. if (getBindingsEnabled()) {
  12340. const exportsMap = localRefs === null ? null : { '': -1 };
  12341. const matchResult = findDirectiveDefMatches(tView, tNode);
  12342. let directiveDefs;
  12343. let hostDirectiveDefs;
  12344. if (matchResult === null) {
  12345. directiveDefs = hostDirectiveDefs = null;
  12346. }
  12347. else {
  12348. [directiveDefs, hostDirectiveDefs] = matchResult;
  12349. }
  12350. if (directiveDefs !== null) {
  12351. initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
  12352. }
  12353. if (exportsMap)
  12354. cacheMatchingLocalNames(tNode, localRefs, exportsMap);
  12355. }
  12356. // Merge the template attrs last so that they have the highest priority.
  12357. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
  12358. }
  12359. /** Initializes the data structures necessary for a list of directives to be instantiated. */
  12360. function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
  12361. ngDevMode && assertFirstCreatePass(tView);
  12362. // Publishes the directive types to DI so they can be injected. Needs to
  12363. // happen in a separate pass before the TNode flags have been initialized.
  12364. for (let i = 0; i < directives.length; i++) {
  12365. diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
  12366. }
  12367. initTNodeFlags(tNode, tView.data.length, directives.length);
  12368. // When the same token is provided by several directives on the same node, some rules apply in
  12369. // the viewEngine:
  12370. // - viewProviders have priority over providers
  12371. // - the last directive in NgModule.declarations has priority over the previous one
  12372. // So to match these rules, the order in which providers are added in the arrays is very
  12373. // important.
  12374. for (let i = 0; i < directives.length; i++) {
  12375. const def = directives[i];
  12376. if (def.providersResolver)
  12377. def.providersResolver(def);
  12378. }
  12379. let preOrderHooksFound = false;
  12380. let preOrderCheckHooksFound = false;
  12381. let directiveIdx = allocExpando(tView, lView, directives.length, null);
  12382. ngDevMode &&
  12383. assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
  12384. for (let i = 0; i < directives.length; i++) {
  12385. const def = directives[i];
  12386. // Merge the attrs in the order of matches. This assumes that the first directive is the
  12387. // component itself, so that the component has the least priority.
  12388. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
  12389. configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
  12390. saveNameToExportMap(directiveIdx, def, exportsMap);
  12391. if (def.contentQueries !== null)
  12392. tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
  12393. if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
  12394. tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
  12395. const lifeCycleHooks = def.type.prototype;
  12396. // Only push a node index into the preOrderHooks array if this is the first
  12397. // pre-order hook found on this node.
  12398. if (!preOrderHooksFound &&
  12399. (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
  12400. // We will push the actual hook function into this array later during dir instantiation.
  12401. // We cannot do it now because we must ensure hooks are registered in the same
  12402. // order that directives are created (i.e. injection order).
  12403. (tView.preOrderHooks ??= []).push(tNode.index);
  12404. preOrderHooksFound = true;
  12405. }
  12406. if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
  12407. (tView.preOrderCheckHooks ??= []).push(tNode.index);
  12408. preOrderCheckHooksFound = true;
  12409. }
  12410. directiveIdx++;
  12411. }
  12412. initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs);
  12413. }
  12414. /**
  12415. * Add `hostBindings` to the `TView.hostBindingOpCodes`.
  12416. *
  12417. * @param tView `TView` to which the `hostBindings` should be added.
  12418. * @param tNode `TNode` the element which contains the directive
  12419. * @param directiveIdx Directive index in view.
  12420. * @param directiveVarsIdx Where will the directive's vars be stored
  12421. * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
  12422. */
  12423. function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
  12424. ngDevMode && assertFirstCreatePass(tView);
  12425. const hostBindings = def.hostBindings;
  12426. if (hostBindings) {
  12427. let hostBindingOpCodes = tView.hostBindingOpCodes;
  12428. if (hostBindingOpCodes === null) {
  12429. hostBindingOpCodes = tView.hostBindingOpCodes = [];
  12430. }
  12431. const elementIndx = ~tNode.index;
  12432. if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
  12433. // Conditionally add select element so that we are more efficient in execution.
  12434. // NOTE: this is strictly not necessary and it trades code size for runtime perf.
  12435. // (We could just always add it.)
  12436. hostBindingOpCodes.push(elementIndx);
  12437. }
  12438. hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
  12439. }
  12440. }
  12441. /**
  12442. * Returns the last selected element index in the `HostBindingOpCodes`
  12443. *
  12444. * For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
  12445. * if it changes. This method returns the last index (or '0' if not found.)
  12446. *
  12447. * Selected element index are only the ones which are negative.
  12448. */
  12449. function lastSelectedElementIdx(hostBindingOpCodes) {
  12450. let i = hostBindingOpCodes.length;
  12451. while (i > 0) {
  12452. const value = hostBindingOpCodes[--i];
  12453. if (typeof value === 'number' && value < 0) {
  12454. return value;
  12455. }
  12456. }
  12457. return 0;
  12458. }
  12459. /**
  12460. * Instantiate all the directives that were previously resolved on the current node.
  12461. */
  12462. function instantiateAllDirectives(tView, lView, tNode, native) {
  12463. const start = tNode.directiveStart;
  12464. const end = tNode.directiveEnd;
  12465. // The component view needs to be created before creating the node injector
  12466. // since it is used to inject some special symbols like `ChangeDetectorRef`.
  12467. if (isComponentHost(tNode)) {
  12468. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
  12469. addComponentLogic(lView, tNode, tView.data[start + tNode.componentOffset]);
  12470. }
  12471. if (!tView.firstCreatePass) {
  12472. getOrCreateNodeInjectorForNode(tNode, lView);
  12473. }
  12474. attachPatchData(native, lView);
  12475. const initialInputs = tNode.initialInputs;
  12476. for (let i = start; i < end; i++) {
  12477. const def = tView.data[i];
  12478. const directive = getNodeInjectable(lView, tView, i, tNode);
  12479. attachPatchData(directive, lView);
  12480. if (initialInputs !== null) {
  12481. setInputsFromAttrs(lView, i - start, directive, def, tNode, initialInputs);
  12482. }
  12483. if (isComponentDef(def)) {
  12484. const componentView = getComponentLViewByIndex(tNode.index, lView);
  12485. componentView[CONTEXT] = getNodeInjectable(lView, tView, i, tNode);
  12486. }
  12487. }
  12488. }
  12489. function invokeDirectivesHostBindings(tView, lView, tNode) {
  12490. const start = tNode.directiveStart;
  12491. const end = tNode.directiveEnd;
  12492. const elementIndex = tNode.index;
  12493. const currentDirectiveIndex = getCurrentDirectiveIndex();
  12494. try {
  12495. setSelectedIndex(elementIndex);
  12496. for (let dirIndex = start; dirIndex < end; dirIndex++) {
  12497. const def = tView.data[dirIndex];
  12498. const directive = lView[dirIndex];
  12499. setCurrentDirectiveIndex(dirIndex);
  12500. if (def.hostBindings !== null || def.hostVars !== 0 || def.hostAttrs !== null) {
  12501. invokeHostBindingsInCreationMode(def, directive);
  12502. }
  12503. }
  12504. }
  12505. finally {
  12506. setSelectedIndex(-1);
  12507. setCurrentDirectiveIndex(currentDirectiveIndex);
  12508. }
  12509. }
  12510. /**
  12511. * Invoke the host bindings in creation mode.
  12512. *
  12513. * @param def `DirectiveDef` which may contain the `hostBindings` function.
  12514. * @param directive Instance of directive.
  12515. */
  12516. function invokeHostBindingsInCreationMode(def, directive) {
  12517. if (def.hostBindings !== null) {
  12518. def.hostBindings(1 /* RenderFlags.Create */, directive);
  12519. }
  12520. }
  12521. /**
  12522. * Matches the current node against all available selectors.
  12523. * If a component is matched (at most one), it is returned in first position in the array.
  12524. */
  12525. function findDirectiveDefMatches(tView, tNode) {
  12526. ngDevMode && assertFirstCreatePass(tView);
  12527. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
  12528. const registry = tView.directiveRegistry;
  12529. let matches = null;
  12530. let hostDirectiveDefs = null;
  12531. if (registry) {
  12532. for (let i = 0; i < registry.length; i++) {
  12533. const def = registry[i];
  12534. if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */ false)) {
  12535. matches || (matches = []);
  12536. if (isComponentDef(def)) {
  12537. if (ngDevMode) {
  12538. assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` +
  12539. `Please use a different tag to activate the ${stringify(def.type)} component.`);
  12540. if (isComponentHost(tNode)) {
  12541. throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type);
  12542. }
  12543. }
  12544. // Components are inserted at the front of the matches array so that their lifecycle
  12545. // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
  12546. // compatibility. This logic doesn't make sense with host directives, because it
  12547. // would allow the host directives to undo any overrides the host may have made.
  12548. // To handle this case, the host directives of components are inserted at the beginning
  12549. // of the array, followed by the component. As such, the insertion order is as follows:
  12550. // 1. Host directives belonging to the selector-matched component.
  12551. // 2. Selector-matched component.
  12552. // 3. Host directives belonging to selector-matched directives.
  12553. // 4. Selector-matched directives.
  12554. if (def.findHostDirectiveDefs !== null) {
  12555. const hostDirectiveMatches = [];
  12556. hostDirectiveDefs = hostDirectiveDefs || new Map();
  12557. def.findHostDirectiveDefs(def, hostDirectiveMatches, hostDirectiveDefs);
  12558. // Add all host directives declared on this component, followed by the component itself.
  12559. // Host directives should execute first so the host has a chance to override changes
  12560. // to the DOM made by them.
  12561. matches.unshift(...hostDirectiveMatches, def);
  12562. // Component is offset starting from the beginning of the host directives array.
  12563. const componentOffset = hostDirectiveMatches.length;
  12564. markAsComponentHost(tView, tNode, componentOffset);
  12565. }
  12566. else {
  12567. // No host directives on this component, just add the
  12568. // component def to the beginning of the matches.
  12569. matches.unshift(def);
  12570. markAsComponentHost(tView, tNode, 0);
  12571. }
  12572. }
  12573. else {
  12574. // Append any host directives to the matches first.
  12575. hostDirectiveDefs = hostDirectiveDefs || new Map();
  12576. def.findHostDirectiveDefs?.(def, matches, hostDirectiveDefs);
  12577. matches.push(def);
  12578. }
  12579. }
  12580. }
  12581. }
  12582. return matches === null ? null : [matches, hostDirectiveDefs];
  12583. }
  12584. /**
  12585. * Marks a given TNode as a component's host. This consists of:
  12586. * - setting the component offset on the TNode.
  12587. * - storing index of component's host element so it will be queued for view refresh during CD.
  12588. */
  12589. function markAsComponentHost(tView, hostTNode, componentOffset) {
  12590. ngDevMode && assertFirstCreatePass(tView);
  12591. ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1');
  12592. hostTNode.componentOffset = componentOffset;
  12593. (tView.components ??= []).push(hostTNode.index);
  12594. }
  12595. /** Caches local names and their matching directive indices for query and template lookups. */
  12596. function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
  12597. if (localRefs) {
  12598. const localNames = tNode.localNames = [];
  12599. // Local names must be stored in tNode in the same order that localRefs are defined
  12600. // in the template to ensure the data is loaded in the same slots as their refs
  12601. // in the template (for template queries).
  12602. for (let i = 0; i < localRefs.length; i += 2) {
  12603. const index = exportsMap[localRefs[i + 1]];
  12604. if (index == null)
  12605. throw new RuntimeError(-301 /* RuntimeErrorCode.EXPORT_NOT_FOUND */, ngDevMode && `Export of name '${localRefs[i + 1]}' not found!`);
  12606. localNames.push(localRefs[i], index);
  12607. }
  12608. }
  12609. }
  12610. /**
  12611. * Builds up an export map as directives are created, so local refs can be quickly mapped
  12612. * to their directive instances.
  12613. */
  12614. function saveNameToExportMap(directiveIdx, def, exportsMap) {
  12615. if (exportsMap) {
  12616. if (def.exportAs) {
  12617. for (let i = 0; i < def.exportAs.length; i++) {
  12618. exportsMap[def.exportAs[i]] = directiveIdx;
  12619. }
  12620. }
  12621. if (isComponentDef(def))
  12622. exportsMap[''] = directiveIdx;
  12623. }
  12624. }
  12625. /**
  12626. * Initializes the flags on the current node, setting all indices to the initial index,
  12627. * the directive count to 0, and adding the isComponent flag.
  12628. * @param index the initial index
  12629. */
  12630. function initTNodeFlags(tNode, index, numberOfDirectives) {
  12631. ngDevMode &&
  12632. assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
  12633. tNode.flags |= 1 /* TNodeFlags.isDirectiveHost */;
  12634. // When the first directive is created on a node, save the index
  12635. tNode.directiveStart = index;
  12636. tNode.directiveEnd = index + numberOfDirectives;
  12637. tNode.providerIndexes = index;
  12638. }
  12639. /**
  12640. * Setup directive for instantiation.
  12641. *
  12642. * We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
  12643. * as `LView`. `TView` gets the `DirectiveDef`.
  12644. *
  12645. * @param tView `TView`
  12646. * @param tNode `TNode`
  12647. * @param lView `LView`
  12648. * @param directiveIndex Index where the directive will be stored in the Expando.
  12649. * @param def `DirectiveDef`
  12650. */
  12651. function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
  12652. ngDevMode &&
  12653. assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
  12654. tView.data[directiveIndex] = def;
  12655. const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
  12656. // Even though `directiveFactory` will already be using `ɵɵdirectiveInject` in its generated code,
  12657. // we also want to support `inject()` directly from the directive constructor context so we set
  12658. // `ɵɵdirectiveInject` as the inject implementation here too.
  12659. const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
  12660. tView.blueprint[directiveIndex] = nodeInjectorFactory;
  12661. lView[directiveIndex] = nodeInjectorFactory;
  12662. registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
  12663. }
  12664. function addComponentLogic(lView, hostTNode, def) {
  12665. const native = getNativeByTNode(hostTNode, lView);
  12666. const tView = getOrCreateComponentTView(def);
  12667. // Only component views should be added to the view tree directly. Embedded views are
  12668. // accessed through their containers because they may be removed / re-added later.
  12669. const rendererFactory = lView[ENVIRONMENT].rendererFactory;
  12670. let lViewFlags = 16 /* LViewFlags.CheckAlways */;
  12671. if (def.signals) {
  12672. lViewFlags = 4096 /* LViewFlags.SignalView */;
  12673. }
  12674. else if (def.onPush) {
  12675. lViewFlags = 64 /* LViewFlags.Dirty */;
  12676. }
  12677. const componentView = addToViewTree(lView, createLView(lView, tView, null, lViewFlags, native, hostTNode, null, rendererFactory.createRenderer(native, def), null, null, null));
  12678. // Component view will always be created before any injected LContainers,
  12679. // so this is a regular element, wrap it with the component view
  12680. lView[hostTNode.index] = componentView;
  12681. }
  12682. function elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace) {
  12683. if (ngDevMode) {
  12684. assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
  12685. validateAgainstEventAttributes(name);
  12686. assertTNodeType(tNode, 2 /* TNodeType.Element */, `Attempted to set attribute \`${name}\` on a container node. ` +
  12687. `Host bindings are not valid on ng-container or ng-template.`);
  12688. }
  12689. const element = getNativeByTNode(tNode, lView);
  12690. setElementAttribute(lView[RENDERER], element, namespace, tNode.value, name, value, sanitizer);
  12691. }
  12692. function setElementAttribute(renderer, element, namespace, tagName, name, value, sanitizer) {
  12693. if (value == null) {
  12694. ngDevMode && ngDevMode.rendererRemoveAttribute++;
  12695. renderer.removeAttribute(element, name, namespace);
  12696. }
  12697. else {
  12698. ngDevMode && ngDevMode.rendererSetAttribute++;
  12699. const strValue = sanitizer == null ? renderStringify(value) : sanitizer(value, tagName || '', name);
  12700. renderer.setAttribute(element, name, strValue, namespace);
  12701. }
  12702. }
  12703. /**
  12704. * Sets initial input properties on directive instances from attribute data
  12705. *
  12706. * @param lView Current LView that is being processed.
  12707. * @param directiveIndex Index of the directive in directives array
  12708. * @param instance Instance of the directive on which to set the initial inputs
  12709. * @param def The directive def that contains the list of inputs
  12710. * @param tNode The static data for this node
  12711. */
  12712. function setInputsFromAttrs(lView, directiveIndex, instance, def, tNode, initialInputData) {
  12713. const initialInputs = initialInputData[directiveIndex];
  12714. if (initialInputs !== null) {
  12715. for (let i = 0; i < initialInputs.length;) {
  12716. const publicName = initialInputs[i++];
  12717. const privateName = initialInputs[i++];
  12718. const value = initialInputs[i++];
  12719. writeToDirectiveInput(def, instance, publicName, privateName, value);
  12720. if (ngDevMode) {
  12721. const nativeElement = getNativeByTNode(tNode, lView);
  12722. setNgReflectProperty(lView, nativeElement, tNode.type, privateName, value);
  12723. }
  12724. }
  12725. }
  12726. }
  12727. function writeToDirectiveInput(def, instance, publicName, privateName, value) {
  12728. const prevConsumer = setActiveConsumer(null);
  12729. try {
  12730. const inputTransforms = def.inputTransforms;
  12731. if (inputTransforms !== null && inputTransforms.hasOwnProperty(privateName)) {
  12732. value = inputTransforms[privateName].call(instance, value);
  12733. }
  12734. if (def.setInput !== null) {
  12735. def.setInput(instance, value, publicName, privateName);
  12736. }
  12737. else {
  12738. instance[privateName] = value;
  12739. }
  12740. }
  12741. finally {
  12742. setActiveConsumer(prevConsumer);
  12743. }
  12744. }
  12745. /**
  12746. * Generates initialInputData for a node and stores it in the template's static storage
  12747. * so subsequent template invocations don't have to recalculate it.
  12748. *
  12749. * initialInputData is an array containing values that need to be set as input properties
  12750. * for directives on this node, but only once on creation. We need this array to support
  12751. * the case where you set an @Input property of a directive using attribute-like syntax.
  12752. * e.g. if you have a `name` @Input, you can set it once like this:
  12753. *
  12754. * <my-component name="Bess"></my-component>
  12755. *
  12756. * @param inputs Input alias map that was generated from the directive def inputs.
  12757. * @param directiveIndex Index of the directive that is currently being processed.
  12758. * @param attrs Static attrs on this node.
  12759. */
  12760. function generateInitialInputs(inputs, directiveIndex, attrs) {
  12761. let inputsToStore = null;
  12762. let i = 0;
  12763. while (i < attrs.length) {
  12764. const attrName = attrs[i];
  12765. if (attrName === 0 /* AttributeMarker.NamespaceURI */) {
  12766. // We do not allow inputs on namespaced attributes.
  12767. i += 4;
  12768. continue;
  12769. }
  12770. else if (attrName === 5 /* AttributeMarker.ProjectAs */) {
  12771. // Skip over the `ngProjectAs` value.
  12772. i += 2;
  12773. continue;
  12774. }
  12775. // If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
  12776. if (typeof attrName === 'number')
  12777. break;
  12778. if (inputs.hasOwnProperty(attrName)) {
  12779. if (inputsToStore === null)
  12780. inputsToStore = [];
  12781. // Find the input's public name from the input store. Note that we can be found easier
  12782. // through the directive def, but we want to do it using the inputs store so that it can
  12783. // account for host directive aliases.
  12784. const inputConfig = inputs[attrName];
  12785. for (let j = 0; j < inputConfig.length; j += 2) {
  12786. if (inputConfig[j] === directiveIndex) {
  12787. inputsToStore.push(attrName, inputConfig[j + 1], attrs[i + 1]);
  12788. // A directive can't have multiple inputs with the same name so we can break here.
  12789. break;
  12790. }
  12791. }
  12792. }
  12793. i += 2;
  12794. }
  12795. return inputsToStore;
  12796. }
  12797. //////////////////////////
  12798. //// ViewContainer & View
  12799. //////////////////////////
  12800. /**
  12801. * Creates a LContainer, either from a container instruction, or for a ViewContainerRef.
  12802. *
  12803. * @param hostNative The host element for the LContainer
  12804. * @param hostTNode The host TNode for the LContainer
  12805. * @param currentView The parent view of the LContainer
  12806. * @param native The native comment element
  12807. * @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case
  12808. * @returns LContainer
  12809. */
  12810. function createLContainer(hostNative, currentView, native, tNode) {
  12811. ngDevMode && assertLView(currentView);
  12812. const lContainer = [
  12813. hostNative,
  12814. true,
  12815. false,
  12816. currentView,
  12817. null,
  12818. 0,
  12819. tNode,
  12820. native,
  12821. null,
  12822. null,
  12823. null, // dehydrated views
  12824. ];
  12825. ngDevMode &&
  12826. assertEqual(lContainer.length, CONTAINER_HEADER_OFFSET, 'Should allocate correct number of slots for LContainer header.');
  12827. return lContainer;
  12828. }
  12829. /** Refreshes all content queries declared by directives in a given view */
  12830. function refreshContentQueries(tView, lView) {
  12831. const contentQueries = tView.contentQueries;
  12832. if (contentQueries !== null) {
  12833. for (let i = 0; i < contentQueries.length; i += 2) {
  12834. const queryStartIdx = contentQueries[i];
  12835. const directiveDefIdx = contentQueries[i + 1];
  12836. if (directiveDefIdx !== -1) {
  12837. const directiveDef = tView.data[directiveDefIdx];
  12838. ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.');
  12839. ngDevMode &&
  12840. assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
  12841. setCurrentQueryIndex(queryStartIdx);
  12842. directiveDef.contentQueries(2 /* RenderFlags.Update */, lView[directiveDefIdx], directiveDefIdx);
  12843. }
  12844. }
  12845. }
  12846. }
  12847. /**
  12848. * Adds LView or LContainer to the end of the current view tree.
  12849. *
  12850. * This structure will be used to traverse through nested views to remove listeners
  12851. * and call onDestroy callbacks.
  12852. *
  12853. * @param lView The view where LView or LContainer should be added
  12854. * @param adjustedHostIndex Index of the view's host node in LView[], adjusted for header
  12855. * @param lViewOrLContainer The LView or LContainer to add to the view tree
  12856. * @returns The state passed in
  12857. */
  12858. function addToViewTree(lView, lViewOrLContainer) {
  12859. // TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer
  12860. // to the end of the queue, which means if the developer retrieves the LContainers from RNodes out
  12861. // of order, the change detection will run out of order, as the act of retrieving the the
  12862. // LContainer from the RNode is what adds it to the queue.
  12863. if (lView[CHILD_HEAD]) {
  12864. lView[CHILD_TAIL][NEXT] = lViewOrLContainer;
  12865. }
  12866. else {
  12867. lView[CHILD_HEAD] = lViewOrLContainer;
  12868. }
  12869. lView[CHILD_TAIL] = lViewOrLContainer;
  12870. return lViewOrLContainer;
  12871. }
  12872. ///////////////////////////////
  12873. //// Change detection
  12874. ///////////////////////////////
  12875. function executeViewQueryFn(flags, viewQueryFn, component) {
  12876. ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');
  12877. setCurrentQueryIndex(0);
  12878. const prevConsumer = setActiveConsumer(null);
  12879. try {
  12880. viewQueryFn(flags, component);
  12881. }
  12882. finally {
  12883. setActiveConsumer(prevConsumer);
  12884. }
  12885. }
  12886. ///////////////////////////////
  12887. //// Bindings & interpolations
  12888. ///////////////////////////////
  12889. /**
  12890. * Stores meta-data for a property binding to be used by TestBed's `DebugElement.properties`.
  12891. *
  12892. * In order to support TestBed's `DebugElement.properties` we need to save, for each binding:
  12893. * - a bound property name;
  12894. * - a static parts of interpolated strings;
  12895. *
  12896. * A given property metadata is saved at the binding's index in the `TView.data` (in other words, a
  12897. * property binding metadata will be stored in `TView.data` at the same index as a bound value in
  12898. * `LView`). Metadata are represented as `INTERPOLATION_DELIMITER`-delimited string with the
  12899. * following format:
  12900. * - `propertyName` for bound properties;
  12901. * - `propertyName�prefix�interpolation_static_part1�..interpolation_static_partN�suffix` for
  12902. * interpolated properties.
  12903. *
  12904. * @param tData `TData` where meta-data will be saved;
  12905. * @param tNode `TNode` that is a target of the binding;
  12906. * @param propertyName bound property name;
  12907. * @param bindingIndex binding index in `LView`
  12908. * @param interpolationParts static interpolation parts (for property interpolations)
  12909. */
  12910. function storePropertyBindingMetadata(tData, tNode, propertyName, bindingIndex, ...interpolationParts) {
  12911. // Binding meta-data are stored only the first time a given property instruction is processed.
  12912. // Since we don't have a concept of the "first update pass" we need to check for presence of the
  12913. // binding meta-data to decide if one should be stored (or if was stored already).
  12914. if (tData[bindingIndex] === null) {
  12915. if (tNode.inputs == null || !tNode.inputs[propertyName]) {
  12916. const propBindingIdxs = tNode.propertyBindings || (tNode.propertyBindings = []);
  12917. propBindingIdxs.push(bindingIndex);
  12918. let bindingMetadata = propertyName;
  12919. if (interpolationParts.length > 0) {
  12920. bindingMetadata +=
  12921. INTERPOLATION_DELIMITER + interpolationParts.join(INTERPOLATION_DELIMITER);
  12922. }
  12923. tData[bindingIndex] = bindingMetadata;
  12924. }
  12925. }
  12926. }
  12927. function getOrCreateLViewCleanup(view) {
  12928. // top level variables should not be exported for performance reasons (PERF_NOTES.md)
  12929. return view[CLEANUP] || (view[CLEANUP] = []);
  12930. }
  12931. function getOrCreateTViewCleanup(tView) {
  12932. return tView.cleanup || (tView.cleanup = []);
  12933. }
  12934. /**
  12935. * There are cases where the sub component's renderer needs to be included
  12936. * instead of the current renderer (see the componentSyntheticHost* instructions).
  12937. */
  12938. function loadComponentRenderer(currentDef, tNode, lView) {
  12939. // TODO(FW-2043): the `currentDef` is null when host bindings are invoked while creating root
  12940. // component (see packages/core/src/render3/component.ts). This is not consistent with the process
  12941. // of creating inner components, when current directive index is available in the state. In order
  12942. // to avoid relying on current def being `null` (thus special-casing root component creation), the
  12943. // process of creating root component should be unified with the process of creating inner
  12944. // components.
  12945. if (currentDef === null || isComponentDef(currentDef)) {
  12946. lView = unwrapLView(lView[tNode.index]);
  12947. }
  12948. return lView[RENDERER];
  12949. }
  12950. /** Handles an error thrown in an LView. */
  12951. function handleError(lView, error) {
  12952. const injector = lView[INJECTOR$1];
  12953. const errorHandler = injector ? injector.get(ErrorHandler, null) : null;
  12954. errorHandler && errorHandler.handleError(error);
  12955. }
  12956. /**
  12957. * Set the inputs of directives at the current node to corresponding value.
  12958. *
  12959. * @param tView The current TView
  12960. * @param lView the `LView` which contains the directives.
  12961. * @param inputs mapping between the public "input" name and privately-known,
  12962. * possibly minified, property names to write to.
  12963. * @param value Value to set.
  12964. */
  12965. function setInputsForProperty(tView, lView, inputs, publicName, value) {
  12966. for (let i = 0; i < inputs.length;) {
  12967. const index = inputs[i++];
  12968. const privateName = inputs[i++];
  12969. const instance = lView[index];
  12970. ngDevMode && assertIndexInRange(lView, index);
  12971. const def = tView.data[index];
  12972. writeToDirectiveInput(def, instance, publicName, privateName, value);
  12973. }
  12974. }
  12975. /**
  12976. * Updates a text binding at a given index in a given LView.
  12977. */
  12978. function textBindingInternal(lView, index, value) {
  12979. ngDevMode && assertString(value, 'Value should be a string');
  12980. ngDevMode && assertNotSame(value, NO_CHANGE, 'value should not be NO_CHANGE');
  12981. ngDevMode && assertIndexInRange(lView, index);
  12982. const element = getNativeByIndex(index, lView);
  12983. ngDevMode && assertDefined(element, 'native element should exist');
  12984. updateTextNode(lView[RENDERER], element, value);
  12985. }
  12986. function renderComponent(hostLView, componentHostIdx) {
  12987. ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
  12988. const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
  12989. const componentTView = componentView[TVIEW];
  12990. syncViewWithBlueprint(componentTView, componentView);
  12991. const hostRNode = componentView[HOST];
  12992. // Populate an LView with hydration info retrieved from the DOM via TransferState.
  12993. if (hostRNode !== null && componentView[HYDRATION] === null) {
  12994. componentView[HYDRATION] = retrieveHydrationInfo(hostRNode, componentView[INJECTOR$1]);
  12995. }
  12996. renderView(componentTView, componentView, componentView[CONTEXT]);
  12997. }
  12998. /**
  12999. * Syncs an LView instance with its blueprint if they have gotten out of sync.
  13000. *
  13001. * Typically, blueprints and their view instances should always be in sync, so the loop here
  13002. * will be skipped. However, consider this case of two components side-by-side:
  13003. *
  13004. * App template:
  13005. * ```
  13006. * <comp></comp>
  13007. * <comp></comp>
  13008. * ```
  13009. *
  13010. * The following will happen:
  13011. * 1. App template begins processing.
  13012. * 2. First <comp> is matched as a component and its LView is created.
  13013. * 3. Second <comp> is matched as a component and its LView is created.
  13014. * 4. App template completes processing, so it's time to check child templates.
  13015. * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
  13016. * 6. Second <comp> template is checked. Its blueprint has been updated by the first
  13017. * <comp> template, but its LView was created before this update, so it is out of sync.
  13018. *
  13019. * Note that embedded views inside ngFor loops will never be out of sync because these views
  13020. * are processed as soon as they are created.
  13021. *
  13022. * @param tView The `TView` that contains the blueprint for syncing
  13023. * @param lView The view to sync
  13024. */
  13025. function syncViewWithBlueprint(tView, lView) {
  13026. for (let i = lView.length; i < tView.blueprint.length; i++) {
  13027. lView.push(tView.blueprint[i]);
  13028. }
  13029. }
  13030. /**
  13031. * Processes a view in the creation mode. This includes a number of steps in a specific order:
  13032. * - creating view query functions (if any);
  13033. * - executing a template function in the creation mode;
  13034. * - updating static queries (if any);
  13035. * - creating child components defined in a given view.
  13036. */
  13037. function renderView(tView, lView, context) {
  13038. ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
  13039. enterView(lView);
  13040. try {
  13041. const viewQuery = tView.viewQuery;
  13042. if (viewQuery !== null) {
  13043. executeViewQueryFn(1 /* RenderFlags.Create */, viewQuery, context);
  13044. }
  13045. // Execute a template associated with this view, if it exists. A template function might not be
  13046. // defined for the root component views.
  13047. const templateFn = tView.template;
  13048. if (templateFn !== null) {
  13049. executeTemplate(tView, lView, templateFn, 1 /* RenderFlags.Create */, context);
  13050. }
  13051. // This needs to be set before children are processed to support recursive components.
  13052. // This must be set to false immediately after the first creation run because in an
  13053. // ngFor loop, all the views will be created together before update mode runs and turns
  13054. // off firstCreatePass. If we don't set it here, instances will perform directive
  13055. // matching, etc again and again.
  13056. if (tView.firstCreatePass) {
  13057. tView.firstCreatePass = false;
  13058. }
  13059. // We resolve content queries specifically marked as `static` in creation mode. Dynamic
  13060. // content queries are resolved during change detection (i.e. update mode), after embedded
  13061. // views are refreshed (see block above).
  13062. if (tView.staticContentQueries) {
  13063. refreshContentQueries(tView, lView);
  13064. }
  13065. // We must materialize query results before child components are processed
  13066. // in case a child component has projected a container. The LContainer needs
  13067. // to exist so the embedded views are properly attached by the container.
  13068. if (tView.staticViewQueries) {
  13069. executeViewQueryFn(2 /* RenderFlags.Update */, tView.viewQuery, context);
  13070. }
  13071. // Render child component views.
  13072. const components = tView.components;
  13073. if (components !== null) {
  13074. renderChildComponents(lView, components);
  13075. }
  13076. }
  13077. catch (error) {
  13078. // If we didn't manage to get past the first template pass due to
  13079. // an error, mark the view as corrupted so we can try to recover.
  13080. if (tView.firstCreatePass) {
  13081. tView.incompleteFirstPass = true;
  13082. tView.firstCreatePass = false;
  13083. }
  13084. throw error;
  13085. }
  13086. finally {
  13087. lView[FLAGS] &= ~4 /* LViewFlags.CreationMode */;
  13088. leaveView();
  13089. }
  13090. }
  13091. /** Renders child components in the current view (creation mode). */
  13092. function renderChildComponents(hostLView, components) {
  13093. for (let i = 0; i < components.length; i++) {
  13094. renderComponent(hostLView, components[i]);
  13095. }
  13096. }
  13097. /**
  13098. * Tracks all effects registered within a given application and runs them via `flush`.
  13099. */
  13100. class EffectManager {
  13101. constructor() {
  13102. this.all = new Set();
  13103. this.queue = new Map();
  13104. }
  13105. create(effectFn, destroyRef, allowSignalWrites) {
  13106. const zone = (typeof Zone === 'undefined') ? null : Zone.current;
  13107. const w = watch(effectFn, (watch) => {
  13108. if (!this.all.has(watch)) {
  13109. return;
  13110. }
  13111. this.queue.set(watch, zone);
  13112. }, allowSignalWrites);
  13113. this.all.add(w);
  13114. // Effects start dirty.
  13115. w.notify();
  13116. let unregisterOnDestroy;
  13117. const destroy = () => {
  13118. w.cleanup();
  13119. unregisterOnDestroy?.();
  13120. this.all.delete(w);
  13121. this.queue.delete(w);
  13122. };
  13123. unregisterOnDestroy = destroyRef?.onDestroy(destroy);
  13124. return {
  13125. destroy,
  13126. };
  13127. }
  13128. flush() {
  13129. if (this.queue.size === 0) {
  13130. return;
  13131. }
  13132. for (const [watch, zone] of this.queue) {
  13133. this.queue.delete(watch);
  13134. if (zone) {
  13135. zone.run(() => watch.run());
  13136. }
  13137. else {
  13138. watch.run();
  13139. }
  13140. }
  13141. }
  13142. get isQueueEmpty() {
  13143. return this.queue.size === 0;
  13144. }
  13145. /** @nocollapse */
  13146. static { this.ɵprov = ɵɵdefineInjectable({
  13147. token: EffectManager,
  13148. providedIn: 'root',
  13149. factory: () => new EffectManager(),
  13150. }); }
  13151. }
  13152. /**
  13153. * Create a global `Effect` for the given reactive function.
  13154. *
  13155. * @developerPreview
  13156. */
  13157. function effect(effectFn, options) {
  13158. !options?.injector && assertInInjectionContext(effect);
  13159. const injector = options?.injector ?? inject(Injector);
  13160. const effectManager = injector.get(EffectManager);
  13161. const destroyRef = options?.manualCleanup !== true ? injector.get(DestroyRef) : null;
  13162. return effectManager.create(effectFn, destroyRef, !!options?.allowSignalWrites);
  13163. }
  13164. /**
  13165. * Compute the static styling (class/style) from `TAttributes`.
  13166. *
  13167. * This function should be called during `firstCreatePass` only.
  13168. *
  13169. * @param tNode The `TNode` into which the styling information should be loaded.
  13170. * @param attrs `TAttributes` containing the styling information.
  13171. * @param writeToHost Where should the resulting static styles be written?
  13172. * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost`
  13173. * - `true` Write to `TNode.styles` / `TNode.classes`
  13174. */
  13175. function computeStaticStyling(tNode, attrs, writeToHost) {
  13176. ngDevMode &&
  13177. assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only');
  13178. let styles = writeToHost ? tNode.styles : null;
  13179. let classes = writeToHost ? tNode.classes : null;
  13180. let mode = 0;
  13181. if (attrs !== null) {
  13182. for (let i = 0; i < attrs.length; i++) {
  13183. const value = attrs[i];
  13184. if (typeof value === 'number') {
  13185. mode = value;
  13186. }
  13187. else if (mode == 1 /* AttributeMarker.Classes */) {
  13188. classes = concatStringsWithSpace(classes, value);
  13189. }
  13190. else if (mode == 2 /* AttributeMarker.Styles */) {
  13191. const style = value;
  13192. const styleValue = attrs[++i];
  13193. styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';');
  13194. }
  13195. }
  13196. }
  13197. writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles;
  13198. writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes;
  13199. }
  13200. function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
  13201. while (tNode !== null) {
  13202. ngDevMode &&
  13203. assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
  13204. const lNode = lView[tNode.index];
  13205. if (lNode !== null) {
  13206. result.push(unwrapRNode(lNode));
  13207. }
  13208. // A given lNode can represent either a native node or a LContainer (when it is a host of a
  13209. // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
  13210. // from the views in this container.
  13211. if (isLContainer(lNode)) {
  13212. collectNativeNodesInLContainer(lNode, result);
  13213. }
  13214. const tNodeType = tNode.type;
  13215. if (tNodeType & 8 /* TNodeType.ElementContainer */) {
  13216. collectNativeNodes(tView, lView, tNode.child, result);
  13217. }
  13218. else if (tNodeType & 32 /* TNodeType.Icu */) {
  13219. const nextRNode = icuContainerIterate(tNode, lView);
  13220. let rNode;
  13221. while (rNode = nextRNode()) {
  13222. result.push(rNode);
  13223. }
  13224. }
  13225. else if (tNodeType & 16 /* TNodeType.Projection */) {
  13226. const nodesInSlot = getProjectionNodes(lView, tNode);
  13227. if (Array.isArray(nodesInSlot)) {
  13228. result.push(...nodesInSlot);
  13229. }
  13230. else {
  13231. const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
  13232. ngDevMode && assertParentView(parentView);
  13233. collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
  13234. }
  13235. }
  13236. tNode = isProjection ? tNode.projectionNext : tNode.next;
  13237. }
  13238. return result;
  13239. }
  13240. /**
  13241. * Collects all root nodes in all views in a given LContainer.
  13242. */
  13243. function collectNativeNodesInLContainer(lContainer, result) {
  13244. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  13245. const lViewInAContainer = lContainer[i];
  13246. const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
  13247. if (lViewFirstChildTNode !== null) {
  13248. collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
  13249. }
  13250. }
  13251. // When an LContainer is created, the anchor (comment) node is:
  13252. // - (1) either reused in case of an ElementContainer (<ng-container>)
  13253. // - (2) or a new comment node is created
  13254. // In the first case, the anchor comment node would be added to the final
  13255. // list by the code in the `collectNativeNodes` function
  13256. // (see the `result.push(unwrapRNode(lNode))` line), but the second
  13257. // case requires extra handling: the anchor node needs to be added to the
  13258. // final list manually. See additional information in the `createAnchorNode`
  13259. // function in the `view_container_ref.ts`.
  13260. //
  13261. // In the first case, the same reference would be stored in the `NATIVE`
  13262. // and `HOST` slots in an LContainer. Otherwise, this is the second case and
  13263. // we should add an element to the final list.
  13264. if (lContainer[NATIVE] !== lContainer[HOST]) {
  13265. result.push(lContainer[NATIVE]);
  13266. }
  13267. }
  13268. function detectChangesInternal(tView, lView, context, notifyErrorHandler = true) {
  13269. const environment = lView[ENVIRONMENT];
  13270. const rendererFactory = environment.rendererFactory;
  13271. const afterRenderEventManager = environment.afterRenderEventManager;
  13272. // Check no changes mode is a dev only mode used to verify that bindings have not changed
  13273. // since they were assigned. We do not want to invoke renderer factory functions in that mode
  13274. // to avoid any possible side-effects.
  13275. const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode();
  13276. if (!checkNoChangesMode) {
  13277. rendererFactory.begin?.();
  13278. afterRenderEventManager?.begin();
  13279. }
  13280. try {
  13281. refreshView(tView, lView, tView.template, context);
  13282. }
  13283. catch (error) {
  13284. if (notifyErrorHandler) {
  13285. handleError(lView, error);
  13286. }
  13287. throw error;
  13288. }
  13289. finally {
  13290. if (!checkNoChangesMode) {
  13291. rendererFactory.end?.();
  13292. // One final flush of the effects queue to catch any effects created in `ngAfterViewInit` or
  13293. // other post-order hooks.
  13294. environment.effectManager?.flush();
  13295. // Invoke all callbacks registered via `after*Render`, if needed.
  13296. afterRenderEventManager?.end();
  13297. }
  13298. }
  13299. }
  13300. function checkNoChangesInternal(tView, lView, context, notifyErrorHandler = true) {
  13301. setIsInCheckNoChangesMode(true);
  13302. try {
  13303. detectChangesInternal(tView, lView, context, notifyErrorHandler);
  13304. }
  13305. finally {
  13306. setIsInCheckNoChangesMode(false);
  13307. }
  13308. }
  13309. /**
  13310. * Synchronously perform change detection on a component (and possibly its sub-components).
  13311. *
  13312. * This function triggers change detection in a synchronous way on a component.
  13313. *
  13314. * @param component The component which the change detection should be performed on.
  13315. */
  13316. function detectChanges(component) {
  13317. const view = getComponentViewByInstance(component);
  13318. detectChangesInternal(view[TVIEW], view, component);
  13319. }
  13320. /**
  13321. * Processes a view in update mode. This includes a number of steps in a specific order:
  13322. * - executing a template function in update mode;
  13323. * - executing hooks;
  13324. * - refreshing queries;
  13325. * - setting host bindings;
  13326. * - refreshing child (embedded and component) views.
  13327. */
  13328. function refreshView(tView, lView, templateFn, context) {
  13329. ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
  13330. const flags = lView[FLAGS];
  13331. if ((flags & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */)
  13332. return;
  13333. // Check no changes mode is a dev only mode used to verify that bindings have not changed
  13334. // since they were assigned. We do not want to execute lifecycle hooks in that mode.
  13335. const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
  13336. !isInCheckNoChangesPass && lView[ENVIRONMENT].effectManager?.flush();
  13337. enterView(lView);
  13338. try {
  13339. resetPreOrderHookFlags(lView);
  13340. setBindingIndex(tView.bindingStartIndex);
  13341. if (templateFn !== null) {
  13342. executeTemplate(tView, lView, templateFn, 2 /* RenderFlags.Update */, context);
  13343. }
  13344. const hooksInitPhaseCompleted = (flags & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
  13345. // execute pre-order hooks (OnInit, OnChanges, DoCheck)
  13346. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  13347. if (!isInCheckNoChangesPass) {
  13348. if (hooksInitPhaseCompleted) {
  13349. const preOrderCheckHooks = tView.preOrderCheckHooks;
  13350. if (preOrderCheckHooks !== null) {
  13351. executeCheckHooks(lView, preOrderCheckHooks, null);
  13352. }
  13353. }
  13354. else {
  13355. const preOrderHooks = tView.preOrderHooks;
  13356. if (preOrderHooks !== null) {
  13357. executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, null);
  13358. }
  13359. incrementInitPhaseFlags(lView, 0 /* InitPhaseState.OnInitHooksToBeRun */);
  13360. }
  13361. }
  13362. // First mark transplanted views that are declared in this lView as needing a refresh at their
  13363. // insertion points. This is needed to avoid the situation where the template is defined in this
  13364. // `LView` but its declaration appears after the insertion component.
  13365. markTransplantedViewsForRefresh(lView);
  13366. detectChangesInEmbeddedViews(lView, 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */);
  13367. // Content query results must be refreshed before content hooks are called.
  13368. if (tView.contentQueries !== null) {
  13369. refreshContentQueries(tView, lView);
  13370. }
  13371. // execute content hooks (AfterContentInit, AfterContentChecked)
  13372. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  13373. if (!isInCheckNoChangesPass) {
  13374. if (hooksInitPhaseCompleted) {
  13375. const contentCheckHooks = tView.contentCheckHooks;
  13376. if (contentCheckHooks !== null) {
  13377. executeCheckHooks(lView, contentCheckHooks);
  13378. }
  13379. }
  13380. else {
  13381. const contentHooks = tView.contentHooks;
  13382. if (contentHooks !== null) {
  13383. executeInitAndCheckHooks(lView, contentHooks, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
  13384. }
  13385. incrementInitPhaseFlags(lView, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
  13386. }
  13387. }
  13388. processHostBindingOpCodes(tView, lView);
  13389. // Refresh child component views.
  13390. const components = tView.components;
  13391. if (components !== null) {
  13392. detectChangesInChildComponents(lView, components, 0 /* ChangeDetectionMode.Global */);
  13393. }
  13394. // View queries must execute after refreshing child components because a template in this view
  13395. // could be inserted in a child component. If the view query executes before child component
  13396. // refresh, the template might not yet be inserted.
  13397. const viewQuery = tView.viewQuery;
  13398. if (viewQuery !== null) {
  13399. executeViewQueryFn(2 /* RenderFlags.Update */, viewQuery, context);
  13400. }
  13401. // execute view hooks (AfterViewInit, AfterViewChecked)
  13402. // PERF WARNING: do NOT extract this to a separate function without running benchmarks
  13403. if (!isInCheckNoChangesPass) {
  13404. if (hooksInitPhaseCompleted) {
  13405. const viewCheckHooks = tView.viewCheckHooks;
  13406. if (viewCheckHooks !== null) {
  13407. executeCheckHooks(lView, viewCheckHooks);
  13408. }
  13409. }
  13410. else {
  13411. const viewHooks = tView.viewHooks;
  13412. if (viewHooks !== null) {
  13413. executeInitAndCheckHooks(lView, viewHooks, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
  13414. }
  13415. incrementInitPhaseFlags(lView, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
  13416. }
  13417. }
  13418. if (tView.firstUpdatePass === true) {
  13419. // We need to make sure that we only flip the flag on successful `refreshView` only
  13420. // Don't do this in `finally` block.
  13421. // If we did this in `finally` block then an exception could block the execution of styling
  13422. // instructions which in turn would be unable to insert themselves into the styling linked
  13423. // list. The result of this would be that if the exception would not be throw on subsequent CD
  13424. // the styling would be unable to process it data and reflect to the DOM.
  13425. tView.firstUpdatePass = false;
  13426. }
  13427. // Do not reset the dirty state when running in check no changes mode. We don't want components
  13428. // to behave differently depending on whether check no changes is enabled or not. For example:
  13429. // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to
  13430. // refresh a `NgClass` binding should work. If we would reset the dirty state in the check
  13431. // no changes cycle, the component would be not be dirty for the next update pass. This would
  13432. // be different in production mode where the component dirty state is not reset.
  13433. if (!isInCheckNoChangesPass) {
  13434. lView[FLAGS] &= ~(64 /* LViewFlags.Dirty */ | 8 /* LViewFlags.FirstLViewPass */);
  13435. }
  13436. clearViewRefreshFlag(lView);
  13437. }
  13438. finally {
  13439. leaveView();
  13440. }
  13441. }
  13442. /**
  13443. * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
  13444. * them by executing an associated template function.
  13445. */
  13446. function detectChangesInEmbeddedViews(lView, mode) {
  13447. for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
  13448. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  13449. const embeddedLView = lContainer[i];
  13450. detectChangesInView(embeddedLView, mode);
  13451. }
  13452. }
  13453. }
  13454. /**
  13455. * Mark transplanted views as needing to be refreshed at their insertion points.
  13456. *
  13457. * @param lView The `LView` that may have transplanted views.
  13458. */
  13459. function markTransplantedViewsForRefresh(lView) {
  13460. for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
  13461. if (!lContainer[HAS_TRANSPLANTED_VIEWS])
  13462. continue;
  13463. const movedViews = lContainer[MOVED_VIEWS];
  13464. ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS');
  13465. for (let i = 0; i < movedViews.length; i++) {
  13466. const movedLView = movedViews[i];
  13467. const insertionLContainer = movedLView[PARENT];
  13468. ngDevMode && assertLContainer(insertionLContainer);
  13469. markViewForRefresh(movedLView);
  13470. }
  13471. }
  13472. }
  13473. /**
  13474. * Detects changes in a component by entering the component view and processing its bindings,
  13475. * queries, etc. if it is CheckAlways, OnPush and Dirty, etc.
  13476. *
  13477. * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
  13478. */
  13479. function detectChangesInComponent(hostLView, componentHostIdx, mode) {
  13480. ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
  13481. const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
  13482. detectChangesInView(componentView, mode);
  13483. }
  13484. /**
  13485. * Visits a view as part of change detection traversal.
  13486. *
  13487. * - If the view is detached, no additional traversal happens.
  13488. *
  13489. * The view is refreshed if:
  13490. * - If the view is CheckAlways or Dirty and ChangeDetectionMode is `Global`
  13491. * - If the view has the `RefreshTransplantedView` flag
  13492. *
  13493. * The view is not refreshed, but descendants are traversed in `ChangeDetectionMode.Targeted` if the
  13494. * view has a non-zero TRANSPLANTED_VIEWS_TO_REFRESH counter.
  13495. *
  13496. */
  13497. function detectChangesInView(lView, mode) {
  13498. if (!viewAttachedToChangeDetector(lView)) {
  13499. return;
  13500. }
  13501. const tView = lView[TVIEW];
  13502. const flags = lView[FLAGS];
  13503. if ((flags & (16 /* LViewFlags.CheckAlways */ | 64 /* LViewFlags.Dirty */) &&
  13504. mode === 0 /* ChangeDetectionMode.Global */) ||
  13505. flags & 1024 /* LViewFlags.RefreshView */ ||
  13506. mode === 2 /* ChangeDetectionMode.BugToForceRefreshAndIgnoreViewFlags */) {
  13507. refreshView(tView, lView, tView.template, lView[CONTEXT]);
  13508. }
  13509. else if (lView[DESCENDANT_VIEWS_TO_REFRESH] > 0) {
  13510. detectChangesInEmbeddedViews(lView, 1 /* ChangeDetectionMode.Targeted */);
  13511. const components = tView.components;
  13512. if (components !== null) {
  13513. detectChangesInChildComponents(lView, components, 1 /* ChangeDetectionMode.Targeted */);
  13514. }
  13515. }
  13516. }
  13517. /** Refreshes child components in the current view (update mode). */
  13518. function detectChangesInChildComponents(hostLView, components, mode) {
  13519. for (let i = 0; i < components.length; i++) {
  13520. detectChangesInComponent(hostLView, components[i], mode);
  13521. }
  13522. }
  13523. class ViewRef$1 {
  13524. get rootNodes() {
  13525. const lView = this._lView;
  13526. const tView = lView[TVIEW];
  13527. return collectNativeNodes(tView, lView, tView.firstChild, []);
  13528. }
  13529. constructor(
  13530. /**
  13531. * This represents `LView` associated with the component when ViewRef is a ChangeDetectorRef.
  13532. *
  13533. * When ViewRef is created for a dynamic component, this also represents the `LView` for the
  13534. * component.
  13535. *
  13536. * For a "regular" ViewRef created for an embedded view, this is the `LView` for the embedded
  13537. * view.
  13538. *
  13539. * @internal
  13540. */
  13541. _lView,
  13542. /**
  13543. * This represents the `LView` associated with the point where `ChangeDetectorRef` was
  13544. * requested.
  13545. *
  13546. * This may be different from `_lView` if the `_cdRefInjectingView` is an embedded view.
  13547. */
  13548. _cdRefInjectingView) {
  13549. this._lView = _lView;
  13550. this._cdRefInjectingView = _cdRefInjectingView;
  13551. this._appRef = null;
  13552. this._attachedToViewContainer = false;
  13553. }
  13554. get context() {
  13555. return this._lView[CONTEXT];
  13556. }
  13557. set context(value) {
  13558. this._lView[CONTEXT] = value;
  13559. }
  13560. get destroyed() {
  13561. return (this._lView[FLAGS] & 256 /* LViewFlags.Destroyed */) === 256 /* LViewFlags.Destroyed */;
  13562. }
  13563. destroy() {
  13564. if (this._appRef) {
  13565. this._appRef.detachView(this);
  13566. }
  13567. else if (this._attachedToViewContainer) {
  13568. const parent = this._lView[PARENT];
  13569. if (isLContainer(parent)) {
  13570. const viewRefs = parent[VIEW_REFS];
  13571. const index = viewRefs ? viewRefs.indexOf(this) : -1;
  13572. if (index > -1) {
  13573. ngDevMode &&
  13574. 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.');
  13575. detachView(parent, index);
  13576. removeFromArray(viewRefs, index);
  13577. }
  13578. }
  13579. this._attachedToViewContainer = false;
  13580. }
  13581. destroyLView(this._lView[TVIEW], this._lView);
  13582. }
  13583. onDestroy(callback) {
  13584. storeLViewOnDestroy(this._lView, callback);
  13585. }
  13586. /**
  13587. * Marks a view and all of its ancestors dirty.
  13588. *
  13589. * This can be used to ensure an {@link ChangeDetectionStrategy#OnPush} component is
  13590. * checked when it needs to be re-rendered but the two normal triggers haven't marked it
  13591. * dirty (i.e. inputs haven't changed and events haven't fired in the view).
  13592. *
  13593. * <!-- TODO: Add a link to a chapter on OnPush components -->
  13594. *
  13595. * @usageNotes
  13596. * ### Example
  13597. *
  13598. * ```typescript
  13599. * @Component({
  13600. * selector: 'app-root',
  13601. * template: `Number of ticks: {{numberOfTicks}}`
  13602. * changeDetection: ChangeDetectionStrategy.OnPush,
  13603. * })
  13604. * class AppComponent {
  13605. * numberOfTicks = 0;
  13606. *
  13607. * constructor(private ref: ChangeDetectorRef) {
  13608. * setInterval(() => {
  13609. * this.numberOfTicks++;
  13610. * // the following is required, otherwise the view will not be updated
  13611. * this.ref.markForCheck();
  13612. * }, 1000);
  13613. * }
  13614. * }
  13615. * ```
  13616. */
  13617. markForCheck() {
  13618. markViewDirty(this._cdRefInjectingView || this._lView);
  13619. }
  13620. /**
  13621. * Detaches the view from the change detection tree.
  13622. *
  13623. * Detached views will not be checked during change detection runs until they are
  13624. * re-attached, even if they are dirty. `detach` can be used in combination with
  13625. * {@link ChangeDetectorRef#detectChanges} to implement local change
  13626. * detection checks.
  13627. *
  13628. * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
  13629. * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
  13630. *
  13631. * @usageNotes
  13632. * ### Example
  13633. *
  13634. * The following example defines a component with a large list of readonly data.
  13635. * Imagine the data changes constantly, many times per second. For performance reasons,
  13636. * we want to check and update the list every five seconds. We can do that by detaching
  13637. * the component's change detector and doing a local check every five seconds.
  13638. *
  13639. * ```typescript
  13640. * class DataProvider {
  13641. * // in a real application the returned data will be different every time
  13642. * get data() {
  13643. * return [1,2,3,4,5];
  13644. * }
  13645. * }
  13646. *
  13647. * @Component({
  13648. * selector: 'giant-list',
  13649. * template: `
  13650. * <li *ngFor="let d of dataProvider.data">Data {{d}}</li>
  13651. * `,
  13652. * })
  13653. * class GiantList {
  13654. * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
  13655. * ref.detach();
  13656. * setInterval(() => {
  13657. * this.ref.detectChanges();
  13658. * }, 5000);
  13659. * }
  13660. * }
  13661. *
  13662. * @Component({
  13663. * selector: 'app',
  13664. * providers: [DataProvider],
  13665. * template: `
  13666. * <giant-list><giant-list>
  13667. * `,
  13668. * })
  13669. * class App {
  13670. * }
  13671. * ```
  13672. */
  13673. detach() {
  13674. this._lView[FLAGS] &= ~128 /* LViewFlags.Attached */;
  13675. }
  13676. /**
  13677. * Re-attaches a view to the change detection tree.
  13678. *
  13679. * This can be used to re-attach views that were previously detached from the tree
  13680. * using {@link ChangeDetectorRef#detach}. Views are attached to the tree by default.
  13681. *
  13682. * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
  13683. *
  13684. * @usageNotes
  13685. * ### Example
  13686. *
  13687. * The following example creates a component displaying `live` data. The component will detach
  13688. * its change detector from the main change detector tree when the component's live property
  13689. * is set to false.
  13690. *
  13691. * ```typescript
  13692. * class DataProvider {
  13693. * data = 1;
  13694. *
  13695. * constructor() {
  13696. * setInterval(() => {
  13697. * this.data = this.data * 2;
  13698. * }, 500);
  13699. * }
  13700. * }
  13701. *
  13702. * @Component({
  13703. * selector: 'live-data',
  13704. * inputs: ['live'],
  13705. * template: 'Data: {{dataProvider.data}}'
  13706. * })
  13707. * class LiveData {
  13708. * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
  13709. *
  13710. * set live(value) {
  13711. * if (value) {
  13712. * this.ref.reattach();
  13713. * } else {
  13714. * this.ref.detach();
  13715. * }
  13716. * }
  13717. * }
  13718. *
  13719. * @Component({
  13720. * selector: 'app-root',
  13721. * providers: [DataProvider],
  13722. * template: `
  13723. * Live Update: <input type="checkbox" [(ngModel)]="live">
  13724. * <live-data [live]="live"><live-data>
  13725. * `,
  13726. * })
  13727. * class AppComponent {
  13728. * live = true;
  13729. * }
  13730. * ```
  13731. */
  13732. reattach() {
  13733. this._lView[FLAGS] |= 128 /* LViewFlags.Attached */;
  13734. }
  13735. /**
  13736. * Checks the view and its children.
  13737. *
  13738. * This can also be used in combination with {@link ChangeDetectorRef#detach} to implement
  13739. * local change detection checks.
  13740. *
  13741. * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
  13742. * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
  13743. *
  13744. * @usageNotes
  13745. * ### Example
  13746. *
  13747. * The following example defines a component with a large list of readonly data.
  13748. * Imagine, the data changes constantly, many times per second. For performance reasons,
  13749. * we want to check and update the list every five seconds.
  13750. *
  13751. * We can do that by detaching the component's change detector and doing a local change detection
  13752. * check every five seconds.
  13753. *
  13754. * See {@link ChangeDetectorRef#detach} for more information.
  13755. */
  13756. detectChanges() {
  13757. detectChangesInternal(this._lView[TVIEW], this._lView, this.context);
  13758. }
  13759. /**
  13760. * Checks the change detector and its children, and throws if any changes are detected.
  13761. *
  13762. * This is used in development mode to verify that running change detection doesn't
  13763. * introduce other changes.
  13764. */
  13765. checkNoChanges() {
  13766. if (ngDevMode) {
  13767. checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context);
  13768. }
  13769. }
  13770. attachToViewContainerRef() {
  13771. if (this._appRef) {
  13772. throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached directly to the ApplicationRef!');
  13773. }
  13774. this._attachedToViewContainer = true;
  13775. }
  13776. detachFromAppRef() {
  13777. this._appRef = null;
  13778. detachViewFromDOM(this._lView[TVIEW], this._lView);
  13779. }
  13780. attachToAppRef(appRef) {
  13781. if (this._attachedToViewContainer) {
  13782. throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached to a ViewContainer!');
  13783. }
  13784. this._appRef = appRef;
  13785. }
  13786. }
  13787. /** @internal */
  13788. class RootViewRef extends ViewRef$1 {
  13789. constructor(_view) {
  13790. super(_view);
  13791. this._view = _view;
  13792. }
  13793. detectChanges() {
  13794. const lView = this._view;
  13795. const tView = lView[TVIEW];
  13796. const context = lView[CONTEXT];
  13797. detectChangesInternal(tView, lView, context, false);
  13798. }
  13799. checkNoChanges() {
  13800. if (ngDevMode) {
  13801. const lView = this._view;
  13802. const tView = lView[TVIEW];
  13803. const context = lView[CONTEXT];
  13804. checkNoChangesInternal(tView, lView, context, false);
  13805. }
  13806. }
  13807. get context() {
  13808. return null;
  13809. }
  13810. }
  13811. class ComponentFactoryResolver extends ComponentFactoryResolver$1 {
  13812. /**
  13813. * @param ngModule The NgModuleRef to which all resolved factories are bound.
  13814. */
  13815. constructor(ngModule) {
  13816. super();
  13817. this.ngModule = ngModule;
  13818. }
  13819. resolveComponentFactory(component) {
  13820. ngDevMode && assertComponentType(component);
  13821. const componentDef = getComponentDef(component);
  13822. return new ComponentFactory(componentDef, this.ngModule);
  13823. }
  13824. }
  13825. function toRefArray(map) {
  13826. const array = [];
  13827. for (let nonMinified in map) {
  13828. if (map.hasOwnProperty(nonMinified)) {
  13829. const minified = map[nonMinified];
  13830. array.push({ propName: minified, templateName: nonMinified });
  13831. }
  13832. }
  13833. return array;
  13834. }
  13835. function getNamespace(elementName) {
  13836. const name = elementName.toLowerCase();
  13837. return name === 'svg' ? SVG_NAMESPACE : (name === 'math' ? MATH_ML_NAMESPACE : null);
  13838. }
  13839. /**
  13840. * Injector that looks up a value using a specific injector, before falling back to the module
  13841. * injector. Used primarily when creating components or embedded views dynamically.
  13842. */
  13843. class ChainedInjector {
  13844. constructor(injector, parentInjector) {
  13845. this.injector = injector;
  13846. this.parentInjector = parentInjector;
  13847. }
  13848. get(token, notFoundValue, flags) {
  13849. flags = convertToBitFlags(flags);
  13850. const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
  13851. if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
  13852. notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
  13853. // Return the value from the root element injector when
  13854. // - it provides it
  13855. // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
  13856. // - the module injector should not be checked
  13857. // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
  13858. return value;
  13859. }
  13860. return this.parentInjector.get(token, notFoundValue, flags);
  13861. }
  13862. }
  13863. /**
  13864. * ComponentFactory interface implementation.
  13865. */
  13866. class ComponentFactory extends ComponentFactory$1 {
  13867. get inputs() {
  13868. const componentDef = this.componentDef;
  13869. const inputTransforms = componentDef.inputTransforms;
  13870. const refArray = toRefArray(componentDef.inputs);
  13871. if (inputTransforms !== null) {
  13872. for (const input of refArray) {
  13873. if (inputTransforms.hasOwnProperty(input.propName)) {
  13874. input.transform = inputTransforms[input.propName];
  13875. }
  13876. }
  13877. }
  13878. return refArray;
  13879. }
  13880. get outputs() {
  13881. return toRefArray(this.componentDef.outputs);
  13882. }
  13883. /**
  13884. * @param componentDef The component definition.
  13885. * @param ngModule The NgModuleRef to which the factory is bound.
  13886. */
  13887. constructor(componentDef, ngModule) {
  13888. super();
  13889. this.componentDef = componentDef;
  13890. this.ngModule = ngModule;
  13891. this.componentType = componentDef.type;
  13892. this.selector = stringifyCSSSelectorList(componentDef.selectors);
  13893. this.ngContentSelectors =
  13894. componentDef.ngContentSelectors ? componentDef.ngContentSelectors : [];
  13895. this.isBoundToModule = !!ngModule;
  13896. }
  13897. create(injector, projectableNodes, rootSelectorOrNode, environmentInjector) {
  13898. environmentInjector = environmentInjector || this.ngModule;
  13899. let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector ?
  13900. environmentInjector :
  13901. environmentInjector?.injector;
  13902. if (realEnvironmentInjector && this.componentDef.getStandaloneInjector !== null) {
  13903. realEnvironmentInjector = this.componentDef.getStandaloneInjector(realEnvironmentInjector) ||
  13904. realEnvironmentInjector;
  13905. }
  13906. const rootViewInjector = realEnvironmentInjector ? new ChainedInjector(injector, realEnvironmentInjector) : injector;
  13907. const rendererFactory = rootViewInjector.get(RendererFactory2, null);
  13908. if (rendererFactory === null) {
  13909. throw new RuntimeError(407 /* RuntimeErrorCode.RENDERER_NOT_FOUND */, ngDevMode &&
  13910. 'Angular was not able to inject a renderer (RendererFactory2). ' +
  13911. 'Likely this is due to a broken DI hierarchy. ' +
  13912. 'Make sure that any injector used to create this component has a correct parent.');
  13913. }
  13914. const sanitizer = rootViewInjector.get(Sanitizer, null);
  13915. const effectManager = rootViewInjector.get(EffectManager, null);
  13916. const afterRenderEventManager = rootViewInjector.get(AfterRenderEventManager, null);
  13917. const environment = {
  13918. rendererFactory,
  13919. sanitizer,
  13920. effectManager,
  13921. afterRenderEventManager,
  13922. };
  13923. const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
  13924. // Determine a tag name used for creating host elements when this component is created
  13925. // dynamically. Default to 'div' if this component did not specify any tag name in its selector.
  13926. const elementName = this.componentDef.selectors[0][0] || 'div';
  13927. const hostRNode = rootSelectorOrNode ?
  13928. locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation, rootViewInjector) :
  13929. createElementNode(hostRenderer, elementName, getNamespace(elementName));
  13930. // Signal components use the granular "RefreshView" for change detection
  13931. const signalFlags = (4096 /* LViewFlags.SignalView */ | 512 /* LViewFlags.IsRoot */);
  13932. // Non-signal components use the traditional "CheckAlways or OnPush/Dirty" change detection
  13933. const nonSignalFlags = this.componentDef.onPush ? 64 /* LViewFlags.Dirty */ | 512 /* LViewFlags.IsRoot */ :
  13934. 16 /* LViewFlags.CheckAlways */ | 512 /* LViewFlags.IsRoot */;
  13935. const rootFlags = this.componentDef.signals ? signalFlags : nonSignalFlags;
  13936. let hydrationInfo = null;
  13937. if (hostRNode !== null) {
  13938. hydrationInfo = retrieveHydrationInfo(hostRNode, rootViewInjector, true /* isRootView */);
  13939. }
  13940. // Create the root view. Uses empty TView and ContentTemplate.
  13941. const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null, null);
  13942. const rootLView = createLView(null, rootTView, null, rootFlags, null, null, environment, hostRenderer, rootViewInjector, null, hydrationInfo);
  13943. // rootView is the parent when bootstrapping
  13944. // TODO(misko): it looks like we are entering view here but we don't really need to as
  13945. // `renderView` does that. However as the code is written it is needed because
  13946. // `createRootComponentView` and `createRootComponent` both read global state. Fixing those
  13947. // issues would allow us to drop this.
  13948. enterView(rootLView);
  13949. let component;
  13950. let tElementNode;
  13951. try {
  13952. const rootComponentDef = this.componentDef;
  13953. let rootDirectives;
  13954. let hostDirectiveDefs = null;
  13955. if (rootComponentDef.findHostDirectiveDefs) {
  13956. rootDirectives = [];
  13957. hostDirectiveDefs = new Map();
  13958. rootComponentDef.findHostDirectiveDefs(rootComponentDef, rootDirectives, hostDirectiveDefs);
  13959. rootDirectives.push(rootComponentDef);
  13960. }
  13961. else {
  13962. rootDirectives = [rootComponentDef];
  13963. }
  13964. const hostTNode = createRootComponentTNode(rootLView, hostRNode);
  13965. const componentView = createRootComponentView(hostTNode, hostRNode, rootComponentDef, rootDirectives, rootLView, environment, hostRenderer);
  13966. tElementNode = getTNode(rootTView, HEADER_OFFSET);
  13967. // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some tests
  13968. // where the renderer is mocked out and `undefined` is returned. We should update the tests so
  13969. // that this check can be removed.
  13970. if (hostRNode) {
  13971. setRootNodeAttributes(hostRenderer, rootComponentDef, hostRNode, rootSelectorOrNode);
  13972. }
  13973. if (projectableNodes !== undefined) {
  13974. projectNodes(tElementNode, this.ngContentSelectors, projectableNodes);
  13975. }
  13976. // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
  13977. // executed here?
  13978. // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
  13979. component = createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, [LifecycleHooksFeature]);
  13980. renderView(rootTView, rootLView, null);
  13981. }
  13982. finally {
  13983. leaveView();
  13984. }
  13985. return new ComponentRef(this.componentType, component, createElementRef(tElementNode, rootLView), rootLView, tElementNode);
  13986. }
  13987. }
  13988. /**
  13989. * Represents an instance of a Component created via a {@link ComponentFactory}.
  13990. *
  13991. * `ComponentRef` provides access to the Component Instance as well other objects related to this
  13992. * Component Instance and allows you to destroy the Component Instance via the {@link #destroy}
  13993. * method.
  13994. *
  13995. */
  13996. class ComponentRef extends ComponentRef$1 {
  13997. constructor(componentType, instance, location, _rootLView, _tNode) {
  13998. super();
  13999. this.location = location;
  14000. this._rootLView = _rootLView;
  14001. this._tNode = _tNode;
  14002. this.previousInputValues = null;
  14003. this.instance = instance;
  14004. this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView);
  14005. this.componentType = componentType;
  14006. }
  14007. setInput(name, value) {
  14008. const inputData = this._tNode.inputs;
  14009. let dataValue;
  14010. if (inputData !== null && (dataValue = inputData[name])) {
  14011. this.previousInputValues ??= new Map();
  14012. // Do not set the input if it is the same as the last value
  14013. // This behavior matches `bindingUpdated` when binding inputs in templates.
  14014. if (this.previousInputValues.has(name) &&
  14015. Object.is(this.previousInputValues.get(name), value)) {
  14016. return;
  14017. }
  14018. const lView = this._rootLView;
  14019. setInputsForProperty(lView[TVIEW], lView, dataValue, name, value);
  14020. this.previousInputValues.set(name, value);
  14021. const childComponentLView = getComponentLViewByIndex(this._tNode.index, lView);
  14022. markViewDirty(childComponentLView);
  14023. }
  14024. else {
  14025. if (ngDevMode) {
  14026. const cmpNameForError = stringifyForError(this.componentType);
  14027. let message = `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `;
  14028. message += `Make sure that the '${name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`;
  14029. reportUnknownPropertyError(message);
  14030. }
  14031. }
  14032. }
  14033. get injector() {
  14034. return new NodeInjector(this._tNode, this._rootLView);
  14035. }
  14036. destroy() {
  14037. this.hostView.destroy();
  14038. }
  14039. onDestroy(callback) {
  14040. this.hostView.onDestroy(callback);
  14041. }
  14042. }
  14043. /** Creates a TNode that can be used to instantiate a root component. */
  14044. function createRootComponentTNode(lView, rNode) {
  14045. const tView = lView[TVIEW];
  14046. const index = HEADER_OFFSET;
  14047. ngDevMode && assertIndexInRange(lView, index);
  14048. lView[index] = rNode;
  14049. // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
  14050. // the same time we want to communicate the debug `TNode` that this is a special `TNode`
  14051. // representing a host element.
  14052. return getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
  14053. }
  14054. /**
  14055. * Creates the root component view and the root component node.
  14056. *
  14057. * @param hostRNode Render host element.
  14058. * @param rootComponentDef ComponentDef
  14059. * @param rootView The parent view where the host node is stored
  14060. * @param rendererFactory Factory to be used for creating child renderers.
  14061. * @param hostRenderer The current renderer
  14062. * @param sanitizer The sanitizer, if provided
  14063. *
  14064. * @returns Component view created
  14065. */
  14066. function createRootComponentView(tNode, hostRNode, rootComponentDef, rootDirectives, rootView, environment, hostRenderer) {
  14067. const tView = rootView[TVIEW];
  14068. applyRootComponentStyling(rootDirectives, tNode, hostRNode, hostRenderer);
  14069. // Hydration info is on the host element and needs to be retrieved
  14070. // and passed to the component LView.
  14071. let hydrationInfo = null;
  14072. if (hostRNode !== null) {
  14073. hydrationInfo = retrieveHydrationInfo(hostRNode, rootView[INJECTOR$1]);
  14074. }
  14075. const viewRenderer = environment.rendererFactory.createRenderer(hostRNode, rootComponentDef);
  14076. let lViewFlags = 16 /* LViewFlags.CheckAlways */;
  14077. if (rootComponentDef.signals) {
  14078. lViewFlags = 4096 /* LViewFlags.SignalView */;
  14079. }
  14080. else if (rootComponentDef.onPush) {
  14081. lViewFlags = 64 /* LViewFlags.Dirty */;
  14082. }
  14083. const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, lViewFlags, rootView[tNode.index], tNode, environment, viewRenderer, null, null, hydrationInfo);
  14084. if (tView.firstCreatePass) {
  14085. markAsComponentHost(tView, tNode, rootDirectives.length - 1);
  14086. }
  14087. addToViewTree(rootView, componentView);
  14088. // Store component view at node index, with node as the HOST
  14089. return rootView[tNode.index] = componentView;
  14090. }
  14091. /** Sets up the styling information on a root component. */
  14092. function applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer) {
  14093. for (const def of rootDirectives) {
  14094. tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
  14095. }
  14096. if (tNode.mergedAttrs !== null) {
  14097. computeStaticStyling(tNode, tNode.mergedAttrs, true);
  14098. if (rNode !== null) {
  14099. setupStaticAttributes(hostRenderer, rNode, tNode);
  14100. }
  14101. }
  14102. }
  14103. /**
  14104. * Creates a root component and sets it up with features and host bindings.Shared by
  14105. * renderComponent() and ViewContainerRef.createComponent().
  14106. */
  14107. function createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, hostFeatures) {
  14108. const rootTNode = getCurrentTNode();
  14109. ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
  14110. const tView = rootLView[TVIEW];
  14111. const native = getNativeByTNode(rootTNode, rootLView);
  14112. initializeDirectives(tView, rootLView, rootTNode, rootDirectives, null, hostDirectiveDefs);
  14113. for (let i = 0; i < rootDirectives.length; i++) {
  14114. const directiveIndex = rootTNode.directiveStart + i;
  14115. const directiveInstance = getNodeInjectable(rootLView, tView, directiveIndex, rootTNode);
  14116. attachPatchData(directiveInstance, rootLView);
  14117. }
  14118. invokeDirectivesHostBindings(tView, rootLView, rootTNode);
  14119. if (native) {
  14120. attachPatchData(native, rootLView);
  14121. }
  14122. // We're guaranteed for the `componentOffset` to be positive here
  14123. // since a root component always matches a component def.
  14124. ngDevMode &&
  14125. assertGreaterThan(rootTNode.componentOffset, -1, 'componentOffset must be great than -1');
  14126. const component = getNodeInjectable(rootLView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
  14127. componentView[CONTEXT] = rootLView[CONTEXT] = component;
  14128. if (hostFeatures !== null) {
  14129. for (const feature of hostFeatures) {
  14130. feature(component, rootComponentDef);
  14131. }
  14132. }
  14133. // We want to generate an empty QueryList for root content queries for backwards
  14134. // compatibility with ViewEngine.
  14135. executeContentQueries(tView, rootTNode, componentView);
  14136. return component;
  14137. }
  14138. /** Sets the static attributes on a root component. */
  14139. function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
  14140. if (rootSelectorOrNode) {
  14141. setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
  14142. }
  14143. else {
  14144. // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
  14145. // is not defined), also apply attributes and classes extracted from component selector.
  14146. // Extract attributes and classes from the first selector only to match VE behavior.
  14147. const { attrs, classes } = extractAttrsAndClassesFromSelector(componentDef.selectors[0]);
  14148. if (attrs) {
  14149. setUpAttributes(hostRenderer, hostRNode, attrs);
  14150. }
  14151. if (classes && classes.length > 0) {
  14152. writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
  14153. }
  14154. }
  14155. }
  14156. /** Projects the `projectableNodes` that were specified when creating a root component. */
  14157. function projectNodes(tNode, ngContentSelectors, projectableNodes) {
  14158. const projection = tNode.projection = [];
  14159. for (let i = 0; i < ngContentSelectors.length; i++) {
  14160. const nodesforSlot = projectableNodes[i];
  14161. // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
  14162. // case). Here we do normalize passed data structure to be an array of arrays to avoid
  14163. // complex checks down the line.
  14164. // We also normalize the length of the passed in projectable nodes (to match the number of
  14165. // <ng-container> slots defined by a component).
  14166. projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
  14167. }
  14168. }
  14169. /**
  14170. * Used to enable lifecycle hooks on the root component.
  14171. *
  14172. * Include this feature when calling `renderComponent` if the root component
  14173. * you are rendering has lifecycle hooks defined. Otherwise, the hooks won't
  14174. * be called properly.
  14175. *
  14176. * Example:
  14177. *
  14178. * ```
  14179. * renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]});
  14180. * ```
  14181. */
  14182. function LifecycleHooksFeature() {
  14183. const tNode = getCurrentTNode();
  14184. ngDevMode && assertDefined(tNode, 'TNode is required');
  14185. registerPostOrderHooks(getLView()[TVIEW], tNode);
  14186. }
  14187. function getSuperType(type) {
  14188. return Object.getPrototypeOf(type.prototype).constructor;
  14189. }
  14190. /**
  14191. * Merges the definition from a super class to a sub class.
  14192. * @param definition The definition that is a SubClass of another directive of component
  14193. *
  14194. * @codeGenApi
  14195. */
  14196. function ɵɵInheritDefinitionFeature(definition) {
  14197. let superType = getSuperType(definition.type);
  14198. let shouldInheritFields = true;
  14199. const inheritanceChain = [definition];
  14200. while (superType) {
  14201. let superDef = undefined;
  14202. if (isComponentDef(definition)) {
  14203. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14204. superDef = superType.ɵcmp || superType.ɵdir;
  14205. }
  14206. else {
  14207. if (superType.ɵcmp) {
  14208. throw new RuntimeError(903 /* RuntimeErrorCode.INVALID_INHERITANCE */, ngDevMode &&
  14209. `Directives cannot inherit Components. Directive ${stringifyForError(definition.type)} is attempting to extend component ${stringifyForError(superType)}`);
  14210. }
  14211. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14212. superDef = superType.ɵdir;
  14213. }
  14214. if (superDef) {
  14215. if (shouldInheritFields) {
  14216. inheritanceChain.push(superDef);
  14217. // Some fields in the definition may be empty, if there were no values to put in them that
  14218. // would've justified object creation. Unwrap them if necessary.
  14219. const writeableDef = definition;
  14220. writeableDef.inputs = maybeUnwrapEmpty(definition.inputs);
  14221. writeableDef.inputTransforms = maybeUnwrapEmpty(definition.inputTransforms);
  14222. writeableDef.declaredInputs = maybeUnwrapEmpty(definition.declaredInputs);
  14223. writeableDef.outputs = maybeUnwrapEmpty(definition.outputs);
  14224. // Merge hostBindings
  14225. const superHostBindings = superDef.hostBindings;
  14226. superHostBindings && inheritHostBindings(definition, superHostBindings);
  14227. // Merge queries
  14228. const superViewQuery = superDef.viewQuery;
  14229. const superContentQueries = superDef.contentQueries;
  14230. superViewQuery && inheritViewQuery(definition, superViewQuery);
  14231. superContentQueries && inheritContentQueries(definition, superContentQueries);
  14232. // Merge inputs and outputs
  14233. fillProperties(definition.inputs, superDef.inputs);
  14234. fillProperties(definition.declaredInputs, superDef.declaredInputs);
  14235. fillProperties(definition.outputs, superDef.outputs);
  14236. if (superDef.inputTransforms !== null) {
  14237. if (writeableDef.inputTransforms === null) {
  14238. writeableDef.inputTransforms = {};
  14239. }
  14240. fillProperties(writeableDef.inputTransforms, superDef.inputTransforms);
  14241. }
  14242. // Merge animations metadata.
  14243. // If `superDef` is a Component, the `data` field is present (defaults to an empty object).
  14244. if (isComponentDef(superDef) && superDef.data.animation) {
  14245. // If super def is a Component, the `definition` is also a Component, since Directives can
  14246. // not inherit Components (we throw an error above and cannot reach this code).
  14247. const defData = definition.data;
  14248. defData.animation = (defData.animation || []).concat(superDef.data.animation);
  14249. }
  14250. }
  14251. // Run parent features
  14252. const features = superDef.features;
  14253. if (features) {
  14254. for (let i = 0; i < features.length; i++) {
  14255. const feature = features[i];
  14256. if (feature && feature.ngInherit) {
  14257. feature(definition);
  14258. }
  14259. // If `InheritDefinitionFeature` is a part of the current `superDef`, it means that this
  14260. // def already has all the necessary information inherited from its super class(es), so we
  14261. // can stop merging fields from super classes. However we need to iterate through the
  14262. // prototype chain to look for classes that might contain other "features" (like
  14263. // NgOnChanges), which we should invoke for the original `definition`. We set the
  14264. // `shouldInheritFields` flag to indicate that, essentially skipping fields inheritance
  14265. // logic and only invoking functions from the "features" list.
  14266. if (feature === ɵɵInheritDefinitionFeature) {
  14267. shouldInheritFields = false;
  14268. }
  14269. }
  14270. }
  14271. }
  14272. superType = Object.getPrototypeOf(superType);
  14273. }
  14274. mergeHostAttrsAcrossInheritance(inheritanceChain);
  14275. }
  14276. /**
  14277. * Merge the `hostAttrs` and `hostVars` from the inherited parent to the base class.
  14278. *
  14279. * @param inheritanceChain A list of `WritableDefs` starting at the top most type and listing
  14280. * sub-types in order. For each type take the `hostAttrs` and `hostVars` and merge it with the child
  14281. * type.
  14282. */
  14283. function mergeHostAttrsAcrossInheritance(inheritanceChain) {
  14284. let hostVars = 0;
  14285. let hostAttrs = null;
  14286. // We process the inheritance order from the base to the leaves here.
  14287. for (let i = inheritanceChain.length - 1; i >= 0; i--) {
  14288. const def = inheritanceChain[i];
  14289. // For each `hostVars`, we need to add the superclass amount.
  14290. def.hostVars = (hostVars += def.hostVars);
  14291. // for each `hostAttrs` we need to merge it with superclass.
  14292. def.hostAttrs =
  14293. mergeHostAttrs(def.hostAttrs, hostAttrs = mergeHostAttrs(hostAttrs, def.hostAttrs));
  14294. }
  14295. }
  14296. function maybeUnwrapEmpty(value) {
  14297. if (value === EMPTY_OBJ) {
  14298. return {};
  14299. }
  14300. else if (value === EMPTY_ARRAY) {
  14301. return [];
  14302. }
  14303. else {
  14304. return value;
  14305. }
  14306. }
  14307. function inheritViewQuery(definition, superViewQuery) {
  14308. const prevViewQuery = definition.viewQuery;
  14309. if (prevViewQuery) {
  14310. definition.viewQuery = (rf, ctx) => {
  14311. superViewQuery(rf, ctx);
  14312. prevViewQuery(rf, ctx);
  14313. };
  14314. }
  14315. else {
  14316. definition.viewQuery = superViewQuery;
  14317. }
  14318. }
  14319. function inheritContentQueries(definition, superContentQueries) {
  14320. const prevContentQueries = definition.contentQueries;
  14321. if (prevContentQueries) {
  14322. definition.contentQueries = (rf, ctx, directiveIndex) => {
  14323. superContentQueries(rf, ctx, directiveIndex);
  14324. prevContentQueries(rf, ctx, directiveIndex);
  14325. };
  14326. }
  14327. else {
  14328. definition.contentQueries = superContentQueries;
  14329. }
  14330. }
  14331. function inheritHostBindings(definition, superHostBindings) {
  14332. const prevHostBindings = definition.hostBindings;
  14333. if (prevHostBindings) {
  14334. definition.hostBindings = (rf, ctx) => {
  14335. superHostBindings(rf, ctx);
  14336. prevHostBindings(rf, ctx);
  14337. };
  14338. }
  14339. else {
  14340. definition.hostBindings = superHostBindings;
  14341. }
  14342. }
  14343. /**
  14344. * Fields which exist on either directive or component definitions, and need to be copied from
  14345. * parent to child classes by the `ɵɵCopyDefinitionFeature`.
  14346. */
  14347. const COPY_DIRECTIVE_FIELDS = [
  14348. // The child class should use the providers of its parent.
  14349. 'providersResolver',
  14350. // Not listed here are any fields which are handled by the `ɵɵInheritDefinitionFeature`, such
  14351. // as inputs, outputs, and host binding functions.
  14352. ];
  14353. /**
  14354. * Fields which exist only on component definitions, and need to be copied from parent to child
  14355. * classes by the `ɵɵCopyDefinitionFeature`.
  14356. *
  14357. * The type here allows any field of `ComponentDef` which is not also a property of `DirectiveDef`,
  14358. * since those should go in `COPY_DIRECTIVE_FIELDS` above.
  14359. */
  14360. const COPY_COMPONENT_FIELDS = [
  14361. // The child class should use the template function of its parent, including all template
  14362. // semantics.
  14363. 'template',
  14364. 'decls',
  14365. 'consts',
  14366. 'vars',
  14367. 'onPush',
  14368. 'ngContentSelectors',
  14369. // The child class should use the CSS styles of its parent, including all styling semantics.
  14370. 'styles',
  14371. 'encapsulation',
  14372. // The child class should be checked by the runtime in the same way as its parent.
  14373. 'schemas',
  14374. ];
  14375. /**
  14376. * Copies the fields not handled by the `ɵɵInheritDefinitionFeature` from the supertype of a
  14377. * definition.
  14378. *
  14379. * This exists primarily to support ngcc migration of an existing View Engine pattern, where an
  14380. * entire decorator is inherited from a parent to a child class. When ngcc detects this case, it
  14381. * generates a skeleton definition on the child class, and applies this feature.
  14382. *
  14383. * The `ɵɵCopyDefinitionFeature` then copies any needed fields from the parent class' definition,
  14384. * including things like the component template function.
  14385. *
  14386. * @param definition The definition of a child class which inherits from a parent class with its
  14387. * own definition.
  14388. *
  14389. * @codeGenApi
  14390. */
  14391. function ɵɵCopyDefinitionFeature(definition) {
  14392. let superType = getSuperType(definition.type);
  14393. let superDef = undefined;
  14394. if (isComponentDef(definition)) {
  14395. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14396. superDef = superType.ɵcmp;
  14397. }
  14398. else {
  14399. // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
  14400. superDef = superType.ɵdir;
  14401. }
  14402. // Needed because `definition` fields are readonly.
  14403. const defAny = definition;
  14404. // Copy over any fields that apply to either directives or components.
  14405. for (const field of COPY_DIRECTIVE_FIELDS) {
  14406. defAny[field] = superDef[field];
  14407. }
  14408. if (isComponentDef(superDef)) {
  14409. // Copy over any component-specific fields.
  14410. for (const field of COPY_COMPONENT_FIELDS) {
  14411. defAny[field] = superDef[field];
  14412. }
  14413. }
  14414. }
  14415. /**
  14416. * This feature adds the host directives behavior to a directive definition by patching a
  14417. * function onto it. The expectation is that the runtime will invoke the function during
  14418. * directive matching.
  14419. *
  14420. * For example:
  14421. * ```ts
  14422. * class ComponentWithHostDirective {
  14423. * static ɵcmp = defineComponent({
  14424. * type: ComponentWithHostDirective,
  14425. * features: [ɵɵHostDirectivesFeature([
  14426. * SimpleHostDirective,
  14427. * {directive: AdvancedHostDirective, inputs: ['foo: alias'], outputs: ['bar']},
  14428. * ])]
  14429. * });
  14430. * }
  14431. * ```
  14432. *
  14433. * @codeGenApi
  14434. */
  14435. function ɵɵHostDirectivesFeature(rawHostDirectives) {
  14436. return (definition) => {
  14437. definition.findHostDirectiveDefs = findHostDirectiveDefs;
  14438. definition.hostDirectives =
  14439. (Array.isArray(rawHostDirectives) ? rawHostDirectives : rawHostDirectives()).map(dir => {
  14440. return typeof dir === 'function' ?
  14441. { directive: resolveForwardRef(dir), inputs: EMPTY_OBJ, outputs: EMPTY_OBJ } :
  14442. {
  14443. directive: resolveForwardRef(dir.directive),
  14444. inputs: bindingArrayToMap(dir.inputs),
  14445. outputs: bindingArrayToMap(dir.outputs)
  14446. };
  14447. });
  14448. };
  14449. }
  14450. function findHostDirectiveDefs(currentDef, matchedDefs, hostDirectiveDefs) {
  14451. if (currentDef.hostDirectives !== null) {
  14452. for (const hostDirectiveConfig of currentDef.hostDirectives) {
  14453. const hostDirectiveDef = getDirectiveDef(hostDirectiveConfig.directive);
  14454. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  14455. validateHostDirective(hostDirectiveConfig, hostDirectiveDef, matchedDefs);
  14456. }
  14457. // We need to patch the `declaredInputs` so that
  14458. // `ngOnChanges` can map the properties correctly.
  14459. patchDeclaredInputs(hostDirectiveDef.declaredInputs, hostDirectiveConfig.inputs);
  14460. // Host directives execute before the host so that its host bindings can be overwritten.
  14461. findHostDirectiveDefs(hostDirectiveDef, matchedDefs, hostDirectiveDefs);
  14462. hostDirectiveDefs.set(hostDirectiveDef, hostDirectiveConfig);
  14463. matchedDefs.push(hostDirectiveDef);
  14464. }
  14465. }
  14466. }
  14467. /**
  14468. * Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into
  14469. * a map in the form of `{publicName: 'alias', otherPublicName: 'otherAlias'}`.
  14470. */
  14471. function bindingArrayToMap(bindings) {
  14472. if (bindings === undefined || bindings.length === 0) {
  14473. return EMPTY_OBJ;
  14474. }
  14475. const result = {};
  14476. for (let i = 0; i < bindings.length; i += 2) {
  14477. result[bindings[i]] = bindings[i + 1];
  14478. }
  14479. return result;
  14480. }
  14481. /**
  14482. * `ngOnChanges` has some leftover legacy ViewEngine behavior where the keys inside the
  14483. * `SimpleChanges` event refer to the *declared* name of the input, not its public name or its
  14484. * minified name. E.g. in `@Input('alias') foo: string`, the name in the `SimpleChanges` object
  14485. * will always be `foo`, and not `alias` or the minified name of `foo` in apps using property
  14486. * minification.
  14487. *
  14488. * This is achieved through the `DirectiveDef.declaredInputs` map that is constructed when the
  14489. * definition is declared. When a property is written to the directive instance, the
  14490. * `NgOnChangesFeature` will try to remap the property name being written to using the
  14491. * `declaredInputs`.
  14492. *
  14493. * Since the host directive input remapping happens during directive matching, `declaredInputs`
  14494. * won't contain the new alias that the input is available under. This function addresses the
  14495. * issue by patching the host directive aliases to the `declaredInputs`. There is *not* a risk of
  14496. * this patching accidentally introducing new inputs to the host directive, because `declaredInputs`
  14497. * is used *only* by the `NgOnChangesFeature` when determining what name is used in the
  14498. * `SimpleChanges` object which won't be reached if an input doesn't exist.
  14499. */
  14500. function patchDeclaredInputs(declaredInputs, exposedInputs) {
  14501. for (const publicName in exposedInputs) {
  14502. if (exposedInputs.hasOwnProperty(publicName)) {
  14503. const remappedPublicName = exposedInputs[publicName];
  14504. const privateName = declaredInputs[publicName];
  14505. // We *technically* shouldn't be able to hit this case because we can't have multiple
  14506. // inputs on the same property and we have validations against conflicting aliases in
  14507. // `validateMappings`. If we somehow did, it would lead to `ngOnChanges` being invoked
  14508. // with the wrong name so we have a non-user-friendly assertion here just in case.
  14509. if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
  14510. declaredInputs.hasOwnProperty(remappedPublicName)) {
  14511. assertEqual(declaredInputs[remappedPublicName], declaredInputs[publicName], `Conflicting host directive input alias ${publicName}.`);
  14512. }
  14513. declaredInputs[remappedPublicName] = privateName;
  14514. }
  14515. }
  14516. }
  14517. /**
  14518. * Verifies that the host directive has been configured correctly.
  14519. * @param hostDirectiveConfig Host directive configuration object.
  14520. * @param directiveDef Directive definition of the host directive.
  14521. * @param matchedDefs Directives that have been matched so far.
  14522. */
  14523. function validateHostDirective(hostDirectiveConfig, directiveDef, matchedDefs) {
  14524. const type = hostDirectiveConfig.directive;
  14525. if (directiveDef === null) {
  14526. if (getComponentDef(type) !== null) {
  14527. throw new RuntimeError(310 /* RuntimeErrorCode.HOST_DIRECTIVE_COMPONENT */, `Host directive ${type.name} cannot be a component.`);
  14528. }
  14529. throw new RuntimeError(307 /* RuntimeErrorCode.HOST_DIRECTIVE_UNRESOLVABLE */, `Could not resolve metadata for host directive ${type.name}. ` +
  14530. `Make sure that the ${type.name} class is annotated with an @Directive decorator.`);
  14531. }
  14532. if (!directiveDef.standalone) {
  14533. throw new RuntimeError(308 /* RuntimeErrorCode.HOST_DIRECTIVE_NOT_STANDALONE */, `Host directive ${directiveDef.type.name} must be standalone.`);
  14534. }
  14535. if (matchedDefs.indexOf(directiveDef) > -1) {
  14536. throw new RuntimeError(309 /* RuntimeErrorCode.DUPLICATE_DIRECTITVE */, `Directive ${directiveDef.type.name} matches multiple times on the same element. ` +
  14537. `Directives can only match an element once.`);
  14538. }
  14539. validateMappings('input', directiveDef, hostDirectiveConfig.inputs);
  14540. validateMappings('output', directiveDef, hostDirectiveConfig.outputs);
  14541. }
  14542. /**
  14543. * Checks that the host directive inputs/outputs configuration is valid.
  14544. * @param bindingType Kind of binding that is being validated. Used in the error message.
  14545. * @param def Definition of the host directive that is being validated against.
  14546. * @param hostDirectiveBindings Host directive mapping object that shold be validated.
  14547. */
  14548. function validateMappings(bindingType, def, hostDirectiveBindings) {
  14549. const className = def.type.name;
  14550. const bindings = bindingType === 'input' ? def.inputs : def.outputs;
  14551. for (const publicName in hostDirectiveBindings) {
  14552. if (hostDirectiveBindings.hasOwnProperty(publicName)) {
  14553. if (!bindings.hasOwnProperty(publicName)) {
  14554. throw new RuntimeError(311 /* RuntimeErrorCode.HOST_DIRECTIVE_UNDEFINED_BINDING */, `Directive ${className} does not have an ${bindingType} with a public name of ${publicName}.`);
  14555. }
  14556. const remappedPublicName = hostDirectiveBindings[publicName];
  14557. if (bindings.hasOwnProperty(remappedPublicName) && remappedPublicName !== publicName &&
  14558. bindings[remappedPublicName] !== publicName) {
  14559. 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.`);
  14560. }
  14561. }
  14562. }
  14563. }
  14564. /**
  14565. * Decorates the directive definition with support for input transform functions.
  14566. *
  14567. * If the directive uses inheritance, the feature should be included before the
  14568. * `InheritDefinitionFeature` to ensure that the `inputTransforms` field is populated.
  14569. *
  14570. * @codeGenApi
  14571. */
  14572. function ɵɵInputTransformsFeature(definition) {
  14573. const inputs = definition.inputConfig;
  14574. const inputTransforms = {};
  14575. for (const minifiedKey in inputs) {
  14576. if (inputs.hasOwnProperty(minifiedKey)) {
  14577. // Note: the private names are used for the keys, rather than the public ones, because public
  14578. // names can be re-aliased in host directives which would invalidate the lookup.
  14579. const value = inputs[minifiedKey];
  14580. if (Array.isArray(value) && value[2]) {
  14581. inputTransforms[minifiedKey] = value[2];
  14582. }
  14583. }
  14584. }
  14585. definition.inputTransforms =
  14586. inputTransforms;
  14587. }
  14588. function isIterable(obj) {
  14589. return obj !== null && typeof obj === 'object' && obj[Symbol.iterator] !== undefined;
  14590. }
  14591. function isListLikeIterable(obj) {
  14592. if (!isJsObject(obj))
  14593. return false;
  14594. return Array.isArray(obj) ||
  14595. (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
  14596. Symbol.iterator in obj); // JS Iterable have a Symbol.iterator prop
  14597. }
  14598. function areIterablesEqual(a, b, comparator) {
  14599. const iterator1 = a[Symbol.iterator]();
  14600. const iterator2 = b[Symbol.iterator]();
  14601. while (true) {
  14602. const item1 = iterator1.next();
  14603. const item2 = iterator2.next();
  14604. if (item1.done && item2.done)
  14605. return true;
  14606. if (item1.done || item2.done)
  14607. return false;
  14608. if (!comparator(item1.value, item2.value))
  14609. return false;
  14610. }
  14611. }
  14612. function iterateListLike(obj, fn) {
  14613. if (Array.isArray(obj)) {
  14614. for (let i = 0; i < obj.length; i++) {
  14615. fn(obj[i]);
  14616. }
  14617. }
  14618. else {
  14619. const iterator = obj[Symbol.iterator]();
  14620. let item;
  14621. while (!((item = iterator.next()).done)) {
  14622. fn(item.value);
  14623. }
  14624. }
  14625. }
  14626. function isJsObject(o) {
  14627. return o !== null && (typeof o === 'function' || typeof o === 'object');
  14628. }
  14629. function devModeEqual(a, b) {
  14630. const isListLikeIterableA = isListLikeIterable(a);
  14631. const isListLikeIterableB = isListLikeIterable(b);
  14632. if (isListLikeIterableA && isListLikeIterableB) {
  14633. return areIterablesEqual(a, b, devModeEqual);
  14634. }
  14635. else {
  14636. const isAObject = a && (typeof a === 'object' || typeof a === 'function');
  14637. const isBObject = b && (typeof b === 'object' || typeof b === 'function');
  14638. if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) {
  14639. return true;
  14640. }
  14641. else {
  14642. return Object.is(a, b);
  14643. }
  14644. }
  14645. }
  14646. // TODO(misko): consider inlining
  14647. /** Updates binding and returns the value. */
  14648. function updateBinding(lView, bindingIndex, value) {
  14649. return lView[bindingIndex] = value;
  14650. }
  14651. /** Gets the current binding value. */
  14652. function getBinding(lView, bindingIndex) {
  14653. ngDevMode && assertIndexInRange(lView, bindingIndex);
  14654. ngDevMode &&
  14655. assertNotSame(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.');
  14656. return lView[bindingIndex];
  14657. }
  14658. /**
  14659. * Updates binding if changed, then returns whether it was updated.
  14660. *
  14661. * This function also checks the `CheckNoChangesMode` and throws if changes are made.
  14662. * Some changes (Objects/iterables) during `CheckNoChangesMode` are exempt to comply with VE
  14663. * behavior.
  14664. *
  14665. * @param lView current `LView`
  14666. * @param bindingIndex The binding in the `LView` to check
  14667. * @param value New value to check against `lView[bindingIndex]`
  14668. * @returns `true` if the bindings has changed. (Throws if binding has changed during
  14669. * `CheckNoChangesMode`)
  14670. */
  14671. function bindingUpdated(lView, bindingIndex, value) {
  14672. ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
  14673. ngDevMode &&
  14674. assertLessThan(bindingIndex, lView.length, `Slot should have been initialized to NO_CHANGE`);
  14675. const oldValue = lView[bindingIndex];
  14676. if (Object.is(oldValue, value)) {
  14677. return false;
  14678. }
  14679. else {
  14680. if (ngDevMode && isInCheckNoChangesMode()) {
  14681. // View engine didn't report undefined values as changed on the first checkNoChanges pass
  14682. // (before the change detection was run).
  14683. const oldValueToCompare = oldValue !== NO_CHANGE ? oldValue : undefined;
  14684. if (!devModeEqual(oldValueToCompare, value)) {
  14685. const details = getExpressionChangedErrorDetails(lView, bindingIndex, oldValueToCompare, value);
  14686. throwErrorIfNoChangesMode(oldValue === NO_CHANGE, details.oldValue, details.newValue, details.propName, lView);
  14687. }
  14688. // There was a change, but the `devModeEqual` decided that the change is exempt from an error.
  14689. // For this reason we exit as if no change. The early exit is needed to prevent the changed
  14690. // value to be written into `LView` (If we would write the new value that we would not see it
  14691. // as change on next CD.)
  14692. return false;
  14693. }
  14694. lView[bindingIndex] = value;
  14695. return true;
  14696. }
  14697. }
  14698. /** Updates 2 bindings if changed, then returns whether either was updated. */
  14699. function bindingUpdated2(lView, bindingIndex, exp1, exp2) {
  14700. const different = bindingUpdated(lView, bindingIndex, exp1);
  14701. return bindingUpdated(lView, bindingIndex + 1, exp2) || different;
  14702. }
  14703. /** Updates 3 bindings if changed, then returns whether any was updated. */
  14704. function bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) {
  14705. const different = bindingUpdated2(lView, bindingIndex, exp1, exp2);
  14706. return bindingUpdated(lView, bindingIndex + 2, exp3) || different;
  14707. }
  14708. /** Updates 4 bindings if changed, then returns whether any was updated. */
  14709. function bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) {
  14710. const different = bindingUpdated2(lView, bindingIndex, exp1, exp2);
  14711. return bindingUpdated2(lView, bindingIndex + 2, exp3, exp4) || different;
  14712. }
  14713. /**
  14714. * Updates the value of or removes a bound attribute on an Element.
  14715. *
  14716. * Used in the case of `[attr.title]="value"`
  14717. *
  14718. * @param name name The name of the attribute.
  14719. * @param value value The attribute is removed when value is `null` or `undefined`.
  14720. * Otherwise the attribute value is set to the stringified value.
  14721. * @param sanitizer An optional function used to sanitize the value.
  14722. * @param namespace Optional namespace to use when setting the attribute.
  14723. *
  14724. * @codeGenApi
  14725. */
  14726. function ɵɵattribute(name, value, sanitizer, namespace) {
  14727. const lView = getLView();
  14728. const bindingIndex = nextBindingIndex();
  14729. if (bindingUpdated(lView, bindingIndex, value)) {
  14730. const tView = getTView();
  14731. const tNode = getSelectedTNode();
  14732. elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace);
  14733. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, 'attr.' + name, bindingIndex);
  14734. }
  14735. return ɵɵattribute;
  14736. }
  14737. /**
  14738. * Create interpolation bindings with a variable number of expressions.
  14739. *
  14740. * If there are 1 to 8 expressions `interpolation1()` to `interpolation8()` should be used instead.
  14741. * Those are faster because there is no need to create an array of expressions and iterate over it.
  14742. *
  14743. * `values`:
  14744. * - has static text at even indexes,
  14745. * - has evaluated expressions at odd indexes.
  14746. *
  14747. * Returns the concatenated string when any of the arguments changes, `NO_CHANGE` otherwise.
  14748. */
  14749. function interpolationV(lView, values) {
  14750. ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values');
  14751. ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values');
  14752. let isBindingUpdated = false;
  14753. let bindingIndex = getBindingIndex();
  14754. for (let i = 1; i < values.length; i += 2) {
  14755. // Check if bindings (odd indexes) have changed
  14756. isBindingUpdated = bindingUpdated(lView, bindingIndex++, values[i]) || isBindingUpdated;
  14757. }
  14758. setBindingIndex(bindingIndex);
  14759. if (!isBindingUpdated) {
  14760. return NO_CHANGE;
  14761. }
  14762. // Build the updated content
  14763. let content = values[0];
  14764. for (let i = 1; i < values.length; i += 2) {
  14765. content += renderStringify(values[i]) + values[i + 1];
  14766. }
  14767. return content;
  14768. }
  14769. /**
  14770. * Creates an interpolation binding with 1 expression.
  14771. *
  14772. * @param prefix static value used for concatenation only.
  14773. * @param v0 value checked for change.
  14774. * @param suffix static value used for concatenation only.
  14775. */
  14776. function interpolation1(lView, prefix, v0, suffix) {
  14777. const different = bindingUpdated(lView, nextBindingIndex(), v0);
  14778. return different ? prefix + renderStringify(v0) + suffix : NO_CHANGE;
  14779. }
  14780. /**
  14781. * Creates an interpolation binding with 2 expressions.
  14782. */
  14783. function interpolation2(lView, prefix, v0, i0, v1, suffix) {
  14784. const bindingIndex = getBindingIndex();
  14785. const different = bindingUpdated2(lView, bindingIndex, v0, v1);
  14786. incrementBindingIndex(2);
  14787. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + suffix : NO_CHANGE;
  14788. }
  14789. /**
  14790. * Creates an interpolation binding with 3 expressions.
  14791. */
  14792. function interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix) {
  14793. const bindingIndex = getBindingIndex();
  14794. const different = bindingUpdated3(lView, bindingIndex, v0, v1, v2);
  14795. incrementBindingIndex(3);
  14796. return different ?
  14797. prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + suffix :
  14798. NO_CHANGE;
  14799. }
  14800. /**
  14801. * Create an interpolation binding with 4 expressions.
  14802. */
  14803. function interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  14804. const bindingIndex = getBindingIndex();
  14805. const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  14806. incrementBindingIndex(4);
  14807. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  14808. renderStringify(v2) + i2 + renderStringify(v3) + suffix :
  14809. NO_CHANGE;
  14810. }
  14811. /**
  14812. * Creates an interpolation binding with 5 expressions.
  14813. */
  14814. function interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  14815. const bindingIndex = getBindingIndex();
  14816. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  14817. different = bindingUpdated(lView, bindingIndex + 4, v4) || different;
  14818. incrementBindingIndex(5);
  14819. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  14820. renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + suffix :
  14821. NO_CHANGE;
  14822. }
  14823. /**
  14824. * Creates an interpolation binding with 6 expressions.
  14825. */
  14826. function interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  14827. const bindingIndex = getBindingIndex();
  14828. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  14829. different = bindingUpdated2(lView, bindingIndex + 4, v4, v5) || different;
  14830. incrementBindingIndex(6);
  14831. return different ?
  14832. prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
  14833. renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + suffix :
  14834. NO_CHANGE;
  14835. }
  14836. /**
  14837. * Creates an interpolation binding with 7 expressions.
  14838. */
  14839. function interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  14840. const bindingIndex = getBindingIndex();
  14841. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  14842. different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different;
  14843. incrementBindingIndex(7);
  14844. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  14845. renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 +
  14846. renderStringify(v5) + i5 + renderStringify(v6) + suffix :
  14847. NO_CHANGE;
  14848. }
  14849. /**
  14850. * Creates an interpolation binding with 8 expressions.
  14851. */
  14852. function interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  14853. const bindingIndex = getBindingIndex();
  14854. let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
  14855. different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different;
  14856. incrementBindingIndex(8);
  14857. return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
  14858. renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 +
  14859. renderStringify(v5) + i5 + renderStringify(v6) + i6 + renderStringify(v7) + suffix :
  14860. NO_CHANGE;
  14861. }
  14862. /**
  14863. *
  14864. * Update an interpolated attribute on an element with single bound value surrounded by text.
  14865. *
  14866. * Used when the value passed to a property has 1 interpolated value in it:
  14867. *
  14868. * ```html
  14869. * <div attr.title="prefix{{v0}}suffix"></div>
  14870. * ```
  14871. *
  14872. * Its compiled representation is::
  14873. *
  14874. * ```ts
  14875. * ɵɵattributeInterpolate1('title', 'prefix', v0, 'suffix');
  14876. * ```
  14877. *
  14878. * @param attrName The name of the attribute to update
  14879. * @param prefix Static value used for concatenation only.
  14880. * @param v0 Value checked for change.
  14881. * @param suffix Static value used for concatenation only.
  14882. * @param sanitizer An optional sanitizer function
  14883. * @returns itself, so that it may be chained.
  14884. * @codeGenApi
  14885. */
  14886. function ɵɵattributeInterpolate1(attrName, prefix, v0, suffix, sanitizer, namespace) {
  14887. const lView = getLView();
  14888. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  14889. if (interpolatedValue !== NO_CHANGE) {
  14890. const tNode = getSelectedTNode();
  14891. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  14892. ngDevMode &&
  14893. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 1, prefix, suffix);
  14894. }
  14895. return ɵɵattributeInterpolate1;
  14896. }
  14897. /**
  14898. *
  14899. * Update an interpolated attribute on an element with 2 bound values surrounded by text.
  14900. *
  14901. * Used when the value passed to a property has 2 interpolated values in it:
  14902. *
  14903. * ```html
  14904. * <div attr.title="prefix{{v0}}-{{v1}}suffix"></div>
  14905. * ```
  14906. *
  14907. * Its compiled representation is::
  14908. *
  14909. * ```ts
  14910. * ɵɵattributeInterpolate2('title', 'prefix', v0, '-', v1, 'suffix');
  14911. * ```
  14912. *
  14913. * @param attrName The name of the attribute to update
  14914. * @param prefix Static value used for concatenation only.
  14915. * @param v0 Value checked for change.
  14916. * @param i0 Static value used for concatenation only.
  14917. * @param v1 Value checked for change.
  14918. * @param suffix Static value used for concatenation only.
  14919. * @param sanitizer An optional sanitizer function
  14920. * @returns itself, so that it may be chained.
  14921. * @codeGenApi
  14922. */
  14923. function ɵɵattributeInterpolate2(attrName, prefix, v0, i0, v1, suffix, sanitizer, namespace) {
  14924. const lView = getLView();
  14925. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  14926. if (interpolatedValue !== NO_CHANGE) {
  14927. const tNode = getSelectedTNode();
  14928. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  14929. ngDevMode &&
  14930. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 2, prefix, i0, suffix);
  14931. }
  14932. return ɵɵattributeInterpolate2;
  14933. }
  14934. /**
  14935. *
  14936. * Update an interpolated attribute on an element with 3 bound values surrounded by text.
  14937. *
  14938. * Used when the value passed to a property has 3 interpolated values in it:
  14939. *
  14940. * ```html
  14941. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  14942. * ```
  14943. *
  14944. * Its compiled representation is::
  14945. *
  14946. * ```ts
  14947. * ɵɵattributeInterpolate3(
  14948. * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix');
  14949. * ```
  14950. *
  14951. * @param attrName The name of the attribute to update
  14952. * @param prefix Static value used for concatenation only.
  14953. * @param v0 Value checked for change.
  14954. * @param i0 Static value used for concatenation only.
  14955. * @param v1 Value checked for change.
  14956. * @param i1 Static value used for concatenation only.
  14957. * @param v2 Value checked for change.
  14958. * @param suffix Static value used for concatenation only.
  14959. * @param sanitizer An optional sanitizer function
  14960. * @returns itself, so that it may be chained.
  14961. * @codeGenApi
  14962. */
  14963. function ɵɵattributeInterpolate3(attrName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer, namespace) {
  14964. const lView = getLView();
  14965. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  14966. if (interpolatedValue !== NO_CHANGE) {
  14967. const tNode = getSelectedTNode();
  14968. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  14969. ngDevMode &&
  14970. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 3, prefix, i0, i1, suffix);
  14971. }
  14972. return ɵɵattributeInterpolate3;
  14973. }
  14974. /**
  14975. *
  14976. * Update an interpolated attribute on an element with 4 bound values surrounded by text.
  14977. *
  14978. * Used when the value passed to a property has 4 interpolated values in it:
  14979. *
  14980. * ```html
  14981. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  14982. * ```
  14983. *
  14984. * Its compiled representation is::
  14985. *
  14986. * ```ts
  14987. * ɵɵattributeInterpolate4(
  14988. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  14989. * ```
  14990. *
  14991. * @param attrName The name of the attribute to update
  14992. * @param prefix Static value used for concatenation only.
  14993. * @param v0 Value checked for change.
  14994. * @param i0 Static value used for concatenation only.
  14995. * @param v1 Value checked for change.
  14996. * @param i1 Static value used for concatenation only.
  14997. * @param v2 Value checked for change.
  14998. * @param i2 Static value used for concatenation only.
  14999. * @param v3 Value checked for change.
  15000. * @param suffix Static value used for concatenation only.
  15001. * @param sanitizer An optional sanitizer function
  15002. * @returns itself, so that it may be chained.
  15003. * @codeGenApi
  15004. */
  15005. function ɵɵattributeInterpolate4(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer, namespace) {
  15006. const lView = getLView();
  15007. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  15008. if (interpolatedValue !== NO_CHANGE) {
  15009. const tNode = getSelectedTNode();
  15010. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15011. ngDevMode &&
  15012. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix);
  15013. }
  15014. return ɵɵattributeInterpolate4;
  15015. }
  15016. /**
  15017. *
  15018. * Update an interpolated attribute on an element with 5 bound values surrounded by text.
  15019. *
  15020. * Used when the value passed to a property has 5 interpolated values in it:
  15021. *
  15022. * ```html
  15023. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  15024. * ```
  15025. *
  15026. * Its compiled representation is::
  15027. *
  15028. * ```ts
  15029. * ɵɵattributeInterpolate5(
  15030. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  15031. * ```
  15032. *
  15033. * @param attrName The name of the attribute to update
  15034. * @param prefix Static value used for concatenation only.
  15035. * @param v0 Value checked for change.
  15036. * @param i0 Static value used for concatenation only.
  15037. * @param v1 Value checked for change.
  15038. * @param i1 Static value used for concatenation only.
  15039. * @param v2 Value checked for change.
  15040. * @param i2 Static value used for concatenation only.
  15041. * @param v3 Value checked for change.
  15042. * @param i3 Static value used for concatenation only.
  15043. * @param v4 Value checked for change.
  15044. * @param suffix Static value used for concatenation only.
  15045. * @param sanitizer An optional sanitizer function
  15046. * @returns itself, so that it may be chained.
  15047. * @codeGenApi
  15048. */
  15049. function ɵɵattributeInterpolate5(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer, namespace) {
  15050. const lView = getLView();
  15051. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  15052. if (interpolatedValue !== NO_CHANGE) {
  15053. const tNode = getSelectedTNode();
  15054. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15055. ngDevMode &&
  15056. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix);
  15057. }
  15058. return ɵɵattributeInterpolate5;
  15059. }
  15060. /**
  15061. *
  15062. * Update an interpolated attribute on an element with 6 bound values surrounded by text.
  15063. *
  15064. * Used when the value passed to a property has 6 interpolated values in it:
  15065. *
  15066. * ```html
  15067. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  15068. * ```
  15069. *
  15070. * Its compiled representation is::
  15071. *
  15072. * ```ts
  15073. * ɵɵattributeInterpolate6(
  15074. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  15075. * ```
  15076. *
  15077. * @param attrName The name of the attribute to update
  15078. * @param prefix Static value used for concatenation only.
  15079. * @param v0 Value checked for change.
  15080. * @param i0 Static value used for concatenation only.
  15081. * @param v1 Value checked for change.
  15082. * @param i1 Static value used for concatenation only.
  15083. * @param v2 Value checked for change.
  15084. * @param i2 Static value used for concatenation only.
  15085. * @param v3 Value checked for change.
  15086. * @param i3 Static value used for concatenation only.
  15087. * @param v4 Value checked for change.
  15088. * @param i4 Static value used for concatenation only.
  15089. * @param v5 Value checked for change.
  15090. * @param suffix Static value used for concatenation only.
  15091. * @param sanitizer An optional sanitizer function
  15092. * @returns itself, so that it may be chained.
  15093. * @codeGenApi
  15094. */
  15095. function ɵɵattributeInterpolate6(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer, namespace) {
  15096. const lView = getLView();
  15097. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  15098. if (interpolatedValue !== NO_CHANGE) {
  15099. const tNode = getSelectedTNode();
  15100. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15101. ngDevMode &&
  15102. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix);
  15103. }
  15104. return ɵɵattributeInterpolate6;
  15105. }
  15106. /**
  15107. *
  15108. * Update an interpolated attribute on an element with 7 bound values surrounded by text.
  15109. *
  15110. * Used when the value passed to a property has 7 interpolated values in it:
  15111. *
  15112. * ```html
  15113. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  15114. * ```
  15115. *
  15116. * Its compiled representation is::
  15117. *
  15118. * ```ts
  15119. * ɵɵattributeInterpolate7(
  15120. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  15121. * ```
  15122. *
  15123. * @param attrName The name of the attribute to update
  15124. * @param prefix Static value used for concatenation only.
  15125. * @param v0 Value checked for change.
  15126. * @param i0 Static value used for concatenation only.
  15127. * @param v1 Value checked for change.
  15128. * @param i1 Static value used for concatenation only.
  15129. * @param v2 Value checked for change.
  15130. * @param i2 Static value used for concatenation only.
  15131. * @param v3 Value checked for change.
  15132. * @param i3 Static value used for concatenation only.
  15133. * @param v4 Value checked for change.
  15134. * @param i4 Static value used for concatenation only.
  15135. * @param v5 Value checked for change.
  15136. * @param i5 Static value used for concatenation only.
  15137. * @param v6 Value checked for change.
  15138. * @param suffix Static value used for concatenation only.
  15139. * @param sanitizer An optional sanitizer function
  15140. * @returns itself, so that it may be chained.
  15141. * @codeGenApi
  15142. */
  15143. function ɵɵattributeInterpolate7(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer, namespace) {
  15144. const lView = getLView();
  15145. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  15146. if (interpolatedValue !== NO_CHANGE) {
  15147. const tNode = getSelectedTNode();
  15148. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15149. ngDevMode &&
  15150. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix);
  15151. }
  15152. return ɵɵattributeInterpolate7;
  15153. }
  15154. /**
  15155. *
  15156. * Update an interpolated attribute on an element with 8 bound values surrounded by text.
  15157. *
  15158. * Used when the value passed to a property has 8 interpolated values in it:
  15159. *
  15160. * ```html
  15161. * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  15162. * ```
  15163. *
  15164. * Its compiled representation is::
  15165. *
  15166. * ```ts
  15167. * ɵɵattributeInterpolate8(
  15168. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  15169. * ```
  15170. *
  15171. * @param attrName The name of the attribute to update
  15172. * @param prefix Static value used for concatenation only.
  15173. * @param v0 Value checked for change.
  15174. * @param i0 Static value used for concatenation only.
  15175. * @param v1 Value checked for change.
  15176. * @param i1 Static value used for concatenation only.
  15177. * @param v2 Value checked for change.
  15178. * @param i2 Static value used for concatenation only.
  15179. * @param v3 Value checked for change.
  15180. * @param i3 Static value used for concatenation only.
  15181. * @param v4 Value checked for change.
  15182. * @param i4 Static value used for concatenation only.
  15183. * @param v5 Value checked for change.
  15184. * @param i5 Static value used for concatenation only.
  15185. * @param v6 Value checked for change.
  15186. * @param i6 Static value used for concatenation only.
  15187. * @param v7 Value checked for change.
  15188. * @param suffix Static value used for concatenation only.
  15189. * @param sanitizer An optional sanitizer function
  15190. * @returns itself, so that it may be chained.
  15191. * @codeGenApi
  15192. */
  15193. function ɵɵattributeInterpolate8(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer, namespace) {
  15194. const lView = getLView();
  15195. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  15196. if (interpolatedValue !== NO_CHANGE) {
  15197. const tNode = getSelectedTNode();
  15198. elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
  15199. ngDevMode &&
  15200. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix);
  15201. }
  15202. return ɵɵattributeInterpolate8;
  15203. }
  15204. /**
  15205. * Update an interpolated attribute on an element with 9 or more bound values surrounded by text.
  15206. *
  15207. * Used when the number of interpolated values exceeds 8.
  15208. *
  15209. * ```html
  15210. * <div
  15211. * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
  15212. * ```
  15213. *
  15214. * Its compiled representation is::
  15215. *
  15216. * ```ts
  15217. * ɵɵattributeInterpolateV(
  15218. * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  15219. * 'suffix']);
  15220. * ```
  15221. *
  15222. * @param attrName The name of the attribute to update.
  15223. * @param values The collection of values and the strings in-between those values, beginning with
  15224. * a string prefix and ending with a string suffix.
  15225. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  15226. * @param sanitizer An optional sanitizer function
  15227. * @returns itself, so that it may be chained.
  15228. * @codeGenApi
  15229. */
  15230. function ɵɵattributeInterpolateV(attrName, values, sanitizer, namespace) {
  15231. const lView = getLView();
  15232. const interpolated = interpolationV(lView, values);
  15233. if (interpolated !== NO_CHANGE) {
  15234. const tNode = getSelectedTNode();
  15235. elementAttributeInternal(tNode, lView, attrName, interpolated, sanitizer, namespace);
  15236. if (ngDevMode) {
  15237. const interpolationInBetween = [values[0]]; // prefix
  15238. for (let i = 2; i < values.length; i += 2) {
  15239. interpolationInBetween.push(values[i]);
  15240. }
  15241. storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
  15242. }
  15243. }
  15244. return ɵɵattributeInterpolateV;
  15245. }
  15246. const AT_THIS_LOCATION = '<-- AT THIS LOCATION';
  15247. /**
  15248. * Retrieves a user friendly string for a given TNodeType for use in
  15249. * friendly error messages
  15250. *
  15251. * @param tNodeType
  15252. * @returns
  15253. */
  15254. function getFriendlyStringFromTNodeType(tNodeType) {
  15255. switch (tNodeType) {
  15256. case 4 /* TNodeType.Container */:
  15257. return 'view container';
  15258. case 2 /* TNodeType.Element */:
  15259. return 'element';
  15260. case 8 /* TNodeType.ElementContainer */:
  15261. return 'ng-container';
  15262. case 32 /* TNodeType.Icu */:
  15263. return 'icu';
  15264. case 64 /* TNodeType.Placeholder */:
  15265. return 'i18n';
  15266. case 16 /* TNodeType.Projection */:
  15267. return 'projection';
  15268. case 1 /* TNodeType.Text */:
  15269. return 'text';
  15270. default:
  15271. // This should not happen as we cover all possible TNode types above.
  15272. return '<unknown>';
  15273. }
  15274. }
  15275. /**
  15276. * Validates that provided nodes match during the hydration process.
  15277. */
  15278. function validateMatchingNode(node, nodeType, tagName, lView, tNode, isViewContainerAnchor = false) {
  15279. if (!node ||
  15280. (node.nodeType !== nodeType ||
  15281. (node.nodeType === Node.ELEMENT_NODE &&
  15282. node.tagName.toLowerCase() !== tagName?.toLowerCase()))) {
  15283. const expectedNode = shortRNodeDescription(nodeType, tagName, null);
  15284. let header = `During hydration Angular expected ${expectedNode} but `;
  15285. const hostComponentDef = getDeclarationComponentDef(lView);
  15286. const componentClassName = hostComponentDef?.type?.name;
  15287. const expected = `Angular expected this DOM:\n\n${describeExpectedDom(lView, tNode, isViewContainerAnchor)}\n\n`;
  15288. let actual = '';
  15289. if (!node) {
  15290. // No node found during hydration.
  15291. header += `the node was not found.\n\n`;
  15292. }
  15293. else {
  15294. const actualNode = shortRNodeDescription(node.nodeType, node.tagName ?? null, node.textContent ?? null);
  15295. header += `found ${actualNode}.\n\n`;
  15296. actual = `Actual DOM is:\n\n${describeDomFromNode(node)}\n\n`;
  15297. }
  15298. const footer = getHydrationErrorFooter(componentClassName);
  15299. const message = header + expected + actual + getHydrationAttributeNote() + footer;
  15300. throw new RuntimeError(-500 /* RuntimeErrorCode.HYDRATION_NODE_MISMATCH */, message);
  15301. }
  15302. }
  15303. /**
  15304. * Validates that a given node has sibling nodes
  15305. */
  15306. function validateSiblingNodeExists(node) {
  15307. validateNodeExists(node);
  15308. if (!node.nextSibling) {
  15309. const header = 'During hydration Angular expected more sibling nodes to be present.\n\n';
  15310. const actual = `Actual DOM is:\n\n${describeDomFromNode(node)}\n\n`;
  15311. const footer = getHydrationErrorFooter();
  15312. const message = header + actual + footer;
  15313. throw new RuntimeError(-501 /* RuntimeErrorCode.HYDRATION_MISSING_SIBLINGS */, message);
  15314. }
  15315. }
  15316. /**
  15317. * Validates that a node exists or throws
  15318. */
  15319. function validateNodeExists(node, lView = null, tNode = null) {
  15320. if (!node) {
  15321. const header = 'During hydration, Angular expected an element to be present at this location.\n\n';
  15322. let expected = '';
  15323. let footer = '';
  15324. if (lView !== null && tNode !== null) {
  15325. expected = `${describeExpectedDom(lView, tNode, false)}\n\n`;
  15326. footer = getHydrationErrorFooter();
  15327. }
  15328. throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + expected + footer);
  15329. }
  15330. }
  15331. /**
  15332. * Builds the hydration error message when a node is not found
  15333. *
  15334. * @param lView the LView where the node exists
  15335. * @param tNode the TNode
  15336. */
  15337. function nodeNotFoundError(lView, tNode) {
  15338. const header = 'During serialization, Angular was unable to find an element in the DOM:\n\n';
  15339. const expected = `${describeExpectedDom(lView, tNode, false)}\n\n`;
  15340. const footer = getHydrationErrorFooter();
  15341. throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + expected + footer);
  15342. }
  15343. /**
  15344. * Builds a hydration error message when a node is not found at a path location
  15345. *
  15346. * @param host the Host Node
  15347. * @param path the path to the node
  15348. */
  15349. function nodeNotFoundAtPathError(host, path) {
  15350. const header = `During hydration Angular was unable to locate a node ` +
  15351. `using the "${path}" path, starting from the ${describeRNode(host)} node.\n\n`;
  15352. const footer = getHydrationErrorFooter();
  15353. throw new RuntimeError(-502 /* RuntimeErrorCode.HYDRATION_MISSING_NODE */, header + footer);
  15354. }
  15355. /**
  15356. * Builds the hydration error message in the case that dom nodes are created outside of
  15357. * the Angular context and are being used as projected nodes
  15358. *
  15359. * @param lView the LView
  15360. * @param tNode the TNode
  15361. * @returns an error
  15362. */
  15363. function unsupportedProjectionOfDomNodes(rNode) {
  15364. const header = 'During serialization, Angular detected DOM nodes ' +
  15365. 'that were created outside of Angular context and provided as projectable nodes ' +
  15366. '(likely via `ViewContainerRef.createComponent` or `createComponent` APIs). ' +
  15367. 'Hydration is not supported for such cases, consider refactoring the code to avoid ' +
  15368. 'this pattern or using `ngSkipHydration` on the host element of the component.\n\n';
  15369. const actual = `${describeDomFromNode(rNode)}\n\n`;
  15370. const message = header + actual + getHydrationAttributeNote();
  15371. return new RuntimeError(-503 /* RuntimeErrorCode.UNSUPPORTED_PROJECTION_DOM_NODES */, message);
  15372. }
  15373. /**
  15374. * Builds the hydration error message in the case that ngSkipHydration was used on a
  15375. * node that is not a component host element or host binding
  15376. *
  15377. * @param rNode the HTML Element
  15378. * @returns an error
  15379. */
  15380. function invalidSkipHydrationHost(rNode) {
  15381. const header = 'The `ngSkipHydration` flag is applied on a node ' +
  15382. 'that doesn\'t act as a component host. Hydration can be ' +
  15383. 'skipped only on per-component basis.\n\n';
  15384. const actual = `${describeDomFromNode(rNode)}\n\n`;
  15385. const footer = 'Please move the `ngSkipHydration` attribute to the component host element.\n\n';
  15386. const message = header + actual + footer;
  15387. return new RuntimeError(-504 /* RuntimeErrorCode.INVALID_SKIP_HYDRATION_HOST */, message);
  15388. }
  15389. // Stringification methods
  15390. /**
  15391. * Stringifies a given TNode's attributes
  15392. *
  15393. * @param tNode a provided TNode
  15394. * @returns string
  15395. */
  15396. function stringifyTNodeAttrs(tNode) {
  15397. const results = [];
  15398. if (tNode.attrs) {
  15399. for (let i = 0; i < tNode.attrs.length;) {
  15400. const attrName = tNode.attrs[i++];
  15401. // Once we reach the first flag, we know that the list of
  15402. // attributes is over.
  15403. if (typeof attrName == 'number') {
  15404. break;
  15405. }
  15406. const attrValue = tNode.attrs[i++];
  15407. results.push(`${attrName}="${shorten(attrValue)}"`);
  15408. }
  15409. }
  15410. return results.join(' ');
  15411. }
  15412. /**
  15413. * The list of internal attributes that should be filtered out while
  15414. * producing an error message.
  15415. */
  15416. const internalAttrs = new Set(['ngh', 'ng-version', 'ng-server-context']);
  15417. /**
  15418. * Stringifies an HTML Element's attributes
  15419. *
  15420. * @param rNode an HTML Element
  15421. * @returns string
  15422. */
  15423. function stringifyRNodeAttrs(rNode) {
  15424. const results = [];
  15425. for (let i = 0; i < rNode.attributes.length; i++) {
  15426. const attr = rNode.attributes[i];
  15427. if (internalAttrs.has(attr.name))
  15428. continue;
  15429. results.push(`${attr.name}="${shorten(attr.value)}"`);
  15430. }
  15431. return results.join(' ');
  15432. }
  15433. // Methods for Describing the DOM
  15434. /**
  15435. * Converts a tNode to a helpful readable string value for use in error messages
  15436. *
  15437. * @param tNode a given TNode
  15438. * @param innerContent the content of the node
  15439. * @returns string
  15440. */
  15441. function describeTNode(tNode, innerContent = '…') {
  15442. switch (tNode.type) {
  15443. case 1 /* TNodeType.Text */:
  15444. const content = tNode.value ? `(${tNode.value})` : '';
  15445. return `#text${content}`;
  15446. case 2 /* TNodeType.Element */:
  15447. const attrs = stringifyTNodeAttrs(tNode);
  15448. const tag = tNode.value.toLowerCase();
  15449. return `<${tag}${attrs ? ' ' + attrs : ''}>${innerContent}</${tag}>`;
  15450. case 8 /* TNodeType.ElementContainer */:
  15451. return '<!-- ng-container -->';
  15452. case 4 /* TNodeType.Container */:
  15453. return '<!-- container -->';
  15454. default:
  15455. const typeAsString = getFriendlyStringFromTNodeType(tNode.type);
  15456. return `#node(${typeAsString})`;
  15457. }
  15458. }
  15459. /**
  15460. * Converts an RNode to a helpful readable string value for use in error messages
  15461. *
  15462. * @param rNode a given RNode
  15463. * @param innerContent the content of the node
  15464. * @returns string
  15465. */
  15466. function describeRNode(rNode, innerContent = '…') {
  15467. const node = rNode;
  15468. switch (node.nodeType) {
  15469. case Node.ELEMENT_NODE:
  15470. const tag = node.tagName.toLowerCase();
  15471. const attrs = stringifyRNodeAttrs(node);
  15472. return `<${tag}${attrs ? ' ' + attrs : ''}>${innerContent}</${tag}>`;
  15473. case Node.TEXT_NODE:
  15474. const content = node.textContent ? shorten(node.textContent) : '';
  15475. return `#text${content ? `(${content})` : ''}`;
  15476. case Node.COMMENT_NODE:
  15477. return `<!-- ${shorten(node.textContent ?? '')} -->`;
  15478. default:
  15479. return `#node(${node.nodeType})`;
  15480. }
  15481. }
  15482. /**
  15483. * Builds the string containing the expected DOM present given the LView and TNode
  15484. * values for a readable error message
  15485. *
  15486. * @param lView the lView containing the DOM
  15487. * @param tNode the tNode
  15488. * @param isViewContainerAnchor boolean
  15489. * @returns string
  15490. */
  15491. function describeExpectedDom(lView, tNode, isViewContainerAnchor) {
  15492. const spacer = ' ';
  15493. let content = '';
  15494. if (tNode.prev) {
  15495. content += spacer + '…\n';
  15496. content += spacer + describeTNode(tNode.prev) + '\n';
  15497. }
  15498. else if (tNode.type && tNode.type & 12 /* TNodeType.AnyContainer */) {
  15499. content += spacer + '…\n';
  15500. }
  15501. if (isViewContainerAnchor) {
  15502. content += spacer + describeTNode(tNode) + '\n';
  15503. content += spacer + `<!-- container --> ${AT_THIS_LOCATION}\n`;
  15504. }
  15505. else {
  15506. content += spacer + describeTNode(tNode) + ` ${AT_THIS_LOCATION}\n`;
  15507. }
  15508. content += spacer + '…\n';
  15509. const parentRNode = tNode.type ? getParentRElement(lView[TVIEW], tNode, lView) : null;
  15510. if (parentRNode) {
  15511. content = describeRNode(parentRNode, '\n' + content);
  15512. }
  15513. return content;
  15514. }
  15515. /**
  15516. * Builds the string containing the DOM present around a given RNode for a
  15517. * readable error message
  15518. *
  15519. * @param node the RNode
  15520. * @returns string
  15521. */
  15522. function describeDomFromNode(node) {
  15523. const spacer = ' ';
  15524. let content = '';
  15525. const currentNode = node;
  15526. if (currentNode.previousSibling) {
  15527. content += spacer + '…\n';
  15528. content += spacer + describeRNode(currentNode.previousSibling) + '\n';
  15529. }
  15530. content += spacer + describeRNode(currentNode) + ` ${AT_THIS_LOCATION}\n`;
  15531. if (node.nextSibling) {
  15532. content += spacer + '…\n';
  15533. }
  15534. if (node.parentNode) {
  15535. content = describeRNode(currentNode.parentNode, '\n' + content);
  15536. }
  15537. return content;
  15538. }
  15539. /**
  15540. * Shortens the description of a given RNode by its type for readability
  15541. *
  15542. * @param nodeType the type of node
  15543. * @param tagName the node tag name
  15544. * @param textContent the text content in the node
  15545. * @returns string
  15546. */
  15547. function shortRNodeDescription(nodeType, tagName, textContent) {
  15548. switch (nodeType) {
  15549. case Node.ELEMENT_NODE:
  15550. return `<${tagName.toLowerCase()}>`;
  15551. case Node.TEXT_NODE:
  15552. const content = textContent ? ` (with the "${shorten(textContent)}" content)` : '';
  15553. return `a text node${content}`;
  15554. case Node.COMMENT_NODE:
  15555. return 'a comment node';
  15556. default:
  15557. return `#node(nodeType=${nodeType})`;
  15558. }
  15559. }
  15560. /**
  15561. * Builds the footer hydration error message
  15562. *
  15563. * @param componentClassName the name of the component class
  15564. * @returns string
  15565. */
  15566. function getHydrationErrorFooter(componentClassName) {
  15567. const componentInfo = componentClassName ? `the "${componentClassName}"` : 'corresponding';
  15568. return `To fix this problem:\n` +
  15569. ` * check ${componentInfo} component for hydration-related issues\n` +
  15570. ` * check to see if your template has valid HTML structure\n` +
  15571. ` * or skip hydration by adding the \`ngSkipHydration\` attribute ` +
  15572. `to its host node in a template\n\n`;
  15573. }
  15574. /**
  15575. * An attribute related note for hydration errors
  15576. */
  15577. function getHydrationAttributeNote() {
  15578. return 'Note: attributes are only displayed to better represent the DOM' +
  15579. ' but have no effect on hydration mismatches.\n\n';
  15580. }
  15581. // Node string utility functions
  15582. /**
  15583. * Strips all newlines out of a given string
  15584. *
  15585. * @param input a string to be cleared of new line characters
  15586. * @returns
  15587. */
  15588. function stripNewlines(input) {
  15589. return input.replace(/\s+/gm, '');
  15590. }
  15591. /**
  15592. * Reduces a string down to a maximum length of characters with ellipsis for readability
  15593. *
  15594. * @param input a string input
  15595. * @param maxLength a maximum length in characters
  15596. * @returns string
  15597. */
  15598. function shorten(input, maxLength = 50) {
  15599. if (!input) {
  15600. return '';
  15601. }
  15602. input = stripNewlines(input);
  15603. return input.length > maxLength ? `${input.substring(0, maxLength - 1)}…` : input;
  15604. }
  15605. /**
  15606. * Regexp that extracts a reference node information from the compressed node location.
  15607. * The reference node is represented as either:
  15608. * - a number which points to an LView slot
  15609. * - the `b` char which indicates that the lookup should start from the `document.body`
  15610. * - the `h` char to start lookup from the component host node (`lView[HOST]`)
  15611. */
  15612. const REF_EXTRACTOR_REGEXP = new RegExp(`^(\\d+)*(${REFERENCE_NODE_BODY}|${REFERENCE_NODE_HOST})*(.*)`);
  15613. /**
  15614. * Helper function that takes a reference node location and a set of navigation steps
  15615. * (from the reference node) to a target node and outputs a string that represents
  15616. * a location.
  15617. *
  15618. * For example, given: referenceNode = 'b' (body) and path = ['firstChild', 'firstChild',
  15619. * 'nextSibling'], the function returns: `bf2n`.
  15620. */
  15621. function compressNodeLocation(referenceNode, path) {
  15622. const result = [referenceNode];
  15623. for (const segment of path) {
  15624. const lastIdx = result.length - 1;
  15625. if (lastIdx > 0 && result[lastIdx - 1] === segment) {
  15626. // An empty string in a count slot represents 1 occurrence of an instruction.
  15627. const value = (result[lastIdx] || 1);
  15628. result[lastIdx] = value + 1;
  15629. }
  15630. else {
  15631. // Adding a new segment to the path.
  15632. // Using an empty string in a counter field to avoid encoding `1`s
  15633. // into the path, since they are implicit (e.g. `f1n1` vs `fn`), so
  15634. // it's enough to have a single char in this case.
  15635. result.push(segment, '');
  15636. }
  15637. }
  15638. return result.join('');
  15639. }
  15640. /**
  15641. * Helper function that reverts the `compressNodeLocation` and transforms a given
  15642. * string into an array where at 0th position there is a reference node info and
  15643. * after that it contains information (in pairs) about a navigation step and the
  15644. * number of repetitions.
  15645. *
  15646. * For example, the path like 'bf2n' will be transformed to:
  15647. * ['b', 'firstChild', 2, 'nextSibling', 1].
  15648. *
  15649. * This information is later consumed by the code that navigates the DOM to find
  15650. * a given node by its location.
  15651. */
  15652. function decompressNodeLocation(path) {
  15653. const matches = path.match(REF_EXTRACTOR_REGEXP);
  15654. const [_, refNodeId, refNodeName, rest] = matches;
  15655. // If a reference node is represented by an index, transform it to a number.
  15656. const ref = refNodeId ? parseInt(refNodeId, 10) : refNodeName;
  15657. const steps = [];
  15658. // Match all segments in a path.
  15659. for (const [_, step, count] of rest.matchAll(/(f|n)(\d*)/g)) {
  15660. const repeat = parseInt(count, 10) || 1;
  15661. steps.push(step, repeat);
  15662. }
  15663. return [ref, ...steps];
  15664. }
  15665. /** Whether current TNode is a first node in an <ng-container>. */
  15666. function isFirstElementInNgContainer(tNode) {
  15667. return !tNode.prev && tNode.parent?.type === 8 /* TNodeType.ElementContainer */;
  15668. }
  15669. /** Returns an instruction index (subtracting HEADER_OFFSET). */
  15670. function getNoOffsetIndex(tNode) {
  15671. return tNode.index - HEADER_OFFSET;
  15672. }
  15673. /**
  15674. * Locate a node in DOM tree that corresponds to a given TNode.
  15675. *
  15676. * @param hydrationInfo The hydration annotation data
  15677. * @param tView the current tView
  15678. * @param lView the current lView
  15679. * @param tNode the current tNode
  15680. * @returns an RNode that represents a given tNode
  15681. */
  15682. function locateNextRNode(hydrationInfo, tView, lView, tNode) {
  15683. let native = null;
  15684. const noOffsetIndex = getNoOffsetIndex(tNode);
  15685. const nodes = hydrationInfo.data[NODES];
  15686. if (nodes?.[noOffsetIndex]) {
  15687. // We know the exact location of the node.
  15688. native = locateRNodeByPath(nodes[noOffsetIndex], lView);
  15689. }
  15690. else if (tView.firstChild === tNode) {
  15691. // We create a first node in this view, so we use a reference
  15692. // to the first child in this DOM segment.
  15693. native = hydrationInfo.firstChild;
  15694. }
  15695. else {
  15696. // Locate a node based on a previous sibling or a parent node.
  15697. const previousTNodeParent = tNode.prev === null;
  15698. const previousTNode = (tNode.prev ?? tNode.parent);
  15699. ngDevMode &&
  15700. assertDefined(previousTNode, 'Unexpected state: current TNode does not have a connection ' +
  15701. 'to the previous node or a parent node.');
  15702. if (isFirstElementInNgContainer(tNode)) {
  15703. const noOffsetParentIndex = getNoOffsetIndex(tNode.parent);
  15704. native = getSegmentHead(hydrationInfo, noOffsetParentIndex);
  15705. }
  15706. else {
  15707. let previousRElement = getNativeByTNode(previousTNode, lView);
  15708. if (previousTNodeParent) {
  15709. native = previousRElement.firstChild;
  15710. }
  15711. else {
  15712. // If the previous node is an element, but it also has container info,
  15713. // this means that we are processing a node like `<div #vcrTarget>`, which is
  15714. // represented in the DOM as `<div></div>...<!--container-->`.
  15715. // In this case, there are nodes *after* this element and we need to skip
  15716. // all of them to reach an element that we are looking for.
  15717. const noOffsetPrevSiblingIndex = getNoOffsetIndex(previousTNode);
  15718. const segmentHead = getSegmentHead(hydrationInfo, noOffsetPrevSiblingIndex);
  15719. if (previousTNode.type === 2 /* TNodeType.Element */ && segmentHead) {
  15720. const numRootNodesToSkip = calcSerializedContainerSize(hydrationInfo, noOffsetPrevSiblingIndex);
  15721. // `+1` stands for an anchor comment node after all the views in this container.
  15722. const nodesToSkip = numRootNodesToSkip + 1;
  15723. // First node after this segment.
  15724. native = siblingAfter(nodesToSkip, segmentHead);
  15725. }
  15726. else {
  15727. native = previousRElement.nextSibling;
  15728. }
  15729. }
  15730. }
  15731. }
  15732. return native;
  15733. }
  15734. /**
  15735. * Skips over a specified number of nodes and returns the next sibling node after that.
  15736. */
  15737. function siblingAfter(skip, from) {
  15738. let currentNode = from;
  15739. for (let i = 0; i < skip; i++) {
  15740. ngDevMode && validateSiblingNodeExists(currentNode);
  15741. currentNode = currentNode.nextSibling;
  15742. }
  15743. return currentNode;
  15744. }
  15745. /**
  15746. * Helper function to produce a string representation of the navigation steps
  15747. * (in terms of `nextSibling` and `firstChild` navigations). Used in error
  15748. * messages in dev mode.
  15749. */
  15750. function stringifyNavigationInstructions(instructions) {
  15751. const container = [];
  15752. for (let i = 0; i < instructions.length; i += 2) {
  15753. const step = instructions[i];
  15754. const repeat = instructions[i + 1];
  15755. for (let r = 0; r < repeat; r++) {
  15756. container.push(step === NodeNavigationStep.FirstChild ? 'firstChild' : 'nextSibling');
  15757. }
  15758. }
  15759. return container.join('.');
  15760. }
  15761. /**
  15762. * Helper function that navigates from a starting point node (the `from` node)
  15763. * using provided set of navigation instructions (within `path` argument).
  15764. */
  15765. function navigateToNode(from, instructions) {
  15766. let node = from;
  15767. for (let i = 0; i < instructions.length; i += 2) {
  15768. const step = instructions[i];
  15769. const repeat = instructions[i + 1];
  15770. for (let r = 0; r < repeat; r++) {
  15771. if (ngDevMode && !node) {
  15772. throw nodeNotFoundAtPathError(from, stringifyNavigationInstructions(instructions));
  15773. }
  15774. switch (step) {
  15775. case NodeNavigationStep.FirstChild:
  15776. node = node.firstChild;
  15777. break;
  15778. case NodeNavigationStep.NextSibling:
  15779. node = node.nextSibling;
  15780. break;
  15781. }
  15782. }
  15783. }
  15784. if (ngDevMode && !node) {
  15785. throw nodeNotFoundAtPathError(from, stringifyNavigationInstructions(instructions));
  15786. }
  15787. return node;
  15788. }
  15789. /**
  15790. * Locates an RNode given a set of navigation instructions (which also contains
  15791. * a starting point node info).
  15792. */
  15793. function locateRNodeByPath(path, lView) {
  15794. const [referenceNode, ...navigationInstructions] = decompressNodeLocation(path);
  15795. let ref;
  15796. if (referenceNode === REFERENCE_NODE_HOST) {
  15797. ref = lView[DECLARATION_COMPONENT_VIEW][HOST];
  15798. }
  15799. else if (referenceNode === REFERENCE_NODE_BODY) {
  15800. ref = ɵɵresolveBody(lView[DECLARATION_COMPONENT_VIEW][HOST]);
  15801. }
  15802. else {
  15803. const parentElementId = Number(referenceNode);
  15804. ref = unwrapRNode(lView[parentElementId + HEADER_OFFSET]);
  15805. }
  15806. return navigateToNode(ref, navigationInstructions);
  15807. }
  15808. /**
  15809. * Generate a list of DOM navigation operations to get from node `start` to node `finish`.
  15810. *
  15811. * Note: assumes that node `start` occurs before node `finish` in an in-order traversal of the DOM
  15812. * tree. That is, we should be able to get from `start` to `finish` purely by using `.firstChild`
  15813. * and `.nextSibling` operations.
  15814. */
  15815. function navigateBetween(start, finish) {
  15816. if (start === finish) {
  15817. return [];
  15818. }
  15819. else if (start.parentElement == null || finish.parentElement == null) {
  15820. return null;
  15821. }
  15822. else if (start.parentElement === finish.parentElement) {
  15823. return navigateBetweenSiblings(start, finish);
  15824. }
  15825. else {
  15826. // `finish` is a child of its parent, so the parent will always have a child.
  15827. const parent = finish.parentElement;
  15828. const parentPath = navigateBetween(start, parent);
  15829. const childPath = navigateBetween(parent.firstChild, finish);
  15830. if (!parentPath || !childPath)
  15831. return null;
  15832. return [
  15833. // First navigate to `finish`'s parent
  15834. ...parentPath,
  15835. // Then to its first child.
  15836. NodeNavigationStep.FirstChild,
  15837. // And finally from that node to `finish` (maybe a no-op if we're already there).
  15838. ...childPath,
  15839. ];
  15840. }
  15841. }
  15842. /**
  15843. * Calculates a path between 2 sibling nodes (generates a number of `NextSibling` navigations).
  15844. * Returns `null` if no such path exists between the given nodes.
  15845. */
  15846. function navigateBetweenSiblings(start, finish) {
  15847. const nav = [];
  15848. let node = null;
  15849. for (node = start; node != null && node !== finish; node = node.nextSibling) {
  15850. nav.push(NodeNavigationStep.NextSibling);
  15851. }
  15852. // If the `node` becomes `null` or `undefined` at the end, that means that we
  15853. // didn't find the `end` node, thus return `null` (which would trigger serialization
  15854. // error to be produced).
  15855. return node == null ? null : nav;
  15856. }
  15857. /**
  15858. * Calculates a path between 2 nodes in terms of `nextSibling` and `firstChild`
  15859. * navigations:
  15860. * - the `from` node is a known node, used as an starting point for the lookup
  15861. * (the `fromNodeName` argument is a string representation of the node).
  15862. * - the `to` node is a node that the runtime logic would be looking up,
  15863. * using the path generated by this function.
  15864. */
  15865. function calcPathBetween(from, to, fromNodeName) {
  15866. const path = navigateBetween(from, to);
  15867. return path === null ? null : compressNodeLocation(fromNodeName, path);
  15868. }
  15869. /**
  15870. * Invoked at serialization time (on the server) when a set of navigation
  15871. * instructions needs to be generated for a TNode.
  15872. */
  15873. function calcPathForNode(tNode, lView) {
  15874. const parentTNode = tNode.parent;
  15875. let parentIndex;
  15876. let parentRNode;
  15877. let referenceNodeName;
  15878. if (parentTNode === null || !(parentTNode.type & 3 /* TNodeType.AnyRNode */)) {
  15879. // If there is no parent TNode or a parent TNode does not represent an RNode
  15880. // (i.e. not a DOM node), use component host element as a reference node.
  15881. parentIndex = referenceNodeName = REFERENCE_NODE_HOST;
  15882. parentRNode = lView[DECLARATION_COMPONENT_VIEW][HOST];
  15883. }
  15884. else {
  15885. // Use parent TNode as a reference node.
  15886. parentIndex = parentTNode.index;
  15887. parentRNode = unwrapRNode(lView[parentIndex]);
  15888. referenceNodeName = renderStringify(parentIndex - HEADER_OFFSET);
  15889. }
  15890. let rNode = unwrapRNode(lView[tNode.index]);
  15891. if (tNode.type & 12 /* TNodeType.AnyContainer */) {
  15892. // For <ng-container> nodes, instead of serializing a reference
  15893. // to the anchor comment node, serialize a location of the first
  15894. // DOM element. Paired with the container size (serialized as a part
  15895. // of `ngh.containers`), it should give enough information for runtime
  15896. // to hydrate nodes in this container.
  15897. const firstRNode = getFirstNativeNode(lView, tNode);
  15898. // If container is not empty, use a reference to the first element,
  15899. // otherwise, rNode would point to an anchor comment node.
  15900. if (firstRNode) {
  15901. rNode = firstRNode;
  15902. }
  15903. }
  15904. let path = calcPathBetween(parentRNode, rNode, referenceNodeName);
  15905. if (path === null && parentRNode !== rNode) {
  15906. // Searching for a path between elements within a host node failed.
  15907. // Trying to find a path to an element starting from the `document.body` instead.
  15908. //
  15909. // Important note: this type of reference is relatively unstable, since Angular
  15910. // may not be able to control parts of the page that the runtime logic navigates
  15911. // through. This is mostly needed to cover "portals" use-case (like menus, dialog boxes,
  15912. // etc), where nodes are content-projected (including direct DOM manipulations) outside
  15913. // of the host node. The better solution is to provide APIs to work with "portals",
  15914. // at which point this code path would not be needed.
  15915. const body = parentRNode.ownerDocument.body;
  15916. path = calcPathBetween(body, rNode, REFERENCE_NODE_BODY);
  15917. if (path === null) {
  15918. // If the path is still empty, it's likely that this node is detached and
  15919. // won't be found during hydration.
  15920. throw nodeNotFoundError(lView, tNode);
  15921. }
  15922. }
  15923. return path;
  15924. }
  15925. function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) {
  15926. ngDevMode && assertFirstCreatePass(tView);
  15927. ngDevMode && ngDevMode.firstCreatePass++;
  15928. const tViewConsts = tView.consts;
  15929. // TODO(pk): refactor getOrCreateTNode to have the "create" only version
  15930. const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex));
  15931. resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
  15932. registerPostOrderHooks(tView, tNode);
  15933. const embeddedTView = tNode.tView = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts, null /* ssrId */);
  15934. if (tView.queries !== null) {
  15935. tView.queries.template(tView, tNode);
  15936. embeddedTView.queries = tView.queries.embeddedTView(tNode);
  15937. }
  15938. return tNode;
  15939. }
  15940. /**
  15941. * Creates an LContainer for an ng-template (dynamically-inserted view), e.g.
  15942. *
  15943. * <ng-template #foo>
  15944. * <div></div>
  15945. * </ng-template>
  15946. *
  15947. * @param index The index of the container in the data array
  15948. * @param templateFn Inline template
  15949. * @param decls The number of nodes, local refs, and pipes for this template
  15950. * @param vars The number of bindings for this template
  15951. * @param tagName The name of the container element, if applicable
  15952. * @param attrsIndex Index of template attributes in the `consts` array.
  15953. * @param localRefs Index of the local references in the `consts` array.
  15954. * @param localRefExtractor A function which extracts local-refs values from the template.
  15955. * Defaults to the current element associated with the local-ref.
  15956. *
  15957. * @codeGenApi
  15958. */
  15959. function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex, localRefExtractor) {
  15960. const lView = getLView();
  15961. const tView = getTView();
  15962. const adjustedIndex = index + HEADER_OFFSET;
  15963. const tNode = tView.firstCreatePass ? templateFirstCreatePass(adjustedIndex, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) :
  15964. tView.data[adjustedIndex];
  15965. setCurrentTNode(tNode, false);
  15966. const comment = _locateOrCreateContainerAnchor(tView, lView, tNode, index);
  15967. if (wasLastNodeCreated()) {
  15968. appendChild(tView, lView, comment, tNode);
  15969. }
  15970. attachPatchData(comment, lView);
  15971. addToViewTree(lView, lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode));
  15972. if (isDirectiveHost(tNode)) {
  15973. createDirectivesInstances(tView, lView, tNode);
  15974. }
  15975. if (localRefsIndex != null) {
  15976. saveResolvedLocalsInData(lView, tNode, localRefExtractor);
  15977. }
  15978. }
  15979. let _locateOrCreateContainerAnchor = createContainerAnchorImpl;
  15980. /**
  15981. * Regular creation mode for LContainers and their anchor (comment) nodes.
  15982. */
  15983. function createContainerAnchorImpl(tView, lView, tNode, index) {
  15984. lastNodeWasCreated(true);
  15985. return lView[RENDERER].createComment(ngDevMode ? 'container' : '');
  15986. }
  15987. /**
  15988. * Enables hydration code path (to lookup existing elements in DOM)
  15989. * in addition to the regular creation mode for LContainers and their
  15990. * anchor (comment) nodes.
  15991. */
  15992. function locateOrCreateContainerAnchorImpl(tView, lView, tNode, index) {
  15993. const hydrationInfo = lView[HYDRATION];
  15994. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode$1(hydrationInfo, index);
  15995. lastNodeWasCreated(isNodeCreationMode);
  15996. // Regular creation mode.
  15997. if (isNodeCreationMode) {
  15998. return createContainerAnchorImpl(tView, lView, tNode, index);
  15999. }
  16000. const ssrId = hydrationInfo.data[TEMPLATES]?.[index] ?? null;
  16001. // Apply `ssrId` value to the underlying TView if it was not previously set.
  16002. //
  16003. // There might be situations when the same component is present in a template
  16004. // multiple times and some instances are opted-out of using hydration via
  16005. // `ngSkipHydration` attribute. In this scenario, at the time a TView is created,
  16006. // the `ssrId` might be `null` (if the first component is opted-out of hydration).
  16007. // The code below makes sure that the `ssrId` is applied to the TView if it's still
  16008. // `null` and verifies we never try to override it with a different value.
  16009. if (ssrId !== null && tNode.tView !== null) {
  16010. if (tNode.tView.ssrId === null) {
  16011. tNode.tView.ssrId = ssrId;
  16012. }
  16013. else {
  16014. ngDevMode &&
  16015. assertEqual(tNode.tView.ssrId, ssrId, 'Unexpected value of the `ssrId` for this TView');
  16016. }
  16017. }
  16018. // Hydration mode, looking up existing elements in DOM.
  16019. const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
  16020. ngDevMode && validateNodeExists(currentRNode, lView, tNode);
  16021. setSegmentHead(hydrationInfo, index, currentRNode);
  16022. const viewContainerSize = calcSerializedContainerSize(hydrationInfo, index);
  16023. const comment = siblingAfter(viewContainerSize, currentRNode);
  16024. if (ngDevMode) {
  16025. validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
  16026. markRNodeAsClaimedByHydration(comment);
  16027. }
  16028. return comment;
  16029. }
  16030. function enableLocateOrCreateContainerAnchorImpl() {
  16031. _locateOrCreateContainerAnchor = locateOrCreateContainerAnchorImpl;
  16032. }
  16033. /** Store a value in the `data` at a given `index`. */
  16034. function store(tView, lView, index, value) {
  16035. // We don't store any static data for local variables, so the first time
  16036. // we see the template, we should store as null to avoid a sparse array
  16037. if (index >= tView.data.length) {
  16038. tView.data[index] = null;
  16039. tView.blueprint[index] = null;
  16040. }
  16041. lView[index] = value;
  16042. }
  16043. /**
  16044. * Retrieves a local reference from the current contextViewData.
  16045. *
  16046. * If the reference to retrieve is in a parent view, this instruction is used in conjunction
  16047. * with a nextContext() call, which walks up the tree and updates the contextViewData instance.
  16048. *
  16049. * @param index The index of the local ref in contextViewData.
  16050. *
  16051. * @codeGenApi
  16052. */
  16053. function ɵɵreference(index) {
  16054. const contextLView = getContextLView();
  16055. return load(contextLView, HEADER_OFFSET + index);
  16056. }
  16057. /**
  16058. * Update a property on a selected element.
  16059. *
  16060. * Operates on the element selected by index via the {@link select} instruction.
  16061. *
  16062. * If the property name also exists as an input property on one of the element's directives,
  16063. * the component property will be set instead of the element property. This check must
  16064. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled
  16065. *
  16066. * @param propName Name of property. Because it is going to DOM, this is not subject to
  16067. * renaming as part of minification.
  16068. * @param value New value to write.
  16069. * @param sanitizer An optional function used to sanitize the value.
  16070. * @returns This function returns itself so that it may be chained
  16071. * (e.g. `property('name', ctx.name)('title', ctx.title)`)
  16072. *
  16073. * @codeGenApi
  16074. */
  16075. function ɵɵproperty(propName, value, sanitizer) {
  16076. const lView = getLView();
  16077. const bindingIndex = nextBindingIndex();
  16078. if (bindingUpdated(lView, bindingIndex, value)) {
  16079. const tView = getTView();
  16080. const tNode = getSelectedTNode();
  16081. elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, false);
  16082. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
  16083. }
  16084. return ɵɵproperty;
  16085. }
  16086. /**
  16087. * Given `<div style="..." my-dir>` and `MyDir` with `@Input('style')` we need to write to
  16088. * directive input.
  16089. */
  16090. function setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased) {
  16091. const inputs = tNode.inputs;
  16092. const property = isClassBased ? 'class' : 'style';
  16093. // We support both 'class' and `className` hence the fallback.
  16094. setInputsForProperty(tView, lView, inputs[property], property, value);
  16095. }
  16096. function elementStartFirstCreatePass(index, tView, lView, name, attrsIndex, localRefsIndex) {
  16097. ngDevMode && assertFirstCreatePass(tView);
  16098. ngDevMode && ngDevMode.firstCreatePass++;
  16099. const tViewConsts = tView.consts;
  16100. const attrs = getConstant(tViewConsts, attrsIndex);
  16101. const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, name, attrs);
  16102. resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
  16103. if (tNode.attrs !== null) {
  16104. computeStaticStyling(tNode, tNode.attrs, false);
  16105. }
  16106. if (tNode.mergedAttrs !== null) {
  16107. computeStaticStyling(tNode, tNode.mergedAttrs, true);
  16108. }
  16109. if (tView.queries !== null) {
  16110. tView.queries.elementStart(tView, tNode);
  16111. }
  16112. return tNode;
  16113. }
  16114. /**
  16115. * Create DOM element. The instruction must later be followed by `elementEnd()` call.
  16116. *
  16117. * @param index Index of the element in the LView array
  16118. * @param name Name of the DOM Node
  16119. * @param attrsIndex Index of the element's attributes in the `consts` array.
  16120. * @param localRefsIndex Index of the element's local references in the `consts` array.
  16121. * @returns This function returns itself so that it may be chained.
  16122. *
  16123. * Attributes and localRefs are passed as an array of strings where elements with an even index
  16124. * hold an attribute name and elements with an odd index hold an attribute value, ex.:
  16125. * ['id', 'warning5', 'class', 'alert']
  16126. *
  16127. * @codeGenApi
  16128. */
  16129. function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
  16130. const lView = getLView();
  16131. const tView = getTView();
  16132. const adjustedIndex = HEADER_OFFSET + index;
  16133. ngDevMode &&
  16134. assertEqual(getBindingIndex(), tView.bindingStartIndex, 'elements should be created before any bindings');
  16135. ngDevMode && assertIndexInRange(lView, adjustedIndex);
  16136. const renderer = lView[RENDERER];
  16137. const tNode = tView.firstCreatePass ?
  16138. elementStartFirstCreatePass(adjustedIndex, tView, lView, name, attrsIndex, localRefsIndex) :
  16139. tView.data[adjustedIndex];
  16140. const native = _locateOrCreateElementNode(tView, lView, tNode, renderer, name, index);
  16141. lView[adjustedIndex] = native;
  16142. const hasDirectives = isDirectiveHost(tNode);
  16143. if (ngDevMode && tView.firstCreatePass) {
  16144. validateElementIsKnown(native, lView, tNode.value, tView.schemas, hasDirectives);
  16145. }
  16146. setCurrentTNode(tNode, true);
  16147. setupStaticAttributes(renderer, native, tNode);
  16148. if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */ && wasLastNodeCreated()) {
  16149. // In the i18n case, the translation may have removed this element, so only add it if it is not
  16150. // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
  16151. appendChild(tView, lView, native, tNode);
  16152. }
  16153. // any immediate children of a component or template container must be pre-emptively
  16154. // monkey-patched with the component view data so that the element can be inspected
  16155. // later on using any element discovery utility methods (see `element_discovery.ts`)
  16156. if (getElementDepthCount() === 0) {
  16157. attachPatchData(native, lView);
  16158. }
  16159. increaseElementDepthCount();
  16160. if (hasDirectives) {
  16161. createDirectivesInstances(tView, lView, tNode);
  16162. executeContentQueries(tView, tNode, lView);
  16163. }
  16164. if (localRefsIndex !== null) {
  16165. saveResolvedLocalsInData(lView, tNode);
  16166. }
  16167. return ɵɵelementStart;
  16168. }
  16169. /**
  16170. * Mark the end of the element.
  16171. * @returns This function returns itself so that it may be chained.
  16172. *
  16173. * @codeGenApi
  16174. */
  16175. function ɵɵelementEnd() {
  16176. let currentTNode = getCurrentTNode();
  16177. ngDevMode && assertDefined(currentTNode, 'No parent node to close.');
  16178. if (isCurrentTNodeParent()) {
  16179. setCurrentTNodeAsNotParent();
  16180. }
  16181. else {
  16182. ngDevMode && assertHasParent(getCurrentTNode());
  16183. currentTNode = currentTNode.parent;
  16184. setCurrentTNode(currentTNode, false);
  16185. }
  16186. const tNode = currentTNode;
  16187. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
  16188. if (isSkipHydrationRootTNode(tNode)) {
  16189. leaveSkipHydrationBlock();
  16190. }
  16191. decreaseElementDepthCount();
  16192. const tView = getTView();
  16193. if (tView.firstCreatePass) {
  16194. registerPostOrderHooks(tView, currentTNode);
  16195. if (isContentQueryHost(currentTNode)) {
  16196. tView.queries.elementEnd(currentTNode);
  16197. }
  16198. }
  16199. if (tNode.classesWithoutHost != null && hasClassInput(tNode)) {
  16200. setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.classesWithoutHost, true);
  16201. }
  16202. if (tNode.stylesWithoutHost != null && hasStyleInput(tNode)) {
  16203. setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.stylesWithoutHost, false);
  16204. }
  16205. return ɵɵelementEnd;
  16206. }
  16207. /**
  16208. * Creates an empty element using {@link elementStart} and {@link elementEnd}
  16209. *
  16210. * @param index Index of the element in the data array
  16211. * @param name Name of the DOM Node
  16212. * @param attrsIndex Index of the element's attributes in the `consts` array.
  16213. * @param localRefsIndex Index of the element's local references in the `consts` array.
  16214. * @returns This function returns itself so that it may be chained.
  16215. *
  16216. * @codeGenApi
  16217. */
  16218. function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
  16219. ɵɵelementStart(index, name, attrsIndex, localRefsIndex);
  16220. ɵɵelementEnd();
  16221. return ɵɵelement;
  16222. }
  16223. let _locateOrCreateElementNode = (tView, lView, tNode, renderer, name, index) => {
  16224. lastNodeWasCreated(true);
  16225. return createElementNode(renderer, name, getNamespace$1());
  16226. };
  16227. /**
  16228. * Enables hydration code path (to lookup existing elements in DOM)
  16229. * in addition to the regular creation mode of element nodes.
  16230. */
  16231. function locateOrCreateElementNodeImpl(tView, lView, tNode, renderer, name, index) {
  16232. const hydrationInfo = lView[HYDRATION];
  16233. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode$1(hydrationInfo, index);
  16234. lastNodeWasCreated(isNodeCreationMode);
  16235. // Regular creation mode.
  16236. if (isNodeCreationMode) {
  16237. return createElementNode(renderer, name, getNamespace$1());
  16238. }
  16239. // Hydration mode, looking up an existing element in DOM.
  16240. const native = locateNextRNode(hydrationInfo, tView, lView, tNode);
  16241. ngDevMode && validateMatchingNode(native, Node.ELEMENT_NODE, name, lView, tNode);
  16242. ngDevMode && markRNodeAsClaimedByHydration(native);
  16243. // This element might also be an anchor of a view container.
  16244. if (getSerializedContainerViews(hydrationInfo, index)) {
  16245. // Important note: this element acts as an anchor, but it's **not** a part
  16246. // of the embedded view, so we start the segment **after** this element, taking
  16247. // a reference to the next sibling. For example, the following template:
  16248. // `<div #vcrTarget>` is represented in the DOM as `<div></div>...<!--container-->`,
  16249. // so while processing a `<div>` instruction, point to the next sibling as a
  16250. // start of a segment.
  16251. ngDevMode && validateNodeExists(native.nextSibling, lView, tNode);
  16252. setSegmentHead(hydrationInfo, index, native.nextSibling);
  16253. }
  16254. // Checks if the skip hydration attribute is present during hydration so we know to
  16255. // skip attempting to hydrate this block. We check both TNode and RElement for an
  16256. // attribute: the RElement case is needed for i18n cases, when we add it to host
  16257. // elements during the annotation phase (after all internal data structures are setup).
  16258. if (hydrationInfo &&
  16259. (hasSkipHydrationAttrOnTNode(tNode) || hasSkipHydrationAttrOnRElement(native))) {
  16260. if (isComponentHost(tNode)) {
  16261. enterSkipHydrationBlock(tNode);
  16262. // Since this isn't hydratable, we need to empty the node
  16263. // so there's no duplicate content after render
  16264. clearElementContents(native);
  16265. ngDevMode && ngDevMode.componentsSkippedHydration++;
  16266. }
  16267. else if (ngDevMode) {
  16268. // If this is not a component host, throw an error.
  16269. // Hydration can be skipped on per-component basis only.
  16270. throw invalidSkipHydrationHost(native);
  16271. }
  16272. }
  16273. return native;
  16274. }
  16275. function enableLocateOrCreateElementNodeImpl() {
  16276. _locateOrCreateElementNode = locateOrCreateElementNodeImpl;
  16277. }
  16278. function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) {
  16279. ngDevMode && ngDevMode.firstCreatePass++;
  16280. const tViewConsts = tView.consts;
  16281. const attrs = getConstant(tViewConsts, attrsIndex);
  16282. const tNode = getOrCreateTNode(tView, index, 8 /* TNodeType.ElementContainer */, 'ng-container', attrs);
  16283. // While ng-container doesn't necessarily support styling, we use the style context to identify
  16284. // and execute directives on the ng-container.
  16285. if (attrs !== null) {
  16286. computeStaticStyling(tNode, attrs, true);
  16287. }
  16288. const localRefs = getConstant(tViewConsts, localRefsIndex);
  16289. resolveDirectives(tView, lView, tNode, localRefs);
  16290. if (tView.queries !== null) {
  16291. tView.queries.elementStart(tView, tNode);
  16292. }
  16293. return tNode;
  16294. }
  16295. /**
  16296. * Creates a logical container for other nodes (<ng-container>) backed by a comment node in the DOM.
  16297. * The instruction must later be followed by `elementContainerEnd()` call.
  16298. *
  16299. * @param index Index of the element in the LView array
  16300. * @param attrsIndex Index of the container attributes in the `consts` array.
  16301. * @param localRefsIndex Index of the container's local references in the `consts` array.
  16302. * @returns This function returns itself so that it may be chained.
  16303. *
  16304. * Even if this instruction accepts a set of attributes no actual attribute values are propagated to
  16305. * the DOM (as a comment node can't have attributes). Attributes are here only for directive
  16306. * matching purposes and setting initial inputs of directives.
  16307. *
  16308. * @codeGenApi
  16309. */
  16310. function ɵɵelementContainerStart(index, attrsIndex, localRefsIndex) {
  16311. const lView = getLView();
  16312. const tView = getTView();
  16313. const adjustedIndex = index + HEADER_OFFSET;
  16314. ngDevMode && assertIndexInRange(lView, adjustedIndex);
  16315. ngDevMode &&
  16316. assertEqual(getBindingIndex(), tView.bindingStartIndex, 'element containers should be created before any bindings');
  16317. const tNode = tView.firstCreatePass ?
  16318. elementContainerStartFirstCreatePass(adjustedIndex, tView, lView, attrsIndex, localRefsIndex) :
  16319. tView.data[adjustedIndex];
  16320. setCurrentTNode(tNode, true);
  16321. const comment = _locateOrCreateElementContainerNode(tView, lView, tNode, index);
  16322. lView[adjustedIndex] = comment;
  16323. if (wasLastNodeCreated()) {
  16324. appendChild(tView, lView, comment, tNode);
  16325. }
  16326. attachPatchData(comment, lView);
  16327. if (isDirectiveHost(tNode)) {
  16328. createDirectivesInstances(tView, lView, tNode);
  16329. executeContentQueries(tView, tNode, lView);
  16330. }
  16331. if (localRefsIndex != null) {
  16332. saveResolvedLocalsInData(lView, tNode);
  16333. }
  16334. return ɵɵelementContainerStart;
  16335. }
  16336. /**
  16337. * Mark the end of the <ng-container>.
  16338. * @returns This function returns itself so that it may be chained.
  16339. *
  16340. * @codeGenApi
  16341. */
  16342. function ɵɵelementContainerEnd() {
  16343. let currentTNode = getCurrentTNode();
  16344. const tView = getTView();
  16345. if (isCurrentTNodeParent()) {
  16346. setCurrentTNodeAsNotParent();
  16347. }
  16348. else {
  16349. ngDevMode && assertHasParent(currentTNode);
  16350. currentTNode = currentTNode.parent;
  16351. setCurrentTNode(currentTNode, false);
  16352. }
  16353. ngDevMode && assertTNodeType(currentTNode, 8 /* TNodeType.ElementContainer */);
  16354. if (tView.firstCreatePass) {
  16355. registerPostOrderHooks(tView, currentTNode);
  16356. if (isContentQueryHost(currentTNode)) {
  16357. tView.queries.elementEnd(currentTNode);
  16358. }
  16359. }
  16360. return ɵɵelementContainerEnd;
  16361. }
  16362. /**
  16363. * Creates an empty logical container using {@link elementContainerStart}
  16364. * and {@link elementContainerEnd}
  16365. *
  16366. * @param index Index of the element in the LView array
  16367. * @param attrsIndex Index of the container attributes in the `consts` array.
  16368. * @param localRefsIndex Index of the container's local references in the `consts` array.
  16369. * @returns This function returns itself so that it may be chained.
  16370. *
  16371. * @codeGenApi
  16372. */
  16373. function ɵɵelementContainer(index, attrsIndex, localRefsIndex) {
  16374. ɵɵelementContainerStart(index, attrsIndex, localRefsIndex);
  16375. ɵɵelementContainerEnd();
  16376. return ɵɵelementContainer;
  16377. }
  16378. let _locateOrCreateElementContainerNode = (tView, lView, tNode, index) => {
  16379. lastNodeWasCreated(true);
  16380. return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
  16381. };
  16382. /**
  16383. * Enables hydration code path (to lookup existing elements in DOM)
  16384. * in addition to the regular creation mode of comment nodes that
  16385. * represent <ng-container>'s anchor.
  16386. */
  16387. function locateOrCreateElementContainerNode(tView, lView, tNode, index) {
  16388. let comment;
  16389. const hydrationInfo = lView[HYDRATION];
  16390. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
  16391. lastNodeWasCreated(isNodeCreationMode);
  16392. // Regular creation mode.
  16393. if (isNodeCreationMode) {
  16394. return createCommentNode(lView[RENDERER], ngDevMode ? 'ng-container' : '');
  16395. }
  16396. // Hydration mode, looking up existing elements in DOM.
  16397. const currentRNode = locateNextRNode(hydrationInfo, tView, lView, tNode);
  16398. ngDevMode && validateNodeExists(currentRNode, lView, tNode);
  16399. const ngContainerSize = getNgContainerSize(hydrationInfo, index);
  16400. ngDevMode &&
  16401. assertNumber(ngContainerSize, 'Unexpected state: hydrating an <ng-container>, ' +
  16402. 'but no hydration info is available.');
  16403. setSegmentHead(hydrationInfo, index, currentRNode);
  16404. comment = siblingAfter(ngContainerSize, currentRNode);
  16405. if (ngDevMode) {
  16406. validateMatchingNode(comment, Node.COMMENT_NODE, null, lView, tNode);
  16407. markRNodeAsClaimedByHydration(comment);
  16408. }
  16409. return comment;
  16410. }
  16411. function enableLocateOrCreateElementContainerNodeImpl() {
  16412. _locateOrCreateElementContainerNode = locateOrCreateElementContainerNode;
  16413. }
  16414. /**
  16415. * Returns the current OpaqueViewState instance.
  16416. *
  16417. * Used in conjunction with the restoreView() instruction to save a snapshot
  16418. * of the current view and restore it when listeners are invoked. This allows
  16419. * walking the declaration view tree in listeners to get vars from parent views.
  16420. *
  16421. * @codeGenApi
  16422. */
  16423. function ɵɵgetCurrentView() {
  16424. return getLView();
  16425. }
  16426. /**
  16427. * Determine if the argument is shaped like a Promise
  16428. */
  16429. function isPromise(obj) {
  16430. // allow any Promise/A+ compliant thenable.
  16431. // It's up to the caller to ensure that obj.then conforms to the spec
  16432. return !!obj && typeof obj.then === 'function';
  16433. }
  16434. /**
  16435. * Determine if the argument is a Subscribable
  16436. */
  16437. function isSubscribable(obj) {
  16438. return !!obj && typeof obj.subscribe === 'function';
  16439. }
  16440. /**
  16441. * Adds an event listener to the current node.
  16442. *
  16443. * If an output exists on one of the node's directives, it also subscribes to the output
  16444. * and saves the subscription for later cleanup.
  16445. *
  16446. * @param eventName Name of the event
  16447. * @param listenerFn The function to be called when event emits
  16448. * @param useCapture Whether or not to use capture in event listener - this argument is a reminder
  16449. * from the Renderer3 infrastructure and should be removed from the instruction arguments
  16450. * @param eventTargetResolver Function that returns global target information in case this listener
  16451. * should be attached to a global object like window, document or body
  16452. *
  16453. * @codeGenApi
  16454. */
  16455. function ɵɵlistener(eventName, listenerFn, useCapture, eventTargetResolver) {
  16456. const lView = getLView();
  16457. const tView = getTView();
  16458. const tNode = getCurrentTNode();
  16459. listenerInternal(tView, lView, lView[RENDERER], tNode, eventName, listenerFn, eventTargetResolver);
  16460. return ɵɵlistener;
  16461. }
  16462. /**
  16463. * Registers a synthetic host listener (e.g. `(@foo.start)`) on a component or directive.
  16464. *
  16465. * This instruction is for compatibility purposes and is designed to ensure that a
  16466. * synthetic host listener (e.g. `@HostListener('@foo.start')`) properly gets rendered
  16467. * in the component's renderer. Normally all host listeners are evaluated with the
  16468. * parent component's renderer, but, in the case of animation @triggers, they need
  16469. * to be evaluated with the sub component's renderer (because that's where the
  16470. * animation triggers are defined).
  16471. *
  16472. * Do not use this instruction as a replacement for `listener`. This instruction
  16473. * only exists to ensure compatibility with the ViewEngine's host binding behavior.
  16474. *
  16475. * @param eventName Name of the event
  16476. * @param listenerFn The function to be called when event emits
  16477. * @param useCapture Whether or not to use capture in event listener
  16478. * @param eventTargetResolver Function that returns global target information in case this listener
  16479. * should be attached to a global object like window, document or body
  16480. *
  16481. * @codeGenApi
  16482. */
  16483. function ɵɵsyntheticHostListener(eventName, listenerFn) {
  16484. const tNode = getCurrentTNode();
  16485. const lView = getLView();
  16486. const tView = getTView();
  16487. const currentDef = getCurrentDirectiveDef(tView.data);
  16488. const renderer = loadComponentRenderer(currentDef, tNode, lView);
  16489. listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn);
  16490. return ɵɵsyntheticHostListener;
  16491. }
  16492. /**
  16493. * A utility function that checks if a given element has already an event handler registered for an
  16494. * event with a specified name. The TView.cleanup data structure is used to find out which events
  16495. * are registered for a given element.
  16496. */
  16497. function findExistingListener(tView, lView, eventName, tNodeIdx) {
  16498. const tCleanup = tView.cleanup;
  16499. if (tCleanup != null) {
  16500. for (let i = 0; i < tCleanup.length - 1; i += 2) {
  16501. const cleanupEventName = tCleanup[i];
  16502. if (cleanupEventName === eventName && tCleanup[i + 1] === tNodeIdx) {
  16503. // We have found a matching event name on the same node but it might not have been
  16504. // registered yet, so we must explicitly verify entries in the LView cleanup data
  16505. // structures.
  16506. const lCleanup = lView[CLEANUP];
  16507. const listenerIdxInLCleanup = tCleanup[i + 2];
  16508. return lCleanup.length > listenerIdxInLCleanup ? lCleanup[listenerIdxInLCleanup] : null;
  16509. }
  16510. // TView.cleanup can have a mix of 4-elements entries (for event handler cleanups) or
  16511. // 2-element entries (for directive and queries destroy hooks). As such we can encounter
  16512. // blocks of 4 or 2 items in the tView.cleanup and this is why we iterate over 2 elements
  16513. // first and jump another 2 elements if we detect listeners cleanup (4 elements). Also check
  16514. // documentation of TView.cleanup for more details of this data structure layout.
  16515. if (typeof cleanupEventName === 'string') {
  16516. i += 2;
  16517. }
  16518. }
  16519. }
  16520. return null;
  16521. }
  16522. function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn, eventTargetResolver) {
  16523. const isTNodeDirectiveHost = isDirectiveHost(tNode);
  16524. const firstCreatePass = tView.firstCreatePass;
  16525. const tCleanup = firstCreatePass && getOrCreateTViewCleanup(tView);
  16526. const context = lView[CONTEXT];
  16527. // When the ɵɵlistener instruction was generated and is executed we know that there is either a
  16528. // native listener or a directive output on this element. As such we we know that we will have to
  16529. // register a listener and store its cleanup function on LView.
  16530. const lCleanup = getOrCreateLViewCleanup(lView);
  16531. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
  16532. let processOutputs = true;
  16533. // Adding a native event listener is applicable when:
  16534. // - The corresponding TNode represents a DOM element.
  16535. // - The event target has a resolver (usually resulting in a global object,
  16536. // such as `window` or `document`).
  16537. if ((tNode.type & 3 /* TNodeType.AnyRNode */) || eventTargetResolver) {
  16538. const native = getNativeByTNode(tNode, lView);
  16539. const target = eventTargetResolver ? eventTargetResolver(native) : native;
  16540. const lCleanupIndex = lCleanup.length;
  16541. const idxOrTargetGetter = eventTargetResolver ?
  16542. (_lView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])) :
  16543. tNode.index;
  16544. // In order to match current behavior, native DOM event listeners must be added for all
  16545. // events (including outputs).
  16546. // There might be cases where multiple directives on the same element try to register an event
  16547. // handler function for the same event. In this situation we want to avoid registration of
  16548. // several native listeners as each registration would be intercepted by NgZone and
  16549. // trigger change detection. This would mean that a single user action would result in several
  16550. // change detections being invoked. To avoid this situation we want to have only one call to
  16551. // native handler registration (for the same element and same type of event).
  16552. //
  16553. // In order to have just one native event handler in presence of multiple handler functions,
  16554. // we just register a first handler function as a native event listener and then chain
  16555. // (coalesce) other handler functions on top of the first native handler function.
  16556. let existingListener = null;
  16557. // Please note that the coalescing described here doesn't happen for events specifying an
  16558. // alternative target (ex. (document:click)) - this is to keep backward compatibility with the
  16559. // view engine.
  16560. // Also, we don't have to search for existing listeners is there are no directives
  16561. // matching on a given node as we can't register multiple event handlers for the same event in
  16562. // a template (this would mean having duplicate attributes).
  16563. if (!eventTargetResolver && isTNodeDirectiveHost) {
  16564. existingListener = findExistingListener(tView, lView, eventName, tNode.index);
  16565. }
  16566. if (existingListener !== null) {
  16567. // Attach a new listener to coalesced listeners list, maintaining the order in which
  16568. // listeners are registered. For performance reasons, we keep a reference to the last
  16569. // listener in that list (in `__ngLastListenerFn__` field), so we can avoid going through
  16570. // the entire set each time we need to add a new listener.
  16571. const lastListenerFn = existingListener.__ngLastListenerFn__ || existingListener;
  16572. lastListenerFn.__ngNextListenerFn__ = listenerFn;
  16573. existingListener.__ngLastListenerFn__ = listenerFn;
  16574. processOutputs = false;
  16575. }
  16576. else {
  16577. listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */);
  16578. const cleanupFn = renderer.listen(target, eventName, listenerFn);
  16579. ngDevMode && ngDevMode.rendererAddEventListener++;
  16580. lCleanup.push(listenerFn, cleanupFn);
  16581. tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, lCleanupIndex + 1);
  16582. }
  16583. }
  16584. else {
  16585. // Even if there is no native listener to add, we still need to wrap the listener so that OnPush
  16586. // ancestors are marked dirty when an event occurs.
  16587. listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */);
  16588. }
  16589. // subscribe to directive outputs
  16590. const outputs = tNode.outputs;
  16591. let props;
  16592. if (processOutputs && outputs !== null && (props = outputs[eventName])) {
  16593. const propsLength = props.length;
  16594. if (propsLength) {
  16595. for (let i = 0; i < propsLength; i += 2) {
  16596. const index = props[i];
  16597. ngDevMode && assertIndexInRange(lView, index);
  16598. const minifiedName = props[i + 1];
  16599. const directiveInstance = lView[index];
  16600. const output = directiveInstance[minifiedName];
  16601. if (ngDevMode && !isSubscribable(output)) {
  16602. throw new Error(`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`);
  16603. }
  16604. const subscription = output.subscribe(listenerFn);
  16605. const idx = lCleanup.length;
  16606. lCleanup.push(listenerFn, subscription);
  16607. tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
  16608. }
  16609. }
  16610. }
  16611. }
  16612. function executeListenerWithErrorHandling(lView, context, listenerFn, e) {
  16613. try {
  16614. profiler(6 /* ProfilerEvent.OutputStart */, context, listenerFn);
  16615. // Only explicitly returning false from a listener should preventDefault
  16616. return listenerFn(e) !== false;
  16617. }
  16618. catch (error) {
  16619. handleError(lView, error);
  16620. return false;
  16621. }
  16622. finally {
  16623. profiler(7 /* ProfilerEvent.OutputEnd */, context, listenerFn);
  16624. }
  16625. }
  16626. /**
  16627. * Wraps an event listener with a function that marks ancestors dirty and prevents default behavior,
  16628. * if applicable.
  16629. *
  16630. * @param tNode The TNode associated with this listener
  16631. * @param lView The LView that contains this listener
  16632. * @param listenerFn The listener function to call
  16633. * @param wrapWithPreventDefault Whether or not to prevent default behavior
  16634. * (the procedural renderer does this already, so in those cases, we should skip)
  16635. */
  16636. function wrapListener(tNode, lView, context, listenerFn, wrapWithPreventDefault) {
  16637. // Note: we are performing most of the work in the listener function itself
  16638. // to optimize listener registration.
  16639. return function wrapListenerIn_markDirtyAndPreventDefault(e) {
  16640. // Ivy uses `Function` as a special token that allows us to unwrap the function
  16641. // so that it can be invoked programmatically by `DebugNode.triggerEventHandler`.
  16642. if (e === Function) {
  16643. return listenerFn;
  16644. }
  16645. // In order to be backwards compatible with View Engine, events on component host nodes
  16646. // must also mark the component view itself dirty (i.e. the view that it owns).
  16647. const startView = tNode.componentOffset > -1 ? getComponentLViewByIndex(tNode.index, lView) : lView;
  16648. markViewDirty(startView);
  16649. let result = executeListenerWithErrorHandling(lView, context, listenerFn, e);
  16650. // A just-invoked listener function might have coalesced listeners so we need to check for
  16651. // their presence and invoke as needed.
  16652. let nextListenerFn = wrapListenerIn_markDirtyAndPreventDefault.__ngNextListenerFn__;
  16653. while (nextListenerFn) {
  16654. // We should prevent default if any of the listeners explicitly return false
  16655. result = executeListenerWithErrorHandling(lView, context, nextListenerFn, e) && result;
  16656. nextListenerFn = nextListenerFn.__ngNextListenerFn__;
  16657. }
  16658. if (wrapWithPreventDefault && result === false) {
  16659. e.preventDefault();
  16660. }
  16661. return result;
  16662. };
  16663. }
  16664. /**
  16665. * Retrieves a context at the level specified and saves it as the global, contextViewData.
  16666. * Will get the next level up if level is not specified.
  16667. *
  16668. * This is used to save contexts of parent views so they can be bound in embedded views, or
  16669. * in conjunction with reference() to bind a ref from a parent view.
  16670. *
  16671. * @param level The relative level of the view from which to grab context compared to contextVewData
  16672. * @returns context
  16673. *
  16674. * @codeGenApi
  16675. */
  16676. function ɵɵnextContext(level = 1) {
  16677. return nextContextImpl(level);
  16678. }
  16679. /**
  16680. * Checks a given node against matching projection slots and returns the
  16681. * determined slot index. Returns "null" if no slot matched the given node.
  16682. *
  16683. * This function takes into account the parsed ngProjectAs selector from the
  16684. * node's attributes. If present, it will check whether the ngProjectAs selector
  16685. * matches any of the projection slot selectors.
  16686. */
  16687. function matchingProjectionSlotIndex(tNode, projectionSlots) {
  16688. let wildcardNgContentIndex = null;
  16689. const ngProjectAsAttrVal = getProjectAsAttrValue(tNode);
  16690. for (let i = 0; i < projectionSlots.length; i++) {
  16691. const slotValue = projectionSlots[i];
  16692. // The last wildcard projection slot should match all nodes which aren't matching
  16693. // any selector. This is necessary to be backwards compatible with view engine.
  16694. if (slotValue === '*') {
  16695. wildcardNgContentIndex = i;
  16696. continue;
  16697. }
  16698. // If we ran into an `ngProjectAs` attribute, we should match its parsed selector
  16699. // to the list of selectors, otherwise we fall back to matching against the node.
  16700. if (ngProjectAsAttrVal === null ?
  16701. isNodeMatchingSelectorList(tNode, slotValue, /* isProjectionMode */ true) :
  16702. isSelectorInSelectorList(ngProjectAsAttrVal, slotValue)) {
  16703. return i; // first matching selector "captures" a given node
  16704. }
  16705. }
  16706. return wildcardNgContentIndex;
  16707. }
  16708. /**
  16709. * Instruction to distribute projectable nodes among <ng-content> occurrences in a given template.
  16710. * It takes all the selectors from the entire component's template and decides where
  16711. * each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is
  16712. * backed by a selector).
  16713. *
  16714. * This function requires CSS selectors to be provided in 2 forms: parsed (by a compiler) and text,
  16715. * un-parsed form.
  16716. *
  16717. * The parsed form is needed for efficient matching of a node against a given CSS selector.
  16718. * The un-parsed, textual form is needed for support of the ngProjectAs attribute.
  16719. *
  16720. * Having a CSS selector in 2 different formats is not ideal, but alternatives have even more
  16721. * drawbacks:
  16722. * - having only a textual form would require runtime parsing of CSS selectors;
  16723. * - we can't have only a parsed as we can't re-construct textual form from it (as entered by a
  16724. * template author).
  16725. *
  16726. * @param projectionSlots? A collection of projection slots. A projection slot can be based
  16727. * on a parsed CSS selectors or set to the wildcard selector ("*") in order to match
  16728. * all nodes which do not match any selector. If not specified, a single wildcard
  16729. * selector projection slot will be defined.
  16730. *
  16731. * @codeGenApi
  16732. */
  16733. function ɵɵprojectionDef(projectionSlots) {
  16734. const componentNode = getLView()[DECLARATION_COMPONENT_VIEW][T_HOST];
  16735. if (!componentNode.projection) {
  16736. // If no explicit projection slots are defined, fall back to a single
  16737. // projection slot with the wildcard selector.
  16738. const numProjectionSlots = projectionSlots ? projectionSlots.length : 1;
  16739. const projectionHeads = componentNode.projection =
  16740. newArray(numProjectionSlots, null);
  16741. const tails = projectionHeads.slice();
  16742. let componentChild = componentNode.child;
  16743. while (componentChild !== null) {
  16744. const slotIndex = projectionSlots ? matchingProjectionSlotIndex(componentChild, projectionSlots) : 0;
  16745. if (slotIndex !== null) {
  16746. if (tails[slotIndex]) {
  16747. tails[slotIndex].projectionNext = componentChild;
  16748. }
  16749. else {
  16750. projectionHeads[slotIndex] = componentChild;
  16751. }
  16752. tails[slotIndex] = componentChild;
  16753. }
  16754. componentChild = componentChild.next;
  16755. }
  16756. }
  16757. }
  16758. /**
  16759. * Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
  16760. * to the projectionDef instruction.
  16761. *
  16762. * @param nodeIndex
  16763. * @param selectorIndex:
  16764. * - 0 when the selector is `*` (or unspecified as this is the default value),
  16765. * - 1 based index of the selector from the {@link projectionDef}
  16766. *
  16767. * @codeGenApi
  16768. */
  16769. function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs) {
  16770. const lView = getLView();
  16771. const tView = getTView();
  16772. const tProjectionNode = getOrCreateTNode(tView, HEADER_OFFSET + nodeIndex, 16 /* TNodeType.Projection */, null, attrs || null);
  16773. // We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
  16774. if (tProjectionNode.projection === null)
  16775. tProjectionNode.projection = selectorIndex;
  16776. // `<ng-content>` has no content
  16777. setCurrentTNodeAsNotParent();
  16778. const hydrationInfo = lView[HYDRATION];
  16779. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1();
  16780. if (isNodeCreationMode &&
  16781. (tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
  16782. // re-distribution of projectable nodes is stored on a component's view level
  16783. applyProjection(tView, lView, tProjectionNode);
  16784. }
  16785. }
  16786. /**
  16787. *
  16788. * Update an interpolated property on an element with a lone bound value
  16789. *
  16790. * Used when the value passed to a property has 1 interpolated value in it, an no additional text
  16791. * surrounds that interpolated value:
  16792. *
  16793. * ```html
  16794. * <div title="{{v0}}"></div>
  16795. * ```
  16796. *
  16797. * Its compiled representation is::
  16798. *
  16799. * ```ts
  16800. * ɵɵpropertyInterpolate('title', v0);
  16801. * ```
  16802. *
  16803. * If the property name also exists as an input property on one of the element's directives,
  16804. * the component property will be set instead of the element property. This check must
  16805. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  16806. *
  16807. * @param propName The name of the property to update
  16808. * @param prefix Static value used for concatenation only.
  16809. * @param v0 Value checked for change.
  16810. * @param suffix Static value used for concatenation only.
  16811. * @param sanitizer An optional sanitizer function
  16812. * @returns itself, so that it may be chained.
  16813. * @codeGenApi
  16814. */
  16815. function ɵɵpropertyInterpolate(propName, v0, sanitizer) {
  16816. ɵɵpropertyInterpolate1(propName, '', v0, '', sanitizer);
  16817. return ɵɵpropertyInterpolate;
  16818. }
  16819. /**
  16820. *
  16821. * Update an interpolated property on an element with single bound value surrounded by text.
  16822. *
  16823. * Used when the value passed to a property has 1 interpolated value in it:
  16824. *
  16825. * ```html
  16826. * <div title="prefix{{v0}}suffix"></div>
  16827. * ```
  16828. *
  16829. * Its compiled representation is::
  16830. *
  16831. * ```ts
  16832. * ɵɵpropertyInterpolate1('title', 'prefix', v0, 'suffix');
  16833. * ```
  16834. *
  16835. * If the property name also exists as an input property on one of the element's directives,
  16836. * the component property will be set instead of the element property. This check must
  16837. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  16838. *
  16839. * @param propName The name of the property to update
  16840. * @param prefix Static value used for concatenation only.
  16841. * @param v0 Value checked for change.
  16842. * @param suffix Static value used for concatenation only.
  16843. * @param sanitizer An optional sanitizer function
  16844. * @returns itself, so that it may be chained.
  16845. * @codeGenApi
  16846. */
  16847. function ɵɵpropertyInterpolate1(propName, prefix, v0, suffix, sanitizer) {
  16848. const lView = getLView();
  16849. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  16850. if (interpolatedValue !== NO_CHANGE) {
  16851. const tView = getTView();
  16852. const tNode = getSelectedTNode();
  16853. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  16854. ngDevMode &&
  16855. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 1, prefix, suffix);
  16856. }
  16857. return ɵɵpropertyInterpolate1;
  16858. }
  16859. /**
  16860. *
  16861. * Update an interpolated property on an element with 2 bound values surrounded by text.
  16862. *
  16863. * Used when the value passed to a property has 2 interpolated values in it:
  16864. *
  16865. * ```html
  16866. * <div title="prefix{{v0}}-{{v1}}suffix"></div>
  16867. * ```
  16868. *
  16869. * Its compiled representation is::
  16870. *
  16871. * ```ts
  16872. * ɵɵpropertyInterpolate2('title', 'prefix', v0, '-', v1, 'suffix');
  16873. * ```
  16874. *
  16875. * If the property name also exists as an input property on one of the element's directives,
  16876. * the component property will be set instead of the element property. This check must
  16877. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  16878. *
  16879. * @param propName The name of the property to update
  16880. * @param prefix Static value used for concatenation only.
  16881. * @param v0 Value checked for change.
  16882. * @param i0 Static value used for concatenation only.
  16883. * @param v1 Value checked for change.
  16884. * @param suffix Static value used for concatenation only.
  16885. * @param sanitizer An optional sanitizer function
  16886. * @returns itself, so that it may be chained.
  16887. * @codeGenApi
  16888. */
  16889. function ɵɵpropertyInterpolate2(propName, prefix, v0, i0, v1, suffix, sanitizer) {
  16890. const lView = getLView();
  16891. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  16892. if (interpolatedValue !== NO_CHANGE) {
  16893. const tView = getTView();
  16894. const tNode = getSelectedTNode();
  16895. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  16896. ngDevMode &&
  16897. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 2, prefix, i0, suffix);
  16898. }
  16899. return ɵɵpropertyInterpolate2;
  16900. }
  16901. /**
  16902. *
  16903. * Update an interpolated property on an element with 3 bound values surrounded by text.
  16904. *
  16905. * Used when the value passed to a property has 3 interpolated values in it:
  16906. *
  16907. * ```html
  16908. * <div title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  16909. * ```
  16910. *
  16911. * Its compiled representation is::
  16912. *
  16913. * ```ts
  16914. * ɵɵpropertyInterpolate3(
  16915. * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix');
  16916. * ```
  16917. *
  16918. * If the property name also exists as an input property on one of the element's directives,
  16919. * the component property will be set instead of the element property. This check must
  16920. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  16921. *
  16922. * @param propName The name of the property to update
  16923. * @param prefix Static value used for concatenation only.
  16924. * @param v0 Value checked for change.
  16925. * @param i0 Static value used for concatenation only.
  16926. * @param v1 Value checked for change.
  16927. * @param i1 Static value used for concatenation only.
  16928. * @param v2 Value checked for change.
  16929. * @param suffix Static value used for concatenation only.
  16930. * @param sanitizer An optional sanitizer function
  16931. * @returns itself, so that it may be chained.
  16932. * @codeGenApi
  16933. */
  16934. function ɵɵpropertyInterpolate3(propName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer) {
  16935. const lView = getLView();
  16936. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  16937. if (interpolatedValue !== NO_CHANGE) {
  16938. const tView = getTView();
  16939. const tNode = getSelectedTNode();
  16940. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  16941. ngDevMode &&
  16942. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 3, prefix, i0, i1, suffix);
  16943. }
  16944. return ɵɵpropertyInterpolate3;
  16945. }
  16946. /**
  16947. *
  16948. * Update an interpolated property on an element with 4 bound values surrounded by text.
  16949. *
  16950. * Used when the value passed to a property has 4 interpolated values in it:
  16951. *
  16952. * ```html
  16953. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  16954. * ```
  16955. *
  16956. * Its compiled representation is::
  16957. *
  16958. * ```ts
  16959. * ɵɵpropertyInterpolate4(
  16960. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  16961. * ```
  16962. *
  16963. * If the property name also exists as an input property on one of the element's directives,
  16964. * the component property will be set instead of the element property. This check must
  16965. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  16966. *
  16967. * @param propName The name of the property to update
  16968. * @param prefix Static value used for concatenation only.
  16969. * @param v0 Value checked for change.
  16970. * @param i0 Static value used for concatenation only.
  16971. * @param v1 Value checked for change.
  16972. * @param i1 Static value used for concatenation only.
  16973. * @param v2 Value checked for change.
  16974. * @param i2 Static value used for concatenation only.
  16975. * @param v3 Value checked for change.
  16976. * @param suffix Static value used for concatenation only.
  16977. * @param sanitizer An optional sanitizer function
  16978. * @returns itself, so that it may be chained.
  16979. * @codeGenApi
  16980. */
  16981. function ɵɵpropertyInterpolate4(propName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer) {
  16982. const lView = getLView();
  16983. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  16984. if (interpolatedValue !== NO_CHANGE) {
  16985. const tView = getTView();
  16986. const tNode = getSelectedTNode();
  16987. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  16988. ngDevMode &&
  16989. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix);
  16990. }
  16991. return ɵɵpropertyInterpolate4;
  16992. }
  16993. /**
  16994. *
  16995. * Update an interpolated property on an element with 5 bound values surrounded by text.
  16996. *
  16997. * Used when the value passed to a property has 5 interpolated values in it:
  16998. *
  16999. * ```html
  17000. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  17001. * ```
  17002. *
  17003. * Its compiled representation is::
  17004. *
  17005. * ```ts
  17006. * ɵɵpropertyInterpolate5(
  17007. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  17008. * ```
  17009. *
  17010. * If the property name also exists as an input property on one of the element's directives,
  17011. * the component property will be set instead of the element property. This check must
  17012. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17013. *
  17014. * @param propName The name of the property to update
  17015. * @param prefix Static value used for concatenation only.
  17016. * @param v0 Value checked for change.
  17017. * @param i0 Static value used for concatenation only.
  17018. * @param v1 Value checked for change.
  17019. * @param i1 Static value used for concatenation only.
  17020. * @param v2 Value checked for change.
  17021. * @param i2 Static value used for concatenation only.
  17022. * @param v3 Value checked for change.
  17023. * @param i3 Static value used for concatenation only.
  17024. * @param v4 Value checked for change.
  17025. * @param suffix Static value used for concatenation only.
  17026. * @param sanitizer An optional sanitizer function
  17027. * @returns itself, so that it may be chained.
  17028. * @codeGenApi
  17029. */
  17030. function ɵɵpropertyInterpolate5(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer) {
  17031. const lView = getLView();
  17032. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  17033. if (interpolatedValue !== NO_CHANGE) {
  17034. const tView = getTView();
  17035. const tNode = getSelectedTNode();
  17036. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17037. ngDevMode &&
  17038. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix);
  17039. }
  17040. return ɵɵpropertyInterpolate5;
  17041. }
  17042. /**
  17043. *
  17044. * Update an interpolated property on an element with 6 bound values surrounded by text.
  17045. *
  17046. * Used when the value passed to a property has 6 interpolated values in it:
  17047. *
  17048. * ```html
  17049. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  17050. * ```
  17051. *
  17052. * Its compiled representation is::
  17053. *
  17054. * ```ts
  17055. * ɵɵpropertyInterpolate6(
  17056. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  17057. * ```
  17058. *
  17059. * If the property name also exists as an input property on one of the element's directives,
  17060. * the component property will be set instead of the element property. This check must
  17061. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17062. *
  17063. * @param propName The name of the property to update
  17064. * @param prefix Static value used for concatenation only.
  17065. * @param v0 Value checked for change.
  17066. * @param i0 Static value used for concatenation only.
  17067. * @param v1 Value checked for change.
  17068. * @param i1 Static value used for concatenation only.
  17069. * @param v2 Value checked for change.
  17070. * @param i2 Static value used for concatenation only.
  17071. * @param v3 Value checked for change.
  17072. * @param i3 Static value used for concatenation only.
  17073. * @param v4 Value checked for change.
  17074. * @param i4 Static value used for concatenation only.
  17075. * @param v5 Value checked for change.
  17076. * @param suffix Static value used for concatenation only.
  17077. * @param sanitizer An optional sanitizer function
  17078. * @returns itself, so that it may be chained.
  17079. * @codeGenApi
  17080. */
  17081. function ɵɵpropertyInterpolate6(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer) {
  17082. const lView = getLView();
  17083. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  17084. if (interpolatedValue !== NO_CHANGE) {
  17085. const tView = getTView();
  17086. const tNode = getSelectedTNode();
  17087. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17088. ngDevMode &&
  17089. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix);
  17090. }
  17091. return ɵɵpropertyInterpolate6;
  17092. }
  17093. /**
  17094. *
  17095. * Update an interpolated property on an element with 7 bound values surrounded by text.
  17096. *
  17097. * Used when the value passed to a property has 7 interpolated values in it:
  17098. *
  17099. * ```html
  17100. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  17101. * ```
  17102. *
  17103. * Its compiled representation is::
  17104. *
  17105. * ```ts
  17106. * ɵɵpropertyInterpolate7(
  17107. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  17108. * ```
  17109. *
  17110. * If the property name also exists as an input property on one of the element's directives,
  17111. * the component property will be set instead of the element property. This check must
  17112. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17113. *
  17114. * @param propName The name of the property to update
  17115. * @param prefix Static value used for concatenation only.
  17116. * @param v0 Value checked for change.
  17117. * @param i0 Static value used for concatenation only.
  17118. * @param v1 Value checked for change.
  17119. * @param i1 Static value used for concatenation only.
  17120. * @param v2 Value checked for change.
  17121. * @param i2 Static value used for concatenation only.
  17122. * @param v3 Value checked for change.
  17123. * @param i3 Static value used for concatenation only.
  17124. * @param v4 Value checked for change.
  17125. * @param i4 Static value used for concatenation only.
  17126. * @param v5 Value checked for change.
  17127. * @param i5 Static value used for concatenation only.
  17128. * @param v6 Value checked for change.
  17129. * @param suffix Static value used for concatenation only.
  17130. * @param sanitizer An optional sanitizer function
  17131. * @returns itself, so that it may be chained.
  17132. * @codeGenApi
  17133. */
  17134. function ɵɵpropertyInterpolate7(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer) {
  17135. const lView = getLView();
  17136. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  17137. if (interpolatedValue !== NO_CHANGE) {
  17138. const tView = getTView();
  17139. const tNode = getSelectedTNode();
  17140. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17141. ngDevMode &&
  17142. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix);
  17143. }
  17144. return ɵɵpropertyInterpolate7;
  17145. }
  17146. /**
  17147. *
  17148. * Update an interpolated property on an element with 8 bound values surrounded by text.
  17149. *
  17150. * Used when the value passed to a property has 8 interpolated values in it:
  17151. *
  17152. * ```html
  17153. * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  17154. * ```
  17155. *
  17156. * Its compiled representation is::
  17157. *
  17158. * ```ts
  17159. * ɵɵpropertyInterpolate8(
  17160. * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  17161. * ```
  17162. *
  17163. * If the property name also exists as an input property on one of the element's directives,
  17164. * the component property will be set instead of the element property. This check must
  17165. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17166. *
  17167. * @param propName The name of the property to update
  17168. * @param prefix Static value used for concatenation only.
  17169. * @param v0 Value checked for change.
  17170. * @param i0 Static value used for concatenation only.
  17171. * @param v1 Value checked for change.
  17172. * @param i1 Static value used for concatenation only.
  17173. * @param v2 Value checked for change.
  17174. * @param i2 Static value used for concatenation only.
  17175. * @param v3 Value checked for change.
  17176. * @param i3 Static value used for concatenation only.
  17177. * @param v4 Value checked for change.
  17178. * @param i4 Static value used for concatenation only.
  17179. * @param v5 Value checked for change.
  17180. * @param i5 Static value used for concatenation only.
  17181. * @param v6 Value checked for change.
  17182. * @param i6 Static value used for concatenation only.
  17183. * @param v7 Value checked for change.
  17184. * @param suffix Static value used for concatenation only.
  17185. * @param sanitizer An optional sanitizer function
  17186. * @returns itself, so that it may be chained.
  17187. * @codeGenApi
  17188. */
  17189. function ɵɵpropertyInterpolate8(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer) {
  17190. const lView = getLView();
  17191. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  17192. if (interpolatedValue !== NO_CHANGE) {
  17193. const tView = getTView();
  17194. const tNode = getSelectedTNode();
  17195. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17196. ngDevMode &&
  17197. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix);
  17198. }
  17199. return ɵɵpropertyInterpolate8;
  17200. }
  17201. /**
  17202. * Update an interpolated property on an element with 9 or more bound values surrounded by text.
  17203. *
  17204. * Used when the number of interpolated values exceeds 8.
  17205. *
  17206. * ```html
  17207. * <div
  17208. * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
  17209. * ```
  17210. *
  17211. * Its compiled representation is::
  17212. *
  17213. * ```ts
  17214. * ɵɵpropertyInterpolateV(
  17215. * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  17216. * 'suffix']);
  17217. * ```
  17218. *
  17219. * If the property name also exists as an input property on one of the element's directives,
  17220. * the component property will be set instead of the element property. This check must
  17221. * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
  17222. *
  17223. * @param propName The name of the property to update.
  17224. * @param values The collection of values and the strings in between those values, beginning with a
  17225. * string prefix and ending with a string suffix.
  17226. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  17227. * @param sanitizer An optional sanitizer function
  17228. * @returns itself, so that it may be chained.
  17229. * @codeGenApi
  17230. */
  17231. function ɵɵpropertyInterpolateV(propName, values, sanitizer) {
  17232. const lView = getLView();
  17233. const interpolatedValue = interpolationV(lView, values);
  17234. if (interpolatedValue !== NO_CHANGE) {
  17235. const tView = getTView();
  17236. const tNode = getSelectedTNode();
  17237. elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
  17238. if (ngDevMode) {
  17239. const interpolationInBetween = [values[0]]; // prefix
  17240. for (let i = 2; i < values.length; i += 2) {
  17241. interpolationInBetween.push(values[i]);
  17242. }
  17243. storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
  17244. }
  17245. }
  17246. return ɵɵpropertyInterpolateV;
  17247. }
  17248. function toTStylingRange(prev, next) {
  17249. ngDevMode && assertNumberInRange(prev, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17250. ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17251. return (prev << 17 /* StylingRange.PREV_SHIFT */ | next << 2 /* StylingRange.NEXT_SHIFT */);
  17252. }
  17253. function getTStylingRangePrev(tStylingRange) {
  17254. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17255. return (tStylingRange >> 17 /* StylingRange.PREV_SHIFT */) & 32767 /* StylingRange.UNSIGNED_MASK */;
  17256. }
  17257. function getTStylingRangePrevDuplicate(tStylingRange) {
  17258. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17259. return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) == 2 /* StylingRange.PREV_DUPLICATE */;
  17260. }
  17261. function setTStylingRangePrev(tStylingRange, previous) {
  17262. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17263. ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17264. return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) | (previous << 17 /* StylingRange.PREV_SHIFT */));
  17265. }
  17266. function setTStylingRangePrevDuplicate(tStylingRange) {
  17267. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17268. return (tStylingRange | 2 /* StylingRange.PREV_DUPLICATE */);
  17269. }
  17270. function getTStylingRangeNext(tStylingRange) {
  17271. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17272. return (tStylingRange & 131068 /* StylingRange.NEXT_MASK */) >> 2 /* StylingRange.NEXT_SHIFT */;
  17273. }
  17274. function setTStylingRangeNext(tStylingRange, next) {
  17275. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17276. ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
  17277. return ((tStylingRange & ~131068 /* StylingRange.NEXT_MASK */) | //
  17278. next << 2 /* StylingRange.NEXT_SHIFT */);
  17279. }
  17280. function getTStylingRangeNextDuplicate(tStylingRange) {
  17281. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17282. return ((tStylingRange) & 1 /* StylingRange.NEXT_DUPLICATE */) === 1 /* StylingRange.NEXT_DUPLICATE */;
  17283. }
  17284. function setTStylingRangeNextDuplicate(tStylingRange) {
  17285. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17286. return (tStylingRange | 1 /* StylingRange.NEXT_DUPLICATE */);
  17287. }
  17288. function getTStylingRangeTail(tStylingRange) {
  17289. ngDevMode && assertNumber(tStylingRange, 'expected number');
  17290. const next = getTStylingRangeNext(tStylingRange);
  17291. return next === 0 ? getTStylingRangePrev(tStylingRange) : next;
  17292. }
  17293. /**
  17294. * NOTE: The word `styling` is used interchangeably as style or class styling.
  17295. *
  17296. * This file contains code to link styling instructions together so that they can be replayed in
  17297. * priority order. The file exists because Ivy styling instruction execution order does not match
  17298. * that of the priority order. The purpose of this code is to create a linked list so that the
  17299. * instructions can be traversed in priority order when computing the styles.
  17300. *
  17301. * Assume we are dealing with the following code:
  17302. * ```
  17303. * @Component({
  17304. * template: `
  17305. * <my-cmp [style]=" {color: '#001'} "
  17306. * [style.color]=" #002 "
  17307. * dir-style-color-1
  17308. * dir-style-color-2> `
  17309. * })
  17310. * class ExampleComponent {
  17311. * static ngComp = ... {
  17312. * ...
  17313. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17314. * ɵɵstyleMap({color: '#001'});
  17315. * ɵɵstyleProp('color', '#002');
  17316. * ...
  17317. * }
  17318. * }
  17319. *
  17320. * @Directive({
  17321. * selector: `[dir-style-color-1]',
  17322. * })
  17323. * class Style1Directive {
  17324. * @HostBinding('style') style = {color: '#005'};
  17325. * @HostBinding('style.color') color = '#006';
  17326. *
  17327. * static ngDir = ... {
  17328. * ...
  17329. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17330. * ɵɵstyleMap({color: '#005'});
  17331. * ɵɵstyleProp('color', '#006');
  17332. * ...
  17333. * }
  17334. * }
  17335. *
  17336. * @Directive({
  17337. * selector: `[dir-style-color-2]',
  17338. * })
  17339. * class Style2Directive {
  17340. * @HostBinding('style') style = {color: '#007'};
  17341. * @HostBinding('style.color') color = '#008';
  17342. *
  17343. * static ngDir = ... {
  17344. * ...
  17345. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17346. * ɵɵstyleMap({color: '#007'});
  17347. * ɵɵstyleProp('color', '#008');
  17348. * ...
  17349. * }
  17350. * }
  17351. *
  17352. * @Directive({
  17353. * selector: `my-cmp',
  17354. * })
  17355. * class MyComponent {
  17356. * @HostBinding('style') style = {color: '#003'};
  17357. * @HostBinding('style.color') color = '#004';
  17358. *
  17359. * static ngComp = ... {
  17360. * ...
  17361. * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
  17362. * ɵɵstyleMap({color: '#003'});
  17363. * ɵɵstyleProp('color', '#004');
  17364. * ...
  17365. * }
  17366. * }
  17367. * ```
  17368. *
  17369. * The Order of instruction execution is:
  17370. *
  17371. * NOTE: the comment binding location is for illustrative purposes only.
  17372. *
  17373. * ```
  17374. * // Template: (ExampleComponent)
  17375. * ɵɵstyleMap({color: '#001'}); // Binding index: 10
  17376. * ɵɵstyleProp('color', '#002'); // Binding index: 12
  17377. * // MyComponent
  17378. * ɵɵstyleMap({color: '#003'}); // Binding index: 20
  17379. * ɵɵstyleProp('color', '#004'); // Binding index: 22
  17380. * // Style1Directive
  17381. * ɵɵstyleMap({color: '#005'}); // Binding index: 24
  17382. * ɵɵstyleProp('color', '#006'); // Binding index: 26
  17383. * // Style2Directive
  17384. * ɵɵstyleMap({color: '#007'}); // Binding index: 28
  17385. * ɵɵstyleProp('color', '#008'); // Binding index: 30
  17386. * ```
  17387. *
  17388. * The correct priority order of concatenation is:
  17389. *
  17390. * ```
  17391. * // MyComponent
  17392. * ɵɵstyleMap({color: '#003'}); // Binding index: 20
  17393. * ɵɵstyleProp('color', '#004'); // Binding index: 22
  17394. * // Style1Directive
  17395. * ɵɵstyleMap({color: '#005'}); // Binding index: 24
  17396. * ɵɵstyleProp('color', '#006'); // Binding index: 26
  17397. * // Style2Directive
  17398. * ɵɵstyleMap({color: '#007'}); // Binding index: 28
  17399. * ɵɵstyleProp('color', '#008'); // Binding index: 30
  17400. * // Template: (ExampleComponent)
  17401. * ɵɵstyleMap({color: '#001'}); // Binding index: 10
  17402. * ɵɵstyleProp('color', '#002'); // Binding index: 12
  17403. * ```
  17404. *
  17405. * What color should be rendered?
  17406. *
  17407. * Once the items are correctly sorted in the list, the answer is simply the last item in the
  17408. * concatenation list which is `#002`.
  17409. *
  17410. * To do so we keep a linked list of all of the bindings which pertain to this element.
  17411. * Notice that the bindings are inserted in the order of execution, but the `TView.data` allows
  17412. * us to traverse them in the order of priority.
  17413. *
  17414. * |Idx|`TView.data`|`LView` | Notes
  17415. * |---|------------|-----------------|--------------
  17416. * |...| | |
  17417. * |10 |`null` |`{color: '#001'}`| `ɵɵstyleMap('color', {color: '#001'})`
  17418. * |11 |`30 | 12` | ... |
  17419. * |12 |`color` |`'#002'` | `ɵɵstyleProp('color', '#002')`
  17420. * |13 |`10 | 0` | ... |
  17421. * |...| | |
  17422. * |20 |`null` |`{color: '#003'}`| `ɵɵstyleMap('color', {color: '#003'})`
  17423. * |21 |`0 | 22` | ... |
  17424. * |22 |`color` |`'#004'` | `ɵɵstyleProp('color', '#004')`
  17425. * |23 |`20 | 24` | ... |
  17426. * |24 |`null` |`{color: '#005'}`| `ɵɵstyleMap('color', {color: '#005'})`
  17427. * |25 |`22 | 26` | ... |
  17428. * |26 |`color` |`'#006'` | `ɵɵstyleProp('color', '#006')`
  17429. * |27 |`24 | 28` | ... |
  17430. * |28 |`null` |`{color: '#007'}`| `ɵɵstyleMap('color', {color: '#007'})`
  17431. * |29 |`26 | 30` | ... |
  17432. * |30 |`color` |`'#008'` | `ɵɵstyleProp('color', '#008')`
  17433. * |31 |`28 | 10` | ... |
  17434. *
  17435. * The above data structure allows us to re-concatenate the styling no matter which data binding
  17436. * changes.
  17437. *
  17438. * NOTE: in addition to keeping track of next/previous index the `TView.data` also stores prev/next
  17439. * duplicate bit. The duplicate bit if true says there either is a binding with the same name or
  17440. * there is a map (which may contain the name). This information is useful in knowing if other
  17441. * styles with higher priority need to be searched for overwrites.
  17442. *
  17443. * NOTE: See `should support example in 'tnode_linked_list.ts' documentation` in
  17444. * `tnode_linked_list_spec.ts` for working example.
  17445. */
  17446. let __unused_const_as_closure_does_not_like_standalone_comment_blocks__;
  17447. /**
  17448. * Insert new `tStyleValue` at `TData` and link existing style bindings such that we maintain linked
  17449. * list of styles and compute the duplicate flag.
  17450. *
  17451. * Note: this function is executed during `firstUpdatePass` only to populate the `TView.data`.
  17452. *
  17453. * The function works by keeping track of `tStylingRange` which contains two pointers pointing to
  17454. * the head/tail of the template portion of the styles.
  17455. * - if `isHost === false` (we are template) then insertion is at tail of `TStylingRange`
  17456. * - if `isHost === true` (we are host binding) then insertion is at head of `TStylingRange`
  17457. *
  17458. * @param tData The `TData` to insert into.
  17459. * @param tNode `TNode` associated with the styling element.
  17460. * @param tStylingKey See `TStylingKey`.
  17461. * @param index location of where `tStyleValue` should be stored (and linked into list.)
  17462. * @param isHostBinding `true` if the insertion is for a `hostBinding`. (insertion is in front of
  17463. * template.)
  17464. * @param isClassBinding True if the associated `tStylingKey` as a `class` styling.
  17465. * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.)
  17466. */
  17467. function insertTStylingBinding(tData, tNode, tStylingKeyWithStatic, index, isHostBinding, isClassBinding) {
  17468. ngDevMode && assertFirstUpdatePass(getTView());
  17469. let tBindings = isClassBinding ? tNode.classBindings : tNode.styleBindings;
  17470. let tmplHead = getTStylingRangePrev(tBindings);
  17471. let tmplTail = getTStylingRangeNext(tBindings);
  17472. tData[index] = tStylingKeyWithStatic;
  17473. let isKeyDuplicateOfStatic = false;
  17474. let tStylingKey;
  17475. if (Array.isArray(tStylingKeyWithStatic)) {
  17476. // We are case when the `TStylingKey` contains static fields as well.
  17477. const staticKeyValueArray = tStylingKeyWithStatic;
  17478. tStylingKey = staticKeyValueArray[1]; // unwrap.
  17479. // We need to check if our key is present in the static so that we can mark it as duplicate.
  17480. if (tStylingKey === null ||
  17481. keyValueArrayIndexOf(staticKeyValueArray, tStylingKey) > 0) {
  17482. // tStylingKey is present in the statics, need to mark it as duplicate.
  17483. isKeyDuplicateOfStatic = true;
  17484. }
  17485. }
  17486. else {
  17487. tStylingKey = tStylingKeyWithStatic;
  17488. }
  17489. if (isHostBinding) {
  17490. // We are inserting host bindings
  17491. // If we don't have template bindings then `tail` is 0.
  17492. const hasTemplateBindings = tmplTail !== 0;
  17493. // This is important to know because that means that the `head` can't point to the first
  17494. // template bindings (there are none.) Instead the head points to the tail of the template.
  17495. if (hasTemplateBindings) {
  17496. // template head's "prev" will point to last host binding or to 0 if no host bindings yet
  17497. const previousNode = getTStylingRangePrev(tData[tmplHead + 1]);
  17498. tData[index + 1] = toTStylingRange(previousNode, tmplHead);
  17499. // if a host binding has already been registered, we need to update the next of that host
  17500. // binding to point to this one
  17501. if (previousNode !== 0) {
  17502. // We need to update the template-tail value to point to us.
  17503. tData[previousNode + 1] =
  17504. setTStylingRangeNext(tData[previousNode + 1], index);
  17505. }
  17506. // The "previous" of the template binding head should point to this host binding
  17507. tData[tmplHead + 1] = setTStylingRangePrev(tData[tmplHead + 1], index);
  17508. }
  17509. else {
  17510. tData[index + 1] = toTStylingRange(tmplHead, 0);
  17511. // if a host binding has already been registered, we need to update the next of that host
  17512. // binding to point to this one
  17513. if (tmplHead !== 0) {
  17514. // We need to update the template-tail value to point to us.
  17515. tData[tmplHead + 1] = setTStylingRangeNext(tData[tmplHead + 1], index);
  17516. }
  17517. // if we don't have template, the head points to template-tail, and needs to be advanced.
  17518. tmplHead = index;
  17519. }
  17520. }
  17521. else {
  17522. // We are inserting in template section.
  17523. // We need to set this binding's "previous" to the current template tail
  17524. tData[index + 1] = toTStylingRange(tmplTail, 0);
  17525. ngDevMode &&
  17526. assertEqual(tmplHead !== 0 && tmplTail === 0, false, 'Adding template bindings after hostBindings is not allowed.');
  17527. if (tmplHead === 0) {
  17528. tmplHead = index;
  17529. }
  17530. else {
  17531. // We need to update the previous value "next" to point to this binding
  17532. tData[tmplTail + 1] = setTStylingRangeNext(tData[tmplTail + 1], index);
  17533. }
  17534. tmplTail = index;
  17535. }
  17536. // Now we need to update / compute the duplicates.
  17537. // Starting with our location search towards head (least priority)
  17538. if (isKeyDuplicateOfStatic) {
  17539. tData[index + 1] = setTStylingRangePrevDuplicate(tData[index + 1]);
  17540. }
  17541. markDuplicates(tData, tStylingKey, index, true, isClassBinding);
  17542. markDuplicates(tData, tStylingKey, index, false, isClassBinding);
  17543. markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding);
  17544. tBindings = toTStylingRange(tmplHead, tmplTail);
  17545. if (isClassBinding) {
  17546. tNode.classBindings = tBindings;
  17547. }
  17548. else {
  17549. tNode.styleBindings = tBindings;
  17550. }
  17551. }
  17552. /**
  17553. * Look into the residual styling to see if the current `tStylingKey` is duplicate of residual.
  17554. *
  17555. * @param tNode `TNode` where the residual is stored.
  17556. * @param tStylingKey `TStylingKey` to store.
  17557. * @param tData `TData` associated with the current `LView`.
  17558. * @param index location of where `tStyleValue` should be stored (and linked into list.)
  17559. * @param isClassBinding True if the associated `tStylingKey` as a `class` styling.
  17560. * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.)
  17561. */
  17562. function markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding) {
  17563. const residual = isClassBinding ? tNode.residualClasses : tNode.residualStyles;
  17564. if (residual != null /* or undefined */ && typeof tStylingKey == 'string' &&
  17565. keyValueArrayIndexOf(residual, tStylingKey) >= 0) {
  17566. // We have duplicate in the residual so mark ourselves as duplicate.
  17567. tData[index + 1] = setTStylingRangeNextDuplicate(tData[index + 1]);
  17568. }
  17569. }
  17570. /**
  17571. * Marks `TStyleValue`s as duplicates if another style binding in the list has the same
  17572. * `TStyleValue`.
  17573. *
  17574. * NOTE: this function is intended to be called twice once with `isPrevDir` set to `true` and once
  17575. * with it set to `false` to search both the previous as well as next items in the list.
  17576. *
  17577. * No duplicate case
  17578. * ```
  17579. * [style.color]
  17580. * [style.width.px] <<- index
  17581. * [style.height.px]
  17582. * ```
  17583. *
  17584. * In the above case adding `[style.width.px]` to the existing `[style.color]` produces no
  17585. * duplicates because `width` is not found in any other part of the linked list.
  17586. *
  17587. * Duplicate case
  17588. * ```
  17589. * [style.color]
  17590. * [style.width.em]
  17591. * [style.width.px] <<- index
  17592. * ```
  17593. * In the above case adding `[style.width.px]` will produce a duplicate with `[style.width.em]`
  17594. * because `width` is found in the chain.
  17595. *
  17596. * Map case 1
  17597. * ```
  17598. * [style.width.px]
  17599. * [style.color]
  17600. * [style] <<- index
  17601. * ```
  17602. * In the above case adding `[style]` will produce a duplicate with any other bindings because
  17603. * `[style]` is a Map and as such is fully dynamic and could produce `color` or `width`.
  17604. *
  17605. * Map case 2
  17606. * ```
  17607. * [style]
  17608. * [style.width.px]
  17609. * [style.color] <<- index
  17610. * ```
  17611. * In the above case adding `[style.color]` will produce a duplicate because there is already a
  17612. * `[style]` binding which is a Map and as such is fully dynamic and could produce `color` or
  17613. * `width`.
  17614. *
  17615. * NOTE: Once `[style]` (Map) is added into the system all things are mapped as duplicates.
  17616. * NOTE: We use `style` as example, but same logic is applied to `class`es as well.
  17617. *
  17618. * @param tData `TData` where the linked list is stored.
  17619. * @param tStylingKey `TStylingKeyPrimitive` which contains the value to compare to other keys in
  17620. * the linked list.
  17621. * @param index Starting location in the linked list to search from
  17622. * @param isPrevDir Direction.
  17623. * - `true` for previous (lower priority);
  17624. * - `false` for next (higher priority).
  17625. */
  17626. function markDuplicates(tData, tStylingKey, index, isPrevDir, isClassBinding) {
  17627. const tStylingAtIndex = tData[index + 1];
  17628. const isMap = tStylingKey === null;
  17629. let cursor = isPrevDir ? getTStylingRangePrev(tStylingAtIndex) : getTStylingRangeNext(tStylingAtIndex);
  17630. let foundDuplicate = false;
  17631. // We keep iterating as long as we have a cursor
  17632. // AND either:
  17633. // - we found what we are looking for, OR
  17634. // - we are a map in which case we have to continue searching even after we find what we were
  17635. // looking for since we are a wild card and everything needs to be flipped to duplicate.
  17636. while (cursor !== 0 && (foundDuplicate === false || isMap)) {
  17637. ngDevMode && assertIndexInRange(tData, cursor);
  17638. const tStylingValueAtCursor = tData[cursor];
  17639. const tStyleRangeAtCursor = tData[cursor + 1];
  17640. if (isStylingMatch(tStylingValueAtCursor, tStylingKey)) {
  17641. foundDuplicate = true;
  17642. tData[cursor + 1] = isPrevDir ? setTStylingRangeNextDuplicate(tStyleRangeAtCursor) :
  17643. setTStylingRangePrevDuplicate(tStyleRangeAtCursor);
  17644. }
  17645. cursor = isPrevDir ? getTStylingRangePrev(tStyleRangeAtCursor) :
  17646. getTStylingRangeNext(tStyleRangeAtCursor);
  17647. }
  17648. if (foundDuplicate) {
  17649. // if we found a duplicate, than mark ourselves.
  17650. tData[index + 1] = isPrevDir ? setTStylingRangePrevDuplicate(tStylingAtIndex) :
  17651. setTStylingRangeNextDuplicate(tStylingAtIndex);
  17652. }
  17653. }
  17654. /**
  17655. * Determines if two `TStylingKey`s are a match.
  17656. *
  17657. * When computing whether a binding contains a duplicate, we need to compare if the instruction
  17658. * `TStylingKey` has a match.
  17659. *
  17660. * Here are examples of `TStylingKey`s which match given `tStylingKeyCursor` is:
  17661. * - `color`
  17662. * - `color` // Match another color
  17663. * - `null` // That means that `tStylingKey` is a `classMap`/`styleMap` instruction
  17664. * - `['', 'color', 'other', true]` // wrapped `color` so match
  17665. * - `['', null, 'other', true]` // wrapped `null` so match
  17666. * - `['', 'width', 'color', 'value']` // wrapped static value contains a match on `'color'`
  17667. * - `null` // `tStylingKeyCursor` always match as it is `classMap`/`styleMap` instruction
  17668. *
  17669. * @param tStylingKeyCursor
  17670. * @param tStylingKey
  17671. */
  17672. function isStylingMatch(tStylingKeyCursor, tStylingKey) {
  17673. ngDevMode &&
  17674. assertNotEqual(Array.isArray(tStylingKey), true, 'Expected that \'tStylingKey\' has been unwrapped');
  17675. if (tStylingKeyCursor === null || // If the cursor is `null` it means that we have map at that
  17676. // location so we must assume that we have a match.
  17677. tStylingKey == null || // If `tStylingKey` is `null` then it is a map therefor assume that it
  17678. // contains a match.
  17679. (Array.isArray(tStylingKeyCursor) ? tStylingKeyCursor[1] : tStylingKeyCursor) ===
  17680. tStylingKey // If the keys match explicitly than we are a match.
  17681. ) {
  17682. return true;
  17683. }
  17684. else if (Array.isArray(tStylingKeyCursor) && typeof tStylingKey === 'string') {
  17685. // if we did not find a match, but `tStylingKeyCursor` is `KeyValueArray` that means cursor has
  17686. // statics and we need to check those as well.
  17687. return keyValueArrayIndexOf(tStylingKeyCursor, tStylingKey) >=
  17688. 0; // see if we are matching the key
  17689. }
  17690. return false;
  17691. }
  17692. // Global state of the parser. (This makes parser non-reentrant, but that is not an issue)
  17693. const parserState = {
  17694. textEnd: 0,
  17695. key: 0,
  17696. keyEnd: 0,
  17697. value: 0,
  17698. valueEnd: 0,
  17699. };
  17700. /**
  17701. * Retrieves the last parsed `key` of style.
  17702. * @param text the text to substring the key from.
  17703. */
  17704. function getLastParsedKey(text) {
  17705. return text.substring(parserState.key, parserState.keyEnd);
  17706. }
  17707. /**
  17708. * Retrieves the last parsed `value` of style.
  17709. * @param text the text to substring the key from.
  17710. */
  17711. function getLastParsedValue(text) {
  17712. return text.substring(parserState.value, parserState.valueEnd);
  17713. }
  17714. /**
  17715. * Initializes `className` string for parsing and parses the first token.
  17716. *
  17717. * This function is intended to be used in this format:
  17718. * ```
  17719. * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
  17720. * const key = getLastParsedKey();
  17721. * ...
  17722. * }
  17723. * ```
  17724. * @param text `className` to parse
  17725. * @returns index where the next invocation of `parseClassNameNext` should resume.
  17726. */
  17727. function parseClassName(text) {
  17728. resetParserState(text);
  17729. return parseClassNameNext(text, consumeWhitespace(text, 0, parserState.textEnd));
  17730. }
  17731. /**
  17732. * Parses next `className` token.
  17733. *
  17734. * This function is intended to be used in this format:
  17735. * ```
  17736. * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
  17737. * const key = getLastParsedKey();
  17738. * ...
  17739. * }
  17740. * ```
  17741. *
  17742. * @param text `className` to parse
  17743. * @param index where the parsing should resume.
  17744. * @returns index where the next invocation of `parseClassNameNext` should resume.
  17745. */
  17746. function parseClassNameNext(text, index) {
  17747. const end = parserState.textEnd;
  17748. if (end === index) {
  17749. return -1;
  17750. }
  17751. index = parserState.keyEnd = consumeClassToken(text, parserState.key = index, end);
  17752. return consumeWhitespace(text, index, end);
  17753. }
  17754. /**
  17755. * Initializes `cssText` string for parsing and parses the first key/values.
  17756. *
  17757. * This function is intended to be used in this format:
  17758. * ```
  17759. * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) {
  17760. * const key = getLastParsedKey();
  17761. * const value = getLastParsedValue();
  17762. * ...
  17763. * }
  17764. * ```
  17765. * @param text `cssText` to parse
  17766. * @returns index where the next invocation of `parseStyleNext` should resume.
  17767. */
  17768. function parseStyle(text) {
  17769. resetParserState(text);
  17770. return parseStyleNext(text, consumeWhitespace(text, 0, parserState.textEnd));
  17771. }
  17772. /**
  17773. * Parses the next `cssText` key/values.
  17774. *
  17775. * This function is intended to be used in this format:
  17776. * ```
  17777. * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) {
  17778. * const key = getLastParsedKey();
  17779. * const value = getLastParsedValue();
  17780. * ...
  17781. * }
  17782. *
  17783. * @param text `cssText` to parse
  17784. * @param index where the parsing should resume.
  17785. * @returns index where the next invocation of `parseStyleNext` should resume.
  17786. */
  17787. function parseStyleNext(text, startIndex) {
  17788. const end = parserState.textEnd;
  17789. let index = parserState.key = consumeWhitespace(text, startIndex, end);
  17790. if (end === index) {
  17791. // we reached an end so just quit
  17792. return -1;
  17793. }
  17794. index = parserState.keyEnd = consumeStyleKey(text, index, end);
  17795. index = consumeSeparator(text, index, end, 58 /* CharCode.COLON */);
  17796. index = parserState.value = consumeWhitespace(text, index, end);
  17797. index = parserState.valueEnd = consumeStyleValue(text, index, end);
  17798. return consumeSeparator(text, index, end, 59 /* CharCode.SEMI_COLON */);
  17799. }
  17800. /**
  17801. * Reset the global state of the styling parser.
  17802. * @param text The styling text to parse.
  17803. */
  17804. function resetParserState(text) {
  17805. parserState.key = 0;
  17806. parserState.keyEnd = 0;
  17807. parserState.value = 0;
  17808. parserState.valueEnd = 0;
  17809. parserState.textEnd = text.length;
  17810. }
  17811. /**
  17812. * Returns index of next non-whitespace character.
  17813. *
  17814. * @param text Text to scan
  17815. * @param startIndex Starting index of character where the scan should start.
  17816. * @param endIndex Ending index of character where the scan should end.
  17817. * @returns Index of next non-whitespace character (May be the same as `start` if no whitespace at
  17818. * that location.)
  17819. */
  17820. function consumeWhitespace(text, startIndex, endIndex) {
  17821. while (startIndex < endIndex && text.charCodeAt(startIndex) <= 32 /* CharCode.SPACE */) {
  17822. startIndex++;
  17823. }
  17824. return startIndex;
  17825. }
  17826. /**
  17827. * Returns index of last char in class token.
  17828. *
  17829. * @param text Text to scan
  17830. * @param startIndex Starting index of character where the scan should start.
  17831. * @param endIndex Ending index of character where the scan should end.
  17832. * @returns Index after last char in class token.
  17833. */
  17834. function consumeClassToken(text, startIndex, endIndex) {
  17835. while (startIndex < endIndex && text.charCodeAt(startIndex) > 32 /* CharCode.SPACE */) {
  17836. startIndex++;
  17837. }
  17838. return startIndex;
  17839. }
  17840. /**
  17841. * Consumes all of the characters belonging to style key and token.
  17842. *
  17843. * @param text Text to scan
  17844. * @param startIndex Starting index of character where the scan should start.
  17845. * @param endIndex Ending index of character where the scan should end.
  17846. * @returns Index after last style key character.
  17847. */
  17848. function consumeStyleKey(text, startIndex, endIndex) {
  17849. let ch;
  17850. while (startIndex < endIndex &&
  17851. ((ch = text.charCodeAt(startIndex)) === 45 /* CharCode.DASH */ || ch === 95 /* CharCode.UNDERSCORE */ ||
  17852. ((ch & -33 /* CharCode.UPPER_CASE */) >= 65 /* CharCode.A */ && (ch & -33 /* CharCode.UPPER_CASE */) <= 90 /* CharCode.Z */) ||
  17853. (ch >= 48 /* CharCode.ZERO */ && ch <= 57 /* CharCode.NINE */))) {
  17854. startIndex++;
  17855. }
  17856. return startIndex;
  17857. }
  17858. /**
  17859. * Consumes all whitespace and the separator `:` after the style key.
  17860. *
  17861. * @param text Text to scan
  17862. * @param startIndex Starting index of character where the scan should start.
  17863. * @param endIndex Ending index of character where the scan should end.
  17864. * @returns Index after separator and surrounding whitespace.
  17865. */
  17866. function consumeSeparator(text, startIndex, endIndex, separator) {
  17867. startIndex = consumeWhitespace(text, startIndex, endIndex);
  17868. if (startIndex < endIndex) {
  17869. if (ngDevMode && text.charCodeAt(startIndex) !== separator) {
  17870. malformedStyleError(text, String.fromCharCode(separator), startIndex);
  17871. }
  17872. startIndex++;
  17873. }
  17874. return startIndex;
  17875. }
  17876. /**
  17877. * Consumes style value honoring `url()` and `""` text.
  17878. *
  17879. * @param text Text to scan
  17880. * @param startIndex Starting index of character where the scan should start.
  17881. * @param endIndex Ending index of character where the scan should end.
  17882. * @returns Index after last style value character.
  17883. */
  17884. function consumeStyleValue(text, startIndex, endIndex) {
  17885. let ch1 = -1; // 1st previous character
  17886. let ch2 = -1; // 2nd previous character
  17887. let ch3 = -1; // 3rd previous character
  17888. let i = startIndex;
  17889. let lastChIndex = i;
  17890. while (i < endIndex) {
  17891. const ch = text.charCodeAt(i++);
  17892. if (ch === 59 /* CharCode.SEMI_COLON */) {
  17893. return lastChIndex;
  17894. }
  17895. else if (ch === 34 /* CharCode.DOUBLE_QUOTE */ || ch === 39 /* CharCode.SINGLE_QUOTE */) {
  17896. lastChIndex = i = consumeQuotedText(text, ch, i, endIndex);
  17897. }
  17898. else if (startIndex ===
  17899. i - 4 && // We have seen only 4 characters so far "URL(" (Ignore "foo_URL()")
  17900. ch3 === 85 /* CharCode.U */ &&
  17901. ch2 === 82 /* CharCode.R */ && ch1 === 76 /* CharCode.L */ && ch === 40 /* CharCode.OPEN_PAREN */) {
  17902. lastChIndex = i = consumeQuotedText(text, 41 /* CharCode.CLOSE_PAREN */, i, endIndex);
  17903. }
  17904. else if (ch > 32 /* CharCode.SPACE */) {
  17905. // if we have a non-whitespace character then capture its location
  17906. lastChIndex = i;
  17907. }
  17908. ch3 = ch2;
  17909. ch2 = ch1;
  17910. ch1 = ch & -33 /* CharCode.UPPER_CASE */;
  17911. }
  17912. return lastChIndex;
  17913. }
  17914. /**
  17915. * Consumes all of the quoted characters.
  17916. *
  17917. * @param text Text to scan
  17918. * @param quoteCharCode CharCode of either `"` or `'` quote or `)` for `url(...)`.
  17919. * @param startIndex Starting index of character where the scan should start.
  17920. * @param endIndex Ending index of character where the scan should end.
  17921. * @returns Index after quoted characters.
  17922. */
  17923. function consumeQuotedText(text, quoteCharCode, startIndex, endIndex) {
  17924. let ch1 = -1; // 1st previous character
  17925. let index = startIndex;
  17926. while (index < endIndex) {
  17927. const ch = text.charCodeAt(index++);
  17928. if (ch == quoteCharCode && ch1 !== 92 /* CharCode.BACK_SLASH */) {
  17929. return index;
  17930. }
  17931. if (ch == 92 /* CharCode.BACK_SLASH */ && ch1 === 92 /* CharCode.BACK_SLASH */) {
  17932. // two back slashes cancel each other out. For example `"\\"` should properly end the
  17933. // quotation. (It should not assume that the last `"` is escaped.)
  17934. ch1 = 0;
  17935. }
  17936. else {
  17937. ch1 = ch;
  17938. }
  17939. }
  17940. throw ngDevMode ? malformedStyleError(text, String.fromCharCode(quoteCharCode), endIndex) :
  17941. new Error();
  17942. }
  17943. function malformedStyleError(text, expecting, index) {
  17944. ngDevMode && assertEqual(typeof text === 'string', true, 'String expected here');
  17945. throw throwError(`Malformed style at location ${index} in string '` + text.substring(0, index) + '[>>' +
  17946. text.substring(index, index + 1) + '<<]' + text.slice(index + 1) +
  17947. `'. Expecting '${expecting}'.`);
  17948. }
  17949. /**
  17950. * Update a style binding on an element with the provided value.
  17951. *
  17952. * If the style value is falsy then it will be removed from the element
  17953. * (or assigned a different value depending if there are any styles placed
  17954. * on the element with `styleMap` or any static styles that are
  17955. * present from when the element was created with `styling`).
  17956. *
  17957. * Note that the styling element is updated as part of `stylingApply`.
  17958. *
  17959. * @param prop A valid CSS property.
  17960. * @param value New value to write (`null` or an empty string to remove).
  17961. * @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
  17962. *
  17963. * Note that this will apply the provided style value to the host element if this function is called
  17964. * within a host binding function.
  17965. *
  17966. * @codeGenApi
  17967. */
  17968. function ɵɵstyleProp(prop, value, suffix) {
  17969. checkStylingProperty(prop, value, suffix, false);
  17970. return ɵɵstyleProp;
  17971. }
  17972. /**
  17973. * Update a class binding on an element with the provided value.
  17974. *
  17975. * This instruction is meant to handle the `[class.foo]="exp"` case and,
  17976. * therefore, the class binding itself must already be allocated using
  17977. * `styling` within the creation block.
  17978. *
  17979. * @param prop A valid CSS class (only one).
  17980. * @param value A true/false value which will turn the class on or off.
  17981. *
  17982. * Note that this will apply the provided class value to the host element if this function
  17983. * is called within a host binding function.
  17984. *
  17985. * @codeGenApi
  17986. */
  17987. function ɵɵclassProp(className, value) {
  17988. checkStylingProperty(className, value, null, true);
  17989. return ɵɵclassProp;
  17990. }
  17991. /**
  17992. * Update style bindings using an object literal on an element.
  17993. *
  17994. * This instruction is meant to apply styling via the `[style]="exp"` template bindings.
  17995. * When styles are applied to the element they will then be updated with respect to
  17996. * any styles/classes set via `styleProp`. If any styles are set to falsy
  17997. * then they will be removed from the element.
  17998. *
  17999. * Note that the styling instruction will not be applied until `stylingApply` is called.
  18000. *
  18001. * @param styles A key/value style map of the styles that will be applied to the given element.
  18002. * Any missing styles (that have already been applied to the element beforehand) will be
  18003. * removed (unset) from the element's styling.
  18004. *
  18005. * Note that this will apply the provided styleMap value to the host element if this function
  18006. * is called within a host binding.
  18007. *
  18008. * @codeGenApi
  18009. */
  18010. function ɵɵstyleMap(styles) {
  18011. checkStylingMap(styleKeyValueArraySet, styleStringParser, styles, false);
  18012. }
  18013. /**
  18014. * Parse text as style and add values to KeyValueArray.
  18015. *
  18016. * This code is pulled out to a separate function so that it can be tree shaken away if it is not
  18017. * needed. It is only referenced from `ɵɵstyleMap`.
  18018. *
  18019. * @param keyValueArray KeyValueArray to add parsed values to.
  18020. * @param text text to parse.
  18021. */
  18022. function styleStringParser(keyValueArray, text) {
  18023. for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i)) {
  18024. styleKeyValueArraySet(keyValueArray, getLastParsedKey(text), getLastParsedValue(text));
  18025. }
  18026. }
  18027. /**
  18028. * Update class bindings using an object literal or class-string on an element.
  18029. *
  18030. * This instruction is meant to apply styling via the `[class]="exp"` template bindings.
  18031. * When classes are applied to the element they will then be updated with
  18032. * respect to any styles/classes set via `classProp`. If any
  18033. * classes are set to falsy then they will be removed from the element.
  18034. *
  18035. * Note that the styling instruction will not be applied until `stylingApply` is called.
  18036. * Note that this will the provided classMap value to the host element if this function is called
  18037. * within a host binding.
  18038. *
  18039. * @param classes A key/value map or string of CSS classes that will be added to the
  18040. * given element. Any missing classes (that have already been applied to the element
  18041. * beforehand) will be removed (unset) from the element's list of CSS classes.
  18042. *
  18043. * @codeGenApi
  18044. */
  18045. function ɵɵclassMap(classes) {
  18046. checkStylingMap(classKeyValueArraySet, classStringParser, classes, true);
  18047. }
  18048. /**
  18049. * Parse text as class and add values to KeyValueArray.
  18050. *
  18051. * This code is pulled out to a separate function so that it can be tree shaken away if it is not
  18052. * needed. It is only referenced from `ɵɵclassMap`.
  18053. *
  18054. * @param keyValueArray KeyValueArray to add parsed values to.
  18055. * @param text text to parse.
  18056. */
  18057. function classStringParser(keyValueArray, text) {
  18058. for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
  18059. keyValueArraySet(keyValueArray, getLastParsedKey(text), true);
  18060. }
  18061. }
  18062. /**
  18063. * Common code between `ɵɵclassProp` and `ɵɵstyleProp`.
  18064. *
  18065. * @param prop property name.
  18066. * @param value binding value.
  18067. * @param suffix suffix for the property (e.g. `em` or `px`)
  18068. * @param isClassBased `true` if `class` change (`false` if `style`)
  18069. */
  18070. function checkStylingProperty(prop, value, suffix, isClassBased) {
  18071. const lView = getLView();
  18072. const tView = getTView();
  18073. // Styling instructions use 2 slots per binding.
  18074. // 1. one for the value / TStylingKey
  18075. // 2. one for the intermittent-value / TStylingRange
  18076. const bindingIndex = incrementBindingIndex(2);
  18077. if (tView.firstUpdatePass) {
  18078. stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
  18079. }
  18080. if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
  18081. const tNode = tView.data[getSelectedIndex()];
  18082. updateStyling(tView, tNode, lView, lView[RENDERER], prop, lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex);
  18083. }
  18084. }
  18085. /**
  18086. * Common code between `ɵɵclassMap` and `ɵɵstyleMap`.
  18087. *
  18088. * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
  18089. * function so that `style` can be processed. This is done for tree shaking purposes.
  18090. * @param stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class`
  18091. * have different parsers.)
  18092. * @param value bound value from application
  18093. * @param isClassBased `true` if `class` change (`false` if `style`)
  18094. */
  18095. function checkStylingMap(keyValueArraySet, stringParser, value, isClassBased) {
  18096. const tView = getTView();
  18097. const bindingIndex = incrementBindingIndex(2);
  18098. if (tView.firstUpdatePass) {
  18099. stylingFirstUpdatePass(tView, null, bindingIndex, isClassBased);
  18100. }
  18101. const lView = getLView();
  18102. if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
  18103. // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
  18104. // if so as not to read unnecessarily.
  18105. const tNode = tView.data[getSelectedIndex()];
  18106. if (hasStylingInputShadow(tNode, isClassBased) && !isInHostBindings(tView, bindingIndex)) {
  18107. if (ngDevMode) {
  18108. // verify that if we are shadowing then `TData` is appropriately marked so that we skip
  18109. // processing this binding in styling resolution.
  18110. const tStylingKey = tView.data[bindingIndex];
  18111. assertEqual(Array.isArray(tStylingKey) ? tStylingKey[1] : tStylingKey, false, 'Styling linked list shadow input should be marked as \'false\'');
  18112. }
  18113. // VE does not concatenate the static portion like we are doing here.
  18114. // Instead VE just ignores the static completely if dynamic binding is present.
  18115. // Because of locality we have already set the static portion because we don't know if there
  18116. // is a dynamic portion until later. If we would ignore the static portion it would look like
  18117. // the binding has removed it. This would confuse `[ngStyle]`/`[ngClass]` to do the wrong
  18118. // thing as it would think that the static portion was removed. For this reason we
  18119. // concatenate it so that `[ngStyle]`/`[ngClass]` can continue to work on changed.
  18120. let staticPrefix = isClassBased ? tNode.classesWithoutHost : tNode.stylesWithoutHost;
  18121. ngDevMode && isClassBased === false && staticPrefix !== null &&
  18122. assertEqual(staticPrefix.endsWith(';'), true, 'Expecting static portion to end with \';\'');
  18123. if (staticPrefix !== null) {
  18124. // We want to make sure that falsy values of `value` become empty strings.
  18125. value = concatStringsWithSpace(staticPrefix, value ? value : '');
  18126. }
  18127. // Given `<div [style] my-dir>` such that `my-dir` has `@Input('style')`.
  18128. // This takes over the `[style]` binding. (Same for `[class]`)
  18129. setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased);
  18130. }
  18131. else {
  18132. updateStylingMap(tView, tNode, lView, lView[RENDERER], lView[bindingIndex + 1], lView[bindingIndex + 1] = toStylingKeyValueArray(keyValueArraySet, stringParser, value), isClassBased, bindingIndex);
  18133. }
  18134. }
  18135. }
  18136. /**
  18137. * Determines when the binding is in `hostBindings` section
  18138. *
  18139. * @param tView Current `TView`
  18140. * @param bindingIndex index of binding which we would like if it is in `hostBindings`
  18141. */
  18142. function isInHostBindings(tView, bindingIndex) {
  18143. // All host bindings are placed after the expando section.
  18144. return bindingIndex >= tView.expandoStartIndex;
  18145. }
  18146. /**
  18147. * Collects the necessary information to insert the binding into a linked list of style bindings
  18148. * using `insertTStylingBinding`.
  18149. *
  18150. * @param tView `TView` where the binding linked list will be stored.
  18151. * @param tStylingKey Property/key of the binding.
  18152. * @param bindingIndex Index of binding associated with the `prop`
  18153. * @param isClassBased `true` if `class` change (`false` if `style`)
  18154. */
  18155. function stylingFirstUpdatePass(tView, tStylingKey, bindingIndex, isClassBased) {
  18156. ngDevMode && assertFirstUpdatePass(tView);
  18157. const tData = tView.data;
  18158. if (tData[bindingIndex + 1] === null) {
  18159. // The above check is necessary because we don't clear first update pass until first successful
  18160. // (no exception) template execution. This prevents the styling instruction from double adding
  18161. // itself to the list.
  18162. // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
  18163. // if so as not to read unnecessarily.
  18164. const tNode = tData[getSelectedIndex()];
  18165. ngDevMode && assertDefined(tNode, 'TNode expected');
  18166. const isHostBindings = isInHostBindings(tView, bindingIndex);
  18167. if (hasStylingInputShadow(tNode, isClassBased) && tStylingKey === null && !isHostBindings) {
  18168. // `tStylingKey === null` implies that we are either `[style]` or `[class]` binding.
  18169. // If there is a directive which uses `@Input('style')` or `@Input('class')` than
  18170. // we need to neutralize this binding since that directive is shadowing it.
  18171. // We turn this into a noop by setting the key to `false`
  18172. tStylingKey = false;
  18173. }
  18174. tStylingKey = wrapInStaticStylingKey(tData, tNode, tStylingKey, isClassBased);
  18175. insertTStylingBinding(tData, tNode, tStylingKey, bindingIndex, isHostBindings, isClassBased);
  18176. }
  18177. }
  18178. /**
  18179. * Adds static styling information to the binding if applicable.
  18180. *
  18181. * The linked list of styles not only stores the list and keys, but also stores static styling
  18182. * information on some of the keys. This function determines if the key should contain the styling
  18183. * information and computes it.
  18184. *
  18185. * See `TStylingStatic` for more details.
  18186. *
  18187. * @param tData `TData` where the linked list is stored.
  18188. * @param tNode `TNode` for which the styling is being computed.
  18189. * @param stylingKey `TStylingKeyPrimitive` which may need to be wrapped into `TStylingKey`
  18190. * @param isClassBased `true` if `class` (`false` if `style`)
  18191. */
  18192. function wrapInStaticStylingKey(tData, tNode, stylingKey, isClassBased) {
  18193. const hostDirectiveDef = getCurrentDirectiveDef(tData);
  18194. let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles;
  18195. if (hostDirectiveDef === null) {
  18196. // We are in template node.
  18197. // If template node already had styling instruction then it has already collected the static
  18198. // styling and there is no need to collect them again. We know that we are the first styling
  18199. // instruction because the `TNode.*Bindings` points to 0 (nothing has been inserted yet).
  18200. const isFirstStylingInstructionInTemplate = (isClassBased ? tNode.classBindings : tNode.styleBindings) === 0;
  18201. if (isFirstStylingInstructionInTemplate) {
  18202. // It would be nice to be able to get the statics from `mergeAttrs`, however, at this point
  18203. // they are already merged and it would not be possible to figure which property belongs where
  18204. // in the priority.
  18205. stylingKey = collectStylingFromDirectives(null, tData, tNode, stylingKey, isClassBased);
  18206. stylingKey = collectStylingFromTAttrs(stylingKey, tNode.attrs, isClassBased);
  18207. // We know that if we have styling binding in template we can't have residual.
  18208. residual = null;
  18209. }
  18210. }
  18211. else {
  18212. // We are in host binding node and there was no binding instruction in template node.
  18213. // This means that we need to compute the residual.
  18214. const directiveStylingLast = tNode.directiveStylingLast;
  18215. const isFirstStylingInstructionInHostBinding = directiveStylingLast === -1 || tData[directiveStylingLast] !== hostDirectiveDef;
  18216. if (isFirstStylingInstructionInHostBinding) {
  18217. stylingKey =
  18218. collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased);
  18219. if (residual === null) {
  18220. // - If `null` than either:
  18221. // - Template styling instruction already ran and it has consumed the static
  18222. // styling into its `TStylingKey` and so there is no need to update residual. Instead
  18223. // we need to update the `TStylingKey` associated with the first template node
  18224. // instruction. OR
  18225. // - Some other styling instruction ran and determined that there are no residuals
  18226. let templateStylingKey = getTemplateHeadTStylingKey(tData, tNode, isClassBased);
  18227. if (templateStylingKey !== undefined && Array.isArray(templateStylingKey)) {
  18228. // Only recompute if `templateStylingKey` had static values. (If no static value found
  18229. // then there is nothing to do since this operation can only produce less static keys, not
  18230. // more.)
  18231. templateStylingKey = collectStylingFromDirectives(null, tData, tNode, templateStylingKey[1] /* unwrap previous statics */, isClassBased);
  18232. templateStylingKey =
  18233. collectStylingFromTAttrs(templateStylingKey, tNode.attrs, isClassBased);
  18234. setTemplateHeadTStylingKey(tData, tNode, isClassBased, templateStylingKey);
  18235. }
  18236. }
  18237. else {
  18238. // We only need to recompute residual if it is not `null`.
  18239. // - If existing residual (implies there was no template styling). This means that some of
  18240. // the statics may have moved from the residual to the `stylingKey` and so we have to
  18241. // recompute.
  18242. // - If `undefined` this is the first time we are running.
  18243. residual = collectResidual(tData, tNode, isClassBased);
  18244. }
  18245. }
  18246. }
  18247. if (residual !== undefined) {
  18248. isClassBased ? (tNode.residualClasses = residual) : (tNode.residualStyles = residual);
  18249. }
  18250. return stylingKey;
  18251. }
  18252. /**
  18253. * Retrieve the `TStylingKey` for the template styling instruction.
  18254. *
  18255. * This is needed since `hostBinding` styling instructions are inserted after the template
  18256. * instruction. While the template instruction needs to update the residual in `TNode` the
  18257. * `hostBinding` instructions need to update the `TStylingKey` of the template instruction because
  18258. * the template instruction is downstream from the `hostBindings` instructions.
  18259. *
  18260. * @param tData `TData` where the linked list is stored.
  18261. * @param tNode `TNode` for which the styling is being computed.
  18262. * @param isClassBased `true` if `class` (`false` if `style`)
  18263. * @return `TStylingKey` if found or `undefined` if not found.
  18264. */
  18265. function getTemplateHeadTStylingKey(tData, tNode, isClassBased) {
  18266. const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings;
  18267. if (getTStylingRangeNext(bindings) === 0) {
  18268. // There does not seem to be a styling instruction in the `template`.
  18269. return undefined;
  18270. }
  18271. return tData[getTStylingRangePrev(bindings)];
  18272. }
  18273. /**
  18274. * Update the `TStylingKey` of the first template instruction in `TNode`.
  18275. *
  18276. * Logically `hostBindings` styling instructions are of lower priority than that of the template.
  18277. * However, they execute after the template styling instructions. This means that they get inserted
  18278. * in front of the template styling instructions.
  18279. *
  18280. * If we have a template styling instruction and a new `hostBindings` styling instruction is
  18281. * executed it means that it may need to steal static fields from the template instruction. This
  18282. * method allows us to update the first template instruction `TStylingKey` with a new value.
  18283. *
  18284. * Assume:
  18285. * ```
  18286. * <div my-dir style="color: red" [style.color]="tmplExp"></div>
  18287. *
  18288. * @Directive({
  18289. * host: {
  18290. * 'style': 'width: 100px',
  18291. * '[style.color]': 'dirExp',
  18292. * }
  18293. * })
  18294. * class MyDir {}
  18295. * ```
  18296. *
  18297. * when `[style.color]="tmplExp"` executes it creates this data structure.
  18298. * ```
  18299. * ['', 'color', 'color', 'red', 'width', '100px'],
  18300. * ```
  18301. *
  18302. * The reason for this is that the template instruction does not know if there are styling
  18303. * instructions and must assume that there are none and must collect all of the static styling.
  18304. * (both
  18305. * `color' and 'width`)
  18306. *
  18307. * When `'[style.color]': 'dirExp',` executes we need to insert a new data into the linked list.
  18308. * ```
  18309. * ['', 'color', 'width', '100px'], // newly inserted
  18310. * ['', 'color', 'color', 'red', 'width', '100px'], // this is wrong
  18311. * ```
  18312. *
  18313. * Notice that the template statics is now wrong as it incorrectly contains `width` so we need to
  18314. * update it like so:
  18315. * ```
  18316. * ['', 'color', 'width', '100px'],
  18317. * ['', 'color', 'color', 'red'], // UPDATE
  18318. * ```
  18319. *
  18320. * @param tData `TData` where the linked list is stored.
  18321. * @param tNode `TNode` for which the styling is being computed.
  18322. * @param isClassBased `true` if `class` (`false` if `style`)
  18323. * @param tStylingKey New `TStylingKey` which is replacing the old one.
  18324. */
  18325. function setTemplateHeadTStylingKey(tData, tNode, isClassBased, tStylingKey) {
  18326. const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings;
  18327. ngDevMode &&
  18328. assertNotEqual(getTStylingRangeNext(bindings), 0, 'Expecting to have at least one template styling binding.');
  18329. tData[getTStylingRangePrev(bindings)] = tStylingKey;
  18330. }
  18331. /**
  18332. * Collect all static values after the current `TNode.directiveStylingLast` index.
  18333. *
  18334. * Collect the remaining styling information which has not yet been collected by an existing
  18335. * styling instruction.
  18336. *
  18337. * @param tData `TData` where the `DirectiveDefs` are stored.
  18338. * @param tNode `TNode` which contains the directive range.
  18339. * @param isClassBased `true` if `class` (`false` if `style`)
  18340. */
  18341. function collectResidual(tData, tNode, isClassBased) {
  18342. let residual = undefined;
  18343. const directiveEnd = tNode.directiveEnd;
  18344. ngDevMode &&
  18345. assertNotEqual(tNode.directiveStylingLast, -1, 'By the time this function gets called at least one hostBindings-node styling instruction must have executed.');
  18346. // We add `1 + tNode.directiveStart` because we need to skip the current directive (as we are
  18347. // collecting things after the last `hostBindings` directive which had a styling instruction.)
  18348. for (let i = 1 + tNode.directiveStylingLast; i < directiveEnd; i++) {
  18349. const attrs = tData[i].hostAttrs;
  18350. residual = collectStylingFromTAttrs(residual, attrs, isClassBased);
  18351. }
  18352. return collectStylingFromTAttrs(residual, tNode.attrs, isClassBased);
  18353. }
  18354. /**
  18355. * Collect the static styling information with lower priority than `hostDirectiveDef`.
  18356. *
  18357. * (This is opposite of residual styling.)
  18358. *
  18359. * @param hostDirectiveDef `DirectiveDef` for which we want to collect lower priority static
  18360. * styling. (Or `null` if template styling)
  18361. * @param tData `TData` where the linked list is stored.
  18362. * @param tNode `TNode` for which the styling is being computed.
  18363. * @param stylingKey Existing `TStylingKey` to update or wrap.
  18364. * @param isClassBased `true` if `class` (`false` if `style`)
  18365. */
  18366. function collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased) {
  18367. // We need to loop because there can be directives which have `hostAttrs` but don't have
  18368. // `hostBindings` so this loop catches up to the current directive..
  18369. let currentDirective = null;
  18370. const directiveEnd = tNode.directiveEnd;
  18371. let directiveStylingLast = tNode.directiveStylingLast;
  18372. if (directiveStylingLast === -1) {
  18373. directiveStylingLast = tNode.directiveStart;
  18374. }
  18375. else {
  18376. directiveStylingLast++;
  18377. }
  18378. while (directiveStylingLast < directiveEnd) {
  18379. currentDirective = tData[directiveStylingLast];
  18380. ngDevMode && assertDefined(currentDirective, 'expected to be defined');
  18381. stylingKey = collectStylingFromTAttrs(stylingKey, currentDirective.hostAttrs, isClassBased);
  18382. if (currentDirective === hostDirectiveDef)
  18383. break;
  18384. directiveStylingLast++;
  18385. }
  18386. if (hostDirectiveDef !== null) {
  18387. // we only advance the styling cursor if we are collecting data from host bindings.
  18388. // Template executes before host bindings and so if we would update the index,
  18389. // host bindings would not get their statics.
  18390. tNode.directiveStylingLast = directiveStylingLast;
  18391. }
  18392. return stylingKey;
  18393. }
  18394. /**
  18395. * Convert `TAttrs` into `TStylingStatic`.
  18396. *
  18397. * @param stylingKey existing `TStylingKey` to update or wrap.
  18398. * @param attrs `TAttributes` to process.
  18399. * @param isClassBased `true` if `class` (`false` if `style`)
  18400. */
  18401. function collectStylingFromTAttrs(stylingKey, attrs, isClassBased) {
  18402. const desiredMarker = isClassBased ? 1 /* AttributeMarker.Classes */ : 2 /* AttributeMarker.Styles */;
  18403. let currentMarker = -1 /* AttributeMarker.ImplicitAttributes */;
  18404. if (attrs !== null) {
  18405. for (let i = 0; i < attrs.length; i++) {
  18406. const item = attrs[i];
  18407. if (typeof item === 'number') {
  18408. currentMarker = item;
  18409. }
  18410. else {
  18411. if (currentMarker === desiredMarker) {
  18412. if (!Array.isArray(stylingKey)) {
  18413. stylingKey = stylingKey === undefined ? [] : ['', stylingKey];
  18414. }
  18415. keyValueArraySet(stylingKey, item, isClassBased ? true : attrs[++i]);
  18416. }
  18417. }
  18418. }
  18419. }
  18420. return stylingKey === undefined ? null : stylingKey;
  18421. }
  18422. /**
  18423. * Convert user input to `KeyValueArray`.
  18424. *
  18425. * This function takes user input which could be `string`, Object literal, or iterable and converts
  18426. * it into a consistent representation. The output of this is `KeyValueArray` (which is an array
  18427. * where
  18428. * even indexes contain keys and odd indexes contain values for those keys).
  18429. *
  18430. * The advantage of converting to `KeyValueArray` is that we can perform diff in an input
  18431. * independent
  18432. * way.
  18433. * (ie we can compare `foo bar` to `['bar', 'baz'] and determine a set of changes which need to be
  18434. * applied)
  18435. *
  18436. * The fact that `KeyValueArray` is sorted is very important because it allows us to compute the
  18437. * difference in linear fashion without the need to allocate any additional data.
  18438. *
  18439. * For example if we kept this as a `Map` we would have to iterate over previous `Map` to determine
  18440. * which values need to be deleted, over the new `Map` to determine additions, and we would have to
  18441. * keep additional `Map` to keep track of duplicates or items which have not yet been visited.
  18442. *
  18443. * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
  18444. * function so that `style` can be processed. This is done
  18445. * for tree shaking purposes.
  18446. * @param stringParser The parser is passed in so that it will be tree shakable. See
  18447. * `styleStringParser` and `classStringParser`
  18448. * @param value The value to parse/convert to `KeyValueArray`
  18449. */
  18450. function toStylingKeyValueArray(keyValueArraySet, stringParser, value) {
  18451. if (value == null /*|| value === undefined */ || value === '')
  18452. return EMPTY_ARRAY;
  18453. const styleKeyValueArray = [];
  18454. const unwrappedValue = unwrapSafeValue(value);
  18455. if (Array.isArray(unwrappedValue)) {
  18456. for (let i = 0; i < unwrappedValue.length; i++) {
  18457. keyValueArraySet(styleKeyValueArray, unwrappedValue[i], true);
  18458. }
  18459. }
  18460. else if (typeof unwrappedValue === 'object') {
  18461. for (const key in unwrappedValue) {
  18462. if (unwrappedValue.hasOwnProperty(key)) {
  18463. keyValueArraySet(styleKeyValueArray, key, unwrappedValue[key]);
  18464. }
  18465. }
  18466. }
  18467. else if (typeof unwrappedValue === 'string') {
  18468. stringParser(styleKeyValueArray, unwrappedValue);
  18469. }
  18470. else {
  18471. ngDevMode &&
  18472. throwError('Unsupported styling type ' + typeof unwrappedValue + ': ' + unwrappedValue);
  18473. }
  18474. return styleKeyValueArray;
  18475. }
  18476. /**
  18477. * Set a `value` for a `key`.
  18478. *
  18479. * See: `keyValueArraySet` for details
  18480. *
  18481. * @param keyValueArray KeyValueArray to add to.
  18482. * @param key Style key to add.
  18483. * @param value The value to set.
  18484. */
  18485. function styleKeyValueArraySet(keyValueArray, key, value) {
  18486. keyValueArraySet(keyValueArray, key, unwrapSafeValue(value));
  18487. }
  18488. /**
  18489. * Class-binding-specific function for setting the `value` for a `key`.
  18490. *
  18491. * See: `keyValueArraySet` for details
  18492. *
  18493. * @param keyValueArray KeyValueArray to add to.
  18494. * @param key Style key to add.
  18495. * @param value The value to set.
  18496. */
  18497. function classKeyValueArraySet(keyValueArray, key, value) {
  18498. // We use `classList.add` to eventually add the CSS classes to the DOM node. Any value passed into
  18499. // `add` is stringified and added to the `class` attribute, e.g. even null, undefined or numbers
  18500. // will be added. Stringify the key here so that our internal data structure matches the value in
  18501. // the DOM. The only exceptions are empty strings and strings that contain spaces for which
  18502. // the browser throws an error. We ignore such values, because the error is somewhat cryptic.
  18503. const stringKey = String(key);
  18504. if (stringKey !== '' && !stringKey.includes(' ')) {
  18505. keyValueArraySet(keyValueArray, stringKey, value);
  18506. }
  18507. }
  18508. /**
  18509. * Update map based styling.
  18510. *
  18511. * Map based styling could be anything which contains more than one binding. For example `string`,
  18512. * or object literal. Dealing with all of these types would complicate the logic so
  18513. * instead this function expects that the complex input is first converted into normalized
  18514. * `KeyValueArray`. The advantage of normalization is that we get the values sorted, which makes it
  18515. * very cheap to compute deltas between the previous and current value.
  18516. *
  18517. * @param tView Associated `TView.data` contains the linked list of binding priorities.
  18518. * @param tNode `TNode` where the binding is located.
  18519. * @param lView `LView` contains the values associated with other styling binding at this `TNode`.
  18520. * @param renderer Renderer to use if any updates.
  18521. * @param oldKeyValueArray Previous value represented as `KeyValueArray`
  18522. * @param newKeyValueArray Current value represented as `KeyValueArray`
  18523. * @param isClassBased `true` if `class` (`false` if `style`)
  18524. * @param bindingIndex Binding index of the binding.
  18525. */
  18526. function updateStylingMap(tView, tNode, lView, renderer, oldKeyValueArray, newKeyValueArray, isClassBased, bindingIndex) {
  18527. if (oldKeyValueArray === NO_CHANGE) {
  18528. // On first execution the oldKeyValueArray is NO_CHANGE => treat it as empty KeyValueArray.
  18529. oldKeyValueArray = EMPTY_ARRAY;
  18530. }
  18531. let oldIndex = 0;
  18532. let newIndex = 0;
  18533. let oldKey = 0 < oldKeyValueArray.length ? oldKeyValueArray[0] : null;
  18534. let newKey = 0 < newKeyValueArray.length ? newKeyValueArray[0] : null;
  18535. while (oldKey !== null || newKey !== null) {
  18536. ngDevMode && assertLessThan(oldIndex, 999, 'Are we stuck in infinite loop?');
  18537. ngDevMode && assertLessThan(newIndex, 999, 'Are we stuck in infinite loop?');
  18538. const oldValue = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex + 1] : undefined;
  18539. const newValue = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex + 1] : undefined;
  18540. let setKey = null;
  18541. let setValue = undefined;
  18542. if (oldKey === newKey) {
  18543. // UPDATE: Keys are equal => new value is overwriting old value.
  18544. oldIndex += 2;
  18545. newIndex += 2;
  18546. if (oldValue !== newValue) {
  18547. setKey = newKey;
  18548. setValue = newValue;
  18549. }
  18550. }
  18551. else if (newKey === null || oldKey !== null && oldKey < newKey) {
  18552. // DELETE: oldKey key is missing or we did not find the oldKey in the newValue
  18553. // (because the keyValueArray is sorted and `newKey` is found later alphabetically).
  18554. // `"background" < "color"` so we need to delete `"background"` because it is not found in the
  18555. // new array.
  18556. oldIndex += 2;
  18557. setKey = oldKey;
  18558. }
  18559. else {
  18560. // CREATE: newKey's is earlier alphabetically than oldKey's (or no oldKey) => we have new key.
  18561. // `"color" > "background"` so we need to add `color` because it is in new array but not in
  18562. // old array.
  18563. ngDevMode && assertDefined(newKey, 'Expecting to have a valid key');
  18564. newIndex += 2;
  18565. setKey = newKey;
  18566. setValue = newValue;
  18567. }
  18568. if (setKey !== null) {
  18569. updateStyling(tView, tNode, lView, renderer, setKey, setValue, isClassBased, bindingIndex);
  18570. }
  18571. oldKey = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex] : null;
  18572. newKey = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex] : null;
  18573. }
  18574. }
  18575. /**
  18576. * Update a simple (property name) styling.
  18577. *
  18578. * This function takes `prop` and updates the DOM to that value. The function takes the binding
  18579. * value as well as binding priority into consideration to determine which value should be written
  18580. * to DOM. (For example it may be determined that there is a higher priority overwrite which blocks
  18581. * the DOM write, or if the value goes to `undefined` a lower priority overwrite may be consulted.)
  18582. *
  18583. * @param tView Associated `TView.data` contains the linked list of binding priorities.
  18584. * @param tNode `TNode` where the binding is located.
  18585. * @param lView `LView` contains the values associated with other styling binding at this `TNode`.
  18586. * @param renderer Renderer to use if any updates.
  18587. * @param prop Either style property name or a class name.
  18588. * @param value Either style value for `prop` or `true`/`false` if `prop` is class.
  18589. * @param isClassBased `true` if `class` (`false` if `style`)
  18590. * @param bindingIndex Binding index of the binding.
  18591. */
  18592. function updateStyling(tView, tNode, lView, renderer, prop, value, isClassBased, bindingIndex) {
  18593. if (!(tNode.type & 3 /* TNodeType.AnyRNode */)) {
  18594. // It is possible to have styling on non-elements (such as ng-container).
  18595. // This is rare, but it does happen. In such a case, just ignore the binding.
  18596. return;
  18597. }
  18598. const tData = tView.data;
  18599. const tRange = tData[bindingIndex + 1];
  18600. const higherPriorityValue = getTStylingRangeNextDuplicate(tRange) ?
  18601. findStylingValue(tData, tNode, lView, prop, getTStylingRangeNext(tRange), isClassBased) :
  18602. undefined;
  18603. if (!isStylingValuePresent(higherPriorityValue)) {
  18604. // We don't have a next duplicate, or we did not find a duplicate value.
  18605. if (!isStylingValuePresent(value)) {
  18606. // We should delete current value or restore to lower priority value.
  18607. if (getTStylingRangePrevDuplicate(tRange)) {
  18608. // We have a possible prev duplicate, let's retrieve it.
  18609. value = findStylingValue(tData, null, lView, prop, bindingIndex, isClassBased);
  18610. }
  18611. }
  18612. const rNode = getNativeByIndex(getSelectedIndex(), lView);
  18613. applyStyling(renderer, isClassBased, rNode, prop, value);
  18614. }
  18615. }
  18616. /**
  18617. * Search for styling value with higher priority which is overwriting current value, or a
  18618. * value of lower priority to which we should fall back if the value is `undefined`.
  18619. *
  18620. * When value is being applied at a location, related values need to be consulted.
  18621. * - If there is a higher priority binding, we should be using that one instead.
  18622. * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp1`
  18623. * requires that we check `exp2` to see if it is set to value other than `undefined`.
  18624. * - If there is a lower priority binding and we are changing to `undefined`
  18625. * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp2` to
  18626. * `undefined` requires that we check `exp1` (and static values) and use that as new value.
  18627. *
  18628. * NOTE: The styling stores two values.
  18629. * 1. The raw value which came from the application is stored at `index + 0` location. (This value
  18630. * is used for dirty checking).
  18631. * 2. The normalized value is stored at `index + 1`.
  18632. *
  18633. * @param tData `TData` used for traversing the priority.
  18634. * @param tNode `TNode` to use for resolving static styling. Also controls search direction.
  18635. * - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true.
  18636. * If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value.
  18637. * - `null` search prev and go all the way to end. Return last value where
  18638. * `isStylingValuePresent(value)` is true.
  18639. * @param lView `LView` used for retrieving the actual values.
  18640. * @param prop Property which we are interested in.
  18641. * @param index Starting index in the linked list of styling bindings where the search should start.
  18642. * @param isClassBased `true` if `class` (`false` if `style`)
  18643. */
  18644. function findStylingValue(tData, tNode, lView, prop, index, isClassBased) {
  18645. // `TNode` to use for resolving static styling. Also controls search direction.
  18646. // - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true.
  18647. // If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value.
  18648. // - `null` search prev and go all the way to end. Return last value where
  18649. // `isStylingValuePresent(value)` is true.
  18650. const isPrevDirection = tNode === null;
  18651. let value = undefined;
  18652. while (index > 0) {
  18653. const rawKey = tData[index];
  18654. const containsStatics = Array.isArray(rawKey);
  18655. // Unwrap the key if we contain static values.
  18656. const key = containsStatics ? rawKey[1] : rawKey;
  18657. const isStylingMap = key === null;
  18658. let valueAtLViewIndex = lView[index + 1];
  18659. if (valueAtLViewIndex === NO_CHANGE) {
  18660. // In firstUpdatePass the styling instructions create a linked list of styling.
  18661. // On subsequent passes it is possible for a styling instruction to try to read a binding
  18662. // which
  18663. // has not yet executed. In that case we will find `NO_CHANGE` and we should assume that
  18664. // we have `undefined` (or empty array in case of styling-map instruction) instead. This
  18665. // allows the resolution to apply the value (which may later be overwritten when the
  18666. // binding actually executes.)
  18667. valueAtLViewIndex = isStylingMap ? EMPTY_ARRAY : undefined;
  18668. }
  18669. let currentValue = isStylingMap ? keyValueArrayGet(valueAtLViewIndex, prop) :
  18670. (key === prop ? valueAtLViewIndex : undefined);
  18671. if (containsStatics && !isStylingValuePresent(currentValue)) {
  18672. currentValue = keyValueArrayGet(rawKey, prop);
  18673. }
  18674. if (isStylingValuePresent(currentValue)) {
  18675. value = currentValue;
  18676. if (isPrevDirection) {
  18677. return value;
  18678. }
  18679. }
  18680. const tRange = tData[index + 1];
  18681. index = isPrevDirection ? getTStylingRangePrev(tRange) : getTStylingRangeNext(tRange);
  18682. }
  18683. if (tNode !== null) {
  18684. // in case where we are going in next direction AND we did not find anything, we need to
  18685. // consult residual styling
  18686. let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles;
  18687. if (residual != null /** OR residual !=== undefined */) {
  18688. value = keyValueArrayGet(residual, prop);
  18689. }
  18690. }
  18691. return value;
  18692. }
  18693. /**
  18694. * Determines if the binding value should be used (or if the value is 'undefined' and hence priority
  18695. * resolution should be used.)
  18696. *
  18697. * @param value Binding style value.
  18698. */
  18699. function isStylingValuePresent(value) {
  18700. // Currently only `undefined` value is considered non-binding. That is `undefined` says I don't
  18701. // have an opinion as to what this binding should be and you should consult other bindings by
  18702. // priority to determine the valid value.
  18703. // This is extracted into a single function so that we have a single place to control this.
  18704. return value !== undefined;
  18705. }
  18706. /**
  18707. * Normalizes and/or adds a suffix to the value.
  18708. *
  18709. * If value is `null`/`undefined` no suffix is added
  18710. * @param value
  18711. * @param suffix
  18712. */
  18713. function normalizeSuffix(value, suffix) {
  18714. if (value == null || value === '') {
  18715. // do nothing
  18716. // Do not add the suffix if the value is going to be empty.
  18717. // As it produce invalid CSS, which the browsers will automatically omit but Domino will not.
  18718. // Example: `"left": "px;"` instead of `"left": ""`.
  18719. }
  18720. else if (typeof suffix === 'string') {
  18721. value = value + suffix;
  18722. }
  18723. else if (typeof value === 'object') {
  18724. value = stringify(unwrapSafeValue(value));
  18725. }
  18726. return value;
  18727. }
  18728. /**
  18729. * Tests if the `TNode` has input shadow.
  18730. *
  18731. * An input shadow is when a directive steals (shadows) the input by using `@Input('style')` or
  18732. * `@Input('class')` as input.
  18733. *
  18734. * @param tNode `TNode` which we would like to see if it has shadow.
  18735. * @param isClassBased `true` if `class` (`false` if `style`)
  18736. */
  18737. function hasStylingInputShadow(tNode, isClassBased) {
  18738. return (tNode.flags & (isClassBased ? 8 /* TNodeFlags.hasClassInput */ : 16 /* TNodeFlags.hasStyleInput */)) !== 0;
  18739. }
  18740. /**
  18741. * Create static text node
  18742. *
  18743. * @param index Index of the node in the data array
  18744. * @param value Static string value to write.
  18745. *
  18746. * @codeGenApi
  18747. */
  18748. function ɵɵtext(index, value = '') {
  18749. const lView = getLView();
  18750. const tView = getTView();
  18751. const adjustedIndex = index + HEADER_OFFSET;
  18752. ngDevMode &&
  18753. assertEqual(getBindingIndex(), tView.bindingStartIndex, 'text nodes should be created before any bindings');
  18754. ngDevMode && assertIndexInRange(lView, adjustedIndex);
  18755. const tNode = tView.firstCreatePass ?
  18756. getOrCreateTNode(tView, adjustedIndex, 1 /* TNodeType.Text */, value, null) :
  18757. tView.data[adjustedIndex];
  18758. const textNative = _locateOrCreateTextNode(tView, lView, tNode, value, index);
  18759. lView[adjustedIndex] = textNative;
  18760. if (wasLastNodeCreated()) {
  18761. appendChild(tView, lView, textNative, tNode);
  18762. }
  18763. // Text nodes are self closing.
  18764. setCurrentTNode(tNode, false);
  18765. }
  18766. let _locateOrCreateTextNode = (tView, lView, tNode, value, index) => {
  18767. lastNodeWasCreated(true);
  18768. return createTextNode(lView[RENDERER], value);
  18769. };
  18770. /**
  18771. * Enables hydration code path (to lookup existing elements in DOM)
  18772. * in addition to the regular creation mode of text nodes.
  18773. */
  18774. function locateOrCreateTextNodeImpl(tView, lView, tNode, value, index) {
  18775. const hydrationInfo = lView[HYDRATION];
  18776. const isNodeCreationMode = !hydrationInfo || isInSkipHydrationBlock$1() || isDisconnectedNode$1(hydrationInfo, index);
  18777. lastNodeWasCreated(isNodeCreationMode);
  18778. // Regular creation mode.
  18779. if (isNodeCreationMode) {
  18780. return createTextNode(lView[RENDERER], value);
  18781. }
  18782. // Hydration mode, looking up an existing element in DOM.
  18783. const textNative = locateNextRNode(hydrationInfo, tView, lView, tNode);
  18784. ngDevMode && validateMatchingNode(textNative, Node.TEXT_NODE, null, lView, tNode);
  18785. ngDevMode && markRNodeAsClaimedByHydration(textNative);
  18786. return textNative;
  18787. }
  18788. function enableLocateOrCreateTextNodeImpl() {
  18789. _locateOrCreateTextNode = locateOrCreateTextNodeImpl;
  18790. }
  18791. /**
  18792. *
  18793. * Update text content with a lone bound value
  18794. *
  18795. * Used when a text node has 1 interpolated value in it, an no additional text
  18796. * surrounds that interpolated value:
  18797. *
  18798. * ```html
  18799. * <div>{{v0}}</div>
  18800. * ```
  18801. *
  18802. * Its compiled representation is:
  18803. *
  18804. * ```ts
  18805. * ɵɵtextInterpolate(v0);
  18806. * ```
  18807. * @returns itself, so that it may be chained.
  18808. * @see textInterpolateV
  18809. * @codeGenApi
  18810. */
  18811. function ɵɵtextInterpolate(v0) {
  18812. ɵɵtextInterpolate1('', v0, '');
  18813. return ɵɵtextInterpolate;
  18814. }
  18815. /**
  18816. *
  18817. * Update text content with single bound value surrounded by other text.
  18818. *
  18819. * Used when a text node has 1 interpolated value in it:
  18820. *
  18821. * ```html
  18822. * <div>prefix{{v0}}suffix</div>
  18823. * ```
  18824. *
  18825. * Its compiled representation is:
  18826. *
  18827. * ```ts
  18828. * ɵɵtextInterpolate1('prefix', v0, 'suffix');
  18829. * ```
  18830. * @returns itself, so that it may be chained.
  18831. * @see textInterpolateV
  18832. * @codeGenApi
  18833. */
  18834. function ɵɵtextInterpolate1(prefix, v0, suffix) {
  18835. const lView = getLView();
  18836. const interpolated = interpolation1(lView, prefix, v0, suffix);
  18837. if (interpolated !== NO_CHANGE) {
  18838. textBindingInternal(lView, getSelectedIndex(), interpolated);
  18839. }
  18840. return ɵɵtextInterpolate1;
  18841. }
  18842. /**
  18843. *
  18844. * Update text content with 2 bound values surrounded by other text.
  18845. *
  18846. * Used when a text node has 2 interpolated values in it:
  18847. *
  18848. * ```html
  18849. * <div>prefix{{v0}}-{{v1}}suffix</div>
  18850. * ```
  18851. *
  18852. * Its compiled representation is:
  18853. *
  18854. * ```ts
  18855. * ɵɵtextInterpolate2('prefix', v0, '-', v1, 'suffix');
  18856. * ```
  18857. * @returns itself, so that it may be chained.
  18858. * @see textInterpolateV
  18859. * @codeGenApi
  18860. */
  18861. function ɵɵtextInterpolate2(prefix, v0, i0, v1, suffix) {
  18862. const lView = getLView();
  18863. const interpolated = interpolation2(lView, prefix, v0, i0, v1, suffix);
  18864. if (interpolated !== NO_CHANGE) {
  18865. textBindingInternal(lView, getSelectedIndex(), interpolated);
  18866. }
  18867. return ɵɵtextInterpolate2;
  18868. }
  18869. /**
  18870. *
  18871. * Update text content with 3 bound values surrounded by other text.
  18872. *
  18873. * Used when a text node has 3 interpolated values in it:
  18874. *
  18875. * ```html
  18876. * <div>prefix{{v0}}-{{v1}}-{{v2}}suffix</div>
  18877. * ```
  18878. *
  18879. * Its compiled representation is:
  18880. *
  18881. * ```ts
  18882. * ɵɵtextInterpolate3(
  18883. * 'prefix', v0, '-', v1, '-', v2, 'suffix');
  18884. * ```
  18885. * @returns itself, so that it may be chained.
  18886. * @see textInterpolateV
  18887. * @codeGenApi
  18888. */
  18889. function ɵɵtextInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
  18890. const lView = getLView();
  18891. const interpolated = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  18892. if (interpolated !== NO_CHANGE) {
  18893. textBindingInternal(lView, getSelectedIndex(), interpolated);
  18894. }
  18895. return ɵɵtextInterpolate3;
  18896. }
  18897. /**
  18898. *
  18899. * Update text content with 4 bound values surrounded by other text.
  18900. *
  18901. * Used when a text node has 4 interpolated values in it:
  18902. *
  18903. * ```html
  18904. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix</div>
  18905. * ```
  18906. *
  18907. * Its compiled representation is:
  18908. *
  18909. * ```ts
  18910. * ɵɵtextInterpolate4(
  18911. * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  18912. * ```
  18913. * @returns itself, so that it may be chained.
  18914. * @see ɵɵtextInterpolateV
  18915. * @codeGenApi
  18916. */
  18917. function ɵɵtextInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  18918. const lView = getLView();
  18919. const interpolated = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  18920. if (interpolated !== NO_CHANGE) {
  18921. textBindingInternal(lView, getSelectedIndex(), interpolated);
  18922. }
  18923. return ɵɵtextInterpolate4;
  18924. }
  18925. /**
  18926. *
  18927. * Update text content with 5 bound values surrounded by other text.
  18928. *
  18929. * Used when a text node has 5 interpolated values in it:
  18930. *
  18931. * ```html
  18932. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix</div>
  18933. * ```
  18934. *
  18935. * Its compiled representation is:
  18936. *
  18937. * ```ts
  18938. * ɵɵtextInterpolate5(
  18939. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  18940. * ```
  18941. * @returns itself, so that it may be chained.
  18942. * @see textInterpolateV
  18943. * @codeGenApi
  18944. */
  18945. function ɵɵtextInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  18946. const lView = getLView();
  18947. const interpolated = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  18948. if (interpolated !== NO_CHANGE) {
  18949. textBindingInternal(lView, getSelectedIndex(), interpolated);
  18950. }
  18951. return ɵɵtextInterpolate5;
  18952. }
  18953. /**
  18954. *
  18955. * Update text content with 6 bound values surrounded by other text.
  18956. *
  18957. * Used when a text node has 6 interpolated values in it:
  18958. *
  18959. * ```html
  18960. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix</div>
  18961. * ```
  18962. *
  18963. * Its compiled representation is:
  18964. *
  18965. * ```ts
  18966. * ɵɵtextInterpolate6(
  18967. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  18968. * ```
  18969. *
  18970. * @param i4 Static value used for concatenation only.
  18971. * @param v5 Value checked for change. @returns itself, so that it may be chained.
  18972. * @see textInterpolateV
  18973. * @codeGenApi
  18974. */
  18975. function ɵɵtextInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  18976. const lView = getLView();
  18977. const interpolated = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  18978. if (interpolated !== NO_CHANGE) {
  18979. textBindingInternal(lView, getSelectedIndex(), interpolated);
  18980. }
  18981. return ɵɵtextInterpolate6;
  18982. }
  18983. /**
  18984. *
  18985. * Update text content with 7 bound values surrounded by other text.
  18986. *
  18987. * Used when a text node has 7 interpolated values in it:
  18988. *
  18989. * ```html
  18990. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix</div>
  18991. * ```
  18992. *
  18993. * Its compiled representation is:
  18994. *
  18995. * ```ts
  18996. * ɵɵtextInterpolate7(
  18997. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  18998. * ```
  18999. * @returns itself, so that it may be chained.
  19000. * @see textInterpolateV
  19001. * @codeGenApi
  19002. */
  19003. function ɵɵtextInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  19004. const lView = getLView();
  19005. const interpolated = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  19006. if (interpolated !== NO_CHANGE) {
  19007. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19008. }
  19009. return ɵɵtextInterpolate7;
  19010. }
  19011. /**
  19012. *
  19013. * Update text content with 8 bound values surrounded by other text.
  19014. *
  19015. * Used when a text node has 8 interpolated values in it:
  19016. *
  19017. * ```html
  19018. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix</div>
  19019. * ```
  19020. *
  19021. * Its compiled representation is:
  19022. *
  19023. * ```ts
  19024. * ɵɵtextInterpolate8(
  19025. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  19026. * ```
  19027. * @returns itself, so that it may be chained.
  19028. * @see textInterpolateV
  19029. * @codeGenApi
  19030. */
  19031. function ɵɵtextInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  19032. const lView = getLView();
  19033. const interpolated = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  19034. if (interpolated !== NO_CHANGE) {
  19035. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19036. }
  19037. return ɵɵtextInterpolate8;
  19038. }
  19039. /**
  19040. * Update text content with 9 or more bound values other surrounded by text.
  19041. *
  19042. * Used when the number of interpolated values exceeds 8.
  19043. *
  19044. * ```html
  19045. * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix</div>
  19046. * ```
  19047. *
  19048. * Its compiled representation is:
  19049. *
  19050. * ```ts
  19051. * ɵɵtextInterpolateV(
  19052. * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  19053. * 'suffix']);
  19054. * ```
  19055. *.
  19056. * @param values The collection of values and the strings in between those values, beginning with
  19057. * a string prefix and ending with a string suffix.
  19058. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  19059. *
  19060. * @returns itself, so that it may be chained.
  19061. * @codeGenApi
  19062. */
  19063. function ɵɵtextInterpolateV(values) {
  19064. const lView = getLView();
  19065. const interpolated = interpolationV(lView, values);
  19066. if (interpolated !== NO_CHANGE) {
  19067. textBindingInternal(lView, getSelectedIndex(), interpolated);
  19068. }
  19069. return ɵɵtextInterpolateV;
  19070. }
  19071. /**
  19072. *
  19073. * Update an interpolated class on an element with single bound value surrounded by text.
  19074. *
  19075. * Used when the value passed to a property has 1 interpolated value in it:
  19076. *
  19077. * ```html
  19078. * <div class="prefix{{v0}}suffix"></div>
  19079. * ```
  19080. *
  19081. * Its compiled representation is:
  19082. *
  19083. * ```ts
  19084. * ɵɵclassMapInterpolate1('prefix', v0, 'suffix');
  19085. * ```
  19086. *
  19087. * @param prefix Static value used for concatenation only.
  19088. * @param v0 Value checked for change.
  19089. * @param suffix Static value used for concatenation only.
  19090. * @codeGenApi
  19091. */
  19092. function ɵɵclassMapInterpolate1(prefix, v0, suffix) {
  19093. const lView = getLView();
  19094. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  19095. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19096. }
  19097. /**
  19098. *
  19099. * Update an interpolated class on an element with 2 bound values surrounded by text.
  19100. *
  19101. * Used when the value passed to a property has 2 interpolated values in it:
  19102. *
  19103. * ```html
  19104. * <div class="prefix{{v0}}-{{v1}}suffix"></div>
  19105. * ```
  19106. *
  19107. * Its compiled representation is:
  19108. *
  19109. * ```ts
  19110. * ɵɵclassMapInterpolate2('prefix', v0, '-', v1, 'suffix');
  19111. * ```
  19112. *
  19113. * @param prefix Static value used for concatenation only.
  19114. * @param v0 Value checked for change.
  19115. * @param i0 Static value used for concatenation only.
  19116. * @param v1 Value checked for change.
  19117. * @param suffix Static value used for concatenation only.
  19118. * @codeGenApi
  19119. */
  19120. function ɵɵclassMapInterpolate2(prefix, v0, i0, v1, suffix) {
  19121. const lView = getLView();
  19122. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  19123. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19124. }
  19125. /**
  19126. *
  19127. * Update an interpolated class on an element with 3 bound values surrounded by text.
  19128. *
  19129. * Used when the value passed to a property has 3 interpolated values in it:
  19130. *
  19131. * ```html
  19132. * <div class="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  19133. * ```
  19134. *
  19135. * Its compiled representation is:
  19136. *
  19137. * ```ts
  19138. * ɵɵclassMapInterpolate3(
  19139. * 'prefix', v0, '-', v1, '-', v2, 'suffix');
  19140. * ```
  19141. *
  19142. * @param prefix Static value used for concatenation only.
  19143. * @param v0 Value checked for change.
  19144. * @param i0 Static value used for concatenation only.
  19145. * @param v1 Value checked for change.
  19146. * @param i1 Static value used for concatenation only.
  19147. * @param v2 Value checked for change.
  19148. * @param suffix Static value used for concatenation only.
  19149. * @codeGenApi
  19150. */
  19151. function ɵɵclassMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
  19152. const lView = getLView();
  19153. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  19154. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19155. }
  19156. /**
  19157. *
  19158. * Update an interpolated class on an element with 4 bound values surrounded by text.
  19159. *
  19160. * Used when the value passed to a property has 4 interpolated values in it:
  19161. *
  19162. * ```html
  19163. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  19164. * ```
  19165. *
  19166. * Its compiled representation is:
  19167. *
  19168. * ```ts
  19169. * ɵɵclassMapInterpolate4(
  19170. * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  19171. * ```
  19172. *
  19173. * @param prefix Static value used for concatenation only.
  19174. * @param v0 Value checked for change.
  19175. * @param i0 Static value used for concatenation only.
  19176. * @param v1 Value checked for change.
  19177. * @param i1 Static value used for concatenation only.
  19178. * @param v2 Value checked for change.
  19179. * @param i2 Static value used for concatenation only.
  19180. * @param v3 Value checked for change.
  19181. * @param suffix Static value used for concatenation only.
  19182. * @codeGenApi
  19183. */
  19184. function ɵɵclassMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  19185. const lView = getLView();
  19186. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  19187. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19188. }
  19189. /**
  19190. *
  19191. * Update an interpolated class on an element with 5 bound values surrounded by text.
  19192. *
  19193. * Used when the value passed to a property has 5 interpolated values in it:
  19194. *
  19195. * ```html
  19196. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  19197. * ```
  19198. *
  19199. * Its compiled representation is:
  19200. *
  19201. * ```ts
  19202. * ɵɵclassMapInterpolate5(
  19203. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  19204. * ```
  19205. *
  19206. * @param prefix Static value used for concatenation only.
  19207. * @param v0 Value checked for change.
  19208. * @param i0 Static value used for concatenation only.
  19209. * @param v1 Value checked for change.
  19210. * @param i1 Static value used for concatenation only.
  19211. * @param v2 Value checked for change.
  19212. * @param i2 Static value used for concatenation only.
  19213. * @param v3 Value checked for change.
  19214. * @param i3 Static value used for concatenation only.
  19215. * @param v4 Value checked for change.
  19216. * @param suffix Static value used for concatenation only.
  19217. * @codeGenApi
  19218. */
  19219. function ɵɵclassMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  19220. const lView = getLView();
  19221. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  19222. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19223. }
  19224. /**
  19225. *
  19226. * Update an interpolated class on an element with 6 bound values surrounded by text.
  19227. *
  19228. * Used when the value passed to a property has 6 interpolated values in it:
  19229. *
  19230. * ```html
  19231. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  19232. * ```
  19233. *
  19234. * Its compiled representation is:
  19235. *
  19236. * ```ts
  19237. * ɵɵclassMapInterpolate6(
  19238. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  19239. * ```
  19240. *
  19241. * @param prefix Static value used for concatenation only.
  19242. * @param v0 Value checked for change.
  19243. * @param i0 Static value used for concatenation only.
  19244. * @param v1 Value checked for change.
  19245. * @param i1 Static value used for concatenation only.
  19246. * @param v2 Value checked for change.
  19247. * @param i2 Static value used for concatenation only.
  19248. * @param v3 Value checked for change.
  19249. * @param i3 Static value used for concatenation only.
  19250. * @param v4 Value checked for change.
  19251. * @param i4 Static value used for concatenation only.
  19252. * @param v5 Value checked for change.
  19253. * @param suffix Static value used for concatenation only.
  19254. * @codeGenApi
  19255. */
  19256. function ɵɵclassMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  19257. const lView = getLView();
  19258. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  19259. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19260. }
  19261. /**
  19262. *
  19263. * Update an interpolated class on an element with 7 bound values surrounded by text.
  19264. *
  19265. * Used when the value passed to a property has 7 interpolated values in it:
  19266. *
  19267. * ```html
  19268. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  19269. * ```
  19270. *
  19271. * Its compiled representation is:
  19272. *
  19273. * ```ts
  19274. * ɵɵclassMapInterpolate7(
  19275. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  19276. * ```
  19277. *
  19278. * @param prefix Static value used for concatenation only.
  19279. * @param v0 Value checked for change.
  19280. * @param i0 Static value used for concatenation only.
  19281. * @param v1 Value checked for change.
  19282. * @param i1 Static value used for concatenation only.
  19283. * @param v2 Value checked for change.
  19284. * @param i2 Static value used for concatenation only.
  19285. * @param v3 Value checked for change.
  19286. * @param i3 Static value used for concatenation only.
  19287. * @param v4 Value checked for change.
  19288. * @param i4 Static value used for concatenation only.
  19289. * @param v5 Value checked for change.
  19290. * @param i5 Static value used for concatenation only.
  19291. * @param v6 Value checked for change.
  19292. * @param suffix Static value used for concatenation only.
  19293. * @codeGenApi
  19294. */
  19295. function ɵɵclassMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  19296. const lView = getLView();
  19297. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  19298. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19299. }
  19300. /**
  19301. *
  19302. * Update an interpolated class on an element with 8 bound values surrounded by text.
  19303. *
  19304. * Used when the value passed to a property has 8 interpolated values in it:
  19305. *
  19306. * ```html
  19307. * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  19308. * ```
  19309. *
  19310. * Its compiled representation is:
  19311. *
  19312. * ```ts
  19313. * ɵɵclassMapInterpolate8(
  19314. * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
  19315. * ```
  19316. *
  19317. * @param prefix Static value used for concatenation only.
  19318. * @param v0 Value checked for change.
  19319. * @param i0 Static value used for concatenation only.
  19320. * @param v1 Value checked for change.
  19321. * @param i1 Static value used for concatenation only.
  19322. * @param v2 Value checked for change.
  19323. * @param i2 Static value used for concatenation only.
  19324. * @param v3 Value checked for change.
  19325. * @param i3 Static value used for concatenation only.
  19326. * @param v4 Value checked for change.
  19327. * @param i4 Static value used for concatenation only.
  19328. * @param v5 Value checked for change.
  19329. * @param i5 Static value used for concatenation only.
  19330. * @param v6 Value checked for change.
  19331. * @param i6 Static value used for concatenation only.
  19332. * @param v7 Value checked for change.
  19333. * @param suffix Static value used for concatenation only.
  19334. * @codeGenApi
  19335. */
  19336. function ɵɵclassMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  19337. const lView = getLView();
  19338. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  19339. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19340. }
  19341. /**
  19342. * Update an interpolated class on an element with 9 or more bound values surrounded by text.
  19343. *
  19344. * Used when the number of interpolated values exceeds 8.
  19345. *
  19346. * ```html
  19347. * <div
  19348. * class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
  19349. * ```
  19350. *
  19351. * Its compiled representation is:
  19352. *
  19353. * ```ts
  19354. * ɵɵclassMapInterpolateV(
  19355. * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  19356. * 'suffix']);
  19357. * ```
  19358. *.
  19359. * @param values The collection of values and the strings in-between those values, beginning with
  19360. * a string prefix and ending with a string suffix.
  19361. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  19362. * @codeGenApi
  19363. */
  19364. function ɵɵclassMapInterpolateV(values) {
  19365. const lView = getLView();
  19366. const interpolatedValue = interpolationV(lView, values);
  19367. checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
  19368. }
  19369. /**
  19370. *
  19371. * Update an interpolated style on an element with single bound value surrounded by text.
  19372. *
  19373. * Used when the value passed to a property has 1 interpolated value in it:
  19374. *
  19375. * ```html
  19376. * <div style="key: {{v0}}suffix"></div>
  19377. * ```
  19378. *
  19379. * Its compiled representation is:
  19380. *
  19381. * ```ts
  19382. * ɵɵstyleMapInterpolate1('key: ', v0, 'suffix');
  19383. * ```
  19384. *
  19385. * @param prefix Static value used for concatenation only.
  19386. * @param v0 Value checked for change.
  19387. * @param suffix Static value used for concatenation only.
  19388. * @codeGenApi
  19389. */
  19390. function ɵɵstyleMapInterpolate1(prefix, v0, suffix) {
  19391. const lView = getLView();
  19392. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  19393. ɵɵstyleMap(interpolatedValue);
  19394. }
  19395. /**
  19396. *
  19397. * Update an interpolated style on an element with 2 bound values surrounded by text.
  19398. *
  19399. * Used when the value passed to a property has 2 interpolated values in it:
  19400. *
  19401. * ```html
  19402. * <div style="key: {{v0}}; key1: {{v1}}suffix"></div>
  19403. * ```
  19404. *
  19405. * Its compiled representation is:
  19406. *
  19407. * ```ts
  19408. * ɵɵstyleMapInterpolate2('key: ', v0, '; key1: ', v1, 'suffix');
  19409. * ```
  19410. *
  19411. * @param prefix Static value used for concatenation only.
  19412. * @param v0 Value checked for change.
  19413. * @param i0 Static value used for concatenation only.
  19414. * @param v1 Value checked for change.
  19415. * @param suffix Static value used for concatenation only.
  19416. * @codeGenApi
  19417. */
  19418. function ɵɵstyleMapInterpolate2(prefix, v0, i0, v1, suffix) {
  19419. const lView = getLView();
  19420. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  19421. ɵɵstyleMap(interpolatedValue);
  19422. }
  19423. /**
  19424. *
  19425. * Update an interpolated style on an element with 3 bound values surrounded by text.
  19426. *
  19427. * Used when the value passed to a property has 3 interpolated values in it:
  19428. *
  19429. * ```html
  19430. * <div style="key: {{v0}}; key2: {{v1}}; key2: {{v2}}suffix"></div>
  19431. * ```
  19432. *
  19433. * Its compiled representation is:
  19434. *
  19435. * ```ts
  19436. * ɵɵstyleMapInterpolate3(
  19437. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, 'suffix');
  19438. * ```
  19439. *
  19440. * @param prefix Static value used for concatenation only.
  19441. * @param v0 Value checked for change.
  19442. * @param i0 Static value used for concatenation only.
  19443. * @param v1 Value checked for change.
  19444. * @param i1 Static value used for concatenation only.
  19445. * @param v2 Value checked for change.
  19446. * @param suffix Static value used for concatenation only.
  19447. * @codeGenApi
  19448. */
  19449. function ɵɵstyleMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
  19450. const lView = getLView();
  19451. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  19452. ɵɵstyleMap(interpolatedValue);
  19453. }
  19454. /**
  19455. *
  19456. * Update an interpolated style on an element with 4 bound values surrounded by text.
  19457. *
  19458. * Used when the value passed to a property has 4 interpolated values in it:
  19459. *
  19460. * ```html
  19461. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}suffix"></div>
  19462. * ```
  19463. *
  19464. * Its compiled representation is:
  19465. *
  19466. * ```ts
  19467. * ɵɵstyleMapInterpolate4(
  19468. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, 'suffix');
  19469. * ```
  19470. *
  19471. * @param prefix Static value used for concatenation only.
  19472. * @param v0 Value checked for change.
  19473. * @param i0 Static value used for concatenation only.
  19474. * @param v1 Value checked for change.
  19475. * @param i1 Static value used for concatenation only.
  19476. * @param v2 Value checked for change.
  19477. * @param i2 Static value used for concatenation only.
  19478. * @param v3 Value checked for change.
  19479. * @param suffix Static value used for concatenation only.
  19480. * @codeGenApi
  19481. */
  19482. function ɵɵstyleMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
  19483. const lView = getLView();
  19484. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  19485. ɵɵstyleMap(interpolatedValue);
  19486. }
  19487. /**
  19488. *
  19489. * Update an interpolated style on an element with 5 bound values surrounded by text.
  19490. *
  19491. * Used when the value passed to a property has 5 interpolated values in it:
  19492. *
  19493. * ```html
  19494. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}suffix"></div>
  19495. * ```
  19496. *
  19497. * Its compiled representation is:
  19498. *
  19499. * ```ts
  19500. * ɵɵstyleMapInterpolate5(
  19501. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, 'suffix');
  19502. * ```
  19503. *
  19504. * @param prefix Static value used for concatenation only.
  19505. * @param v0 Value checked for change.
  19506. * @param i0 Static value used for concatenation only.
  19507. * @param v1 Value checked for change.
  19508. * @param i1 Static value used for concatenation only.
  19509. * @param v2 Value checked for change.
  19510. * @param i2 Static value used for concatenation only.
  19511. * @param v3 Value checked for change.
  19512. * @param i3 Static value used for concatenation only.
  19513. * @param v4 Value checked for change.
  19514. * @param suffix Static value used for concatenation only.
  19515. * @codeGenApi
  19516. */
  19517. function ɵɵstyleMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
  19518. const lView = getLView();
  19519. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  19520. ɵɵstyleMap(interpolatedValue);
  19521. }
  19522. /**
  19523. *
  19524. * Update an interpolated style on an element with 6 bound values surrounded by text.
  19525. *
  19526. * Used when the value passed to a property has 6 interpolated values in it:
  19527. *
  19528. * ```html
  19529. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}};
  19530. * key5: {{v5}}suffix"></div>
  19531. * ```
  19532. *
  19533. * Its compiled representation is:
  19534. *
  19535. * ```ts
  19536. * ɵɵstyleMapInterpolate6(
  19537. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  19538. * 'suffix');
  19539. * ```
  19540. *
  19541. * @param prefix Static value used for concatenation only.
  19542. * @param v0 Value checked for change.
  19543. * @param i0 Static value used for concatenation only.
  19544. * @param v1 Value checked for change.
  19545. * @param i1 Static value used for concatenation only.
  19546. * @param v2 Value checked for change.
  19547. * @param i2 Static value used for concatenation only.
  19548. * @param v3 Value checked for change.
  19549. * @param i3 Static value used for concatenation only.
  19550. * @param v4 Value checked for change.
  19551. * @param i4 Static value used for concatenation only.
  19552. * @param v5 Value checked for change.
  19553. * @param suffix Static value used for concatenation only.
  19554. * @codeGenApi
  19555. */
  19556. function ɵɵstyleMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
  19557. const lView = getLView();
  19558. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  19559. ɵɵstyleMap(interpolatedValue);
  19560. }
  19561. /**
  19562. *
  19563. * Update an interpolated style on an element with 7 bound values surrounded by text.
  19564. *
  19565. * Used when the value passed to a property has 7 interpolated values in it:
  19566. *
  19567. * ```html
  19568. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
  19569. * key6: {{v6}}suffix"></div>
  19570. * ```
  19571. *
  19572. * Its compiled representation is:
  19573. *
  19574. * ```ts
  19575. * ɵɵstyleMapInterpolate7(
  19576. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  19577. * '; key6: ', v6, 'suffix');
  19578. * ```
  19579. *
  19580. * @param prefix Static value used for concatenation only.
  19581. * @param v0 Value checked for change.
  19582. * @param i0 Static value used for concatenation only.
  19583. * @param v1 Value checked for change.
  19584. * @param i1 Static value used for concatenation only.
  19585. * @param v2 Value checked for change.
  19586. * @param i2 Static value used for concatenation only.
  19587. * @param v3 Value checked for change.
  19588. * @param i3 Static value used for concatenation only.
  19589. * @param v4 Value checked for change.
  19590. * @param i4 Static value used for concatenation only.
  19591. * @param v5 Value checked for change.
  19592. * @param i5 Static value used for concatenation only.
  19593. * @param v6 Value checked for change.
  19594. * @param suffix Static value used for concatenation only.
  19595. * @codeGenApi
  19596. */
  19597. function ɵɵstyleMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
  19598. const lView = getLView();
  19599. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  19600. ɵɵstyleMap(interpolatedValue);
  19601. }
  19602. /**
  19603. *
  19604. * Update an interpolated style on an element with 8 bound values surrounded by text.
  19605. *
  19606. * Used when the value passed to a property has 8 interpolated values in it:
  19607. *
  19608. * ```html
  19609. * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
  19610. * key6: {{v6}}; key7: {{v7}}suffix"></div>
  19611. * ```
  19612. *
  19613. * Its compiled representation is:
  19614. *
  19615. * ```ts
  19616. * ɵɵstyleMapInterpolate8(
  19617. * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  19618. * '; key6: ', v6, '; key7: ', v7, '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 i6 Static value used for concatenation only.
  19636. * @param v7 Value checked for change.
  19637. * @param suffix Static value used for concatenation only.
  19638. * @codeGenApi
  19639. */
  19640. function ɵɵstyleMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
  19641. const lView = getLView();
  19642. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  19643. ɵɵstyleMap(interpolatedValue);
  19644. }
  19645. /**
  19646. * Update an interpolated style on an element with 9 or more bound values surrounded by text.
  19647. *
  19648. * Used when the number of interpolated values exceeds 8.
  19649. *
  19650. * ```html
  19651. * <div
  19652. * class="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
  19653. * key6: {{v6}}; key7: {{v7}}; key8: {{v8}}; key9: {{v9}}suffix"></div>
  19654. * ```
  19655. *
  19656. * Its compiled representation is:
  19657. *
  19658. * ```ts
  19659. * ɵɵstyleMapInterpolateV(
  19660. * ['key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
  19661. * '; key6: ', v6, '; key7: ', v7, '; key8: ', v8, '; key9: ', v9, 'suffix']);
  19662. * ```
  19663. *.
  19664. * @param values The collection of values and the strings in-between those values, beginning with
  19665. * a string prefix and ending with a string suffix.
  19666. * (e.g. `['prefix', value0, '; key2: ', value1, '; key2: ', value2, ..., value99, 'suffix']`)
  19667. * @codeGenApi
  19668. */
  19669. function ɵɵstyleMapInterpolateV(values) {
  19670. const lView = getLView();
  19671. const interpolatedValue = interpolationV(lView, values);
  19672. ɵɵstyleMap(interpolatedValue);
  19673. }
  19674. /**
  19675. *
  19676. * Update an interpolated style property on an element with single bound value surrounded by text.
  19677. *
  19678. * Used when the value passed to a property has 1 interpolated value in it:
  19679. *
  19680. * ```html
  19681. * <div style.color="prefix{{v0}}suffix"></div>
  19682. * ```
  19683. *
  19684. * Its compiled representation is:
  19685. *
  19686. * ```ts
  19687. * ɵɵstylePropInterpolate1(0, 'prefix', v0, 'suffix');
  19688. * ```
  19689. *
  19690. * @param styleIndex Index of style to update. This index value refers to the
  19691. * index of the style in the style bindings array that was passed into
  19692. * `styling`.
  19693. * @param prefix Static value used for concatenation only.
  19694. * @param v0 Value checked for change.
  19695. * @param suffix Static value used for concatenation only.
  19696. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19697. * @returns itself, so that it may be chained.
  19698. * @codeGenApi
  19699. */
  19700. function ɵɵstylePropInterpolate1(prop, prefix, v0, suffix, valueSuffix) {
  19701. const lView = getLView();
  19702. const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
  19703. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19704. return ɵɵstylePropInterpolate1;
  19705. }
  19706. /**
  19707. *
  19708. * Update an interpolated style property on an element with 2 bound values surrounded by text.
  19709. *
  19710. * Used when the value passed to a property has 2 interpolated values in it:
  19711. *
  19712. * ```html
  19713. * <div style.color="prefix{{v0}}-{{v1}}suffix"></div>
  19714. * ```
  19715. *
  19716. * Its compiled representation is:
  19717. *
  19718. * ```ts
  19719. * ɵɵstylePropInterpolate2(0, 'prefix', v0, '-', v1, 'suffix');
  19720. * ```
  19721. *
  19722. * @param styleIndex Index of style to update. This index value refers to the
  19723. * index of the style in the style bindings array that was passed into
  19724. * `styling`.
  19725. * @param prefix Static value used for concatenation only.
  19726. * @param v0 Value checked for change.
  19727. * @param i0 Static value used for concatenation only.
  19728. * @param v1 Value checked for change.
  19729. * @param suffix Static value used for concatenation only.
  19730. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19731. * @returns itself, so that it may be chained.
  19732. * @codeGenApi
  19733. */
  19734. function ɵɵstylePropInterpolate2(prop, prefix, v0, i0, v1, suffix, valueSuffix) {
  19735. const lView = getLView();
  19736. const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
  19737. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19738. return ɵɵstylePropInterpolate2;
  19739. }
  19740. /**
  19741. *
  19742. * Update an interpolated style property on an element with 3 bound values surrounded by text.
  19743. *
  19744. * Used when the value passed to a property has 3 interpolated values in it:
  19745. *
  19746. * ```html
  19747. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
  19748. * ```
  19749. *
  19750. * Its compiled representation is:
  19751. *
  19752. * ```ts
  19753. * ɵɵstylePropInterpolate3(0, 'prefix', v0, '-', v1, '-', v2, 'suffix');
  19754. * ```
  19755. *
  19756. * @param styleIndex Index of style to update. This index value refers to the
  19757. * index of the style in the style bindings array that was passed into
  19758. * `styling`.
  19759. * @param prefix Static value used for concatenation only.
  19760. * @param v0 Value checked for change.
  19761. * @param i0 Static value used for concatenation only.
  19762. * @param v1 Value checked for change.
  19763. * @param i1 Static value used for concatenation only.
  19764. * @param v2 Value checked for change.
  19765. * @param suffix Static value used for concatenation only.
  19766. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19767. * @returns itself, so that it may be chained.
  19768. * @codeGenApi
  19769. */
  19770. function ɵɵstylePropInterpolate3(prop, prefix, v0, i0, v1, i1, v2, suffix, valueSuffix) {
  19771. const lView = getLView();
  19772. const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
  19773. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19774. return ɵɵstylePropInterpolate3;
  19775. }
  19776. /**
  19777. *
  19778. * Update an interpolated style property on an element with 4 bound values surrounded by text.
  19779. *
  19780. * Used when the value passed to a property has 4 interpolated values in it:
  19781. *
  19782. * ```html
  19783. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
  19784. * ```
  19785. *
  19786. * Its compiled representation is:
  19787. *
  19788. * ```ts
  19789. * ɵɵstylePropInterpolate4(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
  19790. * ```
  19791. *
  19792. * @param styleIndex Index of style to update. This index value refers to the
  19793. * index of the style in the style bindings array that was passed into
  19794. * `styling`.
  19795. * @param prefix Static value used for concatenation only.
  19796. * @param v0 Value checked for change.
  19797. * @param i0 Static value used for concatenation only.
  19798. * @param v1 Value checked for change.
  19799. * @param i1 Static value used for concatenation only.
  19800. * @param v2 Value checked for change.
  19801. * @param i2 Static value used for concatenation only.
  19802. * @param v3 Value checked for change.
  19803. * @param suffix Static value used for concatenation only.
  19804. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19805. * @returns itself, so that it may be chained.
  19806. * @codeGenApi
  19807. */
  19808. function ɵɵstylePropInterpolate4(prop, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, valueSuffix) {
  19809. const lView = getLView();
  19810. const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
  19811. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19812. return ɵɵstylePropInterpolate4;
  19813. }
  19814. /**
  19815. *
  19816. * Update an interpolated style property on an element with 5 bound values surrounded by text.
  19817. *
  19818. * Used when the value passed to a property has 5 interpolated values in it:
  19819. *
  19820. * ```html
  19821. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
  19822. * ```
  19823. *
  19824. * Its compiled representation is:
  19825. *
  19826. * ```ts
  19827. * ɵɵstylePropInterpolate5(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
  19828. * ```
  19829. *
  19830. * @param styleIndex Index of style to update. This index value refers to the
  19831. * index of the style in the style bindings array that was passed into
  19832. * `styling`.
  19833. * @param prefix Static value used for concatenation only.
  19834. * @param v0 Value checked for change.
  19835. * @param i0 Static value used for concatenation only.
  19836. * @param v1 Value checked for change.
  19837. * @param i1 Static value used for concatenation only.
  19838. * @param v2 Value checked for change.
  19839. * @param i2 Static value used for concatenation only.
  19840. * @param v3 Value checked for change.
  19841. * @param i3 Static value used for concatenation only.
  19842. * @param v4 Value checked for change.
  19843. * @param suffix Static value used for concatenation only.
  19844. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19845. * @returns itself, so that it may be chained.
  19846. * @codeGenApi
  19847. */
  19848. function ɵɵstylePropInterpolate5(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, valueSuffix) {
  19849. const lView = getLView();
  19850. const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
  19851. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19852. return ɵɵstylePropInterpolate5;
  19853. }
  19854. /**
  19855. *
  19856. * Update an interpolated style property on an element with 6 bound values surrounded by text.
  19857. *
  19858. * Used when the value passed to a property has 6 interpolated values in it:
  19859. *
  19860. * ```html
  19861. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
  19862. * ```
  19863. *
  19864. * Its compiled representation is:
  19865. *
  19866. * ```ts
  19867. * ɵɵstylePropInterpolate6(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
  19868. * ```
  19869. *
  19870. * @param styleIndex Index of style to update. This index value refers to the
  19871. * index of the style in the style bindings array that was passed into
  19872. * `styling`.
  19873. * @param prefix Static value used for concatenation only.
  19874. * @param v0 Value checked for change.
  19875. * @param i0 Static value used for concatenation only.
  19876. * @param v1 Value checked for change.
  19877. * @param i1 Static value used for concatenation only.
  19878. * @param v2 Value checked for change.
  19879. * @param i2 Static value used for concatenation only.
  19880. * @param v3 Value checked for change.
  19881. * @param i3 Static value used for concatenation only.
  19882. * @param v4 Value checked for change.
  19883. * @param i4 Static value used for concatenation only.
  19884. * @param v5 Value checked for change.
  19885. * @param suffix Static value used for concatenation only.
  19886. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19887. * @returns itself, so that it may be chained.
  19888. * @codeGenApi
  19889. */
  19890. function ɵɵstylePropInterpolate6(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, valueSuffix) {
  19891. const lView = getLView();
  19892. const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
  19893. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19894. return ɵɵstylePropInterpolate6;
  19895. }
  19896. /**
  19897. *
  19898. * Update an interpolated style property on an element with 7 bound values surrounded by text.
  19899. *
  19900. * Used when the value passed to a property has 7 interpolated values in it:
  19901. *
  19902. * ```html
  19903. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
  19904. * ```
  19905. *
  19906. * Its compiled representation is:
  19907. *
  19908. * ```ts
  19909. * ɵɵstylePropInterpolate7(
  19910. * 0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
  19911. * ```
  19912. *
  19913. * @param styleIndex Index of style to update. This index value refers to the
  19914. * index of the style in the style bindings array that was passed into
  19915. * `styling`.
  19916. * @param prefix Static value used for concatenation only.
  19917. * @param v0 Value checked for change.
  19918. * @param i0 Static value used for concatenation only.
  19919. * @param v1 Value checked for change.
  19920. * @param i1 Static value used for concatenation only.
  19921. * @param v2 Value checked for change.
  19922. * @param i2 Static value used for concatenation only.
  19923. * @param v3 Value checked for change.
  19924. * @param i3 Static value used for concatenation only.
  19925. * @param v4 Value checked for change.
  19926. * @param i4 Static value used for concatenation only.
  19927. * @param v5 Value checked for change.
  19928. * @param i5 Static value used for concatenation only.
  19929. * @param v6 Value checked for change.
  19930. * @param suffix Static value used for concatenation only.
  19931. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19932. * @returns itself, so that it may be chained.
  19933. * @codeGenApi
  19934. */
  19935. function ɵɵstylePropInterpolate7(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, valueSuffix) {
  19936. const lView = getLView();
  19937. const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
  19938. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19939. return ɵɵstylePropInterpolate7;
  19940. }
  19941. /**
  19942. *
  19943. * Update an interpolated style property on an element with 8 bound values surrounded by text.
  19944. *
  19945. * Used when the value passed to a property has 8 interpolated values in it:
  19946. *
  19947. * ```html
  19948. * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
  19949. * ```
  19950. *
  19951. * Its compiled representation is:
  19952. *
  19953. * ```ts
  19954. * ɵɵstylePropInterpolate8(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6,
  19955. * '-', v7, 'suffix');
  19956. * ```
  19957. *
  19958. * @param styleIndex Index of style to update. This index value refers to the
  19959. * index of the style in the style bindings array that was passed into
  19960. * `styling`.
  19961. * @param prefix Static value used for concatenation only.
  19962. * @param v0 Value checked for change.
  19963. * @param i0 Static value used for concatenation only.
  19964. * @param v1 Value checked for change.
  19965. * @param i1 Static value used for concatenation only.
  19966. * @param v2 Value checked for change.
  19967. * @param i2 Static value used for concatenation only.
  19968. * @param v3 Value checked for change.
  19969. * @param i3 Static value used for concatenation only.
  19970. * @param v4 Value checked for change.
  19971. * @param i4 Static value used for concatenation only.
  19972. * @param v5 Value checked for change.
  19973. * @param i5 Static value used for concatenation only.
  19974. * @param v6 Value checked for change.
  19975. * @param i6 Static value used for concatenation only.
  19976. * @param v7 Value checked for change.
  19977. * @param suffix Static value used for concatenation only.
  19978. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  19979. * @returns itself, so that it may be chained.
  19980. * @codeGenApi
  19981. */
  19982. function ɵɵstylePropInterpolate8(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, valueSuffix) {
  19983. const lView = getLView();
  19984. const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
  19985. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  19986. return ɵɵstylePropInterpolate8;
  19987. }
  19988. /**
  19989. * Update an interpolated style property on an element with 9 or more bound values surrounded by
  19990. * text.
  19991. *
  19992. * Used when the number of interpolated values exceeds 8.
  19993. *
  19994. * ```html
  19995. * <div
  19996. * style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix">
  19997. * </div>
  19998. * ```
  19999. *
  20000. * Its compiled representation is:
  20001. *
  20002. * ```ts
  20003. * ɵɵstylePropInterpolateV(
  20004. * 0, ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
  20005. * 'suffix']);
  20006. * ```
  20007. *
  20008. * @param styleIndex Index of style to update. This index value refers to the
  20009. * index of the style in the style bindings array that was passed into
  20010. * `styling`..
  20011. * @param values The collection of values and the strings in-between those values, beginning with
  20012. * a string prefix and ending with a string suffix.
  20013. * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
  20014. * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
  20015. * @returns itself, so that it may be chained.
  20016. * @codeGenApi
  20017. */
  20018. function ɵɵstylePropInterpolateV(prop, values, valueSuffix) {
  20019. const lView = getLView();
  20020. const interpolatedValue = interpolationV(lView, values);
  20021. checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
  20022. return ɵɵstylePropInterpolateV;
  20023. }
  20024. /**
  20025. * Update a property on a host element. Only applies to native node properties, not inputs.
  20026. *
  20027. * Operates on the element selected by index via the {@link select} instruction.
  20028. *
  20029. * @param propName Name of property. Because it is going to DOM, this is not subject to
  20030. * renaming as part of minification.
  20031. * @param value New value to write.
  20032. * @param sanitizer An optional function used to sanitize the value.
  20033. * @returns This function returns itself so that it may be chained
  20034. * (e.g. `property('name', ctx.name)('title', ctx.title)`)
  20035. *
  20036. * @codeGenApi
  20037. */
  20038. function ɵɵhostProperty(propName, value, sanitizer) {
  20039. const lView = getLView();
  20040. const bindingIndex = nextBindingIndex();
  20041. if (bindingUpdated(lView, bindingIndex, value)) {
  20042. const tView = getTView();
  20043. const tNode = getSelectedTNode();
  20044. elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, true);
  20045. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
  20046. }
  20047. return ɵɵhostProperty;
  20048. }
  20049. /**
  20050. * Updates a synthetic host binding (e.g. `[@foo]`) on a component or directive.
  20051. *
  20052. * This instruction is for compatibility purposes and is designed to ensure that a
  20053. * synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
  20054. * the component's renderer. Normally all host bindings are evaluated with the parent
  20055. * component's renderer, but, in the case of animation @triggers, they need to be
  20056. * evaluated with the sub component's renderer (because that's where the animation
  20057. * triggers are defined).
  20058. *
  20059. * Do not use this instruction as a replacement for `elementProperty`. This instruction
  20060. * only exists to ensure compatibility with the ViewEngine's host binding behavior.
  20061. *
  20062. * @param index The index of the element to update in the data array
  20063. * @param propName Name of property. Because it is going to DOM, this is not subject to
  20064. * renaming as part of minification.
  20065. * @param value New value to write.
  20066. * @param sanitizer An optional function used to sanitize the value.
  20067. *
  20068. * @codeGenApi
  20069. */
  20070. function ɵɵsyntheticHostProperty(propName, value, sanitizer) {
  20071. const lView = getLView();
  20072. const bindingIndex = nextBindingIndex();
  20073. if (bindingUpdated(lView, bindingIndex, value)) {
  20074. const tView = getTView();
  20075. const tNode = getSelectedTNode();
  20076. const currentDef = getCurrentDirectiveDef(tView.data);
  20077. const renderer = loadComponentRenderer(currentDef, tNode, lView);
  20078. elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, true);
  20079. ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
  20080. }
  20081. return ɵɵsyntheticHostProperty;
  20082. }
  20083. /**
  20084. * NOTE: changes to the `ngI18nClosureMode` name must be synced with `compiler-cli/src/tooling.ts`.
  20085. */
  20086. if (typeof ngI18nClosureMode === 'undefined') {
  20087. // These property accesses can be ignored because ngI18nClosureMode will be set to false
  20088. // when optimizing code and the whole if statement will be dropped.
  20089. // Make sure to refer to ngI18nClosureMode as ['ngI18nClosureMode'] for closure.
  20090. // NOTE: we need to have it in IIFE so that the tree-shaker is happy.
  20091. (function () {
  20092. // tslint:disable-next-line:no-toplevel-property-access
  20093. _global['ngI18nClosureMode'] =
  20094. // TODO(FW-1250): validate that this actually, you know, works.
  20095. // tslint:disable-next-line:no-toplevel-property-access
  20096. typeof goog !== 'undefined' && typeof goog.getMsg === 'function';
  20097. })();
  20098. }
  20099. // THIS CODE IS GENERATED - DO NOT MODIFY.
  20100. const u = undefined;
  20101. function plural(val) {
  20102. const n = val, i = Math.floor(Math.abs(val)), v = val.toString().replace(/^[^.]*\.?/, '').length;
  20103. if (i === 1 && v === 0)
  20104. return 1;
  20105. return 5;
  20106. }
  20107. 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];
  20108. /**
  20109. * This const is used to store the locale data registered with `registerLocaleData`
  20110. */
  20111. let LOCALE_DATA = {};
  20112. /**
  20113. * Register locale data to be used internally by Angular. See the
  20114. * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale
  20115. * data.
  20116. *
  20117. * The signature `registerLocaleData(data: any, extraData?: any)` is deprecated since v5.1
  20118. */
  20119. function registerLocaleData(data, localeId, extraData) {
  20120. if (typeof localeId !== 'string') {
  20121. extraData = localeId;
  20122. localeId = data[LocaleDataIndex.LocaleId];
  20123. }
  20124. localeId = localeId.toLowerCase().replace(/_/g, '-');
  20125. LOCALE_DATA[localeId] = data;
  20126. if (extraData) {
  20127. LOCALE_DATA[localeId][LocaleDataIndex.ExtraData] = extraData;
  20128. }
  20129. }
  20130. /**
  20131. * Finds the locale data for a given locale.
  20132. *
  20133. * @param locale The locale code.
  20134. * @returns The locale data.
  20135. * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
  20136. */
  20137. function findLocaleData(locale) {
  20138. const normalizedLocale = normalizeLocale(locale);
  20139. let match = getLocaleData(normalizedLocale);
  20140. if (match) {
  20141. return match;
  20142. }
  20143. // let's try to find a parent locale
  20144. const parentLocale = normalizedLocale.split('-')[0];
  20145. match = getLocaleData(parentLocale);
  20146. if (match) {
  20147. return match;
  20148. }
  20149. if (parentLocale === 'en') {
  20150. return localeEn;
  20151. }
  20152. throw new RuntimeError(701 /* RuntimeErrorCode.MISSING_LOCALE_DATA */, ngDevMode && `Missing locale data for the locale "${locale}".`);
  20153. }
  20154. /**
  20155. * Retrieves the default currency code for the given locale.
  20156. *
  20157. * The default is defined as the first currency which is still in use.
  20158. *
  20159. * @param locale The code of the locale whose currency code we want.
  20160. * @returns The code of the default currency for the given locale.
  20161. *
  20162. */
  20163. function getLocaleCurrencyCode(locale) {
  20164. const data = findLocaleData(locale);
  20165. return data[LocaleDataIndex.CurrencyCode] || null;
  20166. }
  20167. /**
  20168. * Retrieves the plural function used by ICU expressions to determine the plural case to use
  20169. * for a given locale.
  20170. * @param locale A locale code for the locale format rules to use.
  20171. * @returns The plural function for the locale.
  20172. * @see {@link NgPlural}
  20173. * @see [Internationalization (i18n) Guide](/guide/i18n-overview)
  20174. */
  20175. function getLocalePluralCase(locale) {
  20176. const data = findLocaleData(locale);
  20177. return data[LocaleDataIndex.PluralCase];
  20178. }
  20179. /**
  20180. * Helper function to get the given `normalizedLocale` from `LOCALE_DATA`
  20181. * or from the global `ng.common.locale`.
  20182. */
  20183. function getLocaleData(normalizedLocale) {
  20184. if (!(normalizedLocale in LOCALE_DATA)) {
  20185. LOCALE_DATA[normalizedLocale] = _global.ng && _global.ng.common && _global.ng.common.locales &&
  20186. _global.ng.common.locales[normalizedLocale];
  20187. }
  20188. return LOCALE_DATA[normalizedLocale];
  20189. }
  20190. /**
  20191. * Helper function to remove all the locale data from `LOCALE_DATA`.
  20192. */
  20193. function unregisterAllLocaleData() {
  20194. LOCALE_DATA = {};
  20195. }
  20196. /**
  20197. * Index of each type of locale data from the locale data array
  20198. */
  20199. var LocaleDataIndex;
  20200. (function (LocaleDataIndex) {
  20201. LocaleDataIndex[LocaleDataIndex["LocaleId"] = 0] = "LocaleId";
  20202. LocaleDataIndex[LocaleDataIndex["DayPeriodsFormat"] = 1] = "DayPeriodsFormat";
  20203. LocaleDataIndex[LocaleDataIndex["DayPeriodsStandalone"] = 2] = "DayPeriodsStandalone";
  20204. LocaleDataIndex[LocaleDataIndex["DaysFormat"] = 3] = "DaysFormat";
  20205. LocaleDataIndex[LocaleDataIndex["DaysStandalone"] = 4] = "DaysStandalone";
  20206. LocaleDataIndex[LocaleDataIndex["MonthsFormat"] = 5] = "MonthsFormat";
  20207. LocaleDataIndex[LocaleDataIndex["MonthsStandalone"] = 6] = "MonthsStandalone";
  20208. LocaleDataIndex[LocaleDataIndex["Eras"] = 7] = "Eras";
  20209. LocaleDataIndex[LocaleDataIndex["FirstDayOfWeek"] = 8] = "FirstDayOfWeek";
  20210. LocaleDataIndex[LocaleDataIndex["WeekendRange"] = 9] = "WeekendRange";
  20211. LocaleDataIndex[LocaleDataIndex["DateFormat"] = 10] = "DateFormat";
  20212. LocaleDataIndex[LocaleDataIndex["TimeFormat"] = 11] = "TimeFormat";
  20213. LocaleDataIndex[LocaleDataIndex["DateTimeFormat"] = 12] = "DateTimeFormat";
  20214. LocaleDataIndex[LocaleDataIndex["NumberSymbols"] = 13] = "NumberSymbols";
  20215. LocaleDataIndex[LocaleDataIndex["NumberFormats"] = 14] = "NumberFormats";
  20216. LocaleDataIndex[LocaleDataIndex["CurrencyCode"] = 15] = "CurrencyCode";
  20217. LocaleDataIndex[LocaleDataIndex["CurrencySymbol"] = 16] = "CurrencySymbol";
  20218. LocaleDataIndex[LocaleDataIndex["CurrencyName"] = 17] = "CurrencyName";
  20219. LocaleDataIndex[LocaleDataIndex["Currencies"] = 18] = "Currencies";
  20220. LocaleDataIndex[LocaleDataIndex["Directionality"] = 19] = "Directionality";
  20221. LocaleDataIndex[LocaleDataIndex["PluralCase"] = 20] = "PluralCase";
  20222. LocaleDataIndex[LocaleDataIndex["ExtraData"] = 21] = "ExtraData";
  20223. })(LocaleDataIndex || (LocaleDataIndex = {}));
  20224. /**
  20225. * Returns the canonical form of a locale name - lowercase with `_` replaced with `-`.
  20226. */
  20227. function normalizeLocale(locale) {
  20228. return locale.toLowerCase().replace(/_/g, '-');
  20229. }
  20230. const pluralMapping = ['zero', 'one', 'two', 'few', 'many'];
  20231. /**
  20232. * Returns the plural case based on the locale
  20233. */
  20234. function getPluralCase(value, locale) {
  20235. const plural = getLocalePluralCase(locale)(parseInt(value, 10));
  20236. const result = pluralMapping[plural];
  20237. return (result !== undefined) ? result : 'other';
  20238. }
  20239. /**
  20240. * The locale id that the application is using by default (for translations and ICU expressions).
  20241. */
  20242. const DEFAULT_LOCALE_ID = 'en-US';
  20243. /**
  20244. * USD currency code that the application uses by default for CurrencyPipe when no
  20245. * DEFAULT_CURRENCY_CODE is provided.
  20246. */
  20247. const USD_CURRENCY_CODE = 'USD';
  20248. /**
  20249. * Marks that the next string is an element name.
  20250. *
  20251. * See `I18nMutateOpCodes` documentation.
  20252. */
  20253. const ELEMENT_MARKER = {
  20254. marker: 'element'
  20255. };
  20256. /**
  20257. * Marks that the next string is comment text need for ICU.
  20258. *
  20259. * See `I18nMutateOpCodes` documentation.
  20260. */
  20261. const ICU_MARKER = {
  20262. marker: 'ICU'
  20263. };
  20264. /**
  20265. * See `I18nCreateOpCodes`
  20266. */
  20267. var I18nCreateOpCode;
  20268. (function (I18nCreateOpCode) {
  20269. /**
  20270. * Number of bits to shift index so that it can be combined with the `APPEND_EAGERLY` and
  20271. * `COMMENT`.
  20272. */
  20273. I18nCreateOpCode[I18nCreateOpCode["SHIFT"] = 2] = "SHIFT";
  20274. /**
  20275. * Should the node be appended to parent immediately after creation.
  20276. */
  20277. I18nCreateOpCode[I18nCreateOpCode["APPEND_EAGERLY"] = 1] = "APPEND_EAGERLY";
  20278. /**
  20279. * If set the node should be comment (rather than a text) node.
  20280. */
  20281. I18nCreateOpCode[I18nCreateOpCode["COMMENT"] = 2] = "COMMENT";
  20282. })(I18nCreateOpCode || (I18nCreateOpCode = {}));
  20283. // Note: This hack is necessary so we don't erroneously get a circular dependency
  20284. // failure based on types.
  20285. const unusedValueExportToPlacateAjd = 1;
  20286. /**
  20287. * The locale id that the application is currently using (for translations and ICU expressions).
  20288. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
  20289. * but is now defined as a global value.
  20290. */
  20291. let LOCALE_ID$1 = DEFAULT_LOCALE_ID;
  20292. /**
  20293. * Sets the locale id that will be used for translations and ICU expressions.
  20294. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
  20295. * but is now defined as a global value.
  20296. *
  20297. * @param localeId
  20298. */
  20299. function setLocaleId(localeId) {
  20300. assertDefined(localeId, `Expected localeId to be defined`);
  20301. if (typeof localeId === 'string') {
  20302. LOCALE_ID$1 = localeId.toLowerCase().replace(/_/g, '-');
  20303. }
  20304. }
  20305. /**
  20306. * Gets the locale id that will be used for translations and ICU expressions.
  20307. * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
  20308. * but is now defined as a global value.
  20309. */
  20310. function getLocaleId() {
  20311. return LOCALE_ID$1;
  20312. }
  20313. /**
  20314. * Find a node in front of which `currentTNode` should be inserted (takes i18n into account).
  20315. *
  20316. * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
  20317. * takes `TNode.insertBeforeIndex` into account.
  20318. *
  20319. * @param parentTNode parent `TNode`
  20320. * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
  20321. * @param lView current `LView`
  20322. */
  20323. function getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView) {
  20324. const tNodeInsertBeforeIndex = currentTNode.insertBeforeIndex;
  20325. const insertBeforeIndex = Array.isArray(tNodeInsertBeforeIndex) ? tNodeInsertBeforeIndex[0] : tNodeInsertBeforeIndex;
  20326. if (insertBeforeIndex === null) {
  20327. return getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView);
  20328. }
  20329. else {
  20330. ngDevMode && assertIndexInRange(lView, insertBeforeIndex);
  20331. return unwrapRNode(lView[insertBeforeIndex]);
  20332. }
  20333. }
  20334. /**
  20335. * Process `TNode.insertBeforeIndex` by adding i18n text nodes.
  20336. *
  20337. * See `TNode.insertBeforeIndex`
  20338. */
  20339. function processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRElement) {
  20340. const tNodeInsertBeforeIndex = childTNode.insertBeforeIndex;
  20341. if (Array.isArray(tNodeInsertBeforeIndex)) {
  20342. // An array indicates that there are i18n nodes that need to be added as children of this
  20343. // `childRNode`. These i18n nodes were created before this `childRNode` was available and so
  20344. // only now can be added. The first element of the array is the normal index where we should
  20345. // insert the `childRNode`. Additional elements are the extra nodes to be added as children of
  20346. // `childRNode`.
  20347. ngDevMode && assertDomNode(childRNode);
  20348. let i18nParent = childRNode;
  20349. let anchorRNode = null;
  20350. if (!(childTNode.type & 3 /* TNodeType.AnyRNode */)) {
  20351. anchorRNode = i18nParent;
  20352. i18nParent = parentRElement;
  20353. }
  20354. if (i18nParent !== null && childTNode.componentOffset === -1) {
  20355. for (let i = 1; i < tNodeInsertBeforeIndex.length; i++) {
  20356. // No need to `unwrapRNode` because all of the indexes point to i18n text nodes.
  20357. // see `assertDomNode` below.
  20358. const i18nChild = lView[tNodeInsertBeforeIndex[i]];
  20359. nativeInsertBefore(renderer, i18nParent, i18nChild, anchorRNode, false);
  20360. }
  20361. }
  20362. }
  20363. }
  20364. /**
  20365. * Add `tNode` to `previousTNodes` list and update relevant `TNode`s in `previousTNodes` list
  20366. * `tNode.insertBeforeIndex`.
  20367. *
  20368. * Things to keep in mind:
  20369. * 1. All i18n text nodes are encoded as `TNodeType.Element` and are created eagerly by the
  20370. * `ɵɵi18nStart` instruction.
  20371. * 2. All `TNodeType.Placeholder` `TNodes` are elements which will be created later by
  20372. * `ɵɵelementStart` instruction.
  20373. * 3. `ɵɵelementStart` instruction will create `TNode`s in the ascending `TNode.index` order. (So a
  20374. * smaller index `TNode` is guaranteed to be created before a larger one)
  20375. *
  20376. * We use the above three invariants to determine `TNode.insertBeforeIndex`.
  20377. *
  20378. * In an ideal world `TNode.insertBeforeIndex` would always be `TNode.next.index`. However,
  20379. * this will not work because `TNode.next.index` may be larger than `TNode.index` which means that
  20380. * the next node is not yet created and therefore we can't insert in front of it.
  20381. *
  20382. * Rule1: `TNode.insertBeforeIndex = null` if `TNode.next === null` (Initial condition, as we don't
  20383. * know if there will be further `TNode`s inserted after.)
  20384. * Rule2: If `previousTNode` is created after the `tNode` being inserted, then
  20385. * `previousTNode.insertBeforeNode = tNode.index` (So when a new `tNode` is added we check
  20386. * previous to see if we can update its `insertBeforeTNode`)
  20387. *
  20388. * See `TNode.insertBeforeIndex` for more context.
  20389. *
  20390. * @param previousTNodes A list of previous TNodes so that we can easily traverse `TNode`s in
  20391. * reverse order. (If `TNode` would have `previous` this would not be necessary.)
  20392. * @param newTNode A TNode to add to the `previousTNodes` list.
  20393. */
  20394. function addTNodeAndUpdateInsertBeforeIndex(previousTNodes, newTNode) {
  20395. // Start with Rule1
  20396. ngDevMode &&
  20397. assertEqual(newTNode.insertBeforeIndex, null, 'We expect that insertBeforeIndex is not set');
  20398. previousTNodes.push(newTNode);
  20399. if (previousTNodes.length > 1) {
  20400. for (let i = previousTNodes.length - 2; i >= 0; i--) {
  20401. const existingTNode = previousTNodes[i];
  20402. // Text nodes are created eagerly and so they don't need their `indexBeforeIndex` updated.
  20403. // It is safe to ignore them.
  20404. if (!isI18nText(existingTNode)) {
  20405. if (isNewTNodeCreatedBefore(existingTNode, newTNode) &&
  20406. getInsertBeforeIndex(existingTNode) === null) {
  20407. // If it was created before us in time, (and it does not yet have `insertBeforeIndex`)
  20408. // then add the `insertBeforeIndex`.
  20409. setInsertBeforeIndex(existingTNode, newTNode.index);
  20410. }
  20411. }
  20412. }
  20413. }
  20414. }
  20415. function isI18nText(tNode) {
  20416. return !(tNode.type & 64 /* TNodeType.Placeholder */);
  20417. }
  20418. function isNewTNodeCreatedBefore(existingTNode, newTNode) {
  20419. return isI18nText(newTNode) || existingTNode.index > newTNode.index;
  20420. }
  20421. function getInsertBeforeIndex(tNode) {
  20422. const index = tNode.insertBeforeIndex;
  20423. return Array.isArray(index) ? index[0] : index;
  20424. }
  20425. function setInsertBeforeIndex(tNode, value) {
  20426. const index = tNode.insertBeforeIndex;
  20427. if (Array.isArray(index)) {
  20428. // Array is stored if we have to insert child nodes. See `TNode.insertBeforeIndex`
  20429. index[0] = value;
  20430. }
  20431. else {
  20432. setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
  20433. tNode.insertBeforeIndex = value;
  20434. }
  20435. }
  20436. /**
  20437. * Retrieve `TIcu` at a given `index`.
  20438. *
  20439. * The `TIcu` can be stored either directly (if it is nested ICU) OR
  20440. * it is stored inside tho `TIcuContainer` if it is top level ICU.
  20441. *
  20442. * The reason for this is that the top level ICU need a `TNode` so that they are part of the render
  20443. * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
  20444. * expressed (parent ICU may have selected a case which does not contain it.)
  20445. *
  20446. * @param tView Current `TView`.
  20447. * @param index Index where the value should be read from.
  20448. */
  20449. function getTIcu(tView, index) {
  20450. const value = tView.data[index];
  20451. if (value === null || typeof value === 'string')
  20452. return null;
  20453. if (ngDevMode &&
  20454. !(value.hasOwnProperty('tView') || value.hasOwnProperty('currentCaseLViewIndex'))) {
  20455. throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value);
  20456. }
  20457. // Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be
  20458. // either TIcu or TIcuContainerNode. This is not ideal, but we still think it is OK because it
  20459. // will be just two cases which fits into the browser inline cache (inline cache can take up to
  20460. // 4)
  20461. const tIcu = value.hasOwnProperty('currentCaseLViewIndex') ? value :
  20462. value.value;
  20463. ngDevMode && assertTIcu(tIcu);
  20464. return tIcu;
  20465. }
  20466. /**
  20467. * Store `TIcu` at a give `index`.
  20468. *
  20469. * The `TIcu` can be stored either directly (if it is nested ICU) OR
  20470. * it is stored inside tho `TIcuContainer` if it is top level ICU.
  20471. *
  20472. * The reason for this is that the top level ICU need a `TNode` so that they are part of the render
  20473. * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
  20474. * expressed (parent ICU may have selected a case which does not contain it.)
  20475. *
  20476. * @param tView Current `TView`.
  20477. * @param index Index where the value should be stored at in `Tview.data`
  20478. * @param tIcu The TIcu to store.
  20479. */
  20480. function setTIcu(tView, index, tIcu) {
  20481. const tNode = tView.data[index];
  20482. ngDevMode &&
  20483. assertEqual(tNode === null || tNode.hasOwnProperty('tView'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
  20484. if (tNode === null) {
  20485. tView.data[index] = tIcu;
  20486. }
  20487. else {
  20488. ngDevMode && assertTNodeType(tNode, 32 /* TNodeType.Icu */);
  20489. tNode.value = tIcu;
  20490. }
  20491. }
  20492. /**
  20493. * Set `TNode.insertBeforeIndex` taking the `Array` into account.
  20494. *
  20495. * See `TNode.insertBeforeIndex`
  20496. */
  20497. function setTNodeInsertBeforeIndex(tNode, index) {
  20498. ngDevMode && assertTNode(tNode);
  20499. let insertBeforeIndex = tNode.insertBeforeIndex;
  20500. if (insertBeforeIndex === null) {
  20501. setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
  20502. insertBeforeIndex = tNode.insertBeforeIndex =
  20503. [null /* may be updated to number later */, index];
  20504. }
  20505. else {
  20506. assertEqual(Array.isArray(insertBeforeIndex), true, 'Expecting array here');
  20507. insertBeforeIndex.push(index);
  20508. }
  20509. }
  20510. /**
  20511. * Create `TNode.type=TNodeType.Placeholder` node.
  20512. *
  20513. * See `TNodeType.Placeholder` for more information.
  20514. */
  20515. function createTNodePlaceholder(tView, previousTNodes, index) {
  20516. const tNode = createTNodeAtIndex(tView, index, 64 /* TNodeType.Placeholder */, null, null);
  20517. addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tNode);
  20518. return tNode;
  20519. }
  20520. /**
  20521. * Returns current ICU case.
  20522. *
  20523. * ICU cases are stored as index into the `TIcu.cases`.
  20524. * At times it is necessary to communicate that the ICU case just switched and that next ICU update
  20525. * should update all bindings regardless of the mask. In such a case the we store negative numbers
  20526. * for cases which have just been switched. This function removes the negative flag.
  20527. */
  20528. function getCurrentICUCaseIndex(tIcu, lView) {
  20529. const currentCase = lView[tIcu.currentCaseLViewIndex];
  20530. return currentCase === null ? currentCase : (currentCase < 0 ? ~currentCase : currentCase);
  20531. }
  20532. function getParentFromIcuCreateOpCode(mergedCode) {
  20533. return mergedCode >>> 17 /* IcuCreateOpCode.SHIFT_PARENT */;
  20534. }
  20535. function getRefFromIcuCreateOpCode(mergedCode) {
  20536. return (mergedCode & 131070 /* IcuCreateOpCode.MASK_REF */) >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
  20537. }
  20538. function getInstructionFromIcuCreateOpCode(mergedCode) {
  20539. return mergedCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */;
  20540. }
  20541. function icuCreateOpCode(opCode, parentIdx, refIdx) {
  20542. ngDevMode && assertGreaterThanOrEqual(parentIdx, 0, 'Missing parent index');
  20543. ngDevMode && assertGreaterThan(refIdx, 0, 'Missing ref index');
  20544. return opCode | parentIdx << 17 /* IcuCreateOpCode.SHIFT_PARENT */ | refIdx << 1 /* IcuCreateOpCode.SHIFT_REF */;
  20545. }
  20546. /**
  20547. * Keep track of which input bindings in `ɵɵi18nExp` have changed.
  20548. *
  20549. * This is used to efficiently update expressions in i18n only when the corresponding input has
  20550. * changed.
  20551. *
  20552. * 1) Each bit represents which of the `ɵɵi18nExp` has changed.
  20553. * 2) There are 32 bits allowed in JS.
  20554. * 3) Bit 32 is special as it is shared for all changes past 32. (In other words if you have more
  20555. * than 32 `ɵɵi18nExp` then all changes past 32nd `ɵɵi18nExp` will be mapped to same bit. This means
  20556. * that we may end up changing more than we need to. But i18n expressions with 32 bindings is rare
  20557. * so in practice it should not be an issue.)
  20558. */
  20559. let changeMask = 0b0;
  20560. /**
  20561. * Keeps track of which bit needs to be updated in `changeMask`
  20562. *
  20563. * This value gets incremented on every call to `ɵɵi18nExp`
  20564. */
  20565. let changeMaskCounter = 0;
  20566. /**
  20567. * Keep track of which input bindings in `ɵɵi18nExp` have changed.
  20568. *
  20569. * `setMaskBit` gets invoked by each call to `ɵɵi18nExp`.
  20570. *
  20571. * @param hasChange did `ɵɵi18nExp` detect a change.
  20572. */
  20573. function setMaskBit(hasChange) {
  20574. if (hasChange) {
  20575. changeMask = changeMask | (1 << Math.min(changeMaskCounter, 31));
  20576. }
  20577. changeMaskCounter++;
  20578. }
  20579. function applyI18n(tView, lView, index) {
  20580. if (changeMaskCounter > 0) {
  20581. ngDevMode && assertDefined(tView, `tView should be defined`);
  20582. const tI18n = tView.data[index];
  20583. // When `index` points to an `ɵɵi18nAttributes` then we have an array otherwise `TI18n`
  20584. const updateOpCodes = Array.isArray(tI18n) ? tI18n : tI18n.update;
  20585. const bindingsStartIndex = getBindingIndex() - changeMaskCounter - 1;
  20586. applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask);
  20587. }
  20588. // Reset changeMask & maskBit to default for the next update cycle
  20589. changeMask = 0b0;
  20590. changeMaskCounter = 0;
  20591. }
  20592. /**
  20593. * Apply `I18nCreateOpCodes` op-codes as stored in `TI18n.create`.
  20594. *
  20595. * Creates text (and comment) nodes which are internationalized.
  20596. *
  20597. * @param lView Current lView
  20598. * @param createOpCodes Set of op-codes to apply
  20599. * @param parentRNode Parent node (so that direct children can be added eagerly) or `null` if it is
  20600. * a root node.
  20601. * @param insertInFrontOf DOM node that should be used as an anchor.
  20602. */
  20603. function applyCreateOpCodes(lView, createOpCodes, parentRNode, insertInFrontOf) {
  20604. const renderer = lView[RENDERER];
  20605. for (let i = 0; i < createOpCodes.length; i++) {
  20606. const opCode = createOpCodes[i++];
  20607. const text = createOpCodes[i];
  20608. const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT;
  20609. const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY;
  20610. const index = opCode >>> I18nCreateOpCode.SHIFT;
  20611. let rNode = lView[index];
  20612. if (rNode === null) {
  20613. // We only create new DOM nodes if they don't already exist: If ICU switches case back to a
  20614. // case which was already instantiated, no need to create new DOM nodes.
  20615. rNode = lView[index] =
  20616. isComment ? renderer.createComment(text) : createTextNode(renderer, text);
  20617. }
  20618. if (appendNow && parentRNode !== null) {
  20619. nativeInsertBefore(renderer, parentRNode, rNode, insertInFrontOf, false);
  20620. }
  20621. }
  20622. }
  20623. /**
  20624. * Apply `I18nMutateOpCodes` OpCodes.
  20625. *
  20626. * @param tView Current `TView`
  20627. * @param mutableOpCodes Mutable OpCodes to process
  20628. * @param lView Current `LView`
  20629. * @param anchorRNode place where the i18n node should be inserted.
  20630. */
  20631. function applyMutableOpCodes(tView, mutableOpCodes, lView, anchorRNode) {
  20632. ngDevMode && assertDomNode(anchorRNode);
  20633. const renderer = lView[RENDERER];
  20634. // `rootIdx` represents the node into which all inserts happen.
  20635. let rootIdx = null;
  20636. // `rootRNode` represents the real node into which we insert. This can be different from
  20637. // `lView[rootIdx]` if we have projection.
  20638. // - null we don't have a parent (as can be the case in when we are inserting into a root of
  20639. // LView which has no parent.)
  20640. // - `RElement` The element representing the root after taking projection into account.
  20641. let rootRNode;
  20642. for (let i = 0; i < mutableOpCodes.length; i++) {
  20643. const opCode = mutableOpCodes[i];
  20644. if (typeof opCode == 'string') {
  20645. const textNodeIndex = mutableOpCodes[++i];
  20646. if (lView[textNodeIndex] === null) {
  20647. ngDevMode && ngDevMode.rendererCreateTextNode++;
  20648. ngDevMode && assertIndexInRange(lView, textNodeIndex);
  20649. lView[textNodeIndex] = createTextNode(renderer, opCode);
  20650. }
  20651. }
  20652. else if (typeof opCode == 'number') {
  20653. switch (opCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */) {
  20654. case 0 /* IcuCreateOpCode.AppendChild */:
  20655. const parentIdx = getParentFromIcuCreateOpCode(opCode);
  20656. if (rootIdx === null) {
  20657. // The first operation should save the `rootIdx` because the first operation
  20658. // must insert into the root. (Only subsequent operations can insert into a dynamic
  20659. // parent)
  20660. rootIdx = parentIdx;
  20661. rootRNode = nativeParentNode(renderer, anchorRNode);
  20662. }
  20663. let insertInFrontOf;
  20664. let parentRNode;
  20665. if (parentIdx === rootIdx) {
  20666. insertInFrontOf = anchorRNode;
  20667. parentRNode = rootRNode;
  20668. }
  20669. else {
  20670. insertInFrontOf = null;
  20671. parentRNode = unwrapRNode(lView[parentIdx]);
  20672. }
  20673. // FIXME(misko): Refactor with `processI18nText`
  20674. if (parentRNode !== null) {
  20675. // This can happen if the `LView` we are adding to is not attached to a parent `LView`.
  20676. // In such a case there is no "root" we can attach to. This is fine, as we still need to
  20677. // create the elements. When the `LView` gets later added to a parent these "root" nodes
  20678. // get picked up and added.
  20679. ngDevMode && assertDomNode(parentRNode);
  20680. const refIdx = getRefFromIcuCreateOpCode(opCode);
  20681. ngDevMode && assertGreaterThan(refIdx, HEADER_OFFSET, 'Missing ref');
  20682. // `unwrapRNode` is not needed here as all of these point to RNodes as part of the i18n
  20683. // which can't have components.
  20684. const child = lView[refIdx];
  20685. ngDevMode && assertDomNode(child);
  20686. nativeInsertBefore(renderer, parentRNode, child, insertInFrontOf, false);
  20687. const tIcu = getTIcu(tView, refIdx);
  20688. if (tIcu !== null && typeof tIcu === 'object') {
  20689. // If we just added a comment node which has ICU then that ICU may have already been
  20690. // rendered and therefore we need to re-add it here.
  20691. ngDevMode && assertTIcu(tIcu);
  20692. const caseIndex = getCurrentICUCaseIndex(tIcu, lView);
  20693. if (caseIndex !== null) {
  20694. applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, lView[tIcu.anchorIdx]);
  20695. }
  20696. }
  20697. }
  20698. break;
  20699. case 1 /* IcuCreateOpCode.Attr */:
  20700. const elementNodeIndex = opCode >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
  20701. const attrName = mutableOpCodes[++i];
  20702. const attrValue = mutableOpCodes[++i];
  20703. // This code is used for ICU expressions only, since we don't support
  20704. // directives/components in ICUs, we don't need to worry about inputs here
  20705. setElementAttribute(renderer, getNativeByIndex(elementNodeIndex, lView), null, null, attrName, attrValue, null);
  20706. break;
  20707. default:
  20708. if (ngDevMode) {
  20709. throw new RuntimeError(700 /* RuntimeErrorCode.INVALID_I18N_STRUCTURE */, `Unable to determine the type of mutate operation for "${opCode}"`);
  20710. }
  20711. }
  20712. }
  20713. else {
  20714. switch (opCode) {
  20715. case ICU_MARKER:
  20716. const commentValue = mutableOpCodes[++i];
  20717. const commentNodeIndex = mutableOpCodes[++i];
  20718. if (lView[commentNodeIndex] === null) {
  20719. ngDevMode &&
  20720. assertEqual(typeof commentValue, 'string', `Expected "${commentValue}" to be a comment node value`);
  20721. ngDevMode && ngDevMode.rendererCreateComment++;
  20722. ngDevMode && assertIndexInExpandoRange(lView, commentNodeIndex);
  20723. const commentRNode = lView[commentNodeIndex] =
  20724. createCommentNode(renderer, commentValue);
  20725. // FIXME(misko): Attaching patch data is only needed for the root (Also add tests)
  20726. attachPatchData(commentRNode, lView);
  20727. }
  20728. break;
  20729. case ELEMENT_MARKER:
  20730. const tagName = mutableOpCodes[++i];
  20731. const elementNodeIndex = mutableOpCodes[++i];
  20732. if (lView[elementNodeIndex] === null) {
  20733. ngDevMode &&
  20734. assertEqual(typeof tagName, 'string', `Expected "${tagName}" to be an element node tag name`);
  20735. ngDevMode && ngDevMode.rendererCreateElement++;
  20736. ngDevMode && assertIndexInExpandoRange(lView, elementNodeIndex);
  20737. const elementRNode = lView[elementNodeIndex] =
  20738. createElementNode(renderer, tagName, null);
  20739. // FIXME(misko): Attaching patch data is only needed for the root (Also add tests)
  20740. attachPatchData(elementRNode, lView);
  20741. }
  20742. break;
  20743. default:
  20744. ngDevMode &&
  20745. throwError(`Unable to determine the type of mutate operation for "${opCode}"`);
  20746. }
  20747. }
  20748. }
  20749. }
  20750. /**
  20751. * Apply `I18nUpdateOpCodes` OpCodes
  20752. *
  20753. * @param tView Current `TView`
  20754. * @param lView Current `LView`
  20755. * @param updateOpCodes OpCodes to process
  20756. * @param bindingsStartIndex Location of the first `ɵɵi18nApply`
  20757. * @param changeMask Each bit corresponds to a `ɵɵi18nExp` (Counting backwards from
  20758. * `bindingsStartIndex`)
  20759. */
  20760. function applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask) {
  20761. for (let i = 0; i < updateOpCodes.length; i++) {
  20762. // bit code to check if we should apply the next update
  20763. const checkBit = updateOpCodes[i];
  20764. // Number of opCodes to skip until next set of update codes
  20765. const skipCodes = updateOpCodes[++i];
  20766. if (checkBit & changeMask) {
  20767. // The value has been updated since last checked
  20768. let value = '';
  20769. for (let j = i + 1; j <= (i + skipCodes); j++) {
  20770. const opCode = updateOpCodes[j];
  20771. if (typeof opCode == 'string') {
  20772. value += opCode;
  20773. }
  20774. else if (typeof opCode == 'number') {
  20775. if (opCode < 0) {
  20776. // Negative opCode represent `i18nExp` values offset.
  20777. value += renderStringify(lView[bindingsStartIndex - opCode]);
  20778. }
  20779. else {
  20780. const nodeIndex = (opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */);
  20781. switch (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) {
  20782. case 1 /* I18nUpdateOpCode.Attr */:
  20783. const propName = updateOpCodes[++j];
  20784. const sanitizeFn = updateOpCodes[++j];
  20785. const tNodeOrTagName = tView.data[nodeIndex];
  20786. ngDevMode && assertDefined(tNodeOrTagName, 'Experting TNode or string');
  20787. if (typeof tNodeOrTagName === 'string') {
  20788. // IF we don't have a `TNode`, then we are an element in ICU (as ICU content does
  20789. // not have TNode), in which case we know that there are no directives, and hence
  20790. // we use attribute setting.
  20791. setElementAttribute(lView[RENDERER], lView[nodeIndex], null, tNodeOrTagName, propName, value, sanitizeFn);
  20792. }
  20793. else {
  20794. elementPropertyInternal(tView, tNodeOrTagName, lView, propName, value, lView[RENDERER], sanitizeFn, false);
  20795. }
  20796. break;
  20797. case 0 /* I18nUpdateOpCode.Text */:
  20798. const rText = lView[nodeIndex];
  20799. rText !== null && updateTextNode(lView[RENDERER], rText, value);
  20800. break;
  20801. case 2 /* I18nUpdateOpCode.IcuSwitch */:
  20802. applyIcuSwitchCase(tView, getTIcu(tView, nodeIndex), lView, value);
  20803. break;
  20804. case 3 /* I18nUpdateOpCode.IcuUpdate */:
  20805. applyIcuUpdateCase(tView, getTIcu(tView, nodeIndex), bindingsStartIndex, lView);
  20806. break;
  20807. }
  20808. }
  20809. }
  20810. }
  20811. }
  20812. else {
  20813. const opCode = updateOpCodes[i + 1];
  20814. if (opCode > 0 && (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) === 3 /* I18nUpdateOpCode.IcuUpdate */) {
  20815. // Special case for the `icuUpdateCase`. It could be that the mask did not match, but
  20816. // we still need to execute `icuUpdateCase` because the case has changed recently due to
  20817. // previous `icuSwitchCase` instruction. (`icuSwitchCase` and `icuUpdateCase` always come in
  20818. // pairs.)
  20819. const nodeIndex = (opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */);
  20820. const tIcu = getTIcu(tView, nodeIndex);
  20821. const currentIndex = lView[tIcu.currentCaseLViewIndex];
  20822. if (currentIndex < 0) {
  20823. applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView);
  20824. }
  20825. }
  20826. }
  20827. i += skipCodes;
  20828. }
  20829. }
  20830. /**
  20831. * Apply OpCodes associated with updating an existing ICU.
  20832. *
  20833. * @param tView Current `TView`
  20834. * @param tIcu Current `TIcu`
  20835. * @param bindingsStartIndex Location of the first `ɵɵi18nApply`
  20836. * @param lView Current `LView`
  20837. */
  20838. function applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView) {
  20839. ngDevMode && assertIndexInRange(lView, tIcu.currentCaseLViewIndex);
  20840. let activeCaseIndex = lView[tIcu.currentCaseLViewIndex];
  20841. if (activeCaseIndex !== null) {
  20842. let mask = changeMask;
  20843. if (activeCaseIndex < 0) {
  20844. // Clear the flag.
  20845. // Negative number means that the ICU was freshly created and we need to force the update.
  20846. activeCaseIndex = lView[tIcu.currentCaseLViewIndex] = ~activeCaseIndex;
  20847. // -1 is same as all bits on, which simulates creation since it marks all bits dirty
  20848. mask = -1;
  20849. }
  20850. applyUpdateOpCodes(tView, lView, tIcu.update[activeCaseIndex], bindingsStartIndex, mask);
  20851. }
  20852. }
  20853. /**
  20854. * Apply OpCodes associated with switching a case on ICU.
  20855. *
  20856. * This involves tearing down existing case and than building up a new case.
  20857. *
  20858. * @param tView Current `TView`
  20859. * @param tIcu Current `TIcu`
  20860. * @param lView Current `LView`
  20861. * @param value Value of the case to update to.
  20862. */
  20863. function applyIcuSwitchCase(tView, tIcu, lView, value) {
  20864. // Rebuild a new case for this ICU
  20865. const caseIndex = getCaseIndex(tIcu, value);
  20866. let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView);
  20867. if (activeCaseIndex !== caseIndex) {
  20868. applyIcuSwitchCaseRemove(tView, tIcu, lView);
  20869. lView[tIcu.currentCaseLViewIndex] = caseIndex === null ? null : ~caseIndex;
  20870. if (caseIndex !== null) {
  20871. // Add the nodes for the new case
  20872. const anchorRNode = lView[tIcu.anchorIdx];
  20873. if (anchorRNode) {
  20874. ngDevMode && assertDomNode(anchorRNode);
  20875. applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, anchorRNode);
  20876. }
  20877. }
  20878. }
  20879. }
  20880. /**
  20881. * Apply OpCodes associated with tearing ICU case.
  20882. *
  20883. * This involves tearing down existing case and than building up a new case.
  20884. *
  20885. * @param tView Current `TView`
  20886. * @param tIcu Current `TIcu`
  20887. * @param lView Current `LView`
  20888. */
  20889. function applyIcuSwitchCaseRemove(tView, tIcu, lView) {
  20890. let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView);
  20891. if (activeCaseIndex !== null) {
  20892. const removeCodes = tIcu.remove[activeCaseIndex];
  20893. for (let i = 0; i < removeCodes.length; i++) {
  20894. const nodeOrIcuIndex = removeCodes[i];
  20895. if (nodeOrIcuIndex > 0) {
  20896. // Positive numbers are `RNode`s.
  20897. const rNode = getNativeByIndex(nodeOrIcuIndex, lView);
  20898. rNode !== null && nativeRemoveNode(lView[RENDERER], rNode);
  20899. }
  20900. else {
  20901. // Negative numbers are ICUs
  20902. applyIcuSwitchCaseRemove(tView, getTIcu(tView, ~nodeOrIcuIndex), lView);
  20903. }
  20904. }
  20905. }
  20906. }
  20907. /**
  20908. * Returns the index of the current case of an ICU expression depending on the main binding value
  20909. *
  20910. * @param icuExpression
  20911. * @param bindingValue The value of the main binding used by this ICU expression
  20912. */
  20913. function getCaseIndex(icuExpression, bindingValue) {
  20914. let index = icuExpression.cases.indexOf(bindingValue);
  20915. if (index === -1) {
  20916. switch (icuExpression.type) {
  20917. case 1 /* IcuType.plural */: {
  20918. const resolvedCase = getPluralCase(bindingValue, getLocaleId());
  20919. index = icuExpression.cases.indexOf(resolvedCase);
  20920. if (index === -1 && resolvedCase !== 'other') {
  20921. index = icuExpression.cases.indexOf('other');
  20922. }
  20923. break;
  20924. }
  20925. case 0 /* IcuType.select */: {
  20926. index = icuExpression.cases.indexOf('other');
  20927. break;
  20928. }
  20929. }
  20930. }
  20931. return index === -1 ? null : index;
  20932. }
  20933. function loadIcuContainerVisitor() {
  20934. const _stack = [];
  20935. let _index = -1;
  20936. let _lView;
  20937. let _removes;
  20938. /**
  20939. * Retrieves a set of root nodes from `TIcu.remove`. Used by `TNodeType.ICUContainer`
  20940. * to determine which root belong to the ICU.
  20941. *
  20942. * Example of usage.
  20943. * ```
  20944. * const nextRNode = icuContainerIteratorStart(tIcuContainerNode, lView);
  20945. * let rNode: RNode|null;
  20946. * while(rNode = nextRNode()) {
  20947. * console.log(rNode);
  20948. * }
  20949. * ```
  20950. *
  20951. * @param tIcuContainerNode Current `TIcuContainerNode`
  20952. * @param lView `LView` where the `RNode`s should be looked up.
  20953. */
  20954. function icuContainerIteratorStart(tIcuContainerNode, lView) {
  20955. _lView = lView;
  20956. while (_stack.length)
  20957. _stack.pop();
  20958. ngDevMode && assertTNodeForLView(tIcuContainerNode, lView);
  20959. enterIcu(tIcuContainerNode.value, lView);
  20960. return icuContainerIteratorNext;
  20961. }
  20962. function enterIcu(tIcu, lView) {
  20963. _index = 0;
  20964. const currentCase = getCurrentICUCaseIndex(tIcu, lView);
  20965. if (currentCase !== null) {
  20966. ngDevMode && assertNumberInRange(currentCase, 0, tIcu.cases.length - 1);
  20967. _removes = tIcu.remove[currentCase];
  20968. }
  20969. else {
  20970. _removes = EMPTY_ARRAY;
  20971. }
  20972. }
  20973. function icuContainerIteratorNext() {
  20974. if (_index < _removes.length) {
  20975. const removeOpCode = _removes[_index++];
  20976. ngDevMode && assertNumber(removeOpCode, 'Expecting OpCode number');
  20977. if (removeOpCode > 0) {
  20978. const rNode = _lView[removeOpCode];
  20979. ngDevMode && assertDomNode(rNode);
  20980. return rNode;
  20981. }
  20982. else {
  20983. _stack.push(_index, _removes);
  20984. // ICUs are represented by negative indices
  20985. const tIcuIndex = ~removeOpCode;
  20986. const tIcu = _lView[TVIEW].data[tIcuIndex];
  20987. ngDevMode && assertTIcu(tIcu);
  20988. enterIcu(tIcu, _lView);
  20989. return icuContainerIteratorNext();
  20990. }
  20991. }
  20992. else {
  20993. if (_stack.length === 0) {
  20994. return null;
  20995. }
  20996. else {
  20997. _removes = _stack.pop();
  20998. _index = _stack.pop();
  20999. return icuContainerIteratorNext();
  21000. }
  21001. }
  21002. }
  21003. return icuContainerIteratorStart;
  21004. }
  21005. /**
  21006. * Converts `I18nCreateOpCodes` array into a human readable format.
  21007. *
  21008. * This function is attached to the `I18nCreateOpCodes.debug` property if `ngDevMode` is enabled.
  21009. * This function provides a human readable view of the opcodes. This is useful when debugging the
  21010. * application as well as writing more readable tests.
  21011. *
  21012. * @param this `I18nCreateOpCodes` if attached as a method.
  21013. * @param opcodes `I18nCreateOpCodes` if invoked as a function.
  21014. */
  21015. function i18nCreateOpCodesToString(opcodes) {
  21016. const createOpCodes = opcodes || (Array.isArray(this) ? this : []);
  21017. let lines = [];
  21018. for (let i = 0; i < createOpCodes.length; i++) {
  21019. const opCode = createOpCodes[i++];
  21020. const text = createOpCodes[i];
  21021. const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT;
  21022. const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY;
  21023. const index = opCode >>> I18nCreateOpCode.SHIFT;
  21024. lines.push(`lView[${index}] = document.${isComment ? 'createComment' : 'createText'}(${JSON.stringify(text)});`);
  21025. if (appendNow) {
  21026. lines.push(`parent.appendChild(lView[${index}]);`);
  21027. }
  21028. }
  21029. return lines;
  21030. }
  21031. /**
  21032. * Converts `I18nUpdateOpCodes` array into a human readable format.
  21033. *
  21034. * This function is attached to the `I18nUpdateOpCodes.debug` property if `ngDevMode` is enabled.
  21035. * This function provides a human readable view of the opcodes. This is useful when debugging the
  21036. * application as well as writing more readable tests.
  21037. *
  21038. * @param this `I18nUpdateOpCodes` if attached as a method.
  21039. * @param opcodes `I18nUpdateOpCodes` if invoked as a function.
  21040. */
  21041. function i18nUpdateOpCodesToString(opcodes) {
  21042. const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : []));
  21043. let lines = [];
  21044. function consumeOpCode(value) {
  21045. const ref = value >>> 2 /* I18nUpdateOpCode.SHIFT_REF */;
  21046. const opCode = value & 3 /* I18nUpdateOpCode.MASK_OPCODE */;
  21047. switch (opCode) {
  21048. case 0 /* I18nUpdateOpCode.Text */:
  21049. return `(lView[${ref}] as Text).textContent = $$$`;
  21050. case 1 /* I18nUpdateOpCode.Attr */:
  21051. const attrName = parser.consumeString();
  21052. const sanitizationFn = parser.consumeFunction();
  21053. const value = sanitizationFn ? `(${sanitizationFn})($$$)` : '$$$';
  21054. return `(lView[${ref}] as Element).setAttribute('${attrName}', ${value})`;
  21055. case 2 /* I18nUpdateOpCode.IcuSwitch */:
  21056. return `icuSwitchCase(${ref}, $$$)`;
  21057. case 3 /* I18nUpdateOpCode.IcuUpdate */:
  21058. return `icuUpdateCase(${ref})`;
  21059. }
  21060. throw new Error('unexpected OpCode');
  21061. }
  21062. while (parser.hasMore()) {
  21063. let mask = parser.consumeNumber();
  21064. let size = parser.consumeNumber();
  21065. const end = parser.i + size;
  21066. const statements = [];
  21067. let statement = '';
  21068. while (parser.i < end) {
  21069. let value = parser.consumeNumberOrString();
  21070. if (typeof value === 'string') {
  21071. statement += value;
  21072. }
  21073. else if (value < 0) {
  21074. // Negative numbers are ref indexes
  21075. // Here `i` refers to current binding index. It is to signify that the value is relative,
  21076. // rather than absolute.
  21077. statement += '${lView[i' + value + ']}';
  21078. }
  21079. else {
  21080. // Positive numbers are operations.
  21081. const opCodeText = consumeOpCode(value);
  21082. statements.push(opCodeText.replace('$$$', '`' + statement + '`') + ';');
  21083. statement = '';
  21084. }
  21085. }
  21086. lines.push(`if (mask & 0b${mask.toString(2)}) { ${statements.join(' ')} }`);
  21087. }
  21088. return lines;
  21089. }
  21090. /**
  21091. * Converts `I18nCreateOpCodes` array into a human readable format.
  21092. *
  21093. * This function is attached to the `I18nCreateOpCodes.debug` if `ngDevMode` is enabled. This
  21094. * function provides a human readable view of the opcodes. This is useful when debugging the
  21095. * application as well as writing more readable tests.
  21096. *
  21097. * @param this `I18nCreateOpCodes` if attached as a method.
  21098. * @param opcodes `I18nCreateOpCodes` if invoked as a function.
  21099. */
  21100. function icuCreateOpCodesToString(opcodes) {
  21101. const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : []));
  21102. let lines = [];
  21103. function consumeOpCode(opCode) {
  21104. const parent = getParentFromIcuCreateOpCode(opCode);
  21105. const ref = getRefFromIcuCreateOpCode(opCode);
  21106. switch (getInstructionFromIcuCreateOpCode(opCode)) {
  21107. case 0 /* IcuCreateOpCode.AppendChild */:
  21108. return `(lView[${parent}] as Element).appendChild(lView[${lastRef}])`;
  21109. case 1 /* IcuCreateOpCode.Attr */:
  21110. return `(lView[${ref}] as Element).setAttribute("${parser.consumeString()}", "${parser.consumeString()}")`;
  21111. }
  21112. throw new Error('Unexpected OpCode: ' + getInstructionFromIcuCreateOpCode(opCode));
  21113. }
  21114. let lastRef = -1;
  21115. while (parser.hasMore()) {
  21116. let value = parser.consumeNumberStringOrMarker();
  21117. if (value === ICU_MARKER) {
  21118. const text = parser.consumeString();
  21119. lastRef = parser.consumeNumber();
  21120. lines.push(`lView[${lastRef}] = document.createComment("${text}")`);
  21121. }
  21122. else if (value === ELEMENT_MARKER) {
  21123. const text = parser.consumeString();
  21124. lastRef = parser.consumeNumber();
  21125. lines.push(`lView[${lastRef}] = document.createElement("${text}")`);
  21126. }
  21127. else if (typeof value === 'string') {
  21128. lastRef = parser.consumeNumber();
  21129. lines.push(`lView[${lastRef}] = document.createTextNode("${value}")`);
  21130. }
  21131. else if (typeof value === 'number') {
  21132. const line = consumeOpCode(value);
  21133. line && lines.push(line);
  21134. }
  21135. else {
  21136. throw new Error('Unexpected value');
  21137. }
  21138. }
  21139. return lines;
  21140. }
  21141. /**
  21142. * Converts `I18nRemoveOpCodes` array into a human readable format.
  21143. *
  21144. * This function is attached to the `I18nRemoveOpCodes.debug` if `ngDevMode` is enabled. This
  21145. * function provides a human readable view of the opcodes. This is useful when debugging the
  21146. * application as well as writing more readable tests.
  21147. *
  21148. * @param this `I18nRemoveOpCodes` if attached as a method.
  21149. * @param opcodes `I18nRemoveOpCodes` if invoked as a function.
  21150. */
  21151. function i18nRemoveOpCodesToString(opcodes) {
  21152. const removeCodes = opcodes || (Array.isArray(this) ? this : []);
  21153. let lines = [];
  21154. for (let i = 0; i < removeCodes.length; i++) {
  21155. const nodeOrIcuIndex = removeCodes[i];
  21156. if (nodeOrIcuIndex > 0) {
  21157. // Positive numbers are `RNode`s.
  21158. lines.push(`remove(lView[${nodeOrIcuIndex}])`);
  21159. }
  21160. else {
  21161. // Negative numbers are ICUs
  21162. lines.push(`removeNestedICU(${~nodeOrIcuIndex})`);
  21163. }
  21164. }
  21165. return lines;
  21166. }
  21167. class OpCodeParser {
  21168. constructor(codes) {
  21169. this.i = 0;
  21170. this.codes = codes;
  21171. }
  21172. hasMore() {
  21173. return this.i < this.codes.length;
  21174. }
  21175. consumeNumber() {
  21176. let value = this.codes[this.i++];
  21177. assertNumber(value, 'expecting number in OpCode');
  21178. return value;
  21179. }
  21180. consumeString() {
  21181. let value = this.codes[this.i++];
  21182. assertString(value, 'expecting string in OpCode');
  21183. return value;
  21184. }
  21185. consumeFunction() {
  21186. let value = this.codes[this.i++];
  21187. if (value === null || typeof value === 'function') {
  21188. return value;
  21189. }
  21190. throw new Error('expecting function in OpCode');
  21191. }
  21192. consumeNumberOrString() {
  21193. let value = this.codes[this.i++];
  21194. if (typeof value === 'string') {
  21195. return value;
  21196. }
  21197. assertNumber(value, 'expecting number or string in OpCode');
  21198. return value;
  21199. }
  21200. consumeNumberStringOrMarker() {
  21201. let value = this.codes[this.i++];
  21202. if (typeof value === 'string' || typeof value === 'number' || value == ICU_MARKER ||
  21203. value == ELEMENT_MARKER) {
  21204. return value;
  21205. }
  21206. assertNumber(value, 'expecting number, string, ICU_MARKER or ELEMENT_MARKER in OpCode');
  21207. return value;
  21208. }
  21209. }
  21210. const BINDING_REGEXP = /�(\d+):?\d*�/gi;
  21211. const ICU_REGEXP = /({\s*�\d+:?\d*�\s*,\s*\S{6}\s*,[\s\S]*})/gi;
  21212. const NESTED_ICU = /�(\d+)�/;
  21213. const ICU_BLOCK_REGEXP = /^\s*(�\d+:?\d*�)\s*,\s*(select|plural)\s*,/;
  21214. const MARKER = `�`;
  21215. const SUBTEMPLATE_REGEXP = /�\/?\*(\d+:\d+)�/gi;
  21216. const PH_REGEXP = /�(\/?[#*]\d+):?\d*�/gi;
  21217. /**
  21218. * Angular uses the special entity &ngsp; as a placeholder for non-removable space.
  21219. * It's replaced by the 0xE500 PUA (Private Use Areas) unicode character and later on replaced by a
  21220. * space.
  21221. * We are re-implementing the same idea since translations might contain this special character.
  21222. */
  21223. const NGSP_UNICODE_REGEXP = /\uE500/g;
  21224. function replaceNgsp(value) {
  21225. return value.replace(NGSP_UNICODE_REGEXP, ' ');
  21226. }
  21227. /**
  21228. * Patch a `debug` property getter on top of the existing object.
  21229. *
  21230. * NOTE: always call this method with `ngDevMode && attachDebugObject(...)`
  21231. *
  21232. * @param obj Object to patch
  21233. * @param debugGetter Getter returning a value to patch
  21234. */
  21235. function attachDebugGetter(obj, debugGetter) {
  21236. if (ngDevMode) {
  21237. Object.defineProperty(obj, 'debug', { get: debugGetter, enumerable: false });
  21238. }
  21239. else {
  21240. throw new Error('This method should be guarded with `ngDevMode` so that it can be tree shaken in production!');
  21241. }
  21242. }
  21243. /**
  21244. * Create dynamic nodes from i18n translation block.
  21245. *
  21246. * - Text nodes are created synchronously
  21247. * - TNodes are linked into tree lazily
  21248. *
  21249. * @param tView Current `TView`
  21250. * @parentTNodeIndex index to the parent TNode of this i18n block
  21251. * @param lView Current `LView`
  21252. * @param index Index of `ɵɵi18nStart` instruction.
  21253. * @param message Message to translate.
  21254. * @param subTemplateIndex Index into the sub template of message translation. (ie in case of
  21255. * `ngIf`) (-1 otherwise)
  21256. */
  21257. function i18nStartFirstCreatePass(tView, parentTNodeIndex, lView, index, message, subTemplateIndex) {
  21258. const rootTNode = getCurrentParentTNode();
  21259. const createOpCodes = [];
  21260. const updateOpCodes = [];
  21261. const existingTNodeStack = [[]];
  21262. if (ngDevMode) {
  21263. attachDebugGetter(createOpCodes, i18nCreateOpCodesToString);
  21264. attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
  21265. }
  21266. message = getTranslationForTemplate(message, subTemplateIndex);
  21267. const msgParts = replaceNgsp(message).split(PH_REGEXP);
  21268. for (let i = 0; i < msgParts.length; i++) {
  21269. let value = msgParts[i];
  21270. if ((i & 1) === 0) {
  21271. // Even indexes are text (including bindings & ICU expressions)
  21272. const parts = i18nParseTextIntoPartsAndICU(value);
  21273. for (let j = 0; j < parts.length; j++) {
  21274. let part = parts[j];
  21275. if ((j & 1) === 0) {
  21276. // `j` is odd therefore `part` is string
  21277. const text = part;
  21278. ngDevMode && assertString(text, 'Parsed ICU part should be string');
  21279. if (text !== '') {
  21280. i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodeStack[0], createOpCodes, updateOpCodes, lView, text);
  21281. }
  21282. }
  21283. else {
  21284. // `j` is Even therefor `part` is an `ICUExpression`
  21285. const icuExpression = part;
  21286. // Verify that ICU expression has the right shape. Translations might contain invalid
  21287. // constructions (while original messages were correct), so ICU parsing at runtime may
  21288. // not succeed (thus `icuExpression` remains a string).
  21289. // Note: we intentionally retain the error here by not using `ngDevMode`, because
  21290. // the value can change based on the locale and users aren't guaranteed to hit
  21291. // an invalid string while they're developing.
  21292. if (typeof icuExpression !== 'object') {
  21293. throw new Error(`Unable to parse ICU expression in "${message}" message.`);
  21294. }
  21295. const icuContainerTNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodeStack[0], lView, createOpCodes, ngDevMode ? `ICU ${index}:${icuExpression.mainBinding}` : '', true);
  21296. const icuNodeIndex = icuContainerTNode.index;
  21297. ngDevMode &&
  21298. assertGreaterThanOrEqual(icuNodeIndex, HEADER_OFFSET, 'Index must be in absolute LView offset');
  21299. icuStart(tView, lView, updateOpCodes, parentTNodeIndex, icuExpression, icuNodeIndex);
  21300. }
  21301. }
  21302. }
  21303. else {
  21304. // Odd indexes are placeholders (elements and sub-templates)
  21305. // At this point value is something like: '/#1:2' (originally coming from '�/#1:2�')
  21306. const isClosing = value.charCodeAt(0) === 47 /* CharCode.SLASH */;
  21307. const type = value.charCodeAt(isClosing ? 1 : 0);
  21308. ngDevMode && assertOneOf(type, 42 /* CharCode.STAR */, 35 /* CharCode.HASH */);
  21309. const index = HEADER_OFFSET + Number.parseInt(value.substring((isClosing ? 2 : 1)));
  21310. if (isClosing) {
  21311. existingTNodeStack.shift();
  21312. setCurrentTNode(getCurrentParentTNode(), false);
  21313. }
  21314. else {
  21315. const tNode = createTNodePlaceholder(tView, existingTNodeStack[0], index);
  21316. existingTNodeStack.unshift([]);
  21317. setCurrentTNode(tNode, true);
  21318. }
  21319. }
  21320. }
  21321. tView.data[index] = {
  21322. create: createOpCodes,
  21323. update: updateOpCodes,
  21324. };
  21325. }
  21326. /**
  21327. * Allocate space in i18n Range add create OpCode instruction to create a text or comment node.
  21328. *
  21329. * @param tView Current `TView` needed to allocate space in i18n range.
  21330. * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will be
  21331. * added as part of the `i18nStart` instruction or as part of the `TNode.insertBeforeIndex`.
  21332. * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
  21333. * @param lView Current `LView` needed to allocate space in i18n range.
  21334. * @param createOpCodes Array storing `I18nCreateOpCodes` where new opCodes will be added.
  21335. * @param text Text to be added when the `Text` or `Comment` node will be created.
  21336. * @param isICU true if a `Comment` node for ICU (instead of `Text`) node should be created.
  21337. */
  21338. function createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, text, isICU) {
  21339. const i18nNodeIdx = allocExpando(tView, lView, 1, null);
  21340. let opCode = i18nNodeIdx << I18nCreateOpCode.SHIFT;
  21341. let parentTNode = getCurrentParentTNode();
  21342. if (rootTNode === parentTNode) {
  21343. // FIXME(misko): A null `parentTNode` should represent when we fall of the `LView` boundary.
  21344. // (there is no parent), but in some circumstances (because we are inconsistent about how we set
  21345. // `previousOrParentTNode`) it could point to `rootTNode` So this is a work around.
  21346. parentTNode = null;
  21347. }
  21348. if (parentTNode === null) {
  21349. // If we don't have a parent that means that we can eagerly add nodes.
  21350. // If we have a parent than these nodes can't be added now (as the parent has not been created
  21351. // yet) and instead the `parentTNode` is responsible for adding it. See
  21352. // `TNode.insertBeforeIndex`
  21353. opCode |= I18nCreateOpCode.APPEND_EAGERLY;
  21354. }
  21355. if (isICU) {
  21356. opCode |= I18nCreateOpCode.COMMENT;
  21357. ensureIcuContainerVisitorLoaded(loadIcuContainerVisitor);
  21358. }
  21359. createOpCodes.push(opCode, text === null ? '' : text);
  21360. // We store `{{?}}` so that when looking at debug `TNodeType.template` we can see where the
  21361. // bindings are.
  21362. const tNode = createTNodeAtIndex(tView, i18nNodeIdx, isICU ? 32 /* TNodeType.Icu */ : 1 /* TNodeType.Text */, text === null ? (ngDevMode ? '{{?}}' : '') : text, null);
  21363. addTNodeAndUpdateInsertBeforeIndex(existingTNodes, tNode);
  21364. const tNodeIdx = tNode.index;
  21365. setCurrentTNode(tNode, false /* Text nodes are self closing */);
  21366. if (parentTNode !== null && rootTNode !== parentTNode) {
  21367. // We are a child of deeper node (rather than a direct child of `i18nStart` instruction.)
  21368. // We have to make sure to add ourselves to the parent.
  21369. setTNodeInsertBeforeIndex(parentTNode, tNodeIdx);
  21370. }
  21371. return tNode;
  21372. }
  21373. /**
  21374. * Processes text node in i18n block.
  21375. *
  21376. * Text nodes can have:
  21377. * - Create instruction in `createOpCodes` for creating the text node.
  21378. * - Allocate spec for text node in i18n range of `LView`
  21379. * - If contains binding:
  21380. * - bindings => allocate space in i18n range of `LView` to store the binding value.
  21381. * - populate `updateOpCodes` with update instructions.
  21382. *
  21383. * @param tView Current `TView`
  21384. * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will
  21385. * be added as part of the `i18nStart` instruction or as part of the
  21386. * `TNode.insertBeforeIndex`.
  21387. * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
  21388. * @param createOpCodes Location where the creation OpCodes will be stored.
  21389. * @param lView Current `LView`
  21390. * @param text The translated text (which may contain binding)
  21391. */
  21392. function i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodes, createOpCodes, updateOpCodes, lView, text) {
  21393. const hasBinding = text.match(BINDING_REGEXP);
  21394. const tNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, hasBinding ? null : text, false);
  21395. if (hasBinding) {
  21396. generateBindingUpdateOpCodes(updateOpCodes, text, tNode.index, null, 0, null);
  21397. }
  21398. }
  21399. /**
  21400. * See `i18nAttributes` above.
  21401. */
  21402. function i18nAttributesFirstPass(tView, index, values) {
  21403. const previousElement = getCurrentTNode();
  21404. const previousElementIndex = previousElement.index;
  21405. const updateOpCodes = [];
  21406. if (ngDevMode) {
  21407. attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
  21408. }
  21409. if (tView.firstCreatePass && tView.data[index] === null) {
  21410. for (let i = 0; i < values.length; i += 2) {
  21411. const attrName = values[i];
  21412. const message = values[i + 1];
  21413. if (message !== '') {
  21414. // Check if attribute value contains an ICU and throw an error if that's the case.
  21415. // ICUs in element attributes are not supported.
  21416. // Note: we intentionally retain the error here by not using `ngDevMode`, because
  21417. // the `value` can change based on the locale and users aren't guaranteed to hit
  21418. // an invalid string while they're developing.
  21419. if (ICU_REGEXP.test(message)) {
  21420. throw new Error(`ICU expressions are not supported in attributes. Message: "${message}".`);
  21421. }
  21422. // i18n attributes that hit this code path are guaranteed to have bindings, because
  21423. // the compiler treats static i18n attributes as regular attribute bindings.
  21424. // Since this may not be the first i18n attribute on this element we need to pass in how
  21425. // many previous bindings there have already been.
  21426. generateBindingUpdateOpCodes(updateOpCodes, message, previousElementIndex, attrName, countBindings(updateOpCodes), null);
  21427. }
  21428. }
  21429. tView.data[index] = updateOpCodes;
  21430. }
  21431. }
  21432. /**
  21433. * Generate the OpCodes to update the bindings of a string.
  21434. *
  21435. * @param updateOpCodes Place where the update opcodes will be stored.
  21436. * @param str The string containing the bindings.
  21437. * @param destinationNode Index of the destination node which will receive the binding.
  21438. * @param attrName Name of the attribute, if the string belongs to an attribute.
  21439. * @param sanitizeFn Sanitization function used to sanitize the string after update, if necessary.
  21440. * @param bindingStart The lView index of the next expression that can be bound via an opCode.
  21441. * @returns The mask value for these bindings
  21442. */
  21443. function generateBindingUpdateOpCodes(updateOpCodes, str, destinationNode, attrName, bindingStart, sanitizeFn) {
  21444. ngDevMode &&
  21445. assertGreaterThanOrEqual(destinationNode, HEADER_OFFSET, 'Index must be in absolute LView offset');
  21446. const maskIndex = updateOpCodes.length; // Location of mask
  21447. const sizeIndex = maskIndex + 1; // location of size for skipping
  21448. updateOpCodes.push(null, null); // Alloc space for mask and size
  21449. const startIndex = maskIndex + 2; // location of first allocation.
  21450. if (ngDevMode) {
  21451. attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
  21452. }
  21453. const textParts = str.split(BINDING_REGEXP);
  21454. let mask = 0;
  21455. for (let j = 0; j < textParts.length; j++) {
  21456. const textValue = textParts[j];
  21457. if (j & 1) {
  21458. // Odd indexes are bindings
  21459. const bindingIndex = bindingStart + parseInt(textValue, 10);
  21460. updateOpCodes.push(-1 - bindingIndex);
  21461. mask = mask | toMaskBit(bindingIndex);
  21462. }
  21463. else if (textValue !== '') {
  21464. // Even indexes are text
  21465. updateOpCodes.push(textValue);
  21466. }
  21467. }
  21468. updateOpCodes.push(destinationNode << 2 /* I18nUpdateOpCode.SHIFT_REF */ |
  21469. (attrName ? 1 /* I18nUpdateOpCode.Attr */ : 0 /* I18nUpdateOpCode.Text */));
  21470. if (attrName) {
  21471. updateOpCodes.push(attrName, sanitizeFn);
  21472. }
  21473. updateOpCodes[maskIndex] = mask;
  21474. updateOpCodes[sizeIndex] = updateOpCodes.length - startIndex;
  21475. return mask;
  21476. }
  21477. /**
  21478. * Count the number of bindings in the given `opCodes`.
  21479. *
  21480. * It could be possible to speed this up, by passing the number of bindings found back from
  21481. * `generateBindingUpdateOpCodes()` to `i18nAttributesFirstPass()` but this would then require more
  21482. * complexity in the code and/or transient objects to be created.
  21483. *
  21484. * Since this function is only called once when the template is instantiated, is trivial in the
  21485. * first instance (since `opCodes` will be an empty array), and it is not common for elements to
  21486. * contain multiple i18n bound attributes, it seems like this is a reasonable compromise.
  21487. */
  21488. function countBindings(opCodes) {
  21489. let count = 0;
  21490. for (let i = 0; i < opCodes.length; i++) {
  21491. const opCode = opCodes[i];
  21492. // Bindings are negative numbers.
  21493. if (typeof opCode === 'number' && opCode < 0) {
  21494. count++;
  21495. }
  21496. }
  21497. return count;
  21498. }
  21499. /**
  21500. * Convert binding index to mask bit.
  21501. *
  21502. * Each index represents a single bit on the bit-mask. Because bit-mask only has 32 bits, we make
  21503. * the 32nd bit share all masks for all bindings higher than 32. Since it is extremely rare to
  21504. * have more than 32 bindings this will be hit very rarely. The downside of hitting this corner
  21505. * case is that we will execute binding code more often than necessary. (penalty of performance)
  21506. */
  21507. function toMaskBit(bindingIndex) {
  21508. return 1 << Math.min(bindingIndex, 31);
  21509. }
  21510. function isRootTemplateMessage(subTemplateIndex) {
  21511. return subTemplateIndex === -1;
  21512. }
  21513. /**
  21514. * Removes everything inside the sub-templates of a message.
  21515. */
  21516. function removeInnerTemplateTranslation(message) {
  21517. let match;
  21518. let res = '';
  21519. let index = 0;
  21520. let inTemplate = false;
  21521. let tagMatched;
  21522. while ((match = SUBTEMPLATE_REGEXP.exec(message)) !== null) {
  21523. if (!inTemplate) {
  21524. res += message.substring(index, match.index + match[0].length);
  21525. tagMatched = match[1];
  21526. inTemplate = true;
  21527. }
  21528. else {
  21529. if (match[0] === `${MARKER}/*${tagMatched}${MARKER}`) {
  21530. index = match.index;
  21531. inTemplate = false;
  21532. }
  21533. }
  21534. }
  21535. ngDevMode &&
  21536. assertEqual(inTemplate, false, `Tag mismatch: unable to find the end of the sub-template in the translation "${message}"`);
  21537. res += message.slice(index);
  21538. return res;
  21539. }
  21540. /**
  21541. * Extracts a part of a message and removes the rest.
  21542. *
  21543. * This method is used for extracting a part of the message associated with a template. A
  21544. * translated message can span multiple templates.
  21545. *
  21546. * Example:
  21547. * ```
  21548. * <div i18n>Translate <span *ngIf>me</span>!</div>
  21549. * ```
  21550. *
  21551. * @param message The message to crop
  21552. * @param subTemplateIndex Index of the sub-template to extract. If undefined it returns the
  21553. * external template and removes all sub-templates.
  21554. */
  21555. function getTranslationForTemplate(message, subTemplateIndex) {
  21556. if (isRootTemplateMessage(subTemplateIndex)) {
  21557. // We want the root template message, ignore all sub-templates
  21558. return removeInnerTemplateTranslation(message);
  21559. }
  21560. else {
  21561. // We want a specific sub-template
  21562. const start = message.indexOf(`:${subTemplateIndex}${MARKER}`) + 2 + subTemplateIndex.toString().length;
  21563. const end = message.search(new RegExp(`${MARKER}\\/\\*\\d+:${subTemplateIndex}${MARKER}`));
  21564. return removeInnerTemplateTranslation(message.substring(start, end));
  21565. }
  21566. }
  21567. /**
  21568. * Generate the OpCodes for ICU expressions.
  21569. *
  21570. * @param icuExpression
  21571. * @param index Index where the anchor is stored and an optional `TIcuContainerNode`
  21572. * - `lView[anchorIdx]` points to a `Comment` node representing the anchor for the ICU.
  21573. * - `tView.data[anchorIdx]` points to the `TIcuContainerNode` if ICU is root (`null` otherwise)
  21574. */
  21575. function icuStart(tView, lView, updateOpCodes, parentIdx, icuExpression, anchorIdx) {
  21576. ngDevMode && assertDefined(icuExpression, 'ICU expression must be defined');
  21577. let bindingMask = 0;
  21578. const tIcu = {
  21579. type: icuExpression.type,
  21580. currentCaseLViewIndex: allocExpando(tView, lView, 1, null),
  21581. anchorIdx,
  21582. cases: [],
  21583. create: [],
  21584. remove: [],
  21585. update: []
  21586. };
  21587. addUpdateIcuSwitch(updateOpCodes, icuExpression, anchorIdx);
  21588. setTIcu(tView, anchorIdx, tIcu);
  21589. const values = icuExpression.values;
  21590. for (let i = 0; i < values.length; i++) {
  21591. // Each value is an array of strings & other ICU expressions
  21592. const valueArr = values[i];
  21593. const nestedIcus = [];
  21594. for (let j = 0; j < valueArr.length; j++) {
  21595. const value = valueArr[j];
  21596. if (typeof value !== 'string') {
  21597. // It is an nested ICU expression
  21598. const icuIndex = nestedIcus.push(value) - 1;
  21599. // Replace nested ICU expression by a comment node
  21600. valueArr[j] = `<!--�${icuIndex}�-->`;
  21601. }
  21602. }
  21603. bindingMask = parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, icuExpression.cases[i], valueArr.join(''), nestedIcus) |
  21604. bindingMask;
  21605. }
  21606. if (bindingMask) {
  21607. addUpdateIcuUpdate(updateOpCodes, bindingMask, anchorIdx);
  21608. }
  21609. }
  21610. /**
  21611. * Parses text containing an ICU expression and produces a JSON object for it.
  21612. * Original code from closure library, modified for Angular.
  21613. *
  21614. * @param pattern Text containing an ICU expression that needs to be parsed.
  21615. *
  21616. */
  21617. function parseICUBlock(pattern) {
  21618. const cases = [];
  21619. const values = [];
  21620. let icuType = 1 /* IcuType.plural */;
  21621. let mainBinding = 0;
  21622. pattern = pattern.replace(ICU_BLOCK_REGEXP, function (str, binding, type) {
  21623. if (type === 'select') {
  21624. icuType = 0 /* IcuType.select */;
  21625. }
  21626. else {
  21627. icuType = 1 /* IcuType.plural */;
  21628. }
  21629. mainBinding = parseInt(binding.slice(1), 10);
  21630. return '';
  21631. });
  21632. const parts = i18nParseTextIntoPartsAndICU(pattern);
  21633. // Looking for (key block)+ sequence. One of the keys has to be "other".
  21634. for (let pos = 0; pos < parts.length;) {
  21635. let key = parts[pos++].trim();
  21636. if (icuType === 1 /* IcuType.plural */) {
  21637. // Key can be "=x", we just want "x"
  21638. key = key.replace(/\s*(?:=)?(\w+)\s*/, '$1');
  21639. }
  21640. if (key.length) {
  21641. cases.push(key);
  21642. }
  21643. const blocks = i18nParseTextIntoPartsAndICU(parts[pos++]);
  21644. if (cases.length > values.length) {
  21645. values.push(blocks);
  21646. }
  21647. }
  21648. // TODO(ocombe): support ICU expressions in attributes, see #21615
  21649. return { type: icuType, mainBinding: mainBinding, cases, values };
  21650. }
  21651. /**
  21652. * Breaks pattern into strings and top level {...} blocks.
  21653. * Can be used to break a message into text and ICU expressions, or to break an ICU expression
  21654. * into keys and cases. Original code from closure library, modified for Angular.
  21655. *
  21656. * @param pattern (sub)Pattern to be broken.
  21657. * @returns An `Array<string|IcuExpression>` where:
  21658. * - odd positions: `string` => text between ICU expressions
  21659. * - even positions: `ICUExpression` => ICU expression parsed into `ICUExpression` record.
  21660. */
  21661. function i18nParseTextIntoPartsAndICU(pattern) {
  21662. if (!pattern) {
  21663. return [];
  21664. }
  21665. let prevPos = 0;
  21666. const braceStack = [];
  21667. const results = [];
  21668. const braces = /[{}]/g;
  21669. // lastIndex doesn't get set to 0 so we have to.
  21670. braces.lastIndex = 0;
  21671. let match;
  21672. while (match = braces.exec(pattern)) {
  21673. const pos = match.index;
  21674. if (match[0] == '}') {
  21675. braceStack.pop();
  21676. if (braceStack.length == 0) {
  21677. // End of the block.
  21678. const block = pattern.substring(prevPos, pos);
  21679. if (ICU_BLOCK_REGEXP.test(block)) {
  21680. results.push(parseICUBlock(block));
  21681. }
  21682. else {
  21683. results.push(block);
  21684. }
  21685. prevPos = pos + 1;
  21686. }
  21687. }
  21688. else {
  21689. if (braceStack.length == 0) {
  21690. const substring = pattern.substring(prevPos, pos);
  21691. results.push(substring);
  21692. prevPos = pos + 1;
  21693. }
  21694. braceStack.push('{');
  21695. }
  21696. }
  21697. const substring = pattern.substring(prevPos);
  21698. results.push(substring);
  21699. return results;
  21700. }
  21701. /**
  21702. * Parses a node, its children and its siblings, and generates the mutate & update OpCodes.
  21703. *
  21704. */
  21705. function parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, caseName, unsafeCaseHtml, nestedIcus) {
  21706. const create = [];
  21707. const remove = [];
  21708. const update = [];
  21709. if (ngDevMode) {
  21710. attachDebugGetter(create, icuCreateOpCodesToString);
  21711. attachDebugGetter(remove, i18nRemoveOpCodesToString);
  21712. attachDebugGetter(update, i18nUpdateOpCodesToString);
  21713. }
  21714. tIcu.cases.push(caseName);
  21715. tIcu.create.push(create);
  21716. tIcu.remove.push(remove);
  21717. tIcu.update.push(update);
  21718. const inertBodyHelper = getInertBodyHelper(getDocument());
  21719. const inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeCaseHtml);
  21720. ngDevMode && assertDefined(inertBodyElement, 'Unable to generate inert body element');
  21721. const inertRootNode = getTemplateContent(inertBodyElement) || inertBodyElement;
  21722. if (inertRootNode) {
  21723. return walkIcuTree(tView, tIcu, lView, updateOpCodes, create, remove, update, inertRootNode, parentIdx, nestedIcus, 0);
  21724. }
  21725. else {
  21726. return 0;
  21727. }
  21728. }
  21729. function walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, parentNode, parentIdx, nestedIcus, depth) {
  21730. let bindingMask = 0;
  21731. let currentNode = parentNode.firstChild;
  21732. while (currentNode) {
  21733. const newIndex = allocExpando(tView, lView, 1, null);
  21734. switch (currentNode.nodeType) {
  21735. case Node.ELEMENT_NODE:
  21736. const element = currentNode;
  21737. const tagName = element.tagName.toLowerCase();
  21738. if (VALID_ELEMENTS.hasOwnProperty(tagName)) {
  21739. addCreateNodeAndAppend(create, ELEMENT_MARKER, tagName, parentIdx, newIndex);
  21740. tView.data[newIndex] = tagName;
  21741. const elAttrs = element.attributes;
  21742. for (let i = 0; i < elAttrs.length; i++) {
  21743. const attr = elAttrs.item(i);
  21744. const lowerAttrName = attr.name.toLowerCase();
  21745. const hasBinding = !!attr.value.match(BINDING_REGEXP);
  21746. // we assume the input string is safe, unless it's using a binding
  21747. if (hasBinding) {
  21748. if (VALID_ATTRS.hasOwnProperty(lowerAttrName)) {
  21749. if (URI_ATTRS[lowerAttrName]) {
  21750. generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, _sanitizeUrl);
  21751. }
  21752. else {
  21753. generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, null);
  21754. }
  21755. }
  21756. else {
  21757. ngDevMode &&
  21758. console.warn(`WARNING: ignoring unsafe attribute value ` +
  21759. `${lowerAttrName} on element ${tagName} ` +
  21760. `(see ${XSS_SECURITY_URL})`);
  21761. }
  21762. }
  21763. else {
  21764. addCreateAttribute(create, newIndex, attr);
  21765. }
  21766. }
  21767. // Parse the children of this node (if any)
  21768. bindingMask = walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, currentNode, newIndex, nestedIcus, depth + 1) |
  21769. bindingMask;
  21770. addRemoveNode(remove, newIndex, depth);
  21771. }
  21772. break;
  21773. case Node.TEXT_NODE:
  21774. const value = currentNode.textContent || '';
  21775. const hasBinding = value.match(BINDING_REGEXP);
  21776. addCreateNodeAndAppend(create, null, hasBinding ? '' : value, parentIdx, newIndex);
  21777. addRemoveNode(remove, newIndex, depth);
  21778. if (hasBinding) {
  21779. bindingMask =
  21780. generateBindingUpdateOpCodes(update, value, newIndex, null, 0, null) | bindingMask;
  21781. }
  21782. break;
  21783. case Node.COMMENT_NODE:
  21784. // Check if the comment node is a placeholder for a nested ICU
  21785. const isNestedIcu = NESTED_ICU.exec(currentNode.textContent || '');
  21786. if (isNestedIcu) {
  21787. const nestedIcuIndex = parseInt(isNestedIcu[1], 10);
  21788. const icuExpression = nestedIcus[nestedIcuIndex];
  21789. // Create the comment node that will anchor the ICU expression
  21790. addCreateNodeAndAppend(create, ICU_MARKER, ngDevMode ? `nested ICU ${nestedIcuIndex}` : '', parentIdx, newIndex);
  21791. icuStart(tView, lView, sharedUpdateOpCodes, parentIdx, icuExpression, newIndex);
  21792. addRemoveNestedIcu(remove, newIndex, depth);
  21793. }
  21794. break;
  21795. }
  21796. currentNode = currentNode.nextSibling;
  21797. }
  21798. return bindingMask;
  21799. }
  21800. function addRemoveNode(remove, index, depth) {
  21801. if (depth === 0) {
  21802. remove.push(index);
  21803. }
  21804. }
  21805. function addRemoveNestedIcu(remove, index, depth) {
  21806. if (depth === 0) {
  21807. remove.push(~index); // remove ICU at `index`
  21808. remove.push(index); // remove ICU comment at `index`
  21809. }
  21810. }
  21811. function addUpdateIcuSwitch(update, icuExpression, index) {
  21812. update.push(toMaskBit(icuExpression.mainBinding), 2, -1 - icuExpression.mainBinding, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 2 /* I18nUpdateOpCode.IcuSwitch */);
  21813. }
  21814. function addUpdateIcuUpdate(update, bindingMask, index) {
  21815. update.push(bindingMask, 1, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 3 /* I18nUpdateOpCode.IcuUpdate */);
  21816. }
  21817. function addCreateNodeAndAppend(create, marker, text, appendToParentIdx, createAtIdx) {
  21818. if (marker !== null) {
  21819. create.push(marker);
  21820. }
  21821. create.push(text, createAtIdx, icuCreateOpCode(0 /* IcuCreateOpCode.AppendChild */, appendToParentIdx, createAtIdx));
  21822. }
  21823. function addCreateAttribute(create, newIndex, attr) {
  21824. create.push(newIndex << 1 /* IcuCreateOpCode.SHIFT_REF */ | 1 /* IcuCreateOpCode.Attr */, attr.name, attr.value);
  21825. }
  21826. // i18nPostprocess consts
  21827. const ROOT_TEMPLATE_ID = 0;
  21828. const PP_MULTI_VALUE_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]/;
  21829. const PP_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]|(�\/?\*\d+:\d+�)/g;
  21830. const PP_ICU_VARS_REGEXP = /({\s*)(VAR_(PLURAL|SELECT)(_\d+)?)(\s*,)/g;
  21831. const PP_ICU_PLACEHOLDERS_REGEXP = /{([A-Z0-9_]+)}/g;
  21832. const PP_ICUS_REGEXP = /�I18N_EXP_(ICU(_\d+)?)�/g;
  21833. const PP_CLOSE_TEMPLATE_REGEXP = /\/\*/;
  21834. const PP_TEMPLATE_ID_REGEXP = /\d+\:(\d+)/;
  21835. /**
  21836. * Handles message string post-processing for internationalization.
  21837. *
  21838. * Handles message string post-processing by transforming it from intermediate
  21839. * format (that might contain some markers that we need to replace) to the final
  21840. * form, consumable by i18nStart instruction. Post processing steps include:
  21841. *
  21842. * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�])
  21843. * 2. Replace all ICU vars (like "VAR_PLURAL")
  21844. * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
  21845. * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�)
  21846. * in case multiple ICUs have the same placeholder name
  21847. *
  21848. * @param message Raw translation string for post processing
  21849. * @param replacements Set of replacements that should be applied
  21850. *
  21851. * @returns Transformed string that can be consumed by i18nStart instruction
  21852. *
  21853. * @codeGenApi
  21854. */
  21855. function i18nPostprocess(message, replacements = {}) {
  21856. /**
  21857. * Step 1: resolve all multi-value placeholders like [�#5�|�*1:1��#2:1�|�#4:1�]
  21858. *
  21859. * Note: due to the way we process nested templates (BFS), multi-value placeholders are typically
  21860. * grouped by templates, for example: [�#5�|�#6�|�#1:1�|�#3:2�] where �#5� and �#6� belong to root
  21861. * template, �#1:1� belong to nested template with index 1 and �#1:2� - nested template with index
  21862. * 3. However in real templates the order might be different: i.e. �#1:1� and/or �#3:2� may go in
  21863. * front of �#6�. The post processing step restores the right order by keeping track of the
  21864. * template id stack and looks for placeholders that belong to the currently active template.
  21865. */
  21866. let result = message;
  21867. if (PP_MULTI_VALUE_PLACEHOLDERS_REGEXP.test(message)) {
  21868. const matches = {};
  21869. const templateIdsStack = [ROOT_TEMPLATE_ID];
  21870. result = result.replace(PP_PLACEHOLDERS_REGEXP, (m, phs, tmpl) => {
  21871. const content = phs || tmpl;
  21872. const placeholders = matches[content] || [];
  21873. if (!placeholders.length) {
  21874. content.split('|').forEach((placeholder) => {
  21875. const match = placeholder.match(PP_TEMPLATE_ID_REGEXP);
  21876. const templateId = match ? parseInt(match[1], 10) : ROOT_TEMPLATE_ID;
  21877. const isCloseTemplateTag = PP_CLOSE_TEMPLATE_REGEXP.test(placeholder);
  21878. placeholders.push([templateId, isCloseTemplateTag, placeholder]);
  21879. });
  21880. matches[content] = placeholders;
  21881. }
  21882. if (!placeholders.length) {
  21883. throw new Error(`i18n postprocess: unmatched placeholder - ${content}`);
  21884. }
  21885. const currentTemplateId = templateIdsStack[templateIdsStack.length - 1];
  21886. let idx = 0;
  21887. // find placeholder index that matches current template id
  21888. for (let i = 0; i < placeholders.length; i++) {
  21889. if (placeholders[i][0] === currentTemplateId) {
  21890. idx = i;
  21891. break;
  21892. }
  21893. }
  21894. // update template id stack based on the current tag extracted
  21895. const [templateId, isCloseTemplateTag, placeholder] = placeholders[idx];
  21896. if (isCloseTemplateTag) {
  21897. templateIdsStack.pop();
  21898. }
  21899. else if (currentTemplateId !== templateId) {
  21900. templateIdsStack.push(templateId);
  21901. }
  21902. // remove processed tag from the list
  21903. placeholders.splice(idx, 1);
  21904. return placeholder;
  21905. });
  21906. }
  21907. // return current result if no replacements specified
  21908. if (!Object.keys(replacements).length) {
  21909. return result;
  21910. }
  21911. /**
  21912. * Step 2: replace all ICU vars (like "VAR_PLURAL")
  21913. */
  21914. result = result.replace(PP_ICU_VARS_REGEXP, (match, start, key, _type, _idx, end) => {
  21915. return replacements.hasOwnProperty(key) ? `${start}${replacements[key]}${end}` : match;
  21916. });
  21917. /**
  21918. * Step 3: replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
  21919. */
  21920. result = result.replace(PP_ICU_PLACEHOLDERS_REGEXP, (match, key) => {
  21921. return replacements.hasOwnProperty(key) ? replacements[key] : match;
  21922. });
  21923. /**
  21924. * Step 4: replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�) in case
  21925. * multiple ICUs have the same placeholder name
  21926. */
  21927. result = result.replace(PP_ICUS_REGEXP, (match, key) => {
  21928. if (replacements.hasOwnProperty(key)) {
  21929. const list = replacements[key];
  21930. if (!list.length) {
  21931. throw new Error(`i18n postprocess: unmatched ICU - ${match} with key: ${key}`);
  21932. }
  21933. return list.shift();
  21934. }
  21935. return match;
  21936. });
  21937. return result;
  21938. }
  21939. /**
  21940. * Marks a block of text as translatable.
  21941. *
  21942. * The instructions `i18nStart` and `i18nEnd` mark the translation block in the template.
  21943. * The translation `message` is the value which is locale specific. The translation string may
  21944. * contain placeholders which associate inner elements and sub-templates within the translation.
  21945. *
  21946. * The translation `message` placeholders are:
  21947. * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be
  21948. * interpolated into. The placeholder `index` points to the expression binding index. An optional
  21949. * `block` that matches the sub-template in which it was declared.
  21950. * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning
  21951. * and end of DOM element that were embedded in the original translation block. The placeholder
  21952. * `index` points to the element index in the template instructions set. An optional `block` that
  21953. * matches the sub-template in which it was declared.
  21954. * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be
  21955. * split up and translated separately in each angular template function. The `index` points to the
  21956. * `template` instruction index. A `block` that matches the sub-template in which it was declared.
  21957. *
  21958. * @param index A unique index of the translation in the static block.
  21959. * @param messageIndex An index of the translation message from the `def.consts` array.
  21960. * @param subTemplateIndex Optional sub-template index in the `message`.
  21961. *
  21962. * @codeGenApi
  21963. */
  21964. function ɵɵi18nStart(index, messageIndex, subTemplateIndex = -1) {
  21965. const tView = getTView();
  21966. const lView = getLView();
  21967. const adjustedIndex = HEADER_OFFSET + index;
  21968. ngDevMode && assertDefined(tView, `tView should be defined`);
  21969. const message = getConstant(tView.consts, messageIndex);
  21970. const parentTNode = getCurrentParentTNode();
  21971. if (tView.firstCreatePass) {
  21972. i18nStartFirstCreatePass(tView, parentTNode === null ? 0 : parentTNode.index, lView, adjustedIndex, message, subTemplateIndex);
  21973. }
  21974. // Set a flag that this LView has i18n blocks.
  21975. // The flag is later used to determine whether this component should
  21976. // be hydrated (currently hydration is not supported for i18n blocks).
  21977. if (tView.type === 2 /* TViewType.Embedded */) {
  21978. // Annotate host component's LView (not embedded view's LView),
  21979. // since hydration can be skipped on per-component basis only.
  21980. const componentLView = lView[DECLARATION_COMPONENT_VIEW];
  21981. componentLView[FLAGS] |= 32 /* LViewFlags.HasI18n */;
  21982. }
  21983. else {
  21984. lView[FLAGS] |= 32 /* LViewFlags.HasI18n */;
  21985. }
  21986. const tI18n = tView.data[adjustedIndex];
  21987. const sameViewParentTNode = parentTNode === lView[T_HOST] ? null : parentTNode;
  21988. const parentRNode = getClosestRElement(tView, sameViewParentTNode, lView);
  21989. // If `parentTNode` is an `ElementContainer` than it has `<!--ng-container--->`.
  21990. // When we do inserts we have to make sure to insert in front of `<!--ng-container--->`.
  21991. const insertInFrontOf = parentTNode && (parentTNode.type & 8 /* TNodeType.ElementContainer */) ?
  21992. lView[parentTNode.index] :
  21993. null;
  21994. applyCreateOpCodes(lView, tI18n.create, parentRNode, insertInFrontOf);
  21995. setInI18nBlock(true);
  21996. }
  21997. /**
  21998. * Translates a translation block marked by `i18nStart` and `i18nEnd`. It inserts the text/ICU nodes
  21999. * into the render tree, moves the placeholder nodes and removes the deleted nodes.
  22000. *
  22001. * @codeGenApi
  22002. */
  22003. function ɵɵi18nEnd() {
  22004. setInI18nBlock(false);
  22005. }
  22006. /**
  22007. *
  22008. * Use this instruction to create a translation block that doesn't contain any placeholder.
  22009. * It calls both {@link i18nStart} and {@link i18nEnd} in one instruction.
  22010. *
  22011. * The translation `message` is the value which is locale specific. The translation string may
  22012. * contain placeholders which associate inner elements and sub-templates within the translation.
  22013. *
  22014. * The translation `message` placeholders are:
  22015. * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be
  22016. * interpolated into. The placeholder `index` points to the expression binding index. An optional
  22017. * `block` that matches the sub-template in which it was declared.
  22018. * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning
  22019. * and end of DOM element that were embedded in the original translation block. The placeholder
  22020. * `index` points to the element index in the template instructions set. An optional `block` that
  22021. * matches the sub-template in which it was declared.
  22022. * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be
  22023. * split up and translated separately in each angular template function. The `index` points to the
  22024. * `template` instruction index. A `block` that matches the sub-template in which it was declared.
  22025. *
  22026. * @param index A unique index of the translation in the static block.
  22027. * @param messageIndex An index of the translation message from the `def.consts` array.
  22028. * @param subTemplateIndex Optional sub-template index in the `message`.
  22029. *
  22030. * @codeGenApi
  22031. */
  22032. function ɵɵi18n(index, messageIndex, subTemplateIndex) {
  22033. ɵɵi18nStart(index, messageIndex, subTemplateIndex);
  22034. ɵɵi18nEnd();
  22035. }
  22036. /**
  22037. * Marks a list of attributes as translatable.
  22038. *
  22039. * @param index A unique index in the static block
  22040. * @param values
  22041. *
  22042. * @codeGenApi
  22043. */
  22044. function ɵɵi18nAttributes(index, attrsIndex) {
  22045. const tView = getTView();
  22046. ngDevMode && assertDefined(tView, `tView should be defined`);
  22047. const attrs = getConstant(tView.consts, attrsIndex);
  22048. i18nAttributesFirstPass(tView, index + HEADER_OFFSET, attrs);
  22049. }
  22050. /**
  22051. * Stores the values of the bindings during each update cycle in order to determine if we need to
  22052. * update the translated nodes.
  22053. *
  22054. * @param value The binding's value
  22055. * @returns This function returns itself so that it may be chained
  22056. * (e.g. `i18nExp(ctx.name)(ctx.title)`)
  22057. *
  22058. * @codeGenApi
  22059. */
  22060. function ɵɵi18nExp(value) {
  22061. const lView = getLView();
  22062. setMaskBit(bindingUpdated(lView, nextBindingIndex(), value));
  22063. return ɵɵi18nExp;
  22064. }
  22065. /**
  22066. * Updates a translation block or an i18n attribute when the bindings have changed.
  22067. *
  22068. * @param index Index of either {@link i18nStart} (translation block) or {@link i18nAttributes}
  22069. * (i18n attribute) on which it should update the content.
  22070. *
  22071. * @codeGenApi
  22072. */
  22073. function ɵɵi18nApply(index) {
  22074. applyI18n(getTView(), getLView(), index + HEADER_OFFSET);
  22075. }
  22076. /**
  22077. * Handles message string post-processing for internationalization.
  22078. *
  22079. * Handles message string post-processing by transforming it from intermediate
  22080. * format (that might contain some markers that we need to replace) to the final
  22081. * form, consumable by i18nStart instruction. Post processing steps include:
  22082. *
  22083. * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�])
  22084. * 2. Replace all ICU vars (like "VAR_PLURAL")
  22085. * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
  22086. * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�)
  22087. * in case multiple ICUs have the same placeholder name
  22088. *
  22089. * @param message Raw translation string for post processing
  22090. * @param replacements Set of replacements that should be applied
  22091. *
  22092. * @returns Transformed string that can be consumed by i18nStart instruction
  22093. *
  22094. * @codeGenApi
  22095. */
  22096. function ɵɵi18nPostprocess(message, replacements = {}) {
  22097. return i18nPostprocess(message, replacements);
  22098. }
  22099. /**
  22100. * Creates runtime data structures for `{#defer}` blocks.
  22101. *
  22102. * @param index The index of the defer block in the data array
  22103. * @param deferredDepsFn Function that contains dependencies for this defer block
  22104. *
  22105. * @codeGenApi
  22106. */
  22107. function ɵɵdefer(index, deferredDepsFn) {
  22108. // TODO: implement runtime logic.
  22109. }
  22110. /*
  22111. * This file re-exports all symbols contained in this directory.
  22112. *
  22113. * Why is this file not `index.ts`?
  22114. *
  22115. * There seems to be an inconsistent path resolution of an `index.ts` file
  22116. * when only the parent directory is referenced. This could be due to the
  22117. * node module resolution configuration differing from rollup and/or typescript.
  22118. *
  22119. * With commit
  22120. * https://github.com/angular/angular/commit/d5e3f2c64bd13ce83e7c70788b7fc514ca4a9918
  22121. * the `instructions.ts` file was moved to `instructions/instructions.ts` and an
  22122. * `index.ts` file was used to re-export everything. Having had file names that were
  22123. * importing from `instructions' directly (not the from the sub file or the `index.ts`
  22124. * file) caused strange CI issues. `index.ts` had to be renamed to `all.ts` for this
  22125. * to work.
  22126. *
  22127. * Jira Issue = FW-1184
  22128. */
  22129. /**
  22130. * Resolves the providers which are defined in the DirectiveDef.
  22131. *
  22132. * When inserting the tokens and the factories in their respective arrays, we can assume that
  22133. * this method is called first for the component (if any), and then for other directives on the same
  22134. * node.
  22135. * As a consequence,the providers are always processed in that order:
  22136. * 1) The view providers of the component
  22137. * 2) The providers of the component
  22138. * 3) The providers of the other directives
  22139. * This matches the structure of the injectables arrays of a view (for each node).
  22140. * So the tokens and the factories can be pushed at the end of the arrays, except
  22141. * in one case for multi providers.
  22142. *
  22143. * @param def the directive definition
  22144. * @param providers: Array of `providers`.
  22145. * @param viewProviders: Array of `viewProviders`.
  22146. */
  22147. function providersResolver(def, providers, viewProviders) {
  22148. const tView = getTView();
  22149. if (tView.firstCreatePass) {
  22150. const isComponent = isComponentDef(def);
  22151. // The list of view providers is processed first, and the flags are updated
  22152. resolveProvider(viewProviders, tView.data, tView.blueprint, isComponent, true);
  22153. // Then, the list of providers is processed, and the flags are updated
  22154. resolveProvider(providers, tView.data, tView.blueprint, isComponent, false);
  22155. }
  22156. }
  22157. /**
  22158. * Resolves a provider and publishes it to the DI system.
  22159. */
  22160. function resolveProvider(provider, tInjectables, lInjectablesBlueprint, isComponent, isViewProvider) {
  22161. provider = resolveForwardRef(provider);
  22162. if (Array.isArray(provider)) {
  22163. // Recursively call `resolveProvider`
  22164. // Recursion is OK in this case because this code will not be in hot-path once we implement
  22165. // cloning of the initial state.
  22166. for (let i = 0; i < provider.length; i++) {
  22167. resolveProvider(provider[i], tInjectables, lInjectablesBlueprint, isComponent, isViewProvider);
  22168. }
  22169. }
  22170. else {
  22171. const tView = getTView();
  22172. const lView = getLView();
  22173. const tNode = getCurrentTNode();
  22174. let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide);
  22175. const providerFactory = providerToFactory(provider);
  22176. if (ngDevMode) {
  22177. const injector = new NodeInjector(tNode, lView);
  22178. runInInjectorProfilerContext(injector, token, () => {
  22179. emitProviderConfiguredEvent(provider, isViewProvider);
  22180. });
  22181. }
  22182. const beginIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
  22183. const endIndex = tNode.directiveStart;
  22184. const cptViewProvidersCount = tNode.providerIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */;
  22185. if (isTypeProvider(provider) || !provider.multi) {
  22186. // Single provider case: the factory is created and pushed immediately
  22187. const factory = new NodeInjectorFactory(providerFactory, isViewProvider, ɵɵdirectiveInject);
  22188. const existingFactoryIndex = indexOf(token, tInjectables, isViewProvider ? beginIndex : beginIndex + cptViewProvidersCount, endIndex);
  22189. if (existingFactoryIndex === -1) {
  22190. diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token);
  22191. registerDestroyHooksIfSupported(tView, provider, tInjectables.length);
  22192. tInjectables.push(token);
  22193. tNode.directiveStart++;
  22194. tNode.directiveEnd++;
  22195. if (isViewProvider) {
  22196. tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */;
  22197. }
  22198. lInjectablesBlueprint.push(factory);
  22199. lView.push(factory);
  22200. }
  22201. else {
  22202. lInjectablesBlueprint[existingFactoryIndex] = factory;
  22203. lView[existingFactoryIndex] = factory;
  22204. }
  22205. }
  22206. else {
  22207. // Multi provider case:
  22208. // We create a multi factory which is going to aggregate all the values.
  22209. // Since the output of such a factory depends on content or view injection,
  22210. // we create two of them, which are linked together.
  22211. //
  22212. // The first one (for view providers) is always in the first block of the injectables array,
  22213. // and the second one (for providers) is always in the second block.
  22214. // This is important because view providers have higher priority. When a multi token
  22215. // is being looked up, the view providers should be found first.
  22216. // Note that it is not possible to have a multi factory in the third block (directive block).
  22217. //
  22218. // The algorithm to process multi providers is as follows:
  22219. // 1) If the multi provider comes from the `viewProviders` of the component:
  22220. // a) If the special view providers factory doesn't exist, it is created and pushed.
  22221. // b) Else, the multi provider is added to the existing multi factory.
  22222. // 2) If the multi provider comes from the `providers` of the component or of another
  22223. // directive:
  22224. // a) If the multi factory doesn't exist, it is created and provider pushed into it.
  22225. // It is also linked to the multi factory for view providers, if it exists.
  22226. // b) Else, the multi provider is added to the existing multi factory.
  22227. const existingProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex + cptViewProvidersCount, endIndex);
  22228. const existingViewProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex, beginIndex + cptViewProvidersCount);
  22229. const doesProvidersFactoryExist = existingProvidersFactoryIndex >= 0 &&
  22230. lInjectablesBlueprint[existingProvidersFactoryIndex];
  22231. const doesViewProvidersFactoryExist = existingViewProvidersFactoryIndex >= 0 &&
  22232. lInjectablesBlueprint[existingViewProvidersFactoryIndex];
  22233. if (isViewProvider && !doesViewProvidersFactoryExist ||
  22234. !isViewProvider && !doesProvidersFactoryExist) {
  22235. // Cases 1.a and 2.a
  22236. diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token);
  22237. const factory = multiFactory(isViewProvider ? multiViewProvidersFactoryResolver : multiProvidersFactoryResolver, lInjectablesBlueprint.length, isViewProvider, isComponent, providerFactory);
  22238. if (!isViewProvider && doesViewProvidersFactoryExist) {
  22239. lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory;
  22240. }
  22241. registerDestroyHooksIfSupported(tView, provider, tInjectables.length, 0);
  22242. tInjectables.push(token);
  22243. tNode.directiveStart++;
  22244. tNode.directiveEnd++;
  22245. if (isViewProvider) {
  22246. tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */;
  22247. }
  22248. lInjectablesBlueprint.push(factory);
  22249. lView.push(factory);
  22250. }
  22251. else {
  22252. // Cases 1.b and 2.b
  22253. const indexInFactory = multiFactoryAdd(lInjectablesBlueprint[isViewProvider ? existingViewProvidersFactoryIndex :
  22254. existingProvidersFactoryIndex], providerFactory, !isViewProvider && isComponent);
  22255. registerDestroyHooksIfSupported(tView, provider, existingProvidersFactoryIndex > -1 ? existingProvidersFactoryIndex :
  22256. existingViewProvidersFactoryIndex, indexInFactory);
  22257. }
  22258. if (!isViewProvider && isComponent && doesViewProvidersFactoryExist) {
  22259. lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders++;
  22260. }
  22261. }
  22262. }
  22263. }
  22264. /**
  22265. * Registers the `ngOnDestroy` hook of a provider, if the provider supports destroy hooks.
  22266. * @param tView `TView` in which to register the hook.
  22267. * @param provider Provider whose hook should be registered.
  22268. * @param contextIndex Index under which to find the context for the hook when it's being invoked.
  22269. * @param indexInFactory Only required for `multi` providers. Index of the provider in the multi
  22270. * provider factory.
  22271. */
  22272. function registerDestroyHooksIfSupported(tView, provider, contextIndex, indexInFactory) {
  22273. const providerIsTypeProvider = isTypeProvider(provider);
  22274. const providerIsClassProvider = isClassProvider(provider);
  22275. if (providerIsTypeProvider || providerIsClassProvider) {
  22276. // Resolve forward references as `useClass` can hold a forward reference.
  22277. const classToken = providerIsClassProvider ? resolveForwardRef(provider.useClass) : provider;
  22278. const prototype = classToken.prototype;
  22279. const ngOnDestroy = prototype.ngOnDestroy;
  22280. if (ngOnDestroy) {
  22281. const hooks = tView.destroyHooks || (tView.destroyHooks = []);
  22282. if (!providerIsTypeProvider && provider.multi) {
  22283. ngDevMode &&
  22284. assertDefined(indexInFactory, 'indexInFactory when registering multi factory destroy hook');
  22285. const existingCallbacksIndex = hooks.indexOf(contextIndex);
  22286. if (existingCallbacksIndex === -1) {
  22287. hooks.push(contextIndex, [indexInFactory, ngOnDestroy]);
  22288. }
  22289. else {
  22290. hooks[existingCallbacksIndex + 1].push(indexInFactory, ngOnDestroy);
  22291. }
  22292. }
  22293. else {
  22294. hooks.push(contextIndex, ngOnDestroy);
  22295. }
  22296. }
  22297. }
  22298. }
  22299. /**
  22300. * Add a factory in a multi factory.
  22301. * @returns Index at which the factory was inserted.
  22302. */
  22303. function multiFactoryAdd(multiFactory, factory, isComponentProvider) {
  22304. if (isComponentProvider) {
  22305. multiFactory.componentProviders++;
  22306. }
  22307. return multiFactory.multi.push(factory) - 1;
  22308. }
  22309. /**
  22310. * Returns the index of item in the array, but only in the begin to end range.
  22311. */
  22312. function indexOf(item, arr, begin, end) {
  22313. for (let i = begin; i < end; i++) {
  22314. if (arr[i] === item)
  22315. return i;
  22316. }
  22317. return -1;
  22318. }
  22319. /**
  22320. * Use this with `multi` `providers`.
  22321. */
  22322. function multiProvidersFactoryResolver(_, tData, lData, tNode) {
  22323. return multiResolve(this.multi, []);
  22324. }
  22325. /**
  22326. * Use this with `multi` `viewProviders`.
  22327. *
  22328. * This factory knows how to concatenate itself with the existing `multi` `providers`.
  22329. */
  22330. function multiViewProvidersFactoryResolver(_, tData, lView, tNode) {
  22331. const factories = this.multi;
  22332. let result;
  22333. if (this.providerFactory) {
  22334. const componentCount = this.providerFactory.componentProviders;
  22335. const multiProviders = getNodeInjectable(lView, lView[TVIEW], this.providerFactory.index, tNode);
  22336. // Copy the section of the array which contains `multi` `providers` from the component
  22337. result = multiProviders.slice(0, componentCount);
  22338. // Insert the `viewProvider` instances.
  22339. multiResolve(factories, result);
  22340. // Copy the section of the array which contains `multi` `providers` from other directives
  22341. for (let i = componentCount; i < multiProviders.length; i++) {
  22342. result.push(multiProviders[i]);
  22343. }
  22344. }
  22345. else {
  22346. result = [];
  22347. // Insert the `viewProvider` instances.
  22348. multiResolve(factories, result);
  22349. }
  22350. return result;
  22351. }
  22352. /**
  22353. * Maps an array of factories into an array of values.
  22354. */
  22355. function multiResolve(factories, result) {
  22356. for (let i = 0; i < factories.length; i++) {
  22357. const factory = factories[i];
  22358. result.push(factory());
  22359. }
  22360. return result;
  22361. }
  22362. /**
  22363. * Creates a multi factory.
  22364. */
  22365. function multiFactory(factoryFn, index, isViewProvider, isComponent, f) {
  22366. const factory = new NodeInjectorFactory(factoryFn, isViewProvider, ɵɵdirectiveInject);
  22367. factory.multi = [];
  22368. factory.index = index;
  22369. factory.componentProviders = 0;
  22370. multiFactoryAdd(factory, f, isComponent && !isViewProvider);
  22371. return factory;
  22372. }
  22373. /**
  22374. * This feature resolves the providers of a directive (or component),
  22375. * and publish them into the DI system, making it visible to others for injection.
  22376. *
  22377. * For example:
  22378. * ```ts
  22379. * class ComponentWithProviders {
  22380. * constructor(private greeter: GreeterDE) {}
  22381. *
  22382. * static ɵcmp = defineComponent({
  22383. * type: ComponentWithProviders,
  22384. * selectors: [['component-with-providers']],
  22385. * factory: () => new ComponentWithProviders(directiveInject(GreeterDE as any)),
  22386. * decls: 1,
  22387. * vars: 1,
  22388. * template: function(fs: RenderFlags, ctx: ComponentWithProviders) {
  22389. * if (fs & RenderFlags.Create) {
  22390. * ɵɵtext(0);
  22391. * }
  22392. * if (fs & RenderFlags.Update) {
  22393. * ɵɵtextInterpolate(ctx.greeter.greet());
  22394. * }
  22395. * },
  22396. * features: [ɵɵProvidersFeature([GreeterDE])]
  22397. * });
  22398. * }
  22399. * ```
  22400. *
  22401. * @param definition
  22402. *
  22403. * @codeGenApi
  22404. */
  22405. function ɵɵProvidersFeature(providers, viewProviders = []) {
  22406. return (definition) => {
  22407. definition.providersResolver =
  22408. (def, processProvidersFn) => {
  22409. return providersResolver(def, //
  22410. processProvidersFn ? processProvidersFn(providers) : providers, //
  22411. viewProviders);
  22412. };
  22413. };
  22414. }
  22415. /**
  22416. * Represents an instance of an `NgModule` created by an `NgModuleFactory`.
  22417. * Provides access to the `NgModule` instance and related objects.
  22418. *
  22419. * @publicApi
  22420. */
  22421. class NgModuleRef$1 {
  22422. }
  22423. /**
  22424. * @publicApi
  22425. *
  22426. * @deprecated
  22427. * This class was mostly used as a part of ViewEngine-based JIT API and is no longer needed in Ivy
  22428. * JIT mode. See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes)
  22429. * for additional context. Angular provides APIs that accept NgModule classes directly (such as
  22430. * [PlatformRef.bootstrapModule](api/core/PlatformRef#bootstrapModule) and
  22431. * [createNgModule](api/core/createNgModule)), consider switching to those APIs instead of
  22432. * using factory-based ones.
  22433. */
  22434. class NgModuleFactory$1 {
  22435. }
  22436. /**
  22437. * Returns a new NgModuleRef instance based on the NgModule class and parent injector provided.
  22438. *
  22439. * @param ngModule NgModule class.
  22440. * @param parentInjector Optional injector instance to use as a parent for the module injector. If
  22441. * not provided, `NullInjector` will be used instead.
  22442. * @returns NgModuleRef that represents an NgModule instance.
  22443. *
  22444. * @publicApi
  22445. */
  22446. function createNgModule(ngModule, parentInjector) {
  22447. return new NgModuleRef(ngModule, parentInjector ?? null, []);
  22448. }
  22449. /**
  22450. * The `createNgModule` function alias for backwards-compatibility.
  22451. * Please avoid using it directly and use `createNgModule` instead.
  22452. *
  22453. * @deprecated Use `createNgModule` instead.
  22454. */
  22455. const createNgModuleRef = createNgModule;
  22456. class NgModuleRef extends NgModuleRef$1 {
  22457. constructor(ngModuleType, _parent, additionalProviders) {
  22458. super();
  22459. this._parent = _parent;
  22460. // tslint:disable-next-line:require-internal-with-underscore
  22461. this._bootstrapComponents = [];
  22462. this.destroyCbs = [];
  22463. // When bootstrapping a module we have a dependency graph that looks like this:
  22464. // ApplicationRef -> ComponentFactoryResolver -> NgModuleRef. The problem is that if the
  22465. // module being resolved tries to inject the ComponentFactoryResolver, it'll create a
  22466. // circular dependency which will result in a runtime error, because the injector doesn't
  22467. // exist yet. We work around the issue by creating the ComponentFactoryResolver ourselves
  22468. // and providing it, rather than letting the injector resolve it.
  22469. this.componentFactoryResolver = new ComponentFactoryResolver(this);
  22470. const ngModuleDef = getNgModuleDef(ngModuleType);
  22471. ngDevMode &&
  22472. assertDefined(ngModuleDef, `NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`);
  22473. this._bootstrapComponents = maybeUnwrapFn(ngModuleDef.bootstrap);
  22474. this._r3Injector = createInjectorWithoutInjectorInstances(ngModuleType, _parent, [
  22475. { provide: NgModuleRef$1, useValue: this }, {
  22476. provide: ComponentFactoryResolver$1,
  22477. useValue: this.componentFactoryResolver
  22478. },
  22479. ...additionalProviders
  22480. ], stringify(ngModuleType), new Set(['environment']));
  22481. // We need to resolve the injector types separately from the injector creation, because
  22482. // the module might be trying to use this ref in its constructor for DI which will cause a
  22483. // circular error that will eventually error out, because the injector isn't created yet.
  22484. this._r3Injector.resolveInjectorInitializers();
  22485. this.instance = this._r3Injector.get(ngModuleType);
  22486. }
  22487. get injector() {
  22488. return this._r3Injector;
  22489. }
  22490. destroy() {
  22491. ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
  22492. const injector = this._r3Injector;
  22493. !injector.destroyed && injector.destroy();
  22494. this.destroyCbs.forEach(fn => fn());
  22495. this.destroyCbs = null;
  22496. }
  22497. onDestroy(callback) {
  22498. ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
  22499. this.destroyCbs.push(callback);
  22500. }
  22501. }
  22502. class NgModuleFactory extends NgModuleFactory$1 {
  22503. constructor(moduleType) {
  22504. super();
  22505. this.moduleType = moduleType;
  22506. }
  22507. create(parentInjector) {
  22508. return new NgModuleRef(this.moduleType, parentInjector, []);
  22509. }
  22510. }
  22511. function createNgModuleRefWithProviders(moduleType, parentInjector, additionalProviders) {
  22512. return new NgModuleRef(moduleType, parentInjector, additionalProviders);
  22513. }
  22514. class EnvironmentNgModuleRefAdapter extends NgModuleRef$1 {
  22515. constructor(config) {
  22516. super();
  22517. this.componentFactoryResolver = new ComponentFactoryResolver(this);
  22518. this.instance = null;
  22519. const injector = new R3Injector([
  22520. ...config.providers,
  22521. { provide: NgModuleRef$1, useValue: this },
  22522. { provide: ComponentFactoryResolver$1, useValue: this.componentFactoryResolver },
  22523. ], config.parent || getNullInjector(), config.debugName, new Set(['environment']));
  22524. this.injector = injector;
  22525. if (config.runEnvironmentInitializers) {
  22526. injector.resolveInjectorInitializers();
  22527. }
  22528. }
  22529. destroy() {
  22530. this.injector.destroy();
  22531. }
  22532. onDestroy(callback) {
  22533. this.injector.onDestroy(callback);
  22534. }
  22535. }
  22536. /**
  22537. * Create a new environment injector.
  22538. *
  22539. * Learn more about environment injectors in
  22540. * [this guide](guide/standalone-components#environment-injectors).
  22541. *
  22542. * @param providers An array of providers.
  22543. * @param parent A parent environment injector.
  22544. * @param debugName An optional name for this injector instance, which will be used in error
  22545. * messages.
  22546. *
  22547. * @publicApi
  22548. */
  22549. function createEnvironmentInjector(providers, parent, debugName = null) {
  22550. const adapter = new EnvironmentNgModuleRefAdapter({ providers, parent, debugName, runEnvironmentInitializers: true });
  22551. return adapter.injector;
  22552. }
  22553. /**
  22554. * A service used by the framework to create instances of standalone injectors. Those injectors are
  22555. * created on demand in case of dynamic component instantiation and contain ambient providers
  22556. * collected from the imports graph rooted at a given standalone component.
  22557. */
  22558. class StandaloneService {
  22559. constructor(_injector) {
  22560. this._injector = _injector;
  22561. this.cachedInjectors = new Map();
  22562. }
  22563. getOrCreateStandaloneInjector(componentDef) {
  22564. if (!componentDef.standalone) {
  22565. return null;
  22566. }
  22567. if (!this.cachedInjectors.has(componentDef)) {
  22568. const providers = internalImportProvidersFrom(false, componentDef.type);
  22569. const standaloneInjector = providers.length > 0 ?
  22570. createEnvironmentInjector([providers], this._injector, `Standalone[${componentDef.type.name}]`) :
  22571. null;
  22572. this.cachedInjectors.set(componentDef, standaloneInjector);
  22573. }
  22574. return this.cachedInjectors.get(componentDef);
  22575. }
  22576. ngOnDestroy() {
  22577. try {
  22578. for (const injector of this.cachedInjectors.values()) {
  22579. if (injector !== null) {
  22580. injector.destroy();
  22581. }
  22582. }
  22583. }
  22584. finally {
  22585. this.cachedInjectors.clear();
  22586. }
  22587. }
  22588. /** @nocollapse */
  22589. static { this.ɵprov = ɵɵdefineInjectable({
  22590. token: StandaloneService,
  22591. providedIn: 'environment',
  22592. factory: () => new StandaloneService(ɵɵinject(EnvironmentInjector)),
  22593. }); }
  22594. }
  22595. /**
  22596. * A feature that acts as a setup code for the {@link StandaloneService}.
  22597. *
  22598. * The most important responsibility of this feature is to expose the "getStandaloneInjector"
  22599. * function (an entry points to a standalone injector creation) on a component definition object. We
  22600. * go through the features infrastructure to make sure that the standalone injector creation logic
  22601. * is tree-shakable and not included in applications that don't use standalone components.
  22602. *
  22603. * @codeGenApi
  22604. */
  22605. function ɵɵStandaloneFeature(definition) {
  22606. definition.getStandaloneInjector = (parentInjector) => {
  22607. return parentInjector.get(StandaloneService).getOrCreateStandaloneInjector(definition);
  22608. };
  22609. }
  22610. /**
  22611. * Retrieves the component instance associated with a given DOM element.
  22612. *
  22613. * @usageNotes
  22614. * Given the following DOM structure:
  22615. *
  22616. * ```html
  22617. * <app-root>
  22618. * <div>
  22619. * <child-comp></child-comp>
  22620. * </div>
  22621. * </app-root>
  22622. * ```
  22623. *
  22624. * Calling `getComponent` on `<child-comp>` will return the instance of `ChildComponent`
  22625. * associated with this DOM element.
  22626. *
  22627. * Calling the function on `<app-root>` will return the `MyApp` instance.
  22628. *
  22629. *
  22630. * @param element DOM element from which the component should be retrieved.
  22631. * @returns Component instance associated with the element or `null` if there
  22632. * is no component associated with it.
  22633. *
  22634. * @publicApi
  22635. * @globalApi ng
  22636. */
  22637. function getComponent(element) {
  22638. ngDevMode && assertDomElement(element);
  22639. const context = getLContext(element);
  22640. if (context === null)
  22641. return null;
  22642. if (context.component === undefined) {
  22643. const lView = context.lView;
  22644. if (lView === null) {
  22645. return null;
  22646. }
  22647. context.component = getComponentAtNodeIndex(context.nodeIndex, lView);
  22648. }
  22649. return context.component;
  22650. }
  22651. /**
  22652. * If inside an embedded view (e.g. `*ngIf` or `*ngFor`), retrieves the context of the embedded
  22653. * view that the element is part of. Otherwise retrieves the instance of the component whose view
  22654. * owns the element (in this case, the result is the same as calling `getOwningComponent`).
  22655. *
  22656. * @param element Element for which to get the surrounding component instance.
  22657. * @returns Instance of the component that is around the element or null if the element isn't
  22658. * inside any component.
  22659. *
  22660. * @publicApi
  22661. * @globalApi ng
  22662. */
  22663. function getContext(element) {
  22664. assertDomElement(element);
  22665. const context = getLContext(element);
  22666. const lView = context ? context.lView : null;
  22667. return lView === null ? null : lView[CONTEXT];
  22668. }
  22669. /**
  22670. * Retrieves the component instance whose view contains the DOM element.
  22671. *
  22672. * For example, if `<child-comp>` is used in the template of `<app-comp>`
  22673. * (i.e. a `ViewChild` of `<app-comp>`), calling `getOwningComponent` on `<child-comp>`
  22674. * would return `<app-comp>`.
  22675. *
  22676. * @param elementOrDir DOM element, component or directive instance
  22677. * for which to retrieve the root components.
  22678. * @returns Component instance whose view owns the DOM element or null if the element is not
  22679. * part of a component view.
  22680. *
  22681. * @publicApi
  22682. * @globalApi ng
  22683. */
  22684. function getOwningComponent(elementOrDir) {
  22685. const context = getLContext(elementOrDir);
  22686. let lView = context ? context.lView : null;
  22687. if (lView === null)
  22688. return null;
  22689. let parent;
  22690. while (lView[TVIEW].type === 2 /* TViewType.Embedded */ && (parent = getLViewParent(lView))) {
  22691. lView = parent;
  22692. }
  22693. return lView[FLAGS] & 512 /* LViewFlags.IsRoot */ ? null : lView[CONTEXT];
  22694. }
  22695. /**
  22696. * Retrieves all root components associated with a DOM element, directive or component instance.
  22697. * Root components are those which have been bootstrapped by Angular.
  22698. *
  22699. * @param elementOrDir DOM element, component or directive instance
  22700. * for which to retrieve the root components.
  22701. * @returns Root components associated with the target object.
  22702. *
  22703. * @publicApi
  22704. * @globalApi ng
  22705. */
  22706. function getRootComponents(elementOrDir) {
  22707. const lView = readPatchedLView(elementOrDir);
  22708. return lView !== null ? [getRootContext(lView)] : [];
  22709. }
  22710. /**
  22711. * Retrieves an `Injector` associated with an element, component or directive instance.
  22712. *
  22713. * @param elementOrDir DOM element, component or directive instance for which to
  22714. * retrieve the injector.
  22715. * @returns Injector associated with the element, component or directive instance.
  22716. *
  22717. * @publicApi
  22718. * @globalApi ng
  22719. */
  22720. function getInjector(elementOrDir) {
  22721. const context = getLContext(elementOrDir);
  22722. const lView = context ? context.lView : null;
  22723. if (lView === null)
  22724. return Injector.NULL;
  22725. const tNode = lView[TVIEW].data[context.nodeIndex];
  22726. return new NodeInjector(tNode, lView);
  22727. }
  22728. /**
  22729. * Retrieve a set of injection tokens at a given DOM node.
  22730. *
  22731. * @param element Element for which the injection tokens should be retrieved.
  22732. */
  22733. function getInjectionTokens(element) {
  22734. const context = getLContext(element);
  22735. const lView = context ? context.lView : null;
  22736. if (lView === null)
  22737. return [];
  22738. const tView = lView[TVIEW];
  22739. const tNode = tView.data[context.nodeIndex];
  22740. const providerTokens = [];
  22741. const startIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
  22742. const endIndex = tNode.directiveEnd;
  22743. for (let i = startIndex; i < endIndex; i++) {
  22744. let value = tView.data[i];
  22745. if (isDirectiveDefHack(value)) {
  22746. // The fact that we sometimes store Type and sometimes DirectiveDef in this location is a
  22747. // design flaw. We should always store same type so that we can be monomorphic. The issue
  22748. // is that for Components/Directives we store the def instead the type. The correct behavior
  22749. // is that we should always be storing injectable type in this location.
  22750. value = value.type;
  22751. }
  22752. providerTokens.push(value);
  22753. }
  22754. return providerTokens;
  22755. }
  22756. /**
  22757. * Retrieves directive instances associated with a given DOM node. Does not include
  22758. * component instances.
  22759. *
  22760. * @usageNotes
  22761. * Given the following DOM structure:
  22762. *
  22763. * ```html
  22764. * <app-root>
  22765. * <button my-button></button>
  22766. * <my-comp></my-comp>
  22767. * </app-root>
  22768. * ```
  22769. *
  22770. * Calling `getDirectives` on `<button>` will return an array with an instance of the `MyButton`
  22771. * directive that is associated with the DOM node.
  22772. *
  22773. * Calling `getDirectives` on `<my-comp>` will return an empty array.
  22774. *
  22775. * @param node DOM node for which to get the directives.
  22776. * @returns Array of directives associated with the node.
  22777. *
  22778. * @publicApi
  22779. * @globalApi ng
  22780. */
  22781. function getDirectives(node) {
  22782. // Skip text nodes because we can't have directives associated with them.
  22783. if (node instanceof Text) {
  22784. return [];
  22785. }
  22786. const context = getLContext(node);
  22787. const lView = context ? context.lView : null;
  22788. if (lView === null) {
  22789. return [];
  22790. }
  22791. const tView = lView[TVIEW];
  22792. const nodeIndex = context.nodeIndex;
  22793. if (!tView?.data[nodeIndex]) {
  22794. return [];
  22795. }
  22796. if (context.directives === undefined) {
  22797. context.directives = getDirectivesAtNodeIndex(nodeIndex, lView);
  22798. }
  22799. // The `directives` in this case are a named array called `LComponentView`. Clone the
  22800. // result so we don't expose an internal data structure in the user's console.
  22801. return context.directives === null ? [] : [...context.directives];
  22802. }
  22803. /**
  22804. * Returns the debug (partial) metadata for a particular directive or component instance.
  22805. * The function accepts an instance of a directive or component and returns the corresponding
  22806. * metadata.
  22807. *
  22808. * @param directiveOrComponentInstance Instance of a directive or component
  22809. * @returns metadata of the passed directive or component
  22810. *
  22811. * @publicApi
  22812. * @globalApi ng
  22813. */
  22814. function getDirectiveMetadata$1(directiveOrComponentInstance) {
  22815. const { constructor } = directiveOrComponentInstance;
  22816. if (!constructor) {
  22817. throw new Error('Unable to find the instance constructor');
  22818. }
  22819. // In case a component inherits from a directive, we may have component and directive metadata
  22820. // To ensure we don't get the metadata of the directive, we want to call `getComponentDef` first.
  22821. const componentDef = getComponentDef(constructor);
  22822. if (componentDef) {
  22823. return {
  22824. inputs: componentDef.inputs,
  22825. outputs: componentDef.outputs,
  22826. encapsulation: componentDef.encapsulation,
  22827. changeDetection: componentDef.onPush ? ChangeDetectionStrategy.OnPush :
  22828. ChangeDetectionStrategy.Default
  22829. };
  22830. }
  22831. const directiveDef = getDirectiveDef(constructor);
  22832. if (directiveDef) {
  22833. return { inputs: directiveDef.inputs, outputs: directiveDef.outputs };
  22834. }
  22835. return null;
  22836. }
  22837. /**
  22838. * Retrieve map of local references.
  22839. *
  22840. * The references are retrieved as a map of local reference name to element or directive instance.
  22841. *
  22842. * @param target DOM element, component or directive instance for which to retrieve
  22843. * the local references.
  22844. */
  22845. function getLocalRefs(target) {
  22846. const context = getLContext(target);
  22847. if (context === null)
  22848. return {};
  22849. if (context.localRefs === undefined) {
  22850. const lView = context.lView;
  22851. if (lView === null) {
  22852. return {};
  22853. }
  22854. context.localRefs = discoverLocalRefs(lView, context.nodeIndex);
  22855. }
  22856. return context.localRefs || {};
  22857. }
  22858. /**
  22859. * Retrieves the host element of a component or directive instance.
  22860. * The host element is the DOM element that matched the selector of the directive.
  22861. *
  22862. * @param componentOrDirective Component or directive instance for which the host
  22863. * element should be retrieved.
  22864. * @returns Host element of the target.
  22865. *
  22866. * @publicApi
  22867. * @globalApi ng
  22868. */
  22869. function getHostElement(componentOrDirective) {
  22870. return getLContext(componentOrDirective).native;
  22871. }
  22872. /**
  22873. * Retrieves the rendered text for a given component.
  22874. *
  22875. * This function retrieves the host element of a component and
  22876. * and then returns the `textContent` for that element. This implies
  22877. * that the text returned will include re-projected content of
  22878. * the component as well.
  22879. *
  22880. * @param component The component to return the content text for.
  22881. */
  22882. function getRenderedText(component) {
  22883. const hostElement = getHostElement(component);
  22884. return hostElement.textContent || '';
  22885. }
  22886. /**
  22887. * Retrieves a list of event listeners associated with a DOM element. The list does include host
  22888. * listeners, but it does not include event listeners defined outside of the Angular context
  22889. * (e.g. through `addEventListener`).
  22890. *
  22891. * @usageNotes
  22892. * Given the following DOM structure:
  22893. *
  22894. * ```html
  22895. * <app-root>
  22896. * <div (click)="doSomething()"></div>
  22897. * </app-root>
  22898. * ```
  22899. *
  22900. * Calling `getListeners` on `<div>` will return an object that looks as follows:
  22901. *
  22902. * ```ts
  22903. * {
  22904. * name: 'click',
  22905. * element: <div>,
  22906. * callback: () => doSomething(),
  22907. * useCapture: false
  22908. * }
  22909. * ```
  22910. *
  22911. * @param element Element for which the DOM listeners should be retrieved.
  22912. * @returns Array of event listeners on the DOM element.
  22913. *
  22914. * @publicApi
  22915. * @globalApi ng
  22916. */
  22917. function getListeners(element) {
  22918. ngDevMode && assertDomElement(element);
  22919. const lContext = getLContext(element);
  22920. const lView = lContext === null ? null : lContext.lView;
  22921. if (lView === null)
  22922. return [];
  22923. const tView = lView[TVIEW];
  22924. const lCleanup = lView[CLEANUP];
  22925. const tCleanup = tView.cleanup;
  22926. const listeners = [];
  22927. if (tCleanup && lCleanup) {
  22928. for (let i = 0; i < tCleanup.length;) {
  22929. const firstParam = tCleanup[i++];
  22930. const secondParam = tCleanup[i++];
  22931. if (typeof firstParam === 'string') {
  22932. const name = firstParam;
  22933. const listenerElement = unwrapRNode(lView[secondParam]);
  22934. const callback = lCleanup[tCleanup[i++]];
  22935. const useCaptureOrIndx = tCleanup[i++];
  22936. // if useCaptureOrIndx is boolean then report it as is.
  22937. // if useCaptureOrIndx is positive number then it in unsubscribe method
  22938. // if useCaptureOrIndx is negative number then it is a Subscription
  22939. const type = (typeof useCaptureOrIndx === 'boolean' || useCaptureOrIndx >= 0) ? 'dom' : 'output';
  22940. const useCapture = typeof useCaptureOrIndx === 'boolean' ? useCaptureOrIndx : false;
  22941. if (element == listenerElement) {
  22942. listeners.push({ element, name, callback, useCapture, type });
  22943. }
  22944. }
  22945. }
  22946. }
  22947. listeners.sort(sortListeners);
  22948. return listeners;
  22949. }
  22950. function sortListeners(a, b) {
  22951. if (a.name == b.name)
  22952. return 0;
  22953. return a.name < b.name ? -1 : 1;
  22954. }
  22955. /**
  22956. * This function should not exist because it is megamorphic and only mostly correct.
  22957. *
  22958. * See call site for more info.
  22959. */
  22960. function isDirectiveDefHack(obj) {
  22961. return obj.type !== undefined && obj.declaredInputs !== undefined &&
  22962. obj.findHostDirectiveDefs !== undefined;
  22963. }
  22964. /**
  22965. * Retrieve the component `LView` from component/element.
  22966. *
  22967. * NOTE: `LView` is a private and should not be leaked outside.
  22968. * Don't export this method to `ng.*` on window.
  22969. *
  22970. * @param target DOM element or component instance for which to retrieve the LView.
  22971. */
  22972. function getComponentLView(target) {
  22973. const lContext = getLContext(target);
  22974. const nodeIndx = lContext.nodeIndex;
  22975. const lView = lContext.lView;
  22976. ngDevMode && assertLView(lView);
  22977. const componentLView = lView[nodeIndx];
  22978. ngDevMode && assertLView(componentLView);
  22979. return componentLView;
  22980. }
  22981. /** Asserts that a value is a DOM Element. */
  22982. function assertDomElement(value) {
  22983. if (typeof Element !== 'undefined' && !(value instanceof Element)) {
  22984. throw new Error('Expecting instance of DOM Element');
  22985. }
  22986. }
  22987. /**
  22988. * Adds decorator, constructor, and property metadata to a given type via static metadata fields
  22989. * on the type.
  22990. *
  22991. * These metadata fields can later be read with Angular's `ReflectionCapabilities` API.
  22992. *
  22993. * Calls to `setClassMetadata` can be guarded by ngDevMode, resulting in the metadata assignments
  22994. * being tree-shaken away during production builds.
  22995. */
  22996. function setClassMetadata(type, decorators, ctorParameters, propDecorators) {
  22997. return noSideEffects(() => {
  22998. const clazz = type;
  22999. if (decorators !== null) {
  23000. if (clazz.hasOwnProperty('decorators') && clazz.decorators !== undefined) {
  23001. clazz.decorators.push(...decorators);
  23002. }
  23003. else {
  23004. clazz.decorators = decorators;
  23005. }
  23006. }
  23007. if (ctorParameters !== null) {
  23008. // Rather than merging, clobber the existing parameters. If other projects exist which
  23009. // use tsickle-style annotations and reflect over them in the same way, this could
  23010. // cause issues, but that is vanishingly unlikely.
  23011. clazz.ctorParameters = ctorParameters;
  23012. }
  23013. if (propDecorators !== null) {
  23014. // The property decorator objects are merged as it is possible different fields have
  23015. // different decorator types. Decorators on individual fields are not merged, as it's
  23016. // also incredibly unlikely that a field will be decorated both with an Angular
  23017. // decorator and a non-Angular decorator that's also been downleveled.
  23018. if (clazz.hasOwnProperty('propDecorators') && clazz.propDecorators !== undefined) {
  23019. clazz.propDecorators = { ...clazz.propDecorators, ...propDecorators };
  23020. }
  23021. else {
  23022. clazz.propDecorators = propDecorators;
  23023. }
  23024. }
  23025. });
  23026. }
  23027. /**
  23028. * Bindings for pure functions are stored after regular bindings.
  23029. *
  23030. * |-------decls------|---------vars---------| |----- hostVars (dir1) ------|
  23031. * ------------------------------------------------------------------------------------------
  23032. * | nodes/refs/pipes | bindings | fn slots | injector | dir1 | host bindings | host slots |
  23033. * ------------------------------------------------------------------------------------------
  23034. * ^ ^
  23035. * TView.bindingStartIndex TView.expandoStartIndex
  23036. *
  23037. * Pure function instructions are given an offset from the binding root. Adding the offset to the
  23038. * binding root gives the first index where the bindings are stored. In component views, the binding
  23039. * root is the bindingStartIndex. In host bindings, the binding root is the expandoStartIndex +
  23040. * any directive instances + any hostVars in directives evaluated before it.
  23041. *
  23042. * See VIEW_DATA.md for more information about host binding resolution.
  23043. */
  23044. /**
  23045. * If the value hasn't been saved, calls the pure function to store and return the
  23046. * value. If it has been saved, returns the saved value.
  23047. *
  23048. * @param slotOffset the offset from binding root to the reserved slot
  23049. * @param pureFn Function that returns a value
  23050. * @param thisArg Optional calling context of pureFn
  23051. * @returns value
  23052. *
  23053. * @codeGenApi
  23054. */
  23055. function ɵɵpureFunction0(slotOffset, pureFn, thisArg) {
  23056. const bindingIndex = getBindingRoot() + slotOffset;
  23057. const lView = getLView();
  23058. return lView[bindingIndex] === NO_CHANGE ?
  23059. updateBinding(lView, bindingIndex, thisArg ? pureFn.call(thisArg) : pureFn()) :
  23060. getBinding(lView, bindingIndex);
  23061. }
  23062. /**
  23063. * If the value of the provided exp has changed, calls the pure function to return
  23064. * an updated value. Or if the value has not changed, returns cached value.
  23065. *
  23066. * @param slotOffset the offset from binding root to the reserved slot
  23067. * @param pureFn Function that returns an updated value
  23068. * @param exp Updated expression value
  23069. * @param thisArg Optional calling context of pureFn
  23070. * @returns Updated or cached value
  23071. *
  23072. * @codeGenApi
  23073. */
  23074. function ɵɵpureFunction1(slotOffset, pureFn, exp, thisArg) {
  23075. return pureFunction1Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp, thisArg);
  23076. }
  23077. /**
  23078. * If the value of any provided exp has changed, calls the pure function to return
  23079. * an updated value. Or if no values have changed, returns cached value.
  23080. *
  23081. * @param slotOffset the offset from binding root to the reserved slot
  23082. * @param pureFn
  23083. * @param exp1
  23084. * @param exp2
  23085. * @param thisArg Optional calling context of pureFn
  23086. * @returns Updated or cached value
  23087. *
  23088. * @codeGenApi
  23089. */
  23090. function ɵɵpureFunction2(slotOffset, pureFn, exp1, exp2, thisArg) {
  23091. return pureFunction2Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, thisArg);
  23092. }
  23093. /**
  23094. * If the value of any provided exp has changed, calls the pure function to return
  23095. * an updated value. Or if no values have changed, returns cached value.
  23096. *
  23097. * @param slotOffset the offset from binding root to the reserved slot
  23098. * @param pureFn
  23099. * @param exp1
  23100. * @param exp2
  23101. * @param exp3
  23102. * @param thisArg Optional calling context of pureFn
  23103. * @returns Updated or cached value
  23104. *
  23105. * @codeGenApi
  23106. */
  23107. function ɵɵpureFunction3(slotOffset, pureFn, exp1, exp2, exp3, thisArg) {
  23108. return pureFunction3Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, thisArg);
  23109. }
  23110. /**
  23111. * If the value of any provided exp has changed, calls the pure function to return
  23112. * an updated value. Or if no values have changed, returns cached value.
  23113. *
  23114. * @param slotOffset the offset from binding root to the reserved slot
  23115. * @param pureFn
  23116. * @param exp1
  23117. * @param exp2
  23118. * @param exp3
  23119. * @param exp4
  23120. * @param thisArg Optional calling context of pureFn
  23121. * @returns Updated or cached value
  23122. *
  23123. * @codeGenApi
  23124. */
  23125. function ɵɵpureFunction4(slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) {
  23126. return pureFunction4Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg);
  23127. }
  23128. /**
  23129. * If the value of any provided exp has changed, calls the pure function to return
  23130. * an updated value. Or if no values have changed, returns cached value.
  23131. *
  23132. * @param slotOffset the offset from binding root to the reserved slot
  23133. * @param pureFn
  23134. * @param exp1
  23135. * @param exp2
  23136. * @param exp3
  23137. * @param exp4
  23138. * @param exp5
  23139. * @param thisArg Optional calling context of pureFn
  23140. * @returns Updated or cached value
  23141. *
  23142. * @codeGenApi
  23143. */
  23144. function ɵɵpureFunction5(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, thisArg) {
  23145. const bindingIndex = getBindingRoot() + slotOffset;
  23146. const lView = getLView();
  23147. const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23148. return bindingUpdated(lView, bindingIndex + 4, exp5) || different ?
  23149. updateBinding(lView, bindingIndex + 5, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) :
  23150. pureFn(exp1, exp2, exp3, exp4, exp5)) :
  23151. getBinding(lView, bindingIndex + 5);
  23152. }
  23153. /**
  23154. * If the value of any provided exp has changed, calls the pure function to return
  23155. * an updated value. Or if no values have changed, returns cached value.
  23156. *
  23157. * @param slotOffset the offset from binding root to the reserved slot
  23158. * @param pureFn
  23159. * @param exp1
  23160. * @param exp2
  23161. * @param exp3
  23162. * @param exp4
  23163. * @param exp5
  23164. * @param exp6
  23165. * @param thisArg Optional calling context of pureFn
  23166. * @returns Updated or cached value
  23167. *
  23168. * @codeGenApi
  23169. */
  23170. function ɵɵpureFunction6(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, thisArg) {
  23171. const bindingIndex = getBindingRoot() + slotOffset;
  23172. const lView = getLView();
  23173. const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23174. return bindingUpdated2(lView, bindingIndex + 4, exp5, exp6) || different ?
  23175. updateBinding(lView, bindingIndex + 6, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) :
  23176. pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) :
  23177. getBinding(lView, bindingIndex + 6);
  23178. }
  23179. /**
  23180. * If the value of any provided exp has changed, calls the pure function to return
  23181. * an updated value. Or if no values have changed, returns cached value.
  23182. *
  23183. * @param slotOffset the offset from binding root to the reserved slot
  23184. * @param pureFn
  23185. * @param exp1
  23186. * @param exp2
  23187. * @param exp3
  23188. * @param exp4
  23189. * @param exp5
  23190. * @param exp6
  23191. * @param exp7
  23192. * @param thisArg Optional calling context of pureFn
  23193. * @returns Updated or cached value
  23194. *
  23195. * @codeGenApi
  23196. */
  23197. function ɵɵpureFunction7(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, thisArg) {
  23198. const bindingIndex = getBindingRoot() + slotOffset;
  23199. const lView = getLView();
  23200. let different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23201. return bindingUpdated3(lView, bindingIndex + 4, exp5, exp6, exp7) || different ?
  23202. updateBinding(lView, bindingIndex + 7, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) :
  23203. pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) :
  23204. getBinding(lView, bindingIndex + 7);
  23205. }
  23206. /**
  23207. * If the value of any provided exp has changed, calls the pure function to return
  23208. * an updated value. Or if no values have changed, returns cached value.
  23209. *
  23210. * @param slotOffset the offset from binding root to the reserved slot
  23211. * @param pureFn
  23212. * @param exp1
  23213. * @param exp2
  23214. * @param exp3
  23215. * @param exp4
  23216. * @param exp5
  23217. * @param exp6
  23218. * @param exp7
  23219. * @param exp8
  23220. * @param thisArg Optional calling context of pureFn
  23221. * @returns Updated or cached value
  23222. *
  23223. * @codeGenApi
  23224. */
  23225. function ɵɵpureFunction8(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8, thisArg) {
  23226. const bindingIndex = getBindingRoot() + slotOffset;
  23227. const lView = getLView();
  23228. const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
  23229. return bindingUpdated4(lView, bindingIndex + 4, exp5, exp6, exp7, exp8) || different ?
  23230. updateBinding(lView, bindingIndex + 8, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) :
  23231. pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) :
  23232. getBinding(lView, bindingIndex + 8);
  23233. }
  23234. /**
  23235. * pureFunction instruction that can support any number of bindings.
  23236. *
  23237. * If the value of any provided exp has changed, calls the pure function to return
  23238. * an updated value. Or if no values have changed, returns cached value.
  23239. *
  23240. * @param slotOffset the offset from binding root to the reserved slot
  23241. * @param pureFn A pure function that takes binding values and builds an object or array
  23242. * containing those values.
  23243. * @param exps An array of binding values
  23244. * @param thisArg Optional calling context of pureFn
  23245. * @returns Updated or cached value
  23246. *
  23247. * @codeGenApi
  23248. */
  23249. function ɵɵpureFunctionV(slotOffset, pureFn, exps, thisArg) {
  23250. return pureFunctionVInternal(getLView(), getBindingRoot(), slotOffset, pureFn, exps, thisArg);
  23251. }
  23252. /**
  23253. * Results of a pure function invocation are stored in LView in a dedicated slot that is initialized
  23254. * to NO_CHANGE. In rare situations a pure pipe might throw an exception on the very first
  23255. * invocation and not produce any valid results. In this case LView would keep holding the NO_CHANGE
  23256. * value. The NO_CHANGE is not something that we can use in expressions / bindings thus we convert
  23257. * it to `undefined`.
  23258. */
  23259. function getPureFunctionReturnValue(lView, returnValueIndex) {
  23260. ngDevMode && assertIndexInRange(lView, returnValueIndex);
  23261. const lastReturnValue = lView[returnValueIndex];
  23262. return lastReturnValue === NO_CHANGE ? undefined : lastReturnValue;
  23263. }
  23264. /**
  23265. * If the value of the provided exp has changed, calls the pure function to return
  23266. * an updated value. Or if the value has not changed, returns cached value.
  23267. *
  23268. * @param lView LView in which the function is being executed.
  23269. * @param bindingRoot Binding root index.
  23270. * @param slotOffset the offset from binding root to the reserved slot
  23271. * @param pureFn Function that returns an updated value
  23272. * @param exp Updated expression value
  23273. * @param thisArg Optional calling context of pureFn
  23274. * @returns Updated or cached value
  23275. */
  23276. function pureFunction1Internal(lView, bindingRoot, slotOffset, pureFn, exp, thisArg) {
  23277. const bindingIndex = bindingRoot + slotOffset;
  23278. return bindingUpdated(lView, bindingIndex, exp) ?
  23279. updateBinding(lView, bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) :
  23280. getPureFunctionReturnValue(lView, bindingIndex + 1);
  23281. }
  23282. /**
  23283. * If the value of any provided exp has changed, calls the pure function to return
  23284. * an updated value. Or if no values have changed, returns cached value.
  23285. *
  23286. * @param lView LView in which the function is being executed.
  23287. * @param bindingRoot Binding root index.
  23288. * @param slotOffset the offset from binding root to the reserved slot
  23289. * @param pureFn
  23290. * @param exp1
  23291. * @param exp2
  23292. * @param thisArg Optional calling context of pureFn
  23293. * @returns Updated or cached value
  23294. */
  23295. function pureFunction2Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, thisArg) {
  23296. const bindingIndex = bindingRoot + slotOffset;
  23297. return bindingUpdated2(lView, bindingIndex, exp1, exp2) ?
  23298. updateBinding(lView, bindingIndex + 2, thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) :
  23299. getPureFunctionReturnValue(lView, bindingIndex + 2);
  23300. }
  23301. /**
  23302. * If the value of any provided exp has changed, calls the pure function to return
  23303. * an updated value. Or if no values have changed, returns cached value.
  23304. *
  23305. * @param lView LView in which the function is being executed.
  23306. * @param bindingRoot Binding root index.
  23307. * @param slotOffset the offset from binding root to the reserved slot
  23308. * @param pureFn
  23309. * @param exp1
  23310. * @param exp2
  23311. * @param exp3
  23312. * @param thisArg Optional calling context of pureFn
  23313. * @returns Updated or cached value
  23314. */
  23315. function pureFunction3Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, thisArg) {
  23316. const bindingIndex = bindingRoot + slotOffset;
  23317. return bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) ?
  23318. updateBinding(lView, bindingIndex + 3, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) :
  23319. getPureFunctionReturnValue(lView, bindingIndex + 3);
  23320. }
  23321. /**
  23322. * If the value of any provided exp has changed, calls the pure function to return
  23323. * an updated value. Or if no values have changed, returns cached value.
  23324. *
  23325. * @param lView LView in which the function is being executed.
  23326. * @param bindingRoot Binding root index.
  23327. * @param slotOffset the offset from binding root to the reserved slot
  23328. * @param pureFn
  23329. * @param exp1
  23330. * @param exp2
  23331. * @param exp3
  23332. * @param exp4
  23333. * @param thisArg Optional calling context of pureFn
  23334. * @returns Updated or cached value
  23335. *
  23336. */
  23337. function pureFunction4Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) {
  23338. const bindingIndex = bindingRoot + slotOffset;
  23339. return bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) ?
  23340. updateBinding(lView, bindingIndex + 4, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) :
  23341. getPureFunctionReturnValue(lView, bindingIndex + 4);
  23342. }
  23343. /**
  23344. * pureFunction instruction that can support any number of bindings.
  23345. *
  23346. * If the value of any provided exp has changed, calls the pure function to return
  23347. * an updated value. Or if no values have changed, returns cached value.
  23348. *
  23349. * @param lView LView in which the function is being executed.
  23350. * @param bindingRoot Binding root index.
  23351. * @param slotOffset the offset from binding root to the reserved slot
  23352. * @param pureFn A pure function that takes binding values and builds an object or array
  23353. * containing those values.
  23354. * @param exps An array of binding values
  23355. * @param thisArg Optional calling context of pureFn
  23356. * @returns Updated or cached value
  23357. */
  23358. function pureFunctionVInternal(lView, bindingRoot, slotOffset, pureFn, exps, thisArg) {
  23359. let bindingIndex = bindingRoot + slotOffset;
  23360. let different = false;
  23361. for (let i = 0; i < exps.length; i++) {
  23362. bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true);
  23363. }
  23364. return different ? updateBinding(lView, bindingIndex, pureFn.apply(thisArg, exps)) :
  23365. getPureFunctionReturnValue(lView, bindingIndex);
  23366. }
  23367. /**
  23368. * Create a pipe.
  23369. *
  23370. * @param index Pipe index where the pipe will be stored.
  23371. * @param pipeName The name of the pipe
  23372. * @returns T the instance of the pipe.
  23373. *
  23374. * @codeGenApi
  23375. */
  23376. function ɵɵpipe(index, pipeName) {
  23377. const tView = getTView();
  23378. let pipeDef;
  23379. const adjustedIndex = index + HEADER_OFFSET;
  23380. if (tView.firstCreatePass) {
  23381. // The `getPipeDef` throws if a pipe with a given name is not found
  23382. // (so we use non-null assertion below).
  23383. pipeDef = getPipeDef(pipeName, tView.pipeRegistry);
  23384. tView.data[adjustedIndex] = pipeDef;
  23385. if (pipeDef.onDestroy) {
  23386. (tView.destroyHooks ??= []).push(adjustedIndex, pipeDef.onDestroy);
  23387. }
  23388. }
  23389. else {
  23390. pipeDef = tView.data[adjustedIndex];
  23391. }
  23392. const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true));
  23393. let previousInjectorProfilerContext;
  23394. if (ngDevMode) {
  23395. previousInjectorProfilerContext = setInjectorProfilerContext({
  23396. injector: new NodeInjector(getCurrentTNode(), getLView()),
  23397. token: pipeDef.type
  23398. });
  23399. }
  23400. const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject);
  23401. try {
  23402. // DI for pipes is supposed to behave like directives when placed on a component
  23403. // host node, which means that we have to disable access to `viewProviders`.
  23404. const previousIncludeViewProviders = setIncludeViewProviders(false);
  23405. const pipeInstance = pipeFactory();
  23406. setIncludeViewProviders(previousIncludeViewProviders);
  23407. store(tView, getLView(), adjustedIndex, pipeInstance);
  23408. return pipeInstance;
  23409. }
  23410. finally {
  23411. // we have to restore the injector implementation in finally, just in case the creation of the
  23412. // pipe throws an error.
  23413. setInjectImplementation(previousInjectImplementation);
  23414. ngDevMode && setInjectorProfilerContext(previousInjectorProfilerContext);
  23415. }
  23416. }
  23417. /**
  23418. * Searches the pipe registry for a pipe with the given name. If one is found,
  23419. * returns the pipe. Otherwise, an error is thrown because the pipe cannot be resolved.
  23420. *
  23421. * @param name Name of pipe to resolve
  23422. * @param registry Full list of available pipes
  23423. * @returns Matching PipeDef
  23424. */
  23425. function getPipeDef(name, registry) {
  23426. if (registry) {
  23427. if (ngDevMode) {
  23428. const pipes = registry.filter(pipe => pipe.name === name);
  23429. // TODO: Throw an error in the next major
  23430. if (pipes.length > 1) {
  23431. console.warn(formatRuntimeError(313 /* RuntimeErrorCode.MULTIPLE_MATCHING_PIPES */, getMultipleMatchingPipesMessage(name)));
  23432. }
  23433. }
  23434. for (let i = registry.length - 1; i >= 0; i--) {
  23435. const pipeDef = registry[i];
  23436. if (name === pipeDef.name) {
  23437. return pipeDef;
  23438. }
  23439. }
  23440. }
  23441. if (ngDevMode) {
  23442. throw new RuntimeError(-302 /* RuntimeErrorCode.PIPE_NOT_FOUND */, getPipeNotFoundErrorMessage(name));
  23443. }
  23444. }
  23445. /**
  23446. * Generates a helpful error message for the user when multiple pipes match the name.
  23447. *
  23448. * @param name Name of the pipe
  23449. * @returns The error message
  23450. */
  23451. function getMultipleMatchingPipesMessage(name) {
  23452. const lView = getLView();
  23453. const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
  23454. const context = declarationLView[CONTEXT];
  23455. const hostIsStandalone = isHostComponentStandalone(lView);
  23456. const componentInfoMessage = context ? ` in the '${context.constructor.name}' component` : '';
  23457. const verifyMessage = `check ${hostIsStandalone ? '\'@Component.imports\' of this component' :
  23458. 'the imports of this module'}`;
  23459. const errorMessage = `Multiple pipes match the name \`${name}\`${componentInfoMessage}. ${verifyMessage}`;
  23460. return errorMessage;
  23461. }
  23462. /**
  23463. * Generates a helpful error message for the user when a pipe is not found.
  23464. *
  23465. * @param name Name of the missing pipe
  23466. * @returns The error message
  23467. */
  23468. function getPipeNotFoundErrorMessage(name) {
  23469. const lView = getLView();
  23470. const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
  23471. const context = declarationLView[CONTEXT];
  23472. const hostIsStandalone = isHostComponentStandalone(lView);
  23473. const componentInfoMessage = context ? ` in the '${context.constructor.name}' component` : '';
  23474. const verifyMessage = `Verify that it is ${hostIsStandalone ? 'included in the \'@Component.imports\' of this component' :
  23475. 'declared or imported in this module'}`;
  23476. const errorMessage = `The pipe '${name}' could not be found${componentInfoMessage}. ${verifyMessage}`;
  23477. return errorMessage;
  23478. }
  23479. /**
  23480. * Invokes a pipe with 1 arguments.
  23481. *
  23482. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23483. * the pipe only when an input to the pipe changes.
  23484. *
  23485. * @param index Pipe index where the pipe was stored on creation.
  23486. * @param slotOffset the offset in the reserved slot space
  23487. * @param v1 1st argument to {@link PipeTransform#transform}.
  23488. *
  23489. * @codeGenApi
  23490. */
  23491. function ɵɵpipeBind1(index, slotOffset, v1) {
  23492. const adjustedIndex = index + HEADER_OFFSET;
  23493. const lView = getLView();
  23494. const pipeInstance = load(lView, adjustedIndex);
  23495. return isPure(lView, adjustedIndex) ?
  23496. pureFunction1Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) :
  23497. pipeInstance.transform(v1);
  23498. }
  23499. /**
  23500. * Invokes a pipe with 2 arguments.
  23501. *
  23502. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23503. * the pipe only when an input to the pipe changes.
  23504. *
  23505. * @param index Pipe index where the pipe was stored on creation.
  23506. * @param slotOffset the offset in the reserved slot space
  23507. * @param v1 1st argument to {@link PipeTransform#transform}.
  23508. * @param v2 2nd argument to {@link PipeTransform#transform}.
  23509. *
  23510. * @codeGenApi
  23511. */
  23512. function ɵɵpipeBind2(index, slotOffset, v1, v2) {
  23513. const adjustedIndex = index + HEADER_OFFSET;
  23514. const lView = getLView();
  23515. const pipeInstance = load(lView, adjustedIndex);
  23516. return isPure(lView, adjustedIndex) ?
  23517. pureFunction2Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
  23518. pipeInstance.transform(v1, v2);
  23519. }
  23520. /**
  23521. * Invokes a pipe with 3 arguments.
  23522. *
  23523. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23524. * the pipe only when an input to the pipe changes.
  23525. *
  23526. * @param index Pipe index where the pipe was stored on creation.
  23527. * @param slotOffset the offset in the reserved slot space
  23528. * @param v1 1st argument to {@link PipeTransform#transform}.
  23529. * @param v2 2nd argument to {@link PipeTransform#transform}.
  23530. * @param v3 4rd argument to {@link PipeTransform#transform}.
  23531. *
  23532. * @codeGenApi
  23533. */
  23534. function ɵɵpipeBind3(index, slotOffset, v1, v2, v3) {
  23535. const adjustedIndex = index + HEADER_OFFSET;
  23536. const lView = getLView();
  23537. const pipeInstance = load(lView, adjustedIndex);
  23538. return isPure(lView, adjustedIndex) ?
  23539. pureFunction3Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) :
  23540. pipeInstance.transform(v1, v2, v3);
  23541. }
  23542. /**
  23543. * Invokes a pipe with 4 arguments.
  23544. *
  23545. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23546. * the pipe only when an input to the pipe changes.
  23547. *
  23548. * @param index Pipe index where the pipe was stored on creation.
  23549. * @param slotOffset the offset in the reserved slot space
  23550. * @param v1 1st argument to {@link PipeTransform#transform}.
  23551. * @param v2 2nd argument to {@link PipeTransform#transform}.
  23552. * @param v3 3rd argument to {@link PipeTransform#transform}.
  23553. * @param v4 4th argument to {@link PipeTransform#transform}.
  23554. *
  23555. * @codeGenApi
  23556. */
  23557. function ɵɵpipeBind4(index, slotOffset, v1, v2, v3, v4) {
  23558. const adjustedIndex = index + HEADER_OFFSET;
  23559. const lView = getLView();
  23560. const pipeInstance = load(lView, adjustedIndex);
  23561. return isPure(lView, adjustedIndex) ? pureFunction4Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
  23562. pipeInstance.transform(v1, v2, v3, v4);
  23563. }
  23564. /**
  23565. * Invokes a pipe with variable number of arguments.
  23566. *
  23567. * This instruction acts as a guard to {@link PipeTransform#transform} invoking
  23568. * the pipe only when an input to the pipe changes.
  23569. *
  23570. * @param index Pipe index where the pipe was stored on creation.
  23571. * @param slotOffset the offset in the reserved slot space
  23572. * @param values Array of arguments to pass to {@link PipeTransform#transform} method.
  23573. *
  23574. * @codeGenApi
  23575. */
  23576. function ɵɵpipeBindV(index, slotOffset, values) {
  23577. const adjustedIndex = index + HEADER_OFFSET;
  23578. const lView = getLView();
  23579. const pipeInstance = load(lView, adjustedIndex);
  23580. return isPure(lView, adjustedIndex) ?
  23581. pureFunctionVInternal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) :
  23582. pipeInstance.transform.apply(pipeInstance, values);
  23583. }
  23584. function isPure(lView, index) {
  23585. return lView[TVIEW].data[index].pure;
  23586. }
  23587. function symbolIterator() {
  23588. // @ts-expect-error accessing a private member
  23589. return this._results[Symbol.iterator]();
  23590. }
  23591. /**
  23592. * An unmodifiable list of items that Angular keeps up to date when the state
  23593. * of the application changes.
  23594. *
  23595. * The type of object that {@link ViewChildren}, {@link ContentChildren}, and {@link QueryList}
  23596. * provide.
  23597. *
  23598. * Implements an iterable interface, therefore it can be used in both ES6
  23599. * javascript `for (var i of items)` loops as well as in Angular templates with
  23600. * `*ngFor="let i of myList"`.
  23601. *
  23602. * Changes can be observed by subscribing to the changes `Observable`.
  23603. *
  23604. * NOTE: In the future this class will implement an `Observable` interface.
  23605. *
  23606. * @usageNotes
  23607. * ### Example
  23608. * ```typescript
  23609. * @Component({...})
  23610. * class Container {
  23611. * @ViewChildren(Item) items:QueryList<Item>;
  23612. * }
  23613. * ```
  23614. *
  23615. * @publicApi
  23616. */
  23617. class QueryList {
  23618. static { Symbol.iterator; }
  23619. /**
  23620. * Returns `Observable` of `QueryList` notifying the subscriber of changes.
  23621. */
  23622. get changes() {
  23623. return this._changes || (this._changes = new EventEmitter());
  23624. }
  23625. /**
  23626. * @param emitDistinctChangesOnly Whether `QueryList.changes` should fire only when actual change
  23627. * has occurred. Or if it should fire when query is recomputed. (recomputing could resolve in
  23628. * the same result)
  23629. */
  23630. constructor(_emitDistinctChangesOnly = false) {
  23631. this._emitDistinctChangesOnly = _emitDistinctChangesOnly;
  23632. this.dirty = true;
  23633. this._results = [];
  23634. this._changesDetected = false;
  23635. this._changes = null;
  23636. this.length = 0;
  23637. this.first = undefined;
  23638. this.last = undefined;
  23639. // This function should be declared on the prototype, but doing so there will cause the class
  23640. // declaration to have side-effects and become not tree-shakable. For this reason we do it in
  23641. // the constructor.
  23642. // [Symbol.iterator](): Iterator<T> { ... }
  23643. const proto = QueryList.prototype;
  23644. if (!proto[Symbol.iterator])
  23645. proto[Symbol.iterator] = symbolIterator;
  23646. }
  23647. /**
  23648. * Returns the QueryList entry at `index`.
  23649. */
  23650. get(index) {
  23651. return this._results[index];
  23652. }
  23653. /**
  23654. * See
  23655. * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
  23656. */
  23657. map(fn) {
  23658. return this._results.map(fn);
  23659. }
  23660. filter(fn) {
  23661. return this._results.filter(fn);
  23662. }
  23663. /**
  23664. * See
  23665. * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
  23666. */
  23667. find(fn) {
  23668. return this._results.find(fn);
  23669. }
  23670. /**
  23671. * See
  23672. * [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
  23673. */
  23674. reduce(fn, init) {
  23675. return this._results.reduce(fn, init);
  23676. }
  23677. /**
  23678. * See
  23679. * [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
  23680. */
  23681. forEach(fn) {
  23682. this._results.forEach(fn);
  23683. }
  23684. /**
  23685. * See
  23686. * [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
  23687. */
  23688. some(fn) {
  23689. return this._results.some(fn);
  23690. }
  23691. /**
  23692. * Returns a copy of the internal results list as an Array.
  23693. */
  23694. toArray() {
  23695. return this._results.slice();
  23696. }
  23697. toString() {
  23698. return this._results.toString();
  23699. }
  23700. /**
  23701. * Updates the stored data of the query list, and resets the `dirty` flag to `false`, so that
  23702. * on change detection, it will not notify of changes to the queries, unless a new change
  23703. * occurs.
  23704. *
  23705. * @param resultsTree The query results to store
  23706. * @param identityAccessor Optional function for extracting stable object identity from a value
  23707. * in the array. This function is executed for each element of the query result list while
  23708. * comparing current query list with the new one (provided as a first argument of the `reset`
  23709. * function) to detect if the lists are different. If the function is not provided, elements
  23710. * are compared as is (without any pre-processing).
  23711. */
  23712. reset(resultsTree, identityAccessor) {
  23713. // Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of
  23714. // QueryList (but not for QueryList itself.)
  23715. const self = this;
  23716. self.dirty = false;
  23717. const newResultFlat = flatten(resultsTree);
  23718. if (this._changesDetected = !arrayEquals(self._results, newResultFlat, identityAccessor)) {
  23719. self._results = newResultFlat;
  23720. self.length = newResultFlat.length;
  23721. self.last = newResultFlat[this.length - 1];
  23722. self.first = newResultFlat[0];
  23723. }
  23724. }
  23725. /**
  23726. * Triggers a change event by emitting on the `changes` {@link EventEmitter}.
  23727. */
  23728. notifyOnChanges() {
  23729. if (this._changes && (this._changesDetected || !this._emitDistinctChangesOnly))
  23730. this._changes.emit(this);
  23731. }
  23732. /** internal */
  23733. setDirty() {
  23734. this.dirty = true;
  23735. }
  23736. /** internal */
  23737. destroy() {
  23738. this.changes.complete();
  23739. this.changes.unsubscribe();
  23740. }
  23741. }
  23742. function createAndRenderEmbeddedLView(declarationLView, templateTNode, context, options) {
  23743. const embeddedTView = templateTNode.tView;
  23744. ngDevMode && assertDefined(embeddedTView, 'TView must be defined for a template node.');
  23745. ngDevMode && assertTNodeForLView(templateTNode, declarationLView);
  23746. // Embedded views follow the change detection strategy of the view they're declared in.
  23747. const isSignalView = declarationLView[FLAGS] & 4096 /* LViewFlags.SignalView */;
  23748. const viewFlags = isSignalView ? 4096 /* LViewFlags.SignalView */ : 16 /* LViewFlags.CheckAlways */;
  23749. const embeddedLView = createLView(declarationLView, embeddedTView, context, viewFlags, null, templateTNode, null, null, null, options?.injector ?? null, options?.hydrationInfo ?? null);
  23750. const declarationLContainer = declarationLView[templateTNode.index];
  23751. ngDevMode && assertLContainer(declarationLContainer);
  23752. embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
  23753. const declarationViewLQueries = declarationLView[QUERIES];
  23754. if (declarationViewLQueries !== null) {
  23755. embeddedLView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView);
  23756. }
  23757. // execute creation mode of a view
  23758. renderView(embeddedTView, embeddedLView, context);
  23759. return embeddedLView;
  23760. }
  23761. function getLViewFromLContainer(lContainer, index) {
  23762. const adjustedIndex = CONTAINER_HEADER_OFFSET + index;
  23763. // avoid reading past the array boundaries
  23764. if (adjustedIndex < lContainer.length) {
  23765. const lView = lContainer[adjustedIndex];
  23766. ngDevMode && assertLView(lView);
  23767. return lView;
  23768. }
  23769. return undefined;
  23770. }
  23771. function addLViewToLContainer(lContainer, lView, index, addToDOM = true) {
  23772. const tView = lView[TVIEW];
  23773. // insert to the view tree so the new view can be change-detected
  23774. insertView(tView, lView, lContainer, index);
  23775. // insert to the view to the DOM tree
  23776. if (addToDOM) {
  23777. const beforeNode = getBeforeNodeForView(index, lContainer);
  23778. const renderer = lView[RENDERER];
  23779. const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
  23780. if (parentRNode !== null) {
  23781. addViewToDOM(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
  23782. }
  23783. }
  23784. }
  23785. function removeLViewFromLContainer(lContainer, index) {
  23786. const lView = detachView(lContainer, index);
  23787. if (lView !== undefined) {
  23788. destroyLView(lView[TVIEW], lView);
  23789. }
  23790. return lView;
  23791. }
  23792. /**
  23793. * Represents an embedded template that can be used to instantiate embedded views.
  23794. * To instantiate embedded views based on a template, use the `ViewContainerRef`
  23795. * method `createEmbeddedView()`.
  23796. *
  23797. * Access a `TemplateRef` instance by placing a directive on an `<ng-template>`
  23798. * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view
  23799. * is injected into the constructor of the directive,
  23800. * using the `TemplateRef` token.
  23801. *
  23802. * You can also use a `Query` to find a `TemplateRef` associated with
  23803. * a component or a directive.
  23804. *
  23805. * @see {@link ViewContainerRef}
  23806. * @see [Navigate the Component Tree with DI](guide/dependency-injection-navtree)
  23807. *
  23808. * @publicApi
  23809. */
  23810. class TemplateRef {
  23811. /**
  23812. * @internal
  23813. * @nocollapse
  23814. */
  23815. static { this.__NG_ELEMENT_ID__ = injectTemplateRef; }
  23816. }
  23817. const ViewEngineTemplateRef = TemplateRef;
  23818. // TODO(alxhub): combine interface and implementation. Currently this is challenging since something
  23819. // in g3 depends on them being separate.
  23820. const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef {
  23821. constructor(_declarationLView, _declarationTContainer, elementRef) {
  23822. super();
  23823. this._declarationLView = _declarationLView;
  23824. this._declarationTContainer = _declarationTContainer;
  23825. this.elementRef = elementRef;
  23826. }
  23827. /**
  23828. * Returns an `ssrId` associated with a TView, which was used to
  23829. * create this instance of the `TemplateRef`.
  23830. *
  23831. * @internal
  23832. */
  23833. get ssrId() {
  23834. return this._declarationTContainer.tView?.ssrId || null;
  23835. }
  23836. createEmbeddedView(context, injector) {
  23837. return this.createEmbeddedViewImpl(context, injector);
  23838. }
  23839. /**
  23840. * @internal
  23841. */
  23842. createEmbeddedViewImpl(context, injector, hydrationInfo) {
  23843. const embeddedLView = createAndRenderEmbeddedLView(this._declarationLView, this._declarationTContainer, context, { injector, hydrationInfo });
  23844. return new ViewRef$1(embeddedLView);
  23845. }
  23846. };
  23847. /**
  23848. * Creates a TemplateRef given a node.
  23849. *
  23850. * @returns The TemplateRef instance to use
  23851. */
  23852. function injectTemplateRef() {
  23853. return createTemplateRef(getCurrentTNode(), getLView());
  23854. }
  23855. /**
  23856. * Creates a TemplateRef and stores it on the injector.
  23857. *
  23858. * @param hostTNode The node on which a TemplateRef is requested
  23859. * @param hostLView The `LView` to which the node belongs
  23860. * @returns The TemplateRef instance or null if we can't create a TemplateRef on a given node type
  23861. */
  23862. function createTemplateRef(hostTNode, hostLView) {
  23863. if (hostTNode.type & 4 /* TNodeType.Container */) {
  23864. ngDevMode && assertDefined(hostTNode.tView, 'TView must be allocated');
  23865. return new R3TemplateRef(hostLView, hostTNode, createElementRef(hostTNode, hostLView));
  23866. }
  23867. return null;
  23868. }
  23869. /**
  23870. * Removes all dehydrated views from a given LContainer:
  23871. * both in internal data structure, as well as removing
  23872. * corresponding DOM nodes that belong to that dehydrated view.
  23873. */
  23874. function removeDehydratedViews(lContainer) {
  23875. const views = lContainer[DEHYDRATED_VIEWS] ?? [];
  23876. const parentLView = lContainer[PARENT];
  23877. const renderer = parentLView[RENDERER];
  23878. for (const view of views) {
  23879. removeDehydratedView(view, renderer);
  23880. ngDevMode && ngDevMode.dehydratedViewsRemoved++;
  23881. }
  23882. // Reset the value to an empty array to indicate that no
  23883. // further processing of dehydrated views is needed for
  23884. // this view container (i.e. do not trigger the lookup process
  23885. // once again in case a `ViewContainerRef` is created later).
  23886. lContainer[DEHYDRATED_VIEWS] = EMPTY_ARRAY;
  23887. }
  23888. /**
  23889. * Helper function to remove all nodes from a dehydrated view.
  23890. */
  23891. function removeDehydratedView(dehydratedView, renderer) {
  23892. let nodesRemoved = 0;
  23893. let currentRNode = dehydratedView.firstChild;
  23894. if (currentRNode) {
  23895. const numNodes = dehydratedView.data[NUM_ROOT_NODES];
  23896. while (nodesRemoved < numNodes) {
  23897. ngDevMode && validateSiblingNodeExists(currentRNode);
  23898. const nextSibling = currentRNode.nextSibling;
  23899. nativeRemoveNode(renderer, currentRNode, false);
  23900. currentRNode = nextSibling;
  23901. nodesRemoved++;
  23902. }
  23903. }
  23904. }
  23905. /**
  23906. * Walks over all views within this LContainer invokes dehydrated views
  23907. * cleanup function for each one.
  23908. */
  23909. function cleanupLContainer(lContainer) {
  23910. removeDehydratedViews(lContainer);
  23911. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  23912. cleanupLView(lContainer[i]);
  23913. }
  23914. }
  23915. /**
  23916. * Walks over `LContainer`s and components registered within
  23917. * this LView and invokes dehydrated views cleanup function for each one.
  23918. */
  23919. function cleanupLView(lView) {
  23920. const tView = lView[TVIEW];
  23921. for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
  23922. if (isLContainer(lView[i])) {
  23923. const lContainer = lView[i];
  23924. cleanupLContainer(lContainer);
  23925. }
  23926. else if (Array.isArray(lView[i])) {
  23927. // This is a component, enter the `cleanupLView` recursively.
  23928. cleanupLView(lView[i]);
  23929. }
  23930. }
  23931. }
  23932. /**
  23933. * Walks over all views registered within the ApplicationRef and removes
  23934. * all dehydrated views from all `LContainer`s along the way.
  23935. */
  23936. function cleanupDehydratedViews(appRef) {
  23937. const viewRefs = appRef._views;
  23938. for (const viewRef of viewRefs) {
  23939. const lNode = getLNodeForHydration(viewRef);
  23940. // An `lView` might be `null` if a `ViewRef` represents
  23941. // an embedded view (not a component view).
  23942. if (lNode !== null && lNode[HOST] !== null) {
  23943. if (isLView(lNode)) {
  23944. cleanupLView(lNode);
  23945. }
  23946. else {
  23947. // Cleanup in the root component view
  23948. const componentLView = lNode[HOST];
  23949. cleanupLView(componentLView);
  23950. // Cleanup in all views within this view container
  23951. cleanupLContainer(lNode);
  23952. }
  23953. ngDevMode && ngDevMode.dehydratedViewsCleanupRuns++;
  23954. }
  23955. }
  23956. }
  23957. /**
  23958. * Given a current DOM node and a serialized information about the views
  23959. * in a container, walks over the DOM structure, collecting the list of
  23960. * dehydrated views.
  23961. */
  23962. function locateDehydratedViewsInContainer(currentRNode, serializedViews) {
  23963. const dehydratedViews = [];
  23964. for (const serializedView of serializedViews) {
  23965. // Repeats a view multiple times as needed, based on the serialized information
  23966. // (for example, for *ngFor-produced views).
  23967. for (let i = 0; i < (serializedView[MULTIPLIER] ?? 1); i++) {
  23968. const view = {
  23969. data: serializedView,
  23970. firstChild: null,
  23971. };
  23972. if (serializedView[NUM_ROOT_NODES] > 0) {
  23973. // Keep reference to the first node in this view,
  23974. // so it can be accessed while invoking template instructions.
  23975. view.firstChild = currentRNode;
  23976. // Move over to the next node after this view, which can
  23977. // either be a first node of the next view or an anchor comment
  23978. // node after the last view in a container.
  23979. currentRNode = siblingAfter(serializedView[NUM_ROOT_NODES], currentRNode);
  23980. }
  23981. dehydratedViews.push(view);
  23982. }
  23983. }
  23984. return [currentRNode, dehydratedViews];
  23985. }
  23986. /**
  23987. * Reference to a function that searches for a matching dehydrated views
  23988. * stored on a given lContainer.
  23989. * Returns `null` by default, when hydration is not enabled.
  23990. */
  23991. let _findMatchingDehydratedViewImpl = (lContainer, template) => null;
  23992. /**
  23993. * Retrieves the next dehydrated view from the LContainer and verifies that
  23994. * it matches a given template id (from the TView that was used to create this
  23995. * instance of a view). If the id doesn't match, that means that we are in an
  23996. * unexpected state and can not complete the reconciliation process. Thus,
  23997. * all dehydrated views from this LContainer are removed (including corresponding
  23998. * DOM nodes) and the rendering is performed as if there were no dehydrated views
  23999. * in this container.
  24000. */
  24001. function findMatchingDehydratedViewImpl(lContainer, template) {
  24002. const views = lContainer[DEHYDRATED_VIEWS];
  24003. if (!template || views === null || views.length === 0) {
  24004. return null;
  24005. }
  24006. const view = views[0];
  24007. // Verify whether the first dehydrated view in the container matches
  24008. // the template id passed to this function (that originated from a TView
  24009. // that was used to create an instance of an embedded or component views.
  24010. if (view.data[TEMPLATE_ID] === template) {
  24011. // If the template id matches - extract the first view and return it.
  24012. return views.shift();
  24013. }
  24014. else {
  24015. // Otherwise, we are at the state when reconciliation can not be completed,
  24016. // thus we remove all dehydrated views within this container (remove them
  24017. // from internal data structures as well as delete associated elements from
  24018. // the DOM tree).
  24019. removeDehydratedViews(lContainer);
  24020. return null;
  24021. }
  24022. }
  24023. function enableFindMatchingDehydratedViewImpl() {
  24024. _findMatchingDehydratedViewImpl = findMatchingDehydratedViewImpl;
  24025. }
  24026. function findMatchingDehydratedView(lContainer, template) {
  24027. return _findMatchingDehydratedViewImpl(lContainer, template);
  24028. }
  24029. /**
  24030. * Represents a container where one or more views can be attached to a component.
  24031. *
  24032. * Can contain *host views* (created by instantiating a
  24033. * component with the `createComponent()` method), and *embedded views*
  24034. * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
  24035. *
  24036. * A view container instance can contain other view containers,
  24037. * creating a [view hierarchy](guide/glossary#view-hierarchy).
  24038. *
  24039. * @usageNotes
  24040. *
  24041. * The example below demonstrates how the `createComponent` function can be used
  24042. * to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
  24043. * so that it gets included into change detection cycles.
  24044. *
  24045. * Note: the example uses standalone components, but the function can also be used for
  24046. * non-standalone components (declared in an NgModule) as well.
  24047. *
  24048. * ```typescript
  24049. * @Component({
  24050. * standalone: true,
  24051. * selector: 'dynamic',
  24052. * template: `<span>This is a content of a dynamic component.</span>`,
  24053. * })
  24054. * class DynamicComponent {
  24055. * vcr = inject(ViewContainerRef);
  24056. * }
  24057. *
  24058. * @Component({
  24059. * standalone: true,
  24060. * selector: 'app',
  24061. * template: `<main>Hi! This is the main content.</main>`,
  24062. * })
  24063. * class AppComponent {
  24064. * vcr = inject(ViewContainerRef);
  24065. *
  24066. * ngAfterViewInit() {
  24067. * const compRef = this.vcr.createComponent(DynamicComponent);
  24068. * compRef.changeDetectorRef.detectChanges();
  24069. * }
  24070. * }
  24071. * ```
  24072. *
  24073. * @see {@link ComponentRef}
  24074. * @see {@link EmbeddedViewRef}
  24075. *
  24076. * @publicApi
  24077. */
  24078. class ViewContainerRef {
  24079. /**
  24080. * @internal
  24081. * @nocollapse
  24082. */
  24083. static { this.__NG_ELEMENT_ID__ = injectViewContainerRef; }
  24084. }
  24085. /**
  24086. * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
  24087. * already exists, retrieves the existing ViewContainerRef.
  24088. *
  24089. * @returns The ViewContainerRef instance to use
  24090. */
  24091. function injectViewContainerRef() {
  24092. const previousTNode = getCurrentTNode();
  24093. return createContainerRef(previousTNode, getLView());
  24094. }
  24095. const VE_ViewContainerRef = ViewContainerRef;
  24096. // TODO(alxhub): cleaning up this indirection triggers a subtle bug in Closure in g3. Once the fix
  24097. // for that lands, this can be cleaned up.
  24098. const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
  24099. constructor(_lContainer, _hostTNode, _hostLView) {
  24100. super();
  24101. this._lContainer = _lContainer;
  24102. this._hostTNode = _hostTNode;
  24103. this._hostLView = _hostLView;
  24104. }
  24105. get element() {
  24106. return createElementRef(this._hostTNode, this._hostLView);
  24107. }
  24108. get injector() {
  24109. return new NodeInjector(this._hostTNode, this._hostLView);
  24110. }
  24111. /** @deprecated No replacement */
  24112. get parentInjector() {
  24113. const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostLView);
  24114. if (hasParentInjector(parentLocation)) {
  24115. const parentView = getParentInjectorView(parentLocation, this._hostLView);
  24116. const injectorIndex = getParentInjectorIndex(parentLocation);
  24117. ngDevMode && assertNodeInjector(parentView, injectorIndex);
  24118. const parentTNode = parentView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
  24119. return new NodeInjector(parentTNode, parentView);
  24120. }
  24121. else {
  24122. return new NodeInjector(null, this._hostLView);
  24123. }
  24124. }
  24125. clear() {
  24126. while (this.length > 0) {
  24127. this.remove(this.length - 1);
  24128. }
  24129. }
  24130. get(index) {
  24131. const viewRefs = getViewRefs(this._lContainer);
  24132. return viewRefs !== null && viewRefs[index] || null;
  24133. }
  24134. get length() {
  24135. return this._lContainer.length - CONTAINER_HEADER_OFFSET;
  24136. }
  24137. createEmbeddedView(templateRef, context, indexOrOptions) {
  24138. let index;
  24139. let injector;
  24140. if (typeof indexOrOptions === 'number') {
  24141. index = indexOrOptions;
  24142. }
  24143. else if (indexOrOptions != null) {
  24144. index = indexOrOptions.index;
  24145. injector = indexOrOptions.injector;
  24146. }
  24147. const hydrationInfo = findMatchingDehydratedView(this._lContainer, templateRef.ssrId);
  24148. const viewRef = templateRef.createEmbeddedViewImpl(context || {}, injector, hydrationInfo);
  24149. // If there is a matching dehydrated view, but the host TNode is located in the skip
  24150. // hydration block, this means that the content was detached (as a part of the skip
  24151. // hydration logic) and it needs to be appended into the DOM.
  24152. const skipDomInsertion = !!hydrationInfo && !hasInSkipHydrationBlockFlag(this._hostTNode);
  24153. this.insertImpl(viewRef, index, skipDomInsertion);
  24154. return viewRef;
  24155. }
  24156. createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) {
  24157. const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType);
  24158. let index;
  24159. // This function supports 2 signatures and we need to handle options correctly for both:
  24160. // 1. When first argument is a Component type. This signature also requires extra
  24161. // options to be provided as object (more ergonomic option).
  24162. // 2. First argument is a Component factory. In this case extra options are represented as
  24163. // positional arguments. This signature is less ergonomic and will be deprecated.
  24164. if (isComponentFactory) {
  24165. if (ngDevMode) {
  24166. assertEqual(typeof indexOrOptions !== 'object', true, 'It looks like Component factory was provided as the first argument ' +
  24167. 'and an options object as the second argument. This combination of arguments ' +
  24168. 'is incompatible. You can either change the first argument to provide Component ' +
  24169. 'type or change the second argument to be a number (representing an index at ' +
  24170. 'which to insert the new component\'s host view into this container)');
  24171. }
  24172. index = indexOrOptions;
  24173. }
  24174. else {
  24175. if (ngDevMode) {
  24176. assertDefined(getComponentDef(componentFactoryOrType), `Provided Component class doesn't contain Component definition. ` +
  24177. `Please check whether provided class has @Component decorator.`);
  24178. assertEqual(typeof indexOrOptions !== 'number', true, 'It looks like Component type was provided as the first argument ' +
  24179. 'and a number (representing an index at which to insert the new component\'s ' +
  24180. 'host view into this container as the second argument. This combination of arguments ' +
  24181. 'is incompatible. Please use an object as the second argument instead.');
  24182. }
  24183. const options = (indexOrOptions || {});
  24184. if (ngDevMode && options.environmentInjector && options.ngModuleRef) {
  24185. throwError(`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`);
  24186. }
  24187. index = options.index;
  24188. injector = options.injector;
  24189. projectableNodes = options.projectableNodes;
  24190. environmentInjector = options.environmentInjector || options.ngModuleRef;
  24191. }
  24192. const componentFactory = isComponentFactory ?
  24193. componentFactoryOrType :
  24194. new ComponentFactory(getComponentDef(componentFactoryOrType));
  24195. const contextInjector = injector || this.parentInjector;
  24196. // If an `NgModuleRef` is not provided explicitly, try retrieving it from the DI tree.
  24197. if (!environmentInjector && componentFactory.ngModule == null) {
  24198. // For the `ComponentFactory` case, entering this logic is very unlikely, since we expect that
  24199. // an instance of a `ComponentFactory`, resolved via `ComponentFactoryResolver` would have an
  24200. // `ngModule` field. This is possible in some test scenarios and potentially in some JIT-based
  24201. // use-cases. For the `ComponentFactory` case we preserve backwards-compatibility and try
  24202. // using a provided injector first, then fall back to the parent injector of this
  24203. // `ViewContainerRef` instance.
  24204. //
  24205. // For the factory-less case, it's critical to establish a connection with the module
  24206. // injector tree (by retrieving an instance of an `NgModuleRef` and accessing its injector),
  24207. // so that a component can use DI tokens provided in MgModules. For this reason, we can not
  24208. // rely on the provided injector, since it might be detached from the DI tree (for example, if
  24209. // it was created via `Injector.create` without specifying a parent injector, or if an
  24210. // injector is retrieved from an `NgModuleRef` created via `createNgModule` using an
  24211. // NgModule outside of a module tree). Instead, we always use `ViewContainerRef`'s parent
  24212. // injector, which is normally connected to the DI tree, which includes module injector
  24213. // subtree.
  24214. const _injector = isComponentFactory ? contextInjector : this.parentInjector;
  24215. // DO NOT REFACTOR. The code here used to have a `injector.get(NgModuleRef, null) ||
  24216. // undefined` expression which seems to cause internal google apps to fail. This is documented
  24217. // in the following internal bug issue: go/b/142967802
  24218. const result = _injector.get(EnvironmentInjector, null);
  24219. if (result) {
  24220. environmentInjector = result;
  24221. }
  24222. }
  24223. const componentDef = getComponentDef(componentFactory.componentType ?? {});
  24224. const dehydratedView = findMatchingDehydratedView(this._lContainer, componentDef?.id ?? null);
  24225. const rNode = dehydratedView?.firstChild ?? null;
  24226. const componentRef = componentFactory.create(contextInjector, projectableNodes, rNode, environmentInjector);
  24227. // If there is a matching dehydrated view, but the host TNode is located in the skip
  24228. // hydration block, this means that the content was detached (as a part of the skip
  24229. // hydration logic) and it needs to be appended into the DOM.
  24230. const skipDomInsertion = !!dehydratedView && !hasInSkipHydrationBlockFlag(this._hostTNode);
  24231. this.insertImpl(componentRef.hostView, index, skipDomInsertion);
  24232. return componentRef;
  24233. }
  24234. insert(viewRef, index) {
  24235. return this.insertImpl(viewRef, index, false);
  24236. }
  24237. insertImpl(viewRef, index, skipDomInsertion) {
  24238. const lView = viewRef._lView;
  24239. const tView = lView[TVIEW];
  24240. if (ngDevMode && viewRef.destroyed) {
  24241. throw new Error('Cannot insert a destroyed View in a ViewContainer!');
  24242. }
  24243. if (viewAttachedToContainer(lView)) {
  24244. // If view is already attached, detach it first so we clean up references appropriately.
  24245. const prevIdx = this.indexOf(viewRef);
  24246. // A view might be attached either to this or a different container. The `prevIdx` for
  24247. // those cases will be:
  24248. // equal to -1 for views attached to this ViewContainerRef
  24249. // >= 0 for views attached to a different ViewContainerRef
  24250. if (prevIdx !== -1) {
  24251. this.detach(prevIdx);
  24252. }
  24253. else {
  24254. const prevLContainer = lView[PARENT];
  24255. ngDevMode &&
  24256. assertEqual(isLContainer(prevLContainer), true, 'An attached view should have its PARENT point to a container.');
  24257. // We need to re-create a R3ViewContainerRef instance since those are not stored on
  24258. // LView (nor anywhere else).
  24259. const prevVCRef = new R3ViewContainerRef(prevLContainer, prevLContainer[T_HOST], prevLContainer[PARENT]);
  24260. prevVCRef.detach(prevVCRef.indexOf(viewRef));
  24261. }
  24262. }
  24263. // Logical operation of adding `LView` to `LContainer`
  24264. const adjustedIdx = this._adjustIndex(index);
  24265. const lContainer = this._lContainer;
  24266. addLViewToLContainer(lContainer, lView, adjustedIdx, !skipDomInsertion);
  24267. viewRef.attachToViewContainerRef();
  24268. addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
  24269. return viewRef;
  24270. }
  24271. move(viewRef, newIndex) {
  24272. if (ngDevMode && viewRef.destroyed) {
  24273. throw new Error('Cannot move a destroyed View in a ViewContainer!');
  24274. }
  24275. return this.insert(viewRef, newIndex);
  24276. }
  24277. indexOf(viewRef) {
  24278. const viewRefsArr = getViewRefs(this._lContainer);
  24279. return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
  24280. }
  24281. remove(index) {
  24282. const adjustedIdx = this._adjustIndex(index, -1);
  24283. const detachedView = detachView(this._lContainer, adjustedIdx);
  24284. if (detachedView) {
  24285. // Before destroying the view, remove it from the container's array of `ViewRef`s.
  24286. // This ensures the view container length is updated before calling
  24287. // `destroyLView`, which could recursively call view container methods that
  24288. // rely on an accurate container length.
  24289. // (e.g. a method on this view container being called by a child directive's OnDestroy
  24290. // lifecycle hook)
  24291. removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx);
  24292. destroyLView(detachedView[TVIEW], detachedView);
  24293. }
  24294. }
  24295. detach(index) {
  24296. const adjustedIdx = this._adjustIndex(index, -1);
  24297. const view = detachView(this._lContainer, adjustedIdx);
  24298. const wasDetached = view && removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx) != null;
  24299. return wasDetached ? new ViewRef$1(view) : null;
  24300. }
  24301. _adjustIndex(index, shift = 0) {
  24302. if (index == null) {
  24303. return this.length + shift;
  24304. }
  24305. if (ngDevMode) {
  24306. assertGreaterThan(index, -1, `ViewRef index must be positive, got ${index}`);
  24307. // +1 because it's legal to insert at the end.
  24308. assertLessThan(index, this.length + 1 + shift, 'index');
  24309. }
  24310. return index;
  24311. }
  24312. };
  24313. function getViewRefs(lContainer) {
  24314. return lContainer[VIEW_REFS];
  24315. }
  24316. function getOrCreateViewRefs(lContainer) {
  24317. return (lContainer[VIEW_REFS] || (lContainer[VIEW_REFS] = []));
  24318. }
  24319. /**
  24320. * Creates a ViewContainerRef and stores it on the injector.
  24321. *
  24322. * @param hostTNode The node that is requesting a ViewContainerRef
  24323. * @param hostLView The view to which the node belongs
  24324. * @returns The ViewContainerRef instance to use
  24325. */
  24326. function createContainerRef(hostTNode, hostLView) {
  24327. ngDevMode && assertTNodeType(hostTNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
  24328. let lContainer;
  24329. const slotValue = hostLView[hostTNode.index];
  24330. if (isLContainer(slotValue)) {
  24331. // If the host is a container, we don't need to create a new LContainer
  24332. lContainer = slotValue;
  24333. }
  24334. else {
  24335. // An LContainer anchor can not be `null`, but we set it here temporarily
  24336. // and update to the actual value later in this function (see
  24337. // `_locateOrCreateAnchorNode`).
  24338. lContainer = createLContainer(slotValue, hostLView, null, hostTNode);
  24339. hostLView[hostTNode.index] = lContainer;
  24340. addToViewTree(hostLView, lContainer);
  24341. }
  24342. _locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue);
  24343. return new R3ViewContainerRef(lContainer, hostTNode, hostLView);
  24344. }
  24345. /**
  24346. * Creates and inserts a comment node that acts as an anchor for a view container.
  24347. *
  24348. * If the host is a regular element, we have to insert a comment node manually which will
  24349. * be used as an anchor when inserting elements. In this specific case we use low-level DOM
  24350. * manipulation to insert it.
  24351. */
  24352. function insertAnchorNode(hostLView, hostTNode) {
  24353. const renderer = hostLView[RENDERER];
  24354. ngDevMode && ngDevMode.rendererCreateComment++;
  24355. const commentNode = renderer.createComment(ngDevMode ? 'container' : '');
  24356. const hostNative = getNativeByTNode(hostTNode, hostLView);
  24357. const parentOfHostNative = nativeParentNode(renderer, hostNative);
  24358. nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
  24359. return commentNode;
  24360. }
  24361. let _locateOrCreateAnchorNode = createAnchorNode;
  24362. /**
  24363. * Regular creation mode: an anchor is created and
  24364. * assigned to the `lContainer[NATIVE]` slot.
  24365. */
  24366. function createAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
  24367. // We already have a native element (anchor) set, return.
  24368. if (lContainer[NATIVE])
  24369. return;
  24370. let commentNode;
  24371. // If the host is an element container, the native host element is guaranteed to be a
  24372. // comment and we can reuse that comment as anchor element for the new LContainer.
  24373. // The comment node in question is already part of the DOM structure so we don't need to append
  24374. // it again.
  24375. if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
  24376. commentNode = unwrapRNode(slotValue);
  24377. }
  24378. else {
  24379. commentNode = insertAnchorNode(hostLView, hostTNode);
  24380. }
  24381. lContainer[NATIVE] = commentNode;
  24382. }
  24383. /**
  24384. * Hydration logic that looks up:
  24385. * - an anchor node in the DOM and stores the node in `lContainer[NATIVE]`
  24386. * - all dehydrated views in this container and puts them into `lContainer[DEHYDRATED_VIEWS]`
  24387. */
  24388. function locateOrCreateAnchorNode(lContainer, hostLView, hostTNode, slotValue) {
  24389. // We already have a native element (anchor) set and the process
  24390. // of finding dehydrated views happened (so the `lContainer[DEHYDRATED_VIEWS]`
  24391. // is not null), exit early.
  24392. if (lContainer[NATIVE] && lContainer[DEHYDRATED_VIEWS])
  24393. return;
  24394. const hydrationInfo = hostLView[HYDRATION];
  24395. const noOffsetIndex = hostTNode.index - HEADER_OFFSET;
  24396. // TODO(akushnir): this should really be a single condition, refactor the code
  24397. // to use `hasInSkipHydrationBlockFlag` logic inside `isInSkipHydrationBlock`.
  24398. const skipHydration = isInSkipHydrationBlock(hostTNode) || hasInSkipHydrationBlockFlag(hostTNode);
  24399. const isNodeCreationMode = !hydrationInfo || skipHydration || isDisconnectedNode$1(hydrationInfo, noOffsetIndex);
  24400. // Regular creation mode.
  24401. if (isNodeCreationMode) {
  24402. return createAnchorNode(lContainer, hostLView, hostTNode, slotValue);
  24403. }
  24404. // Hydration mode, looking up an anchor node and dehydrated views in DOM.
  24405. const currentRNode = getSegmentHead(hydrationInfo, noOffsetIndex);
  24406. const serializedViews = hydrationInfo.data[CONTAINERS]?.[noOffsetIndex];
  24407. ngDevMode &&
  24408. assertDefined(serializedViews, 'Unexpected state: no hydration info available for a given TNode, ' +
  24409. 'which represents a view container.');
  24410. const [commentNode, dehydratedViews] = locateDehydratedViewsInContainer(currentRNode, serializedViews);
  24411. if (ngDevMode) {
  24412. validateMatchingNode(commentNode, Node.COMMENT_NODE, null, hostLView, hostTNode, true);
  24413. // Do not throw in case this node is already claimed (thus `false` as a second
  24414. // argument). If this container is created based on an `<ng-template>`, the comment
  24415. // node would be already claimed from the `template` instruction. If an element acts
  24416. // as an anchor (e.g. <div #vcRef>), a separate comment node would be created/located,
  24417. // so we need to claim it here.
  24418. markRNodeAsClaimedByHydration(commentNode, false);
  24419. }
  24420. lContainer[NATIVE] = commentNode;
  24421. lContainer[DEHYDRATED_VIEWS] = dehydratedViews;
  24422. }
  24423. function enableLocateOrCreateContainerRefImpl() {
  24424. _locateOrCreateAnchorNode = locateOrCreateAnchorNode;
  24425. }
  24426. class LQuery_ {
  24427. constructor(queryList) {
  24428. this.queryList = queryList;
  24429. this.matches = null;
  24430. }
  24431. clone() {
  24432. return new LQuery_(this.queryList);
  24433. }
  24434. setDirty() {
  24435. this.queryList.setDirty();
  24436. }
  24437. }
  24438. class LQueries_ {
  24439. constructor(queries = []) {
  24440. this.queries = queries;
  24441. }
  24442. createEmbeddedView(tView) {
  24443. const tQueries = tView.queries;
  24444. if (tQueries !== null) {
  24445. const noOfInheritedQueries = tView.contentQueries !== null ? tView.contentQueries[0] : tQueries.length;
  24446. const viewLQueries = [];
  24447. // An embedded view has queries propagated from a declaration view at the beginning of the
  24448. // TQueries collection and up until a first content query declared in the embedded view. Only
  24449. // propagated LQueries are created at this point (LQuery corresponding to declared content
  24450. // queries will be instantiated from the content query instructions for each directive).
  24451. for (let i = 0; i < noOfInheritedQueries; i++) {
  24452. const tQuery = tQueries.getByIndex(i);
  24453. const parentLQuery = this.queries[tQuery.indexInDeclarationView];
  24454. viewLQueries.push(parentLQuery.clone());
  24455. }
  24456. return new LQueries_(viewLQueries);
  24457. }
  24458. return null;
  24459. }
  24460. insertView(tView) {
  24461. this.dirtyQueriesWithMatches(tView);
  24462. }
  24463. detachView(tView) {
  24464. this.dirtyQueriesWithMatches(tView);
  24465. }
  24466. dirtyQueriesWithMatches(tView) {
  24467. for (let i = 0; i < this.queries.length; i++) {
  24468. if (getTQuery(tView, i).matches !== null) {
  24469. this.queries[i].setDirty();
  24470. }
  24471. }
  24472. }
  24473. }
  24474. class TQueryMetadata_ {
  24475. constructor(predicate, flags, read = null) {
  24476. this.predicate = predicate;
  24477. this.flags = flags;
  24478. this.read = read;
  24479. }
  24480. }
  24481. class TQueries_ {
  24482. constructor(queries = []) {
  24483. this.queries = queries;
  24484. }
  24485. elementStart(tView, tNode) {
  24486. ngDevMode &&
  24487. assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only');
  24488. for (let i = 0; i < this.queries.length; i++) {
  24489. this.queries[i].elementStart(tView, tNode);
  24490. }
  24491. }
  24492. elementEnd(tNode) {
  24493. for (let i = 0; i < this.queries.length; i++) {
  24494. this.queries[i].elementEnd(tNode);
  24495. }
  24496. }
  24497. embeddedTView(tNode) {
  24498. let queriesForTemplateRef = null;
  24499. for (let i = 0; i < this.length; i++) {
  24500. const childQueryIndex = queriesForTemplateRef !== null ? queriesForTemplateRef.length : 0;
  24501. const tqueryClone = this.getByIndex(i).embeddedTView(tNode, childQueryIndex);
  24502. if (tqueryClone) {
  24503. tqueryClone.indexInDeclarationView = i;
  24504. if (queriesForTemplateRef !== null) {
  24505. queriesForTemplateRef.push(tqueryClone);
  24506. }
  24507. else {
  24508. queriesForTemplateRef = [tqueryClone];
  24509. }
  24510. }
  24511. }
  24512. return queriesForTemplateRef !== null ? new TQueries_(queriesForTemplateRef) : null;
  24513. }
  24514. template(tView, tNode) {
  24515. ngDevMode &&
  24516. assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only');
  24517. for (let i = 0; i < this.queries.length; i++) {
  24518. this.queries[i].template(tView, tNode);
  24519. }
  24520. }
  24521. getByIndex(index) {
  24522. ngDevMode && assertIndexInRange(this.queries, index);
  24523. return this.queries[index];
  24524. }
  24525. get length() {
  24526. return this.queries.length;
  24527. }
  24528. track(tquery) {
  24529. this.queries.push(tquery);
  24530. }
  24531. }
  24532. class TQuery_ {
  24533. constructor(metadata, nodeIndex = -1) {
  24534. this.metadata = metadata;
  24535. this.matches = null;
  24536. this.indexInDeclarationView = -1;
  24537. this.crossesNgTemplate = false;
  24538. /**
  24539. * A flag indicating if a given query still applies to nodes it is crossing. We use this flag
  24540. * (alongside with _declarationNodeIndex) to know when to stop applying content queries to
  24541. * elements in a template.
  24542. */
  24543. this._appliesToNextNode = true;
  24544. this._declarationNodeIndex = nodeIndex;
  24545. }
  24546. elementStart(tView, tNode) {
  24547. if (this.isApplyingToNode(tNode)) {
  24548. this.matchTNode(tView, tNode);
  24549. }
  24550. }
  24551. elementEnd(tNode) {
  24552. if (this._declarationNodeIndex === tNode.index) {
  24553. this._appliesToNextNode = false;
  24554. }
  24555. }
  24556. template(tView, tNode) {
  24557. this.elementStart(tView, tNode);
  24558. }
  24559. embeddedTView(tNode, childQueryIndex) {
  24560. if (this.isApplyingToNode(tNode)) {
  24561. this.crossesNgTemplate = true;
  24562. // A marker indicating a `<ng-template>` element (a placeholder for query results from
  24563. // embedded views created based on this `<ng-template>`).
  24564. this.addMatch(-tNode.index, childQueryIndex);
  24565. return new TQuery_(this.metadata);
  24566. }
  24567. return null;
  24568. }
  24569. isApplyingToNode(tNode) {
  24570. if (this._appliesToNextNode &&
  24571. (this.metadata.flags & 1 /* QueryFlags.descendants */) !== 1 /* QueryFlags.descendants */) {
  24572. const declarationNodeIdx = this._declarationNodeIndex;
  24573. let parent = tNode.parent;
  24574. // Determine if a given TNode is a "direct" child of a node on which a content query was
  24575. // declared (only direct children of query's host node can match with the descendants: false
  24576. // option). There are 3 main use-case / conditions to consider here:
  24577. // - <needs-target><i #target></i></needs-target>: here <i #target> parent node is a query
  24578. // host node;
  24579. // - <needs-target><ng-template [ngIf]="true"><i #target></i></ng-template></needs-target>:
  24580. // here <i #target> parent node is null;
  24581. // - <needs-target><ng-container><i #target></i></ng-container></needs-target>: here we need
  24582. // to go past `<ng-container>` to determine <i #target> parent node (but we shouldn't traverse
  24583. // up past the query's host node!).
  24584. while (parent !== null && (parent.type & 8 /* TNodeType.ElementContainer */) &&
  24585. parent.index !== declarationNodeIdx) {
  24586. parent = parent.parent;
  24587. }
  24588. return declarationNodeIdx === (parent !== null ? parent.index : -1);
  24589. }
  24590. return this._appliesToNextNode;
  24591. }
  24592. matchTNode(tView, tNode) {
  24593. const predicate = this.metadata.predicate;
  24594. if (Array.isArray(predicate)) {
  24595. for (let i = 0; i < predicate.length; i++) {
  24596. const name = predicate[i];
  24597. this.matchTNodeWithReadOption(tView, tNode, getIdxOfMatchingSelector(tNode, name));
  24598. // Also try matching the name to a provider since strings can be used as DI tokens too.
  24599. this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, name, false, false));
  24600. }
  24601. }
  24602. else {
  24603. if (predicate === TemplateRef) {
  24604. if (tNode.type & 4 /* TNodeType.Container */) {
  24605. this.matchTNodeWithReadOption(tView, tNode, -1);
  24606. }
  24607. }
  24608. else {
  24609. this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, predicate, false, false));
  24610. }
  24611. }
  24612. }
  24613. matchTNodeWithReadOption(tView, tNode, nodeMatchIdx) {
  24614. if (nodeMatchIdx !== null) {
  24615. const read = this.metadata.read;
  24616. if (read !== null) {
  24617. if (read === ElementRef || read === ViewContainerRef ||
  24618. read === TemplateRef && (tNode.type & 4 /* TNodeType.Container */)) {
  24619. this.addMatch(tNode.index, -2);
  24620. }
  24621. else {
  24622. const directiveOrProviderIdx = locateDirectiveOrProvider(tNode, tView, read, false, false);
  24623. if (directiveOrProviderIdx !== null) {
  24624. this.addMatch(tNode.index, directiveOrProviderIdx);
  24625. }
  24626. }
  24627. }
  24628. else {
  24629. this.addMatch(tNode.index, nodeMatchIdx);
  24630. }
  24631. }
  24632. }
  24633. addMatch(tNodeIdx, matchIdx) {
  24634. if (this.matches === null) {
  24635. this.matches = [tNodeIdx, matchIdx];
  24636. }
  24637. else {
  24638. this.matches.push(tNodeIdx, matchIdx);
  24639. }
  24640. }
  24641. }
  24642. /**
  24643. * Iterates over local names for a given node and returns directive index
  24644. * (or -1 if a local name points to an element).
  24645. *
  24646. * @param tNode static data of a node to check
  24647. * @param selector selector to match
  24648. * @returns directive index, -1 or null if a selector didn't match any of the local names
  24649. */
  24650. function getIdxOfMatchingSelector(tNode, selector) {
  24651. const localNames = tNode.localNames;
  24652. if (localNames !== null) {
  24653. for (let i = 0; i < localNames.length; i += 2) {
  24654. if (localNames[i] === selector) {
  24655. return localNames[i + 1];
  24656. }
  24657. }
  24658. }
  24659. return null;
  24660. }
  24661. function createResultByTNodeType(tNode, currentView) {
  24662. if (tNode.type & (3 /* TNodeType.AnyRNode */ | 8 /* TNodeType.ElementContainer */)) {
  24663. return createElementRef(tNode, currentView);
  24664. }
  24665. else if (tNode.type & 4 /* TNodeType.Container */) {
  24666. return createTemplateRef(tNode, currentView);
  24667. }
  24668. return null;
  24669. }
  24670. function createResultForNode(lView, tNode, matchingIdx, read) {
  24671. if (matchingIdx === -1) {
  24672. // if read token and / or strategy is not specified, detect it using appropriate tNode type
  24673. return createResultByTNodeType(tNode, lView);
  24674. }
  24675. else if (matchingIdx === -2) {
  24676. // read a special token from a node injector
  24677. return createSpecialToken(lView, tNode, read);
  24678. }
  24679. else {
  24680. // read a token
  24681. return getNodeInjectable(lView, lView[TVIEW], matchingIdx, tNode);
  24682. }
  24683. }
  24684. function createSpecialToken(lView, tNode, read) {
  24685. if (read === ElementRef) {
  24686. return createElementRef(tNode, lView);
  24687. }
  24688. else if (read === TemplateRef) {
  24689. return createTemplateRef(tNode, lView);
  24690. }
  24691. else if (read === ViewContainerRef) {
  24692. ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
  24693. return createContainerRef(tNode, lView);
  24694. }
  24695. else {
  24696. ngDevMode &&
  24697. throwError(`Special token to read should be one of ElementRef, TemplateRef or ViewContainerRef but got ${stringify(read)}.`);
  24698. }
  24699. }
  24700. /**
  24701. * A helper function that creates query results for a given view. This function is meant to do the
  24702. * processing once and only once for a given view instance (a set of results for a given view
  24703. * doesn't change).
  24704. */
  24705. function materializeViewResults(tView, lView, tQuery, queryIndex) {
  24706. const lQuery = lView[QUERIES].queries[queryIndex];
  24707. if (lQuery.matches === null) {
  24708. const tViewData = tView.data;
  24709. const tQueryMatches = tQuery.matches;
  24710. const result = [];
  24711. for (let i = 0; i < tQueryMatches.length; i += 2) {
  24712. const matchedNodeIdx = tQueryMatches[i];
  24713. if (matchedNodeIdx < 0) {
  24714. // we at the <ng-template> marker which might have results in views created based on this
  24715. // <ng-template> - those results will be in separate views though, so here we just leave
  24716. // null as a placeholder
  24717. result.push(null);
  24718. }
  24719. else {
  24720. ngDevMode && assertIndexInRange(tViewData, matchedNodeIdx);
  24721. const tNode = tViewData[matchedNodeIdx];
  24722. result.push(createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read));
  24723. }
  24724. }
  24725. lQuery.matches = result;
  24726. }
  24727. return lQuery.matches;
  24728. }
  24729. /**
  24730. * A helper function that collects (already materialized) query results from a tree of views,
  24731. * starting with a provided LView.
  24732. */
  24733. function collectQueryResults(tView, lView, queryIndex, result) {
  24734. const tQuery = tView.queries.getByIndex(queryIndex);
  24735. const tQueryMatches = tQuery.matches;
  24736. if (tQueryMatches !== null) {
  24737. const lViewResults = materializeViewResults(tView, lView, tQuery, queryIndex);
  24738. for (let i = 0; i < tQueryMatches.length; i += 2) {
  24739. const tNodeIdx = tQueryMatches[i];
  24740. if (tNodeIdx > 0) {
  24741. result.push(lViewResults[i / 2]);
  24742. }
  24743. else {
  24744. const childQueryIndex = tQueryMatches[i + 1];
  24745. const declarationLContainer = lView[-tNodeIdx];
  24746. ngDevMode && assertLContainer(declarationLContainer);
  24747. // collect matches for views inserted in this container
  24748. for (let i = CONTAINER_HEADER_OFFSET; i < declarationLContainer.length; i++) {
  24749. const embeddedLView = declarationLContainer[i];
  24750. if (embeddedLView[DECLARATION_LCONTAINER] === embeddedLView[PARENT]) {
  24751. collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result);
  24752. }
  24753. }
  24754. // collect matches for views created from this declaration container and inserted into
  24755. // different containers
  24756. if (declarationLContainer[MOVED_VIEWS] !== null) {
  24757. const embeddedLViews = declarationLContainer[MOVED_VIEWS];
  24758. for (let i = 0; i < embeddedLViews.length; i++) {
  24759. const embeddedLView = embeddedLViews[i];
  24760. collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result);
  24761. }
  24762. }
  24763. }
  24764. }
  24765. }
  24766. return result;
  24767. }
  24768. /**
  24769. * Refreshes a query by combining matches from all active views and removing matches from deleted
  24770. * views.
  24771. *
  24772. * @returns `true` if a query got dirty during change detection or if this is a static query
  24773. * resolving in creation mode, `false` otherwise.
  24774. *
  24775. * @codeGenApi
  24776. */
  24777. function ɵɵqueryRefresh(queryList) {
  24778. const lView = getLView();
  24779. const tView = getTView();
  24780. const queryIndex = getCurrentQueryIndex();
  24781. setCurrentQueryIndex(queryIndex + 1);
  24782. const tQuery = getTQuery(tView, queryIndex);
  24783. if (queryList.dirty &&
  24784. (isCreationMode(lView) ===
  24785. ((tQuery.metadata.flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */))) {
  24786. if (tQuery.matches === null) {
  24787. queryList.reset([]);
  24788. }
  24789. else {
  24790. const result = tQuery.crossesNgTemplate ?
  24791. collectQueryResults(tView, lView, queryIndex, []) :
  24792. materializeViewResults(tView, lView, tQuery, queryIndex);
  24793. queryList.reset(result, unwrapElementRef);
  24794. queryList.notifyOnChanges();
  24795. }
  24796. return true;
  24797. }
  24798. return false;
  24799. }
  24800. /**
  24801. * Creates new QueryList, stores the reference in LView and returns QueryList.
  24802. *
  24803. * @param predicate The type for which the query will search
  24804. * @param flags Flags associated with the query
  24805. * @param read What to save in the query
  24806. *
  24807. * @codeGenApi
  24808. */
  24809. function ɵɵviewQuery(predicate, flags, read) {
  24810. ngDevMode && assertNumber(flags, 'Expecting flags');
  24811. const tView = getTView();
  24812. if (tView.firstCreatePass) {
  24813. createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1);
  24814. if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) {
  24815. tView.staticViewQueries = true;
  24816. }
  24817. }
  24818. createLQuery(tView, getLView(), flags);
  24819. }
  24820. /**
  24821. * Registers a QueryList, associated with a content query, for later refresh (part of a view
  24822. * refresh).
  24823. *
  24824. * @param directiveIndex Current directive index
  24825. * @param predicate The type for which the query will search
  24826. * @param flags Flags associated with the query
  24827. * @param read What to save in the query
  24828. * @returns QueryList<T>
  24829. *
  24830. * @codeGenApi
  24831. */
  24832. function ɵɵcontentQuery(directiveIndex, predicate, flags, read) {
  24833. ngDevMode && assertNumber(flags, 'Expecting flags');
  24834. const tView = getTView();
  24835. if (tView.firstCreatePass) {
  24836. const tNode = getCurrentTNode();
  24837. createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index);
  24838. saveContentQueryAndDirectiveIndex(tView, directiveIndex);
  24839. if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) {
  24840. tView.staticContentQueries = true;
  24841. }
  24842. }
  24843. createLQuery(tView, getLView(), flags);
  24844. }
  24845. /**
  24846. * Loads a QueryList corresponding to the current view or content query.
  24847. *
  24848. * @codeGenApi
  24849. */
  24850. function ɵɵloadQuery() {
  24851. return loadQueryInternal(getLView(), getCurrentQueryIndex());
  24852. }
  24853. function loadQueryInternal(lView, queryIndex) {
  24854. ngDevMode &&
  24855. assertDefined(lView[QUERIES], 'LQueries should be defined when trying to load a query');
  24856. ngDevMode && assertIndexInRange(lView[QUERIES].queries, queryIndex);
  24857. return lView[QUERIES].queries[queryIndex].queryList;
  24858. }
  24859. function createLQuery(tView, lView, flags) {
  24860. const queryList = new QueryList((flags & 4 /* QueryFlags.emitDistinctChangesOnly */) === 4 /* QueryFlags.emitDistinctChangesOnly */);
  24861. storeCleanupWithContext(tView, lView, queryList, queryList.destroy);
  24862. if (lView[QUERIES] === null)
  24863. lView[QUERIES] = new LQueries_();
  24864. lView[QUERIES].queries.push(new LQuery_(queryList));
  24865. }
  24866. function createTQuery(tView, metadata, nodeIndex) {
  24867. if (tView.queries === null)
  24868. tView.queries = new TQueries_();
  24869. tView.queries.track(new TQuery_(metadata, nodeIndex));
  24870. }
  24871. function saveContentQueryAndDirectiveIndex(tView, directiveIndex) {
  24872. const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
  24873. const lastSavedDirectiveIndex = tViewContentQueries.length ? tViewContentQueries[tViewContentQueries.length - 1] : -1;
  24874. if (directiveIndex !== lastSavedDirectiveIndex) {
  24875. tViewContentQueries.push(tView.queries.length - 1, directiveIndex);
  24876. }
  24877. }
  24878. function getTQuery(tView, index) {
  24879. ngDevMode && assertDefined(tView.queries, 'TQueries must be defined to retrieve a TQuery');
  24880. return tView.queries.getByIndex(index);
  24881. }
  24882. /**
  24883. * Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the
  24884. * `<ng-template>` element.
  24885. *
  24886. * @codeGenApi
  24887. */
  24888. function ɵɵtemplateRefExtractor(tNode, lView) {
  24889. return createTemplateRef(tNode, lView);
  24890. }
  24891. /**
  24892. * A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
  24893. *
  24894. * This should be kept up to date with the public exports of @angular/core.
  24895. */
  24896. const angularCoreEnv = (() => ({
  24897. 'ɵɵattribute': ɵɵattribute,
  24898. 'ɵɵattributeInterpolate1': ɵɵattributeInterpolate1,
  24899. 'ɵɵattributeInterpolate2': ɵɵattributeInterpolate2,
  24900. 'ɵɵattributeInterpolate3': ɵɵattributeInterpolate3,
  24901. 'ɵɵattributeInterpolate4': ɵɵattributeInterpolate4,
  24902. 'ɵɵattributeInterpolate5': ɵɵattributeInterpolate5,
  24903. 'ɵɵattributeInterpolate6': ɵɵattributeInterpolate6,
  24904. 'ɵɵattributeInterpolate7': ɵɵattributeInterpolate7,
  24905. 'ɵɵattributeInterpolate8': ɵɵattributeInterpolate8,
  24906. 'ɵɵattributeInterpolateV': ɵɵattributeInterpolateV,
  24907. 'ɵɵdefineComponent': ɵɵdefineComponent,
  24908. 'ɵɵdefineDirective': ɵɵdefineDirective,
  24909. 'ɵɵdefineInjectable': ɵɵdefineInjectable,
  24910. 'ɵɵdefineInjector': ɵɵdefineInjector,
  24911. 'ɵɵdefineNgModule': ɵɵdefineNgModule,
  24912. 'ɵɵdefinePipe': ɵɵdefinePipe,
  24913. 'ɵɵdirectiveInject': ɵɵdirectiveInject,
  24914. 'ɵɵgetInheritedFactory': ɵɵgetInheritedFactory,
  24915. 'ɵɵinject': ɵɵinject,
  24916. 'ɵɵinjectAttribute': ɵɵinjectAttribute,
  24917. 'ɵɵinvalidFactory': ɵɵinvalidFactory,
  24918. 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep,
  24919. 'ɵɵtemplateRefExtractor': ɵɵtemplateRefExtractor,
  24920. 'ɵɵresetView': ɵɵresetView,
  24921. 'ɵɵHostDirectivesFeature': ɵɵHostDirectivesFeature,
  24922. 'ɵɵNgOnChangesFeature': ɵɵNgOnChangesFeature,
  24923. 'ɵɵProvidersFeature': ɵɵProvidersFeature,
  24924. 'ɵɵCopyDefinitionFeature': ɵɵCopyDefinitionFeature,
  24925. 'ɵɵInheritDefinitionFeature': ɵɵInheritDefinitionFeature,
  24926. 'ɵɵInputTransformsFeature': ɵɵInputTransformsFeature,
  24927. 'ɵɵStandaloneFeature': ɵɵStandaloneFeature,
  24928. 'ɵɵnextContext': ɵɵnextContext,
  24929. 'ɵɵnamespaceHTML': ɵɵnamespaceHTML,
  24930. 'ɵɵnamespaceMathML': ɵɵnamespaceMathML,
  24931. 'ɵɵnamespaceSVG': ɵɵnamespaceSVG,
  24932. 'ɵɵenableBindings': ɵɵenableBindings,
  24933. 'ɵɵdisableBindings': ɵɵdisableBindings,
  24934. 'ɵɵelementStart': ɵɵelementStart,
  24935. 'ɵɵelementEnd': ɵɵelementEnd,
  24936. 'ɵɵelement': ɵɵelement,
  24937. 'ɵɵelementContainerStart': ɵɵelementContainerStart,
  24938. 'ɵɵelementContainerEnd': ɵɵelementContainerEnd,
  24939. 'ɵɵelementContainer': ɵɵelementContainer,
  24940. 'ɵɵpureFunction0': ɵɵpureFunction0,
  24941. 'ɵɵpureFunction1': ɵɵpureFunction1,
  24942. 'ɵɵpureFunction2': ɵɵpureFunction2,
  24943. 'ɵɵpureFunction3': ɵɵpureFunction3,
  24944. 'ɵɵpureFunction4': ɵɵpureFunction4,
  24945. 'ɵɵpureFunction5': ɵɵpureFunction5,
  24946. 'ɵɵpureFunction6': ɵɵpureFunction6,
  24947. 'ɵɵpureFunction7': ɵɵpureFunction7,
  24948. 'ɵɵpureFunction8': ɵɵpureFunction8,
  24949. 'ɵɵpureFunctionV': ɵɵpureFunctionV,
  24950. 'ɵɵgetCurrentView': ɵɵgetCurrentView,
  24951. 'ɵɵrestoreView': ɵɵrestoreView,
  24952. 'ɵɵlistener': ɵɵlistener,
  24953. 'ɵɵprojection': ɵɵprojection,
  24954. 'ɵɵsyntheticHostProperty': ɵɵsyntheticHostProperty,
  24955. 'ɵɵsyntheticHostListener': ɵɵsyntheticHostListener,
  24956. 'ɵɵpipeBind1': ɵɵpipeBind1,
  24957. 'ɵɵpipeBind2': ɵɵpipeBind2,
  24958. 'ɵɵpipeBind3': ɵɵpipeBind3,
  24959. 'ɵɵpipeBind4': ɵɵpipeBind4,
  24960. 'ɵɵpipeBindV': ɵɵpipeBindV,
  24961. 'ɵɵprojectionDef': ɵɵprojectionDef,
  24962. 'ɵɵhostProperty': ɵɵhostProperty,
  24963. 'ɵɵproperty': ɵɵproperty,
  24964. 'ɵɵpropertyInterpolate': ɵɵpropertyInterpolate,
  24965. 'ɵɵpropertyInterpolate1': ɵɵpropertyInterpolate1,
  24966. 'ɵɵpropertyInterpolate2': ɵɵpropertyInterpolate2,
  24967. 'ɵɵpropertyInterpolate3': ɵɵpropertyInterpolate3,
  24968. 'ɵɵpropertyInterpolate4': ɵɵpropertyInterpolate4,
  24969. 'ɵɵpropertyInterpolate5': ɵɵpropertyInterpolate5,
  24970. 'ɵɵpropertyInterpolate6': ɵɵpropertyInterpolate6,
  24971. 'ɵɵpropertyInterpolate7': ɵɵpropertyInterpolate7,
  24972. 'ɵɵpropertyInterpolate8': ɵɵpropertyInterpolate8,
  24973. 'ɵɵpropertyInterpolateV': ɵɵpropertyInterpolateV,
  24974. 'ɵɵpipe': ɵɵpipe,
  24975. 'ɵɵqueryRefresh': ɵɵqueryRefresh,
  24976. 'ɵɵviewQuery': ɵɵviewQuery,
  24977. 'ɵɵloadQuery': ɵɵloadQuery,
  24978. 'ɵɵcontentQuery': ɵɵcontentQuery,
  24979. 'ɵɵreference': ɵɵreference,
  24980. 'ɵɵclassMap': ɵɵclassMap,
  24981. 'ɵɵclassMapInterpolate1': ɵɵclassMapInterpolate1,
  24982. 'ɵɵclassMapInterpolate2': ɵɵclassMapInterpolate2,
  24983. 'ɵɵclassMapInterpolate3': ɵɵclassMapInterpolate3,
  24984. 'ɵɵclassMapInterpolate4': ɵɵclassMapInterpolate4,
  24985. 'ɵɵclassMapInterpolate5': ɵɵclassMapInterpolate5,
  24986. 'ɵɵclassMapInterpolate6': ɵɵclassMapInterpolate6,
  24987. 'ɵɵclassMapInterpolate7': ɵɵclassMapInterpolate7,
  24988. 'ɵɵclassMapInterpolate8': ɵɵclassMapInterpolate8,
  24989. 'ɵɵclassMapInterpolateV': ɵɵclassMapInterpolateV,
  24990. 'ɵɵstyleMap': ɵɵstyleMap,
  24991. 'ɵɵstyleMapInterpolate1': ɵɵstyleMapInterpolate1,
  24992. 'ɵɵstyleMapInterpolate2': ɵɵstyleMapInterpolate2,
  24993. 'ɵɵstyleMapInterpolate3': ɵɵstyleMapInterpolate3,
  24994. 'ɵɵstyleMapInterpolate4': ɵɵstyleMapInterpolate4,
  24995. 'ɵɵstyleMapInterpolate5': ɵɵstyleMapInterpolate5,
  24996. 'ɵɵstyleMapInterpolate6': ɵɵstyleMapInterpolate6,
  24997. 'ɵɵstyleMapInterpolate7': ɵɵstyleMapInterpolate7,
  24998. 'ɵɵstyleMapInterpolate8': ɵɵstyleMapInterpolate8,
  24999. 'ɵɵstyleMapInterpolateV': ɵɵstyleMapInterpolateV,
  25000. 'ɵɵstyleProp': ɵɵstyleProp,
  25001. 'ɵɵstylePropInterpolate1': ɵɵstylePropInterpolate1,
  25002. 'ɵɵstylePropInterpolate2': ɵɵstylePropInterpolate2,
  25003. 'ɵɵstylePropInterpolate3': ɵɵstylePropInterpolate3,
  25004. 'ɵɵstylePropInterpolate4': ɵɵstylePropInterpolate4,
  25005. 'ɵɵstylePropInterpolate5': ɵɵstylePropInterpolate5,
  25006. 'ɵɵstylePropInterpolate6': ɵɵstylePropInterpolate6,
  25007. 'ɵɵstylePropInterpolate7': ɵɵstylePropInterpolate7,
  25008. 'ɵɵstylePropInterpolate8': ɵɵstylePropInterpolate8,
  25009. 'ɵɵstylePropInterpolateV': ɵɵstylePropInterpolateV,
  25010. 'ɵɵclassProp': ɵɵclassProp,
  25011. 'ɵɵadvance': ɵɵadvance,
  25012. 'ɵɵtemplate': ɵɵtemplate,
  25013. 'ɵɵdefer': ɵɵdefer,
  25014. 'ɵɵtext': ɵɵtext,
  25015. 'ɵɵtextInterpolate': ɵɵtextInterpolate,
  25016. 'ɵɵtextInterpolate1': ɵɵtextInterpolate1,
  25017. 'ɵɵtextInterpolate2': ɵɵtextInterpolate2,
  25018. 'ɵɵtextInterpolate3': ɵɵtextInterpolate3,
  25019. 'ɵɵtextInterpolate4': ɵɵtextInterpolate4,
  25020. 'ɵɵtextInterpolate5': ɵɵtextInterpolate5,
  25021. 'ɵɵtextInterpolate6': ɵɵtextInterpolate6,
  25022. 'ɵɵtextInterpolate7': ɵɵtextInterpolate7,
  25023. 'ɵɵtextInterpolate8': ɵɵtextInterpolate8,
  25024. 'ɵɵtextInterpolateV': ɵɵtextInterpolateV,
  25025. 'ɵɵi18n': ɵɵi18n,
  25026. 'ɵɵi18nAttributes': ɵɵi18nAttributes,
  25027. 'ɵɵi18nExp': ɵɵi18nExp,
  25028. 'ɵɵi18nStart': ɵɵi18nStart,
  25029. 'ɵɵi18nEnd': ɵɵi18nEnd,
  25030. 'ɵɵi18nApply': ɵɵi18nApply,
  25031. 'ɵɵi18nPostprocess': ɵɵi18nPostprocess,
  25032. 'ɵɵresolveWindow': ɵɵresolveWindow,
  25033. 'ɵɵresolveDocument': ɵɵresolveDocument,
  25034. 'ɵɵresolveBody': ɵɵresolveBody,
  25035. 'ɵɵsetComponentScope': ɵɵsetComponentScope,
  25036. 'ɵɵsetNgModuleScope': ɵɵsetNgModuleScope,
  25037. 'ɵɵregisterNgModuleType': registerNgModuleType,
  25038. 'ɵɵsanitizeHtml': ɵɵsanitizeHtml,
  25039. 'ɵɵsanitizeStyle': ɵɵsanitizeStyle,
  25040. 'ɵɵsanitizeResourceUrl': ɵɵsanitizeResourceUrl,
  25041. 'ɵɵsanitizeScript': ɵɵsanitizeScript,
  25042. 'ɵɵsanitizeUrl': ɵɵsanitizeUrl,
  25043. 'ɵɵsanitizeUrlOrResourceUrl': ɵɵsanitizeUrlOrResourceUrl,
  25044. 'ɵɵtrustConstantHtml': ɵɵtrustConstantHtml,
  25045. 'ɵɵtrustConstantResourceUrl': ɵɵtrustConstantResourceUrl,
  25046. 'ɵɵvalidateIframeAttribute': ɵɵvalidateIframeAttribute,
  25047. 'forwardRef': forwardRef,
  25048. 'resolveForwardRef': resolveForwardRef,
  25049. }))();
  25050. let jitOptions = null;
  25051. function setJitOptions(options) {
  25052. if (jitOptions !== null) {
  25053. if (options.defaultEncapsulation !== jitOptions.defaultEncapsulation) {
  25054. ngDevMode &&
  25055. console.error('Provided value for `defaultEncapsulation` can not be changed once it has been set.');
  25056. return;
  25057. }
  25058. if (options.preserveWhitespaces !== jitOptions.preserveWhitespaces) {
  25059. ngDevMode &&
  25060. console.error('Provided value for `preserveWhitespaces` can not be changed once it has been set.');
  25061. return;
  25062. }
  25063. }
  25064. jitOptions = options;
  25065. }
  25066. function getJitOptions() {
  25067. return jitOptions;
  25068. }
  25069. function resetJitOptions() {
  25070. jitOptions = null;
  25071. }
  25072. function patchModuleCompilation() {
  25073. // Does nothing, but exists as a target for patching.
  25074. }
  25075. function isModuleWithProviders(value) {
  25076. return value.ngModule !== undefined;
  25077. }
  25078. function isNgModule(value) {
  25079. return !!getNgModuleDef(value);
  25080. }
  25081. function isPipe(value) {
  25082. return !!getPipeDef$1(value);
  25083. }
  25084. function isDirective(value) {
  25085. return !!getDirectiveDef(value);
  25086. }
  25087. function isComponent(value) {
  25088. return !!getComponentDef(value);
  25089. }
  25090. const moduleQueue = [];
  25091. /**
  25092. * Enqueues moduleDef to be checked later to see if scope can be set on its
  25093. * component declarations.
  25094. */
  25095. function enqueueModuleForDelayedScoping(moduleType, ngModule) {
  25096. moduleQueue.push({ moduleType, ngModule });
  25097. }
  25098. let flushingModuleQueue = false;
  25099. /**
  25100. * Loops over queued module definitions, if a given module definition has all of its
  25101. * declarations resolved, it dequeues that module definition and sets the scope on
  25102. * its declarations.
  25103. */
  25104. function flushModuleScopingQueueAsMuchAsPossible() {
  25105. if (!flushingModuleQueue) {
  25106. flushingModuleQueue = true;
  25107. try {
  25108. for (let i = moduleQueue.length - 1; i >= 0; i--) {
  25109. const { moduleType, ngModule } = moduleQueue[i];
  25110. if (ngModule.declarations && ngModule.declarations.every(isResolvedDeclaration)) {
  25111. // dequeue
  25112. moduleQueue.splice(i, 1);
  25113. setScopeOnDeclaredComponents(moduleType, ngModule);
  25114. }
  25115. }
  25116. }
  25117. finally {
  25118. flushingModuleQueue = false;
  25119. }
  25120. }
  25121. }
  25122. /**
  25123. * Returns truthy if a declaration has resolved. If the declaration happens to be
  25124. * an array of declarations, it will recurse to check each declaration in that array
  25125. * (which may also be arrays).
  25126. */
  25127. function isResolvedDeclaration(declaration) {
  25128. if (Array.isArray(declaration)) {
  25129. return declaration.every(isResolvedDeclaration);
  25130. }
  25131. return !!resolveForwardRef(declaration);
  25132. }
  25133. /**
  25134. * Compiles a module in JIT mode.
  25135. *
  25136. * This function automatically gets called when a class has a `@NgModule` decorator.
  25137. */
  25138. function compileNgModule(moduleType, ngModule = {}) {
  25139. patchModuleCompilation();
  25140. compileNgModuleDefs(moduleType, ngModule);
  25141. if (ngModule.id !== undefined) {
  25142. registerNgModuleType(moduleType, ngModule.id);
  25143. }
  25144. // Because we don't know if all declarations have resolved yet at the moment the
  25145. // NgModule decorator is executing, we're enqueueing the setting of module scope
  25146. // on its declarations to be run at a later time when all declarations for the module,
  25147. // including forward refs, have resolved.
  25148. enqueueModuleForDelayedScoping(moduleType, ngModule);
  25149. }
  25150. /**
  25151. * Compiles and adds the `ɵmod`, `ɵfac` and `ɵinj` properties to the module class.
  25152. *
  25153. * It's possible to compile a module via this API which will allow duplicate declarations in its
  25154. * root.
  25155. */
  25156. function compileNgModuleDefs(moduleType, ngModule, allowDuplicateDeclarationsInRoot = false) {
  25157. ngDevMode && assertDefined(moduleType, 'Required value moduleType');
  25158. ngDevMode && assertDefined(ngModule, 'Required value ngModule');
  25159. const declarations = flatten(ngModule.declarations || EMPTY_ARRAY);
  25160. let ngModuleDef = null;
  25161. Object.defineProperty(moduleType, NG_MOD_DEF, {
  25162. configurable: true,
  25163. get: () => {
  25164. if (ngModuleDef === null) {
  25165. if (ngDevMode && ngModule.imports && ngModule.imports.indexOf(moduleType) > -1) {
  25166. // We need to assert this immediately, because allowing it to continue will cause it to
  25167. // go into an infinite loop before we've reached the point where we throw all the errors.
  25168. throw new Error(`'${stringifyForError(moduleType)}' module can't import itself`);
  25169. }
  25170. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
  25171. ngModuleDef = compiler.compileNgModule(angularCoreEnv, `ng:///${moduleType.name}/ɵmod.js`, {
  25172. type: moduleType,
  25173. bootstrap: flatten(ngModule.bootstrap || EMPTY_ARRAY).map(resolveForwardRef),
  25174. declarations: declarations.map(resolveForwardRef),
  25175. imports: flatten(ngModule.imports || EMPTY_ARRAY)
  25176. .map(resolveForwardRef)
  25177. .map(expandModuleWithProviders),
  25178. exports: flatten(ngModule.exports || EMPTY_ARRAY)
  25179. .map(resolveForwardRef)
  25180. .map(expandModuleWithProviders),
  25181. schemas: ngModule.schemas ? flatten(ngModule.schemas) : null,
  25182. id: ngModule.id || null,
  25183. });
  25184. // Set `schemas` on ngModuleDef to an empty array in JIT mode to indicate that runtime
  25185. // should verify that there are no unknown elements in a template. In AOT mode, that check
  25186. // happens at compile time and `schemas` information is not present on Component and Module
  25187. // defs after compilation (so the check doesn't happen the second time at runtime).
  25188. if (!ngModuleDef.schemas) {
  25189. ngModuleDef.schemas = [];
  25190. }
  25191. }
  25192. return ngModuleDef;
  25193. }
  25194. });
  25195. let ngFactoryDef = null;
  25196. Object.defineProperty(moduleType, NG_FACTORY_DEF, {
  25197. get: () => {
  25198. if (ngFactoryDef === null) {
  25199. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
  25200. ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${moduleType.name}/ɵfac.js`, {
  25201. name: moduleType.name,
  25202. type: moduleType,
  25203. deps: reflectDependencies(moduleType),
  25204. target: compiler.FactoryTarget.NgModule,
  25205. typeArgumentCount: 0,
  25206. });
  25207. }
  25208. return ngFactoryDef;
  25209. },
  25210. // Make the property configurable in dev mode to allow overriding in tests
  25211. configurable: !!ngDevMode,
  25212. });
  25213. let ngInjectorDef = null;
  25214. Object.defineProperty(moduleType, NG_INJ_DEF, {
  25215. get: () => {
  25216. if (ngInjectorDef === null) {
  25217. ngDevMode && verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot);
  25218. const meta = {
  25219. name: moduleType.name,
  25220. type: moduleType,
  25221. providers: ngModule.providers || EMPTY_ARRAY,
  25222. imports: [
  25223. (ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef),
  25224. (ngModule.exports || EMPTY_ARRAY).map(resolveForwardRef),
  25225. ],
  25226. };
  25227. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
  25228. ngInjectorDef =
  25229. compiler.compileInjector(angularCoreEnv, `ng:///${moduleType.name}/ɵinj.js`, meta);
  25230. }
  25231. return ngInjectorDef;
  25232. },
  25233. // Make the property configurable in dev mode to allow overriding in tests
  25234. configurable: !!ngDevMode,
  25235. });
  25236. }
  25237. function generateStandaloneInDeclarationsError(type, location) {
  25238. const prefix = `Unexpected "${stringifyForError(type)}" found in the "declarations" array of the`;
  25239. const suffix = `"${stringifyForError(type)}" is marked as standalone and can't be declared ` +
  25240. 'in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?';
  25241. return `${prefix} ${location}, ${suffix}`;
  25242. }
  25243. function verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot, importingModule) {
  25244. if (verifiedNgModule.get(moduleType))
  25245. return;
  25246. // skip verifications of standalone components, directives, and pipes
  25247. if (isStandalone(moduleType))
  25248. return;
  25249. verifiedNgModule.set(moduleType, true);
  25250. moduleType = resolveForwardRef(moduleType);
  25251. let ngModuleDef;
  25252. if (importingModule) {
  25253. ngModuleDef = getNgModuleDef(moduleType);
  25254. if (!ngModuleDef) {
  25255. throw new Error(`Unexpected value '${moduleType.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
  25256. }
  25257. }
  25258. else {
  25259. ngModuleDef = getNgModuleDef(moduleType, true);
  25260. }
  25261. const errors = [];
  25262. const declarations = maybeUnwrapFn(ngModuleDef.declarations);
  25263. const imports = maybeUnwrapFn(ngModuleDef.imports);
  25264. flatten(imports).map(unwrapModuleWithProvidersImports).forEach(modOrStandaloneCmpt => {
  25265. verifySemanticsOfNgModuleImport(modOrStandaloneCmpt, moduleType);
  25266. verifySemanticsOfNgModuleDef(modOrStandaloneCmpt, false, moduleType);
  25267. });
  25268. const exports = maybeUnwrapFn(ngModuleDef.exports);
  25269. declarations.forEach(verifyDeclarationsHaveDefinitions);
  25270. declarations.forEach(verifyDirectivesHaveSelector);
  25271. declarations.forEach((declarationType) => verifyNotStandalone(declarationType, moduleType));
  25272. const combinedDeclarations = [
  25273. ...declarations.map(resolveForwardRef),
  25274. ...flatten(imports.map(computeCombinedExports)).map(resolveForwardRef),
  25275. ];
  25276. exports.forEach(verifyExportsAreDeclaredOrReExported);
  25277. declarations.forEach(decl => verifyDeclarationIsUnique(decl, allowDuplicateDeclarationsInRoot));
  25278. const ngModule = getAnnotation(moduleType, 'NgModule');
  25279. if (ngModule) {
  25280. ngModule.imports &&
  25281. flatten(ngModule.imports).map(unwrapModuleWithProvidersImports).forEach(mod => {
  25282. verifySemanticsOfNgModuleImport(mod, moduleType);
  25283. verifySemanticsOfNgModuleDef(mod, false, moduleType);
  25284. });
  25285. ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyCorrectBootstrapType);
  25286. ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyComponentIsPartOfNgModule);
  25287. }
  25288. // Throw Error if any errors were detected.
  25289. if (errors.length) {
  25290. throw new Error(errors.join('\n'));
  25291. }
  25292. ////////////////////////////////////////////////////////////////////////////////////////////////
  25293. function verifyDeclarationsHaveDefinitions(type) {
  25294. type = resolveForwardRef(type);
  25295. const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef$1(type);
  25296. if (!def) {
  25297. errors.push(`Unexpected value '${stringifyForError(type)}' declared by the module '${stringifyForError(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`);
  25298. }
  25299. }
  25300. function verifyDirectivesHaveSelector(type) {
  25301. type = resolveForwardRef(type);
  25302. const def = getDirectiveDef(type);
  25303. if (!getComponentDef(type) && def && def.selectors.length == 0) {
  25304. errors.push(`Directive ${stringifyForError(type)} has no selector, please add it!`);
  25305. }
  25306. }
  25307. function verifyNotStandalone(type, moduleType) {
  25308. type = resolveForwardRef(type);
  25309. const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef$1(type);
  25310. if (def?.standalone) {
  25311. const location = `"${stringifyForError(moduleType)}" NgModule`;
  25312. errors.push(generateStandaloneInDeclarationsError(type, location));
  25313. }
  25314. }
  25315. function verifyExportsAreDeclaredOrReExported(type) {
  25316. type = resolveForwardRef(type);
  25317. const kind = getComponentDef(type) && 'component' || getDirectiveDef(type) && 'directive' ||
  25318. getPipeDef$1(type) && 'pipe';
  25319. if (kind) {
  25320. // only checked if we are declared as Component, Directive, or Pipe
  25321. // Modules don't need to be declared or imported.
  25322. if (combinedDeclarations.lastIndexOf(type) === -1) {
  25323. // We are exporting something which we don't explicitly declare or import.
  25324. errors.push(`Can't export ${kind} ${stringifyForError(type)} from ${stringifyForError(moduleType)} as it was neither declared nor imported!`);
  25325. }
  25326. }
  25327. }
  25328. function verifyDeclarationIsUnique(type, suppressErrors) {
  25329. type = resolveForwardRef(type);
  25330. const existingModule = ownerNgModule.get(type);
  25331. if (existingModule && existingModule !== moduleType) {
  25332. if (!suppressErrors) {
  25333. const modules = [existingModule, moduleType].map(stringifyForError).sort();
  25334. errors.push(`Type ${stringifyForError(type)} is part of the declarations of 2 modules: ${modules[0]} and ${modules[1]}! ` +
  25335. `Please consider moving ${stringifyForError(type)} to a higher module that imports ${modules[0]} and ${modules[1]}. ` +
  25336. `You can also create a new NgModule that exports and includes ${stringifyForError(type)} then import that NgModule in ${modules[0]} and ${modules[1]}.`);
  25337. }
  25338. }
  25339. else {
  25340. // Mark type as having owner.
  25341. ownerNgModule.set(type, moduleType);
  25342. }
  25343. }
  25344. function verifyComponentIsPartOfNgModule(type) {
  25345. type = resolveForwardRef(type);
  25346. const existingModule = ownerNgModule.get(type);
  25347. if (!existingModule && !isStandalone(type)) {
  25348. errors.push(`Component ${stringifyForError(type)} is not part of any NgModule or the module has not been imported into your module.`);
  25349. }
  25350. }
  25351. function verifyCorrectBootstrapType(type) {
  25352. type = resolveForwardRef(type);
  25353. if (!getComponentDef(type)) {
  25354. errors.push(`${stringifyForError(type)} cannot be used as an entry component.`);
  25355. }
  25356. if (isStandalone(type)) {
  25357. // Note: this error should be the same as the
  25358. // `NGMODULE_BOOTSTRAP_IS_STANDALONE` one in AOT compiler.
  25359. errors.push(`The \`${stringifyForError(type)}\` class is a standalone component, which can ` +
  25360. `not be used in the \`@NgModule.bootstrap\` array. Use the \`bootstrapApplication\` ` +
  25361. `function for bootstrap instead.`);
  25362. }
  25363. }
  25364. function verifySemanticsOfNgModuleImport(type, importingModule) {
  25365. type = resolveForwardRef(type);
  25366. const directiveDef = getComponentDef(type) || getDirectiveDef(type);
  25367. if (directiveDef !== null && !directiveDef.standalone) {
  25368. throw new Error(`Unexpected directive '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
  25369. }
  25370. const pipeDef = getPipeDef$1(type);
  25371. if (pipeDef !== null && !pipeDef.standalone) {
  25372. throw new Error(`Unexpected pipe '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
  25373. }
  25374. }
  25375. }
  25376. function unwrapModuleWithProvidersImports(typeOrWithProviders) {
  25377. typeOrWithProviders = resolveForwardRef(typeOrWithProviders);
  25378. return typeOrWithProviders.ngModule || typeOrWithProviders;
  25379. }
  25380. function getAnnotation(type, name) {
  25381. let annotation = null;
  25382. collect(type.__annotations__);
  25383. collect(type.decorators);
  25384. return annotation;
  25385. function collect(annotations) {
  25386. if (annotations) {
  25387. annotations.forEach(readAnnotation);
  25388. }
  25389. }
  25390. function readAnnotation(decorator) {
  25391. if (!annotation) {
  25392. const proto = Object.getPrototypeOf(decorator);
  25393. if (proto.ngMetadataName == name) {
  25394. annotation = decorator;
  25395. }
  25396. else if (decorator.type) {
  25397. const proto = Object.getPrototypeOf(decorator.type);
  25398. if (proto.ngMetadataName == name) {
  25399. annotation = decorator.args[0];
  25400. }
  25401. }
  25402. }
  25403. }
  25404. }
  25405. /**
  25406. * Keep track of compiled components. This is needed because in tests we often want to compile the
  25407. * same component with more than one NgModule. This would cause an error unless we reset which
  25408. * NgModule the component belongs to. We keep the list of compiled components here so that the
  25409. * TestBed can reset it later.
  25410. */
  25411. let ownerNgModule = new WeakMap();
  25412. let verifiedNgModule = new WeakMap();
  25413. function resetCompiledComponents() {
  25414. ownerNgModule = new WeakMap();
  25415. verifiedNgModule = new WeakMap();
  25416. moduleQueue.length = 0;
  25417. GENERATED_COMP_IDS.clear();
  25418. }
  25419. /**
  25420. * Computes the combined declarations of explicit declarations, as well as declarations inherited by
  25421. * traversing the exports of imported modules.
  25422. * @param type
  25423. */
  25424. function computeCombinedExports(type) {
  25425. type = resolveForwardRef(type);
  25426. const ngModuleDef = getNgModuleDef(type);
  25427. // a standalone component, directive or pipe
  25428. if (ngModuleDef === null) {
  25429. return [type];
  25430. }
  25431. return flatten(maybeUnwrapFn(ngModuleDef.exports).map((type) => {
  25432. const ngModuleDef = getNgModuleDef(type);
  25433. if (ngModuleDef) {
  25434. verifySemanticsOfNgModuleDef(type, false);
  25435. return computeCombinedExports(type);
  25436. }
  25437. else {
  25438. return type;
  25439. }
  25440. }));
  25441. }
  25442. /**
  25443. * Some declared components may be compiled asynchronously, and thus may not have their
  25444. * ɵcmp set yet. If this is the case, then a reference to the module is written into
  25445. * the `ngSelectorScope` property of the declared type.
  25446. */
  25447. function setScopeOnDeclaredComponents(moduleType, ngModule) {
  25448. const declarations = flatten(ngModule.declarations || EMPTY_ARRAY);
  25449. const transitiveScopes = transitiveScopesFor(moduleType);
  25450. declarations.forEach(declaration => {
  25451. declaration = resolveForwardRef(declaration);
  25452. if (declaration.hasOwnProperty(NG_COMP_DEF)) {
  25453. // A `ɵcmp` field exists - go ahead and patch the component directly.
  25454. const component = declaration;
  25455. const componentDef = getComponentDef(component);
  25456. patchComponentDefWithScope(componentDef, transitiveScopes);
  25457. }
  25458. else if (!declaration.hasOwnProperty(NG_DIR_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) {
  25459. // Set `ngSelectorScope` for future reference when the component compilation finishes.
  25460. declaration.ngSelectorScope = moduleType;
  25461. }
  25462. });
  25463. }
  25464. /**
  25465. * Patch the definition of a component with directives and pipes from the compilation scope of
  25466. * a given module.
  25467. */
  25468. function patchComponentDefWithScope(componentDef, transitiveScopes) {
  25469. componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives)
  25470. .map(dir => dir.hasOwnProperty(NG_COMP_DEF) ? getComponentDef(dir) : getDirectiveDef(dir))
  25471. .filter(def => !!def);
  25472. componentDef.pipeDefs = () => Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef$1(pipe));
  25473. componentDef.schemas = transitiveScopes.schemas;
  25474. // Since we avoid Components/Directives/Pipes recompiling in case there are no overrides, we
  25475. // may face a problem where previously compiled defs available to a given Component/Directive
  25476. // are cached in TView and may become stale (in case any of these defs gets recompiled). In
  25477. // order to avoid this problem, we force fresh TView to be created.
  25478. componentDef.tView = null;
  25479. }
  25480. /**
  25481. * Compute the pair of transitive scopes (compilation scope and exported scope) for a given type
  25482. * (either a NgModule or a standalone component / directive / pipe).
  25483. */
  25484. function transitiveScopesFor(type) {
  25485. if (isNgModule(type)) {
  25486. return transitiveScopesForNgModule(type);
  25487. }
  25488. else if (isStandalone(type)) {
  25489. const directiveDef = getComponentDef(type) || getDirectiveDef(type);
  25490. if (directiveDef !== null) {
  25491. return {
  25492. schemas: null,
  25493. compilation: {
  25494. directives: new Set(),
  25495. pipes: new Set(),
  25496. },
  25497. exported: {
  25498. directives: new Set([type]),
  25499. pipes: new Set(),
  25500. },
  25501. };
  25502. }
  25503. const pipeDef = getPipeDef$1(type);
  25504. if (pipeDef !== null) {
  25505. return {
  25506. schemas: null,
  25507. compilation: {
  25508. directives: new Set(),
  25509. pipes: new Set(),
  25510. },
  25511. exported: {
  25512. directives: new Set(),
  25513. pipes: new Set([type]),
  25514. },
  25515. };
  25516. }
  25517. }
  25518. // TODO: change the error message to be more user-facing and take standalone into account
  25519. throw new Error(`${type.name} does not have a module def (ɵmod property)`);
  25520. }
  25521. /**
  25522. * Compute the pair of transitive scopes (compilation scope and exported scope) for a given module.
  25523. *
  25524. * This operation is memoized and the result is cached on the module's definition. This function can
  25525. * be called on modules with components that have not fully compiled yet, but the result should not
  25526. * be used until they have.
  25527. *
  25528. * @param moduleType module that transitive scope should be calculated for.
  25529. */
  25530. function transitiveScopesForNgModule(moduleType) {
  25531. const def = getNgModuleDef(moduleType, true);
  25532. if (def.transitiveCompileScopes !== null) {
  25533. return def.transitiveCompileScopes;
  25534. }
  25535. const scopes = {
  25536. schemas: def.schemas || null,
  25537. compilation: {
  25538. directives: new Set(),
  25539. pipes: new Set(),
  25540. },
  25541. exported: {
  25542. directives: new Set(),
  25543. pipes: new Set(),
  25544. },
  25545. };
  25546. maybeUnwrapFn(def.imports).forEach((imported) => {
  25547. // When this module imports another, the imported module's exported directives and pipes are
  25548. // added to the compilation scope of this module.
  25549. const importedScope = transitiveScopesFor(imported);
  25550. importedScope.exported.directives.forEach(entry => scopes.compilation.directives.add(entry));
  25551. importedScope.exported.pipes.forEach(entry => scopes.compilation.pipes.add(entry));
  25552. });
  25553. maybeUnwrapFn(def.declarations).forEach(declared => {
  25554. const declaredWithDefs = declared;
  25555. if (getPipeDef$1(declaredWithDefs)) {
  25556. scopes.compilation.pipes.add(declared);
  25557. }
  25558. else {
  25559. // Either declared has a ɵcmp or ɵdir, or it's a component which hasn't
  25560. // had its template compiled yet. In either case, it gets added to the compilation's
  25561. // directives.
  25562. scopes.compilation.directives.add(declared);
  25563. }
  25564. });
  25565. maybeUnwrapFn(def.exports).forEach((exported) => {
  25566. const exportedType = exported;
  25567. // Either the type is a module, a pipe, or a component/directive (which may not have a
  25568. // ɵcmp as it might be compiled asynchronously).
  25569. if (isNgModule(exportedType)) {
  25570. // When this module exports another, the exported module's exported directives and pipes are
  25571. // added to both the compilation and exported scopes of this module.
  25572. const exportedScope = transitiveScopesFor(exportedType);
  25573. exportedScope.exported.directives.forEach(entry => {
  25574. scopes.compilation.directives.add(entry);
  25575. scopes.exported.directives.add(entry);
  25576. });
  25577. exportedScope.exported.pipes.forEach(entry => {
  25578. scopes.compilation.pipes.add(entry);
  25579. scopes.exported.pipes.add(entry);
  25580. });
  25581. }
  25582. else if (getPipeDef$1(exportedType)) {
  25583. scopes.exported.pipes.add(exportedType);
  25584. }
  25585. else {
  25586. scopes.exported.directives.add(exportedType);
  25587. }
  25588. });
  25589. def.transitiveCompileScopes = scopes;
  25590. return scopes;
  25591. }
  25592. function expandModuleWithProviders(value) {
  25593. if (isModuleWithProviders(value)) {
  25594. return value.ngModule;
  25595. }
  25596. return value;
  25597. }
  25598. /**
  25599. * Keep track of the compilation depth to avoid reentrancy issues during JIT compilation. This
  25600. * matters in the following scenario:
  25601. *
  25602. * Consider a component 'A' that extends component 'B', both declared in module 'M'. During
  25603. * the compilation of 'A' the definition of 'B' is requested to capture the inheritance chain,
  25604. * potentially triggering compilation of 'B'. If this nested compilation were to trigger
  25605. * `flushModuleScopingQueueAsMuchAsPossible` it may happen that module 'M' is still pending in the
  25606. * queue, resulting in 'A' and 'B' to be patched with the NgModule scope. As the compilation of
  25607. * 'A' is still in progress, this would introduce a circular dependency on its compilation. To avoid
  25608. * this issue, the module scope queue is only flushed for compilations at the depth 0, to ensure
  25609. * all compilations have finished.
  25610. */
  25611. let compilationDepth = 0;
  25612. /**
  25613. * Compile an Angular component according to its decorator metadata, and patch the resulting
  25614. * component def (ɵcmp) onto the component type.
  25615. *
  25616. * Compilation may be asynchronous (due to the need to resolve URLs for the component template or
  25617. * other resources, for example). In the event that compilation is not immediate, `compileComponent`
  25618. * will enqueue resource resolution into a global queue and will fail to return the `ɵcmp`
  25619. * until the global queue has been resolved with a call to `resolveComponentResources`.
  25620. */
  25621. function compileComponent(type, metadata) {
  25622. // Initialize ngDevMode. This must be the first statement in compileComponent.
  25623. // See the `initNgDevMode` docstring for more information.
  25624. (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
  25625. let ngComponentDef = null;
  25626. // Metadata may have resources which need to be resolved.
  25627. maybeQueueResolutionOfComponentResources(type, metadata);
  25628. // Note that we're using the same function as `Directive`, because that's only subset of metadata
  25629. // that we need to create the ngFactoryDef. We're avoiding using the component metadata
  25630. // because we'd have to resolve the asynchronous templates.
  25631. addDirectiveFactoryDef(type, metadata);
  25632. Object.defineProperty(type, NG_COMP_DEF, {
  25633. get: () => {
  25634. if (ngComponentDef === null) {
  25635. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'component', type: type });
  25636. if (componentNeedsResolution(metadata)) {
  25637. const error = [`Component '${type.name}' is not resolved:`];
  25638. if (metadata.templateUrl) {
  25639. error.push(` - templateUrl: ${metadata.templateUrl}`);
  25640. }
  25641. if (metadata.styleUrls && metadata.styleUrls.length) {
  25642. error.push(` - styleUrls: ${JSON.stringify(metadata.styleUrls)}`);
  25643. }
  25644. error.push(`Did you run and wait for 'resolveComponentResources()'?`);
  25645. throw new Error(error.join('\n'));
  25646. }
  25647. // This const was called `jitOptions` previously but had to be renamed to `options` because
  25648. // of a bug with Terser that caused optimized JIT builds to throw a `ReferenceError`.
  25649. // This bug was investigated in https://github.com/angular/angular-cli/issues/17264.
  25650. // We should not rename it back until https://github.com/terser/terser/issues/615 is fixed.
  25651. const options = getJitOptions();
  25652. let preserveWhitespaces = metadata.preserveWhitespaces;
  25653. if (preserveWhitespaces === undefined) {
  25654. if (options !== null && options.preserveWhitespaces !== undefined) {
  25655. preserveWhitespaces = options.preserveWhitespaces;
  25656. }
  25657. else {
  25658. preserveWhitespaces = false;
  25659. }
  25660. }
  25661. let encapsulation = metadata.encapsulation;
  25662. if (encapsulation === undefined) {
  25663. if (options !== null && options.defaultEncapsulation !== undefined) {
  25664. encapsulation = options.defaultEncapsulation;
  25665. }
  25666. else {
  25667. encapsulation = ViewEncapsulation$1.Emulated;
  25668. }
  25669. }
  25670. const templateUrl = metadata.templateUrl || `ng:///${type.name}/template.html`;
  25671. const meta = {
  25672. ...directiveMetadata(type, metadata),
  25673. typeSourceSpan: compiler.createParseSourceSpan('Component', type.name, templateUrl),
  25674. template: metadata.template || '',
  25675. preserveWhitespaces,
  25676. styles: metadata.styles || EMPTY_ARRAY,
  25677. animations: metadata.animations,
  25678. // JIT components are always compiled against an empty set of `declarations`. Instead, the
  25679. // `directiveDefs` and `pipeDefs` are updated at a later point:
  25680. // * for NgModule-based components, they're set when the NgModule which declares the
  25681. // component resolves in the module scoping queue
  25682. // * for standalone components, they're set just below, after `compileComponent`.
  25683. declarations: [],
  25684. changeDetection: metadata.changeDetection,
  25685. encapsulation,
  25686. interpolation: metadata.interpolation,
  25687. viewProviders: metadata.viewProviders || null,
  25688. };
  25689. compilationDepth++;
  25690. try {
  25691. if (meta.usesInheritance) {
  25692. addDirectiveDefToUndecoratedParents(type);
  25693. }
  25694. ngComponentDef =
  25695. compiler.compileComponent(angularCoreEnv, templateUrl, meta);
  25696. if (metadata.standalone) {
  25697. // Patch the component definition for standalone components with `directiveDefs` and
  25698. // `pipeDefs` functions which lazily compute the directives/pipes available in the
  25699. // standalone component. Also set `dependencies` to the lazily resolved list of imports.
  25700. const imports = flatten(metadata.imports || EMPTY_ARRAY);
  25701. const { directiveDefs, pipeDefs } = getStandaloneDefFunctions(type, imports);
  25702. ngComponentDef.directiveDefs = directiveDefs;
  25703. ngComponentDef.pipeDefs = pipeDefs;
  25704. ngComponentDef.dependencies = () => imports.map(resolveForwardRef);
  25705. }
  25706. }
  25707. finally {
  25708. // Ensure that the compilation depth is decremented even when the compilation failed.
  25709. compilationDepth--;
  25710. }
  25711. if (compilationDepth === 0) {
  25712. // When NgModule decorator executed, we enqueued the module definition such that
  25713. // it would only dequeue and add itself as module scope to all of its declarations,
  25714. // but only if if all of its declarations had resolved. This call runs the check
  25715. // to see if any modules that are in the queue can be dequeued and add scope to
  25716. // their declarations.
  25717. flushModuleScopingQueueAsMuchAsPossible();
  25718. }
  25719. // If component compilation is async, then the @NgModule annotation which declares the
  25720. // component may execute and set an ngSelectorScope property on the component type. This
  25721. // allows the component to patch itself with directiveDefs from the module after it
  25722. // finishes compiling.
  25723. if (hasSelectorScope(type)) {
  25724. const scopes = transitiveScopesFor(type.ngSelectorScope);
  25725. patchComponentDefWithScope(ngComponentDef, scopes);
  25726. }
  25727. if (metadata.schemas) {
  25728. if (metadata.standalone) {
  25729. ngComponentDef.schemas = metadata.schemas;
  25730. }
  25731. else {
  25732. throw new Error(`The 'schemas' was specified for the ${stringifyForError(type)} but is only valid on a component that is standalone.`);
  25733. }
  25734. }
  25735. else if (metadata.standalone) {
  25736. ngComponentDef.schemas = [];
  25737. }
  25738. }
  25739. return ngComponentDef;
  25740. },
  25741. // Make the property configurable in dev mode to allow overriding in tests
  25742. configurable: !!ngDevMode,
  25743. });
  25744. }
  25745. function getDependencyTypeForError(type) {
  25746. if (getComponentDef(type))
  25747. return 'component';
  25748. if (getDirectiveDef(type))
  25749. return 'directive';
  25750. if (getPipeDef$1(type))
  25751. return 'pipe';
  25752. return 'type';
  25753. }
  25754. function verifyStandaloneImport(depType, importingType) {
  25755. if (isForwardRef(depType)) {
  25756. depType = resolveForwardRef(depType);
  25757. if (!depType) {
  25758. throw new Error(`Expected forwardRef function, imported from "${stringifyForError(importingType)}", to return a standalone entity or NgModule but got "${stringifyForError(depType) || depType}".`);
  25759. }
  25760. }
  25761. if (getNgModuleDef(depType) == null) {
  25762. const def = getComponentDef(depType) || getDirectiveDef(depType) || getPipeDef$1(depType);
  25763. if (def != null) {
  25764. // if a component, directive or pipe is imported make sure that it is standalone
  25765. if (!def.standalone) {
  25766. throw new Error(`The "${stringifyForError(depType)}" ${getDependencyTypeForError(depType)}, imported from "${stringifyForError(importingType)}", is not standalone. Did you forget to add the standalone: true flag?`);
  25767. }
  25768. }
  25769. else {
  25770. // it can be either a module with provider or an unknown (not annotated) type
  25771. if (isModuleWithProviders(depType)) {
  25772. throw new Error(`A module with providers was imported from "${stringifyForError(importingType)}". Modules with providers are not supported in standalone components imports.`);
  25773. }
  25774. else {
  25775. throw new Error(`The "${stringifyForError(depType)}" type, imported from "${stringifyForError(importingType)}", must be a standalone component / directive / pipe or an NgModule. Did you forget to add the required @Component / @Directive / @Pipe or @NgModule annotation?`);
  25776. }
  25777. }
  25778. }
  25779. }
  25780. /**
  25781. * Build memoized `directiveDefs` and `pipeDefs` functions for the component definition of a
  25782. * standalone component, which process `imports` and filter out directives and pipes. The use of
  25783. * memoized functions here allows for the delayed resolution of any `forwardRef`s present in the
  25784. * component's `imports`.
  25785. */
  25786. function getStandaloneDefFunctions(type, imports) {
  25787. let cachedDirectiveDefs = null;
  25788. let cachedPipeDefs = null;
  25789. const directiveDefs = () => {
  25790. if (cachedDirectiveDefs === null) {
  25791. // Standalone components are always able to self-reference, so include the component's own
  25792. // definition in its `directiveDefs`.
  25793. cachedDirectiveDefs = [getComponentDef(type)];
  25794. const seen = new Set([type]);
  25795. for (const rawDep of imports) {
  25796. ngDevMode && verifyStandaloneImport(rawDep, type);
  25797. const dep = resolveForwardRef(rawDep);
  25798. if (seen.has(dep)) {
  25799. continue;
  25800. }
  25801. seen.add(dep);
  25802. if (!!getNgModuleDef(dep)) {
  25803. const scope = transitiveScopesFor(dep);
  25804. for (const dir of scope.exported.directives) {
  25805. const def = getComponentDef(dir) || getDirectiveDef(dir);
  25806. if (def && !seen.has(dir)) {
  25807. seen.add(dir);
  25808. cachedDirectiveDefs.push(def);
  25809. }
  25810. }
  25811. }
  25812. else {
  25813. const def = getComponentDef(dep) || getDirectiveDef(dep);
  25814. if (def) {
  25815. cachedDirectiveDefs.push(def);
  25816. }
  25817. }
  25818. }
  25819. }
  25820. return cachedDirectiveDefs;
  25821. };
  25822. const pipeDefs = () => {
  25823. if (cachedPipeDefs === null) {
  25824. cachedPipeDefs = [];
  25825. const seen = new Set();
  25826. for (const rawDep of imports) {
  25827. const dep = resolveForwardRef(rawDep);
  25828. if (seen.has(dep)) {
  25829. continue;
  25830. }
  25831. seen.add(dep);
  25832. if (!!getNgModuleDef(dep)) {
  25833. const scope = transitiveScopesFor(dep);
  25834. for (const pipe of scope.exported.pipes) {
  25835. const def = getPipeDef$1(pipe);
  25836. if (def && !seen.has(pipe)) {
  25837. seen.add(pipe);
  25838. cachedPipeDefs.push(def);
  25839. }
  25840. }
  25841. }
  25842. else {
  25843. const def = getPipeDef$1(dep);
  25844. if (def) {
  25845. cachedPipeDefs.push(def);
  25846. }
  25847. }
  25848. }
  25849. }
  25850. return cachedPipeDefs;
  25851. };
  25852. return {
  25853. directiveDefs,
  25854. pipeDefs,
  25855. };
  25856. }
  25857. function hasSelectorScope(component) {
  25858. return component.ngSelectorScope !== undefined;
  25859. }
  25860. /**
  25861. * Compile an Angular directive according to its decorator metadata, and patch the resulting
  25862. * directive def onto the component type.
  25863. *
  25864. * In the event that compilation is not immediate, `compileDirective` will return a `Promise` which
  25865. * will resolve when compilation completes and the directive becomes usable.
  25866. */
  25867. function compileDirective(type, directive) {
  25868. let ngDirectiveDef = null;
  25869. addDirectiveFactoryDef(type, directive || {});
  25870. Object.defineProperty(type, NG_DIR_DEF, {
  25871. get: () => {
  25872. if (ngDirectiveDef === null) {
  25873. // `directive` can be null in the case of abstract directives as a base class
  25874. // that use `@Directive()` with no selector. In that case, pass empty object to the
  25875. // `directiveMetadata` function instead of null.
  25876. const meta = getDirectiveMetadata(type, directive || {});
  25877. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'directive', type });
  25878. ngDirectiveDef =
  25879. compiler.compileDirective(angularCoreEnv, meta.sourceMapUrl, meta.metadata);
  25880. }
  25881. return ngDirectiveDef;
  25882. },
  25883. // Make the property configurable in dev mode to allow overriding in tests
  25884. configurable: !!ngDevMode,
  25885. });
  25886. }
  25887. function getDirectiveMetadata(type, metadata) {
  25888. const name = type && type.name;
  25889. const sourceMapUrl = `ng:///${name}/ɵdir.js`;
  25890. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'directive', type });
  25891. const facade = directiveMetadata(type, metadata);
  25892. facade.typeSourceSpan = compiler.createParseSourceSpan('Directive', name, sourceMapUrl);
  25893. if (facade.usesInheritance) {
  25894. addDirectiveDefToUndecoratedParents(type);
  25895. }
  25896. return { metadata: facade, sourceMapUrl };
  25897. }
  25898. function addDirectiveFactoryDef(type, metadata) {
  25899. let ngFactoryDef = null;
  25900. Object.defineProperty(type, NG_FACTORY_DEF, {
  25901. get: () => {
  25902. if (ngFactoryDef === null) {
  25903. const meta = getDirectiveMetadata(type, metadata);
  25904. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'directive', type });
  25905. ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${type.name}/ɵfac.js`, {
  25906. name: meta.metadata.name,
  25907. type: meta.metadata.type,
  25908. typeArgumentCount: 0,
  25909. deps: reflectDependencies(type),
  25910. target: compiler.FactoryTarget.Directive
  25911. });
  25912. }
  25913. return ngFactoryDef;
  25914. },
  25915. // Make the property configurable in dev mode to allow overriding in tests
  25916. configurable: !!ngDevMode,
  25917. });
  25918. }
  25919. function extendsDirectlyFromObject(type) {
  25920. return Object.getPrototypeOf(type.prototype) === Object.prototype;
  25921. }
  25922. /**
  25923. * Extract the `R3DirectiveMetadata` for a particular directive (either a `Directive` or a
  25924. * `Component`).
  25925. */
  25926. function directiveMetadata(type, metadata) {
  25927. // Reflect inputs and outputs.
  25928. const reflect = getReflect();
  25929. const propMetadata = reflect.ownPropMetadata(type);
  25930. return {
  25931. name: type.name,
  25932. type: type,
  25933. selector: metadata.selector !== undefined ? metadata.selector : null,
  25934. host: metadata.host || EMPTY_OBJ,
  25935. propMetadata: propMetadata,
  25936. inputs: metadata.inputs || EMPTY_ARRAY,
  25937. outputs: metadata.outputs || EMPTY_ARRAY,
  25938. queries: extractQueriesMetadata(type, propMetadata, isContentQuery),
  25939. lifecycle: { usesOnChanges: reflect.hasLifecycleHook(type, 'ngOnChanges') },
  25940. typeSourceSpan: null,
  25941. usesInheritance: !extendsDirectlyFromObject(type),
  25942. exportAs: extractExportAs(metadata.exportAs),
  25943. providers: metadata.providers || null,
  25944. viewQueries: extractQueriesMetadata(type, propMetadata, isViewQuery),
  25945. isStandalone: !!metadata.standalone,
  25946. isSignal: !!metadata.signals,
  25947. hostDirectives: metadata.hostDirectives?.map(directive => typeof directive === 'function' ? { directive } : directive) ||
  25948. null
  25949. };
  25950. }
  25951. /**
  25952. * Adds a directive definition to all parent classes of a type that don't have an Angular decorator.
  25953. */
  25954. function addDirectiveDefToUndecoratedParents(type) {
  25955. const objPrototype = Object.prototype;
  25956. let parent = Object.getPrototypeOf(type.prototype).constructor;
  25957. // Go up the prototype until we hit `Object`.
  25958. while (parent && parent !== objPrototype) {
  25959. // Since inheritance works if the class was annotated already, we only need to add
  25960. // the def if there are no annotations and the def hasn't been created already.
  25961. if (!getDirectiveDef(parent) && !getComponentDef(parent) &&
  25962. shouldAddAbstractDirective(parent)) {
  25963. compileDirective(parent, null);
  25964. }
  25965. parent = Object.getPrototypeOf(parent);
  25966. }
  25967. }
  25968. function convertToR3QueryPredicate(selector) {
  25969. return typeof selector === 'string' ? splitByComma(selector) : resolveForwardRef(selector);
  25970. }
  25971. function convertToR3QueryMetadata(propertyName, ann) {
  25972. return {
  25973. propertyName: propertyName,
  25974. predicate: convertToR3QueryPredicate(ann.selector),
  25975. descendants: ann.descendants,
  25976. first: ann.first,
  25977. read: ann.read ? ann.read : null,
  25978. static: !!ann.static,
  25979. emitDistinctChangesOnly: !!ann.emitDistinctChangesOnly,
  25980. };
  25981. }
  25982. function extractQueriesMetadata(type, propMetadata, isQueryAnn) {
  25983. const queriesMeta = [];
  25984. for (const field in propMetadata) {
  25985. if (propMetadata.hasOwnProperty(field)) {
  25986. const annotations = propMetadata[field];
  25987. annotations.forEach(ann => {
  25988. if (isQueryAnn(ann)) {
  25989. if (!ann.selector) {
  25990. throw new Error(`Can't construct a query for the property "${field}" of ` +
  25991. `"${stringifyForError(type)}" since the query selector wasn't defined.`);
  25992. }
  25993. if (annotations.some(isInputAnnotation)) {
  25994. throw new Error(`Cannot combine @Input decorators with query decorators`);
  25995. }
  25996. queriesMeta.push(convertToR3QueryMetadata(field, ann));
  25997. }
  25998. });
  25999. }
  26000. }
  26001. return queriesMeta;
  26002. }
  26003. function extractExportAs(exportAs) {
  26004. return exportAs === undefined ? null : splitByComma(exportAs);
  26005. }
  26006. function isContentQuery(value) {
  26007. const name = value.ngMetadataName;
  26008. return name === 'ContentChild' || name === 'ContentChildren';
  26009. }
  26010. function isViewQuery(value) {
  26011. const name = value.ngMetadataName;
  26012. return name === 'ViewChild' || name === 'ViewChildren';
  26013. }
  26014. function isInputAnnotation(value) {
  26015. return value.ngMetadataName === 'Input';
  26016. }
  26017. function splitByComma(value) {
  26018. return value.split(',').map(piece => piece.trim());
  26019. }
  26020. const LIFECYCLE_HOOKS = [
  26021. 'ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked',
  26022. 'ngAfterContentInit', 'ngAfterContentChecked'
  26023. ];
  26024. function shouldAddAbstractDirective(type) {
  26025. const reflect = getReflect();
  26026. if (LIFECYCLE_HOOKS.some(hookName => reflect.hasLifecycleHook(type, hookName))) {
  26027. return true;
  26028. }
  26029. const propMetadata = reflect.propMetadata(type);
  26030. for (const field in propMetadata) {
  26031. const annotations = propMetadata[field];
  26032. for (let i = 0; i < annotations.length; i++) {
  26033. const current = annotations[i];
  26034. const metadataName = current.ngMetadataName;
  26035. if (isInputAnnotation(current) || isContentQuery(current) || isViewQuery(current) ||
  26036. metadataName === 'Output' || metadataName === 'HostBinding' ||
  26037. metadataName === 'HostListener') {
  26038. return true;
  26039. }
  26040. }
  26041. }
  26042. return false;
  26043. }
  26044. function compilePipe(type, meta) {
  26045. let ngPipeDef = null;
  26046. let ngFactoryDef = null;
  26047. Object.defineProperty(type, NG_FACTORY_DEF, {
  26048. get: () => {
  26049. if (ngFactoryDef === null) {
  26050. const metadata = getPipeMetadata(type, meta);
  26051. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'pipe', type: metadata.type });
  26052. ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${metadata.name}/ɵfac.js`, {
  26053. name: metadata.name,
  26054. type: metadata.type,
  26055. typeArgumentCount: 0,
  26056. deps: reflectDependencies(type),
  26057. target: compiler.FactoryTarget.Pipe
  26058. });
  26059. }
  26060. return ngFactoryDef;
  26061. },
  26062. // Make the property configurable in dev mode to allow overriding in tests
  26063. configurable: !!ngDevMode,
  26064. });
  26065. Object.defineProperty(type, NG_PIPE_DEF, {
  26066. get: () => {
  26067. if (ngPipeDef === null) {
  26068. const metadata = getPipeMetadata(type, meta);
  26069. const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'pipe', type: metadata.type });
  26070. ngPipeDef =
  26071. compiler.compilePipe(angularCoreEnv, `ng:///${metadata.name}/ɵpipe.js`, metadata);
  26072. }
  26073. return ngPipeDef;
  26074. },
  26075. // Make the property configurable in dev mode to allow overriding in tests
  26076. configurable: !!ngDevMode,
  26077. });
  26078. }
  26079. function getPipeMetadata(type, meta) {
  26080. return {
  26081. type: type,
  26082. name: type.name,
  26083. pipeName: meta.name,
  26084. pure: meta.pure !== undefined ? meta.pure : true,
  26085. isStandalone: !!meta.standalone,
  26086. };
  26087. }
  26088. /**
  26089. * Type of the Directive metadata.
  26090. *
  26091. * @publicApi
  26092. */
  26093. const Directive = makeDecorator('Directive', (dir = {}) => dir, undefined, undefined, (type, meta) => compileDirective(type, meta));
  26094. /**
  26095. * Component decorator and metadata.
  26096. *
  26097. * @Annotation
  26098. * @publicApi
  26099. */
  26100. const Component = makeDecorator('Component', (c = {}) => ({ changeDetection: ChangeDetectionStrategy.Default, ...c }), Directive, undefined, (type, meta) => compileComponent(type, meta));
  26101. /**
  26102. * @Annotation
  26103. * @publicApi
  26104. */
  26105. const Pipe = makeDecorator('Pipe', (p) => ({ pure: true, ...p }), undefined, undefined, (type, meta) => compilePipe(type, meta));
  26106. /**
  26107. * @Annotation
  26108. * @publicApi
  26109. */
  26110. const Input = makePropDecorator('Input', (arg) => {
  26111. if (!arg) {
  26112. return {};
  26113. }
  26114. return typeof arg === 'string' ? { alias: arg } : arg;
  26115. });
  26116. /**
  26117. * @Annotation
  26118. * @publicApi
  26119. */
  26120. const Output = makePropDecorator('Output', (alias) => ({ alias }));
  26121. /**
  26122. * @Annotation
  26123. * @publicApi
  26124. */
  26125. const HostBinding = makePropDecorator('HostBinding', (hostPropertyName) => ({ hostPropertyName }));
  26126. /**
  26127. * Decorator that binds a DOM event to a host listener and supplies configuration metadata.
  26128. * Angular invokes the supplied handler method when the host element emits the specified event,
  26129. * and updates the bound element with the result.
  26130. *
  26131. * If the handler method returns false, applies `preventDefault` on the bound element.
  26132. *
  26133. * @usageNotes
  26134. *
  26135. * The following example declares a directive
  26136. * that attaches a click listener to a button and counts clicks.
  26137. *
  26138. * ```ts
  26139. * @Directive({selector: 'button[counting]'})
  26140. * class CountClicks {
  26141. * numberOfClicks = 0;
  26142. *
  26143. * @HostListener('click', ['$event.target'])
  26144. * onClick(btn) {
  26145. * console.log('button', btn, 'number of clicks:', this.numberOfClicks++);
  26146. * }
  26147. * }
  26148. *
  26149. * @Component({
  26150. * selector: 'app',
  26151. * template: '<button counting>Increment</button>',
  26152. * })
  26153. * class App {}
  26154. *
  26155. * ```
  26156. *
  26157. * The following example registers another DOM event handler that listens for `Enter` key-press
  26158. * events on the global `window`.
  26159. * ``` ts
  26160. * import { HostListener, Component } from "@angular/core";
  26161. *
  26162. * @Component({
  26163. * selector: 'app',
  26164. * template: `<h1>Hello, you have pressed enter {{counter}} number of times!</h1> Press enter key
  26165. * to increment the counter.
  26166. * <button (click)="resetCounter()">Reset Counter</button>`
  26167. * })
  26168. * class AppComponent {
  26169. * counter = 0;
  26170. * @HostListener('window:keydown.enter', ['$event'])
  26171. * handleKeyDown(event: KeyboardEvent) {
  26172. * this.counter++;
  26173. * }
  26174. * resetCounter() {
  26175. * this.counter = 0;
  26176. * }
  26177. * }
  26178. * ```
  26179. * The list of valid key names for `keydown` and `keyup` events
  26180. * can be found here:
  26181. * https://www.w3.org/TR/DOM-Level-3-Events-key/#named-key-attribute-values
  26182. *
  26183. * Note that keys can also be combined, e.g. `@HostListener('keydown.shift.a')`.
  26184. *
  26185. * The global target names that can be used to prefix an event name are
  26186. * `document:`, `window:` and `body:`.
  26187. *
  26188. * @Annotation
  26189. * @publicApi
  26190. */
  26191. const HostListener = makePropDecorator('HostListener', (eventName, args) => ({ eventName, args }));
  26192. /**
  26193. * @Annotation
  26194. */
  26195. const NgModule = makeDecorator('NgModule', (ngModule) => ngModule, undefined, undefined,
  26196. /**
  26197. * Decorator that marks the following class as an NgModule, and supplies
  26198. * configuration metadata for it.
  26199. *
  26200. * * The `declarations` option configures the compiler
  26201. * with information about what belongs to the NgModule.
  26202. * * The `providers` options configures the NgModule's injector to provide
  26203. * dependencies the NgModule members.
  26204. * * The `imports` and `exports` options bring in members from other modules, and make
  26205. * this module's members available to others.
  26206. */
  26207. (type, meta) => compileNgModule(type, meta));
  26208. /**
  26209. * This indirection is needed to free up Component, etc symbols in the public API
  26210. * to be used by the decorator versions of these annotations.
  26211. */
  26212. /*
  26213. * This file exists to support compilation of @angular/core in Ivy mode.
  26214. *
  26215. * When the Angular compiler processes a compilation unit, it normally writes imports to
  26216. * @angular/core. When compiling the core package itself this strategy isn't usable. Instead, the
  26217. * compiler writes imports to this file.
  26218. *
  26219. * Only a subset of such imports are supported - core is not allowed to declare components or pipes.
  26220. * A check in ngtsc's `R3SymbolsImportRewriter` validates this condition. The rewriter is only used
  26221. * when compiling @angular/core and is responsible for translating an external name (prefixed with
  26222. * ɵ) to the internal symbol name as exported below.
  26223. *
  26224. * The below symbols are used for @Injectable and @NgModule compilation.
  26225. */
  26226. /**
  26227. * The existence of this constant (in this particular file) informs the Angular compiler that the
  26228. * current program is actually @angular/core, which needs to be compiled specially.
  26229. */
  26230. const ITS_JUST_ANGULAR = true;
  26231. /**
  26232. * A [DI token](guide/glossary#di-token "DI token definition") that you can use to provide
  26233. * one or more initialization functions.
  26234. *
  26235. * The provided functions are injected at application startup and executed during
  26236. * app initialization. If any of these functions returns a Promise or an Observable, initialization
  26237. * does not complete until the Promise is resolved or the Observable is completed.
  26238. *
  26239. * You can, for example, create a factory function that loads language data
  26240. * or an external configuration, and provide that function to the `APP_INITIALIZER` token.
  26241. * The function is executed during the application bootstrap process,
  26242. * and the needed data is available on startup.
  26243. *
  26244. * @see {@link ApplicationInitStatus}
  26245. *
  26246. * @usageNotes
  26247. *
  26248. * The following example illustrates how to configure a multi-provider using `APP_INITIALIZER` token
  26249. * and a function returning a promise.
  26250. * ### Example with NgModule-based application
  26251. * ```
  26252. * function initializeApp(): Promise<any> {
  26253. * return new Promise((resolve, reject) => {
  26254. * // Do some asynchronous stuff
  26255. * resolve();
  26256. * });
  26257. * }
  26258. *
  26259. * @NgModule({
  26260. * imports: [BrowserModule],
  26261. * declarations: [AppComponent],
  26262. * bootstrap: [AppComponent],
  26263. * providers: [{
  26264. * provide: APP_INITIALIZER,
  26265. * useFactory: () => initializeApp,
  26266. * multi: true
  26267. * }]
  26268. * })
  26269. * export class AppModule {}
  26270. * ```
  26271. *
  26272. * ### Example with standalone application
  26273. * ```
  26274. * export function initializeApp(http: HttpClient) {
  26275. * return (): Promise<any> =>
  26276. * firstValueFrom(
  26277. * http
  26278. * .get("https://someUrl.com/api/user")
  26279. * .pipe(tap(user => { ... }))
  26280. * );
  26281. * }
  26282. *
  26283. * bootstrapApplication(App, {
  26284. * providers: [
  26285. * provideHttpClient(),
  26286. * {
  26287. * provide: APP_INITIALIZER,
  26288. * useFactory: initializeApp,
  26289. * multi: true,
  26290. * deps: [HttpClient],
  26291. * },
  26292. * ],
  26293. * });
  26294. * ```
  26295. *
  26296. *
  26297. * It's also possible to configure a multi-provider using `APP_INITIALIZER` token and a function
  26298. * returning an observable, see an example below. Note: the `HttpClient` in this example is used for
  26299. * demo purposes to illustrate how the factory function can work with other providers available
  26300. * through DI.
  26301. *
  26302. * ### Example with NgModule-based application
  26303. * ```
  26304. * function initializeAppFactory(httpClient: HttpClient): () => Observable<any> {
  26305. * return () => httpClient.get("https://someUrl.com/api/user")
  26306. * .pipe(
  26307. * tap(user => { ... })
  26308. * );
  26309. * }
  26310. *
  26311. * @NgModule({
  26312. * imports: [BrowserModule, HttpClientModule],
  26313. * declarations: [AppComponent],
  26314. * bootstrap: [AppComponent],
  26315. * providers: [{
  26316. * provide: APP_INITIALIZER,
  26317. * useFactory: initializeAppFactory,
  26318. * deps: [HttpClient],
  26319. * multi: true
  26320. * }]
  26321. * })
  26322. * export class AppModule {}
  26323. * ```
  26324. *
  26325. * ### Example with standalone application
  26326. *
  26327. * function initializeAppFactory(httpClient: HttpClient): () => Observable<any> {
  26328. * return () => httpClient.get("https://someUrl.com/api/user")
  26329. * .pipe(
  26330. * tap(user => { ... })
  26331. * );
  26332. * }
  26333. *
  26334. * bootstrapApplication(App, {
  26335. * providers: [
  26336. * provideHttpClient(),
  26337. * {
  26338. * provide: APP_INITIALIZER,
  26339. * useFactory: initializeApp,
  26340. * multi: true,
  26341. * deps: [HttpClient],
  26342. * },
  26343. * ],
  26344. * });
  26345. *
  26346. * @publicApi
  26347. */
  26348. const APP_INITIALIZER = new InjectionToken('Application Initializer');
  26349. /**
  26350. * A class that reflects the state of running {@link APP_INITIALIZER} functions.
  26351. *
  26352. * @publicApi
  26353. */
  26354. class ApplicationInitStatus {
  26355. constructor() {
  26356. this.initialized = false;
  26357. this.done = false;
  26358. this.donePromise = new Promise((res, rej) => {
  26359. this.resolve = res;
  26360. this.reject = rej;
  26361. });
  26362. this.appInits = inject(APP_INITIALIZER, { optional: true }) ?? [];
  26363. if ((typeof ngDevMode === 'undefined' || ngDevMode) && !Array.isArray(this.appInits)) {
  26364. throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `APP_INITIALIZER` token value ' +
  26365. `(expected an array, but got ${typeof this.appInits}). ` +
  26366. 'Please check that the `APP_INITIALIZER` token is configured as a ' +
  26367. '`multi: true` provider.');
  26368. }
  26369. }
  26370. /** @internal */
  26371. runInitializers() {
  26372. if (this.initialized) {
  26373. return;
  26374. }
  26375. const asyncInitPromises = [];
  26376. for (const appInits of this.appInits) {
  26377. const initResult = appInits();
  26378. if (isPromise(initResult)) {
  26379. asyncInitPromises.push(initResult);
  26380. }
  26381. else if (isSubscribable(initResult)) {
  26382. const observableAsPromise = new Promise((resolve, reject) => {
  26383. initResult.subscribe({ complete: resolve, error: reject });
  26384. });
  26385. asyncInitPromises.push(observableAsPromise);
  26386. }
  26387. }
  26388. const complete = () => {
  26389. // @ts-expect-error overwriting a readonly
  26390. this.done = true;
  26391. this.resolve();
  26392. };
  26393. Promise.all(asyncInitPromises)
  26394. .then(() => {
  26395. complete();
  26396. })
  26397. .catch(e => {
  26398. this.reject(e);
  26399. });
  26400. if (asyncInitPromises.length === 0) {
  26401. complete();
  26402. }
  26403. this.initialized = true;
  26404. }
  26405. static { this.ɵfac = function ApplicationInitStatus_Factory(t) { return new (t || ApplicationInitStatus)(); }; }
  26406. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: ApplicationInitStatus, factory: ApplicationInitStatus.ɵfac, providedIn: 'root' }); }
  26407. }
  26408. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationInitStatus, [{
  26409. type: Injectable,
  26410. args: [{ providedIn: 'root' }]
  26411. }], function () { return []; }, null); })();
  26412. class Console {
  26413. log(message) {
  26414. // tslint:disable-next-line:no-console
  26415. console.log(message);
  26416. }
  26417. // Note: for reporting errors use `DOM.logError()` as it is platform specific
  26418. warn(message) {
  26419. // tslint:disable-next-line:no-console
  26420. console.warn(message);
  26421. }
  26422. static { this.ɵfac = function Console_Factory(t) { return new (t || Console)(); }; }
  26423. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: Console, factory: Console.ɵfac, providedIn: 'platform' }); }
  26424. }
  26425. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Console, [{
  26426. type: Injectable,
  26427. args: [{ providedIn: 'platform' }]
  26428. }], null, null); })();
  26429. /**
  26430. * Work out the locale from the potential global properties.
  26431. *
  26432. * * Closure Compiler: use `goog.LOCALE`.
  26433. * * Ivy enabled: use `$localize.locale`
  26434. */
  26435. function getGlobalLocale() {
  26436. if (typeof ngI18nClosureMode !== 'undefined' && ngI18nClosureMode &&
  26437. typeof goog !== 'undefined' && goog.LOCALE !== 'en') {
  26438. // * The default `goog.LOCALE` value is `en`, while Angular used `en-US`.
  26439. // * In order to preserve backwards compatibility, we use Angular default value over
  26440. // Closure Compiler's one.
  26441. return goog.LOCALE;
  26442. }
  26443. else {
  26444. // KEEP `typeof $localize !== 'undefined' && $localize.locale` IN SYNC WITH THE LOCALIZE
  26445. // COMPILE-TIME INLINER.
  26446. //
  26447. // * During compile time inlining of translations the expression will be replaced
  26448. // with a string literal that is the current locale. Other forms of this expression are not
  26449. // guaranteed to be replaced.
  26450. //
  26451. // * During runtime translation evaluation, the developer is required to set `$localize.locale`
  26452. // if required, or just to provide their own `LOCALE_ID` provider.
  26453. return (typeof $localize !== 'undefined' && $localize.locale) || DEFAULT_LOCALE_ID;
  26454. }
  26455. }
  26456. /**
  26457. * Provide this token to set the locale of your application.
  26458. * It is used for i18n extraction, by i18n pipes (DatePipe, I18nPluralPipe, CurrencyPipe,
  26459. * DecimalPipe and PercentPipe) and by ICU expressions.
  26460. *
  26461. * See the [i18n guide](guide/i18n-common-locale-id) for more information.
  26462. *
  26463. * @usageNotes
  26464. * ### Example
  26465. *
  26466. * ```typescript
  26467. * import { LOCALE_ID } from '@angular/core';
  26468. * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  26469. * import { AppModule } from './app/app.module';
  26470. *
  26471. * platformBrowserDynamic().bootstrapModule(AppModule, {
  26472. * providers: [{provide: LOCALE_ID, useValue: 'en-US' }]
  26473. * });
  26474. * ```
  26475. *
  26476. * @publicApi
  26477. */
  26478. const LOCALE_ID = new InjectionToken('LocaleId', {
  26479. providedIn: 'root',
  26480. factory: () => inject(LOCALE_ID, InjectFlags.Optional | InjectFlags.SkipSelf) || getGlobalLocale(),
  26481. });
  26482. /**
  26483. * Provide this token to set the default currency code your application uses for
  26484. * CurrencyPipe when there is no currency code passed into it. This is only used by
  26485. * CurrencyPipe and has no relation to locale currency. Defaults to USD if not configured.
  26486. *
  26487. * See the [i18n guide](guide/i18n-common-locale-id) for more information.
  26488. *
  26489. * <div class="alert is-helpful">
  26490. *
  26491. * **Deprecation notice:**
  26492. *
  26493. * The default currency code is currently always `USD` but this is deprecated from v9.
  26494. *
  26495. * **In v10 the default currency code will be taken from the current locale.**
  26496. *
  26497. * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in
  26498. * your application `NgModule`:
  26499. *
  26500. * ```ts
  26501. * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}
  26502. * ```
  26503. *
  26504. * </div>
  26505. *
  26506. * @usageNotes
  26507. * ### Example
  26508. *
  26509. * ```typescript
  26510. * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  26511. * import { AppModule } from './app/app.module';
  26512. *
  26513. * platformBrowserDynamic().bootstrapModule(AppModule, {
  26514. * providers: [{provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR' }]
  26515. * });
  26516. * ```
  26517. *
  26518. * @publicApi
  26519. */
  26520. const DEFAULT_CURRENCY_CODE = new InjectionToken('DefaultCurrencyCode', {
  26521. providedIn: 'root',
  26522. factory: () => USD_CURRENCY_CODE,
  26523. });
  26524. /**
  26525. * Use this token at bootstrap to provide the content of your translation file (`xtb`,
  26526. * `xlf` or `xlf2`) when you want to translate your application in another language.
  26527. *
  26528. * See the [i18n guide](guide/i18n-common-merge) for more information.
  26529. *
  26530. * @usageNotes
  26531. * ### Example
  26532. *
  26533. * ```typescript
  26534. * import { TRANSLATIONS } from '@angular/core';
  26535. * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  26536. * import { AppModule } from './app/app.module';
  26537. *
  26538. * // content of your translation file
  26539. * const translations = '....';
  26540. *
  26541. * platformBrowserDynamic().bootstrapModule(AppModule, {
  26542. * providers: [{provide: TRANSLATIONS, useValue: translations }]
  26543. * });
  26544. * ```
  26545. *
  26546. * @publicApi
  26547. */
  26548. const TRANSLATIONS = new InjectionToken('Translations');
  26549. /**
  26550. * Provide this token at bootstrap to set the format of your {@link TRANSLATIONS}: `xtb`,
  26551. * `xlf` or `xlf2`.
  26552. *
  26553. * See the [i18n guide](guide/i18n-common-merge) for more information.
  26554. *
  26555. * @usageNotes
  26556. * ### Example
  26557. *
  26558. * ```typescript
  26559. * import { TRANSLATIONS_FORMAT } from '@angular/core';
  26560. * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  26561. * import { AppModule } from './app/app.module';
  26562. *
  26563. * platformBrowserDynamic().bootstrapModule(AppModule, {
  26564. * providers: [{provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }]
  26565. * });
  26566. * ```
  26567. *
  26568. * @publicApi
  26569. */
  26570. const TRANSLATIONS_FORMAT = new InjectionToken('TranslationsFormat');
  26571. /**
  26572. * Use this enum at bootstrap as an option of `bootstrapModule` to define the strategy
  26573. * that the compiler should use in case of missing translations:
  26574. * - Error: throw if you have missing translations.
  26575. * - Warning (default): show a warning in the console and/or shell.
  26576. * - Ignore: do nothing.
  26577. *
  26578. * See the [i18n guide](guide/i18n-common-merge#report-missing-translations) for more information.
  26579. *
  26580. * @usageNotes
  26581. * ### Example
  26582. * ```typescript
  26583. * import { MissingTranslationStrategy } from '@angular/core';
  26584. * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
  26585. * import { AppModule } from './app/app.module';
  26586. *
  26587. * platformBrowserDynamic().bootstrapModule(AppModule, {
  26588. * missingTranslation: MissingTranslationStrategy.Error
  26589. * });
  26590. * ```
  26591. *
  26592. * @publicApi
  26593. */
  26594. var MissingTranslationStrategy;
  26595. (function (MissingTranslationStrategy) {
  26596. MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
  26597. MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
  26598. MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
  26599. })(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
  26600. /**
  26601. * *Internal* service that keeps track of pending tasks happening in the system
  26602. * during the initial rendering. No tasks are tracked after an initial
  26603. * rendering.
  26604. *
  26605. * This information is needed to make sure that the serialization on the server
  26606. * is delayed until all tasks in the queue (such as an initial navigation or a
  26607. * pending HTTP request) are completed.
  26608. */
  26609. class InitialRenderPendingTasks {
  26610. constructor() {
  26611. this.taskId = 0;
  26612. this.pendingTasks = new Set();
  26613. this.hasPendingTasks = new BehaviorSubject(false);
  26614. }
  26615. add() {
  26616. this.hasPendingTasks.next(true);
  26617. const taskId = this.taskId++;
  26618. this.pendingTasks.add(taskId);
  26619. return taskId;
  26620. }
  26621. remove(taskId) {
  26622. this.pendingTasks.delete(taskId);
  26623. if (this.pendingTasks.size === 0) {
  26624. this.hasPendingTasks.next(false);
  26625. }
  26626. }
  26627. ngOnDestroy() {
  26628. this.pendingTasks.clear();
  26629. this.hasPendingTasks.next(false);
  26630. }
  26631. static { this.ɵfac = function InitialRenderPendingTasks_Factory(t) { return new (t || InitialRenderPendingTasks)(); }; }
  26632. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: InitialRenderPendingTasks, factory: InitialRenderPendingTasks.ɵfac, providedIn: 'root' }); }
  26633. }
  26634. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(InitialRenderPendingTasks, [{
  26635. type: Injectable,
  26636. args: [{ providedIn: 'root' }]
  26637. }], null, null); })();
  26638. /**
  26639. * Combination of NgModuleFactory and ComponentFactories.
  26640. *
  26641. * @publicApi
  26642. *
  26643. * @deprecated
  26644. * Ivy JIT mode doesn't require accessing this symbol.
  26645. * See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes) for
  26646. * additional context.
  26647. */
  26648. class ModuleWithComponentFactories {
  26649. constructor(ngModuleFactory, componentFactories) {
  26650. this.ngModuleFactory = ngModuleFactory;
  26651. this.componentFactories = componentFactories;
  26652. }
  26653. }
  26654. /**
  26655. * Low-level service for running the angular compiler during runtime
  26656. * to create {@link ComponentFactory}s, which
  26657. * can later be used to create and render a Component instance.
  26658. *
  26659. * Each `@NgModule` provides an own `Compiler` to its injector,
  26660. * that will use the directives/pipes of the ng module for compilation
  26661. * of components.
  26662. *
  26663. * @publicApi
  26664. *
  26665. * @deprecated
  26666. * Ivy JIT mode doesn't require accessing this symbol.
  26667. * See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes) for
  26668. * additional context.
  26669. */
  26670. class Compiler {
  26671. /**
  26672. * Compiles the given NgModule and all of its components. All templates of the components
  26673. * have to be inlined.
  26674. */
  26675. compileModuleSync(moduleType) {
  26676. return new NgModuleFactory(moduleType);
  26677. }
  26678. /**
  26679. * Compiles the given NgModule and all of its components
  26680. */
  26681. compileModuleAsync(moduleType) {
  26682. return Promise.resolve(this.compileModuleSync(moduleType));
  26683. }
  26684. /**
  26685. * Same as {@link #compileModuleSync} but also creates ComponentFactories for all components.
  26686. */
  26687. compileModuleAndAllComponentsSync(moduleType) {
  26688. const ngModuleFactory = this.compileModuleSync(moduleType);
  26689. const moduleDef = getNgModuleDef(moduleType);
  26690. const componentFactories = maybeUnwrapFn(moduleDef.declarations)
  26691. .reduce((factories, declaration) => {
  26692. const componentDef = getComponentDef(declaration);
  26693. componentDef && factories.push(new ComponentFactory(componentDef));
  26694. return factories;
  26695. }, []);
  26696. return new ModuleWithComponentFactories(ngModuleFactory, componentFactories);
  26697. }
  26698. /**
  26699. * Same as {@link #compileModuleAsync} but also creates ComponentFactories for all components.
  26700. */
  26701. compileModuleAndAllComponentsAsync(moduleType) {
  26702. return Promise.resolve(this.compileModuleAndAllComponentsSync(moduleType));
  26703. }
  26704. /**
  26705. * Clears all caches.
  26706. */
  26707. clearCache() { }
  26708. /**
  26709. * Clears the cache for the given component/ngModule.
  26710. */
  26711. clearCacheFor(type) { }
  26712. /**
  26713. * Returns the id for a given NgModule, if one is defined and known to the compiler.
  26714. */
  26715. getModuleId(moduleType) {
  26716. return undefined;
  26717. }
  26718. static { this.ɵfac = function Compiler_Factory(t) { return new (t || Compiler)(); }; }
  26719. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: Compiler, factory: Compiler.ɵfac, providedIn: 'root' }); }
  26720. }
  26721. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Compiler, [{
  26722. type: Injectable,
  26723. args: [{ providedIn: 'root' }]
  26724. }], null, null); })();
  26725. /**
  26726. * Token to provide CompilerOptions in the platform injector.
  26727. *
  26728. * @publicApi
  26729. */
  26730. const COMPILER_OPTIONS = new InjectionToken('compilerOptions');
  26731. /**
  26732. * A factory for creating a Compiler
  26733. *
  26734. * @publicApi
  26735. *
  26736. * @deprecated
  26737. * Ivy JIT mode doesn't require accessing this symbol.
  26738. * See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes) for
  26739. * additional context.
  26740. */
  26741. class CompilerFactory {
  26742. }
  26743. /**
  26744. * These are the data structures that our framework injector profiler will fill with data in order
  26745. * to support DI debugging APIs.
  26746. *
  26747. * resolverToTokenToDependencies: Maps an injector to a Map of tokens to an Array of
  26748. * dependencies. Injector -> Token -> Dependencies This is used to support the
  26749. * getDependenciesFromInjectable API, which takes in an injector and a token and returns it's
  26750. * dependencies.
  26751. *
  26752. * resolverToProviders: Maps a DI resolver (an Injector or an LView) to the providers configured
  26753. * within it This is used to support the getInjectorProviders API, which takes in an injector and
  26754. * returns the providers that it was configured with.
  26755. *
  26756. * standaloneInjectorToComponent: Maps the injector of a standalone component to the standalone
  26757. * component that it is associated with. Used in the getInjectorProviders API, specificially in the
  26758. * discovery of import paths for each provider. This is necessary because the imports array of a
  26759. * standalone component is processed and configured in its standalone injector, but exists within
  26760. * the component's definition. Because getInjectorProviders takes in an injector, if that injector
  26761. * is the injector of a standalone component, we need to be able to discover the place where the
  26762. * imports array is located (the component) in order to flatten the imports array within it to
  26763. * discover all of it's providers.
  26764. *
  26765. *
  26766. * All of these data structures are instantiated with WeakMaps. This will ensure that the presence
  26767. * of any object in the keys of these maps does not prevent the garbage collector from collecting
  26768. * those objects. Because of this property of WeakMaps, these data structures will never be the
  26769. * source of a memory leak.
  26770. *
  26771. * An example of this advantage: When components are destroyed, we don't need to do
  26772. * any additional work to remove that component from our mappings.
  26773. *
  26774. */
  26775. class DIDebugData {
  26776. constructor() {
  26777. this.resolverToTokenToDependencies = new WeakMap();
  26778. this.resolverToProviders = new WeakMap();
  26779. this.standaloneInjectorToComponent = new WeakMap();
  26780. }
  26781. reset() {
  26782. this.resolverToTokenToDependencies =
  26783. new WeakMap();
  26784. this.resolverToProviders = new WeakMap();
  26785. this.standaloneInjectorToComponent = new WeakMap();
  26786. }
  26787. }
  26788. let frameworkDIDebugData = new DIDebugData();
  26789. function getFrameworkDIDebugData() {
  26790. return frameworkDIDebugData;
  26791. }
  26792. /**
  26793. * Initalize default handling of injector events. This handling parses events
  26794. * as they are emitted and constructs the data structures necessary to support
  26795. * some of debug APIs.
  26796. *
  26797. * See handleInjectEvent, handleCreateEvent and handleProviderConfiguredEvent
  26798. * for descriptions of each handler
  26799. *
  26800. * Supported APIs:
  26801. * - getDependenciesFromInjectable
  26802. * - getInjectorProviders
  26803. */
  26804. function setupFrameworkInjectorProfiler() {
  26805. frameworkDIDebugData.reset();
  26806. setInjectorProfiler((injectorProfilerEvent) => handleInjectorProfilerEvent(injectorProfilerEvent));
  26807. }
  26808. function handleInjectorProfilerEvent(injectorProfilerEvent) {
  26809. const { context, type } = injectorProfilerEvent;
  26810. if (type === 0 /* InjectorProfilerEventType.Inject */) {
  26811. handleInjectEvent(context, injectorProfilerEvent.service);
  26812. }
  26813. else if (type === 1 /* InjectorProfilerEventType.InstanceCreatedByInjector */) {
  26814. handleInstanceCreatedByInjectorEvent(context, injectorProfilerEvent.instance);
  26815. }
  26816. else if (type === 2 /* InjectorProfilerEventType.ProviderConfigured */) {
  26817. handleProviderConfiguredEvent(context, injectorProfilerEvent.providerRecord);
  26818. }
  26819. }
  26820. /**
  26821. *
  26822. * Stores the injected service in frameworkDIDebugData.resolverToTokenToDependencies
  26823. * based on it's injector and token.
  26824. *
  26825. * @param context InjectorProfilerContext the injection context that this event occurred in.
  26826. * @param data InjectedService the service associated with this inject event.
  26827. *
  26828. */
  26829. function handleInjectEvent(context, data) {
  26830. const diResolver = getDIResolver(context.injector);
  26831. if (diResolver === null) {
  26832. throwError('An Inject event must be run within an injection context.');
  26833. }
  26834. const diResolverToInstantiatedToken = frameworkDIDebugData.resolverToTokenToDependencies;
  26835. if (!diResolverToInstantiatedToken.has(diResolver)) {
  26836. diResolverToInstantiatedToken.set(diResolver, new WeakMap());
  26837. }
  26838. // if token is a primitive type, ignore this event. We do this because we cannot keep track of
  26839. // non-primitive tokens in WeakMaps since they are not garbage collectable.
  26840. if (!canBeHeldWeakly(context.token)) {
  26841. return;
  26842. }
  26843. const instantiatedTokenToDependencies = diResolverToInstantiatedToken.get(diResolver);
  26844. if (!instantiatedTokenToDependencies.has(context.token)) {
  26845. instantiatedTokenToDependencies.set(context.token, []);
  26846. }
  26847. const { token, value, flags } = data;
  26848. instantiatedTokenToDependencies.get(context.token).push({ token, value, flags });
  26849. }
  26850. /**
  26851. *
  26852. * If the created instance is an instance of a standalone component, maps the injector to that
  26853. * standalone component in frameworkDIDebugData.standaloneInjectorToComponent
  26854. *
  26855. * @param context InjectorProfilerContext the injection context that this event occurred in.
  26856. * @param data InjectorCreatedInstance an object containing the instance that was just created
  26857. *
  26858. */
  26859. function handleInstanceCreatedByInjectorEvent(context, data) {
  26860. const { value } = data;
  26861. if (getDIResolver(context.injector) === null) {
  26862. throwError('An InjectorCreatedInstance event must be run within an injection context.');
  26863. }
  26864. // if our value is an instance of a standalone component, map the injector of that standalone
  26865. // component to the component class. Otherwise, this event is a noop.
  26866. let standaloneComponent = undefined;
  26867. if (typeof value === 'object') {
  26868. standaloneComponent = value?.constructor;
  26869. }
  26870. if (standaloneComponent === undefined || !isStandaloneComponent(standaloneComponent)) {
  26871. return;
  26872. }
  26873. const environmentInjector = context.injector.get(EnvironmentInjector, null, { optional: true });
  26874. // Standalone components should have an environment injector. If one cannot be
  26875. // found we may be in a test case for low level functionality that did not explictly
  26876. // setup this injector. In those cases, we simply ignore this event.
  26877. if (environmentInjector === null) {
  26878. return;
  26879. }
  26880. const { standaloneInjectorToComponent } = frameworkDIDebugData;
  26881. // If our injector has already been mapped, as is the case
  26882. // when a standalone component imports another standalone component,
  26883. // we consider the original component (the component doing the importing)
  26884. // as the component connected to our injector.
  26885. if (standaloneInjectorToComponent.has(environmentInjector)) {
  26886. return;
  26887. }
  26888. // If our injector hasn't been mapped, then we map it to the standalone component
  26889. standaloneInjectorToComponent.set(environmentInjector, standaloneComponent);
  26890. }
  26891. function isStandaloneComponent(value) {
  26892. const def = getComponentDef(value);
  26893. return !!def?.standalone;
  26894. }
  26895. /**
  26896. *
  26897. * Stores the emitted ProviderRecords from the InjectorProfilerEventType.ProviderConfigured
  26898. * event in frameworkDIDebugData.resolverToProviders
  26899. *
  26900. * @param context InjectorProfilerContext the injection context that this event occurred in.
  26901. * @param data ProviderRecord an object containing the instance that was just created
  26902. *
  26903. */
  26904. function handleProviderConfiguredEvent(context, data) {
  26905. const { resolverToProviders } = frameworkDIDebugData;
  26906. const diResolver = getDIResolver(context?.injector);
  26907. if (diResolver === null) {
  26908. throwError('A ProviderConfigured event must be run within an injection context.');
  26909. }
  26910. if (!resolverToProviders.has(diResolver)) {
  26911. resolverToProviders.set(diResolver, []);
  26912. }
  26913. resolverToProviders.get(diResolver).push(data);
  26914. }
  26915. function getDIResolver(injector) {
  26916. let diResolver = null;
  26917. if (injector === undefined) {
  26918. return diResolver;
  26919. }
  26920. // We use the LView as the diResolver for NodeInjectors because they
  26921. // do not persist anywhere in the framework. They are simply wrappers around an LView and a TNode
  26922. // that do persist. Because of this, we rely on the LView of the NodeInjector in order to use
  26923. // as a concrete key to represent this injector. If we get the same LView back later, we know
  26924. // we're looking at the same injector.
  26925. if (injector instanceof NodeInjector) {
  26926. diResolver = getNodeInjectorLView(injector);
  26927. }
  26928. // Other injectors can be used a keys for a map because their instances
  26929. // persist
  26930. else {
  26931. diResolver = injector;
  26932. }
  26933. return diResolver;
  26934. }
  26935. // inspired by
  26936. // https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-canbeheldweakly
  26937. function canBeHeldWeakly(value) {
  26938. // we check for value !== null here because typeof null === 'object
  26939. return value !== null &&
  26940. (typeof value === 'object' || typeof value === 'function' || typeof value === 'symbol');
  26941. }
  26942. /**
  26943. * Marks a component for check (in case of OnPush components) and synchronously
  26944. * performs change detection on the application this component belongs to.
  26945. *
  26946. * @param component Component to {@link ChangeDetectorRef#markForCheck mark for check}.
  26947. *
  26948. * @publicApi
  26949. * @globalApi ng
  26950. */
  26951. function applyChanges(component) {
  26952. ngDevMode && assertDefined(component, 'component');
  26953. markViewDirty(getComponentViewByInstance(component));
  26954. getRootComponents(component).forEach(rootComponent => detectChanges(rootComponent));
  26955. }
  26956. /**
  26957. * Discovers the dependencies of an injectable instance. Provides DI information about each
  26958. * dependency that the injectable was instantiated with, including where they were provided from.
  26959. *
  26960. * @param injector An injector instance
  26961. * @param token a DI token that was constructed by the given injector instance
  26962. * @returns an object that contains the created instance of token as well as all of the dependencies
  26963. * that it was instantiated with OR undefined if the token was not created within the given
  26964. * injector.
  26965. */
  26966. function getDependenciesFromInjectable(injector, token) {
  26967. // First we check to see if the token given maps to an actual instance in the injector given.
  26968. // We use `self: true` because we only want to look at the injector we were given.
  26969. // We use `optional: true` because it's possible that the token we were given was never
  26970. // constructed by the injector we were given.
  26971. const instance = injector.get(token, null, { self: true, optional: true });
  26972. if (instance === null) {
  26973. throw new Error(`Unable to determine instance of ${token} in given injector`);
  26974. }
  26975. let diResolver = injector;
  26976. if (injector instanceof NodeInjector) {
  26977. diResolver = getNodeInjectorLView(injector);
  26978. }
  26979. const { resolverToTokenToDependencies } = getFrameworkDIDebugData();
  26980. let dependencies = resolverToTokenToDependencies.get(diResolver)?.get?.(token) ?? [];
  26981. const resolutionPath = getInjectorResolutionPath(injector);
  26982. dependencies = dependencies.map(dep => {
  26983. const flags = dep.flags;
  26984. dep.flags = {
  26985. optional: (8 /* InternalInjectFlags.Optional */ & flags) === 8 /* InternalInjectFlags.Optional */,
  26986. host: (1 /* InternalInjectFlags.Host */ & flags) === 1 /* InternalInjectFlags.Host */,
  26987. self: (2 /* InternalInjectFlags.Self */ & flags) === 2 /* InternalInjectFlags.Self */,
  26988. skipSelf: (4 /* InternalInjectFlags.SkipSelf */ & flags) === 4 /* InternalInjectFlags.SkipSelf */,
  26989. };
  26990. for (let i = 0; i < resolutionPath.length; i++) {
  26991. const injectorToCheck = resolutionPath[i];
  26992. // if skipSelf is true we skip the first injector
  26993. if (i === 0 && dep.flags.skipSelf) {
  26994. continue;
  26995. }
  26996. // host only applies to NodeInjectors
  26997. if (dep.flags.host && injectorToCheck instanceof EnvironmentInjector) {
  26998. break;
  26999. }
  27000. const instance = injectorToCheck.get(dep.token, null, { self: true, optional: true });
  27001. if (instance !== null) {
  27002. // if host flag is true we double check that we can get the service from the first element
  27003. // in the resolution path by using the host flag. This is done to make sure that we've found
  27004. // the correct providing injector, and not a node injector that is connected to our path via
  27005. // a router outlet.
  27006. if (dep.flags.host) {
  27007. const firstInjector = resolutionPath[0];
  27008. const lookupFromFirstInjector = firstInjector.get(dep.token, null, { ...dep.flags, optional: true });
  27009. if (lookupFromFirstInjector !== null) {
  27010. dep.providedIn = injectorToCheck;
  27011. }
  27012. break;
  27013. }
  27014. dep.providedIn = injectorToCheck;
  27015. break;
  27016. }
  27017. // if self is true we stop after the first injector
  27018. if (i === 0 && dep.flags.self) {
  27019. break;
  27020. }
  27021. }
  27022. return dep;
  27023. });
  27024. return { instance, dependencies };
  27025. }
  27026. /**
  27027. * Gets the class associated with an injector that contains a provider `imports` array in it's
  27028. * definition
  27029. *
  27030. * For Module Injectors this returns the NgModule constructor.
  27031. *
  27032. * For Standalone injectors this returns the standalone component constructor.
  27033. *
  27034. * @param injector Injector an injector instance
  27035. * @returns the constructor where the `imports` array that configures this injector is located
  27036. */
  27037. function getProviderImportsContainer(injector) {
  27038. const { standaloneInjectorToComponent } = getFrameworkDIDebugData();
  27039. // standalone components configure providers through a component def, so we have to
  27040. // use the standalone component associated with this injector if Injector represents
  27041. // a standalone components EnvironmentInjector
  27042. if (standaloneInjectorToComponent.has(injector)) {
  27043. return standaloneInjectorToComponent.get(injector);
  27044. }
  27045. // Module injectors configure providers through their NgModule def, so we use the
  27046. // injector to lookup its NgModuleRef and through that grab its instance
  27047. const defTypeRef = injector.get(NgModuleRef$1, null, { self: true, optional: true });
  27048. // If we can't find an associated imports container, return null.
  27049. // This could be the case if this function is called with an R3Injector that does not represent
  27050. // a standalone component or NgModule.
  27051. if (defTypeRef === null) {
  27052. return null;
  27053. }
  27054. return defTypeRef.instance.constructor;
  27055. }
  27056. /**
  27057. * Gets the providers configured on a NodeInjector
  27058. *
  27059. * @param injector A NodeInjector instance
  27060. * @returns ProviderRecord[] an array of objects representing the providers configured on this
  27061. * injector
  27062. */
  27063. function getNodeInjectorProviders(injector) {
  27064. const diResolver = getNodeInjectorLView(injector);
  27065. const { resolverToProviders } = getFrameworkDIDebugData();
  27066. return resolverToProviders.get(diResolver) ?? [];
  27067. }
  27068. /**
  27069. * Gets a mapping of providers configured on an injector to their import paths
  27070. *
  27071. * ModuleA -> imports ModuleB
  27072. * ModuleB -> imports ModuleC
  27073. * ModuleB -> provides MyServiceA
  27074. * ModuleC -> provides MyServiceB
  27075. *
  27076. * getProviderImportPaths(ModuleA)
  27077. * > Map(2) {
  27078. * MyServiceA => [ModuleA, ModuleB]
  27079. * MyServiceB => [ModuleA, ModuleB, ModuleC]
  27080. * }
  27081. *
  27082. * @param providerImportsContainer constructor of class that contains an `imports` array in it's
  27083. * definition
  27084. * @returns A Map object that maps providers to an array of constructors representing it's import
  27085. * path
  27086. *
  27087. */
  27088. function getProviderImportPaths(providerImportsContainer) {
  27089. const providerToPath = new Map();
  27090. const visitedContainers = new Set();
  27091. const visitor = walkProviderTreeToDiscoverImportPaths(providerToPath, visitedContainers);
  27092. walkProviderTree(providerImportsContainer, visitor, [], new Set());
  27093. return providerToPath;
  27094. }
  27095. /**
  27096. *
  27097. * Higher order function that returns a visitor for WalkProviderTree
  27098. *
  27099. * Takes in a Map and Set to keep track of the providers and containers
  27100. * visited, so that we can discover the import paths of these providers
  27101. * during the traversal.
  27102. *
  27103. * This visitor takes advantage of the fact that walkProviderTree performs a
  27104. * postorder traversal of the provider tree for the passed in container. Because postorder
  27105. * traversal recursively processes subtrees from leaf nodes until the traversal reaches the root,
  27106. * we write a visitor that constructs provider import paths in reverse.
  27107. *
  27108. *
  27109. * We use the visitedContainers set defined outside this visitor
  27110. * because we want to run some logic only once for
  27111. * each container in the tree. That logic can be described as:
  27112. *
  27113. *
  27114. * 1. for each discovered_provider and discovered_path in the incomplete provider paths we've
  27115. * already discovered
  27116. * 2. get the first container in discovered_path
  27117. * 3. if that first container is in the imports array of the container we're visiting
  27118. * Then the container we're visiting is also in the import path of discovered_provider, so we
  27119. * unshift discovered_path with the container we're currently visiting
  27120. *
  27121. *
  27122. * Example Run:
  27123. * ```
  27124. * ┌──────────┐
  27125. * │containerA│
  27126. * ┌─imports-─┤ ├──imports─┐
  27127. * │ │ provA │ │
  27128. * │ │ provB │ │
  27129. * │ └──────────┘ │
  27130. * │ │
  27131. * ┌▼─────────┐ ┌────────▼─┐
  27132. * │containerB│ │containerC│
  27133. * │ │ │ │
  27134. * │ provD │ │ provF │
  27135. * │ provE │ │ provG │
  27136. * └──────────┘ └──────────┘
  27137. * ```
  27138. *
  27139. * Each step of the traversal,
  27140. *
  27141. * ```
  27142. * visitor(provD, containerB)
  27143. * providerToPath === Map { provD => [containerB] }
  27144. * visitedContainers === Set { containerB }
  27145. *
  27146. * visitor(provE, containerB)
  27147. * providerToPath === Map { provD => [containerB], provE => [containerB] }
  27148. * visitedContainers === Set { containerB }
  27149. *
  27150. * visitor(provF, containerC)
  27151. * providerToPath === Map { provD => [containerB], provE => [containerB], provF => [containerC] }
  27152. * visitedContainers === Set { containerB, containerC }
  27153. *
  27154. * visitor(provG, containerC)
  27155. * providerToPath === Map {
  27156. * provD => [containerB], provE => [containerB], provF => [containerC], provG => [containerC]
  27157. * }
  27158. * visitedContainers === Set { containerB, containerC }
  27159. *
  27160. * visitor(provA, containerA)
  27161. * providerToPath === Map {
  27162. * provD => [containerA, containerB],
  27163. * provE => [containerA, containerB],
  27164. * provF => [containerA, containerC],
  27165. * provG => [containerA, containerC],
  27166. * provA => [containerA]
  27167. * }
  27168. * visitedContainers === Set { containerB, containerC, containerA }
  27169. *
  27170. * visitor(provB, containerA)
  27171. * providerToPath === Map {
  27172. * provD => [containerA, containerB],
  27173. * provE => [containerA, containerB],
  27174. * provF => [containerA, containerC],
  27175. * provG => [containerA, containerC],
  27176. * provA => [containerA]
  27177. * provB => [containerA]
  27178. * }
  27179. * visitedContainers === Set { containerB, containerC, containerA }
  27180. * ```
  27181. *
  27182. * @param providerToPath Map map of providers to paths that this function fills
  27183. * @param visitedContainers Set a set to keep track of the containers we've already visited
  27184. * @return function(provider SingleProvider, container: Type<unknown> | InjectorType<unknown>) =>
  27185. * void
  27186. */
  27187. function walkProviderTreeToDiscoverImportPaths(providerToPath, visitedContainers) {
  27188. return (provider, container) => {
  27189. // If the provider is not already in the providerToPath map,
  27190. // add an entry with the provider as the key and an array containing the current container as
  27191. // the value
  27192. if (!providerToPath.has(provider)) {
  27193. providerToPath.set(provider, [container]);
  27194. }
  27195. // This block will run exactly once for each container in the import tree.
  27196. // This is where we run the logic to check the imports array of the current
  27197. // container to see if it's the next container in the path for our currently
  27198. // discovered providers.
  27199. if (!visitedContainers.has(container)) {
  27200. // Iterate through the providers we've already seen
  27201. for (const prov of providerToPath.keys()) {
  27202. const existingImportPath = providerToPath.get(prov);
  27203. let containerDef = getInjectorDef(container);
  27204. if (!containerDef) {
  27205. const ngModule = container.ngModule;
  27206. containerDef = getInjectorDef(ngModule);
  27207. }
  27208. if (!containerDef) {
  27209. return;
  27210. }
  27211. const lastContainerAddedToPath = existingImportPath[0];
  27212. let isNextStepInPath = false;
  27213. deepForEach(containerDef.imports, (moduleImport) => {
  27214. if (isNextStepInPath) {
  27215. return;
  27216. }
  27217. isNextStepInPath = moduleImport.ngModule === lastContainerAddedToPath ||
  27218. moduleImport === lastContainerAddedToPath;
  27219. if (isNextStepInPath) {
  27220. providerToPath.get(prov)?.unshift(container);
  27221. }
  27222. });
  27223. }
  27224. }
  27225. visitedContainers.add(container);
  27226. };
  27227. }
  27228. /**
  27229. * Gets the providers configured on an EnvironmentInjector
  27230. *
  27231. * @param injector EnvironmentInjector
  27232. * @returns an array of objects representing the providers of the given injector
  27233. */
  27234. function getEnvironmentInjectorProviders(injector) {
  27235. const providerImportsContainer = getProviderImportsContainer(injector);
  27236. if (providerImportsContainer === null) {
  27237. throwError('Could not determine where injector providers were configured.');
  27238. }
  27239. const providerToPath = getProviderImportPaths(providerImportsContainer);
  27240. const providerRecords = getFrameworkDIDebugData().resolverToProviders.get(injector) ?? [];
  27241. return providerRecords.map(providerRecord => {
  27242. let importPath = providerToPath.get(providerRecord.provider) ?? [providerImportsContainer];
  27243. const def = getComponentDef(providerImportsContainer);
  27244. const isStandaloneComponent = !!def?.standalone;
  27245. // We prepend the component constructor in the standalone case
  27246. // because walkProviderTree does not visit this constructor during it's traversal
  27247. if (isStandaloneComponent) {
  27248. importPath = [providerImportsContainer, ...providerToPath.get(providerRecord.provider) ?? []];
  27249. }
  27250. return { ...providerRecord, importPath };
  27251. });
  27252. }
  27253. /**
  27254. * Gets the providers configured on an injector.
  27255. *
  27256. * @param injector the injector to lookup the providers of
  27257. * @returns ProviderRecord[] an array of objects representing the providers of the given injector
  27258. */
  27259. function getInjectorProviders(injector) {
  27260. if (injector instanceof NodeInjector) {
  27261. return getNodeInjectorProviders(injector);
  27262. }
  27263. else if (injector instanceof EnvironmentInjector) {
  27264. return getEnvironmentInjectorProviders(injector);
  27265. }
  27266. throwError('getInjectorProviders only supports NodeInjector and EnvironmentInjector');
  27267. }
  27268. function getInjectorResolutionPath(injector) {
  27269. const resolutionPath = [injector];
  27270. getInjectorResolutionPathHelper(injector, resolutionPath);
  27271. return resolutionPath;
  27272. }
  27273. function getInjectorResolutionPathHelper(injector, resolutionPath) {
  27274. const parent = getInjectorParent(injector);
  27275. // if getInjectorParent can't find a parent, then we've either reached the end
  27276. // of the path, or we need to move from the Element Injector tree to the
  27277. // module injector tree using the first injector in our path as the connection point.
  27278. if (parent === null) {
  27279. if (injector instanceof NodeInjector) {
  27280. const firstInjector = resolutionPath[0];
  27281. if (firstInjector instanceof NodeInjector) {
  27282. const moduleInjector = getModuleInjectorOfNodeInjector(firstInjector);
  27283. if (moduleInjector === null) {
  27284. throwError('NodeInjector must have some connection to the module injector tree');
  27285. }
  27286. resolutionPath.push(moduleInjector);
  27287. getInjectorResolutionPathHelper(moduleInjector, resolutionPath);
  27288. }
  27289. return resolutionPath;
  27290. }
  27291. }
  27292. else {
  27293. resolutionPath.push(parent);
  27294. getInjectorResolutionPathHelper(parent, resolutionPath);
  27295. }
  27296. return resolutionPath;
  27297. }
  27298. /**
  27299. * Gets the parent of an injector.
  27300. *
  27301. * This function is not able to make the jump from the Element Injector Tree to the Module
  27302. * injector tree. This is because the "parent" (the next step in the reoslution path)
  27303. * of a root NodeInjector is dependent on which NodeInjector ancestor initiated
  27304. * the DI lookup. See getInjectorResolutionPath for a function that can make this jump.
  27305. *
  27306. * In the below diagram:
  27307. * ```ts
  27308. * getInjectorParent(NodeInjectorB)
  27309. * > NodeInjectorA
  27310. * getInjectorParent(NodeInjectorA) // or getInjectorParent(getInjectorParent(NodeInjectorB))
  27311. * > null // cannot jump to ModuleInjector tree
  27312. * ```
  27313. *
  27314. * ```
  27315. * ┌───────┐ ┌───────────────────┐
  27316. * ┌───────────┤ModuleA├───Injector────►│EnvironmentInjector│
  27317. * │ └───┬───┘ └───────────────────┘
  27318. * │ │
  27319. * │ bootstraps
  27320. * │ │
  27321. * │ │
  27322. * │ ┌────▼─────┐ ┌─────────────┐
  27323. * declares │ComponentA├────Injector────►│NodeInjectorA│
  27324. * │ └────┬─────┘ └─────▲───────┘
  27325. * │ │ │
  27326. * │ renders parent
  27327. * │ │ │
  27328. * │ ┌────▼─────┐ ┌─────┴───────┐
  27329. * └─────────►│ComponentB├────Injector────►│NodeInjectorB│
  27330. * └──────────┘ └─────────────┘
  27331. *```
  27332. *
  27333. * @param injector an Injector to get the parent of
  27334. * @returns Injector the parent of the given injector
  27335. */
  27336. function getInjectorParent(injector) {
  27337. if (injector instanceof R3Injector) {
  27338. return injector.parent;
  27339. }
  27340. let tNode;
  27341. let lView;
  27342. if (injector instanceof NodeInjector) {
  27343. tNode = getNodeInjectorTNode(injector);
  27344. lView = getNodeInjectorLView(injector);
  27345. }
  27346. else if (injector instanceof NullInjector) {
  27347. return null;
  27348. }
  27349. else {
  27350. throwError('getInjectorParent only support injectors of type R3Injector, NodeInjector, NullInjector');
  27351. }
  27352. const parentLocation = getParentInjectorLocation(tNode, lView);
  27353. if (hasParentInjector(parentLocation)) {
  27354. const parentInjectorIndex = getParentInjectorIndex(parentLocation);
  27355. const parentLView = getParentInjectorView(parentLocation, lView);
  27356. const parentTView = parentLView[TVIEW];
  27357. const parentTNode = parentTView.data[parentInjectorIndex + 8 /* NodeInjectorOffset.TNODE */];
  27358. return new NodeInjector(parentTNode, parentLView);
  27359. }
  27360. else {
  27361. const chainedInjector = lView[INJECTOR$1];
  27362. // Case where chainedInjector.injector is an OutletInjector and chainedInjector.injector.parent
  27363. // is a NodeInjector.
  27364. // todo(aleksanderbodurri): ideally nothing in packages/core should deal
  27365. // directly with router concerns. Refactor this so that we can make the jump from
  27366. // NodeInjector -> OutletInjector -> NodeInjector
  27367. // without explictly relying on types contracts from packages/router
  27368. const injectorParent = chainedInjector.injector?.parent;
  27369. if (injectorParent instanceof NodeInjector) {
  27370. return injectorParent;
  27371. }
  27372. }
  27373. return null;
  27374. }
  27375. /**
  27376. * Gets the module injector of a NodeInjector.
  27377. *
  27378. * @param injector NodeInjector to get module injector of
  27379. * @returns Injector representing module injector of the given NodeInjector
  27380. */
  27381. function getModuleInjectorOfNodeInjector(injector) {
  27382. let lView;
  27383. if (injector instanceof NodeInjector) {
  27384. lView = getNodeInjectorLView(injector);
  27385. }
  27386. else {
  27387. throwError('getModuleInjectorOfNodeInjector must be called with a NodeInjector');
  27388. }
  27389. const chainedInjector = lView[INJECTOR$1];
  27390. const moduleInjector = chainedInjector.parentInjector;
  27391. if (!moduleInjector) {
  27392. throwError('NodeInjector must have some connection to the module injector tree');
  27393. }
  27394. return moduleInjector;
  27395. }
  27396. /**
  27397. * This file introduces series of globally accessible debug tools
  27398. * to allow for the Angular debugging story to function.
  27399. *
  27400. * To see this in action run the following command:
  27401. *
  27402. * bazel run //packages/core/test/bundling/todo:devserver
  27403. *
  27404. * Then load `localhost:5432` and start using the console tools.
  27405. */
  27406. /**
  27407. * This value reflects the property on the window where the dev
  27408. * tools are patched (window.ng).
  27409. * */
  27410. const GLOBAL_PUBLISH_EXPANDO_KEY = 'ng';
  27411. let _published = false;
  27412. /**
  27413. * Publishes a collection of default debug tools onto`window.ng`.
  27414. *
  27415. * These functions are available globally when Angular is in development
  27416. * mode and are automatically stripped away from prod mode is on.
  27417. */
  27418. function publishDefaultGlobalUtils$1() {
  27419. if (!_published) {
  27420. _published = true;
  27421. setupFrameworkInjectorProfiler();
  27422. publishGlobalUtil('ɵgetDependenciesFromInjectable', getDependenciesFromInjectable);
  27423. publishGlobalUtil('ɵgetInjectorProviders', getInjectorProviders);
  27424. publishGlobalUtil('ɵgetInjectorResolutionPath', getInjectorResolutionPath);
  27425. /**
  27426. * Warning: this function is *INTERNAL* and should not be relied upon in application's code.
  27427. * The contract of the function might be changed in any release and/or the function can be
  27428. * removed completely.
  27429. */
  27430. publishGlobalUtil('ɵsetProfiler', setProfiler);
  27431. publishGlobalUtil('getDirectiveMetadata', getDirectiveMetadata$1);
  27432. publishGlobalUtil('getComponent', getComponent);
  27433. publishGlobalUtil('getContext', getContext);
  27434. publishGlobalUtil('getListeners', getListeners);
  27435. publishGlobalUtil('getOwningComponent', getOwningComponent);
  27436. publishGlobalUtil('getHostElement', getHostElement);
  27437. publishGlobalUtil('getInjector', getInjector);
  27438. publishGlobalUtil('getRootComponents', getRootComponents);
  27439. publishGlobalUtil('getDirectives', getDirectives);
  27440. publishGlobalUtil('applyChanges', applyChanges);
  27441. }
  27442. }
  27443. /**
  27444. * Publishes the given function to `window.ng` so that it can be
  27445. * used from the browser console when an application is not in production.
  27446. */
  27447. function publishGlobalUtil(name, fn) {
  27448. if (typeof COMPILED === 'undefined' || !COMPILED) {
  27449. // Note: we can't export `ng` when using closure enhanced optimization as:
  27450. // - closure declares globals itself for minified names, which sometimes clobber our `ng` global
  27451. // - we can't declare a closure extern as the namespace `ng` is already used within Google
  27452. // for typings for AngularJS (via `goog.provide('ng....')`).
  27453. const w = _global;
  27454. ngDevMode && assertDefined(fn, 'function not defined');
  27455. if (w) {
  27456. let container = w[GLOBAL_PUBLISH_EXPANDO_KEY];
  27457. if (!container) {
  27458. container = w[GLOBAL_PUBLISH_EXPANDO_KEY] = {};
  27459. }
  27460. container[name] = fn;
  27461. }
  27462. }
  27463. }
  27464. /**
  27465. * Internal injection token that can used to access an instance of a Testability class.
  27466. *
  27467. * This token acts as a bridge between the core bootstrap code and the `Testability` class. This is
  27468. * needed to ensure that there are no direct references to the `Testability` class, so it can be
  27469. * tree-shaken away (if not referenced). For the environments/setups when the `Testability` class
  27470. * should be available, this token is used to add a provider that references the `Testability`
  27471. * class. Otherwise, only this token is retained in a bundle, but the `Testability` class is not.
  27472. */
  27473. const TESTABILITY = new InjectionToken('');
  27474. /**
  27475. * Internal injection token to retrieve Testability getter class instance.
  27476. */
  27477. const TESTABILITY_GETTER = new InjectionToken('');
  27478. /**
  27479. * The Testability service provides testing hooks that can be accessed from
  27480. * the browser.
  27481. *
  27482. * Angular applications bootstrapped using an NgModule (via `@NgModule.bootstrap` field) will also
  27483. * instantiate Testability by default (in both development and production modes).
  27484. *
  27485. * For applications bootstrapped using the `bootstrapApplication` function, Testability is not
  27486. * included by default. You can include it into your applications by getting the list of necessary
  27487. * providers using the `provideProtractorTestingSupport()` function and adding them into the
  27488. * `options.providers` array. Example:
  27489. *
  27490. * ```typescript
  27491. * import {provideProtractorTestingSupport} from '@angular/platform-browser';
  27492. *
  27493. * await bootstrapApplication(RootComponent, providers: [provideProtractorTestingSupport()]);
  27494. * ```
  27495. *
  27496. * @publicApi
  27497. */
  27498. class Testability {
  27499. constructor(_ngZone, registry, testabilityGetter) {
  27500. this._ngZone = _ngZone;
  27501. this.registry = registry;
  27502. this._pendingCount = 0;
  27503. this._isZoneStable = true;
  27504. /**
  27505. * Whether any work was done since the last 'whenStable' callback. This is
  27506. * useful to detect if this could have potentially destabilized another
  27507. * component while it is stabilizing.
  27508. * @internal
  27509. */
  27510. this._didWork = false;
  27511. this._callbacks = [];
  27512. this.taskTrackingZone = null;
  27513. // If there was no Testability logic registered in the global scope
  27514. // before, register the current testability getter as a global one.
  27515. if (!_testabilityGetter) {
  27516. setTestabilityGetter(testabilityGetter);
  27517. testabilityGetter.addToWindow(registry);
  27518. }
  27519. this._watchAngularEvents();
  27520. _ngZone.run(() => {
  27521. this.taskTrackingZone =
  27522. typeof Zone == 'undefined' ? null : Zone.current.get('TaskTrackingZone');
  27523. });
  27524. }
  27525. _watchAngularEvents() {
  27526. this._ngZone.onUnstable.subscribe({
  27527. next: () => {
  27528. this._didWork = true;
  27529. this._isZoneStable = false;
  27530. }
  27531. });
  27532. this._ngZone.runOutsideAngular(() => {
  27533. this._ngZone.onStable.subscribe({
  27534. next: () => {
  27535. NgZone.assertNotInAngularZone();
  27536. queueMicrotask(() => {
  27537. this._isZoneStable = true;
  27538. this._runCallbacksIfReady();
  27539. });
  27540. }
  27541. });
  27542. });
  27543. }
  27544. /**
  27545. * Increases the number of pending request
  27546. * @deprecated pending requests are now tracked with zones.
  27547. */
  27548. increasePendingRequestCount() {
  27549. this._pendingCount += 1;
  27550. this._didWork = true;
  27551. return this._pendingCount;
  27552. }
  27553. /**
  27554. * Decreases the number of pending request
  27555. * @deprecated pending requests are now tracked with zones
  27556. */
  27557. decreasePendingRequestCount() {
  27558. this._pendingCount -= 1;
  27559. if (this._pendingCount < 0) {
  27560. throw new Error('pending async requests below zero');
  27561. }
  27562. this._runCallbacksIfReady();
  27563. return this._pendingCount;
  27564. }
  27565. /**
  27566. * Whether an associated application is stable
  27567. */
  27568. isStable() {
  27569. return this._isZoneStable && this._pendingCount === 0 && !this._ngZone.hasPendingMacrotasks;
  27570. }
  27571. _runCallbacksIfReady() {
  27572. if (this.isStable()) {
  27573. // Schedules the call backs in a new frame so that it is always async.
  27574. queueMicrotask(() => {
  27575. while (this._callbacks.length !== 0) {
  27576. let cb = this._callbacks.pop();
  27577. clearTimeout(cb.timeoutId);
  27578. cb.doneCb(this._didWork);
  27579. }
  27580. this._didWork = false;
  27581. });
  27582. }
  27583. else {
  27584. // Still not stable, send updates.
  27585. let pending = this.getPendingTasks();
  27586. this._callbacks = this._callbacks.filter((cb) => {
  27587. if (cb.updateCb && cb.updateCb(pending)) {
  27588. clearTimeout(cb.timeoutId);
  27589. return false;
  27590. }
  27591. return true;
  27592. });
  27593. this._didWork = true;
  27594. }
  27595. }
  27596. getPendingTasks() {
  27597. if (!this.taskTrackingZone) {
  27598. return [];
  27599. }
  27600. // Copy the tasks data so that we don't leak tasks.
  27601. return this.taskTrackingZone.macroTasks.map((t) => {
  27602. return {
  27603. source: t.source,
  27604. // From TaskTrackingZone:
  27605. // https://github.com/angular/zone.js/blob/master/lib/zone-spec/task-tracking.ts#L40
  27606. creationLocation: t.creationLocation,
  27607. data: t.data
  27608. };
  27609. });
  27610. }
  27611. addCallback(cb, timeout, updateCb) {
  27612. let timeoutId = -1;
  27613. if (timeout && timeout > 0) {
  27614. timeoutId = setTimeout(() => {
  27615. this._callbacks = this._callbacks.filter((cb) => cb.timeoutId !== timeoutId);
  27616. cb(this._didWork, this.getPendingTasks());
  27617. }, timeout);
  27618. }
  27619. this._callbacks.push({ doneCb: cb, timeoutId: timeoutId, updateCb: updateCb });
  27620. }
  27621. /**
  27622. * Wait for the application to be stable with a timeout. If the timeout is reached before that
  27623. * happens, the callback receives a list of the macro tasks that were pending, otherwise null.
  27624. *
  27625. * @param doneCb The callback to invoke when Angular is stable or the timeout expires
  27626. * whichever comes first.
  27627. * @param timeout Optional. The maximum time to wait for Angular to become stable. If not
  27628. * specified, whenStable() will wait forever.
  27629. * @param updateCb Optional. If specified, this callback will be invoked whenever the set of
  27630. * pending macrotasks changes. If this callback returns true doneCb will not be invoked
  27631. * and no further updates will be issued.
  27632. */
  27633. whenStable(doneCb, timeout, updateCb) {
  27634. if (updateCb && !this.taskTrackingZone) {
  27635. throw new Error('Task tracking zone is required when passing an update callback to ' +
  27636. 'whenStable(). Is "zone.js/plugins/task-tracking" loaded?');
  27637. }
  27638. // These arguments are 'Function' above to keep the public API simple.
  27639. this.addCallback(doneCb, timeout, updateCb);
  27640. this._runCallbacksIfReady();
  27641. }
  27642. /**
  27643. * Get the number of pending requests
  27644. * @deprecated pending requests are now tracked with zones
  27645. */
  27646. getPendingRequestCount() {
  27647. return this._pendingCount;
  27648. }
  27649. /**
  27650. * Registers an application with a testability hook so that it can be tracked.
  27651. * @param token token of application, root element
  27652. *
  27653. * @internal
  27654. */
  27655. registerApplication(token) {
  27656. this.registry.registerApplication(token, this);
  27657. }
  27658. /**
  27659. * Unregisters an application.
  27660. * @param token token of application, root element
  27661. *
  27662. * @internal
  27663. */
  27664. unregisterApplication(token) {
  27665. this.registry.unregisterApplication(token);
  27666. }
  27667. /**
  27668. * Find providers by name
  27669. * @param using The root element to search from
  27670. * @param provider The name of binding variable
  27671. * @param exactMatch Whether using exactMatch
  27672. */
  27673. findProviders(using, provider, exactMatch) {
  27674. // TODO(juliemr): implement.
  27675. return [];
  27676. }
  27677. static { this.ɵfac = function Testability_Factory(t) { return new (t || Testability)(ɵɵinject(NgZone), ɵɵinject(TestabilityRegistry), ɵɵinject(TESTABILITY_GETTER)); }; }
  27678. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: Testability, factory: Testability.ɵfac }); }
  27679. }
  27680. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(Testability, [{
  27681. type: Injectable
  27682. }], function () { return [{ type: NgZone }, { type: TestabilityRegistry }, { type: undefined, decorators: [{
  27683. type: Inject,
  27684. args: [TESTABILITY_GETTER]
  27685. }] }]; }, null); })();
  27686. /**
  27687. * A global registry of {@link Testability} instances for specific elements.
  27688. * @publicApi
  27689. */
  27690. class TestabilityRegistry {
  27691. constructor() {
  27692. /** @internal */
  27693. this._applications = new Map();
  27694. }
  27695. /**
  27696. * Registers an application with a testability hook so that it can be tracked
  27697. * @param token token of application, root element
  27698. * @param testability Testability hook
  27699. */
  27700. registerApplication(token, testability) {
  27701. this._applications.set(token, testability);
  27702. }
  27703. /**
  27704. * Unregisters an application.
  27705. * @param token token of application, root element
  27706. */
  27707. unregisterApplication(token) {
  27708. this._applications.delete(token);
  27709. }
  27710. /**
  27711. * Unregisters all applications
  27712. */
  27713. unregisterAllApplications() {
  27714. this._applications.clear();
  27715. }
  27716. /**
  27717. * Get a testability hook associated with the application
  27718. * @param elem root element
  27719. */
  27720. getTestability(elem) {
  27721. return this._applications.get(elem) || null;
  27722. }
  27723. /**
  27724. * Get all registered testabilities
  27725. */
  27726. getAllTestabilities() {
  27727. return Array.from(this._applications.values());
  27728. }
  27729. /**
  27730. * Get all registered applications(root elements)
  27731. */
  27732. getAllRootElements() {
  27733. return Array.from(this._applications.keys());
  27734. }
  27735. /**
  27736. * Find testability of a node in the Tree
  27737. * @param elem node
  27738. * @param findInAncestors whether finding testability in ancestors if testability was not found in
  27739. * current node
  27740. */
  27741. findTestabilityInTree(elem, findInAncestors = true) {
  27742. return _testabilityGetter?.findTestabilityInTree(this, elem, findInAncestors) ?? null;
  27743. }
  27744. static { this.ɵfac = function TestabilityRegistry_Factory(t) { return new (t || TestabilityRegistry)(); }; }
  27745. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: TestabilityRegistry, factory: TestabilityRegistry.ɵfac, providedIn: 'platform' }); }
  27746. }
  27747. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(TestabilityRegistry, [{
  27748. type: Injectable,
  27749. args: [{ providedIn: 'platform' }]
  27750. }], null, null); })();
  27751. /**
  27752. * Set the {@link GetTestability} implementation used by the Angular testing framework.
  27753. * @publicApi
  27754. */
  27755. function setTestabilityGetter(getter) {
  27756. _testabilityGetter = getter;
  27757. }
  27758. let _testabilityGetter;
  27759. let _platformInjector = null;
  27760. /**
  27761. * Internal token to indicate whether having multiple bootstrapped platform should be allowed (only
  27762. * one bootstrapped platform is allowed by default). This token helps to support SSR scenarios.
  27763. */
  27764. const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken('AllowMultipleToken');
  27765. /**
  27766. * Internal token that allows to register extra callbacks that should be invoked during the
  27767. * `PlatformRef.destroy` operation. This token is needed to avoid a direct reference to the
  27768. * `PlatformRef` class (i.e. register the callback via `PlatformRef.onDestroy`), thus making the
  27769. * entire class tree-shakeable.
  27770. */
  27771. const PLATFORM_DESTROY_LISTENERS = new InjectionToken('PlatformDestroyListeners');
  27772. /**
  27773. * A [DI token](guide/glossary#di-token "DI token definition") that provides a set of callbacks to
  27774. * be called for every component that is bootstrapped.
  27775. *
  27776. * Each callback must take a `ComponentRef` instance and return nothing.
  27777. *
  27778. * `(componentRef: ComponentRef) => void`
  27779. *
  27780. * @publicApi
  27781. */
  27782. const APP_BOOTSTRAP_LISTENER = new InjectionToken('appBootstrapListener');
  27783. function compileNgModuleFactory(injector, options, moduleType) {
  27784. ngDevMode && assertNgModuleType(moduleType);
  27785. const moduleFactory = new NgModuleFactory(moduleType);
  27786. // All of the logic below is irrelevant for AOT-compiled code.
  27787. if (typeof ngJitMode !== 'undefined' && !ngJitMode) {
  27788. return Promise.resolve(moduleFactory);
  27789. }
  27790. const compilerOptions = injector.get(COMPILER_OPTIONS, []).concat(options);
  27791. // Configure the compiler to use the provided options. This call may fail when multiple modules
  27792. // are bootstrapped with incompatible options, as a component can only be compiled according to
  27793. // a single set of options.
  27794. setJitOptions({
  27795. defaultEncapsulation: _lastDefined(compilerOptions.map(opts => opts.defaultEncapsulation)),
  27796. preserveWhitespaces: _lastDefined(compilerOptions.map(opts => opts.preserveWhitespaces)),
  27797. });
  27798. if (isComponentResourceResolutionQueueEmpty()) {
  27799. return Promise.resolve(moduleFactory);
  27800. }
  27801. const compilerProviders = compilerOptions.flatMap((option) => option.providers ?? []);
  27802. // In case there are no compiler providers, we just return the module factory as
  27803. // there won't be any resource loader. This can happen with Ivy, because AOT compiled
  27804. // modules can be still passed through "bootstrapModule". In that case we shouldn't
  27805. // unnecessarily require the JIT compiler.
  27806. if (compilerProviders.length === 0) {
  27807. return Promise.resolve(moduleFactory);
  27808. }
  27809. const compiler = getCompilerFacade({
  27810. usage: 0 /* JitCompilerUsage.Decorator */,
  27811. kind: 'NgModule',
  27812. type: moduleType,
  27813. });
  27814. const compilerInjector = Injector.create({ providers: compilerProviders });
  27815. const resourceLoader = compilerInjector.get(compiler.ResourceLoader);
  27816. // The resource loader can also return a string while the "resolveComponentResources"
  27817. // always expects a promise. Therefore we need to wrap the returned value in a promise.
  27818. return resolveComponentResources(url => Promise.resolve(resourceLoader.get(url)))
  27819. .then(() => moduleFactory);
  27820. }
  27821. function publishDefaultGlobalUtils() {
  27822. ngDevMode && publishDefaultGlobalUtils$1();
  27823. }
  27824. /**
  27825. * Sets the error for an invalid write to a signal to be an Angular `RuntimeError`.
  27826. */
  27827. function publishSignalConfiguration() {
  27828. setThrowInvalidWriteToSignalError(() => {
  27829. throw new RuntimeError(600 /* RuntimeErrorCode.SIGNAL_WRITE_FROM_ILLEGAL_CONTEXT */, ngDevMode &&
  27830. 'Writing to signals is not allowed in a `computed` or an `effect` by default. ' +
  27831. 'Use `allowSignalWrites` in the `CreateEffectOptions` to enable this inside effects.');
  27832. });
  27833. }
  27834. function isBoundToModule(cf) {
  27835. return cf.isBoundToModule;
  27836. }
  27837. /**
  27838. * A token for third-party components that can register themselves with NgProbe.
  27839. *
  27840. * @publicApi
  27841. */
  27842. class NgProbeToken {
  27843. constructor(name, token) {
  27844. this.name = name;
  27845. this.token = token;
  27846. }
  27847. }
  27848. /**
  27849. * Creates a platform.
  27850. * Platforms must be created on launch using this function.
  27851. *
  27852. * @publicApi
  27853. */
  27854. function createPlatform(injector) {
  27855. if (_platformInjector && !_platformInjector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
  27856. throw new RuntimeError(400 /* RuntimeErrorCode.MULTIPLE_PLATFORMS */, ngDevMode &&
  27857. 'There can be only one platform. Destroy the previous one to create a new one.');
  27858. }
  27859. publishDefaultGlobalUtils();
  27860. publishSignalConfiguration();
  27861. _platformInjector = injector;
  27862. const platform = injector.get(PlatformRef);
  27863. runPlatformInitializers(injector);
  27864. return platform;
  27865. }
  27866. /**
  27867. * The goal of this function is to bootstrap a platform injector,
  27868. * but avoid referencing `PlatformRef` class.
  27869. * This function is needed for bootstrapping a Standalone Component.
  27870. */
  27871. function createOrReusePlatformInjector(providers = []) {
  27872. // If a platform injector already exists, it means that the platform
  27873. // is already bootstrapped and no additional actions are required.
  27874. if (_platformInjector)
  27875. return _platformInjector;
  27876. // Otherwise, setup a new platform injector and run platform initializers.
  27877. const injector = createPlatformInjector(providers);
  27878. _platformInjector = injector;
  27879. publishDefaultGlobalUtils();
  27880. publishSignalConfiguration();
  27881. runPlatformInitializers(injector);
  27882. return injector;
  27883. }
  27884. function runPlatformInitializers(injector) {
  27885. const inits = injector.get(PLATFORM_INITIALIZER, null);
  27886. inits?.forEach((init) => init());
  27887. }
  27888. /**
  27889. * Internal create application API that implements the core application creation logic and optional
  27890. * bootstrap logic.
  27891. *
  27892. * Platforms (such as `platform-browser`) may require different set of application and platform
  27893. * providers for an application to function correctly. As a result, platforms may use this function
  27894. * internally and supply the necessary providers during the bootstrap, while exposing
  27895. * platform-specific APIs as a part of their public API.
  27896. *
  27897. * @returns A promise that returns an `ApplicationRef` instance once resolved.
  27898. */
  27899. function internalCreateApplication(config) {
  27900. try {
  27901. const { rootComponent, appProviders, platformProviders } = config;
  27902. if ((typeof ngDevMode === 'undefined' || ngDevMode) && rootComponent !== undefined) {
  27903. assertStandaloneComponentType(rootComponent);
  27904. }
  27905. const platformInjector = createOrReusePlatformInjector(platformProviders);
  27906. // Create root application injector based on a set of providers configured at the platform
  27907. // bootstrap level as well as providers passed to the bootstrap call by a user.
  27908. const allAppProviders = [
  27909. provideZoneChangeDetection(),
  27910. ...(appProviders || []),
  27911. ];
  27912. const adapter = new EnvironmentNgModuleRefAdapter({
  27913. providers: allAppProviders,
  27914. parent: platformInjector,
  27915. debugName: (typeof ngDevMode === 'undefined' || ngDevMode) ? 'Environment Injector' : '',
  27916. // We skip environment initializers because we need to run them inside the NgZone, which
  27917. // happens after we get the NgZone instance from the Injector.
  27918. runEnvironmentInitializers: false,
  27919. });
  27920. const envInjector = adapter.injector;
  27921. const ngZone = envInjector.get(NgZone);
  27922. return ngZone.run(() => {
  27923. envInjector.resolveInjectorInitializers();
  27924. const exceptionHandler = envInjector.get(ErrorHandler, null);
  27925. if ((typeof ngDevMode === 'undefined' || ngDevMode) && !exceptionHandler) {
  27926. throw new RuntimeError(402 /* RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP */, 'No `ErrorHandler` found in the Dependency Injection tree.');
  27927. }
  27928. let onErrorSubscription;
  27929. ngZone.runOutsideAngular(() => {
  27930. onErrorSubscription = ngZone.onError.subscribe({
  27931. next: (error) => {
  27932. exceptionHandler.handleError(error);
  27933. }
  27934. });
  27935. });
  27936. // If the whole platform is destroyed, invoke the `destroy` method
  27937. // for all bootstrapped applications as well.
  27938. const destroyListener = () => envInjector.destroy();
  27939. const onPlatformDestroyListeners = platformInjector.get(PLATFORM_DESTROY_LISTENERS);
  27940. onPlatformDestroyListeners.add(destroyListener);
  27941. envInjector.onDestroy(() => {
  27942. onErrorSubscription.unsubscribe();
  27943. onPlatformDestroyListeners.delete(destroyListener);
  27944. });
  27945. return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => {
  27946. const initStatus = envInjector.get(ApplicationInitStatus);
  27947. initStatus.runInitializers();
  27948. return initStatus.donePromise.then(() => {
  27949. const localeId = envInjector.get(LOCALE_ID, DEFAULT_LOCALE_ID);
  27950. setLocaleId(localeId || DEFAULT_LOCALE_ID);
  27951. const appRef = envInjector.get(ApplicationRef);
  27952. if (rootComponent !== undefined) {
  27953. appRef.bootstrap(rootComponent);
  27954. }
  27955. return appRef;
  27956. });
  27957. });
  27958. });
  27959. }
  27960. catch (e) {
  27961. return Promise.reject(e);
  27962. }
  27963. }
  27964. /**
  27965. * Creates a factory for a platform. Can be used to provide or override `Providers` specific to
  27966. * your application's runtime needs, such as `PLATFORM_INITIALIZER` and `PLATFORM_ID`.
  27967. * @param parentPlatformFactory Another platform factory to modify. Allows you to compose factories
  27968. * to build up configurations that might be required by different libraries or parts of the
  27969. * application.
  27970. * @param name Identifies the new platform factory.
  27971. * @param providers A set of dependency providers for platforms created with the new factory.
  27972. *
  27973. * @publicApi
  27974. */
  27975. function createPlatformFactory(parentPlatformFactory, name, providers = []) {
  27976. const desc = `Platform: ${name}`;
  27977. const marker = new InjectionToken(desc);
  27978. return (extraProviders = []) => {
  27979. let platform = getPlatform();
  27980. if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
  27981. const platformProviders = [
  27982. ...providers,
  27983. ...extraProviders,
  27984. { provide: marker, useValue: true }
  27985. ];
  27986. if (parentPlatformFactory) {
  27987. parentPlatformFactory(platformProviders);
  27988. }
  27989. else {
  27990. createPlatform(createPlatformInjector(platformProviders, desc));
  27991. }
  27992. }
  27993. return assertPlatform(marker);
  27994. };
  27995. }
  27996. /**
  27997. * Checks that there is currently a platform that contains the given token as a provider.
  27998. *
  27999. * @publicApi
  28000. */
  28001. function assertPlatform(requiredToken) {
  28002. const platform = getPlatform();
  28003. if (!platform) {
  28004. throw new RuntimeError(401 /* RuntimeErrorCode.PLATFORM_NOT_FOUND */, ngDevMode && 'No platform exists!');
  28005. }
  28006. if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
  28007. !platform.injector.get(requiredToken, null)) {
  28008. throw new RuntimeError(400 /* RuntimeErrorCode.MULTIPLE_PLATFORMS */, 'A platform with a different configuration has been created. Please destroy it first.');
  28009. }
  28010. return platform;
  28011. }
  28012. /**
  28013. * Helper function to create an instance of a platform injector (that maintains the 'platform'
  28014. * scope).
  28015. */
  28016. function createPlatformInjector(providers = [], name) {
  28017. return Injector.create({
  28018. name,
  28019. providers: [
  28020. { provide: INJECTOR_SCOPE, useValue: 'platform' },
  28021. { provide: PLATFORM_DESTROY_LISTENERS, useValue: new Set([() => _platformInjector = null]) },
  28022. ...providers
  28023. ],
  28024. });
  28025. }
  28026. /**
  28027. * Destroys the current Angular platform and all Angular applications on the page.
  28028. * Destroys all modules and listeners registered with the platform.
  28029. *
  28030. * @publicApi
  28031. */
  28032. function destroyPlatform() {
  28033. getPlatform()?.destroy();
  28034. }
  28035. /**
  28036. * Returns the current platform.
  28037. *
  28038. * @publicApi
  28039. */
  28040. function getPlatform() {
  28041. return _platformInjector?.get(PlatformRef) ?? null;
  28042. }
  28043. /**
  28044. * The Angular platform is the entry point for Angular on a web page.
  28045. * Each page has exactly one platform. Services (such as reflection) which are common
  28046. * to every Angular application running on the page are bound in its scope.
  28047. * A page's platform is initialized implicitly when a platform is created using a platform
  28048. * factory such as `PlatformBrowser`, or explicitly by calling the `createPlatform()` function.
  28049. *
  28050. * @publicApi
  28051. */
  28052. class PlatformRef {
  28053. /** @internal */
  28054. constructor(_injector) {
  28055. this._injector = _injector;
  28056. this._modules = [];
  28057. this._destroyListeners = [];
  28058. this._destroyed = false;
  28059. }
  28060. /**
  28061. * Creates an instance of an `@NgModule` for the given platform.
  28062. *
  28063. * @deprecated Passing NgModule factories as the `PlatformRef.bootstrapModuleFactory` function
  28064. * argument is deprecated. Use the `PlatformRef.bootstrapModule` API instead.
  28065. */
  28066. bootstrapModuleFactory(moduleFactory, options) {
  28067. // Note: We need to create the NgZone _before_ we instantiate the module,
  28068. // as instantiating the module creates some providers eagerly.
  28069. // So we create a mini parent injector that just contains the new NgZone and
  28070. // pass that as parent to the NgModuleFactory.
  28071. const ngZone = getNgZone(options?.ngZone, getNgZoneOptions({
  28072. eventCoalescing: options?.ngZoneEventCoalescing,
  28073. runCoalescing: options?.ngZoneRunCoalescing
  28074. }));
  28075. // Note: Create ngZoneInjector within ngZone.run so that all of the instantiated services are
  28076. // created within the Angular zone
  28077. // Do not try to replace ngZone.run with ApplicationRef#run because ApplicationRef would then be
  28078. // created outside of the Angular zone.
  28079. return ngZone.run(() => {
  28080. const moduleRef = createNgModuleRefWithProviders(moduleFactory.moduleType, this.injector, internalProvideZoneChangeDetection(() => ngZone));
  28081. if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
  28082. moduleRef.injector.get(PROVIDED_NG_ZONE, null) !== null) {
  28083. throw new RuntimeError(207 /* RuntimeErrorCode.PROVIDER_IN_WRONG_CONTEXT */, '`bootstrapModule` does not support `provideZoneChangeDetection`. Use `BootstrapOptions` instead.');
  28084. }
  28085. const exceptionHandler = moduleRef.injector.get(ErrorHandler, null);
  28086. if ((typeof ngDevMode === 'undefined' || ngDevMode) && exceptionHandler === null) {
  28087. throw new RuntimeError(402 /* RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP */, 'No ErrorHandler. Is platform module (BrowserModule) included?');
  28088. }
  28089. ngZone.runOutsideAngular(() => {
  28090. const subscription = ngZone.onError.subscribe({
  28091. next: (error) => {
  28092. exceptionHandler.handleError(error);
  28093. }
  28094. });
  28095. moduleRef.onDestroy(() => {
  28096. remove(this._modules, moduleRef);
  28097. subscription.unsubscribe();
  28098. });
  28099. });
  28100. return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => {
  28101. const initStatus = moduleRef.injector.get(ApplicationInitStatus);
  28102. initStatus.runInitializers();
  28103. return initStatus.donePromise.then(() => {
  28104. // If the `LOCALE_ID` provider is defined at bootstrap then we set the value for ivy
  28105. const localeId = moduleRef.injector.get(LOCALE_ID, DEFAULT_LOCALE_ID);
  28106. setLocaleId(localeId || DEFAULT_LOCALE_ID);
  28107. this._moduleDoBootstrap(moduleRef);
  28108. return moduleRef;
  28109. });
  28110. });
  28111. });
  28112. }
  28113. /**
  28114. * Creates an instance of an `@NgModule` for a given platform.
  28115. *
  28116. * @usageNotes
  28117. * ### Simple Example
  28118. *
  28119. * ```typescript
  28120. * @NgModule({
  28121. * imports: [BrowserModule]
  28122. * })
  28123. * class MyModule {}
  28124. *
  28125. * let moduleRef = platformBrowser().bootstrapModule(MyModule);
  28126. * ```
  28127. *
  28128. */
  28129. bootstrapModule(moduleType, compilerOptions = []) {
  28130. const options = optionsReducer({}, compilerOptions);
  28131. return compileNgModuleFactory(this.injector, options, moduleType)
  28132. .then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
  28133. }
  28134. _moduleDoBootstrap(moduleRef) {
  28135. const appRef = moduleRef.injector.get(ApplicationRef);
  28136. if (moduleRef._bootstrapComponents.length > 0) {
  28137. moduleRef._bootstrapComponents.forEach(f => appRef.bootstrap(f));
  28138. }
  28139. else if (moduleRef.instance.ngDoBootstrap) {
  28140. moduleRef.instance.ngDoBootstrap(appRef);
  28141. }
  28142. else {
  28143. throw new RuntimeError(-403 /* RuntimeErrorCode.BOOTSTRAP_COMPONENTS_NOT_FOUND */, ngDevMode &&
  28144. `The module ${stringify(moduleRef.instance.constructor)} was bootstrapped, ` +
  28145. `but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. ` +
  28146. `Please define one of these.`);
  28147. }
  28148. this._modules.push(moduleRef);
  28149. }
  28150. /**
  28151. * Registers a listener to be called when the platform is destroyed.
  28152. */
  28153. onDestroy(callback) {
  28154. this._destroyListeners.push(callback);
  28155. }
  28156. /**
  28157. * Retrieves the platform {@link Injector}, which is the parent injector for
  28158. * every Angular application on the page and provides singleton providers.
  28159. */
  28160. get injector() {
  28161. return this._injector;
  28162. }
  28163. /**
  28164. * Destroys the current Angular platform and all Angular applications on the page.
  28165. * Destroys all modules and listeners registered with the platform.
  28166. */
  28167. destroy() {
  28168. if (this._destroyed) {
  28169. throw new RuntimeError(404 /* RuntimeErrorCode.PLATFORM_ALREADY_DESTROYED */, ngDevMode && 'The platform has already been destroyed!');
  28170. }
  28171. this._modules.slice().forEach(module => module.destroy());
  28172. this._destroyListeners.forEach(listener => listener());
  28173. const destroyListeners = this._injector.get(PLATFORM_DESTROY_LISTENERS, null);
  28174. if (destroyListeners) {
  28175. destroyListeners.forEach(listener => listener());
  28176. destroyListeners.clear();
  28177. }
  28178. this._destroyed = true;
  28179. }
  28180. /**
  28181. * Indicates whether this instance was destroyed.
  28182. */
  28183. get destroyed() {
  28184. return this._destroyed;
  28185. }
  28186. static { this.ɵfac = function PlatformRef_Factory(t) { return new (t || PlatformRef)(ɵɵinject(Injector)); }; }
  28187. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: PlatformRef, factory: PlatformRef.ɵfac, providedIn: 'platform' }); }
  28188. }
  28189. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(PlatformRef, [{
  28190. type: Injectable,
  28191. args: [{ providedIn: 'platform' }]
  28192. }], function () { return [{ type: Injector }]; }, null); })();
  28193. // Transforms a set of `BootstrapOptions` (supported by the NgModule-based bootstrap APIs) ->
  28194. // `NgZoneOptions` that are recognized by the NgZone constructor. Passing no options will result in
  28195. // a set of default options returned.
  28196. function getNgZoneOptions(options) {
  28197. return {
  28198. enableLongStackTrace: typeof ngDevMode === 'undefined' ? false : !!ngDevMode,
  28199. shouldCoalesceEventChangeDetection: options?.eventCoalescing ?? false,
  28200. shouldCoalesceRunChangeDetection: options?.runCoalescing ?? false,
  28201. };
  28202. }
  28203. function getNgZone(ngZoneToUse = 'zone.js', options) {
  28204. if (ngZoneToUse === 'noop') {
  28205. return new NoopNgZone();
  28206. }
  28207. if (ngZoneToUse === 'zone.js') {
  28208. return new NgZone(options);
  28209. }
  28210. return ngZoneToUse;
  28211. }
  28212. function _callAndReportToErrorHandler(errorHandler, ngZone, callback) {
  28213. try {
  28214. const result = callback();
  28215. if (isPromise(result)) {
  28216. return result.catch((e) => {
  28217. ngZone.runOutsideAngular(() => errorHandler.handleError(e));
  28218. // rethrow as the exception handler might not do it
  28219. throw e;
  28220. });
  28221. }
  28222. return result;
  28223. }
  28224. catch (e) {
  28225. ngZone.runOutsideAngular(() => errorHandler.handleError(e));
  28226. // rethrow as the exception handler might not do it
  28227. throw e;
  28228. }
  28229. }
  28230. function optionsReducer(dst, objs) {
  28231. if (Array.isArray(objs)) {
  28232. return objs.reduce(optionsReducer, dst);
  28233. }
  28234. return { ...dst, ...objs };
  28235. }
  28236. /**
  28237. * A reference to an Angular application running on a page.
  28238. *
  28239. * @usageNotes
  28240. * {@a is-stable-examples}
  28241. * ### isStable examples and caveats
  28242. *
  28243. * Note two important points about `isStable`, demonstrated in the examples below:
  28244. * - the application will never be stable if you start any kind
  28245. * of recurrent asynchronous task when the application starts
  28246. * (for example for a polling process, started with a `setInterval`, a `setTimeout`
  28247. * or using RxJS operators like `interval`);
  28248. * - the `isStable` Observable runs outside of the Angular zone.
  28249. *
  28250. * Let's imagine that you start a recurrent task
  28251. * (here incrementing a counter, using RxJS `interval`),
  28252. * and at the same time subscribe to `isStable`.
  28253. *
  28254. * ```
  28255. * constructor(appRef: ApplicationRef) {
  28256. * appRef.isStable.pipe(
  28257. * filter(stable => stable)
  28258. * ).subscribe(() => console.log('App is stable now');
  28259. * interval(1000).subscribe(counter => console.log(counter));
  28260. * }
  28261. * ```
  28262. * In this example, `isStable` will never emit `true`,
  28263. * and the trace "App is stable now" will never get logged.
  28264. *
  28265. * If you want to execute something when the app is stable,
  28266. * you have to wait for the application to be stable
  28267. * before starting your polling process.
  28268. *
  28269. * ```
  28270. * constructor(appRef: ApplicationRef) {
  28271. * appRef.isStable.pipe(
  28272. * first(stable => stable),
  28273. * tap(stable => console.log('App is stable now')),
  28274. * switchMap(() => interval(1000))
  28275. * ).subscribe(counter => console.log(counter));
  28276. * }
  28277. * ```
  28278. * In this example, the trace "App is stable now" will be logged
  28279. * and then the counter starts incrementing every second.
  28280. *
  28281. * Note also that this Observable runs outside of the Angular zone,
  28282. * which means that the code in the subscription
  28283. * to this Observable will not trigger the change detection.
  28284. *
  28285. * Let's imagine that instead of logging the counter value,
  28286. * you update a field of your component
  28287. * and display it in its template.
  28288. *
  28289. * ```
  28290. * constructor(appRef: ApplicationRef) {
  28291. * appRef.isStable.pipe(
  28292. * first(stable => stable),
  28293. * switchMap(() => interval(1000))
  28294. * ).subscribe(counter => this.value = counter);
  28295. * }
  28296. * ```
  28297. * As the `isStable` Observable runs outside the zone,
  28298. * the `value` field will be updated properly,
  28299. * but the template will not be refreshed!
  28300. *
  28301. * You'll have to manually trigger the change detection to update the template.
  28302. *
  28303. * ```
  28304. * constructor(appRef: ApplicationRef, cd: ChangeDetectorRef) {
  28305. * appRef.isStable.pipe(
  28306. * first(stable => stable),
  28307. * switchMap(() => interval(1000))
  28308. * ).subscribe(counter => {
  28309. * this.value = counter;
  28310. * cd.detectChanges();
  28311. * });
  28312. * }
  28313. * ```
  28314. *
  28315. * Or make the subscription callback run inside the zone.
  28316. *
  28317. * ```
  28318. * constructor(appRef: ApplicationRef, zone: NgZone) {
  28319. * appRef.isStable.pipe(
  28320. * first(stable => stable),
  28321. * switchMap(() => interval(1000))
  28322. * ).subscribe(counter => zone.run(() => this.value = counter));
  28323. * }
  28324. * ```
  28325. *
  28326. * @publicApi
  28327. */
  28328. class ApplicationRef {
  28329. constructor() {
  28330. /** @internal */
  28331. this._bootstrapListeners = [];
  28332. this._runningTick = false;
  28333. this._destroyed = false;
  28334. this._destroyListeners = [];
  28335. /** @internal */
  28336. this._views = [];
  28337. this.internalErrorHandler = inject(INTERNAL_APPLICATION_ERROR_HANDLER);
  28338. this.zoneIsStable = inject(ZONE_IS_STABLE_OBSERVABLE);
  28339. /**
  28340. * Get a list of component types registered to this application.
  28341. * This list is populated even before the component is created.
  28342. */
  28343. this.componentTypes = [];
  28344. /**
  28345. * Get a list of components registered to this application.
  28346. */
  28347. this.components = [];
  28348. /**
  28349. * Returns an Observable that indicates when the application is stable or unstable.
  28350. */
  28351. this.isStable = inject(InitialRenderPendingTasks)
  28352. .hasPendingTasks.pipe(switchMap(hasPendingTasks => hasPendingTasks ? of(false) : this.zoneIsStable), distinctUntilChanged(), share());
  28353. this._injector = inject(EnvironmentInjector);
  28354. }
  28355. /**
  28356. * Indicates whether this instance was destroyed.
  28357. */
  28358. get destroyed() {
  28359. return this._destroyed;
  28360. }
  28361. /**
  28362. * The `EnvironmentInjector` used to create this application.
  28363. */
  28364. get injector() {
  28365. return this._injector;
  28366. }
  28367. /**
  28368. * Bootstrap a component onto the element identified by its selector or, optionally, to a
  28369. * specified element.
  28370. *
  28371. * @usageNotes
  28372. * ### Bootstrap process
  28373. *
  28374. * When bootstrapping a component, Angular mounts it onto a target DOM element
  28375. * and kicks off automatic change detection. The target DOM element can be
  28376. * provided using the `rootSelectorOrNode` argument.
  28377. *
  28378. * If the target DOM element is not provided, Angular tries to find one on a page
  28379. * using the `selector` of the component that is being bootstrapped
  28380. * (first matched element is used).
  28381. *
  28382. * ### Example
  28383. *
  28384. * Generally, we define the component to bootstrap in the `bootstrap` array of `NgModule`,
  28385. * but it requires us to know the component while writing the application code.
  28386. *
  28387. * Imagine a situation where we have to wait for an API call to decide about the component to
  28388. * bootstrap. We can use the `ngDoBootstrap` hook of the `NgModule` and call this method to
  28389. * dynamically bootstrap a component.
  28390. *
  28391. * {@example core/ts/platform/platform.ts region='componentSelector'}
  28392. *
  28393. * Optionally, a component can be mounted onto a DOM element that does not match the
  28394. * selector of the bootstrapped component.
  28395. *
  28396. * In the following example, we are providing a CSS selector to match the target element.
  28397. *
  28398. * {@example core/ts/platform/platform.ts region='cssSelector'}
  28399. *
  28400. * While in this example, we are providing reference to a DOM node.
  28401. *
  28402. * {@example core/ts/platform/platform.ts region='domNode'}
  28403. */
  28404. bootstrap(componentOrFactory, rootSelectorOrNode) {
  28405. (typeof ngDevMode === 'undefined' || ngDevMode) && this.warnIfDestroyed();
  28406. const isComponentFactory = componentOrFactory instanceof ComponentFactory$1;
  28407. const initStatus = this._injector.get(ApplicationInitStatus);
  28408. if (!initStatus.done) {
  28409. const standalone = !isComponentFactory && isStandalone(componentOrFactory);
  28410. const errorMessage = 'Cannot bootstrap as there are still asynchronous initializers running.' +
  28411. (standalone ? '' :
  28412. ' Bootstrap components in the `ngDoBootstrap` method of the root module.');
  28413. throw new RuntimeError(405 /* RuntimeErrorCode.ASYNC_INITIALIZERS_STILL_RUNNING */, (typeof ngDevMode === 'undefined' || ngDevMode) && errorMessage);
  28414. }
  28415. let componentFactory;
  28416. if (isComponentFactory) {
  28417. componentFactory = componentOrFactory;
  28418. }
  28419. else {
  28420. const resolver = this._injector.get(ComponentFactoryResolver$1);
  28421. componentFactory = resolver.resolveComponentFactory(componentOrFactory);
  28422. }
  28423. this.componentTypes.push(componentFactory.componentType);
  28424. // Create a factory associated with the current module if it's not bound to some other
  28425. const ngModule = isBoundToModule(componentFactory) ? undefined : this._injector.get(NgModuleRef$1);
  28426. const selectorOrNode = rootSelectorOrNode || componentFactory.selector;
  28427. const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);
  28428. const nativeElement = compRef.location.nativeElement;
  28429. const testability = compRef.injector.get(TESTABILITY, null);
  28430. testability?.registerApplication(nativeElement);
  28431. compRef.onDestroy(() => {
  28432. this.detachView(compRef.hostView);
  28433. remove(this.components, compRef);
  28434. testability?.unregisterApplication(nativeElement);
  28435. });
  28436. this._loadComponent(compRef);
  28437. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  28438. const _console = this._injector.get(Console);
  28439. _console.log(`Angular is running in development mode.`);
  28440. }
  28441. return compRef;
  28442. }
  28443. /**
  28444. * Invoke this method to explicitly process change detection and its side-effects.
  28445. *
  28446. * In development mode, `tick()` also performs a second change detection cycle to ensure that no
  28447. * further changes are detected. If additional changes are picked up during this second cycle,
  28448. * bindings in the app have side-effects that cannot be resolved in a single change detection
  28449. * pass.
  28450. * In this case, Angular throws an error, since an Angular application can only have one change
  28451. * detection pass during which all change detection must complete.
  28452. */
  28453. tick() {
  28454. (typeof ngDevMode === 'undefined' || ngDevMode) && this.warnIfDestroyed();
  28455. if (this._runningTick) {
  28456. throw new RuntimeError(101 /* RuntimeErrorCode.RECURSIVE_APPLICATION_REF_TICK */, ngDevMode && 'ApplicationRef.tick is called recursively');
  28457. }
  28458. try {
  28459. this._runningTick = true;
  28460. for (let view of this._views) {
  28461. view.detectChanges();
  28462. }
  28463. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  28464. for (let view of this._views) {
  28465. view.checkNoChanges();
  28466. }
  28467. }
  28468. }
  28469. catch (e) {
  28470. // Attention: Don't rethrow as it could cancel subscriptions to Observables!
  28471. this.internalErrorHandler(e);
  28472. }
  28473. finally {
  28474. this._runningTick = false;
  28475. }
  28476. }
  28477. /**
  28478. * Attaches a view so that it will be dirty checked.
  28479. * The view will be automatically detached when it is destroyed.
  28480. * This will throw if the view is already attached to a ViewContainer.
  28481. */
  28482. attachView(viewRef) {
  28483. (typeof ngDevMode === 'undefined' || ngDevMode) && this.warnIfDestroyed();
  28484. const view = viewRef;
  28485. this._views.push(view);
  28486. view.attachToAppRef(this);
  28487. }
  28488. /**
  28489. * Detaches a view from dirty checking again.
  28490. */
  28491. detachView(viewRef) {
  28492. (typeof ngDevMode === 'undefined' || ngDevMode) && this.warnIfDestroyed();
  28493. const view = viewRef;
  28494. remove(this._views, view);
  28495. view.detachFromAppRef();
  28496. }
  28497. _loadComponent(componentRef) {
  28498. this.attachView(componentRef.hostView);
  28499. this.tick();
  28500. this.components.push(componentRef);
  28501. // Get the listeners lazily to prevent DI cycles.
  28502. const listeners = this._injector.get(APP_BOOTSTRAP_LISTENER, []);
  28503. if (ngDevMode && !Array.isArray(listeners)) {
  28504. throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `APP_BOOTSTRAP_LISTENER` token value ' +
  28505. `(expected an array, but got ${typeof listeners}). ` +
  28506. 'Please check that the `APP_BOOTSTRAP_LISTENER` token is configured as a ' +
  28507. '`multi: true` provider.');
  28508. }
  28509. listeners.push(...this._bootstrapListeners);
  28510. listeners.forEach((listener) => listener(componentRef));
  28511. }
  28512. /** @internal */
  28513. ngOnDestroy() {
  28514. if (this._destroyed)
  28515. return;
  28516. try {
  28517. // Call all the lifecycle hooks.
  28518. this._destroyListeners.forEach(listener => listener());
  28519. // Destroy all registered views.
  28520. this._views.slice().forEach((view) => view.destroy());
  28521. }
  28522. finally {
  28523. // Indicate that this instance is destroyed.
  28524. this._destroyed = true;
  28525. // Release all references.
  28526. this._views = [];
  28527. this._bootstrapListeners = [];
  28528. this._destroyListeners = [];
  28529. }
  28530. }
  28531. /**
  28532. * Registers a listener to be called when an instance is destroyed.
  28533. *
  28534. * @param callback A callback function to add as a listener.
  28535. * @returns A function which unregisters a listener.
  28536. */
  28537. onDestroy(callback) {
  28538. (typeof ngDevMode === 'undefined' || ngDevMode) && this.warnIfDestroyed();
  28539. this._destroyListeners.push(callback);
  28540. return () => remove(this._destroyListeners, callback);
  28541. }
  28542. /**
  28543. * Destroys an Angular application represented by this `ApplicationRef`. Calling this function
  28544. * will destroy the associated environment injectors as well as all the bootstrapped components
  28545. * with their views.
  28546. */
  28547. destroy() {
  28548. if (this._destroyed) {
  28549. throw new RuntimeError(406 /* RuntimeErrorCode.APPLICATION_REF_ALREADY_DESTROYED */, ngDevMode && 'This instance of the `ApplicationRef` has already been destroyed.');
  28550. }
  28551. const injector = this._injector;
  28552. // Check that this injector instance supports destroy operation.
  28553. if (injector.destroy && !injector.destroyed) {
  28554. // Destroying an underlying injector will trigger the `ngOnDestroy` lifecycle
  28555. // hook, which invokes the remaining cleanup actions.
  28556. injector.destroy();
  28557. }
  28558. }
  28559. /**
  28560. * Returns the number of attached views.
  28561. */
  28562. get viewCount() {
  28563. return this._views.length;
  28564. }
  28565. warnIfDestroyed() {
  28566. if ((typeof ngDevMode === 'undefined' || ngDevMode) && this._destroyed) {
  28567. console.warn(formatRuntimeError(406 /* RuntimeErrorCode.APPLICATION_REF_ALREADY_DESTROYED */, 'This instance of the `ApplicationRef` has already been destroyed.'));
  28568. }
  28569. }
  28570. static { this.ɵfac = function ApplicationRef_Factory(t) { return new (t || ApplicationRef)(); }; }
  28571. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: ApplicationRef, factory: ApplicationRef.ɵfac, providedIn: 'root' }); }
  28572. }
  28573. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationRef, [{
  28574. type: Injectable,
  28575. args: [{ providedIn: 'root' }]
  28576. }], null, null); })();
  28577. function remove(list, el) {
  28578. const index = list.indexOf(el);
  28579. if (index > -1) {
  28580. list.splice(index, 1);
  28581. }
  28582. }
  28583. function _lastDefined(args) {
  28584. for (let i = args.length - 1; i >= 0; i--) {
  28585. if (args[i] !== undefined) {
  28586. return args[i];
  28587. }
  28588. }
  28589. return undefined;
  28590. }
  28591. /**
  28592. * `InjectionToken` used to configure how to call the `ErrorHandler`.
  28593. *
  28594. * `NgZone` is provided by default today so the default (and only) implementation for this
  28595. * is calling `ErrorHandler.handleError` outside of the Angular zone.
  28596. */
  28597. const INTERNAL_APPLICATION_ERROR_HANDLER = new InjectionToken((typeof ngDevMode === 'undefined' || ngDevMode) ? 'internal error handler' : '', {
  28598. providedIn: 'root',
  28599. factory: () => {
  28600. const userErrorHandler = inject(ErrorHandler);
  28601. return userErrorHandler.handleError.bind(undefined);
  28602. }
  28603. });
  28604. function ngZoneApplicationErrorHandlerFactory() {
  28605. const zone = inject(NgZone);
  28606. const userErrorHandler = inject(ErrorHandler);
  28607. return (e) => zone.runOutsideAngular(() => userErrorHandler.handleError(e));
  28608. }
  28609. class NgZoneChangeDetectionScheduler {
  28610. constructor() {
  28611. this.zone = inject(NgZone);
  28612. this.applicationRef = inject(ApplicationRef);
  28613. }
  28614. initialize() {
  28615. if (this._onMicrotaskEmptySubscription) {
  28616. return;
  28617. }
  28618. this._onMicrotaskEmptySubscription = this.zone.onMicrotaskEmpty.subscribe({
  28619. next: () => {
  28620. this.zone.run(() => {
  28621. this.applicationRef.tick();
  28622. });
  28623. }
  28624. });
  28625. }
  28626. ngOnDestroy() {
  28627. this._onMicrotaskEmptySubscription?.unsubscribe();
  28628. }
  28629. static { this.ɵfac = function NgZoneChangeDetectionScheduler_Factory(t) { return new (t || NgZoneChangeDetectionScheduler)(); }; }
  28630. static { this.ɵprov = /*@__PURE__*/ ɵɵdefineInjectable({ token: NgZoneChangeDetectionScheduler, factory: NgZoneChangeDetectionScheduler.ɵfac, providedIn: 'root' }); }
  28631. }
  28632. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(NgZoneChangeDetectionScheduler, [{
  28633. type: Injectable,
  28634. args: [{ providedIn: 'root' }]
  28635. }], null, null); })();
  28636. /**
  28637. * Internal token used to verify that `provideZoneChangeDetection` is not used
  28638. * with the bootstrapModule API.
  28639. */
  28640. const PROVIDED_NG_ZONE = new InjectionToken((typeof ngDevMode === 'undefined' || ngDevMode) ? 'provideZoneChangeDetection token' : '');
  28641. function internalProvideZoneChangeDetection(ngZoneFactory) {
  28642. return [
  28643. { provide: NgZone, useFactory: ngZoneFactory },
  28644. {
  28645. provide: ENVIRONMENT_INITIALIZER,
  28646. multi: true,
  28647. useFactory: () => {
  28648. const ngZoneChangeDetectionScheduler = inject(NgZoneChangeDetectionScheduler, { optional: true });
  28649. if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
  28650. ngZoneChangeDetectionScheduler === null) {
  28651. throw new RuntimeError(402 /* RuntimeErrorCode.MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP */, `A required Injectable was not found in the dependency injection tree. ` +
  28652. 'If you are bootstrapping an NgModule, make sure that the `BrowserModule` is imported.');
  28653. }
  28654. return () => ngZoneChangeDetectionScheduler.initialize();
  28655. },
  28656. },
  28657. { provide: INTERNAL_APPLICATION_ERROR_HANDLER, useFactory: ngZoneApplicationErrorHandlerFactory },
  28658. { provide: ZONE_IS_STABLE_OBSERVABLE, useFactory: isStableFactory },
  28659. ];
  28660. }
  28661. /**
  28662. * Provides `NgZone`-based change detection for the application bootstrapped using
  28663. * `bootstrapApplication`.
  28664. *
  28665. * `NgZone` is already provided in applications by default. This provider allows you to configure
  28666. * options like `eventCoalescing` in the `NgZone`.
  28667. * This provider is not available for `platformBrowser().bootstrapModule`, which uses
  28668. * `BootstrapOptions` instead.
  28669. *
  28670. * @usageNotes
  28671. * ```typescript=
  28672. * bootstrapApplication(MyApp, {providers: [
  28673. * provideZoneChangeDetection({eventCoalescing: true}),
  28674. * ]});
  28675. * ```
  28676. *
  28677. * @publicApi
  28678. * @see {@link bootstrapApplication}
  28679. * @see {@link NgZoneOptions}
  28680. */
  28681. function provideZoneChangeDetection(options) {
  28682. const zoneProviders = internalProvideZoneChangeDetection(() => new NgZone(getNgZoneOptions(options)));
  28683. return makeEnvironmentProviders([
  28684. (typeof ngDevMode === 'undefined' || ngDevMode) ? { provide: PROVIDED_NG_ZONE, useValue: true } :
  28685. [],
  28686. zoneProviders,
  28687. ]);
  28688. }
  28689. /**
  28690. * Returns whether Angular is in development mode.
  28691. *
  28692. * By default, this is true, unless `enableProdMode` is invoked prior to calling this method or the
  28693. * application is built using the Angular CLI with the `optimization` option.
  28694. * @see {@link cli/build ng build}
  28695. *
  28696. * @publicApi
  28697. */
  28698. function isDevMode() {
  28699. return typeof ngDevMode === 'undefined' || !!ngDevMode;
  28700. }
  28701. /**
  28702. * Disable Angular's development mode, which turns off assertions and other
  28703. * checks within the framework.
  28704. *
  28705. * One important assertion this disables verifies that a change detection pass
  28706. * does not result in additional changes to any bindings (also known as
  28707. * unidirectional data flow).
  28708. *
  28709. * Using this method is discouraged as the Angular CLI will set production mode when using the
  28710. * `optimization` option.
  28711. * @see {@link cli/build ng build}
  28712. *
  28713. * @publicApi
  28714. */
  28715. function enableProdMode() {
  28716. // The below check is there so when ngDevMode is set via terser
  28717. // `global['ngDevMode'] = false;` is also dropped.
  28718. if (typeof ngDevMode === 'undefined' || ngDevMode) {
  28719. _global['ngDevMode'] = false;
  28720. }
  28721. }
  28722. // Public API for render
  28723. /**
  28724. * Returns the NgModuleFactory with the given id (specified using [@NgModule.id
  28725. * field](api/core/NgModule#id)), if it exists and has been loaded. Factories for NgModules that do
  28726. * not specify an `id` cannot be retrieved. Throws if an NgModule cannot be found.
  28727. * @publicApi
  28728. * @deprecated Use `getNgModuleById` instead.
  28729. */
  28730. function getModuleFactory(id) {
  28731. const type = getRegisteredNgModuleType(id);
  28732. if (!type)
  28733. throw noModuleError(id);
  28734. return new NgModuleFactory(type);
  28735. }
  28736. /**
  28737. * Returns the NgModule class with the given id (specified using [@NgModule.id
  28738. * field](api/core/NgModule#id)), if it exists and has been loaded. Classes for NgModules that do
  28739. * not specify an `id` cannot be retrieved. Throws if an NgModule cannot be found.
  28740. * @publicApi
  28741. */
  28742. function getNgModuleById(id) {
  28743. const type = getRegisteredNgModuleType(id);
  28744. if (!type)
  28745. throw noModuleError(id);
  28746. return type;
  28747. }
  28748. function noModuleError(id) {
  28749. return new Error(`No module with ID ${id} loaded`);
  28750. }
  28751. /**
  28752. * Base class that provides change detection functionality.
  28753. * A change-detection tree collects all views that are to be checked for changes.
  28754. * Use the methods to add and remove views from the tree, initiate change-detection,
  28755. * and explicitly mark views as _dirty_, meaning that they have changed and need to be re-rendered.
  28756. *
  28757. * @see [Using change detection hooks](guide/lifecycle-hooks#using-change-detection-hooks)
  28758. * @see [Defining custom change detection](guide/lifecycle-hooks#defining-custom-change-detection)
  28759. *
  28760. * @usageNotes
  28761. *
  28762. * The following examples demonstrate how to modify default change-detection behavior
  28763. * to perform explicit detection when needed.
  28764. *
  28765. * ### Use `markForCheck()` with `CheckOnce` strategy
  28766. *
  28767. * The following example sets the `OnPush` change-detection strategy for a component
  28768. * (`CheckOnce`, rather than the default `CheckAlways`), then forces a second check
  28769. * after an interval.
  28770. *
  28771. * <code-example path="core/ts/change_detect/change-detection.ts"
  28772. * region="mark-for-check"></code-example>
  28773. *
  28774. * ### Detach change detector to limit how often check occurs
  28775. *
  28776. * The following example defines a component with a large list of read-only data
  28777. * that is expected to change constantly, many times per second.
  28778. * To improve performance, we want to check and update the list
  28779. * less often than the changes actually occur. To do that, we detach
  28780. * the component's change detector and perform an explicit local check every five seconds.
  28781. *
  28782. * <code-example path="core/ts/change_detect/change-detection.ts" region="detach"></code-example>
  28783. *
  28784. *
  28785. * ### Reattaching a detached component
  28786. *
  28787. * The following example creates a component displaying live data.
  28788. * The component detaches its change detector from the main change detector tree
  28789. * when the `live` property is set to false, and reattaches it when the property
  28790. * becomes true.
  28791. *
  28792. * <code-example path="core/ts/change_detect/change-detection.ts" region="reattach"></code-example>
  28793. *
  28794. * @publicApi
  28795. */
  28796. class ChangeDetectorRef {
  28797. /**
  28798. * @internal
  28799. * @nocollapse
  28800. */
  28801. static { this.__NG_ELEMENT_ID__ = injectChangeDetectorRef; }
  28802. }
  28803. /** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */
  28804. function injectChangeDetectorRef(flags) {
  28805. return createViewRef(getCurrentTNode(), getLView(), (flags & 16 /* InternalInjectFlags.ForPipe */) === 16 /* InternalInjectFlags.ForPipe */);
  28806. }
  28807. /**
  28808. * Creates a ViewRef and stores it on the injector as ChangeDetectorRef (public alias).
  28809. *
  28810. * @param tNode The node that is requesting a ChangeDetectorRef
  28811. * @param lView The view to which the node belongs
  28812. * @param isPipe Whether the view is being injected into a pipe.
  28813. * @returns The ChangeDetectorRef to use
  28814. */
  28815. function createViewRef(tNode, lView, isPipe) {
  28816. if (isComponentHost(tNode) && !isPipe) {
  28817. // The LView represents the location where the component is declared.
  28818. // Instead we want the LView for the component View and so we need to look it up.
  28819. const componentView = getComponentLViewByIndex(tNode.index, lView); // look down
  28820. return new ViewRef$1(componentView, componentView);
  28821. }
  28822. else if (tNode.type & (3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */)) {
  28823. // The LView represents the location where the injection is requested from.
  28824. // We need to locate the containing LView (in case where the `lView` is an embedded view)
  28825. const hostComponentView = lView[DECLARATION_COMPONENT_VIEW]; // look up
  28826. return new ViewRef$1(hostComponentView, lView);
  28827. }
  28828. return null;
  28829. }
  28830. /**
  28831. * Represents an Angular [view](guide/glossary#view "Definition").
  28832. *
  28833. * @see {@link ChangeDetectorRef#usage-notes Change detection usage}
  28834. *
  28835. * @publicApi
  28836. */
  28837. class ViewRef extends ChangeDetectorRef {
  28838. }
  28839. /**
  28840. * Represents an Angular [view](guide/glossary#view) in a view container.
  28841. * An [embedded view](guide/glossary#view-hierarchy) can be referenced from a component
  28842. * other than the hosting component whose template defines it, or it can be defined
  28843. * independently by a `TemplateRef`.
  28844. *
  28845. * Properties of elements in a view can change, but the structure (number and order) of elements in
  28846. * a view cannot. Change the structure of elements by inserting, moving, or
  28847. * removing nested views in a view container.
  28848. *
  28849. * @see {@link ViewContainerRef}
  28850. *
  28851. * @usageNotes
  28852. *
  28853. * The following template breaks down into two separate `TemplateRef` instances,
  28854. * an outer one and an inner one.
  28855. *
  28856. * ```
  28857. * Count: {{items.length}}
  28858. * <ul>
  28859. * <li *ngFor="let item of items">{{item}}</li>
  28860. * </ul>
  28861. * ```
  28862. *
  28863. * This is the outer `TemplateRef`:
  28864. *
  28865. * ```
  28866. * Count: {{items.length}}
  28867. * <ul>
  28868. * <ng-template ngFor let-item [ngForOf]="items"></ng-template>
  28869. * </ul>
  28870. * ```
  28871. *
  28872. * This is the inner `TemplateRef`:
  28873. *
  28874. * ```
  28875. * <li>{{item}}</li>
  28876. * ```
  28877. *
  28878. * The outer and inner `TemplateRef` instances are assembled into views as follows:
  28879. *
  28880. * ```
  28881. * <!-- ViewRef: outer-0 -->
  28882. * Count: 2
  28883. * <ul>
  28884. * <ng-template view-container-ref></ng-template>
  28885. * <!-- ViewRef: inner-1 --><li>first</li><!-- /ViewRef: inner-1 -->
  28886. * <!-- ViewRef: inner-2 --><li>second</li><!-- /ViewRef: inner-2 -->
  28887. * </ul>
  28888. * <!-- /ViewRef: outer-0 -->
  28889. * ```
  28890. * @publicApi
  28891. */
  28892. class EmbeddedViewRef extends ViewRef {
  28893. }
  28894. // Public API for compiler
  28895. // This file exists for easily patching NgModuleFactoryLoader in g3
  28896. var ng_module_factory_loader_impl = {};
  28897. /**
  28898. * @publicApi
  28899. */
  28900. class DebugEventListener {
  28901. constructor(name, callback) {
  28902. this.name = name;
  28903. this.callback = callback;
  28904. }
  28905. }
  28906. /**
  28907. * @publicApi
  28908. */
  28909. function asNativeElements(debugEls) {
  28910. return debugEls.map((el) => el.nativeElement);
  28911. }
  28912. /**
  28913. * @publicApi
  28914. */
  28915. class DebugNode {
  28916. constructor(nativeNode) {
  28917. this.nativeNode = nativeNode;
  28918. }
  28919. /**
  28920. * The `DebugElement` parent. Will be `null` if this is the root element.
  28921. */
  28922. get parent() {
  28923. const parent = this.nativeNode.parentNode;
  28924. return parent ? new DebugElement(parent) : null;
  28925. }
  28926. /**
  28927. * The host dependency injector. For example, the root element's component instance injector.
  28928. */
  28929. get injector() {
  28930. return getInjector(this.nativeNode);
  28931. }
  28932. /**
  28933. * The element's own component instance, if it has one.
  28934. */
  28935. get componentInstance() {
  28936. const nativeElement = this.nativeNode;
  28937. return nativeElement &&
  28938. (getComponent(nativeElement) || getOwningComponent(nativeElement));
  28939. }
  28940. /**
  28941. * An object that provides parent context for this element. Often an ancestor component instance
  28942. * that governs this element.
  28943. *
  28944. * When an element is repeated within *ngFor, the context is an `NgForOf` whose `$implicit`
  28945. * property is the value of the row instance value. For example, the `hero` in `*ngFor="let hero
  28946. * of heroes"`.
  28947. */
  28948. get context() {
  28949. return getComponent(this.nativeNode) || getContext(this.nativeNode);
  28950. }
  28951. /**
  28952. * The callbacks attached to the component's @Output properties and/or the element's event
  28953. * properties.
  28954. */
  28955. get listeners() {
  28956. return getListeners(this.nativeNode).filter(listener => listener.type === 'dom');
  28957. }
  28958. /**
  28959. * Dictionary of objects associated with template local variables (e.g. #foo), keyed by the local
  28960. * variable name.
  28961. */
  28962. get references() {
  28963. return getLocalRefs(this.nativeNode);
  28964. }
  28965. /**
  28966. * This component's injector lookup tokens. Includes the component itself plus the tokens that the
  28967. * component lists in its providers metadata.
  28968. */
  28969. get providerTokens() {
  28970. return getInjectionTokens(this.nativeNode);
  28971. }
  28972. }
  28973. /**
  28974. * @publicApi
  28975. *
  28976. * @see [Component testing scenarios](guide/testing-components-scenarios)
  28977. * @see [Basics of testing components](guide/testing-components-basics)
  28978. * @see [Testing utility APIs](guide/testing-utility-apis)
  28979. */
  28980. class DebugElement extends DebugNode {
  28981. constructor(nativeNode) {
  28982. ngDevMode && assertDomNode(nativeNode);
  28983. super(nativeNode);
  28984. }
  28985. /**
  28986. * The underlying DOM element at the root of the component.
  28987. */
  28988. get nativeElement() {
  28989. return this.nativeNode.nodeType == Node.ELEMENT_NODE ? this.nativeNode : null;
  28990. }
  28991. /**
  28992. * The element tag name, if it is an element.
  28993. */
  28994. get name() {
  28995. const context = getLContext(this.nativeNode);
  28996. const lView = context ? context.lView : null;
  28997. if (lView !== null) {
  28998. const tData = lView[TVIEW].data;
  28999. const tNode = tData[context.nodeIndex];
  29000. return tNode.value;
  29001. }
  29002. else {
  29003. return this.nativeNode.nodeName;
  29004. }
  29005. }
  29006. /**
  29007. * Gets a map of property names to property values for an element.
  29008. *
  29009. * This map includes:
  29010. * - Regular property bindings (e.g. `[id]="id"`)
  29011. * - Host property bindings (e.g. `host: { '[id]': "id" }`)
  29012. * - Interpolated property bindings (e.g. `id="{{ value }}")
  29013. *
  29014. * It does not include:
  29015. * - input property bindings (e.g. `[myCustomInput]="value"`)
  29016. * - attribute bindings (e.g. `[attr.role]="menu"`)
  29017. */
  29018. get properties() {
  29019. const context = getLContext(this.nativeNode);
  29020. const lView = context ? context.lView : null;
  29021. if (lView === null) {
  29022. return {};
  29023. }
  29024. const tData = lView[TVIEW].data;
  29025. const tNode = tData[context.nodeIndex];
  29026. const properties = {};
  29027. // Collect properties from the DOM.
  29028. copyDomProperties(this.nativeElement, properties);
  29029. // Collect properties from the bindings. This is needed for animation renderer which has
  29030. // synthetic properties which don't get reflected into the DOM.
  29031. collectPropertyBindings(properties, tNode, lView, tData);
  29032. return properties;
  29033. }
  29034. /**
  29035. * A map of attribute names to attribute values for an element.
  29036. */
  29037. get attributes() {
  29038. const attributes = {};
  29039. const element = this.nativeElement;
  29040. if (!element) {
  29041. return attributes;
  29042. }
  29043. const context = getLContext(element);
  29044. const lView = context ? context.lView : null;
  29045. if (lView === null) {
  29046. return {};
  29047. }
  29048. const tNodeAttrs = lView[TVIEW].data[context.nodeIndex].attrs;
  29049. const lowercaseTNodeAttrs = [];
  29050. // For debug nodes we take the element's attribute directly from the DOM since it allows us
  29051. // to account for ones that weren't set via bindings (e.g. ViewEngine keeps track of the ones
  29052. // that are set through `Renderer2`). The problem is that the browser will lowercase all names,
  29053. // however since we have the attributes already on the TNode, we can preserve the case by going
  29054. // through them once, adding them to the `attributes` map and putting their lower-cased name
  29055. // into an array. Afterwards when we're going through the native DOM attributes, we can check
  29056. // whether we haven't run into an attribute already through the TNode.
  29057. if (tNodeAttrs) {
  29058. let i = 0;
  29059. while (i < tNodeAttrs.length) {
  29060. const attrName = tNodeAttrs[i];
  29061. // Stop as soon as we hit a marker. We only care about the regular attributes. Everything
  29062. // else will be handled below when we read the final attributes off the DOM.
  29063. if (typeof attrName !== 'string')
  29064. break;
  29065. const attrValue = tNodeAttrs[i + 1];
  29066. attributes[attrName] = attrValue;
  29067. lowercaseTNodeAttrs.push(attrName.toLowerCase());
  29068. i += 2;
  29069. }
  29070. }
  29071. for (const attr of element.attributes) {
  29072. // Make sure that we don't assign the same attribute both in its
  29073. // case-sensitive form and the lower-cased one from the browser.
  29074. if (!lowercaseTNodeAttrs.includes(attr.name)) {
  29075. attributes[attr.name] = attr.value;
  29076. }
  29077. }
  29078. return attributes;
  29079. }
  29080. /**
  29081. * The inline styles of the DOM element.
  29082. *
  29083. * Will be `null` if there is no `style` property on the underlying DOM element.
  29084. *
  29085. * @see [ElementCSSInlineStyle](https://developer.mozilla.org/en-US/docs/Web/API/ElementCSSInlineStyle/style)
  29086. */
  29087. get styles() {
  29088. if (this.nativeElement && this.nativeElement.style) {
  29089. return this.nativeElement.style;
  29090. }
  29091. return {};
  29092. }
  29093. /**
  29094. * A map containing the class names on the element as keys.
  29095. *
  29096. * This map is derived from the `className` property of the DOM element.
  29097. *
  29098. * Note: The values of this object will always be `true`. The class key will not appear in the KV
  29099. * object if it does not exist on the element.
  29100. *
  29101. * @see [Element.className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className)
  29102. */
  29103. get classes() {
  29104. const result = {};
  29105. const element = this.nativeElement;
  29106. // SVG elements return an `SVGAnimatedString` instead of a plain string for the `className`.
  29107. const className = element.className;
  29108. const classes = typeof className !== 'string' ? className.baseVal.split(' ') : className.split(' ');
  29109. classes.forEach((value) => result[value] = true);
  29110. return result;
  29111. }
  29112. /**
  29113. * The `childNodes` of the DOM element as a `DebugNode` array.
  29114. *
  29115. * @see [Node.childNodes](https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes)
  29116. */
  29117. get childNodes() {
  29118. const childNodes = this.nativeNode.childNodes;
  29119. const children = [];
  29120. for (let i = 0; i < childNodes.length; i++) {
  29121. const element = childNodes[i];
  29122. children.push(getDebugNode(element));
  29123. }
  29124. return children;
  29125. }
  29126. /**
  29127. * The immediate `DebugElement` children. Walk the tree by descending through `children`.
  29128. */
  29129. get children() {
  29130. const nativeElement = this.nativeElement;
  29131. if (!nativeElement)
  29132. return [];
  29133. const childNodes = nativeElement.children;
  29134. const children = [];
  29135. for (let i = 0; i < childNodes.length; i++) {
  29136. const element = childNodes[i];
  29137. children.push(getDebugNode(element));
  29138. }
  29139. return children;
  29140. }
  29141. /**
  29142. * @returns the first `DebugElement` that matches the predicate at any depth in the subtree.
  29143. */
  29144. query(predicate) {
  29145. const results = this.queryAll(predicate);
  29146. return results[0] || null;
  29147. }
  29148. /**
  29149. * @returns All `DebugElement` matches for the predicate at any depth in the subtree.
  29150. */
  29151. queryAll(predicate) {
  29152. const matches = [];
  29153. _queryAll(this, predicate, matches, true);
  29154. return matches;
  29155. }
  29156. /**
  29157. * @returns All `DebugNode` matches for the predicate at any depth in the subtree.
  29158. */
  29159. queryAllNodes(predicate) {
  29160. const matches = [];
  29161. _queryAll(this, predicate, matches, false);
  29162. return matches;
  29163. }
  29164. /**
  29165. * Triggers the event by its name if there is a corresponding listener in the element's
  29166. * `listeners` collection.
  29167. *
  29168. * If the event lacks a listener or there's some other problem, consider
  29169. * calling `nativeElement.dispatchEvent(eventObject)`.
  29170. *
  29171. * @param eventName The name of the event to trigger
  29172. * @param eventObj The _event object_ expected by the handler
  29173. *
  29174. * @see [Testing components scenarios](guide/testing-components-scenarios#trigger-event-handler)
  29175. */
  29176. triggerEventHandler(eventName, eventObj) {
  29177. const node = this.nativeNode;
  29178. const invokedListeners = [];
  29179. this.listeners.forEach(listener => {
  29180. if (listener.name === eventName) {
  29181. const callback = listener.callback;
  29182. callback.call(node, eventObj);
  29183. invokedListeners.push(callback);
  29184. }
  29185. });
  29186. // We need to check whether `eventListeners` exists, because it's something
  29187. // that Zone.js only adds to `EventTarget` in browser environments.
  29188. if (typeof node.eventListeners === 'function') {
  29189. // Note that in Ivy we wrap event listeners with a call to `event.preventDefault` in some
  29190. // cases. We use '__ngUnwrap__' as a special token that gives us access to the actual event
  29191. // listener.
  29192. node.eventListeners(eventName).forEach((listener) => {
  29193. // In order to ensure that we can detect the special __ngUnwrap__ token described above, we
  29194. // use `toString` on the listener and see if it contains the token. We use this approach to
  29195. // ensure that it still worked with compiled code since it cannot remove or rename string
  29196. // literals. We also considered using a special function name (i.e. if(listener.name ===
  29197. // special)) but that was more cumbersome and we were also concerned the compiled code could
  29198. // strip the name, turning the condition in to ("" === "") and always returning true.
  29199. if (listener.toString().indexOf('__ngUnwrap__') !== -1) {
  29200. const unwrappedListener = listener('__ngUnwrap__');
  29201. return invokedListeners.indexOf(unwrappedListener) === -1 &&
  29202. unwrappedListener.call(node, eventObj);
  29203. }
  29204. });
  29205. }
  29206. }
  29207. }
  29208. function copyDomProperties(element, properties) {
  29209. if (element) {
  29210. // Skip own properties (as those are patched)
  29211. let obj = Object.getPrototypeOf(element);
  29212. const NodePrototype = Node.prototype;
  29213. while (obj !== null && obj !== NodePrototype) {
  29214. const descriptors = Object.getOwnPropertyDescriptors(obj);
  29215. for (let key in descriptors) {
  29216. if (!key.startsWith('__') && !key.startsWith('on')) {
  29217. // don't include properties starting with `__` and `on`.
  29218. // `__` are patched values which should not be included.
  29219. // `on` are listeners which also should not be included.
  29220. const value = element[key];
  29221. if (isPrimitiveValue(value)) {
  29222. properties[key] = value;
  29223. }
  29224. }
  29225. }
  29226. obj = Object.getPrototypeOf(obj);
  29227. }
  29228. }
  29229. }
  29230. function isPrimitiveValue(value) {
  29231. return typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number' ||
  29232. value === null;
  29233. }
  29234. function _queryAll(parentElement, predicate, matches, elementsOnly) {
  29235. const context = getLContext(parentElement.nativeNode);
  29236. const lView = context ? context.lView : null;
  29237. if (lView !== null) {
  29238. const parentTNode = lView[TVIEW].data[context.nodeIndex];
  29239. _queryNodeChildren(parentTNode, lView, predicate, matches, elementsOnly, parentElement.nativeNode);
  29240. }
  29241. else {
  29242. // If the context is null, then `parentElement` was either created with Renderer2 or native DOM
  29243. // APIs.
  29244. _queryNativeNodeDescendants(parentElement.nativeNode, predicate, matches, elementsOnly);
  29245. }
  29246. }
  29247. /**
  29248. * Recursively match the current TNode against the predicate, and goes on with the next ones.
  29249. *
  29250. * @param tNode the current TNode
  29251. * @param lView the LView of this TNode
  29252. * @param predicate the predicate to match
  29253. * @param matches the list of positive matches
  29254. * @param elementsOnly whether only elements should be searched
  29255. * @param rootNativeNode the root native node on which predicate should not be matched
  29256. */
  29257. function _queryNodeChildren(tNode, lView, predicate, matches, elementsOnly, rootNativeNode) {
  29258. ngDevMode && assertTNodeForLView(tNode, lView);
  29259. const nativeNode = getNativeByTNodeOrNull(tNode, lView);
  29260. // For each type of TNode, specific logic is executed.
  29261. if (tNode.type & (3 /* TNodeType.AnyRNode */ | 8 /* TNodeType.ElementContainer */)) {
  29262. // Case 1: the TNode is an element
  29263. // The native node has to be checked.
  29264. _addQueryMatch(nativeNode, predicate, matches, elementsOnly, rootNativeNode);
  29265. if (isComponentHost(tNode)) {
  29266. // If the element is the host of a component, then all nodes in its view have to be processed.
  29267. // Note: the component's content (tNode.child) will be processed from the insertion points.
  29268. const componentView = getComponentLViewByIndex(tNode.index, lView);
  29269. if (componentView && componentView[TVIEW].firstChild) {
  29270. _queryNodeChildren(componentView[TVIEW].firstChild, componentView, predicate, matches, elementsOnly, rootNativeNode);
  29271. }
  29272. }
  29273. else {
  29274. if (tNode.child) {
  29275. // Otherwise, its children have to be processed.
  29276. _queryNodeChildren(tNode.child, lView, predicate, matches, elementsOnly, rootNativeNode);
  29277. }
  29278. // We also have to query the DOM directly in order to catch elements inserted through
  29279. // Renderer2. Note that this is __not__ optimal, because we're walking similar trees multiple
  29280. // times. ViewEngine could do it more efficiently, because all the insertions go through
  29281. // Renderer2, however that's not the case in Ivy. This approach is being used because:
  29282. // 1. Matching the ViewEngine behavior would mean potentially introducing a dependency
  29283. // from `Renderer2` to Ivy which could bring Ivy code into ViewEngine.
  29284. // 2. It allows us to capture nodes that were inserted directly via the DOM.
  29285. nativeNode && _queryNativeNodeDescendants(nativeNode, predicate, matches, elementsOnly);
  29286. }
  29287. // In all cases, if a dynamic container exists for this node, each view inside it has to be
  29288. // processed.
  29289. const nodeOrContainer = lView[tNode.index];
  29290. if (isLContainer(nodeOrContainer)) {
  29291. _queryNodeChildrenInContainer(nodeOrContainer, predicate, matches, elementsOnly, rootNativeNode);
  29292. }
  29293. }
  29294. else if (tNode.type & 4 /* TNodeType.Container */) {
  29295. // Case 2: the TNode is a container
  29296. // The native node has to be checked.
  29297. const lContainer = lView[tNode.index];
  29298. _addQueryMatch(lContainer[NATIVE], predicate, matches, elementsOnly, rootNativeNode);
  29299. // Each view inside the container has to be processed.
  29300. _queryNodeChildrenInContainer(lContainer, predicate, matches, elementsOnly, rootNativeNode);
  29301. }
  29302. else if (tNode.type & 16 /* TNodeType.Projection */) {
  29303. // Case 3: the TNode is a projection insertion point (i.e. a <ng-content>).
  29304. // The nodes projected at this location all need to be processed.
  29305. const componentView = lView[DECLARATION_COMPONENT_VIEW];
  29306. const componentHost = componentView[T_HOST];
  29307. const head = componentHost.projection[tNode.projection];
  29308. if (Array.isArray(head)) {
  29309. for (let nativeNode of head) {
  29310. _addQueryMatch(nativeNode, predicate, matches, elementsOnly, rootNativeNode);
  29311. }
  29312. }
  29313. else if (head) {
  29314. const nextLView = componentView[PARENT];
  29315. const nextTNode = nextLView[TVIEW].data[head.index];
  29316. _queryNodeChildren(nextTNode, nextLView, predicate, matches, elementsOnly, rootNativeNode);
  29317. }
  29318. }
  29319. else if (tNode.child) {
  29320. // Case 4: the TNode is a view.
  29321. _queryNodeChildren(tNode.child, lView, predicate, matches, elementsOnly, rootNativeNode);
  29322. }
  29323. // We don't want to go to the next sibling of the root node.
  29324. if (rootNativeNode !== nativeNode) {
  29325. // To determine the next node to be processed, we need to use the next or the projectionNext
  29326. // link, depending on whether the current node has been projected.
  29327. const nextTNode = (tNode.flags & 2 /* TNodeFlags.isProjected */) ? tNode.projectionNext : tNode.next;
  29328. if (nextTNode) {
  29329. _queryNodeChildren(nextTNode, lView, predicate, matches, elementsOnly, rootNativeNode);
  29330. }
  29331. }
  29332. }
  29333. /**
  29334. * Process all TNodes in a given container.
  29335. *
  29336. * @param lContainer the container to be processed
  29337. * @param predicate the predicate to match
  29338. * @param matches the list of positive matches
  29339. * @param elementsOnly whether only elements should be searched
  29340. * @param rootNativeNode the root native node on which predicate should not be matched
  29341. */
  29342. function _queryNodeChildrenInContainer(lContainer, predicate, matches, elementsOnly, rootNativeNode) {
  29343. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  29344. const childView = lContainer[i];
  29345. const firstChild = childView[TVIEW].firstChild;
  29346. if (firstChild) {
  29347. _queryNodeChildren(firstChild, childView, predicate, matches, elementsOnly, rootNativeNode);
  29348. }
  29349. }
  29350. }
  29351. /**
  29352. * Match the current native node against the predicate.
  29353. *
  29354. * @param nativeNode the current native node
  29355. * @param predicate the predicate to match
  29356. * @param matches the list of positive matches
  29357. * @param elementsOnly whether only elements should be searched
  29358. * @param rootNativeNode the root native node on which predicate should not be matched
  29359. */
  29360. function _addQueryMatch(nativeNode, predicate, matches, elementsOnly, rootNativeNode) {
  29361. if (rootNativeNode !== nativeNode) {
  29362. const debugNode = getDebugNode(nativeNode);
  29363. if (!debugNode) {
  29364. return;
  29365. }
  29366. // Type of the "predicate and "matches" array are set based on the value of
  29367. // the "elementsOnly" parameter. TypeScript is not able to properly infer these
  29368. // types with generics, so we manually cast the parameters accordingly.
  29369. if (elementsOnly && (debugNode instanceof DebugElement) && predicate(debugNode) &&
  29370. matches.indexOf(debugNode) === -1) {
  29371. matches.push(debugNode);
  29372. }
  29373. else if (!elementsOnly && predicate(debugNode) &&
  29374. matches.indexOf(debugNode) === -1) {
  29375. matches.push(debugNode);
  29376. }
  29377. }
  29378. }
  29379. /**
  29380. * Match all the descendants of a DOM node against a predicate.
  29381. *
  29382. * @param nativeNode the current native node
  29383. * @param predicate the predicate to match
  29384. * @param matches the list where matches are stored
  29385. * @param elementsOnly whether only elements should be searched
  29386. */
  29387. function _queryNativeNodeDescendants(parentNode, predicate, matches, elementsOnly) {
  29388. const nodes = parentNode.childNodes;
  29389. const length = nodes.length;
  29390. for (let i = 0; i < length; i++) {
  29391. const node = nodes[i];
  29392. const debugNode = getDebugNode(node);
  29393. if (debugNode) {
  29394. if (elementsOnly && (debugNode instanceof DebugElement) && predicate(debugNode) &&
  29395. matches.indexOf(debugNode) === -1) {
  29396. matches.push(debugNode);
  29397. }
  29398. else if (!elementsOnly && predicate(debugNode) &&
  29399. matches.indexOf(debugNode) === -1) {
  29400. matches.push(debugNode);
  29401. }
  29402. _queryNativeNodeDescendants(node, predicate, matches, elementsOnly);
  29403. }
  29404. }
  29405. }
  29406. /**
  29407. * Iterates through the property bindings for a given node and generates
  29408. * a map of property names to values. This map only contains property bindings
  29409. * defined in templates, not in host bindings.
  29410. */
  29411. function collectPropertyBindings(properties, tNode, lView, tData) {
  29412. let bindingIndexes = tNode.propertyBindings;
  29413. if (bindingIndexes !== null) {
  29414. for (let i = 0; i < bindingIndexes.length; i++) {
  29415. const bindingIndex = bindingIndexes[i];
  29416. const propMetadata = tData[bindingIndex];
  29417. const metadataParts = propMetadata.split(INTERPOLATION_DELIMITER);
  29418. const propertyName = metadataParts[0];
  29419. if (metadataParts.length > 1) {
  29420. let value = metadataParts[1];
  29421. for (let j = 1; j < metadataParts.length - 1; j++) {
  29422. value += renderStringify(lView[bindingIndex + j - 1]) + metadataParts[j + 1];
  29423. }
  29424. properties[propertyName] = value;
  29425. }
  29426. else {
  29427. properties[propertyName] = lView[bindingIndex];
  29428. }
  29429. }
  29430. }
  29431. }
  29432. // Need to keep the nodes in a global Map so that multiple angular apps are supported.
  29433. const _nativeNodeToDebugNode = new Map();
  29434. const NG_DEBUG_PROPERTY = '__ng_debug__';
  29435. /**
  29436. * @publicApi
  29437. */
  29438. function getDebugNode(nativeNode) {
  29439. if (nativeNode instanceof Node) {
  29440. if (!(nativeNode.hasOwnProperty(NG_DEBUG_PROPERTY))) {
  29441. nativeNode[NG_DEBUG_PROPERTY] = nativeNode.nodeType == Node.ELEMENT_NODE ?
  29442. new DebugElement(nativeNode) :
  29443. new DebugNode(nativeNode);
  29444. }
  29445. return nativeNode[NG_DEBUG_PROPERTY];
  29446. }
  29447. return null;
  29448. }
  29449. function getAllDebugNodes() {
  29450. return Array.from(_nativeNodeToDebugNode.values());
  29451. }
  29452. function indexDebugNode(node) {
  29453. _nativeNodeToDebugNode.set(node.nativeNode, node);
  29454. }
  29455. function removeDebugNodeFromIndex(node) {
  29456. _nativeNodeToDebugNode.delete(node.nativeNode);
  29457. }
  29458. class DefaultIterableDifferFactory {
  29459. constructor() { }
  29460. supports(obj) {
  29461. return isListLikeIterable(obj);
  29462. }
  29463. create(trackByFn) {
  29464. return new DefaultIterableDiffer(trackByFn);
  29465. }
  29466. }
  29467. const trackByIdentity = (index, item) => item;
  29468. /**
  29469. * @deprecated v4.0.0 - Should not be part of public API.
  29470. * @publicApi
  29471. */
  29472. class DefaultIterableDiffer {
  29473. constructor(trackByFn) {
  29474. this.length = 0;
  29475. // Keeps track of the used records at any point in time (during & across `_check()` calls)
  29476. this._linkedRecords = null;
  29477. // Keeps track of the removed records at any point in time during `_check()` calls.
  29478. this._unlinkedRecords = null;
  29479. this._previousItHead = null;
  29480. this._itHead = null;
  29481. this._itTail = null;
  29482. this._additionsHead = null;
  29483. this._additionsTail = null;
  29484. this._movesHead = null;
  29485. this._movesTail = null;
  29486. this._removalsHead = null;
  29487. this._removalsTail = null;
  29488. // Keeps track of records where custom track by is the same, but item identity has changed
  29489. this._identityChangesHead = null;
  29490. this._identityChangesTail = null;
  29491. this._trackByFn = trackByFn || trackByIdentity;
  29492. }
  29493. forEachItem(fn) {
  29494. let record;
  29495. for (record = this._itHead; record !== null; record = record._next) {
  29496. fn(record);
  29497. }
  29498. }
  29499. forEachOperation(fn) {
  29500. let nextIt = this._itHead;
  29501. let nextRemove = this._removalsHead;
  29502. let addRemoveOffset = 0;
  29503. let moveOffsets = null;
  29504. while (nextIt || nextRemove) {
  29505. // Figure out which is the next record to process
  29506. // Order: remove, add, move
  29507. const record = !nextRemove ||
  29508. nextIt &&
  29509. nextIt.currentIndex <
  29510. getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ?
  29511. nextIt :
  29512. nextRemove;
  29513. const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets);
  29514. const currentIndex = record.currentIndex;
  29515. // consume the item, and adjust the addRemoveOffset and update moveDistance if necessary
  29516. if (record === nextRemove) {
  29517. addRemoveOffset--;
  29518. nextRemove = nextRemove._nextRemoved;
  29519. }
  29520. else {
  29521. nextIt = nextIt._next;
  29522. if (record.previousIndex == null) {
  29523. addRemoveOffset++;
  29524. }
  29525. else {
  29526. // INVARIANT: currentIndex < previousIndex
  29527. if (!moveOffsets)
  29528. moveOffsets = [];
  29529. const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset;
  29530. const localCurrentIndex = currentIndex - addRemoveOffset;
  29531. if (localMovePreviousIndex != localCurrentIndex) {
  29532. for (let i = 0; i < localMovePreviousIndex; i++) {
  29533. const offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0);
  29534. const index = offset + i;
  29535. if (localCurrentIndex <= index && index < localMovePreviousIndex) {
  29536. moveOffsets[i] = offset + 1;
  29537. }
  29538. }
  29539. const previousIndex = record.previousIndex;
  29540. moveOffsets[previousIndex] = localCurrentIndex - localMovePreviousIndex;
  29541. }
  29542. }
  29543. }
  29544. if (adjPreviousIndex !== currentIndex) {
  29545. fn(record, adjPreviousIndex, currentIndex);
  29546. }
  29547. }
  29548. }
  29549. forEachPreviousItem(fn) {
  29550. let record;
  29551. for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
  29552. fn(record);
  29553. }
  29554. }
  29555. forEachAddedItem(fn) {
  29556. let record;
  29557. for (record = this._additionsHead; record !== null; record = record._nextAdded) {
  29558. fn(record);
  29559. }
  29560. }
  29561. forEachMovedItem(fn) {
  29562. let record;
  29563. for (record = this._movesHead; record !== null; record = record._nextMoved) {
  29564. fn(record);
  29565. }
  29566. }
  29567. forEachRemovedItem(fn) {
  29568. let record;
  29569. for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
  29570. fn(record);
  29571. }
  29572. }
  29573. forEachIdentityChange(fn) {
  29574. let record;
  29575. for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
  29576. fn(record);
  29577. }
  29578. }
  29579. diff(collection) {
  29580. if (collection == null)
  29581. collection = [];
  29582. if (!isListLikeIterable(collection)) {
  29583. throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode &&
  29584. `Error trying to diff '${stringify(collection)}'. Only arrays and iterables are allowed`);
  29585. }
  29586. if (this.check(collection)) {
  29587. return this;
  29588. }
  29589. else {
  29590. return null;
  29591. }
  29592. }
  29593. onDestroy() { }
  29594. check(collection) {
  29595. this._reset();
  29596. let record = this._itHead;
  29597. let mayBeDirty = false;
  29598. let index;
  29599. let item;
  29600. let itemTrackBy;
  29601. if (Array.isArray(collection)) {
  29602. this.length = collection.length;
  29603. for (let index = 0; index < this.length; index++) {
  29604. item = collection[index];
  29605. itemTrackBy = this._trackByFn(index, item);
  29606. if (record === null || !Object.is(record.trackById, itemTrackBy)) {
  29607. record = this._mismatch(record, item, itemTrackBy, index);
  29608. mayBeDirty = true;
  29609. }
  29610. else {
  29611. if (mayBeDirty) {
  29612. // TODO(misko): can we limit this to duplicates only?
  29613. record = this._verifyReinsertion(record, item, itemTrackBy, index);
  29614. }
  29615. if (!Object.is(record.item, item))
  29616. this._addIdentityChange(record, item);
  29617. }
  29618. record = record._next;
  29619. }
  29620. }
  29621. else {
  29622. index = 0;
  29623. iterateListLike(collection, (item) => {
  29624. itemTrackBy = this._trackByFn(index, item);
  29625. if (record === null || !Object.is(record.trackById, itemTrackBy)) {
  29626. record = this._mismatch(record, item, itemTrackBy, index);
  29627. mayBeDirty = true;
  29628. }
  29629. else {
  29630. if (mayBeDirty) {
  29631. // TODO(misko): can we limit this to duplicates only?
  29632. record = this._verifyReinsertion(record, item, itemTrackBy, index);
  29633. }
  29634. if (!Object.is(record.item, item))
  29635. this._addIdentityChange(record, item);
  29636. }
  29637. record = record._next;
  29638. index++;
  29639. });
  29640. this.length = index;
  29641. }
  29642. this._truncate(record);
  29643. // @ts-expect-error overwriting a readonly member
  29644. this.collection = collection;
  29645. return this.isDirty;
  29646. }
  29647. /* CollectionChanges is considered dirty if it has any additions, moves, removals, or identity
  29648. * changes.
  29649. */
  29650. get isDirty() {
  29651. return this._additionsHead !== null || this._movesHead !== null ||
  29652. this._removalsHead !== null || this._identityChangesHead !== null;
  29653. }
  29654. /**
  29655. * Reset the state of the change objects to show no changes. This means set previousKey to
  29656. * currentKey, and clear all of the queues (additions, moves, removals).
  29657. * Set the previousIndexes of moved and added items to their currentIndexes
  29658. * Reset the list of additions, moves and removals
  29659. *
  29660. * @internal
  29661. */
  29662. _reset() {
  29663. if (this.isDirty) {
  29664. let record;
  29665. for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
  29666. record._nextPrevious = record._next;
  29667. }
  29668. for (record = this._additionsHead; record !== null; record = record._nextAdded) {
  29669. record.previousIndex = record.currentIndex;
  29670. }
  29671. this._additionsHead = this._additionsTail = null;
  29672. for (record = this._movesHead; record !== null; record = record._nextMoved) {
  29673. record.previousIndex = record.currentIndex;
  29674. }
  29675. this._movesHead = this._movesTail = null;
  29676. this._removalsHead = this._removalsTail = null;
  29677. this._identityChangesHead = this._identityChangesTail = null;
  29678. // TODO(vicb): when assert gets supported
  29679. // assert(!this.isDirty);
  29680. }
  29681. }
  29682. /**
  29683. * This is the core function which handles differences between collections.
  29684. *
  29685. * - `record` is the record which we saw at this position last time. If null then it is a new
  29686. * item.
  29687. * - `item` is the current item in the collection
  29688. * - `index` is the position of the item in the collection
  29689. *
  29690. * @internal
  29691. */
  29692. _mismatch(record, item, itemTrackBy, index) {
  29693. // The previous record after which we will append the current one.
  29694. let previousRecord;
  29695. if (record === null) {
  29696. previousRecord = this._itTail;
  29697. }
  29698. else {
  29699. previousRecord = record._prev;
  29700. // Remove the record from the collection since we know it does not match the item.
  29701. this._remove(record);
  29702. }
  29703. // See if we have evicted the item, which used to be at some anterior position of _itHead list.
  29704. record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
  29705. if (record !== null) {
  29706. // It is an item which we have evicted earlier: reinsert it back into the list.
  29707. // But first we need to check if identity changed, so we can update in view if necessary.
  29708. if (!Object.is(record.item, item))
  29709. this._addIdentityChange(record, item);
  29710. this._reinsertAfter(record, previousRecord, index);
  29711. }
  29712. else {
  29713. // Attempt to see if the item is at some posterior position of _itHead list.
  29714. record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index);
  29715. if (record !== null) {
  29716. // We have the item in _itHead at/after `index` position. We need to move it forward in the
  29717. // collection.
  29718. // But first we need to check if identity changed, so we can update in view if necessary.
  29719. if (!Object.is(record.item, item))
  29720. this._addIdentityChange(record, item);
  29721. this._moveAfter(record, previousRecord, index);
  29722. }
  29723. else {
  29724. // It is a new item: add it.
  29725. record =
  29726. this._addAfter(new IterableChangeRecord_(item, itemTrackBy), previousRecord, index);
  29727. }
  29728. }
  29729. return record;
  29730. }
  29731. /**
  29732. * This check is only needed if an array contains duplicates. (Short circuit of nothing dirty)
  29733. *
  29734. * Use case: `[a, a]` => `[b, a, a]`
  29735. *
  29736. * If we did not have this check then the insertion of `b` would:
  29737. * 1) evict first `a`
  29738. * 2) insert `b` at `0` index.
  29739. * 3) leave `a` at index `1` as is. <-- this is wrong!
  29740. * 3) reinsert `a` at index 2. <-- this is wrong!
  29741. *
  29742. * The correct behavior is:
  29743. * 1) evict first `a`
  29744. * 2) insert `b` at `0` index.
  29745. * 3) reinsert `a` at index 1.
  29746. * 3) move `a` at from `1` to `2`.
  29747. *
  29748. *
  29749. * Double check that we have not evicted a duplicate item. We need to check if the item type may
  29750. * have already been removed:
  29751. * The insertion of b will evict the first 'a'. If we don't reinsert it now it will be reinserted
  29752. * at the end. Which will show up as the two 'a's switching position. This is incorrect, since a
  29753. * better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
  29754. * at the end.
  29755. *
  29756. * @internal
  29757. */
  29758. _verifyReinsertion(record, item, itemTrackBy, index) {
  29759. let reinsertRecord = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
  29760. if (reinsertRecord !== null) {
  29761. record = this._reinsertAfter(reinsertRecord, record._prev, index);
  29762. }
  29763. else if (record.currentIndex != index) {
  29764. record.currentIndex = index;
  29765. this._addToMoves(record, index);
  29766. }
  29767. return record;
  29768. }
  29769. /**
  29770. * Get rid of any excess {@link IterableChangeRecord_}s from the previous collection
  29771. *
  29772. * - `record` The first excess {@link IterableChangeRecord_}.
  29773. *
  29774. * @internal
  29775. */
  29776. _truncate(record) {
  29777. // Anything after that needs to be removed;
  29778. while (record !== null) {
  29779. const nextRecord = record._next;
  29780. this._addToRemovals(this._unlink(record));
  29781. record = nextRecord;
  29782. }
  29783. if (this._unlinkedRecords !== null) {
  29784. this._unlinkedRecords.clear();
  29785. }
  29786. if (this._additionsTail !== null) {
  29787. this._additionsTail._nextAdded = null;
  29788. }
  29789. if (this._movesTail !== null) {
  29790. this._movesTail._nextMoved = null;
  29791. }
  29792. if (this._itTail !== null) {
  29793. this._itTail._next = null;
  29794. }
  29795. if (this._removalsTail !== null) {
  29796. this._removalsTail._nextRemoved = null;
  29797. }
  29798. if (this._identityChangesTail !== null) {
  29799. this._identityChangesTail._nextIdentityChange = null;
  29800. }
  29801. }
  29802. /** @internal */
  29803. _reinsertAfter(record, prevRecord, index) {
  29804. if (this._unlinkedRecords !== null) {
  29805. this._unlinkedRecords.remove(record);
  29806. }
  29807. const prev = record._prevRemoved;
  29808. const next = record._nextRemoved;
  29809. if (prev === null) {
  29810. this._removalsHead = next;
  29811. }
  29812. else {
  29813. prev._nextRemoved = next;
  29814. }
  29815. if (next === null) {
  29816. this._removalsTail = prev;
  29817. }
  29818. else {
  29819. next._prevRemoved = prev;
  29820. }
  29821. this._insertAfter(record, prevRecord, index);
  29822. this._addToMoves(record, index);
  29823. return record;
  29824. }
  29825. /** @internal */
  29826. _moveAfter(record, prevRecord, index) {
  29827. this._unlink(record);
  29828. this._insertAfter(record, prevRecord, index);
  29829. this._addToMoves(record, index);
  29830. return record;
  29831. }
  29832. /** @internal */
  29833. _addAfter(record, prevRecord, index) {
  29834. this._insertAfter(record, prevRecord, index);
  29835. if (this._additionsTail === null) {
  29836. // TODO(vicb):
  29837. // assert(this._additionsHead === null);
  29838. this._additionsTail = this._additionsHead = record;
  29839. }
  29840. else {
  29841. // TODO(vicb):
  29842. // assert(_additionsTail._nextAdded === null);
  29843. // assert(record._nextAdded === null);
  29844. this._additionsTail = this._additionsTail._nextAdded = record;
  29845. }
  29846. return record;
  29847. }
  29848. /** @internal */
  29849. _insertAfter(record, prevRecord, index) {
  29850. // TODO(vicb):
  29851. // assert(record != prevRecord);
  29852. // assert(record._next === null);
  29853. // assert(record._prev === null);
  29854. const next = prevRecord === null ? this._itHead : prevRecord._next;
  29855. // TODO(vicb):
  29856. // assert(next != record);
  29857. // assert(prevRecord != record);
  29858. record._next = next;
  29859. record._prev = prevRecord;
  29860. if (next === null) {
  29861. this._itTail = record;
  29862. }
  29863. else {
  29864. next._prev = record;
  29865. }
  29866. if (prevRecord === null) {
  29867. this._itHead = record;
  29868. }
  29869. else {
  29870. prevRecord._next = record;
  29871. }
  29872. if (this._linkedRecords === null) {
  29873. this._linkedRecords = new _DuplicateMap();
  29874. }
  29875. this._linkedRecords.put(record);
  29876. record.currentIndex = index;
  29877. return record;
  29878. }
  29879. /** @internal */
  29880. _remove(record) {
  29881. return this._addToRemovals(this._unlink(record));
  29882. }
  29883. /** @internal */
  29884. _unlink(record) {
  29885. if (this._linkedRecords !== null) {
  29886. this._linkedRecords.remove(record);
  29887. }
  29888. const prev = record._prev;
  29889. const next = record._next;
  29890. // TODO(vicb):
  29891. // assert((record._prev = null) === null);
  29892. // assert((record._next = null) === null);
  29893. if (prev === null) {
  29894. this._itHead = next;
  29895. }
  29896. else {
  29897. prev._next = next;
  29898. }
  29899. if (next === null) {
  29900. this._itTail = prev;
  29901. }
  29902. else {
  29903. next._prev = prev;
  29904. }
  29905. return record;
  29906. }
  29907. /** @internal */
  29908. _addToMoves(record, toIndex) {
  29909. // TODO(vicb):
  29910. // assert(record._nextMoved === null);
  29911. if (record.previousIndex === toIndex) {
  29912. return record;
  29913. }
  29914. if (this._movesTail === null) {
  29915. // TODO(vicb):
  29916. // assert(_movesHead === null);
  29917. this._movesTail = this._movesHead = record;
  29918. }
  29919. else {
  29920. // TODO(vicb):
  29921. // assert(_movesTail._nextMoved === null);
  29922. this._movesTail = this._movesTail._nextMoved = record;
  29923. }
  29924. return record;
  29925. }
  29926. _addToRemovals(record) {
  29927. if (this._unlinkedRecords === null) {
  29928. this._unlinkedRecords = new _DuplicateMap();
  29929. }
  29930. this._unlinkedRecords.put(record);
  29931. record.currentIndex = null;
  29932. record._nextRemoved = null;
  29933. if (this._removalsTail === null) {
  29934. // TODO(vicb):
  29935. // assert(_removalsHead === null);
  29936. this._removalsTail = this._removalsHead = record;
  29937. record._prevRemoved = null;
  29938. }
  29939. else {
  29940. // TODO(vicb):
  29941. // assert(_removalsTail._nextRemoved === null);
  29942. // assert(record._nextRemoved === null);
  29943. record._prevRemoved = this._removalsTail;
  29944. this._removalsTail = this._removalsTail._nextRemoved = record;
  29945. }
  29946. return record;
  29947. }
  29948. /** @internal */
  29949. _addIdentityChange(record, item) {
  29950. record.item = item;
  29951. if (this._identityChangesTail === null) {
  29952. this._identityChangesTail = this._identityChangesHead = record;
  29953. }
  29954. else {
  29955. this._identityChangesTail = this._identityChangesTail._nextIdentityChange = record;
  29956. }
  29957. return record;
  29958. }
  29959. }
  29960. class IterableChangeRecord_ {
  29961. constructor(item, trackById) {
  29962. this.item = item;
  29963. this.trackById = trackById;
  29964. this.currentIndex = null;
  29965. this.previousIndex = null;
  29966. /** @internal */
  29967. this._nextPrevious = null;
  29968. /** @internal */
  29969. this._prev = null;
  29970. /** @internal */
  29971. this._next = null;
  29972. /** @internal */
  29973. this._prevDup = null;
  29974. /** @internal */
  29975. this._nextDup = null;
  29976. /** @internal */
  29977. this._prevRemoved = null;
  29978. /** @internal */
  29979. this._nextRemoved = null;
  29980. /** @internal */
  29981. this._nextAdded = null;
  29982. /** @internal */
  29983. this._nextMoved = null;
  29984. /** @internal */
  29985. this._nextIdentityChange = null;
  29986. }
  29987. }
  29988. // A linked list of IterableChangeRecords with the same IterableChangeRecord_.item
  29989. class _DuplicateItemRecordList {
  29990. constructor() {
  29991. /** @internal */
  29992. this._head = null;
  29993. /** @internal */
  29994. this._tail = null;
  29995. }
  29996. /**
  29997. * Append the record to the list of duplicates.
  29998. *
  29999. * Note: by design all records in the list of duplicates hold the same value in record.item.
  30000. */
  30001. add(record) {
  30002. if (this._head === null) {
  30003. this._head = this._tail = record;
  30004. record._nextDup = null;
  30005. record._prevDup = null;
  30006. }
  30007. else {
  30008. // TODO(vicb):
  30009. // assert(record.item == _head.item ||
  30010. // record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
  30011. this._tail._nextDup = record;
  30012. record._prevDup = this._tail;
  30013. record._nextDup = null;
  30014. this._tail = record;
  30015. }
  30016. }
  30017. // Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and
  30018. // IterableChangeRecord_.currentIndex >= atOrAfterIndex
  30019. get(trackById, atOrAfterIndex) {
  30020. let record;
  30021. for (record = this._head; record !== null; record = record._nextDup) {
  30022. if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex) &&
  30023. Object.is(record.trackById, trackById)) {
  30024. return record;
  30025. }
  30026. }
  30027. return null;
  30028. }
  30029. /**
  30030. * Remove one {@link IterableChangeRecord_} from the list of duplicates.
  30031. *
  30032. * Returns whether the list of duplicates is empty.
  30033. */
  30034. remove(record) {
  30035. // TODO(vicb):
  30036. // assert(() {
  30037. // // verify that the record being removed is in the list.
  30038. // for (IterableChangeRecord_ cursor = _head; cursor != null; cursor = cursor._nextDup) {
  30039. // if (identical(cursor, record)) return true;
  30040. // }
  30041. // return false;
  30042. //});
  30043. const prev = record._prevDup;
  30044. const next = record._nextDup;
  30045. if (prev === null) {
  30046. this._head = next;
  30047. }
  30048. else {
  30049. prev._nextDup = next;
  30050. }
  30051. if (next === null) {
  30052. this._tail = prev;
  30053. }
  30054. else {
  30055. next._prevDup = prev;
  30056. }
  30057. return this._head === null;
  30058. }
  30059. }
  30060. class _DuplicateMap {
  30061. constructor() {
  30062. this.map = new Map();
  30063. }
  30064. put(record) {
  30065. const key = record.trackById;
  30066. let duplicates = this.map.get(key);
  30067. if (!duplicates) {
  30068. duplicates = new _DuplicateItemRecordList();
  30069. this.map.set(key, duplicates);
  30070. }
  30071. duplicates.add(record);
  30072. }
  30073. /**
  30074. * Retrieve the `value` using key. Because the IterableChangeRecord_ value may be one which we
  30075. * have already iterated over, we use the `atOrAfterIndex` to pretend it is not there.
  30076. *
  30077. * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
  30078. * have any more `a`s needs to return the second `a`.
  30079. */
  30080. get(trackById, atOrAfterIndex) {
  30081. const key = trackById;
  30082. const recordList = this.map.get(key);
  30083. return recordList ? recordList.get(trackById, atOrAfterIndex) : null;
  30084. }
  30085. /**
  30086. * Removes a {@link IterableChangeRecord_} from the list of duplicates.
  30087. *
  30088. * The list of duplicates also is removed from the map if it gets empty.
  30089. */
  30090. remove(record) {
  30091. const key = record.trackById;
  30092. const recordList = this.map.get(key);
  30093. // Remove the list of duplicates when it gets empty
  30094. if (recordList.remove(record)) {
  30095. this.map.delete(key);
  30096. }
  30097. return record;
  30098. }
  30099. get isEmpty() {
  30100. return this.map.size === 0;
  30101. }
  30102. clear() {
  30103. this.map.clear();
  30104. }
  30105. }
  30106. function getPreviousIndex(item, addRemoveOffset, moveOffsets) {
  30107. const previousIndex = item.previousIndex;
  30108. if (previousIndex === null)
  30109. return previousIndex;
  30110. let moveOffset = 0;
  30111. if (moveOffsets && previousIndex < moveOffsets.length) {
  30112. moveOffset = moveOffsets[previousIndex];
  30113. }
  30114. return previousIndex + addRemoveOffset + moveOffset;
  30115. }
  30116. class DefaultKeyValueDifferFactory {
  30117. constructor() { }
  30118. supports(obj) {
  30119. return obj instanceof Map || isJsObject(obj);
  30120. }
  30121. create() {
  30122. return new DefaultKeyValueDiffer();
  30123. }
  30124. }
  30125. class DefaultKeyValueDiffer {
  30126. constructor() {
  30127. this._records = new Map();
  30128. this._mapHead = null;
  30129. // _appendAfter is used in the check loop
  30130. this._appendAfter = null;
  30131. this._previousMapHead = null;
  30132. this._changesHead = null;
  30133. this._changesTail = null;
  30134. this._additionsHead = null;
  30135. this._additionsTail = null;
  30136. this._removalsHead = null;
  30137. this._removalsTail = null;
  30138. }
  30139. get isDirty() {
  30140. return this._additionsHead !== null || this._changesHead !== null ||
  30141. this._removalsHead !== null;
  30142. }
  30143. forEachItem(fn) {
  30144. let record;
  30145. for (record = this._mapHead; record !== null; record = record._next) {
  30146. fn(record);
  30147. }
  30148. }
  30149. forEachPreviousItem(fn) {
  30150. let record;
  30151. for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
  30152. fn(record);
  30153. }
  30154. }
  30155. forEachChangedItem(fn) {
  30156. let record;
  30157. for (record = this._changesHead; record !== null; record = record._nextChanged) {
  30158. fn(record);
  30159. }
  30160. }
  30161. forEachAddedItem(fn) {
  30162. let record;
  30163. for (record = this._additionsHead; record !== null; record = record._nextAdded) {
  30164. fn(record);
  30165. }
  30166. }
  30167. forEachRemovedItem(fn) {
  30168. let record;
  30169. for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
  30170. fn(record);
  30171. }
  30172. }
  30173. diff(map) {
  30174. if (!map) {
  30175. map = new Map();
  30176. }
  30177. else if (!(map instanceof Map || isJsObject(map))) {
  30178. throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode &&
  30179. `Error trying to diff '${stringify(map)}'. Only maps and objects are allowed`);
  30180. }
  30181. return this.check(map) ? this : null;
  30182. }
  30183. onDestroy() { }
  30184. /**
  30185. * Check the current state of the map vs the previous.
  30186. * The algorithm is optimised for when the keys do no change.
  30187. */
  30188. check(map) {
  30189. this._reset();
  30190. let insertBefore = this._mapHead;
  30191. this._appendAfter = null;
  30192. this._forEach(map, (value, key) => {
  30193. if (insertBefore && insertBefore.key === key) {
  30194. this._maybeAddToChanges(insertBefore, value);
  30195. this._appendAfter = insertBefore;
  30196. insertBefore = insertBefore._next;
  30197. }
  30198. else {
  30199. const record = this._getOrCreateRecordForKey(key, value);
  30200. insertBefore = this._insertBeforeOrAppend(insertBefore, record);
  30201. }
  30202. });
  30203. // Items remaining at the end of the list have been deleted
  30204. if (insertBefore) {
  30205. if (insertBefore._prev) {
  30206. insertBefore._prev._next = null;
  30207. }
  30208. this._removalsHead = insertBefore;
  30209. for (let record = insertBefore; record !== null; record = record._nextRemoved) {
  30210. if (record === this._mapHead) {
  30211. this._mapHead = null;
  30212. }
  30213. this._records.delete(record.key);
  30214. record._nextRemoved = record._next;
  30215. record.previousValue = record.currentValue;
  30216. record.currentValue = null;
  30217. record._prev = null;
  30218. record._next = null;
  30219. }
  30220. }
  30221. // Make sure tails have no next records from previous runs
  30222. if (this._changesTail)
  30223. this._changesTail._nextChanged = null;
  30224. if (this._additionsTail)
  30225. this._additionsTail._nextAdded = null;
  30226. return this.isDirty;
  30227. }
  30228. /**
  30229. * Inserts a record before `before` or append at the end of the list when `before` is null.
  30230. *
  30231. * Notes:
  30232. * - This method appends at `this._appendAfter`,
  30233. * - This method updates `this._appendAfter`,
  30234. * - The return value is the new value for the insertion pointer.
  30235. */
  30236. _insertBeforeOrAppend(before, record) {
  30237. if (before) {
  30238. const prev = before._prev;
  30239. record._next = before;
  30240. record._prev = prev;
  30241. before._prev = record;
  30242. if (prev) {
  30243. prev._next = record;
  30244. }
  30245. if (before === this._mapHead) {
  30246. this._mapHead = record;
  30247. }
  30248. this._appendAfter = before;
  30249. return before;
  30250. }
  30251. if (this._appendAfter) {
  30252. this._appendAfter._next = record;
  30253. record._prev = this._appendAfter;
  30254. }
  30255. else {
  30256. this._mapHead = record;
  30257. }
  30258. this._appendAfter = record;
  30259. return null;
  30260. }
  30261. _getOrCreateRecordForKey(key, value) {
  30262. if (this._records.has(key)) {
  30263. const record = this._records.get(key);
  30264. this._maybeAddToChanges(record, value);
  30265. const prev = record._prev;
  30266. const next = record._next;
  30267. if (prev) {
  30268. prev._next = next;
  30269. }
  30270. if (next) {
  30271. next._prev = prev;
  30272. }
  30273. record._next = null;
  30274. record._prev = null;
  30275. return record;
  30276. }
  30277. const record = new KeyValueChangeRecord_(key);
  30278. this._records.set(key, record);
  30279. record.currentValue = value;
  30280. this._addToAdditions(record);
  30281. return record;
  30282. }
  30283. /** @internal */
  30284. _reset() {
  30285. if (this.isDirty) {
  30286. let record;
  30287. // let `_previousMapHead` contain the state of the map before the changes
  30288. this._previousMapHead = this._mapHead;
  30289. for (record = this._previousMapHead; record !== null; record = record._next) {
  30290. record._nextPrevious = record._next;
  30291. }
  30292. // Update `record.previousValue` with the value of the item before the changes
  30293. // We need to update all changed items (that's those which have been added and changed)
  30294. for (record = this._changesHead; record !== null; record = record._nextChanged) {
  30295. record.previousValue = record.currentValue;
  30296. }
  30297. for (record = this._additionsHead; record != null; record = record._nextAdded) {
  30298. record.previousValue = record.currentValue;
  30299. }
  30300. this._changesHead = this._changesTail = null;
  30301. this._additionsHead = this._additionsTail = null;
  30302. this._removalsHead = null;
  30303. }
  30304. }
  30305. // Add the record or a given key to the list of changes only when the value has actually changed
  30306. _maybeAddToChanges(record, newValue) {
  30307. if (!Object.is(newValue, record.currentValue)) {
  30308. record.previousValue = record.currentValue;
  30309. record.currentValue = newValue;
  30310. this._addToChanges(record);
  30311. }
  30312. }
  30313. _addToAdditions(record) {
  30314. if (this._additionsHead === null) {
  30315. this._additionsHead = this._additionsTail = record;
  30316. }
  30317. else {
  30318. this._additionsTail._nextAdded = record;
  30319. this._additionsTail = record;
  30320. }
  30321. }
  30322. _addToChanges(record) {
  30323. if (this._changesHead === null) {
  30324. this._changesHead = this._changesTail = record;
  30325. }
  30326. else {
  30327. this._changesTail._nextChanged = record;
  30328. this._changesTail = record;
  30329. }
  30330. }
  30331. /** @internal */
  30332. _forEach(obj, fn) {
  30333. if (obj instanceof Map) {
  30334. obj.forEach(fn);
  30335. }
  30336. else {
  30337. Object.keys(obj).forEach(k => fn(obj[k], k));
  30338. }
  30339. }
  30340. }
  30341. class KeyValueChangeRecord_ {
  30342. constructor(key) {
  30343. this.key = key;
  30344. this.previousValue = null;
  30345. this.currentValue = null;
  30346. /** @internal */
  30347. this._nextPrevious = null;
  30348. /** @internal */
  30349. this._next = null;
  30350. /** @internal */
  30351. this._prev = null;
  30352. /** @internal */
  30353. this._nextAdded = null;
  30354. /** @internal */
  30355. this._nextRemoved = null;
  30356. /** @internal */
  30357. this._nextChanged = null;
  30358. }
  30359. }
  30360. function defaultIterableDiffersFactory() {
  30361. return new IterableDiffers([new DefaultIterableDifferFactory()]);
  30362. }
  30363. /**
  30364. * A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
  30365. *
  30366. * @publicApi
  30367. */
  30368. class IterableDiffers {
  30369. /** @nocollapse */
  30370. static { this.ɵprov = ɵɵdefineInjectable({ token: IterableDiffers, providedIn: 'root', factory: defaultIterableDiffersFactory }); }
  30371. constructor(factories) {
  30372. this.factories = factories;
  30373. }
  30374. static create(factories, parent) {
  30375. if (parent != null) {
  30376. const copied = parent.factories.slice();
  30377. factories = factories.concat(copied);
  30378. }
  30379. return new IterableDiffers(factories);
  30380. }
  30381. /**
  30382. * Takes an array of {@link IterableDifferFactory} and returns a provider used to extend the
  30383. * inherited {@link IterableDiffers} instance with the provided factories and return a new
  30384. * {@link IterableDiffers} instance.
  30385. *
  30386. * @usageNotes
  30387. * ### Example
  30388. *
  30389. * The following example shows how to extend an existing list of factories,
  30390. * which will only be applied to the injector for this component and its children.
  30391. * This step is all that's required to make a new {@link IterableDiffer} available.
  30392. *
  30393. * ```
  30394. * @Component({
  30395. * viewProviders: [
  30396. * IterableDiffers.extend([new ImmutableListDiffer()])
  30397. * ]
  30398. * })
  30399. * ```
  30400. */
  30401. static extend(factories) {
  30402. return {
  30403. provide: IterableDiffers,
  30404. useFactory: (parent) => {
  30405. // if parent is null, it means that we are in the root injector and we have just overridden
  30406. // the default injection mechanism for IterableDiffers, in such a case just assume
  30407. // `defaultIterableDiffersFactory`.
  30408. return IterableDiffers.create(factories, parent || defaultIterableDiffersFactory());
  30409. },
  30410. // Dependency technically isn't optional, but we can provide a better error message this way.
  30411. deps: [[IterableDiffers, new SkipSelf(), new Optional()]]
  30412. };
  30413. }
  30414. find(iterable) {
  30415. const factory = this.factories.find(f => f.supports(iterable));
  30416. if (factory != null) {
  30417. return factory;
  30418. }
  30419. else {
  30420. throw new RuntimeError(901 /* RuntimeErrorCode.NO_SUPPORTING_DIFFER_FACTORY */, ngDevMode &&
  30421. `Cannot find a differ supporting object '${iterable}' of type '${getTypeNameForDebugging(iterable)}'`);
  30422. }
  30423. }
  30424. }
  30425. function getTypeNameForDebugging(type) {
  30426. return type['name'] || typeof type;
  30427. }
  30428. function defaultKeyValueDiffersFactory() {
  30429. return new KeyValueDiffers([new DefaultKeyValueDifferFactory()]);
  30430. }
  30431. /**
  30432. * A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
  30433. *
  30434. * @publicApi
  30435. */
  30436. class KeyValueDiffers {
  30437. /** @nocollapse */
  30438. static { this.ɵprov = ɵɵdefineInjectable({ token: KeyValueDiffers, providedIn: 'root', factory: defaultKeyValueDiffersFactory }); }
  30439. constructor(factories) {
  30440. this.factories = factories;
  30441. }
  30442. static create(factories, parent) {
  30443. if (parent) {
  30444. const copied = parent.factories.slice();
  30445. factories = factories.concat(copied);
  30446. }
  30447. return new KeyValueDiffers(factories);
  30448. }
  30449. /**
  30450. * Takes an array of {@link KeyValueDifferFactory} and returns a provider used to extend the
  30451. * inherited {@link KeyValueDiffers} instance with the provided factories and return a new
  30452. * {@link KeyValueDiffers} instance.
  30453. *
  30454. * @usageNotes
  30455. * ### Example
  30456. *
  30457. * The following example shows how to extend an existing list of factories,
  30458. * which will only be applied to the injector for this component and its children.
  30459. * This step is all that's required to make a new {@link KeyValueDiffer} available.
  30460. *
  30461. * ```
  30462. * @Component({
  30463. * viewProviders: [
  30464. * KeyValueDiffers.extend([new ImmutableMapDiffer()])
  30465. * ]
  30466. * })
  30467. * ```
  30468. */
  30469. static extend(factories) {
  30470. return {
  30471. provide: KeyValueDiffers,
  30472. useFactory: (parent) => {
  30473. // if parent is null, it means that we are in the root injector and we have just overridden
  30474. // the default injection mechanism for KeyValueDiffers, in such a case just assume
  30475. // `defaultKeyValueDiffersFactory`.
  30476. return KeyValueDiffers.create(factories, parent || defaultKeyValueDiffersFactory());
  30477. },
  30478. // Dependency technically isn't optional, but we can provide a better error message this way.
  30479. deps: [[KeyValueDiffers, new SkipSelf(), new Optional()]]
  30480. };
  30481. }
  30482. find(kv) {
  30483. const factory = this.factories.find(f => f.supports(kv));
  30484. if (factory) {
  30485. return factory;
  30486. }
  30487. throw new RuntimeError(901 /* RuntimeErrorCode.NO_SUPPORTING_DIFFER_FACTORY */, ngDevMode && `Cannot find a differ supporting object '${kv}'`);
  30488. }
  30489. }
  30490. /**
  30491. * Structural diffing for `Object`s and `Map`s.
  30492. */
  30493. const keyValDiff = [new DefaultKeyValueDifferFactory()];
  30494. /**
  30495. * Structural diffing for `Iterable` types such as `Array`s.
  30496. */
  30497. const iterableDiff = [new DefaultIterableDifferFactory()];
  30498. const defaultIterableDiffers = new IterableDiffers(iterableDiff);
  30499. const defaultKeyValueDiffers = new KeyValueDiffers(keyValDiff);
  30500. /**
  30501. * @module
  30502. * @description
  30503. * Change detection enables data binding in Angular.
  30504. */
  30505. /**
  30506. * This platform has to be included in any other platform
  30507. *
  30508. * @publicApi
  30509. */
  30510. const platformCore = createPlatformFactory(null, 'core', []);
  30511. /**
  30512. * Re-exported by `BrowserModule`, which is included automatically in the root
  30513. * `AppModule` when you create a new app with the CLI `new` command. Eagerly injects
  30514. * `ApplicationRef` to instantiate it.
  30515. *
  30516. * @publicApi
  30517. */
  30518. class ApplicationModule {
  30519. // Inject ApplicationRef to make it eager...
  30520. constructor(appRef) { }
  30521. static { this.ɵfac = function ApplicationModule_Factory(t) { return new (t || ApplicationModule)(ɵɵinject(ApplicationRef)); }; }
  30522. static { this.ɵmod = /*@__PURE__*/ ɵɵdefineNgModule({ type: ApplicationModule }); }
  30523. static { this.ɵinj = /*@__PURE__*/ ɵɵdefineInjector({}); }
  30524. }
  30525. (function () { (typeof ngDevMode === "undefined" || ngDevMode) && setClassMetadata(ApplicationModule, [{
  30526. type: NgModule
  30527. }], function () { return [{ type: ApplicationRef }]; }, null); })();
  30528. /**
  30529. * A collection that tracks all serialized views (`ngh` DOM annotations)
  30530. * to avoid duplication. An attempt to add a duplicate view results in the
  30531. * collection returning the index of the previously collected serialized view.
  30532. * This reduces the number of annotations needed for a given page.
  30533. */
  30534. class SerializedViewCollection {
  30535. constructor() {
  30536. this.views = [];
  30537. this.indexByContent = new Map();
  30538. }
  30539. add(serializedView) {
  30540. const viewAsString = JSON.stringify(serializedView);
  30541. if (!this.indexByContent.has(viewAsString)) {
  30542. const index = this.views.length;
  30543. this.views.push(serializedView);
  30544. this.indexByContent.set(viewAsString, index);
  30545. return index;
  30546. }
  30547. return this.indexByContent.get(viewAsString);
  30548. }
  30549. getAll() {
  30550. return this.views;
  30551. }
  30552. }
  30553. /**
  30554. * Global counter that is used to generate a unique id for TViews
  30555. * during the serialization process.
  30556. */
  30557. let tViewSsrId = 0;
  30558. /**
  30559. * Generates a unique id for a given TView and returns this id.
  30560. * The id is also stored on this instance of a TView and reused in
  30561. * subsequent calls.
  30562. *
  30563. * This id is needed to uniquely identify and pick up dehydrated views
  30564. * at runtime.
  30565. */
  30566. function getSsrId(tView) {
  30567. if (!tView.ssrId) {
  30568. tView.ssrId = `t${tViewSsrId++}`;
  30569. }
  30570. return tView.ssrId;
  30571. }
  30572. /**
  30573. * Computes the number of root nodes in a given view
  30574. * (or child nodes in a given container if a tNode is provided).
  30575. */
  30576. function calcNumRootNodes(tView, lView, tNode) {
  30577. const rootNodes = [];
  30578. collectNativeNodes(tView, lView, tNode, rootNodes);
  30579. return rootNodes.length;
  30580. }
  30581. /**
  30582. * Computes the number of root nodes in all views in a given LContainer.
  30583. */
  30584. function calcNumRootNodesInLContainer(lContainer) {
  30585. const rootNodes = [];
  30586. collectNativeNodesInLContainer(lContainer, rootNodes);
  30587. return rootNodes.length;
  30588. }
  30589. /**
  30590. * Annotates root level component's LView for hydration,
  30591. * see `annotateHostElementForHydration` for additional information.
  30592. */
  30593. function annotateComponentLViewForHydration(lView, context) {
  30594. const hostElement = lView[HOST];
  30595. // Root elements might also be annotated with the `ngSkipHydration` attribute,
  30596. // check if it's present before starting the serialization process.
  30597. if (hostElement && !hostElement.hasAttribute(SKIP_HYDRATION_ATTR_NAME)) {
  30598. return annotateHostElementForHydration(hostElement, lView, context);
  30599. }
  30600. return null;
  30601. }
  30602. /**
  30603. * Annotates root level LContainer for hydration. This happens when a root component
  30604. * injects ViewContainerRef, thus making the component an anchor for a view container.
  30605. * This function serializes the component itself as well as all views from the view
  30606. * container.
  30607. */
  30608. function annotateLContainerForHydration(lContainer, context) {
  30609. const componentLView = unwrapLView(lContainer[HOST]);
  30610. // Serialize the root component itself.
  30611. const componentLViewNghIndex = annotateComponentLViewForHydration(componentLView, context);
  30612. const hostElement = unwrapRNode(componentLView[HOST]);
  30613. // Serialize all views within this view container.
  30614. const rootLView = lContainer[PARENT];
  30615. const rootLViewNghIndex = annotateHostElementForHydration(hostElement, rootLView, context);
  30616. const renderer = componentLView[RENDERER];
  30617. // For cases when a root component also acts as an anchor node for a ViewContainerRef
  30618. // (for example, when ViewContainerRef is injected in a root component), there is a need
  30619. // to serialize information about the component itself, as well as an LContainer that
  30620. // represents this ViewContainerRef. Effectively, we need to serialize 2 pieces of info:
  30621. // (1) hydration info for the root component itself and (2) hydration info for the
  30622. // ViewContainerRef instance (an LContainer). Each piece of information is included into
  30623. // the hydration data (in the TransferState object) separately, thus we end up with 2 ids.
  30624. // Since we only have 1 root element, we encode both bits of info into a single string:
  30625. // ids are separated by the `|` char (e.g. `10|25`, where `10` is the ngh for a component view
  30626. // and 25 is the `ngh` for a root view which holds LContainer).
  30627. const finalIndex = `${componentLViewNghIndex}|${rootLViewNghIndex}`;
  30628. renderer.setAttribute(hostElement, NGH_ATTR_NAME, finalIndex);
  30629. }
  30630. /**
  30631. * Annotates all components bootstrapped in a given ApplicationRef
  30632. * with info needed for hydration.
  30633. *
  30634. * @param appRef An instance of an ApplicationRef.
  30635. * @param doc A reference to the current Document instance.
  30636. */
  30637. function annotateForHydration(appRef, doc) {
  30638. const serializedViewCollection = new SerializedViewCollection();
  30639. const corruptedTextNodes = new Map();
  30640. const viewRefs = appRef._views;
  30641. for (const viewRef of viewRefs) {
  30642. const lNode = getLNodeForHydration(viewRef);
  30643. // An `lView` might be `null` if a `ViewRef` represents
  30644. // an embedded view (not a component view).
  30645. if (lNode !== null) {
  30646. const context = {
  30647. serializedViewCollection,
  30648. corruptedTextNodes,
  30649. };
  30650. if (isLContainer(lNode)) {
  30651. annotateLContainerForHydration(lNode, context);
  30652. }
  30653. else {
  30654. annotateComponentLViewForHydration(lNode, context);
  30655. }
  30656. insertCorruptedTextNodeMarkers(corruptedTextNodes, doc);
  30657. }
  30658. }
  30659. // Note: we *always* include hydration info key and a corresponding value
  30660. // into the TransferState, even if the list of serialized views is empty.
  30661. // This is needed as a signal to the client that the server part of the
  30662. // hydration logic was setup and enabled correctly. Otherwise, if a client
  30663. // hydration doesn't find a key in the transfer state - an error is produced.
  30664. const serializedViews = serializedViewCollection.getAll();
  30665. const transferState = appRef.injector.get(TransferState);
  30666. transferState.set(NGH_DATA_KEY, serializedViews);
  30667. }
  30668. /**
  30669. * Serializes the lContainer data into a list of SerializedView objects,
  30670. * that represent views within this lContainer.
  30671. *
  30672. * @param lContainer the lContainer we are serializing
  30673. * @param context the hydration context
  30674. * @returns an array of the `SerializedView` objects
  30675. */
  30676. function serializeLContainer(lContainer, context) {
  30677. const views = [];
  30678. let lastViewAsString = '';
  30679. for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
  30680. let childLView = lContainer[i];
  30681. let template;
  30682. let numRootNodes;
  30683. let serializedView;
  30684. if (isRootView(childLView)) {
  30685. // If this is a root view, get an LView for the underlying component,
  30686. // because it contains information about the view to serialize.
  30687. childLView = childLView[HEADER_OFFSET];
  30688. // If we have an LContainer at this position, this indicates that the
  30689. // host element was used as a ViewContainerRef anchor (e.g. a `ViewContainerRef`
  30690. // was injected within the component class). This case requires special handling.
  30691. if (isLContainer(childLView)) {
  30692. // Calculate the number of root nodes in all views in a given container
  30693. // and increment by one to account for an anchor node itself, i.e. in this
  30694. // scenario we'll have a layout that would look like this:
  30695. // `<app-root /><#VIEW1><#VIEW2>...<!--container-->`
  30696. // The `+1` is to capture the `<app-root />` element.
  30697. numRootNodes = calcNumRootNodesInLContainer(childLView) + 1;
  30698. annotateLContainerForHydration(childLView, context);
  30699. const componentLView = unwrapLView(childLView[HOST]);
  30700. serializedView = {
  30701. [TEMPLATE_ID]: componentLView[TVIEW].ssrId,
  30702. [NUM_ROOT_NODES]: numRootNodes,
  30703. };
  30704. }
  30705. }
  30706. if (!serializedView) {
  30707. const childTView = childLView[TVIEW];
  30708. if (childTView.type === 1 /* TViewType.Component */) {
  30709. template = childTView.ssrId;
  30710. // This is a component view, thus it has only 1 root node: the component
  30711. // host node itself (other nodes would be inside that host node).
  30712. numRootNodes = 1;
  30713. }
  30714. else {
  30715. template = getSsrId(childTView);
  30716. numRootNodes = calcNumRootNodes(childTView, childLView, childTView.firstChild);
  30717. }
  30718. serializedView = {
  30719. [TEMPLATE_ID]: template,
  30720. [NUM_ROOT_NODES]: numRootNodes,
  30721. ...serializeLView(lContainer[i], context),
  30722. };
  30723. }
  30724. // Check if the previous view has the same shape (for example, it was
  30725. // produced by the *ngFor), in which case bump the counter on the previous
  30726. // view instead of including the same information again.
  30727. const currentViewAsString = JSON.stringify(serializedView);
  30728. if (views.length > 0 && currentViewAsString === lastViewAsString) {
  30729. const previousView = views[views.length - 1];
  30730. previousView[MULTIPLIER] ??= 1;
  30731. previousView[MULTIPLIER]++;
  30732. }
  30733. else {
  30734. // Record this view as most recently added.
  30735. lastViewAsString = currentViewAsString;
  30736. views.push(serializedView);
  30737. }
  30738. }
  30739. return views;
  30740. }
  30741. /**
  30742. * Helper function to produce a node path (which navigation steps runtime logic
  30743. * needs to take to locate a node) and stores it in the `NODES` section of the
  30744. * current serialized view.
  30745. */
  30746. function appendSerializedNodePath(ngh, tNode, lView) {
  30747. const noOffsetIndex = tNode.index - HEADER_OFFSET;
  30748. ngh[NODES] ??= {};
  30749. ngh[NODES][noOffsetIndex] = calcPathForNode(tNode, lView);
  30750. }
  30751. /**
  30752. * Helper function to append information about a disconnected node.
  30753. * This info is needed at runtime to avoid DOM lookups for this element
  30754. * and instead, the element would be created from scratch.
  30755. */
  30756. function appendDisconnectedNodeIndex(ngh, tNode) {
  30757. const noOffsetIndex = tNode.index - HEADER_OFFSET;
  30758. ngh[DISCONNECTED_NODES] ??= [];
  30759. if (!ngh[DISCONNECTED_NODES].includes(noOffsetIndex)) {
  30760. ngh[DISCONNECTED_NODES].push(noOffsetIndex);
  30761. }
  30762. }
  30763. /**
  30764. * Serializes the lView data into a SerializedView object that will later be added
  30765. * to the TransferState storage and referenced using the `ngh` attribute on a host
  30766. * element.
  30767. *
  30768. * @param lView the lView we are serializing
  30769. * @param context the hydration context
  30770. * @returns the `SerializedView` object containing the data to be added to the host node
  30771. */
  30772. function serializeLView(lView, context) {
  30773. const ngh = {};
  30774. const tView = lView[TVIEW];
  30775. // Iterate over DOM element references in an LView.
  30776. for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
  30777. const tNode = tView.data[i];
  30778. const noOffsetIndex = i - HEADER_OFFSET;
  30779. // Local refs (e.g. <div #localRef>) take up an extra slot in LViews
  30780. // to store the same element. In this case, there is no information in
  30781. // a corresponding slot in TNode data structure. If that's the case, just
  30782. // skip this slot and move to the next one.
  30783. if (!tNode) {
  30784. continue;
  30785. }
  30786. // Check if a native node that represents a given TNode is disconnected from the DOM tree.
  30787. // Such nodes must be excluded from the hydration (since the hydration won't be able to
  30788. // find them), so the TNode ids are collected and used at runtime to skip the hydration.
  30789. //
  30790. // This situation may happen during the content projection, when some nodes don't make it
  30791. // into one of the content projection slots (for example, when there is no default
  30792. // <ng-content /> slot in projector component's template).
  30793. if (isDisconnectedNode(tNode, lView) && isContentProjectedNode(tNode)) {
  30794. appendDisconnectedNodeIndex(ngh, tNode);
  30795. continue;
  30796. }
  30797. if (Array.isArray(tNode.projection)) {
  30798. for (const projectionHeadTNode of tNode.projection) {
  30799. // We may have `null`s in slots with no projected content.
  30800. if (!projectionHeadTNode)
  30801. continue;
  30802. if (!Array.isArray(projectionHeadTNode)) {
  30803. // If we process re-projected content (i.e. `<ng-content>`
  30804. // appears at projection location), skip annotations for this content
  30805. // since all DOM nodes in this projection were handled while processing
  30806. // a parent lView, which contains those nodes.
  30807. if (!isProjectionTNode(projectionHeadTNode) &&
  30808. !isInSkipHydrationBlock(projectionHeadTNode)) {
  30809. if (isDisconnectedNode(projectionHeadTNode, lView)) {
  30810. // Check whether this node is connected, since we may have a TNode
  30811. // in the data structure as a projection segment head, but the
  30812. // content projection slot might be disabled (e.g.
  30813. // <ng-content *ngIf="false" />).
  30814. appendDisconnectedNodeIndex(ngh, projectionHeadTNode);
  30815. }
  30816. else {
  30817. appendSerializedNodePath(ngh, projectionHeadTNode, lView);
  30818. }
  30819. }
  30820. }
  30821. else {
  30822. // If a value is an array, it means that we are processing a projection
  30823. // where projectable nodes were passed in as DOM nodes (for example, when
  30824. // calling `ViewContainerRef.createComponent(CmpA, {projectableNodes: [...]})`).
  30825. //
  30826. // In this scenario, nodes can come from anywhere (either created manually,
  30827. // accessed via `document.querySelector`, etc) and may be in any state
  30828. // (attached or detached from the DOM tree). As a result, we can not reliably
  30829. // restore the state for such cases during hydration.
  30830. throw unsupportedProjectionOfDomNodes(unwrapRNode(lView[i]));
  30831. }
  30832. }
  30833. }
  30834. if (isLContainer(lView[i])) {
  30835. // Serialize information about a template.
  30836. const embeddedTView = tNode.tView;
  30837. if (embeddedTView !== null) {
  30838. ngh[TEMPLATES] ??= {};
  30839. ngh[TEMPLATES][noOffsetIndex] = getSsrId(embeddedTView);
  30840. }
  30841. // Serialize views within this LContainer.
  30842. const hostNode = lView[i][HOST]; // host node of this container
  30843. // LView[i][HOST] can be of 2 different types:
  30844. // - either a DOM node
  30845. // - or an array that represents an LView of a component
  30846. if (Array.isArray(hostNode)) {
  30847. // This is a component, serialize info about it.
  30848. const targetNode = unwrapRNode(hostNode);
  30849. if (!targetNode.hasAttribute(SKIP_HYDRATION_ATTR_NAME)) {
  30850. annotateHostElementForHydration(targetNode, hostNode, context);
  30851. }
  30852. }
  30853. ngh[CONTAINERS] ??= {};
  30854. ngh[CONTAINERS][noOffsetIndex] = serializeLContainer(lView[i], context);
  30855. }
  30856. else if (Array.isArray(lView[i])) {
  30857. // This is a component, annotate the host node with an `ngh` attribute.
  30858. const targetNode = unwrapRNode(lView[i][HOST]);
  30859. if (!targetNode.hasAttribute(SKIP_HYDRATION_ATTR_NAME)) {
  30860. annotateHostElementForHydration(targetNode, lView[i], context);
  30861. }
  30862. }
  30863. else {
  30864. // <ng-container> case
  30865. if (tNode.type & 8 /* TNodeType.ElementContainer */) {
  30866. // An <ng-container> is represented by the number of
  30867. // top-level nodes. This information is needed to skip over
  30868. // those nodes to reach a corresponding anchor node (comment node).
  30869. ngh[ELEMENT_CONTAINERS] ??= {};
  30870. ngh[ELEMENT_CONTAINERS][noOffsetIndex] = calcNumRootNodes(tView, lView, tNode.child);
  30871. }
  30872. else if (tNode.type & 16 /* TNodeType.Projection */) {
  30873. // Current TNode represents an `<ng-content>` slot, thus it has no
  30874. // DOM elements associated with it, so the **next sibling** node would
  30875. // not be able to find an anchor. In this case, use full path instead.
  30876. let nextTNode = tNode.next;
  30877. // Skip over all `<ng-content>` slots in a row.
  30878. while (nextTNode !== null && (nextTNode.type & 16 /* TNodeType.Projection */)) {
  30879. nextTNode = nextTNode.next;
  30880. }
  30881. if (nextTNode && !isInSkipHydrationBlock(nextTNode)) {
  30882. // Handle a tNode after the `<ng-content>` slot.
  30883. appendSerializedNodePath(ngh, nextTNode, lView);
  30884. }
  30885. }
  30886. else {
  30887. // Handle cases where text nodes can be lost after DOM serialization:
  30888. // 1. When there is an *empty text node* in DOM: in this case, this
  30889. // node would not make it into the serialized string and as a result,
  30890. // this node wouldn't be created in a browser. This would result in
  30891. // a mismatch during the hydration, where the runtime logic would expect
  30892. // a text node to be present in live DOM, but no text node would exist.
  30893. // Example: `<span>{{ name }}</span>` when the `name` is an empty string.
  30894. // This would result in `<span></span>` string after serialization and
  30895. // in a browser only the `span` element would be created. To resolve that,
  30896. // an extra comment node is appended in place of an empty text node and
  30897. // that special comment node is replaced with an empty text node *before*
  30898. // hydration.
  30899. // 2. When there are 2 consecutive text nodes present in the DOM.
  30900. // Example: `<div>Hello <ng-container *ngIf="true">world</ng-container></div>`.
  30901. // In this scenario, the live DOM would look like this:
  30902. // <div>#text('Hello ') #text('world') #comment('container')</div>
  30903. // Serialized string would look like this: `<div>Hello world<!--container--></div>`.
  30904. // The live DOM in a browser after that would be:
  30905. // <div>#text('Hello world') #comment('container')</div>
  30906. // Notice how 2 text nodes are now "merged" into one. This would cause hydration
  30907. // logic to fail, since it'd expect 2 text nodes being present, not one.
  30908. // To fix this, we insert a special comment node in between those text nodes, so
  30909. // serialized representation is: `<div>Hello <!--ngtns-->world<!--container--></div>`.
  30910. // This forces browser to create 2 text nodes separated by a comment node.
  30911. // Before running a hydration process, this special comment node is removed, so the
  30912. // live DOM has exactly the same state as it was before serialization.
  30913. if (tNode.type & 1 /* TNodeType.Text */) {
  30914. const rNode = unwrapRNode(lView[i]);
  30915. // Collect this node as required special annotation only when its
  30916. // contents is empty. Otherwise, such text node would be present on
  30917. // the client after server-side rendering and no special handling needed.
  30918. if (rNode.textContent === '') {
  30919. context.corruptedTextNodes.set(rNode, "ngetn" /* TextNodeMarker.EmptyNode */);
  30920. }
  30921. else if (rNode.nextSibling?.nodeType === Node.TEXT_NODE) {
  30922. context.corruptedTextNodes.set(rNode, "ngtns" /* TextNodeMarker.Separator */);
  30923. }
  30924. }
  30925. if (tNode.projectionNext && tNode.projectionNext !== tNode.next &&
  30926. !isInSkipHydrationBlock(tNode.projectionNext)) {
  30927. // Check if projection next is not the same as next, in which case
  30928. // the node would not be found at creation time at runtime and we
  30929. // need to provide a location for that node.
  30930. appendSerializedNodePath(ngh, tNode.projectionNext, lView);
  30931. }
  30932. }
  30933. }
  30934. }
  30935. return ngh;
  30936. }
  30937. /**
  30938. * Determines whether a component instance that is represented
  30939. * by a given LView uses `ViewEncapsulation.ShadowDom`.
  30940. */
  30941. function componentUsesShadowDomEncapsulation(lView) {
  30942. const instance = lView[CONTEXT];
  30943. return instance?.constructor ?
  30944. getComponentDef(instance.constructor)?.encapsulation === ViewEncapsulation$1.ShadowDom :
  30945. false;
  30946. }
  30947. /**
  30948. * Annotates component host element for hydration:
  30949. * - by either adding the `ngh` attribute and collecting hydration-related info
  30950. * for the serialization and transferring to the client
  30951. * - or by adding the `ngSkipHydration` attribute in case Angular detects that
  30952. * component contents is not compatible with hydration.
  30953. *
  30954. * @param element The Host element to be annotated
  30955. * @param lView The associated LView
  30956. * @param context The hydration context
  30957. * @returns An index of serialized view from the transfer state object
  30958. * or `null` when a given component can not be serialized.
  30959. */
  30960. function annotateHostElementForHydration(element, lView, context) {
  30961. const renderer = lView[RENDERER];
  30962. if (hasI18n(lView) || componentUsesShadowDomEncapsulation(lView)) {
  30963. // Attach the skip hydration attribute if this component:
  30964. // - either has i18n blocks, since hydrating such blocks is not yet supported
  30965. // - or uses ShadowDom view encapsulation, since Domino doesn't support
  30966. // shadow DOM, so we can not guarantee that client and server representations
  30967. // would exactly match
  30968. renderer.setAttribute(element, SKIP_HYDRATION_ATTR_NAME, '');
  30969. return null;
  30970. }
  30971. else {
  30972. const ngh = serializeLView(lView, context);
  30973. const index = context.serializedViewCollection.add(ngh);
  30974. renderer.setAttribute(element, NGH_ATTR_NAME, index.toString());
  30975. return index;
  30976. }
  30977. }
  30978. /**
  30979. * Physically inserts the comment nodes to ensure empty text nodes and adjacent
  30980. * text node separators are preserved after server serialization of the DOM.
  30981. * These get swapped back for empty text nodes or separators once hydration happens
  30982. * on the client.
  30983. *
  30984. * @param corruptedTextNodes The Map of text nodes to be replaced with comments
  30985. * @param doc The document
  30986. */
  30987. function insertCorruptedTextNodeMarkers(corruptedTextNodes, doc) {
  30988. for (const [textNode, marker] of corruptedTextNodes) {
  30989. textNode.after(doc.createComment(marker));
  30990. }
  30991. }
  30992. /**
  30993. * Detects whether a given TNode represents a node that
  30994. * is being content projected.
  30995. */
  30996. function isContentProjectedNode(tNode) {
  30997. let currentTNode = tNode;
  30998. while (currentTNode != null) {
  30999. // If we come across a component host node in parent nodes -
  31000. // this TNode is in the content projection section.
  31001. if (isComponentHost(currentTNode)) {
  31002. return true;
  31003. }
  31004. currentTNode = currentTNode.parent;
  31005. }
  31006. return false;
  31007. }
  31008. /**
  31009. * Check whether a given node exists, but is disconnected from the DOM.
  31010. *
  31011. * Note: we leverage the fact that we have this information available in the DOM emulation
  31012. * layer (in Domino) for now. Longer-term solution should not rely on the DOM emulation and
  31013. * only use internal data structures and state to compute this information.
  31014. */
  31015. function isDisconnectedNode(tNode, lView) {
  31016. return !(tNode.type & 16 /* TNodeType.Projection */) && !!lView[tNode.index] &&
  31017. !unwrapRNode(lView[tNode.index]).isConnected;
  31018. }
  31019. /**
  31020. * Indicates whether the hydration-related code was added,
  31021. * prevents adding it multiple times.
  31022. */
  31023. let isHydrationSupportEnabled = false;
  31024. /**
  31025. * Defines a period of time that Angular waits for the `ApplicationRef.isStable` to emit `true`.
  31026. * If there was no event with the `true` value during this time, Angular reports a warning.
  31027. */
  31028. const APPLICATION_IS_STABLE_TIMEOUT = 10000;
  31029. /**
  31030. * Brings the necessary hydration code in tree-shakable manner.
  31031. * The code is only present when the `provideClientHydration` is
  31032. * invoked. Otherwise, this code is tree-shaken away during the
  31033. * build optimization step.
  31034. *
  31035. * This technique allows us to swap implementations of methods so
  31036. * tree shaking works appropriately when hydration is disabled or
  31037. * enabled. It brings in the appropriate version of the method that
  31038. * supports hydration only when enabled.
  31039. */
  31040. function enableHydrationRuntimeSupport() {
  31041. if (!isHydrationSupportEnabled) {
  31042. isHydrationSupportEnabled = true;
  31043. enableRetrieveHydrationInfoImpl();
  31044. enableLocateOrCreateElementNodeImpl();
  31045. enableLocateOrCreateTextNodeImpl();
  31046. enableLocateOrCreateElementContainerNodeImpl();
  31047. enableLocateOrCreateContainerAnchorImpl();
  31048. enableLocateOrCreateContainerRefImpl();
  31049. enableFindMatchingDehydratedViewImpl();
  31050. enableApplyRootElementTransformImpl();
  31051. }
  31052. }
  31053. /**
  31054. * Outputs a message with hydration stats into a console.
  31055. */
  31056. function printHydrationStats(injector) {
  31057. const console = injector.get(Console);
  31058. const message = `Angular hydrated ${ngDevMode.hydratedComponents} component(s) ` +
  31059. `and ${ngDevMode.hydratedNodes} node(s), ` +
  31060. `${ngDevMode.componentsSkippedHydration} component(s) were skipped. ` +
  31061. `Note: this feature is in Developer Preview mode. ` +
  31062. `Learn more at https://angular.io/guide/hydration.`;
  31063. // tslint:disable-next-line:no-console
  31064. console.log(message);
  31065. }
  31066. /**
  31067. * Returns a Promise that is resolved when an application becomes stable.
  31068. */
  31069. function whenStable(appRef, injector) {
  31070. const isStablePromise = appRef.isStable.pipe(first((isStable) => isStable)).toPromise();
  31071. if (typeof ngDevMode !== 'undefined' && ngDevMode) {
  31072. const timeoutTime = APPLICATION_IS_STABLE_TIMEOUT;
  31073. const console = injector.get(Console);
  31074. const ngZone = injector.get(NgZone);
  31075. // The following call should not and does not prevent the app to become stable
  31076. // We cannot use RxJS timer here because the app would remain unstable.
  31077. // This also avoids an extra change detection cycle.
  31078. const timeoutId = ngZone.runOutsideAngular(() => {
  31079. return setTimeout(() => logWarningOnStableTimedout(timeoutTime, console), timeoutTime);
  31080. });
  31081. isStablePromise.finally(() => clearTimeout(timeoutId));
  31082. }
  31083. return isStablePromise.then(() => { });
  31084. }
  31085. /**
  31086. * Returns a set of providers required to setup hydration support
  31087. * for an application that is server side rendered. This function is
  31088. * included into the `provideClientHydration` public API function from
  31089. * the `platform-browser` package.
  31090. *
  31091. * The function sets up an internal flag that would be recognized during
  31092. * the server side rendering time as well, so there is no need to
  31093. * configure or change anything in NgUniversal to enable the feature.
  31094. */
  31095. function withDomHydration() {
  31096. return makeEnvironmentProviders([
  31097. {
  31098. provide: IS_HYDRATION_DOM_REUSE_ENABLED,
  31099. useFactory: () => {
  31100. let isEnabled = true;
  31101. if (isPlatformBrowser()) {
  31102. // On the client, verify that the server response contains
  31103. // hydration annotations. Otherwise, keep hydration disabled.
  31104. const transferState = inject(TransferState, { optional: true });
  31105. isEnabled = !!transferState?.get(NGH_DATA_KEY, null);
  31106. if (!isEnabled && (typeof ngDevMode !== 'undefined' && ngDevMode)) {
  31107. const console = inject(Console);
  31108. const message = formatRuntimeError(-505 /* RuntimeErrorCode.MISSING_HYDRATION_ANNOTATIONS */, 'Angular hydration was requested on the client, but there was no ' +
  31109. 'serialized information present in the server response, ' +
  31110. 'thus hydration was not enabled. ' +
  31111. 'Make sure the `provideClientHydration()` is included into the list ' +
  31112. 'of providers in the server part of the application configuration.');
  31113. // tslint:disable-next-line:no-console
  31114. console.warn(message);
  31115. }
  31116. }
  31117. if (isEnabled) {
  31118. inject(ENABLED_SSR_FEATURES).add('hydration');
  31119. }
  31120. return isEnabled;
  31121. },
  31122. },
  31123. {
  31124. provide: ENVIRONMENT_INITIALIZER,
  31125. useValue: () => {
  31126. // Since this function is used across both server and client,
  31127. // make sure that the runtime code is only added when invoked
  31128. // on the client. Moving forward, the `isPlatformBrowser` check should
  31129. // be replaced with a tree-shakable alternative (e.g. `isServer`
  31130. // flag).
  31131. if (isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED)) {
  31132. verifySsrContentsIntegrity();
  31133. enableHydrationRuntimeSupport();
  31134. }
  31135. },
  31136. multi: true,
  31137. },
  31138. {
  31139. provide: PRESERVE_HOST_CONTENT,
  31140. useFactory: () => {
  31141. // Preserve host element content only in a browser
  31142. // environment and when hydration is configured properly.
  31143. // On a server, an application is rendered from scratch,
  31144. // so the host content needs to be empty.
  31145. return isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED);
  31146. }
  31147. },
  31148. {
  31149. provide: APP_BOOTSTRAP_LISTENER,
  31150. useFactory: () => {
  31151. if (isPlatformBrowser() && inject(IS_HYDRATION_DOM_REUSE_ENABLED)) {
  31152. const appRef = inject(ApplicationRef);
  31153. const injector = inject(Injector);
  31154. return () => {
  31155. // Wait until an app becomes stable and cleanup all views that
  31156. // were not claimed during the application bootstrap process.
  31157. // The timing is similar to when we start the serialization process
  31158. // on the server.
  31159. //
  31160. // Note: the cleanup task *MUST* be scheduled within the Angular zone
  31161. // to ensure that change detection is properly run afterward.
  31162. whenStable(appRef, injector).then(() => {
  31163. NgZone.assertInAngularZone();
  31164. cleanupDehydratedViews(appRef);
  31165. if (typeof ngDevMode !== 'undefined' && ngDevMode) {
  31166. printHydrationStats(injector);
  31167. }
  31168. });
  31169. };
  31170. }
  31171. return () => { }; // noop
  31172. },
  31173. multi: true,
  31174. }
  31175. ]);
  31176. }
  31177. /**
  31178. *
  31179. * @param time The time in ms until the stable timedout warning message is logged
  31180. */
  31181. function logWarningOnStableTimedout(time, console) {
  31182. const message = `Angular hydration expected the ApplicationRef.isStable() to emit \`true\`, but it ` +
  31183. `didn't happen within ${time}ms. Angular hydration logic depends on the application becoming stable ` +
  31184. `as a signal to complete hydration process.`;
  31185. console.warn(formatRuntimeError(-506 /* RuntimeErrorCode.HYDRATION_STABLE_TIMEDOUT */, message));
  31186. }
  31187. /**
  31188. * Verifies whether the DOM contains a special marker added during SSR time to make sure
  31189. * there is no SSR'ed contents transformations happen after SSR is completed. Typically that
  31190. * happens either by CDN or during the build process as an optimization to remove comment nodes.
  31191. * Hydration process requires comment nodes produced by Angular to locate correct DOM segments.
  31192. * When this special marker is *not* present - throw an error and do not proceed with hydration,
  31193. * since it will not be able to function correctly.
  31194. *
  31195. * Note: this function is invoked only on the client, so it's safe to use DOM APIs.
  31196. */
  31197. function verifySsrContentsIntegrity() {
  31198. const doc = getDocument();
  31199. let hydrationMarker;
  31200. for (const node of doc.body.childNodes) {
  31201. if (node.nodeType === Node.COMMENT_NODE &&
  31202. node.textContent?.trim() === SSR_CONTENT_INTEGRITY_MARKER) {
  31203. hydrationMarker = node;
  31204. break;
  31205. }
  31206. }
  31207. if (!hydrationMarker) {
  31208. throw new RuntimeError(-507 /* RuntimeErrorCode.MISSING_SSR_CONTENT_INTEGRITY_MARKER */, typeof ngDevMode !== 'undefined' && ngDevMode &&
  31209. 'Angular hydration logic detected that HTML content of this page was modified after it ' +
  31210. 'was produced during server side rendering. Make sure that there are no optimizations ' +
  31211. 'that remove comment nodes from HTML enabled on your CDN. Angular hydration ' +
  31212. 'relies on HTML produced by the server, including whitespaces and comment nodes.');
  31213. }
  31214. }
  31215. /**
  31216. * Transforms a value (typically a string) to a boolean.
  31217. * Intended to be used as a transform function of an input.
  31218. *
  31219. * @usageNotes
  31220. * ```typescript
  31221. * @Input({ transform: booleanAttribute }) status!: boolean;
  31222. * ```
  31223. * @param value Value to be transformed.
  31224. *
  31225. * @publicApi
  31226. */
  31227. function booleanAttribute(value) {
  31228. return typeof value === 'boolean' ? value : (value != null && value !== 'false');
  31229. }
  31230. /**
  31231. * Transforms a value (typically a string) to a number.
  31232. * Intended to be used as a transform function of an input.
  31233. * @param value Value to be transformed.
  31234. * @param fallbackValue Value to use if the provided value can't be parsed as a number.
  31235. *
  31236. * @usageNotes
  31237. * ```typescript
  31238. * @Input({ transform: numberAttribute }) id!: number;
  31239. * ```
  31240. *
  31241. * @publicApi
  31242. */
  31243. function numberAttribute(value, fallbackValue = NaN) {
  31244. // parseFloat(value) handles most of the cases we're interested in (it treats null, empty string,
  31245. // and other non-number values as NaN, where Number just uses 0) but it considers the string
  31246. // '123hello' to be a valid number. Therefore we also check if Number(value) is NaN.
  31247. const isNumberValue = !isNaN(parseFloat(value)) && !isNaN(Number(value));
  31248. return isNumberValue ? Number(value) : fallbackValue;
  31249. }
  31250. /**
  31251. * Compiles a partial directive declaration object into a full directive definition object.
  31252. *
  31253. * @codeGenApi
  31254. */
  31255. function ɵɵngDeclareDirective(decl) {
  31256. const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'directive', type: decl.type });
  31257. return compiler.compileDirectiveDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵfac.js`, decl);
  31258. }
  31259. /**
  31260. * Evaluates the class metadata declaration.
  31261. *
  31262. * @codeGenApi
  31263. */
  31264. function ɵɵngDeclareClassMetadata(decl) {
  31265. setClassMetadata(decl.type, decl.decorators, decl.ctorParameters ?? null, decl.propDecorators ?? null);
  31266. }
  31267. /**
  31268. * Compiles a partial component declaration object into a full component definition object.
  31269. *
  31270. * @codeGenApi
  31271. */
  31272. function ɵɵngDeclareComponent(decl) {
  31273. const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'component', type: decl.type });
  31274. return compiler.compileComponentDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵcmp.js`, decl);
  31275. }
  31276. /**
  31277. * Compiles a partial pipe declaration object into a full pipe definition object.
  31278. *
  31279. * @codeGenApi
  31280. */
  31281. function ɵɵngDeclareFactory(decl) {
  31282. const compiler = getCompilerFacade({
  31283. usage: 1 /* JitCompilerUsage.PartialDeclaration */,
  31284. kind: getFactoryKind(decl.target),
  31285. type: decl.type
  31286. });
  31287. return compiler.compileFactoryDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵfac.js`, decl);
  31288. }
  31289. function getFactoryKind(target) {
  31290. switch (target) {
  31291. case FactoryTarget.Directive:
  31292. return 'directive';
  31293. case FactoryTarget.Component:
  31294. return 'component';
  31295. case FactoryTarget.Injectable:
  31296. return 'injectable';
  31297. case FactoryTarget.Pipe:
  31298. return 'pipe';
  31299. case FactoryTarget.NgModule:
  31300. return 'NgModule';
  31301. }
  31302. }
  31303. /**
  31304. * Compiles a partial injectable declaration object into a full injectable definition object.
  31305. *
  31306. * @codeGenApi
  31307. */
  31308. function ɵɵngDeclareInjectable(decl) {
  31309. const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'injectable', type: decl.type });
  31310. return compiler.compileInjectableDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵprov.js`, decl);
  31311. }
  31312. /**
  31313. * Compiles a partial injector declaration object into a full injector definition object.
  31314. *
  31315. * @codeGenApi
  31316. */
  31317. function ɵɵngDeclareInjector(decl) {
  31318. const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'NgModule', type: decl.type });
  31319. return compiler.compileInjectorDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵinj.js`, decl);
  31320. }
  31321. /**
  31322. * Compiles a partial NgModule declaration object into a full NgModule definition object.
  31323. *
  31324. * @codeGenApi
  31325. */
  31326. function ɵɵngDeclareNgModule(decl) {
  31327. const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'NgModule', type: decl.type });
  31328. return compiler.compileNgModuleDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵmod.js`, decl);
  31329. }
  31330. /**
  31331. * Compiles a partial pipe declaration object into a full pipe definition object.
  31332. *
  31333. * @codeGenApi
  31334. */
  31335. function ɵɵngDeclarePipe(decl) {
  31336. const compiler = getCompilerFacade({ usage: 1 /* JitCompilerUsage.PartialDeclaration */, kind: 'pipe', type: decl.type });
  31337. return compiler.compilePipeDeclaration(angularCoreEnv, `ng:///${decl.type.name}/ɵpipe.js`, decl);
  31338. }
  31339. // clang-format off
  31340. // clang-format on
  31341. // clang-format off
  31342. // clang-format on
  31343. // This file exists to allow the set of reactivity exports to be modified in g3, as these APIs are
  31344. /**
  31345. * Creates a `ComponentRef` instance based on provided component type and a set of options.
  31346. *
  31347. * @usageNotes
  31348. *
  31349. * The example below demonstrates how the `createComponent` function can be used
  31350. * to create an instance of a ComponentRef dynamically and attach it to an ApplicationRef,
  31351. * so that it gets included into change detection cycles.
  31352. *
  31353. * Note: the example uses standalone components, but the function can also be used for
  31354. * non-standalone components (declared in an NgModule) as well.
  31355. *
  31356. * ```typescript
  31357. * @Component({
  31358. * standalone: true,
  31359. * template: `Hello {{ name }}!`
  31360. * })
  31361. * class HelloComponent {
  31362. * name = 'Angular';
  31363. * }
  31364. *
  31365. * @Component({
  31366. * standalone: true,
  31367. * template: `<div id="hello-component-host"></div>`
  31368. * })
  31369. * class RootComponent {}
  31370. *
  31371. * // Bootstrap an application.
  31372. * const applicationRef = await bootstrapApplication(RootComponent);
  31373. *
  31374. * // Locate a DOM node that would be used as a host.
  31375. * const hostElement = document.getElementById('hello-component-host');
  31376. *
  31377. * // Get an `EnvironmentInjector` instance from the `ApplicationRef`.
  31378. * const environmentInjector = applicationRef.injector;
  31379. *
  31380. * // We can now create a `ComponentRef` instance.
  31381. * const componentRef = createComponent(HelloComponent, {hostElement, environmentInjector});
  31382. *
  31383. * // Last step is to register the newly created ref using the `ApplicationRef` instance
  31384. * // to include the component view into change detection cycles.
  31385. * applicationRef.attachView(componentRef.hostView);
  31386. * componentRef.changeDetectorRef.detectChanges();
  31387. * ```
  31388. *
  31389. * @param component Component class reference.
  31390. * @param options Set of options to use:
  31391. * * `environmentInjector`: An `EnvironmentInjector` instance to be used for the component, see
  31392. * additional info about it [here](/guide/standalone-components#environment-injectors).
  31393. * * `hostElement` (optional): A DOM node that should act as a host node for the component. If not
  31394. * provided, Angular creates one based on the tag name used in the component selector (and falls
  31395. * back to using `div` if selector doesn't have tag name info).
  31396. * * `elementInjector` (optional): An `ElementInjector` instance, see additional info about it
  31397. * [here](/guide/hierarchical-dependency-injection#elementinjector).
  31398. * * `projectableNodes` (optional): A list of DOM nodes that should be projected through
  31399. * [`<ng-content>`](api/core/ng-content) of the new component instance.
  31400. * @returns ComponentRef instance that represents a given Component.
  31401. *
  31402. * @publicApi
  31403. */
  31404. function createComponent(component, options) {
  31405. ngDevMode && assertComponentDef(component);
  31406. const componentDef = getComponentDef(component);
  31407. const elementInjector = options.elementInjector || getNullInjector();
  31408. const factory = new ComponentFactory(componentDef);
  31409. return factory.create(elementInjector, options.projectableNodes, options.hostElement, options.environmentInjector);
  31410. }
  31411. /**
  31412. * Creates an object that allows to retrieve component metadata.
  31413. *
  31414. * @usageNotes
  31415. *
  31416. * The example below demonstrates how to use the function and how the fields
  31417. * of the returned object map to the component metadata.
  31418. *
  31419. * ```typescript
  31420. * @Component({
  31421. * standalone: true,
  31422. * selector: 'foo-component',
  31423. * template: `
  31424. * <ng-content></ng-content>
  31425. * <ng-content select="content-selector-a"></ng-content>
  31426. * `,
  31427. * })
  31428. * class FooComponent {
  31429. * @Input('inputName') inputPropName: string;
  31430. * @Output('outputName') outputPropName = new EventEmitter<void>();
  31431. * }
  31432. *
  31433. * const mirror = reflectComponentType(FooComponent);
  31434. * expect(mirror.type).toBe(FooComponent);
  31435. * expect(mirror.selector).toBe('foo-component');
  31436. * expect(mirror.isStandalone).toBe(true);
  31437. * expect(mirror.inputs).toEqual([{propName: 'inputName', templateName: 'inputPropName'}]);
  31438. * expect(mirror.outputs).toEqual([{propName: 'outputName', templateName: 'outputPropName'}]);
  31439. * expect(mirror.ngContentSelectors).toEqual([
  31440. * '*', // first `<ng-content>` in a template, the selector defaults to `*`
  31441. * 'content-selector-a' // second `<ng-content>` in a template
  31442. * ]);
  31443. * ```
  31444. *
  31445. * @param component Component class reference.
  31446. * @returns An object that allows to retrieve component metadata.
  31447. *
  31448. * @publicApi
  31449. */
  31450. function reflectComponentType(component) {
  31451. const componentDef = getComponentDef(component);
  31452. if (!componentDef)
  31453. return null;
  31454. const factory = new ComponentFactory(componentDef);
  31455. return {
  31456. get selector() {
  31457. return factory.selector;
  31458. },
  31459. get type() {
  31460. return factory.componentType;
  31461. },
  31462. get inputs() {
  31463. return factory.inputs;
  31464. },
  31465. get outputs() {
  31466. return factory.outputs;
  31467. },
  31468. get ngContentSelectors() {
  31469. return factory.ngContentSelectors;
  31470. },
  31471. get isStandalone() {
  31472. return componentDef.standalone;
  31473. },
  31474. get isSignal() {
  31475. return componentDef.signals;
  31476. },
  31477. };
  31478. }
  31479. /**
  31480. * Merge multiple application configurations from left to right.
  31481. *
  31482. * @param configs Two or more configurations to be merged.
  31483. * @returns A merged [ApplicationConfig](api/core/ApplicationConfig).
  31484. *
  31485. * @publicApi
  31486. */
  31487. function mergeApplicationConfig(...configs) {
  31488. return configs.reduce((prev, curr) => {
  31489. return Object.assign(prev, curr, { providers: [...prev.providers, ...curr.providers] });
  31490. }, { providers: [] });
  31491. }
  31492. /**
  31493. * @module
  31494. * @description
  31495. * Entry point from which you should import all public core APIs.
  31496. */
  31497. if (typeof ngDevMode !== 'undefined' && ngDevMode) {
  31498. // This helper is to give a reasonable error message to people upgrading to v9 that have not yet
  31499. // installed `@angular/localize` in their app.
  31500. // tslint:disable-next-line: no-toplevel-property-access
  31501. _global.$localize = _global.$localize || function () {
  31502. throw new Error('It looks like your application or one of its dependencies is using i18n.\n' +
  31503. 'Angular 9 introduced a global `$localize()` function that needs to be loaded.\n' +
  31504. 'Please run `ng add @angular/localize` from the Angular CLI.\n' +
  31505. '(For non-CLI projects, add `import \'@angular/localize/init\';` to your `polyfills.ts` file.\n' +
  31506. 'For server-side rendering applications add the import to your `main.server.ts` file.)');
  31507. };
  31508. }
  31509. /**
  31510. * @module
  31511. * @description
  31512. * Entry point for all public APIs of this package.
  31513. */
  31514. // This file only reexports content of the `src` folder. Keep it that way.
  31515. /* This file is not used to build this module. It is only used during editing
  31516. * by the TypeScript language service and during build for verification. `ngc`
  31517. * replaces this file with production index.ts when it rewrites private symbol
  31518. * names.
  31519. */
  31520. /**
  31521. * Generated bundle index. Do not edit.
  31522. */
  31523. export { ANIMATION_MODULE_TYPE, APP_BOOTSTRAP_LISTENER, APP_ID, APP_INITIALIZER, ApplicationInitStatus, ApplicationModule, ApplicationRef, Attribute, COMPILER_OPTIONS, CSP_NONCE, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, ChangeDetectorRef, Compiler, CompilerFactory, Component, ComponentFactory$1 as ComponentFactory, ComponentFactoryResolver$1 as ComponentFactoryResolver, ComponentRef$1 as ComponentRef, ContentChild, ContentChildren, DEFAULT_CURRENCY_CODE, DebugElement, DebugEventListener, DebugNode, DefaultIterableDiffer, DestroyRef, Directive, ENVIRONMENT_INITIALIZER, ElementRef, EmbeddedViewRef, EnvironmentInjector, ErrorHandler, EventEmitter, Host, HostBinding, HostListener, INJECTOR, Inject, InjectFlags, Injectable, InjectionToken, Injector, Input, IterableDiffers, KeyValueDiffers, LOCALE_ID, MissingTranslationStrategy, ModuleWithComponentFactories, NO_ERRORS_SCHEMA, NgModule, NgModuleFactory$1 as NgModuleFactory, NgModuleRef$1 as NgModuleRef, NgProbeToken, NgZone, Optional, Output, PACKAGE_ROOT_URL, PLATFORM_ID, PLATFORM_INITIALIZER, Pipe, PlatformRef, Query, QueryList, Renderer2, RendererFactory2, RendererStyleFlags2, Sanitizer, SecurityContext, Self, SimpleChange, SkipSelf, TRANSLATIONS, TRANSLATIONS_FORMAT, TemplateRef, Testability, TestabilityRegistry, TransferState, Type, VERSION, Version, ViewChild, ViewChildren, ViewContainerRef, ViewEncapsulation$1 as ViewEncapsulation, ViewRef, afterNextRender, afterRender, asNativeElements, assertInInjectionContext, assertPlatform, booleanAttribute, computed, createComponent, createEnvironmentInjector, createNgModule, createNgModuleRef, createPlatform, createPlatformFactory, defineInjectable, destroyPlatform, effect, enableProdMode, forwardRef, getDebugNode, getModuleFactory, getNgModuleById, getPlatform, importProvidersFrom, inject, isDevMode, isSignal, isStandalone, makeEnvironmentProviders, makeStateKey, mergeApplicationConfig, numberAttribute, platformCore, provideZoneChangeDetection, reflectComponentType, resolveForwardRef, runInInjectionContext, setTestabilityGetter, signal, untracked, ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS, AfterRenderEventManager as ɵAfterRenderEventManager, ComponentFactory$1 as ɵComponentFactory, Console as ɵConsole, DEFAULT_LOCALE_ID as ɵDEFAULT_LOCALE_ID, ENABLED_SSR_FEATURES as ɵENABLED_SSR_FEATURES, INJECTOR_SCOPE as ɵINJECTOR_SCOPE, IS_HYDRATION_DOM_REUSE_ENABLED as ɵIS_HYDRATION_DOM_REUSE_ENABLED, InitialRenderPendingTasks as ɵInitialRenderPendingTasks, LContext as ɵLContext, LifecycleHooksFeature as ɵLifecycleHooksFeature, LocaleDataIndex as ɵLocaleDataIndex, NG_COMP_DEF as ɵNG_COMP_DEF, NG_DIR_DEF as ɵNG_DIR_DEF, NG_ELEMENT_ID as ɵNG_ELEMENT_ID, NG_INJ_DEF as ɵNG_INJ_DEF, NG_MOD_DEF as ɵNG_MOD_DEF, NG_PIPE_DEF as ɵNG_PIPE_DEF, NG_PROV_DEF as ɵNG_PROV_DEF, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, NO_CHANGE as ɵNO_CHANGE, NgModuleFactory as ɵNgModuleFactory, NoopNgZone as ɵNoopNgZone, ReflectionCapabilities as ɵReflectionCapabilities, ComponentFactory as ɵRender3ComponentFactory, ComponentRef as ɵRender3ComponentRef, NgModuleRef as ɵRender3NgModuleRef, RuntimeError as ɵRuntimeError, SSR_CONTENT_INTEGRITY_MARKER as ɵSSR_CONTENT_INTEGRITY_MARKER, TESTABILITY as ɵTESTABILITY, TESTABILITY_GETTER as ɵTESTABILITY_GETTER, ViewRef$1 as ɵViewRef, XSS_SECURITY_URL as ɵXSS_SECURITY_URL, _sanitizeHtml as ɵ_sanitizeHtml, _sanitizeUrl as ɵ_sanitizeUrl, allowSanitizationBypassAndThrow as ɵallowSanitizationBypassAndThrow, annotateForHydration as ɵannotateForHydration, bypassSanitizationTrustHtml as ɵbypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl as ɵbypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript as ɵbypassSanitizationTrustScript, bypassSanitizationTrustStyle as ɵbypassSanitizationTrustStyle, bypassSanitizationTrustUrl as ɵbypassSanitizationTrustUrl, clearResolutionOfComponentResourcesQueue as ɵclearResolutionOfComponentResourcesQueue, compileComponent as ɵcompileComponent, compileDirective as ɵcompileDirective, compileNgModule as ɵcompileNgModule, compileNgModuleDefs as ɵcompileNgModuleDefs, compileNgModuleFactory as ɵcompileNgModuleFactory, compilePipe as ɵcompilePipe, convertToBitFlags as ɵconvertToBitFlags, createInjector as ɵcreateInjector, defaultIterableDiffers as ɵdefaultIterableDiffers, defaultKeyValueDiffers as ɵdefaultKeyValueDiffers, detectChanges as ɵdetectChanges, devModeEqual as ɵdevModeEqual, findLocaleData as ɵfindLocaleData, flushModuleScopingQueueAsMuchAsPossible as ɵflushModuleScopingQueueAsMuchAsPossible, formatRuntimeError as ɵformatRuntimeError, getDebugNode as ɵgetDebugNode, getDirectives as ɵgetDirectives, getHostElement as ɵgetHostElement, getInjectableDef as ɵgetInjectableDef, getLContext as ɵgetLContext, getLocaleCurrencyCode as ɵgetLocaleCurrencyCode, getLocalePluralCase as ɵgetLocalePluralCase, getSanitizationBypassType as ɵgetSanitizationBypassType, ɵgetUnknownElementStrictMode, ɵgetUnknownPropertyStrictMode, _global as ɵglobal, injectChangeDetectorRef as ɵinjectChangeDetectorRef, internalCreateApplication as ɵinternalCreateApplication, isBoundToModule as ɵisBoundToModule, isEnvironmentProviders as ɵisEnvironmentProviders, isInjectable as ɵisInjectable, isNgModule as ɵisNgModule, isPromise as ɵisPromise, isSubscribable as ɵisSubscribable, noSideEffects as ɵnoSideEffects, patchComponentDefWithScope as ɵpatchComponentDefWithScope, publishDefaultGlobalUtils$1 as ɵpublishDefaultGlobalUtils, publishGlobalUtil as ɵpublishGlobalUtil, registerLocaleData as ɵregisterLocaleData, resetCompiledComponents as ɵresetCompiledComponents, resetJitOptions as ɵresetJitOptions, resolveComponentResources as ɵresolveComponentResources, setAllowDuplicateNgModuleIdsForTest as ɵsetAllowDuplicateNgModuleIdsForTest, setAlternateWeakRefImpl as ɵsetAlternateWeakRefImpl, setClassMetadata as ɵsetClassMetadata, setCurrentInjector as ɵsetCurrentInjector, setDocument as ɵsetDocument, setInjectorProfilerContext as ɵsetInjectorProfilerContext, setLocaleId as ɵsetLocaleId, ɵsetUnknownElementStrictMode, ɵsetUnknownPropertyStrictMode, store as ɵstore, stringify as ɵstringify, transitiveScopesFor as ɵtransitiveScopesFor, unregisterAllLocaleData as ɵunregisterLocaleData, unwrapSafeValue as ɵunwrapSafeValue, withDomHydration as ɵwithDomHydration, ɵɵCopyDefinitionFeature, FactoryTarget as ɵɵFactoryTarget, ɵɵHostDirectivesFeature, ɵɵInheritDefinitionFeature, ɵɵInputTransformsFeature, ɵɵNgOnChangesFeature, ɵɵProvidersFeature, ɵɵStandaloneFeature, ɵɵadvance, ɵɵattribute, ɵɵattributeInterpolate1, ɵɵattributeInterpolate2, ɵɵattributeInterpolate3, ɵɵattributeInterpolate4, ɵɵattributeInterpolate5, ɵɵattributeInterpolate6, ɵɵattributeInterpolate7, ɵɵattributeInterpolate8, ɵɵattributeInterpolateV, ɵɵclassMap, ɵɵclassMapInterpolate1, ɵɵclassMapInterpolate2, ɵɵclassMapInterpolate3, ɵɵclassMapInterpolate4, ɵɵclassMapInterpolate5, ɵɵclassMapInterpolate6, ɵɵclassMapInterpolate7, ɵɵclassMapInterpolate8, ɵɵclassMapInterpolateV, ɵɵclassProp, ɵɵcontentQuery, ɵɵdefer, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵdefineInjectable, ɵɵdefineInjector, ɵɵdefineNgModule, ɵɵdefinePipe, ɵɵdirectiveInject, ɵɵdisableBindings, ɵɵelement, ɵɵelementContainer, ɵɵelementContainerEnd, ɵɵelementContainerStart, ɵɵelementEnd, ɵɵelementStart, ɵɵenableBindings, ɵɵgetCurrentView, ɵɵgetInheritedFactory, ɵɵhostProperty, ɵɵi18n, ɵɵi18nApply, ɵɵi18nAttributes, ɵɵi18nEnd, ɵɵi18nExp, ɵɵi18nPostprocess, ɵɵi18nStart, ɵɵinject, ɵɵinjectAttribute, ɵɵinvalidFactory, ɵɵinvalidFactoryDep, ɵɵlistener, ɵɵloadQuery, ɵɵnamespaceHTML, ɵɵnamespaceMathML, ɵɵnamespaceSVG, ɵɵnextContext, ɵɵngDeclareClassMetadata, ɵɵngDeclareComponent, ɵɵngDeclareDirective, ɵɵngDeclareFactory, ɵɵngDeclareInjectable, ɵɵngDeclareInjector, ɵɵngDeclareNgModule, ɵɵngDeclarePipe, ɵɵpipe, ɵɵpipeBind1, ɵɵpipeBind2, ɵɵpipeBind3, ɵɵpipeBind4, ɵɵpipeBindV, ɵɵprojection, ɵɵprojectionDef, ɵɵproperty, ɵɵpropertyInterpolate, ɵɵpropertyInterpolate1, ɵɵpropertyInterpolate2, ɵɵpropertyInterpolate3, ɵɵpropertyInterpolate4, ɵɵpropertyInterpolate5, ɵɵpropertyInterpolate6, ɵɵpropertyInterpolate7, ɵɵpropertyInterpolate8, ɵɵpropertyInterpolateV, ɵɵpureFunction0, ɵɵpureFunction1, ɵɵpureFunction2, ɵɵpureFunction3, ɵɵpureFunction4, ɵɵpureFunction5, ɵɵpureFunction6, ɵɵpureFunction7, ɵɵpureFunction8, ɵɵpureFunctionV, ɵɵqueryRefresh, ɵɵreference, registerNgModuleType as ɵɵregisterNgModuleType, ɵɵresetView, ɵɵresolveBody, ɵɵresolveDocument, ɵɵresolveWindow, ɵɵrestoreView, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl, ɵɵsanitizeUrlOrResourceUrl, ɵɵsetComponentScope, ɵɵsetNgModuleScope, ɵɵstyleMap, ɵɵstyleMapInterpolate1, ɵɵstyleMapInterpolate2, ɵɵstyleMapInterpolate3, ɵɵstyleMapInterpolate4, ɵɵstyleMapInterpolate5, ɵɵstyleMapInterpolate6, ɵɵstyleMapInterpolate7, ɵɵstyleMapInterpolate8, ɵɵstyleMapInterpolateV, ɵɵstyleProp, ɵɵstylePropInterpolate1, ɵɵstylePropInterpolate2, ɵɵstylePropInterpolate3, ɵɵstylePropInterpolate4, ɵɵstylePropInterpolate5, ɵɵstylePropInterpolate6, ɵɵstylePropInterpolate7, ɵɵstylePropInterpolate8, ɵɵstylePropInterpolateV, ɵɵsyntheticHostListener, ɵɵsyntheticHostProperty, ɵɵtemplate, ɵɵtemplateRefExtractor, ɵɵtext, ɵɵtextInterpolate, ɵɵtextInterpolate1, ɵɵtextInterpolate2, ɵɵtextInterpolate3, ɵɵtextInterpolate4, ɵɵtextInterpolate5, ɵɵtextInterpolate6, ɵɵtextInterpolate7, ɵɵtextInterpolate8, ɵɵtextInterpolateV, ɵɵtrustConstantHtml, ɵɵtrustConstantResourceUrl, ɵɵvalidateIframeAttribute, ɵɵviewQuery };
  31524. //# sourceMappingURL=core.mjs.map