UNPKG

1.03 MBJavaScriptView Raw
1/**
2 * @license Angular v15.1.5
3 * (c) 2010-2022 Google LLC. https://angular.io/
4 * License: MIT
5 */
6
7import { getDebugNode, RendererFactory2 as RendererFactory2$1, InjectionToken as InjectionToken$1, ɵstringify, ɵReflectionCapabilities, Directive, Component, Pipe, NgModule, ɵgetInjectableDef, resolveForwardRef as resolveForwardRef$1, ɵNG_COMP_DEF, ɵRender3NgModuleRef, ApplicationInitStatus, LOCALE_ID as LOCALE_ID$1, ɵDEFAULT_LOCALE_ID, ɵsetLocaleId, ɵRender3ComponentFactory, ɵcompileComponent, ɵNG_DIR_DEF, ɵcompileDirective, ɵNG_PIPE_DEF, ɵcompilePipe, ɵNG_MOD_DEF, ɵtransitiveScopesFor, ɵpatchComponentDefWithScope, ɵNG_INJ_DEF, ɵcompileNgModuleDefs, NgZone, Compiler, COMPILER_OPTIONS, ɵNgModuleFactory, ɵisEnvironmentProviders, ModuleWithComponentFactories, ɵconvertToBitFlags, Injector as Injector$1, InjectFlags as InjectFlags$1, ɵsetAllowDuplicateNgModuleIdsForTest, ɵresetCompiledComponents, ɵsetUnknownElementStrictMode as ɵsetUnknownElementStrictMode$1, ɵsetUnknownPropertyStrictMode as ɵsetUnknownPropertyStrictMode$1, ɵgetUnknownElementStrictMode as ɵgetUnknownElementStrictMode$1, ɵgetUnknownPropertyStrictMode as ɵgetUnknownPropertyStrictMode$1, EnvironmentInjector as EnvironmentInjector$1, ɵflushModuleScopingQueueAsMuchAsPossible } from '@angular/core';
8import { ResourceLoader } from '@angular/compiler';
9import { Subject, Subscription } from 'rxjs';
10
11/**
12 * Wraps a test function in an asynchronous test zone. The test will automatically
13 * complete when all asynchronous calls within this zone are done. Can be used
14 * to wrap an {@link inject} call.
15 *
16 * Example:
17 *
18 * ```
19 * it('...', waitForAsync(inject([AClass], (object) => {
20 * object.doSomething.then(() => {
21 * expect(...);
22 * })
23 * });
24 * ```
25 *
26 * @publicApi
27 */
28function waitForAsync(fn) {
29 const _Zone = typeof Zone !== 'undefined' ? Zone : null;
30 if (!_Zone) {
31 return function () {
32 return Promise.reject('Zone is needed for the waitForAsync() test helper but could not be found. ' +
33 'Please make sure that your environment includes zone.js');
34 };
35 }
36 const asyncTest = _Zone && _Zone[_Zone.__symbol__('asyncTest')];
37 if (typeof asyncTest === 'function') {
38 return asyncTest(fn);
39 }
40 return function () {
41 return Promise.reject('zone-testing.js is needed for the async() test helper but could not be found. ' +
42 'Please make sure that your environment includes zone.js/testing');
43 };
44}
45/**
46 * @deprecated use `waitForAsync()`, (expected removal in v12)
47 * @see {@link waitForAsync}
48 * @publicApi
49 * */
50function async(fn) {
51 return waitForAsync(fn);
52}
53
54/**
55 * Fixture for debugging and testing a component.
56 *
57 * @publicApi
58 */
59class ComponentFixture {
60 constructor(componentRef, ngZone, _autoDetect) {
61 this.componentRef = componentRef;
62 this.ngZone = ngZone;
63 this._autoDetect = _autoDetect;
64 this._isStable = true;
65 this._isDestroyed = false;
66 this._resolve = null;
67 this._promise = null;
68 this._onUnstableSubscription = null;
69 this._onStableSubscription = null;
70 this._onMicrotaskEmptySubscription = null;
71 this._onErrorSubscription = null;
72 this.changeDetectorRef = componentRef.changeDetectorRef;
73 this.elementRef = componentRef.location;
74 this.debugElement = getDebugNode(this.elementRef.nativeElement);
75 this.componentInstance = componentRef.instance;
76 this.nativeElement = this.elementRef.nativeElement;
77 this.componentRef = componentRef;
78 this.ngZone = ngZone;
79 if (ngZone) {
80 // Create subscriptions outside the NgZone so that the callbacks run oustide
81 // of NgZone.
82 ngZone.runOutsideAngular(() => {
83 this._onUnstableSubscription = ngZone.onUnstable.subscribe({
84 next: () => {
85 this._isStable = false;
86 }
87 });
88 this._onMicrotaskEmptySubscription = ngZone.onMicrotaskEmpty.subscribe({
89 next: () => {
90 if (this._autoDetect) {
91 // Do a change detection run with checkNoChanges set to true to check
92 // there are no changes on the second run.
93 this.detectChanges(true);
94 }
95 }
96 });
97 this._onStableSubscription = ngZone.onStable.subscribe({
98 next: () => {
99 this._isStable = true;
100 // Check whether there is a pending whenStable() completer to resolve.
101 if (this._promise !== null) {
102 // If so check whether there are no pending macrotasks before resolving.
103 // Do this check in the next tick so that ngZone gets a chance to update the state of
104 // pending macrotasks.
105 scheduleMicroTask(() => {
106 if (!ngZone.hasPendingMacrotasks) {
107 if (this._promise !== null) {
108 this._resolve(true);
109 this._resolve = null;
110 this._promise = null;
111 }
112 }
113 });
114 }
115 }
116 });
117 this._onErrorSubscription = ngZone.onError.subscribe({
118 next: (error) => {
119 throw error;
120 }
121 });
122 });
123 }
124 }
125 _tick(checkNoChanges) {
126 this.changeDetectorRef.detectChanges();
127 if (checkNoChanges) {
128 this.checkNoChanges();
129 }
130 }
131 /**
132 * Trigger a change detection cycle for the component.
133 */
134 detectChanges(checkNoChanges = true) {
135 if (this.ngZone != null) {
136 // Run the change detection inside the NgZone so that any async tasks as part of the change
137 // detection are captured by the zone and can be waited for in isStable.
138 this.ngZone.run(() => {
139 this._tick(checkNoChanges);
140 });
141 }
142 else {
143 // Running without zone. Just do the change detection.
144 this._tick(checkNoChanges);
145 }
146 }
147 /**
148 * Do a change detection run to make sure there were no changes.
149 */
150 checkNoChanges() {
151 this.changeDetectorRef.checkNoChanges();
152 }
153 /**
154 * Set whether the fixture should autodetect changes.
155 *
156 * Also runs detectChanges once so that any existing change is detected.
157 */
158 autoDetectChanges(autoDetect = true) {
159 if (this.ngZone == null) {
160 throw new Error('Cannot call autoDetectChanges when ComponentFixtureNoNgZone is set');
161 }
162 this._autoDetect = autoDetect;
163 this.detectChanges();
164 }
165 /**
166 * Return whether the fixture is currently stable or has async tasks that have not been completed
167 * yet.
168 */
169 isStable() {
170 return this._isStable && !this.ngZone.hasPendingMacrotasks;
171 }
172 /**
173 * Get a promise that resolves when the fixture is stable.
174 *
175 * This can be used to resume testing after events have triggered asynchronous activity or
176 * asynchronous change detection.
177 */
178 whenStable() {
179 if (this.isStable()) {
180 return Promise.resolve(false);
181 }
182 else if (this._promise !== null) {
183 return this._promise;
184 }
185 else {
186 this._promise = new Promise(res => {
187 this._resolve = res;
188 });
189 return this._promise;
190 }
191 }
192 _getRenderer() {
193 if (this._renderer === undefined) {
194 this._renderer = this.componentRef.injector.get(RendererFactory2$1, null);
195 }
196 return this._renderer;
197 }
198 /**
199 * Get a promise that resolves when the ui state is stable following animations.
200 */
201 whenRenderingDone() {
202 const renderer = this._getRenderer();
203 if (renderer && renderer.whenRenderingDone) {
204 return renderer.whenRenderingDone();
205 }
206 return this.whenStable();
207 }
208 /**
209 * Trigger component destruction.
210 */
211 destroy() {
212 if (!this._isDestroyed) {
213 this.componentRef.destroy();
214 if (this._onUnstableSubscription != null) {
215 this._onUnstableSubscription.unsubscribe();
216 this._onUnstableSubscription = null;
217 }
218 if (this._onStableSubscription != null) {
219 this._onStableSubscription.unsubscribe();
220 this._onStableSubscription = null;
221 }
222 if (this._onMicrotaskEmptySubscription != null) {
223 this._onMicrotaskEmptySubscription.unsubscribe();
224 this._onMicrotaskEmptySubscription = null;
225 }
226 if (this._onErrorSubscription != null) {
227 this._onErrorSubscription.unsubscribe();
228 this._onErrorSubscription = null;
229 }
230 this._isDestroyed = true;
231 }
232 }
233}
234function scheduleMicroTask(fn) {
235 Zone.current.scheduleMicroTask('scheduleMicrotask', fn);
236}
237
238const _Zone = typeof Zone !== 'undefined' ? Zone : null;
239const fakeAsyncTestModule = _Zone && _Zone[_Zone.__symbol__('fakeAsyncTest')];
240const fakeAsyncTestModuleNotLoadedErrorMessage = `zone-testing.js is needed for the fakeAsync() test helper but could not be found.
241 Please make sure that your environment includes zone.js/testing`;
242/**
243 * Clears out the shared fake async zone for a test.
244 * To be called in a global `beforeEach`.
245 *
246 * @publicApi
247 */
248function resetFakeAsyncZone() {
249 if (fakeAsyncTestModule) {
250 return fakeAsyncTestModule.resetFakeAsyncZone();
251 }
252 throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
253}
254/**
255 * Wraps a function to be executed in the `fakeAsync` zone:
256 * - Microtasks are manually executed by calling `flushMicrotasks()`.
257 * - Timers are synchronous; `tick()` simulates the asynchronous passage of time.
258 *
259 * If there are any pending timers at the end of the function, an exception is thrown.
260 *
261 * Can be used to wrap `inject()` calls.
262 *
263 * @param fn The function that you want to wrap in the `fakeAysnc` zone.
264 *
265 * @usageNotes
266 * ### Example
267 *
268 * {@example core/testing/ts/fake_async.ts region='basic'}
269 *
270 *
271 * @returns The function wrapped to be executed in the `fakeAsync` zone.
272 * Any arguments passed when calling this returned function will be passed through to the `fn`
273 * function in the parameters when it is called.
274 *
275 * @publicApi
276 */
277function fakeAsync(fn) {
278 if (fakeAsyncTestModule) {
279 return fakeAsyncTestModule.fakeAsync(fn);
280 }
281 throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
282}
283/**
284 * Simulates the asynchronous passage of time for the timers in the `fakeAsync` zone.
285 *
286 * The microtasks queue is drained at the very start of this function and after any timer callback
287 * has been executed.
288 *
289 * @param millis The number of milliseconds to advance the virtual timer.
290 * @param tickOptions The options to pass to the `tick()` function.
291 *
292 * @usageNotes
293 *
294 * The `tick()` option is a flag called `processNewMacroTasksSynchronously`,
295 * which determines whether or not to invoke new macroTasks.
296 *
297 * If you provide a `tickOptions` object, but do not specify a
298 * `processNewMacroTasksSynchronously` property (`tick(100, {})`),
299 * then `processNewMacroTasksSynchronously` defaults to true.
300 *
301 * If you omit the `tickOptions` parameter (`tick(100))`), then
302 * `tickOptions` defaults to `{processNewMacroTasksSynchronously: true}`.
303 *
304 * ### Example
305 *
306 * {@example core/testing/ts/fake_async.ts region='basic'}
307 *
308 * The following example includes a nested timeout (new macroTask), and
309 * the `tickOptions` parameter is allowed to default. In this case,
310 * `processNewMacroTasksSynchronously` defaults to true, and the nested
311 * function is executed on each tick.
312 *
313 * ```
314 * it ('test with nested setTimeout', fakeAsync(() => {
315 * let nestedTimeoutInvoked = false;
316 * function funcWithNestedTimeout() {
317 * setTimeout(() => {
318 * nestedTimeoutInvoked = true;
319 * });
320 * };
321 * setTimeout(funcWithNestedTimeout);
322 * tick();
323 * expect(nestedTimeoutInvoked).toBe(true);
324 * }));
325 * ```
326 *
327 * In the following case, `processNewMacroTasksSynchronously` is explicitly
328 * set to false, so the nested timeout function is not invoked.
329 *
330 * ```
331 * it ('test with nested setTimeout', fakeAsync(() => {
332 * let nestedTimeoutInvoked = false;
333 * function funcWithNestedTimeout() {
334 * setTimeout(() => {
335 * nestedTimeoutInvoked = true;
336 * });
337 * };
338 * setTimeout(funcWithNestedTimeout);
339 * tick(0, {processNewMacroTasksSynchronously: false});
340 * expect(nestedTimeoutInvoked).toBe(false);
341 * }));
342 * ```
343 *
344 *
345 * @publicApi
346 */
347function tick(millis = 0, tickOptions = {
348 processNewMacroTasksSynchronously: true
349}) {
350 if (fakeAsyncTestModule) {
351 return fakeAsyncTestModule.tick(millis, tickOptions);
352 }
353 throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
354}
355/**
356 * Flushes any pending microtasks and simulates the asynchronous passage of time for the timers in
357 * the `fakeAsync` zone by
358 * draining the macrotask queue until it is empty.
359 *
360 * @param maxTurns The maximum number of times the scheduler attempts to clear its queue before
361 * throwing an error.
362 * @returns The simulated time elapsed, in milliseconds.
363 *
364 * @publicApi
365 */
366function flush(maxTurns) {
367 if (fakeAsyncTestModule) {
368 return fakeAsyncTestModule.flush(maxTurns);
369 }
370 throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
371}
372/**
373 * Discard all remaining periodic tasks.
374 *
375 * @publicApi
376 */
377function discardPeriodicTasks() {
378 if (fakeAsyncTestModule) {
379 return fakeAsyncTestModule.discardPeriodicTasks();
380 }
381 throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
382}
383/**
384 * Flush any pending microtasks.
385 *
386 * @publicApi
387 */
388function flushMicrotasks() {
389 if (fakeAsyncTestModule) {
390 return fakeAsyncTestModule.flushMicrotasks();
391 }
392 throw new Error(fakeAsyncTestModuleNotLoadedErrorMessage);
393}
394
395/** Whether test modules should be torn down by default. */
396const TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT = true;
397/** Whether unknown elements in templates should throw by default. */
398const THROW_ON_UNKNOWN_ELEMENTS_DEFAULT = false;
399/** Whether unknown properties in templates should throw by default. */
400const THROW_ON_UNKNOWN_PROPERTIES_DEFAULT = false;
401/**
402 * An abstract class for inserting the root test component element in a platform independent way.
403 *
404 * @publicApi
405 */
406class TestComponentRenderer {
407 insertRootElement(rootElementId) { }
408 removeAllRootElements() { }
409}
410/**
411 * @publicApi
412 */
413const ComponentFixtureAutoDetect = new InjectionToken$1('ComponentFixtureAutoDetect');
414/**
415 * @publicApi
416 */
417const ComponentFixtureNoNgZone = new InjectionToken$1('ComponentFixtureNoNgZone');
418
419/**
420 * Used to resolve resource URLs on `@Component` when used with JIT compilation.
421 *
422 * Example:
423 * ```
424 * @Component({
425 * selector: 'my-comp',
426 * templateUrl: 'my-comp.html', // This requires asynchronous resolution
427 * })
428 * class MyComponent{
429 * }
430 *
431 * // Calling `renderComponent` will fail because `renderComponent` is a synchronous process
432 * // and `MyComponent`'s `@Component.templateUrl` needs to be resolved asynchronously.
433 *
434 * // Calling `resolveComponentResources()` will resolve `@Component.templateUrl` into
435 * // `@Component.template`, which allows `renderComponent` to proceed in a synchronous manner.
436 *
437 * // Use browser's `fetch()` function as the default resource resolution strategy.
438 * resolveComponentResources(fetch).then(() => {
439 * // After resolution all URLs have been converted into `template` strings.
440 * renderComponent(MyComponent);
441 * });
442 *
443 * ```
444 *
445 * NOTE: In AOT the resolution happens during compilation, and so there should be no need
446 * to call this method outside JIT mode.
447 *
448 * @param resourceResolver a function which is responsible for returning a `Promise` to the
449 * contents of the resolved URL. Browser's `fetch()` method is a good default implementation.
450 */
451function resolveComponentResources(resourceResolver) {
452 // Store all promises which are fetching the resources.
453 const componentResolved = [];
454 // Cache so that we don't fetch the same resource more than once.
455 const urlMap = new Map();
456 function cachedResourceResolve(url) {
457 let promise = urlMap.get(url);
458 if (!promise) {
459 const resp = resourceResolver(url);
460 urlMap.set(url, promise = resp.then(unwrapResponse));
461 }
462 return promise;
463 }
464 componentResourceResolutionQueue.forEach((component, type) => {
465 const promises = [];
466 if (component.templateUrl) {
467 promises.push(cachedResourceResolve(component.templateUrl).then((template) => {
468 component.template = template;
469 }));
470 }
471 const styleUrls = component.styleUrls;
472 const styles = component.styles || (component.styles = []);
473 const styleOffset = component.styles.length;
474 styleUrls && styleUrls.forEach((styleUrl, index) => {
475 styles.push(''); // pre-allocate array.
476 promises.push(cachedResourceResolve(styleUrl).then((style) => {
477 styles[styleOffset + index] = style;
478 styleUrls.splice(styleUrls.indexOf(styleUrl), 1);
479 if (styleUrls.length == 0) {
480 component.styleUrls = undefined;
481 }
482 }));
483 });
484 const fullyResolved = Promise.all(promises).then(() => componentDefResolved(type));
485 componentResolved.push(fullyResolved);
486 });
487 clearResolutionOfComponentResourcesQueue();
488 return Promise.all(componentResolved).then(() => undefined);
489}
490let componentResourceResolutionQueue = new Map();
491// Track when existing ɵcmp for a Type is waiting on resources.
492const componentDefPendingResolution = new Set();
493function maybeQueueResolutionOfComponentResources(type, metadata) {
494 if (componentNeedsResolution(metadata)) {
495 componentResourceResolutionQueue.set(type, metadata);
496 componentDefPendingResolution.add(type);
497 }
498}
499function isComponentDefPendingResolution(type) {
500 return componentDefPendingResolution.has(type);
501}
502function componentNeedsResolution(component) {
503 return !!((component.templateUrl && !component.hasOwnProperty('template')) ||
504 component.styleUrls && component.styleUrls.length);
505}
506function clearResolutionOfComponentResourcesQueue() {
507 const old = componentResourceResolutionQueue;
508 componentResourceResolutionQueue = new Map();
509 return old;
510}
511function restoreComponentResolutionQueue(queue) {
512 componentDefPendingResolution.clear();
513 queue.forEach((_, type) => componentDefPendingResolution.add(type));
514 componentResourceResolutionQueue = queue;
515}
516function isComponentResourceResolutionQueueEmpty() {
517 return componentResourceResolutionQueue.size === 0;
518}
519function unwrapResponse(response) {
520 return typeof response == 'string' ? response : response.text();
521}
522function componentDefResolved(type) {
523 componentDefPendingResolution.delete(type);
524}
525
526// Always use __globalThis if available, which is the spec-defined global variable across all
527// environments, then fallback to __global first, because in Node tests both __global and
528// __window may be defined and _global should be __global in that case. Note: Typeof/Instanceof
529// checks are considered side-effects in Terser. We explicitly mark this as side-effect free:
530// https://github.com/terser/terser/issues/250.
531const _global$1 = ( /* @__PURE__ */(() => (typeof globalThis !== 'undefined' && globalThis) ||
532 (typeof global !== 'undefined' && global) || (typeof window !== 'undefined' && window) ||
533 (typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
534 self instanceof WorkerGlobalScope && self))());
535
536var FactoryTarget;
537(function (FactoryTarget) {
538 FactoryTarget[FactoryTarget["Directive"] = 0] = "Directive";
539 FactoryTarget[FactoryTarget["Component"] = 1] = "Component";
540 FactoryTarget[FactoryTarget["Injectable"] = 2] = "Injectable";
541 FactoryTarget[FactoryTarget["Pipe"] = 3] = "Pipe";
542 FactoryTarget[FactoryTarget["NgModule"] = 4] = "NgModule";
543})(FactoryTarget || (FactoryTarget = {}));
544var R3TemplateDependencyKind;
545(function (R3TemplateDependencyKind) {
546 R3TemplateDependencyKind[R3TemplateDependencyKind["Directive"] = 0] = "Directive";
547 R3TemplateDependencyKind[R3TemplateDependencyKind["Pipe"] = 1] = "Pipe";
548 R3TemplateDependencyKind[R3TemplateDependencyKind["NgModule"] = 2] = "NgModule";
549})(R3TemplateDependencyKind || (R3TemplateDependencyKind = {}));
550var ViewEncapsulation$1;
551(function (ViewEncapsulation) {
552 ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
553 // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
554 ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
555 ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
556})(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
557
558function getCompilerFacade(request) {
559 const globalNg = _global$1['ng'];
560 if (globalNg && globalNg.ɵcompilerFacade) {
561 return globalNg.ɵcompilerFacade;
562 }
563 if (typeof ngDevMode === 'undefined' || ngDevMode) {
564 // Log the type as an error so that a developer can easily navigate to the type from the
565 // console.
566 console.error(`JIT compilation failed for ${request.kind}`, request.type);
567 let message = `The ${request.kind} '${request
568 .type.name}' needs to be compiled using the JIT compiler, but '@angular/compiler' is not available.\n\n`;
569 if (request.usage === 1 /* JitCompilerUsage.PartialDeclaration */) {
570 message += `The ${request.kind} is part of a library that has been partially compiled.\n`;
571 message +=
572 `However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.\n`;
573 message += '\n';
574 message +=
575 `Ideally, the library is processed using the Angular Linker to become fully AOT compiled.\n`;
576 }
577 else {
578 message +=
579 `JIT compilation is discouraged for production use-cases! Consider using AOT mode instead.\n`;
580 }
581 message +=
582 `Alternatively, the JIT compiler should be loaded by bootstrapping using '@angular/platform-browser-dynamic' or '@angular/platform-server',\n`;
583 message +=
584 `or manually provide the compiler with 'import "@angular/compiler";' before bootstrapping.`;
585 throw new Error(message);
586 }
587 else {
588 throw new Error('JIT compiler unavailable');
589 }
590}
591
592function getClosureSafeProperty(objWithPropertyToExtract) {
593 for (let key in objWithPropertyToExtract) {
594 if (objWithPropertyToExtract[key] === getClosureSafeProperty) {
595 return key;
596 }
597 }
598 throw Error('Could not find renamed property on target object.');
599}
600/**
601 * Sets properties on a target object from a source object, but only if
602 * the property doesn't already exist on the target object.
603 * @param target The target to set properties on
604 * @param source The source of the property keys and values to set
605 */
606function fillProperties(target, source) {
607 for (const key in source) {
608 if (source.hasOwnProperty(key) && !target.hasOwnProperty(key)) {
609 target[key] = source[key];
610 }
611 }
612}
613
614function stringify(token) {
615 if (typeof token === 'string') {
616 return token;
617 }
618 if (Array.isArray(token)) {
619 return '[' + token.map(stringify).join(', ') + ']';
620 }
621 if (token == null) {
622 return '' + token;
623 }
624 if (token.overriddenName) {
625 return `${token.overriddenName}`;
626 }
627 if (token.name) {
628 return `${token.name}`;
629 }
630 const res = token.toString();
631 if (res == null) {
632 return '' + res;
633 }
634 const newLineIndex = res.indexOf('\n');
635 return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
636}
637/**
638 * Concatenates two strings with separator, allocating new strings only when necessary.
639 *
640 * @param before before string.
641 * @param separator separator string.
642 * @param after after string.
643 * @returns concatenated string.
644 */
645function concatStringsWithSpace(before, after) {
646 return (before == null || before === '') ?
647 (after === null ? '' : after) :
648 ((after == null || after === '') ? before : before + ' ' + after);
649}
650
651const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty });
652/**
653 * Allows to refer to references which are not yet defined.
654 *
655 * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
656 * DI is declared, but not yet defined. It is also used when the `token` which we use when creating
657 * a query is not yet defined.
658 *
659 * @usageNotes
660 * ### Example
661 * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
662 * @publicApi
663 */
664function forwardRef(forwardRefFn) {
665 forwardRefFn.__forward_ref__ = forwardRef;
666 forwardRefFn.toString = function () {
667 return stringify(this());
668 };
669 return forwardRefFn;
670}
671/**
672 * Lazily retrieves the reference value from a forwardRef.
673 *
674 * Acts as the identity function when given a non-forward-ref value.
675 *
676 * @usageNotes
677 * ### Example
678 *
679 * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
680 *
681 * @see `forwardRef`
682 * @publicApi
683 */
684function resolveForwardRef(type) {
685 return isForwardRef(type) ? type() : type;
686}
687/** Checks whether a function is wrapped by a `forwardRef`. */
688function isForwardRef(fn) {
689 return typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
690 fn.__forward_ref__ === forwardRef;
691}
692
693/**
694 * Construct an injectable definition which defines how a token will be constructed by the DI
695 * system, and in which injectors (if any) it will be available.
696 *
697 * This should be assigned to a static `ɵprov` field on a type, which will then be an
698 * `InjectableType`.
699 *
700 * Options:
701 * * `providedIn` determines which injectors will include the injectable, by either associating it
702 * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
703 * provided in the `'root'` injector, which will be the application-level injector in most apps.
704 * * `factory` gives the zero argument function which will create an instance of the injectable.
705 * The factory can call `inject` to access the `Injector` and request injection of dependencies.
706 *
707 * @codeGenApi
708 * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm.
709 */
710function ɵɵdefineInjectable(opts) {
711 return {
712 token: opts.token,
713 providedIn: opts.providedIn || null,
714 factory: opts.factory,
715 value: undefined,
716 };
717}
718/**
719 * @deprecated in v8, delete after v10. This API should be used only by generated code, and that
720 * code should now use ɵɵdefineInjectable instead.
721 * @publicApi
722 */
723const defineInjectable = ɵɵdefineInjectable;
724/**
725 * Construct an `InjectorDef` which configures an injector.
726 *
727 * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
728 * `InjectorType`.
729 *
730 * Options:
731 *
732 * * `providers`: an optional array of providers to add to the injector. Each provider must
733 * either have a factory or point to a type which has a `ɵprov` static property (the
734 * type must be an `InjectableType`).
735 * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
736 * whose providers will also be added to the injector. Locally provided types will override
737 * providers from imports.
738 *
739 * @codeGenApi
740 */
741function ɵɵdefineInjector(options) {
742 return { providers: options.providers || [], imports: options.imports || [] };
743}
744/**
745 * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
746 * inherited value.
747 *
748 * @param type A type which may have its own (non-inherited) `ɵprov`.
749 */
750function getInjectableDef(type) {
751 return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF);
752}
753function isInjectable(type) {
754 return getInjectableDef(type) !== null;
755}
756/**
757 * Return definition only if it is defined directly on `type` and is not inherited from a base
758 * class of `type`.
759 */
760function getOwnDefinition(type, field) {
761 return type.hasOwnProperty(field) ? type[field] : null;
762}
763/**
764 * Read the injectable def (`ɵprov`) for `type` or read the `ɵprov` from one of its ancestors.
765 *
766 * @param type A type which may have `ɵprov`, via inheritance.
767 *
768 * @deprecated Will be removed in a future version of Angular, where an error will occur in the
769 * scenario if we find the `ɵprov` on an ancestor only.
770 */
771function getInheritedInjectableDef(type) {
772 const def = type && (type[NG_PROV_DEF] || type[NG_INJECTABLE_DEF]);
773 if (def) {
774 const typeName = getTypeName(type);
775 // TODO(FW-1307): Re-add ngDevMode when closure can handle it
776 // ngDevMode &&
777 console.warn(`DEPRECATED: DI is instantiating a token "${typeName}" that inherits its @Injectable decorator but does not provide one itself.\n` +
778 `This will become an error in a future version of Angular. Please add @Injectable() to the "${typeName}" class.`);
779 return def;
780 }
781 else {
782 return null;
783 }
784}
785/** Gets the name of a type, accounting for some cross-browser differences. */
786function getTypeName(type) {
787 // `Function.prototype.name` behaves differently between IE and other browsers. In most browsers
788 // it'll always return the name of the function itself, no matter how many other functions it
789 // inherits from. On IE the function doesn't have its own `name` property, but it takes it from
790 // the lowest level in the prototype chain. E.g. if we have `class Foo extends Parent` most
791 // browsers will evaluate `Foo.name` to `Foo` while IE will return `Parent`. We work around
792 // the issue by converting the function to a string and parsing its name out that way via a regex.
793 if (type.hasOwnProperty('name')) {
794 return type.name;
795 }
796 const match = ('' + type).match(/^function\s*([^\s(]+)/);
797 return match === null ? '' : match[1];
798}
799/**
800 * Read the injector def type in a way which is immune to accidentally reading inherited value.
801 *
802 * @param type type which may have an injector def (`ɵinj`)
803 */
804function getInjectorDef(type) {
805 return type && (type.hasOwnProperty(NG_INJ_DEF) || type.hasOwnProperty(NG_INJECTOR_DEF)) ?
806 type[NG_INJ_DEF] :
807 null;
808}
809const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty });
810const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty });
811// We need to keep these around so we can read off old defs if new defs are unavailable
812const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty });
813const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty });
814
815/**
816 * Base URL for the error details page.
817 *
818 * Keep this constant in sync across:
819 * - packages/compiler-cli/src/ngtsc/diagnostics/src/error_details_base_url.ts
820 * - packages/core/src/error_details_base_url.ts
821 */
822const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
823/**
824 * URL for the XSS security documentation.
825 */
826const XSS_SECURITY_URL = 'https://g.co/ng/security#xss';
827
828/**
829 * Class that represents a runtime error.
830 * Formats and outputs the error message in a consistent way.
831 *
832 * Example:
833 * ```
834 * throw new RuntimeError(
835 * RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED,
836 * ngDevMode && 'Injector has already been destroyed.');
837 * ```
838 *
839 * Note: the `message` argument contains a descriptive error message as a string in development
840 * mode (when the `ngDevMode` is defined). In production mode (after tree-shaking pass), the
841 * `message` argument becomes `false`, thus we account for it in the typings and the runtime logic.
842 */
843class RuntimeError extends Error {
844 constructor(code, message) {
845 super(formatRuntimeError(code, message));
846 this.code = code;
847 }
848}
849/**
850 * Called to format a runtime error.
851 * See additional info on the `message` argument type in the `RuntimeError` class description.
852 */
853function formatRuntimeError(code, message) {
854 // Error code might be a negative number, which is a special marker that instructs the logic to
855 // generate a link to the error details page on angular.io.
856 // We also prepend `0` to non-compile-time errors.
857 const fullCode = `NG0${Math.abs(code)}`;
858 let errorMessage = `${fullCode}${message ? ': ' + message.trim() : ''}`;
859 if (ngDevMode && code < 0) {
860 const addPeriodSeparator = !errorMessage.match(/[.,;!?]$/);
861 const separator = addPeriodSeparator ? '.' : '';
862 errorMessage =
863 `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
864 }
865 return errorMessage;
866}
867
868/**
869 * @description
870 *
871 * Represents a type that a Component or other object is instances of.
872 *
873 * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by
874 * the `MyCustomComponent` constructor function.
875 *
876 * @publicApi
877 */
878const Type = Function;
879function isType(v) {
880 return typeof v === 'function';
881}
882
883// The functions in this file verify that the assumptions we are making
884function assertNumber(actual, msg) {
885 if (!(typeof actual === 'number')) {
886 throwError(msg, typeof actual, 'number', '===');
887 }
888}
889function assertNumberInRange(actual, minInclusive, maxInclusive) {
890 assertNumber(actual, 'Expected a number');
891 assertLessThanOrEqual(actual, maxInclusive, 'Expected number to be less than or equal to');
892 assertGreaterThanOrEqual(actual, minInclusive, 'Expected number to be greater than or equal to');
893}
894function assertString(actual, msg) {
895 if (!(typeof actual === 'string')) {
896 throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
897 }
898}
899function assertFunction(actual, msg) {
900 if (!(typeof actual === 'function')) {
901 throwError(msg, actual === null ? 'null' : typeof actual, 'function', '===');
902 }
903}
904function assertEqual(actual, expected, msg) {
905 if (!(actual == expected)) {
906 throwError(msg, actual, expected, '==');
907 }
908}
909function assertNotEqual(actual, expected, msg) {
910 if (!(actual != expected)) {
911 throwError(msg, actual, expected, '!=');
912 }
913}
914function assertSame(actual, expected, msg) {
915 if (!(actual === expected)) {
916 throwError(msg, actual, expected, '===');
917 }
918}
919function assertNotSame(actual, expected, msg) {
920 if (!(actual !== expected)) {
921 throwError(msg, actual, expected, '!==');
922 }
923}
924function assertLessThan(actual, expected, msg) {
925 if (!(actual < expected)) {
926 throwError(msg, actual, expected, '<');
927 }
928}
929function assertLessThanOrEqual(actual, expected, msg) {
930 if (!(actual <= expected)) {
931 throwError(msg, actual, expected, '<=');
932 }
933}
934function assertGreaterThan(actual, expected, msg) {
935 if (!(actual > expected)) {
936 throwError(msg, actual, expected, '>');
937 }
938}
939function assertGreaterThanOrEqual(actual, expected, msg) {
940 if (!(actual >= expected)) {
941 throwError(msg, actual, expected, '>=');
942 }
943}
944function assertNotDefined(actual, msg) {
945 if (actual != null) {
946 throwError(msg, actual, null, '==');
947 }
948}
949function assertDefined(actual, msg) {
950 if (actual == null) {
951 throwError(msg, actual, null, '!=');
952 }
953}
954function throwError(msg, actual, expected, comparison) {
955 throw new Error(`ASSERTION ERROR: ${msg}` +
956 (comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
957}
958function assertDomNode(node) {
959 // If we're in a worker, `Node` will not be defined.
960 if (!(typeof Node !== 'undefined' && node instanceof Node) &&
961 !(typeof node === 'object' && node != null &&
962 node.constructor.name === 'WebWorkerRenderNode')) {
963 throwError(`The provided value must be an instance of a DOM Node but got ${stringify(node)}`);
964 }
965}
966function assertIndexInRange(arr, index) {
967 assertDefined(arr, 'Array must be defined.');
968 const maxLen = arr.length;
969 if (index < 0 || index >= maxLen) {
970 throwError(`Index expected to be less than ${maxLen} but got ${index}`);
971 }
972}
973function assertOneOf(value, ...validValues) {
974 if (validValues.indexOf(value) !== -1)
975 return true;
976 throwError(`Expected value to be one of ${JSON.stringify(validValues)} but was ${JSON.stringify(value)}.`);
977}
978
979/**
980 * Determines if the contents of two arrays is identical
981 *
982 * @param a first array
983 * @param b second array
984 * @param identityAccessor Optional function for extracting stable object identity from a value in
985 * the array.
986 */
987function arrayEquals(a, b, identityAccessor) {
988 if (a.length !== b.length)
989 return false;
990 for (let i = 0; i < a.length; i++) {
991 let valueA = a[i];
992 let valueB = b[i];
993 if (identityAccessor) {
994 valueA = identityAccessor(valueA);
995 valueB = identityAccessor(valueB);
996 }
997 if (valueB !== valueA) {
998 return false;
999 }
1000 }
1001 return true;
1002}
1003/**
1004 * Flattens an array.
1005 */
1006function flatten$1(list) {
1007 return list.flat(Number.POSITIVE_INFINITY);
1008}
1009function deepForEach(input, fn) {
1010 input.forEach(value => Array.isArray(value) ? deepForEach(value, fn) : fn(value));
1011}
1012function addToArray(arr, index, value) {
1013 // perf: array.push is faster than array.splice!
1014 if (index >= arr.length) {
1015 arr.push(value);
1016 }
1017 else {
1018 arr.splice(index, 0, value);
1019 }
1020}
1021function removeFromArray(arr, index) {
1022 // perf: array.pop is faster than array.splice!
1023 if (index >= arr.length - 1) {
1024 return arr.pop();
1025 }
1026 else {
1027 return arr.splice(index, 1)[0];
1028 }
1029}
1030function newArray(size, value) {
1031 const list = [];
1032 for (let i = 0; i < size; i++) {
1033 list.push(value);
1034 }
1035 return list;
1036}
1037/**
1038 * Remove item from array (Same as `Array.splice()` but faster.)
1039 *
1040 * `Array.splice()` is not as fast because it has to allocate an array for the elements which were
1041 * removed. This causes memory pressure and slows down code when most of the time we don't
1042 * care about the deleted items array.
1043 *
1044 * https://jsperf.com/fast-array-splice (About 20x faster)
1045 *
1046 * @param array Array to splice
1047 * @param index Index of element in array to remove.
1048 * @param count Number of items to remove.
1049 */
1050function arraySplice(array, index, count) {
1051 const length = array.length - count;
1052 while (index < length) {
1053 array[index] = array[index + count];
1054 index++;
1055 }
1056 while (count--) {
1057 array.pop(); // shrink the array
1058 }
1059}
1060/**
1061 * Same as `Array.splice(index, 0, value)` but faster.
1062 *
1063 * `Array.splice()` is not fast because it has to allocate an array for the elements which were
1064 * removed. This causes memory pressure and slows down code when most of the time we don't
1065 * care about the deleted items array.
1066 *
1067 * @param array Array to splice.
1068 * @param index Index in array where the `value` should be added.
1069 * @param value Value to add to array.
1070 */
1071function arrayInsert(array, index, value) {
1072 ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.');
1073 let end = array.length;
1074 while (end > index) {
1075 const previousEnd = end - 1;
1076 array[end] = array[previousEnd];
1077 end = previousEnd;
1078 }
1079 array[index] = value;
1080}
1081/**
1082 * Same as `Array.splice2(index, 0, value1, value2)` but faster.
1083 *
1084 * `Array.splice()` is not fast because it has to allocate an array for the elements which were
1085 * removed. This causes memory pressure and slows down code when most of the time we don't
1086 * care about the deleted items array.
1087 *
1088 * @param array Array to splice.
1089 * @param index Index in array where the `value` should be added.
1090 * @param value1 Value to add to array.
1091 * @param value2 Value to add to array.
1092 */
1093function arrayInsert2(array, index, value1, value2) {
1094 ngDevMode && assertLessThanOrEqual(index, array.length, 'Can\'t insert past array end.');
1095 let end = array.length;
1096 if (end == index) {
1097 // inserting at the end.
1098 array.push(value1, value2);
1099 }
1100 else if (end === 1) {
1101 // corner case when we have less items in array than we have items to insert.
1102 array.push(value2, array[0]);
1103 array[0] = value1;
1104 }
1105 else {
1106 end--;
1107 array.push(array[end - 1], array[end]);
1108 while (end > index) {
1109 const previousEnd = end - 2;
1110 array[end] = array[previousEnd];
1111 end--;
1112 }
1113 array[index] = value1;
1114 array[index + 1] = value2;
1115 }
1116}
1117/**
1118 * Get an index of an `value` in a sorted `array`.
1119 *
1120 * NOTE:
1121 * - This uses binary search algorithm for fast removals.
1122 *
1123 * @param array A sorted array to binary search.
1124 * @param value The value to look for.
1125 * @returns index of the value.
1126 * - positive index if value found.
1127 * - negative index if value not found. (`~index` to get the value where it should have been
1128 * located)
1129 */
1130function arrayIndexOfSorted(array, value) {
1131 return _arrayIndexOfSorted(array, value, 0);
1132}
1133/**
1134 * Set a `value` for a `key`.
1135 *
1136 * @param keyValueArray to modify.
1137 * @param key The key to locate or create.
1138 * @param value The value to set for a `key`.
1139 * @returns index (always even) of where the value vas set.
1140 */
1141function keyValueArraySet(keyValueArray, key, value) {
1142 let index = keyValueArrayIndexOf(keyValueArray, key);
1143 if (index >= 0) {
1144 // if we found it set it.
1145 keyValueArray[index | 1] = value;
1146 }
1147 else {
1148 index = ~index;
1149 arrayInsert2(keyValueArray, index, key, value);
1150 }
1151 return index;
1152}
1153/**
1154 * Retrieve a `value` for a `key` (on `undefined` if not found.)
1155 *
1156 * @param keyValueArray to search.
1157 * @param key The key to locate.
1158 * @return The `value` stored at the `key` location or `undefined if not found.
1159 */
1160function keyValueArrayGet(keyValueArray, key) {
1161 const index = keyValueArrayIndexOf(keyValueArray, key);
1162 if (index >= 0) {
1163 // if we found it retrieve it.
1164 return keyValueArray[index | 1];
1165 }
1166 return undefined;
1167}
1168/**
1169 * Retrieve a `key` index value in the array or `-1` if not found.
1170 *
1171 * @param keyValueArray to search.
1172 * @param key The key to locate.
1173 * @returns index of where the key is (or should have been.)
1174 * - positive (even) index if key found.
1175 * - negative index if key not found. (`~index` (even) to get the index where it should have
1176 * been inserted.)
1177 */
1178function keyValueArrayIndexOf(keyValueArray, key) {
1179 return _arrayIndexOfSorted(keyValueArray, key, 1);
1180}
1181/**
1182 * Delete a `key` (and `value`) from the `KeyValueArray`.
1183 *
1184 * @param keyValueArray to modify.
1185 * @param key The key to locate or delete (if exist).
1186 * @returns index of where the key was (or should have been.)
1187 * - positive (even) index if key found and deleted.
1188 * - negative index if key not found. (`~index` (even) to get the index where it should have
1189 * been.)
1190 */
1191function keyValueArrayDelete(keyValueArray, key) {
1192 const index = keyValueArrayIndexOf(keyValueArray, key);
1193 if (index >= 0) {
1194 // if we found it remove it.
1195 arraySplice(keyValueArray, index, 2);
1196 }
1197 return index;
1198}
1199/**
1200 * INTERNAL: Get an index of an `value` in a sorted `array` by grouping search by `shift`.
1201 *
1202 * NOTE:
1203 * - This uses binary search algorithm for fast removals.
1204 *
1205 * @param array A sorted array to binary search.
1206 * @param value The value to look for.
1207 * @param shift grouping shift.
1208 * - `0` means look at every location
1209 * - `1` means only look at every other (even) location (the odd locations are to be ignored as
1210 * they are values.)
1211 * @returns index of the value.
1212 * - positive index if value found.
1213 * - negative index if value not found. (`~index` to get the value where it should have been
1214 * inserted)
1215 */
1216function _arrayIndexOfSorted(array, value, shift) {
1217 ngDevMode && assertEqual(Array.isArray(array), true, 'Expecting an array');
1218 let start = 0;
1219 let end = array.length >> shift;
1220 while (end !== start) {
1221 const middle = start + ((end - start) >> 1); // find the middle.
1222 const current = array[middle << shift];
1223 if (value === current) {
1224 return (middle << shift);
1225 }
1226 else if (current > value) {
1227 end = middle;
1228 }
1229 else {
1230 start = middle + 1; // We already searched middle so make it non-inclusive by adding 1
1231 }
1232 }
1233 return ~(end << shift);
1234}
1235
1236/**
1237 * Convince closure compiler that the wrapped function has no side-effects.
1238 *
1239 * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to
1240 * allow us to execute a function but have closure compiler mark the call as no-side-effects.
1241 * It is important that the return value for the `noSideEffects` function be assigned
1242 * to something which is retained otherwise the call to `noSideEffects` will be removed by closure
1243 * compiler.
1244 */
1245function noSideEffects(fn) {
1246 return { toString: fn }.toString();
1247}
1248
1249const ANNOTATIONS = '__annotations__';
1250const PARAMETERS = '__parameters__';
1251const PROP_METADATA = '__prop__metadata__';
1252/**
1253 * @suppress {globalThis}
1254 */
1255function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) {
1256 return noSideEffects(() => {
1257 const metaCtor = makeMetadataCtor(props);
1258 function DecoratorFactory(...args) {
1259 if (this instanceof DecoratorFactory) {
1260 metaCtor.call(this, ...args);
1261 return this;
1262 }
1263 const annotationInstance = new DecoratorFactory(...args);
1264 return function TypeDecorator(cls) {
1265 if (typeFn)
1266 typeFn(cls, ...args);
1267 // Use of Object.defineProperty is important since it creates non-enumerable property which
1268 // prevents the property is copied during subclassing.
1269 const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
1270 cls[ANNOTATIONS] :
1271 Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS];
1272 annotations.push(annotationInstance);
1273 if (additionalProcessing)
1274 additionalProcessing(cls);
1275 return cls;
1276 };
1277 }
1278 if (parentClass) {
1279 DecoratorFactory.prototype = Object.create(parentClass.prototype);
1280 }
1281 DecoratorFactory.prototype.ngMetadataName = name;
1282 DecoratorFactory.annotationCls = DecoratorFactory;
1283 return DecoratorFactory;
1284 });
1285}
1286function makeMetadataCtor(props) {
1287 return function ctor(...args) {
1288 if (props) {
1289 const values = props(...args);
1290 for (const propName in values) {
1291 this[propName] = values[propName];
1292 }
1293 }
1294 };
1295}
1296function makeParamDecorator(name, props, parentClass) {
1297 return noSideEffects(() => {
1298 const metaCtor = makeMetadataCtor(props);
1299 function ParamDecoratorFactory(...args) {
1300 if (this instanceof ParamDecoratorFactory) {
1301 metaCtor.apply(this, args);
1302 return this;
1303 }
1304 const annotationInstance = new ParamDecoratorFactory(...args);
1305 ParamDecorator.annotation = annotationInstance;
1306 return ParamDecorator;
1307 function ParamDecorator(cls, unusedKey, index) {
1308 // Use of Object.defineProperty is important since it creates non-enumerable property which
1309 // prevents the property is copied during subclassing.
1310 const parameters = cls.hasOwnProperty(PARAMETERS) ?
1311 cls[PARAMETERS] :
1312 Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS];
1313 // there might be gaps if some in between parameters do not have annotations.
1314 // we pad with nulls.
1315 while (parameters.length <= index) {
1316 parameters.push(null);
1317 }
1318 (parameters[index] = parameters[index] || []).push(annotationInstance);
1319 return cls;
1320 }
1321 }
1322 if (parentClass) {
1323 ParamDecoratorFactory.prototype = Object.create(parentClass.prototype);
1324 }
1325 ParamDecoratorFactory.prototype.ngMetadataName = name;
1326 ParamDecoratorFactory.annotationCls = ParamDecoratorFactory;
1327 return ParamDecoratorFactory;
1328 });
1329}
1330function makePropDecorator(name, props, parentClass, additionalProcessing) {
1331 return noSideEffects(() => {
1332 const metaCtor = makeMetadataCtor(props);
1333 function PropDecoratorFactory(...args) {
1334 if (this instanceof PropDecoratorFactory) {
1335 metaCtor.apply(this, args);
1336 return this;
1337 }
1338 const decoratorInstance = new PropDecoratorFactory(...args);
1339 function PropDecorator(target, name) {
1340 const constructor = target.constructor;
1341 // Use of Object.defineProperty is important because it creates a non-enumerable property
1342 // which prevents the property from being copied during subclassing.
1343 const meta = constructor.hasOwnProperty(PROP_METADATA) ?
1344 constructor[PROP_METADATA] :
1345 Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA];
1346 meta[name] = meta.hasOwnProperty(name) && meta[name] || [];
1347 meta[name].unshift(decoratorInstance);
1348 if (additionalProcessing)
1349 additionalProcessing(target, name, ...args);
1350 }
1351 return PropDecorator;
1352 }
1353 if (parentClass) {
1354 PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
1355 }
1356 PropDecoratorFactory.prototype.ngMetadataName = name;
1357 PropDecoratorFactory.annotationCls = PropDecoratorFactory;
1358 return PropDecoratorFactory;
1359 });
1360}
1361
1362/*
1363 * #########################
1364 * Attention: These Regular expressions have to hold even if the code is minified!
1365 * ##########################
1366 */
1367/**
1368 * Regular expression that detects pass-through constructors for ES5 output. This Regex
1369 * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also
1370 * it intends to capture the pattern where existing constructors have been downleveled from
1371 * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g.
1372 *
1373 * ```
1374 * function MyClass() {
1375 * var _this = _super.apply(this, arguments) || this;
1376 * ```
1377 *
1378 * downleveled to ES5 with `downlevelIteration` for TypeScript < 4.2:
1379 * ```
1380 * function MyClass() {
1381 * var _this = _super.apply(this, __spread(arguments)) || this;
1382 * ```
1383 *
1384 * or downleveled to ES5 with `downlevelIteration` for TypeScript >= 4.2:
1385 * ```
1386 * function MyClass() {
1387 * var _this = _super.apply(this, __spreadArray([], __read(arguments), false)) || this;
1388 * ```
1389 *
1390 * More details can be found in: https://github.com/angular/angular/issues/38453.
1391 */
1392const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|(?:[^()]+\(\[\],)?[^()]+\(arguments\).*)\)/;
1393/** Regular expression that detects ES2015 classes which extend from other classes. */
1394const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/;
1395/**
1396 * Regular expression that detects ES2015 classes which extend from other classes and
1397 * have an explicit constructor defined.
1398 */
1399const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/;
1400/**
1401 * Regular expression that detects ES2015 classes which extend from other classes
1402 * and inherit a constructor.
1403 */
1404const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{[^}]*super\(\.\.\.arguments\)/;
1405/**
1406 * Determine whether a stringified type is a class which delegates its constructor
1407 * to its parent.
1408 *
1409 * This is not trivial since compiled code can actually contain a constructor function
1410 * even if the original source code did not. For instance, when the child class contains
1411 * an initialized instance property.
1412 */
1413function isDelegateCtor(typeStr) {
1414 return ES5_DELEGATE_CTOR.test(typeStr) ||
1415 ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) ||
1416 (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr));
1417}
1418class ReflectionCapabilities {
1419 constructor(reflect) {
1420 this._reflect = reflect || _global$1['Reflect'];
1421 }
1422 factory(t) {
1423 return (...args) => new t(...args);
1424 }
1425 /** @internal */
1426 _zipTypesAndAnnotations(paramTypes, paramAnnotations) {
1427 let result;
1428 if (typeof paramTypes === 'undefined') {
1429 result = newArray(paramAnnotations.length);
1430 }
1431 else {
1432 result = newArray(paramTypes.length);
1433 }
1434 for (let i = 0; i < result.length; i++) {
1435 // TS outputs Object for parameters without types, while Traceur omits
1436 // the annotations. For now we preserve the Traceur behavior to aid
1437 // migration, but this can be revisited.
1438 if (typeof paramTypes === 'undefined') {
1439 result[i] = [];
1440 }
1441 else if (paramTypes[i] && paramTypes[i] != Object) {
1442 result[i] = [paramTypes[i]];
1443 }
1444 else {
1445 result[i] = [];
1446 }
1447 if (paramAnnotations && paramAnnotations[i] != null) {
1448 result[i] = result[i].concat(paramAnnotations[i]);
1449 }
1450 }
1451 return result;
1452 }
1453 _ownParameters(type, parentCtor) {
1454 const typeStr = type.toString();
1455 // If we have no decorators, we only have function.length as metadata.
1456 // In that case, to detect whether a child class declared an own constructor or not,
1457 // we need to look inside of that constructor to check whether it is
1458 // just calling the parent.
1459 // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
1460 // that sets 'design:paramtypes' to []
1461 // if a class inherits from another class but has no ctor declared itself.
1462 if (isDelegateCtor(typeStr)) {
1463 return null;
1464 }
1465 // Prefer the direct API.
1466 if (type.parameters && type.parameters !== parentCtor.parameters) {
1467 return type.parameters;
1468 }
1469 // API of tsickle for lowering decorators to properties on the class.
1470 const tsickleCtorParams = type.ctorParameters;
1471 if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) {
1472 // Newer tsickle uses a function closure
1473 // Retain the non-function case for compatibility with older tsickle
1474 const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams;
1475 const paramTypes = ctorParameters.map((ctorParam) => ctorParam && ctorParam.type);
1476 const paramAnnotations = ctorParameters.map((ctorParam) => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
1477 return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
1478 }
1479 // API for metadata created by invoking the decorators.
1480 const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS];
1481 const paramTypes = this._reflect && this._reflect.getOwnMetadata &&
1482 this._reflect.getOwnMetadata('design:paramtypes', type);
1483 if (paramTypes || paramAnnotations) {
1484 return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
1485 }
1486 // If a class has no decorators, at least create metadata
1487 // based on function.length.
1488 // Note: We know that this is a real constructor as we checked
1489 // the content of the constructor above.
1490 return newArray(type.length);
1491 }
1492 parameters(type) {
1493 // Note: only report metadata if we have at least one class decorator
1494 // to stay in sync with the static reflector.
1495 if (!isType(type)) {
1496 return [];
1497 }
1498 const parentCtor = getParentCtor(type);
1499 let parameters = this._ownParameters(type, parentCtor);
1500 if (!parameters && parentCtor !== Object) {
1501 parameters = this.parameters(parentCtor);
1502 }
1503 return parameters || [];
1504 }
1505 _ownAnnotations(typeOrFunc, parentCtor) {
1506 // Prefer the direct API.
1507 if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) {
1508 let annotations = typeOrFunc.annotations;
1509 if (typeof annotations === 'function' && annotations.annotations) {
1510 annotations = annotations.annotations;
1511 }
1512 return annotations;
1513 }
1514 // API of tsickle for lowering decorators to properties on the class.
1515 if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) {
1516 return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators);
1517 }
1518 // API for metadata created by invoking the decorators.
1519 if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
1520 return typeOrFunc[ANNOTATIONS];
1521 }
1522 return null;
1523 }
1524 annotations(typeOrFunc) {
1525 if (!isType(typeOrFunc)) {
1526 return [];
1527 }
1528 const parentCtor = getParentCtor(typeOrFunc);
1529 const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || [];
1530 const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : [];
1531 return parentAnnotations.concat(ownAnnotations);
1532 }
1533 _ownPropMetadata(typeOrFunc, parentCtor) {
1534 // Prefer the direct API.
1535 if (typeOrFunc.propMetadata &&
1536 typeOrFunc.propMetadata !== parentCtor.propMetadata) {
1537 let propMetadata = typeOrFunc.propMetadata;
1538 if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
1539 propMetadata = propMetadata.propMetadata;
1540 }
1541 return propMetadata;
1542 }
1543 // API of tsickle for lowering decorators to properties on the class.
1544 if (typeOrFunc.propDecorators &&
1545 typeOrFunc.propDecorators !== parentCtor.propDecorators) {
1546 const propDecorators = typeOrFunc.propDecorators;
1547 const propMetadata = {};
1548 Object.keys(propDecorators).forEach(prop => {
1549 propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
1550 });
1551 return propMetadata;
1552 }
1553 // API for metadata created by invoking the decorators.
1554 if (typeOrFunc.hasOwnProperty(PROP_METADATA)) {
1555 return typeOrFunc[PROP_METADATA];
1556 }
1557 return null;
1558 }
1559 propMetadata(typeOrFunc) {
1560 if (!isType(typeOrFunc)) {
1561 return {};
1562 }
1563 const parentCtor = getParentCtor(typeOrFunc);
1564 const propMetadata = {};
1565 if (parentCtor !== Object) {
1566 const parentPropMetadata = this.propMetadata(parentCtor);
1567 Object.keys(parentPropMetadata).forEach((propName) => {
1568 propMetadata[propName] = parentPropMetadata[propName];
1569 });
1570 }
1571 const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor);
1572 if (ownPropMetadata) {
1573 Object.keys(ownPropMetadata).forEach((propName) => {
1574 const decorators = [];
1575 if (propMetadata.hasOwnProperty(propName)) {
1576 decorators.push(...propMetadata[propName]);
1577 }
1578 decorators.push(...ownPropMetadata[propName]);
1579 propMetadata[propName] = decorators;
1580 });
1581 }
1582 return propMetadata;
1583 }
1584 ownPropMetadata(typeOrFunc) {
1585 if (!isType(typeOrFunc)) {
1586 return {};
1587 }
1588 return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {};
1589 }
1590 hasLifecycleHook(type, lcProperty) {
1591 return type instanceof Type && lcProperty in type.prototype;
1592 }
1593}
1594function convertTsickleDecoratorIntoMetadata(decoratorInvocations) {
1595 if (!decoratorInvocations) {
1596 return [];
1597 }
1598 return decoratorInvocations.map(decoratorInvocation => {
1599 const decoratorType = decoratorInvocation.type;
1600 const annotationCls = decoratorType.annotationCls;
1601 const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
1602 return new annotationCls(...annotationArgs);
1603 });
1604}
1605function getParentCtor(ctor) {
1606 const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null;
1607 const parentCtor = parentProto ? parentProto.constructor : null;
1608 // Note: We always use `Object` as the null value
1609 // to simplify checking later on.
1610 return parentCtor || Object;
1611}
1612
1613function ngDevModeResetPerfCounters() {
1614 const locationString = typeof location !== 'undefined' ? location.toString() : '';
1615 const newCounters = {
1616 namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1,
1617 firstCreatePass: 0,
1618 tNode: 0,
1619 tView: 0,
1620 rendererCreateTextNode: 0,
1621 rendererSetText: 0,
1622 rendererCreateElement: 0,
1623 rendererAddEventListener: 0,
1624 rendererSetAttribute: 0,
1625 rendererRemoveAttribute: 0,
1626 rendererSetProperty: 0,
1627 rendererSetClassName: 0,
1628 rendererAddClass: 0,
1629 rendererRemoveClass: 0,
1630 rendererSetStyle: 0,
1631 rendererRemoveStyle: 0,
1632 rendererDestroy: 0,
1633 rendererDestroyNode: 0,
1634 rendererMoveNode: 0,
1635 rendererRemoveNode: 0,
1636 rendererAppendChild: 0,
1637 rendererInsertBefore: 0,
1638 rendererCreateComment: 0,
1639 };
1640 // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
1641 const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
1642 _global$1['ngDevMode'] = allowNgDevModeTrue && newCounters;
1643 return newCounters;
1644}
1645/**
1646 * This function checks to see if the `ngDevMode` has been set. If yes,
1647 * then we honor it, otherwise we default to dev mode with additional checks.
1648 *
1649 * The idea is that unless we are doing production build where we explicitly
1650 * set `ngDevMode == false` we should be helping the developer by providing
1651 * as much early warning and errors as possible.
1652 *
1653 * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
1654 * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
1655 * is defined for the entire instruction set.
1656 *
1657 * When checking `ngDevMode` on toplevel, always init it before referencing it
1658 * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
1659 * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
1660 *
1661 * Details on possible values for `ngDevMode` can be found on its docstring.
1662 *
1663 * NOTE:
1664 * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`.
1665 */
1666function initNgDevMode() {
1667 // The below checks are to ensure that calling `initNgDevMode` multiple times does not
1668 // reset the counters.
1669 // If the `ngDevMode` is not an object, then it means we have not created the perf counters
1670 // yet.
1671 if (typeof ngDevMode === 'undefined' || ngDevMode) {
1672 if (typeof ngDevMode !== 'object') {
1673 ngDevModeResetPerfCounters();
1674 }
1675 return typeof ngDevMode !== 'undefined' && !!ngDevMode;
1676 }
1677 return false;
1678}
1679
1680function isEnvironmentProviders(value) {
1681 return value && !!value.ɵproviders;
1682}
1683
1684/**
1685 * Used for stringify render output in Ivy.
1686 * Important! This function is very performance-sensitive and we should
1687 * be extra careful not to introduce megamorphic reads in it.
1688 * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations.
1689 */
1690function renderStringify(value) {
1691 if (typeof value === 'string')
1692 return value;
1693 if (value == null)
1694 return '';
1695 // Use `String` so that it invokes the `toString` method of the value. Note that this
1696 // appears to be faster than calling `value.toString` (see `render_stringify` benchmark).
1697 return String(value);
1698}
1699/**
1700 * Used to stringify a value so that it can be displayed in an error message.
1701 * Important! This function contains a megamorphic read and should only be
1702 * used for error messages.
1703 */
1704function stringifyForError(value) {
1705 if (typeof value === 'function')
1706 return value.name || value.toString();
1707 if (typeof value === 'object' && value != null && typeof value.type === 'function') {
1708 return value.type.name || value.type.toString();
1709 }
1710 return renderStringify(value);
1711}
1712
1713/** Called when directives inject each other (creating a circular dependency) */
1714function throwCyclicDependencyError(token, path) {
1715 const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
1716 throw new RuntimeError(-200 /* RuntimeErrorCode.CYCLIC_DI_DEPENDENCY */, `Circular dependency in DI detected for ${token}${depPath}`);
1717}
1718function throwMixedMultiProviderError() {
1719 throw new Error(`Cannot mix multi providers and regular providers`);
1720}
1721function throwInvalidProviderError(ngModuleType, providers, provider) {
1722 if (ngModuleType && providers) {
1723 const providerDetail = providers.map(v => v == provider ? '?' + provider + '?' : '...');
1724 throw new Error(`Invalid provider for the NgModule '${stringify(ngModuleType)}' - only instances of Provider and Type are allowed, got: [${providerDetail.join(', ')}]`);
1725 }
1726 else if (isEnvironmentProviders(provider)) {
1727 if (provider.ɵfromNgModule) {
1728 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.`);
1729 }
1730 else {
1731 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.`);
1732 }
1733 }
1734 else {
1735 throw new Error('Invalid provider');
1736 }
1737}
1738/** Throws an error when a token is not found in DI. */
1739function throwProviderNotFoundError(token, injectorName) {
1740 const injectorDetails = injectorName ? ` in ${injectorName}` : '';
1741 throw new RuntimeError(-201 /* RuntimeErrorCode.PROVIDER_NOT_FOUND */, ngDevMode && `No provider for ${stringifyForError(token)} found${injectorDetails}`);
1742}
1743
1744/**
1745 * Injection flags for DI.
1746 *
1747 * @publicApi
1748 * @deprecated use an options object for `inject` instead.
1749 */
1750var InjectFlags;
1751(function (InjectFlags) {
1752 // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer
1753 // writes exports of it into ngfactory files.
1754 /** Check self and check parent injector if needed */
1755 InjectFlags[InjectFlags["Default"] = 0] = "Default";
1756 /**
1757 * Specifies that an injector should retrieve a dependency from any injector until reaching the
1758 * host element of the current component. (Only used with Element Injector)
1759 */
1760 InjectFlags[InjectFlags["Host"] = 1] = "Host";
1761 /** Don't ascend to ancestors of the node requesting injection. */
1762 InjectFlags[InjectFlags["Self"] = 2] = "Self";
1763 /** Skip the node that is requesting injection. */
1764 InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf";
1765 /** Inject `defaultValue` instead if token not found. */
1766 InjectFlags[InjectFlags["Optional"] = 8] = "Optional";
1767})(InjectFlags || (InjectFlags = {}));
1768
1769/**
1770 * Current implementation of inject.
1771 *
1772 * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
1773 * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
1774 * way for two reasons:
1775 * 1. `Injector` should not depend on ivy logic.
1776 * 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
1777 */
1778let _injectImplementation;
1779function getInjectImplementation() {
1780 return _injectImplementation;
1781}
1782/**
1783 * Sets the current inject implementation.
1784 */
1785function setInjectImplementation(impl) {
1786 const previous = _injectImplementation;
1787 _injectImplementation = impl;
1788 return previous;
1789}
1790/**
1791 * Injects `root` tokens in limp mode.
1792 *
1793 * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
1794 * `"root"`. This is known as the limp mode injection. In such case the value is stored in the
1795 * injectable definition.
1796 */
1797function injectRootLimpMode(token, notFoundValue, flags) {
1798 const injectableDef = getInjectableDef(token);
1799 if (injectableDef && injectableDef.providedIn == 'root') {
1800 return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
1801 injectableDef.value;
1802 }
1803 if (flags & InjectFlags.Optional)
1804 return null;
1805 if (notFoundValue !== undefined)
1806 return notFoundValue;
1807 throwProviderNotFoundError(stringify(token), 'Injector');
1808}
1809/**
1810 * Assert that `_injectImplementation` is not `fn`.
1811 *
1812 * This is useful, to prevent infinite recursion.
1813 *
1814 * @param fn Function which it should not equal to
1815 */
1816function assertInjectImplementationNotEqual(fn) {
1817 ngDevMode &&
1818 assertNotEqual(_injectImplementation, fn, 'Calling ɵɵinject would cause infinite recursion');
1819}
1820
1821const _THROW_IF_NOT_FOUND = {};
1822const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
1823/*
1824 * Name of a property (that we patch onto DI decorator), which is used as an annotation of which
1825 * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators
1826 * in the code, thus making them tree-shakable.
1827 */
1828const DI_DECORATOR_FLAG = '__NG_DI_FLAG__';
1829const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
1830const NG_TOKEN_PATH = 'ngTokenPath';
1831const NEW_LINE = /\n/gm;
1832const NO_NEW_LINE = 'ɵ';
1833const SOURCE = '__source';
1834/**
1835 * Current injector value used by `inject`.
1836 * - `undefined`: it is an error to call `inject`
1837 * - `null`: `inject` can be called but there is no injector (limp-mode).
1838 * - Injector instance: Use the injector for resolution.
1839 */
1840let _currentInjector = undefined;
1841function setCurrentInjector(injector) {
1842 const former = _currentInjector;
1843 _currentInjector = injector;
1844 return former;
1845}
1846function injectInjectorOnly(token, flags = InjectFlags.Default) {
1847 if (_currentInjector === undefined) {
1848 throw new RuntimeError(-203 /* RuntimeErrorCode.MISSING_INJECTION_CONTEXT */, ngDevMode &&
1849 `inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with \`EnvironmentInjector#runInContext\`.`);
1850 }
1851 else if (_currentInjector === null) {
1852 return injectRootLimpMode(token, undefined, flags);
1853 }
1854 else {
1855 return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
1856 }
1857}
1858function ɵɵinject(token, flags = InjectFlags.Default) {
1859 return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef(token), flags);
1860}
1861/**
1862 * Throws an error indicating that a factory function could not be generated by the compiler for a
1863 * particular class.
1864 *
1865 * The name of the class is not mentioned here, but will be in the generated factory function name
1866 * and thus in the stack trace.
1867 *
1868 * @codeGenApi
1869 */
1870function ɵɵinvalidFactoryDep(index) {
1871 throw new RuntimeError(202 /* RuntimeErrorCode.INVALID_FACTORY_DEPENDENCY */, ngDevMode &&
1872 `This constructor is not compatible with Angular Dependency Injection because its dependency at index ${index} of the parameter list is invalid.
1873This can happen if the dependency type is a primitive like a string or if an ancestor of this class is missing an Angular decorator.
1874
1875Please 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.`);
1876}
1877/**
1878 * Injects a token from the currently active injector.
1879 * `inject` is only supported during instantiation of a dependency by the DI system. It can be used
1880 * during:
1881 * - Construction (via the `constructor`) of a class being instantiated by the DI system, such
1882 * as an `@Injectable` or `@Component`.
1883 * - In the initializer for fields of such classes.
1884 * - In the factory function specified for `useFactory` of a `Provider` or an `@Injectable`.
1885 * - In the `factory` function specified for an `InjectionToken`.
1886 *
1887 * @param token A token that represents a dependency that should be injected.
1888 * @param flags Optional flags that control how injection is executed.
1889 * The flags correspond to injection strategies that can be specified with
1890 * parameter decorators `@Host`, `@Self`, `@SkipSelf`, and `@Optional`.
1891 * @returns the injected value if operation is successful, `null` otherwise.
1892 * @throws if called outside of a supported context.
1893 *
1894 * @usageNotes
1895 * In practice the `inject()` calls are allowed in a constructor, a constructor parameter and a
1896 * field initializer:
1897 *
1898 * ```typescript
1899 * @Injectable({providedIn: 'root'})
1900 * export class Car {
1901 * radio: Radio|undefined;
1902 * // OK: field initializer
1903 * spareTyre = inject(Tyre);
1904 *
1905 * constructor() {
1906 * // OK: constructor body
1907 * this.radio = inject(Radio);
1908 * }
1909 * }
1910 * ```
1911 *
1912 * It is also legal to call `inject` from a provider's factory:
1913 *
1914 * ```typescript
1915 * providers: [
1916 * {provide: Car, useFactory: () => {
1917 * // OK: a class factory
1918 * const engine = inject(Engine);
1919 * return new Car(engine);
1920 * }}
1921 * ]
1922 * ```
1923 *
1924 * Calls to the `inject()` function outside of the class creation context will result in error. Most
1925 * notably, calls to `inject()` are disallowed after a class instance was created, in methods
1926 * (including lifecycle hooks):
1927 *
1928 * ```typescript
1929 * @Component({ ... })
1930 * export class CarComponent {
1931 * ngOnInit() {
1932 * // ERROR: too late, the component instance was already created
1933 * const engine = inject(Engine);
1934 * engine.start();
1935 * }
1936 * }
1937 * ```
1938 *
1939 * @publicApi
1940 */
1941function inject$1(token, flags = InjectFlags.Default) {
1942 return ɵɵinject(token, convertToBitFlags(flags));
1943}
1944// Converts object-based DI flags (`InjectOptions`) to bit flags (`InjectFlags`).
1945function convertToBitFlags(flags) {
1946 if (typeof flags === 'undefined' || typeof flags === 'number') {
1947 return flags;
1948 }
1949 // While TypeScript doesn't accept it without a cast, bitwise OR with false-y values in
1950 // JavaScript is a no-op. We can use that for a very codesize-efficient conversion from
1951 // `InjectOptions` to `InjectFlags`.
1952 return (0 /* InternalInjectFlags.Default */ | // comment to force a line break in the formatter
1953 (flags.optional && 8 /* InternalInjectFlags.Optional */) |
1954 (flags.host && 1 /* InternalInjectFlags.Host */) |
1955 (flags.self && 2 /* InternalInjectFlags.Self */) |
1956 (flags.skipSelf && 4 /* InternalInjectFlags.SkipSelf */));
1957}
1958function injectArgs(types) {
1959 const args = [];
1960 for (let i = 0; i < types.length; i++) {
1961 const arg = resolveForwardRef(types[i]);
1962 if (Array.isArray(arg)) {
1963 if (arg.length === 0) {
1964 throw new RuntimeError(900 /* RuntimeErrorCode.INVALID_DIFFER_INPUT */, ngDevMode && 'Arguments array must have arguments.');
1965 }
1966 let type = undefined;
1967 let flags = InjectFlags.Default;
1968 for (let j = 0; j < arg.length; j++) {
1969 const meta = arg[j];
1970 const flag = getInjectFlag(meta);
1971 if (typeof flag === 'number') {
1972 // Special case when we handle @Inject decorator.
1973 if (flag === -1 /* DecoratorFlags.Inject */) {
1974 type = meta.token;
1975 }
1976 else {
1977 flags |= flag;
1978 }
1979 }
1980 else {
1981 type = meta;
1982 }
1983 }
1984 args.push(ɵɵinject(type, flags));
1985 }
1986 else {
1987 args.push(ɵɵinject(arg));
1988 }
1989 }
1990 return args;
1991}
1992/**
1993 * Attaches a given InjectFlag to a given decorator using monkey-patching.
1994 * Since DI decorators can be used in providers `deps` array (when provider is configured using
1995 * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we
1996 * attach the flag to make it available both as a static property and as a field on decorator
1997 * instance.
1998 *
1999 * @param decorator Provided DI decorator.
2000 * @param flag InjectFlag that should be applied.
2001 */
2002function attachInjectFlag(decorator, flag) {
2003 decorator[DI_DECORATOR_FLAG] = flag;
2004 decorator.prototype[DI_DECORATOR_FLAG] = flag;
2005 return decorator;
2006}
2007/**
2008 * Reads monkey-patched property that contains InjectFlag attached to a decorator.
2009 *
2010 * @param token Token that may contain monkey-patched DI flags property.
2011 */
2012function getInjectFlag(token) {
2013 return token[DI_DECORATOR_FLAG];
2014}
2015function catchInjectorError(e, token, injectorErrorName, source) {
2016 const tokenPath = e[NG_TEMP_TOKEN_PATH];
2017 if (token[SOURCE]) {
2018 tokenPath.unshift(token[SOURCE]);
2019 }
2020 e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
2021 e[NG_TOKEN_PATH] = tokenPath;
2022 e[NG_TEMP_TOKEN_PATH] = null;
2023 throw e;
2024}
2025function formatError(text, obj, injectorErrorName, source = null) {
2026 text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.slice(2) : text;
2027 let context = stringify(obj);
2028 if (Array.isArray(obj)) {
2029 context = obj.map(stringify).join(' -> ');
2030 }
2031 else if (typeof obj === 'object') {
2032 let parts = [];
2033 for (let key in obj) {
2034 if (obj.hasOwnProperty(key)) {
2035 let value = obj[key];
2036 parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify(value)));
2037 }
2038 }
2039 context = `{${parts.join(', ')}}`;
2040 }
2041 return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
2042}
2043
2044/**
2045 * Inject decorator and metadata.
2046 *
2047 * @Annotation
2048 * @publicApi
2049 */
2050const Inject = attachInjectFlag(
2051// Disable tslint because `DecoratorFlags` is a const enum which gets inlined.
2052// tslint:disable-next-line: no-toplevel-property-access
2053makeParamDecorator('Inject', (token) => ({ token })), -1 /* DecoratorFlags.Inject */);
2054/**
2055 * Optional decorator and metadata.
2056 *
2057 * @Annotation
2058 * @publicApi
2059 */
2060const Optional =
2061// Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
2062// tslint:disable-next-line: no-toplevel-property-access
2063attachInjectFlag(makeParamDecorator('Optional'), 8 /* InternalInjectFlags.Optional */);
2064/**
2065 * Self decorator and metadata.
2066 *
2067 * @Annotation
2068 * @publicApi
2069 */
2070const Self =
2071// Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
2072// tslint:disable-next-line: no-toplevel-property-access
2073attachInjectFlag(makeParamDecorator('Self'), 2 /* InternalInjectFlags.Self */);
2074/**
2075 * `SkipSelf` decorator and metadata.
2076 *
2077 * @Annotation
2078 * @publicApi
2079 */
2080const SkipSelf =
2081// Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
2082// tslint:disable-next-line: no-toplevel-property-access
2083attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* InternalInjectFlags.SkipSelf */);
2084/**
2085 * Host decorator and metadata.
2086 *
2087 * @Annotation
2088 * @publicApi
2089 */
2090const Host =
2091// Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
2092// tslint:disable-next-line: no-toplevel-property-access
2093attachInjectFlag(makeParamDecorator('Host'), 1 /* InternalInjectFlags.Host */);
2094
2095/**
2096 * The strategy that the default change detector uses to detect changes.
2097 * When set, takes effect the next time change detection is triggered.
2098 *
2099 * @see {@link ChangeDetectorRef#usage-notes Change detection usage}
2100 *
2101 * @publicApi
2102 */
2103var ChangeDetectionStrategy;
2104(function (ChangeDetectionStrategy) {
2105 /**
2106 * Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
2107 * until reactivated by setting the strategy to `Default` (`CheckAlways`).
2108 * Change detection can still be explicitly invoked.
2109 * This strategy applies to all child directives and cannot be overridden.
2110 */
2111 ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
2112 /**
2113 * Use the default `CheckAlways` strategy, in which change detection is automatic until
2114 * explicitly deactivated.
2115 */
2116 ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
2117})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
2118/**
2119 * Defines the possible states of the default change detector.
2120 * @see `ChangeDetectorRef`
2121 */
2122var ChangeDetectorStatus;
2123(function (ChangeDetectorStatus) {
2124 /**
2125 * A state in which, after calling `detectChanges()`, the change detector
2126 * state becomes `Checked`, and must be explicitly invoked or reactivated.
2127 */
2128 ChangeDetectorStatus[ChangeDetectorStatus["CheckOnce"] = 0] = "CheckOnce";
2129 /**
2130 * A state in which change detection is skipped until the change detector mode
2131 * becomes `CheckOnce`.
2132 */
2133 ChangeDetectorStatus[ChangeDetectorStatus["Checked"] = 1] = "Checked";
2134 /**
2135 * A state in which change detection continues automatically until explicitly
2136 * deactivated.
2137 */
2138 ChangeDetectorStatus[ChangeDetectorStatus["CheckAlways"] = 2] = "CheckAlways";
2139 /**
2140 * A state in which a change detector sub tree is not a part of the main tree and
2141 * should be skipped.
2142 */
2143 ChangeDetectorStatus[ChangeDetectorStatus["Detached"] = 3] = "Detached";
2144 /**
2145 * Indicates that the change detector encountered an error checking a binding
2146 * or calling a directive lifecycle method and is now in an inconsistent state. Change
2147 * detectors in this state do not detect changes.
2148 */
2149 ChangeDetectorStatus[ChangeDetectorStatus["Errored"] = 4] = "Errored";
2150 /**
2151 * Indicates that the change detector has been destroyed.
2152 */
2153 ChangeDetectorStatus[ChangeDetectorStatus["Destroyed"] = 5] = "Destroyed";
2154})(ChangeDetectorStatus || (ChangeDetectorStatus = {}));
2155/**
2156 * Reports whether a given strategy is currently the default for change detection.
2157 * @param changeDetectionStrategy The strategy to check.
2158 * @returns True if the given strategy is the current default, false otherwise.
2159 * @see `ChangeDetectorStatus`
2160 * @see `ChangeDetectorRef`
2161 */
2162function isDefaultChangeDetectionStrategy(changeDetectionStrategy) {
2163 return changeDetectionStrategy == null ||
2164 changeDetectionStrategy === ChangeDetectionStrategy.Default;
2165}
2166
2167/**
2168 * Defines the CSS styles encapsulation policies for the {@link Component} decorator's
2169 * `encapsulation` option.
2170 *
2171 * See {@link Component#encapsulation encapsulation}.
2172 *
2173 * @usageNotes
2174 * ### Example
2175 *
2176 * {@example core/ts/metadata/encapsulation.ts region='longform'}
2177 *
2178 * @publicApi
2179 */
2180var ViewEncapsulation;
2181(function (ViewEncapsulation) {
2182 // TODO: consider making `ViewEncapsulation` a `const enum` instead. See
2183 // https://github.com/angular/angular/issues/44119 for additional information.
2184 /**
2185 * Emulates a native Shadow DOM encapsulation behavior by adding a specific attribute to the
2186 * component's host element and applying the same attribute to all the CSS selectors provided
2187 * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls}.
2188 *
2189 * This is the default option.
2190 */
2191 ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
2192 // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
2193 /**
2194 * Doesn't provide any sort of CSS style encapsulation, meaning that all the styles provided
2195 * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls} are applicable
2196 * to any HTML element of the application regardless of their host Component.
2197 */
2198 ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
2199 /**
2200 * Uses the browser's native Shadow DOM API to encapsulate CSS styles, meaning that it creates
2201 * a ShadowRoot for the component's host element which is then used to encapsulate
2202 * all the Component's styling.
2203 */
2204 ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
2205})(ViewEncapsulation || (ViewEncapsulation = {}));
2206
2207/**
2208 * This file contains reuseable "empty" symbols that can be used as default return values
2209 * in different parts of the rendering code. Because the same symbols are returned, this
2210 * allows for identity checks against these values to be consistently used by the framework
2211 * code.
2212 */
2213const EMPTY_OBJ = {};
2214const EMPTY_ARRAY = [];
2215// freezing the values prevents any code from accidentally inserting new values in
2216if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
2217 // These property accesses can be ignored because ngDevMode will be set to false
2218 // when optimizing code and the whole if statement will be dropped.
2219 // tslint:disable-next-line:no-toplevel-property-access
2220 Object.freeze(EMPTY_OBJ);
2221 // tslint:disable-next-line:no-toplevel-property-access
2222 Object.freeze(EMPTY_ARRAY);
2223}
2224
2225const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty });
2226const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty });
2227const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty });
2228const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty });
2229const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty });
2230/**
2231 * If a directive is diPublic, bloomAdd sets a property on the type with this constant as
2232 * the key and the directive's unique ID as the value. This allows us to map directives to their
2233 * bloom filter bit for DI.
2234 */
2235// TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
2236const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty });
2237
2238/** Counter used to generate unique IDs for component definitions. */
2239let componentDefCount = 0;
2240/**
2241 * Create a component definition object.
2242 *
2243 *
2244 * # Example
2245 * ```
2246 * class MyDirective {
2247 * // Generated by Angular Template Compiler
2248 * // [Symbol] syntax will not be supported by TypeScript until v2.7
2249 * static ɵcmp = defineComponent({
2250 * ...
2251 * });
2252 * }
2253 * ```
2254 * @codeGenApi
2255 */
2256function ɵɵdefineComponent(componentDefinition) {
2257 return noSideEffects(() => {
2258 // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent.
2259 // See the `initNgDevMode` docstring for more information.
2260 (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
2261 const type = componentDefinition.type;
2262 const standalone = componentDefinition.standalone === true;
2263 const declaredInputs = {};
2264 const def = {
2265 type: type,
2266 providersResolver: null,
2267 decls: componentDefinition.decls,
2268 vars: componentDefinition.vars,
2269 factory: null,
2270 template: componentDefinition.template || null,
2271 consts: componentDefinition.consts || null,
2272 ngContentSelectors: componentDefinition.ngContentSelectors,
2273 hostBindings: componentDefinition.hostBindings || null,
2274 hostVars: componentDefinition.hostVars || 0,
2275 hostAttrs: componentDefinition.hostAttrs || null,
2276 contentQueries: componentDefinition.contentQueries || null,
2277 declaredInputs: declaredInputs,
2278 inputs: null,
2279 outputs: null,
2280 exportAs: componentDefinition.exportAs || null,
2281 onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
2282 directiveDefs: null,
2283 pipeDefs: null,
2284 standalone,
2285 dependencies: standalone && componentDefinition.dependencies || null,
2286 getStandaloneInjector: null,
2287 selectors: componentDefinition.selectors || EMPTY_ARRAY,
2288 viewQuery: componentDefinition.viewQuery || null,
2289 features: componentDefinition.features || null,
2290 data: componentDefinition.data || {},
2291 encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated,
2292 id: `c${componentDefCount++}`,
2293 styles: componentDefinition.styles || EMPTY_ARRAY,
2294 _: null,
2295 setInput: null,
2296 schemas: componentDefinition.schemas || null,
2297 tView: null,
2298 findHostDirectiveDefs: null,
2299 hostDirectives: null,
2300 };
2301 const dependencies = componentDefinition.dependencies;
2302 const feature = componentDefinition.features;
2303 def.inputs = invertObject(componentDefinition.inputs, declaredInputs),
2304 def.outputs = invertObject(componentDefinition.outputs),
2305 feature && feature.forEach((fn) => fn(def));
2306 def.directiveDefs = dependencies ?
2307 (() => (typeof dependencies === 'function' ? dependencies() : dependencies)
2308 .map(extractDirectiveDef)
2309 .filter(nonNull)) :
2310 null;
2311 def.pipeDefs = dependencies ?
2312 (() => (typeof dependencies === 'function' ? dependencies() : dependencies)
2313 .map(getPipeDef$1)
2314 .filter(nonNull)) :
2315 null;
2316 return def;
2317 });
2318}
2319/**
2320 * Generated next to NgModules to monkey-patch directive and pipe references onto a component's
2321 * definition, when generating a direct reference in the component file would otherwise create an
2322 * import cycle.
2323 *
2324 * See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details.
2325 *
2326 * @codeGenApi
2327 */
2328function ɵɵsetComponentScope(type, directives, pipes) {
2329 const def = type.ɵcmp;
2330 def.directiveDefs = () => (typeof directives === 'function' ? directives() : directives).map(extractDirectiveDef);
2331 def.pipeDefs = () => (typeof pipes === 'function' ? pipes() : pipes).map(getPipeDef$1);
2332}
2333function extractDirectiveDef(type) {
2334 return getComponentDef$1(type) || getDirectiveDef(type);
2335}
2336function nonNull(value) {
2337 return value !== null;
2338}
2339/**
2340 * @codeGenApi
2341 */
2342function ɵɵdefineNgModule(def) {
2343 return noSideEffects(() => {
2344 const res = {
2345 type: def.type,
2346 bootstrap: def.bootstrap || EMPTY_ARRAY,
2347 declarations: def.declarations || EMPTY_ARRAY,
2348 imports: def.imports || EMPTY_ARRAY,
2349 exports: def.exports || EMPTY_ARRAY,
2350 transitiveCompileScopes: null,
2351 schemas: def.schemas || null,
2352 id: def.id || null,
2353 };
2354 return res;
2355 });
2356}
2357/**
2358 * Adds the module metadata that is necessary to compute the module's transitive scope to an
2359 * existing module definition.
2360 *
2361 * Scope metadata of modules is not used in production builds, so calls to this function can be
2362 * marked pure to tree-shake it from the bundle, allowing for all referenced declarations
2363 * to become eligible for tree-shaking as well.
2364 *
2365 * @codeGenApi
2366 */
2367function ɵɵsetNgModuleScope(type, scope) {
2368 return noSideEffects(() => {
2369 const ngModuleDef = getNgModuleDef(type, true);
2370 ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
2371 ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
2372 ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
2373 });
2374}
2375/**
2376 * Inverts an inputs or outputs lookup such that the keys, which were the
2377 * minified keys, are part of the values, and the values are parsed so that
2378 * the publicName of the property is the new key
2379 *
2380 * e.g. for
2381 *
2382 * ```
2383 * class Comp {
2384 * @Input()
2385 * propName1: string;
2386 *
2387 * @Input('publicName2')
2388 * declaredPropName2: number;
2389 * }
2390 * ```
2391 *
2392 * will be serialized as
2393 *
2394 * ```
2395 * {
2396 * propName1: 'propName1',
2397 * declaredPropName2: ['publicName2', 'declaredPropName2'],
2398 * }
2399 * ```
2400 *
2401 * which is than translated by the minifier as:
2402 *
2403 * ```
2404 * {
2405 * minifiedPropName1: 'propName1',
2406 * minifiedPropName2: ['publicName2', 'declaredPropName2'],
2407 * }
2408 * ```
2409 *
2410 * becomes: (public name => minifiedName)
2411 *
2412 * ```
2413 * {
2414 * 'propName1': 'minifiedPropName1',
2415 * 'publicName2': 'minifiedPropName2',
2416 * }
2417 * ```
2418 *
2419 * Optionally the function can take `secondary` which will result in: (public name => declared name)
2420 *
2421 * ```
2422 * {
2423 * 'propName1': 'propName1',
2424 * 'publicName2': 'declaredPropName2',
2425 * }
2426 * ```
2427 *
2428
2429 */
2430function invertObject(obj, secondary) {
2431 if (obj == null)
2432 return EMPTY_OBJ;
2433 const newLookup = {};
2434 for (const minifiedKey in obj) {
2435 if (obj.hasOwnProperty(minifiedKey)) {
2436 let publicName = obj[minifiedKey];
2437 let declaredName = publicName;
2438 if (Array.isArray(publicName)) {
2439 declaredName = publicName[1];
2440 publicName = publicName[0];
2441 }
2442 newLookup[publicName] = minifiedKey;
2443 if (secondary) {
2444 (secondary[publicName] = declaredName);
2445 }
2446 }
2447 }
2448 return newLookup;
2449}
2450/**
2451 * Create a directive definition object.
2452 *
2453 * # Example
2454 * ```ts
2455 * class MyDirective {
2456 * // Generated by Angular Template Compiler
2457 * // [Symbol] syntax will not be supported by TypeScript until v2.7
2458 * static ɵdir = ɵɵdefineDirective({
2459 * ...
2460 * });
2461 * }
2462 * ```
2463 *
2464 * @codeGenApi
2465 */
2466const ɵɵdefineDirective = ɵɵdefineComponent;
2467/**
2468 * Create a pipe definition object.
2469 *
2470 * # Example
2471 * ```
2472 * class MyPipe implements PipeTransform {
2473 * // Generated by Angular Template Compiler
2474 * static ɵpipe = definePipe({
2475 * ...
2476 * });
2477 * }
2478 * ```
2479 * @param pipeDef Pipe definition generated by the compiler
2480 *
2481 * @codeGenApi
2482 */
2483function ɵɵdefinePipe(pipeDef) {
2484 return {
2485 type: pipeDef.type,
2486 name: pipeDef.name,
2487 factory: null,
2488 pure: pipeDef.pure !== false,
2489 standalone: pipeDef.standalone === true,
2490 onDestroy: pipeDef.type.prototype.ngOnDestroy || null
2491 };
2492}
2493/**
2494 * The following getter methods retrieve the definition from the type. Currently the retrieval
2495 * honors inheritance, but in the future we may change the rule to require that definitions are
2496 * explicit. This would require some sort of migration strategy.
2497 */
2498function getComponentDef$1(type) {
2499 return type[NG_COMP_DEF] || null;
2500}
2501function getDirectiveDef(type) {
2502 return type[NG_DIR_DEF] || null;
2503}
2504function getPipeDef$1(type) {
2505 return type[NG_PIPE_DEF] || null;
2506}
2507/**
2508 * Checks whether a given Component, Directive or Pipe is marked as standalone.
2509 * This will return false if passed anything other than a Component, Directive, or Pipe class
2510 * See this guide for additional information: https://angular.io/guide/standalone-components
2511 *
2512 * @param type A reference to a Component, Directive or Pipe.
2513 * @publicApi
2514 */
2515function isStandalone(type) {
2516 const def = getComponentDef$1(type) || getDirectiveDef(type) || getPipeDef$1(type);
2517 return def !== null ? def.standalone : false;
2518}
2519function getNgModuleDef(type, throwNotFound) {
2520 const ngModuleDef = type[NG_MOD_DEF] || null;
2521 if (!ngModuleDef && throwNotFound === true) {
2522 throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`);
2523 }
2524 return ngModuleDef;
2525}
2526
2527// Below are constants for LView indices to help us look up LView members
2528// without having to remember the specific indices.
2529// Uglify will inline these when minifying so there shouldn't be a cost.
2530const HOST = 0;
2531const TVIEW = 1;
2532const FLAGS = 2;
2533const PARENT = 3;
2534const NEXT = 4;
2535const TRANSPLANTED_VIEWS_TO_REFRESH = 5;
2536const T_HOST = 6;
2537const CLEANUP = 7;
2538const CONTEXT = 8;
2539const INJECTOR$1 = 9;
2540const RENDERER_FACTORY = 10;
2541const RENDERER = 11;
2542const SANITIZER = 12;
2543const CHILD_HEAD = 13;
2544const CHILD_TAIL = 14;
2545// FIXME(misko): Investigate if the three declarations aren't all same thing.
2546const DECLARATION_VIEW = 15;
2547const DECLARATION_COMPONENT_VIEW = 16;
2548const DECLARATION_LCONTAINER = 17;
2549const PREORDER_HOOK_FLAGS = 18;
2550const QUERIES = 19;
2551const ID = 20;
2552const EMBEDDED_VIEW_INJECTOR = 21;
2553/**
2554 * Size of LView's header. Necessary to adjust for it when setting slots.
2555 *
2556 * IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
2557 * instruction index into `LView` index. All other indexes should be in the `LView` index space and
2558 * there should be no need to refer to `HEADER_OFFSET` anywhere else.
2559 */
2560const HEADER_OFFSET = 22;
2561// Note: This hack is necessary so we don't erroneously get a circular dependency
2562// failure based on types.
2563const unusedValueExportToPlacateAjd$4 = 1;
2564
2565/**
2566 * Special location which allows easy identification of type. If we have an array which was
2567 * retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is
2568 * `LContainer`.
2569 */
2570const TYPE = 1;
2571/**
2572 * Below are constants for LContainer indices to help us look up LContainer members
2573 * without having to remember the specific indices.
2574 * Uglify will inline these when minifying so there shouldn't be a cost.
2575 */
2576/**
2577 * Flag to signify that this `LContainer` may have transplanted views which need to be change
2578 * detected. (see: `LView[DECLARATION_COMPONENT_VIEW])`.
2579 *
2580 * This flag, once set, is never unset for the `LContainer`. This means that when unset we can skip
2581 * a lot of work in `refreshEmbeddedViews`. But when set we still need to verify
2582 * that the `MOVED_VIEWS` are transplanted and on-push.
2583 */
2584const HAS_TRANSPLANTED_VIEWS = 2;
2585// PARENT, NEXT, TRANSPLANTED_VIEWS_TO_REFRESH are indices 3, 4, and 5
2586// As we already have these constants in LView, we don't need to re-create them.
2587// T_HOST is index 6
2588// We already have this constants in LView, we don't need to re-create it.
2589const NATIVE = 7;
2590const VIEW_REFS = 8;
2591const MOVED_VIEWS = 9;
2592/**
2593 * Size of LContainer's header. Represents the index after which all views in the
2594 * container will be inserted. We need to keep a record of current views so we know
2595 * which views are already in the DOM (and don't need to be re-added) and so we can
2596 * remove views from the DOM when they are no longer required.
2597 */
2598const CONTAINER_HEADER_OFFSET = 10;
2599// Note: This hack is necessary so we don't erroneously get a circular dependency
2600// failure based on types.
2601const unusedValueExportToPlacateAjd$3 = 1;
2602
2603/**
2604 * True if `value` is `LView`.
2605 * @param value wrapped value of `RNode`, `LView`, `LContainer`
2606 */
2607function isLView(value) {
2608 return Array.isArray(value) && typeof value[TYPE] === 'object';
2609}
2610/**
2611 * True if `value` is `LContainer`.
2612 * @param value wrapped value of `RNode`, `LView`, `LContainer`
2613 */
2614function isLContainer(value) {
2615 return Array.isArray(value) && value[TYPE] === true;
2616}
2617function isContentQueryHost(tNode) {
2618 return (tNode.flags & 4 /* TNodeFlags.hasContentQuery */) !== 0;
2619}
2620function isComponentHost(tNode) {
2621 return tNode.componentOffset > -1;
2622}
2623function isDirectiveHost(tNode) {
2624 return (tNode.flags & 1 /* TNodeFlags.isDirectiveHost */) === 1 /* TNodeFlags.isDirectiveHost */;
2625}
2626function isComponentDef(def) {
2627 return def.template !== null;
2628}
2629function isRootView(target) {
2630 return (target[FLAGS] & 256 /* LViewFlags.IsRoot */) !== 0;
2631}
2632
2633// [Assert functions do not constraint type when they are guarded by a truthy
2634// expression.](https://github.com/microsoft/TypeScript/issues/37295)
2635function assertTNodeForLView(tNode, lView) {
2636 assertTNodeForTView(tNode, lView[TVIEW]);
2637}
2638function assertTNodeForTView(tNode, tView) {
2639 assertTNode(tNode);
2640 tNode.hasOwnProperty('tView_') &&
2641 assertEqual(tNode.tView_, tView, 'This TNode does not belong to this TView.');
2642}
2643function assertTNode(tNode) {
2644 assertDefined(tNode, 'TNode must be defined');
2645 if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) {
2646 throwError('Not of type TNode, got: ' + tNode);
2647 }
2648}
2649function assertTIcu(tIcu) {
2650 assertDefined(tIcu, 'Expected TIcu to be defined');
2651 if (!(typeof tIcu.currentCaseLViewIndex === 'number')) {
2652 throwError('Object is not of TIcu type.');
2653 }
2654}
2655function assertComponentType(actual, msg = 'Type passed in is not ComponentType, it does not have \'ɵcmp\' property.') {
2656 if (!getComponentDef$1(actual)) {
2657 throwError(msg);
2658 }
2659}
2660function assertNgModuleType(actual, msg = 'Type passed in is not NgModuleType, it does not have \'ɵmod\' property.') {
2661 if (!getNgModuleDef(actual)) {
2662 throwError(msg);
2663 }
2664}
2665function assertCurrentTNodeIsParent(isParent) {
2666 assertEqual(isParent, true, 'currentTNode should be a parent');
2667}
2668function assertHasParent(tNode) {
2669 assertDefined(tNode, 'currentTNode should exist!');
2670 assertDefined(tNode.parent, 'currentTNode should have a parent');
2671}
2672function assertLContainer(value) {
2673 assertDefined(value, 'LContainer must be defined');
2674 assertEqual(isLContainer(value), true, 'Expecting LContainer');
2675}
2676function assertLViewOrUndefined(value) {
2677 value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
2678}
2679function assertLView(value) {
2680 assertDefined(value, 'LView must be defined');
2681 assertEqual(isLView(value), true, 'Expecting LView');
2682}
2683function assertFirstCreatePass(tView, errMessage) {
2684 assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.');
2685}
2686function assertFirstUpdatePass(tView, errMessage) {
2687 assertEqual(tView.firstUpdatePass, true, errMessage || 'Should only be called in first update pass.');
2688}
2689/**
2690 * This is a basic sanity check that an object is probably a directive def. DirectiveDef is
2691 * an interface, so we can't do a direct instanceof check.
2692 */
2693function assertDirectiveDef(obj) {
2694 if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
2695 throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
2696 }
2697}
2698function assertIndexInDeclRange(lView, index) {
2699 const tView = lView[1];
2700 assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index);
2701}
2702function assertIndexInExpandoRange(lView, index) {
2703 const tView = lView[1];
2704 assertBetween(tView.expandoStartIndex, lView.length, index);
2705}
2706function assertBetween(lower, upper, index) {
2707 if (!(lower <= index && index < upper)) {
2708 throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`);
2709 }
2710}
2711function assertProjectionSlots(lView, errMessage) {
2712 assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.');
2713 assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, errMessage ||
2714 'Components with projection nodes (<ng-content>) must have projection slots defined.');
2715}
2716function assertParentView(lView, errMessage) {
2717 assertDefined(lView, errMessage || 'Component views should always have a parent view (component\'s host view)');
2718}
2719/**
2720 * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a
2721 * NodeInjector data structure.
2722 *
2723 * @param lView `LView` which should be checked.
2724 * @param injectorIndex index into the `LView` where the `NodeInjector` is expected.
2725 */
2726function assertNodeInjector(lView, injectorIndex) {
2727 assertIndexInExpandoRange(lView, injectorIndex);
2728 assertIndexInExpandoRange(lView, injectorIndex + 8 /* NodeInjectorOffset.PARENT */);
2729 assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter');
2730 assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter');
2731 assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter');
2732 assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter');
2733 assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter');
2734 assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter');
2735 assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter');
2736 assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter');
2737 assertNumber(lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */], 'injectorIndex should point to parent injector');
2738}
2739
2740function getFactoryDef(type, throwNotFound) {
2741 const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF);
2742 if (!hasFactoryDef && throwNotFound === true && ngDevMode) {
2743 throw new Error(`Type ${stringify(type)} does not have 'ɵfac' property.`);
2744 }
2745 return hasFactoryDef ? type[NG_FACTORY_DEF] : null;
2746}
2747
2748/**
2749 * Represents a basic change from a previous to a new value for a single
2750 * property on a directive instance. Passed as a value in a
2751 * {@link SimpleChanges} object to the `ngOnChanges` hook.
2752 *
2753 * @see `OnChanges`
2754 *
2755 * @publicApi
2756 */
2757class SimpleChange {
2758 constructor(previousValue, currentValue, firstChange) {
2759 this.previousValue = previousValue;
2760 this.currentValue = currentValue;
2761 this.firstChange = firstChange;
2762 }
2763 /**
2764 * Check whether the new value is the first value assigned.
2765 */
2766 isFirstChange() {
2767 return this.firstChange;
2768 }
2769}
2770
2771/**
2772 * The NgOnChangesFeature decorates a component with support for the ngOnChanges
2773 * lifecycle hook, so it should be included in any component that implements
2774 * that hook.
2775 *
2776 * If the component or directive uses inheritance, the NgOnChangesFeature MUST
2777 * be included as a feature AFTER {@link InheritDefinitionFeature}, otherwise
2778 * inherited properties will not be propagated to the ngOnChanges lifecycle
2779 * hook.
2780 *
2781 * Example usage:
2782 *
2783 * ```
2784 * static ɵcmp = defineComponent({
2785 * ...
2786 * inputs: {name: 'publicName'},
2787 * features: [NgOnChangesFeature]
2788 * });
2789 * ```
2790 *
2791 * @codeGenApi
2792 */
2793function ɵɵNgOnChangesFeature() {
2794 return NgOnChangesFeatureImpl;
2795}
2796function NgOnChangesFeatureImpl(definition) {
2797 if (definition.type.prototype.ngOnChanges) {
2798 definition.setInput = ngOnChangesSetInput;
2799 }
2800 return rememberChangeHistoryAndInvokeOnChangesHook;
2801}
2802// This option ensures that the ngOnChanges lifecycle hook will be inherited
2803// from superclasses (in InheritDefinitionFeature).
2804/** @nocollapse */
2805// tslint:disable-next-line:no-toplevel-property-access
2806ɵɵNgOnChangesFeature.ngInherit = true;
2807/**
2808 * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate
2809 * `ngOnChanges`.
2810 *
2811 * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are
2812 * found it invokes `ngOnChanges` on the component instance.
2813 *
2814 * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`,
2815 * it is guaranteed to be called with component instance.
2816 */
2817function rememberChangeHistoryAndInvokeOnChangesHook() {
2818 const simpleChangesStore = getSimpleChangesStore(this);
2819 const current = simpleChangesStore?.current;
2820 if (current) {
2821 const previous = simpleChangesStore.previous;
2822 if (previous === EMPTY_OBJ) {
2823 simpleChangesStore.previous = current;
2824 }
2825 else {
2826 // New changes are copied to the previous store, so that we don't lose history for inputs
2827 // which were not changed this time
2828 for (let key in current) {
2829 previous[key] = current[key];
2830 }
2831 }
2832 simpleChangesStore.current = null;
2833 this.ngOnChanges(current);
2834 }
2835}
2836function ngOnChangesSetInput(instance, value, publicName, privateName) {
2837 const declaredName = this.declaredInputs[publicName];
2838 ngDevMode && assertString(declaredName, 'Name of input in ngOnChanges has to be a string');
2839 const simpleChangesStore = getSimpleChangesStore(instance) ||
2840 setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null });
2841 const current = simpleChangesStore.current || (simpleChangesStore.current = {});
2842 const previous = simpleChangesStore.previous;
2843 const previousChange = previous[declaredName];
2844 current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ);
2845 instance[privateName] = value;
2846}
2847const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__';
2848function getSimpleChangesStore(instance) {
2849 return instance[SIMPLE_CHANGES_STORE] || null;
2850}
2851function setSimpleChangesStore(instance, store) {
2852 return instance[SIMPLE_CHANGES_STORE] = store;
2853}
2854
2855let profilerCallback = null;
2856/**
2857 * Sets the callback function which will be invoked before and after performing certain actions at
2858 * runtime (for example, before and after running change detection).
2859 *
2860 * Warning: this function is *INTERNAL* and should not be relied upon in application's code.
2861 * The contract of the function might be changed in any release and/or the function can be removed
2862 * completely.
2863 *
2864 * @param profiler function provided by the caller or null value to disable profiling.
2865 */
2866const setProfiler = (profiler) => {
2867 profilerCallback = profiler;
2868};
2869/**
2870 * Profiler function which wraps user code executed by the runtime.
2871 *
2872 * @param event ProfilerEvent corresponding to the execution context
2873 * @param instance component instance
2874 * @param hookOrListener lifecycle hook function or output listener. The value depends on the
2875 * execution context
2876 * @returns
2877 */
2878const profiler = function (event, instance, hookOrListener) {
2879 if (profilerCallback != null /* both `null` and `undefined` */) {
2880 profilerCallback(event, instance, hookOrListener);
2881 }
2882};
2883
2884const SVG_NAMESPACE = 'svg';
2885const MATH_ML_NAMESPACE = 'math';
2886
2887/**
2888 * For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
2889 * in same location in `LView`. This is because we don't want to pre-allocate space for it
2890 * because the storage is sparse. This file contains utilities for dealing with such data types.
2891 *
2892 * How do we know what is stored at a given location in `LView`.
2893 * - `Array.isArray(value) === false` => `RNode` (The normal storage value)
2894 * - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value.
2895 * - `typeof value[TYPE] === 'object'` => `LView`
2896 * - This happens when we have a component at a given location
2897 * - `typeof value[TYPE] === true` => `LContainer`
2898 * - This happens when we have `LContainer` binding at a given location.
2899 *
2900 *
2901 * NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.
2902 */
2903/**
2904 * Returns `RNode`.
2905 * @param value wrapped value of `RNode`, `LView`, `LContainer`
2906 */
2907function unwrapRNode(value) {
2908 while (Array.isArray(value)) {
2909 value = value[HOST];
2910 }
2911 return value;
2912}
2913/**
2914 * Returns `LView` or `null` if not found.
2915 * @param value wrapped value of `RNode`, `LView`, `LContainer`
2916 */
2917function unwrapLView(value) {
2918 while (Array.isArray(value)) {
2919 // This check is same as `isLView()` but we don't call at as we don't want to call
2920 // `Array.isArray()` twice and give JITer more work for inlining.
2921 if (typeof value[TYPE] === 'object')
2922 return value;
2923 value = value[HOST];
2924 }
2925 return null;
2926}
2927/**
2928 * Retrieves an element value from the provided `viewData`, by unwrapping
2929 * from any containers, component views, or style contexts.
2930 */
2931function getNativeByIndex(index, lView) {
2932 ngDevMode && assertIndexInRange(lView, index);
2933 ngDevMode && assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Expected to be past HEADER_OFFSET');
2934 return unwrapRNode(lView[index]);
2935}
2936/**
2937 * Retrieve an `RNode` for a given `TNode` and `LView`.
2938 *
2939 * This function guarantees in dev mode to retrieve a non-null `RNode`.
2940 *
2941 * @param tNode
2942 * @param lView
2943 */
2944function getNativeByTNode(tNode, lView) {
2945 ngDevMode && assertTNodeForLView(tNode, lView);
2946 ngDevMode && assertIndexInRange(lView, tNode.index);
2947 const node = unwrapRNode(lView[tNode.index]);
2948 return node;
2949}
2950/**
2951 * Retrieve an `RNode` or `null` for a given `TNode` and `LView`.
2952 *
2953 * Some `TNode`s don't have associated `RNode`s. For example `Projection`
2954 *
2955 * @param tNode
2956 * @param lView
2957 */
2958function getNativeByTNodeOrNull(tNode, lView) {
2959 const index = tNode === null ? -1 : tNode.index;
2960 if (index !== -1) {
2961 ngDevMode && assertTNodeForLView(tNode, lView);
2962 const node = unwrapRNode(lView[index]);
2963 return node;
2964 }
2965 return null;
2966}
2967// fixme(misko): The return Type should be `TNode|null`
2968function getTNode(tView, index) {
2969 ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
2970 ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode');
2971 const tNode = tView.data[index];
2972 ngDevMode && tNode !== null && assertTNode(tNode);
2973 return tNode;
2974}
2975/** Retrieves a value from any `LView` or `TData`. */
2976function load(view, index) {
2977 ngDevMode && assertIndexInRange(view, index);
2978 return view[index];
2979}
2980function getComponentLViewByIndex(nodeIndex, hostView) {
2981 // Could be an LView or an LContainer. If LContainer, unwrap to find LView.
2982 ngDevMode && assertIndexInRange(hostView, nodeIndex);
2983 const slotValue = hostView[nodeIndex];
2984 const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
2985 return lView;
2986}
2987/** Checks whether a given view is in creation mode */
2988function isCreationMode(view) {
2989 return (view[FLAGS] & 4 /* LViewFlags.CreationMode */) === 4 /* LViewFlags.CreationMode */;
2990}
2991/**
2992 * Returns a boolean for whether the view is attached to the change detection tree.
2993 *
2994 * Note: This determines whether a view should be checked, not whether it's inserted
2995 * into a container. For that, you'll want `viewAttachedToContainer` below.
2996 */
2997function viewAttachedToChangeDetector(view) {
2998 return (view[FLAGS] & 64 /* LViewFlags.Attached */) === 64 /* LViewFlags.Attached */;
2999}
3000/** Returns a boolean for whether the view is attached to a container. */
3001function viewAttachedToContainer(view) {
3002 return isLContainer(view[PARENT]);
3003}
3004function getConstant(consts, index) {
3005 if (index === null || index === undefined)
3006 return null;
3007 ngDevMode && assertIndexInRange(consts, index);
3008 return consts[index];
3009}
3010/**
3011 * Resets the pre-order hook flags of the view.
3012 * @param lView the LView on which the flags are reset
3013 */
3014function resetPreOrderHookFlags(lView) {
3015 lView[PREORDER_HOOK_FLAGS] = 0;
3016}
3017/**
3018 * Updates the `TRANSPLANTED_VIEWS_TO_REFRESH` counter on the `LContainer` as well as the parents
3019 * whose
3020 * 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh
3021 * or
3022 * 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh
3023 */
3024function updateTransplantedViewCount(lContainer, amount) {
3025 lContainer[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
3026 let viewOrContainer = lContainer;
3027 let parent = lContainer[PARENT];
3028 while (parent !== null &&
3029 ((amount === 1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 1) ||
3030 (amount === -1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 0))) {
3031 parent[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
3032 viewOrContainer = parent;
3033 parent = parent[PARENT];
3034 }
3035}
3036
3037const instructionState = {
3038 lFrame: createLFrame(null),
3039 bindingsEnabled: true,
3040};
3041/**
3042 * In this mode, any changes in bindings will throw an ExpressionChangedAfterChecked error.
3043 *
3044 * Necessary to support ChangeDetectorRef.checkNoChanges().
3045 *
3046 * The `checkNoChanges` function is invoked only in ngDevMode=true and verifies that no unintended
3047 * changes exist in the change detector or its children.
3048 */
3049let _isInCheckNoChangesMode = false;
3050/**
3051 * Returns true if the instruction state stack is empty.
3052 *
3053 * Intended to be called from tests only (tree shaken otherwise).
3054 */
3055function specOnlyIsInstructionStateEmpty() {
3056 return instructionState.lFrame.parent === null;
3057}
3058function getElementDepthCount() {
3059 return instructionState.lFrame.elementDepthCount;
3060}
3061function increaseElementDepthCount() {
3062 instructionState.lFrame.elementDepthCount++;
3063}
3064function decreaseElementDepthCount() {
3065 instructionState.lFrame.elementDepthCount--;
3066}
3067function getBindingsEnabled() {
3068 return instructionState.bindingsEnabled;
3069}
3070/**
3071 * Enables directive matching on elements.
3072 *
3073 * * Example:
3074 * ```
3075 * <my-comp my-directive>
3076 * Should match component / directive.
3077 * </my-comp>
3078 * <div ngNonBindable>
3079 * <!-- ɵɵdisableBindings() -->
3080 * <my-comp my-directive>
3081 * Should not match component / directive because we are in ngNonBindable.
3082 * </my-comp>
3083 * <!-- ɵɵenableBindings() -->
3084 * </div>
3085 * ```
3086 *
3087 * @codeGenApi
3088 */
3089function ɵɵenableBindings() {
3090 instructionState.bindingsEnabled = true;
3091}
3092/**
3093 * Disables directive matching on element.
3094 *
3095 * * Example:
3096 * ```
3097 * <my-comp my-directive>
3098 * Should match component / directive.
3099 * </my-comp>
3100 * <div ngNonBindable>
3101 * <!-- ɵɵdisableBindings() -->
3102 * <my-comp my-directive>
3103 * Should not match component / directive because we are in ngNonBindable.
3104 * </my-comp>
3105 * <!-- ɵɵenableBindings() -->
3106 * </div>
3107 * ```
3108 *
3109 * @codeGenApi
3110 */
3111function ɵɵdisableBindings() {
3112 instructionState.bindingsEnabled = false;
3113}
3114/**
3115 * Return the current `LView`.
3116 */
3117function getLView() {
3118 return instructionState.lFrame.lView;
3119}
3120/**
3121 * Return the current `TView`.
3122 */
3123function getTView() {
3124 return instructionState.lFrame.tView;
3125}
3126/**
3127 * Restores `contextViewData` to the given OpaqueViewState instance.
3128 *
3129 * Used in conjunction with the getCurrentView() instruction to save a snapshot
3130 * of the current view and restore it when listeners are invoked. This allows
3131 * walking the declaration view tree in listeners to get vars from parent views.
3132 *
3133 * @param viewToRestore The OpaqueViewState instance to restore.
3134 * @returns Context of the restored OpaqueViewState instance.
3135 *
3136 * @codeGenApi
3137 */
3138function ɵɵrestoreView(viewToRestore) {
3139 instructionState.lFrame.contextLView = viewToRestore;
3140 return viewToRestore[CONTEXT];
3141}
3142/**
3143 * Clears the view set in `ɵɵrestoreView` from memory. Returns the passed in
3144 * value so that it can be used as a return value of an instruction.
3145 *
3146 * @codeGenApi
3147 */
3148function ɵɵresetView(value) {
3149 instructionState.lFrame.contextLView = null;
3150 return value;
3151}
3152function getCurrentTNode() {
3153 let currentTNode = getCurrentTNodePlaceholderOk();
3154 while (currentTNode !== null && currentTNode.type === 64 /* TNodeType.Placeholder */) {
3155 currentTNode = currentTNode.parent;
3156 }
3157 return currentTNode;
3158}
3159function getCurrentTNodePlaceholderOk() {
3160 return instructionState.lFrame.currentTNode;
3161}
3162function getCurrentParentTNode() {
3163 const lFrame = instructionState.lFrame;
3164 const currentTNode = lFrame.currentTNode;
3165 return lFrame.isParent ? currentTNode : currentTNode.parent;
3166}
3167function setCurrentTNode(tNode, isParent) {
3168 ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView);
3169 const lFrame = instructionState.lFrame;
3170 lFrame.currentTNode = tNode;
3171 lFrame.isParent = isParent;
3172}
3173function isCurrentTNodeParent() {
3174 return instructionState.lFrame.isParent;
3175}
3176function setCurrentTNodeAsNotParent() {
3177 instructionState.lFrame.isParent = false;
3178}
3179function getContextLView() {
3180 const contextLView = instructionState.lFrame.contextLView;
3181 ngDevMode && assertDefined(contextLView, 'contextLView must be defined.');
3182 return contextLView;
3183}
3184function isInCheckNoChangesMode() {
3185 !ngDevMode && throwError('Must never be called in production mode');
3186 return _isInCheckNoChangesMode;
3187}
3188function setIsInCheckNoChangesMode(mode) {
3189 !ngDevMode && throwError('Must never be called in production mode');
3190 _isInCheckNoChangesMode = mode;
3191}
3192// top level variables should not be exported for performance reasons (PERF_NOTES.md)
3193function getBindingRoot() {
3194 const lFrame = instructionState.lFrame;
3195 let index = lFrame.bindingRootIndex;
3196 if (index === -1) {
3197 index = lFrame.bindingRootIndex = lFrame.tView.bindingStartIndex;
3198 }
3199 return index;
3200}
3201function getBindingIndex() {
3202 return instructionState.lFrame.bindingIndex;
3203}
3204function setBindingIndex(value) {
3205 return instructionState.lFrame.bindingIndex = value;
3206}
3207function nextBindingIndex() {
3208 return instructionState.lFrame.bindingIndex++;
3209}
3210function incrementBindingIndex(count) {
3211 const lFrame = instructionState.lFrame;
3212 const index = lFrame.bindingIndex;
3213 lFrame.bindingIndex = lFrame.bindingIndex + count;
3214 return index;
3215}
3216function isInI18nBlock() {
3217 return instructionState.lFrame.inI18n;
3218}
3219function setInI18nBlock(isInI18nBlock) {
3220 instructionState.lFrame.inI18n = isInI18nBlock;
3221}
3222/**
3223 * Set a new binding root index so that host template functions can execute.
3224 *
3225 * Bindings inside the host template are 0 index. But because we don't know ahead of time
3226 * how many host bindings we have we can't pre-compute them. For this reason they are all
3227 * 0 index and we just shift the root so that they match next available location in the LView.
3228 *
3229 * @param bindingRootIndex Root index for `hostBindings`
3230 * @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive
3231 * whose `hostBindings` are being processed.
3232 */
3233function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) {
3234 const lFrame = instructionState.lFrame;
3235 lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex;
3236 setCurrentDirectiveIndex(currentDirectiveIndex);
3237}
3238/**
3239 * When host binding is executing this points to the directive index.
3240 * `TView.data[getCurrentDirectiveIndex()]` is `DirectiveDef`
3241 * `LView[getCurrentDirectiveIndex()]` is directive instance.
3242 */
3243function getCurrentDirectiveIndex() {
3244 return instructionState.lFrame.currentDirectiveIndex;
3245}
3246/**
3247 * Sets an index of a directive whose `hostBindings` are being processed.
3248 *
3249 * @param currentDirectiveIndex `TData` index where current directive instance can be found.
3250 */
3251function setCurrentDirectiveIndex(currentDirectiveIndex) {
3252 instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex;
3253}
3254/**
3255 * Retrieve the current `DirectiveDef` which is active when `hostBindings` instruction is being
3256 * executed.
3257 *
3258 * @param tData Current `TData` where the `DirectiveDef` will be looked up at.
3259 */
3260function getCurrentDirectiveDef(tData) {
3261 const currentDirectiveIndex = instructionState.lFrame.currentDirectiveIndex;
3262 return currentDirectiveIndex === -1 ? null : tData[currentDirectiveIndex];
3263}
3264function getCurrentQueryIndex() {
3265 return instructionState.lFrame.currentQueryIndex;
3266}
3267function setCurrentQueryIndex(value) {
3268 instructionState.lFrame.currentQueryIndex = value;
3269}
3270/**
3271 * Returns a `TNode` of the location where the current `LView` is declared at.
3272 *
3273 * @param lView an `LView` that we want to find parent `TNode` for.
3274 */
3275function getDeclarationTNode(lView) {
3276 const tView = lView[TVIEW];
3277 // Return the declaration parent for embedded views
3278 if (tView.type === 2 /* TViewType.Embedded */) {
3279 ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
3280 return tView.declTNode;
3281 }
3282 // Components don't have `TView.declTNode` because each instance of component could be
3283 // inserted in different location, hence `TView.declTNode` is meaningless.
3284 // Falling back to `T_HOST` in case we cross component boundary.
3285 if (tView.type === 1 /* TViewType.Component */) {
3286 return lView[T_HOST];
3287 }
3288 // Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode.
3289 return null;
3290}
3291/**
3292 * This is a light weight version of the `enterView` which is needed by the DI system.
3293 *
3294 * @param lView `LView` location of the DI context.
3295 * @param tNode `TNode` for DI context
3296 * @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration
3297 * tree from `tNode` until we find parent declared `TElementNode`.
3298 * @returns `true` if we have successfully entered DI associated with `tNode` (or with declared
3299 * `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated
3300 * `NodeInjector` can be found and we should instead use `ModuleInjector`.
3301 * - If `true` than this call must be fallowed by `leaveDI`
3302 * - If `false` than this call failed and we should NOT call `leaveDI`
3303 */
3304function enterDI(lView, tNode, flags) {
3305 ngDevMode && assertLViewOrUndefined(lView);
3306 if (flags & InjectFlags.SkipSelf) {
3307 ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]);
3308 let parentTNode = tNode;
3309 let parentLView = lView;
3310 while (true) {
3311 ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined');
3312 parentTNode = parentTNode.parent;
3313 if (parentTNode === null && !(flags & InjectFlags.Host)) {
3314 parentTNode = getDeclarationTNode(parentLView);
3315 if (parentTNode === null)
3316 break;
3317 // In this case, a parent exists and is definitely an element. So it will definitely
3318 // have an existing lView as the declaration view, which is why we can assume it's defined.
3319 ngDevMode && assertDefined(parentLView, 'Parent LView should be defined');
3320 parentLView = parentLView[DECLARATION_VIEW];
3321 // In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives
3322 // We want to skip those and look only at Elements and ElementContainers to ensure
3323 // we're looking at true parent nodes, and not content or other types.
3324 if (parentTNode.type & (2 /* TNodeType.Element */ | 8 /* TNodeType.ElementContainer */)) {
3325 break;
3326 }
3327 }
3328 else {
3329 break;
3330 }
3331 }
3332 if (parentTNode === null) {
3333 // If we failed to find a parent TNode this means that we should use module injector.
3334 return false;
3335 }
3336 else {
3337 tNode = parentTNode;
3338 lView = parentLView;
3339 }
3340 }
3341 ngDevMode && assertTNodeForLView(tNode, lView);
3342 const lFrame = instructionState.lFrame = allocLFrame();
3343 lFrame.currentTNode = tNode;
3344 lFrame.lView = lView;
3345 return true;
3346}
3347/**
3348 * Swap the current lView with a new lView.
3349 *
3350 * For performance reasons we store the lView in the top level of the module.
3351 * This way we minimize the number of properties to read. Whenever a new view
3352 * is entered we have to store the lView for later, and when the view is
3353 * exited the state has to be restored
3354 *
3355 * @param newView New lView to become active
3356 * @returns the previously active lView;
3357 */
3358function enterView(newView) {
3359 ngDevMode && assertNotEqual(newView[0], newView[1], '????');
3360 ngDevMode && assertLViewOrUndefined(newView);
3361 const newLFrame = allocLFrame();
3362 if (ngDevMode) {
3363 assertEqual(newLFrame.isParent, true, 'Expected clean LFrame');
3364 assertEqual(newLFrame.lView, null, 'Expected clean LFrame');
3365 assertEqual(newLFrame.tView, null, 'Expected clean LFrame');
3366 assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame');
3367 assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
3368 assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
3369 assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
3370 assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
3371 assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
3372 }
3373 const tView = newView[TVIEW];
3374 instructionState.lFrame = newLFrame;
3375 ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView);
3376 newLFrame.currentTNode = tView.firstChild;
3377 newLFrame.lView = newView;
3378 newLFrame.tView = tView;
3379 newLFrame.contextLView = newView;
3380 newLFrame.bindingIndex = tView.bindingStartIndex;
3381 newLFrame.inI18n = false;
3382}
3383/**
3384 * Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
3385 */
3386function allocLFrame() {
3387 const currentLFrame = instructionState.lFrame;
3388 const childLFrame = currentLFrame === null ? null : currentLFrame.child;
3389 const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
3390 return newLFrame;
3391}
3392function createLFrame(parent) {
3393 const lFrame = {
3394 currentTNode: null,
3395 isParent: true,
3396 lView: null,
3397 tView: null,
3398 selectedIndex: -1,
3399 contextLView: null,
3400 elementDepthCount: 0,
3401 currentNamespace: null,
3402 currentDirectiveIndex: -1,
3403 bindingRootIndex: -1,
3404 bindingIndex: -1,
3405 currentQueryIndex: 0,
3406 parent: parent,
3407 child: null,
3408 inI18n: false,
3409 };
3410 parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
3411 return lFrame;
3412}
3413/**
3414 * A lightweight version of leave which is used with DI.
3415 *
3416 * This function only resets `currentTNode` and `LView` as those are the only properties
3417 * used with DI (`enterDI()`).
3418 *
3419 * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where
3420 * as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`.
3421 */
3422function leaveViewLight() {
3423 const oldLFrame = instructionState.lFrame;
3424 instructionState.lFrame = oldLFrame.parent;
3425 oldLFrame.currentTNode = null;
3426 oldLFrame.lView = null;
3427 return oldLFrame;
3428}
3429/**
3430 * This is a lightweight version of the `leaveView` which is needed by the DI system.
3431 *
3432 * NOTE: this function is an alias so that we can change the type of the function to have `void`
3433 * return type.
3434 */
3435const leaveDI = leaveViewLight;
3436/**
3437 * Leave the current `LView`
3438 *
3439 * This pops the `LFrame` with the associated `LView` from the stack.
3440 *
3441 * IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is
3442 * because for performance reasons we don't release `LFrame` but rather keep it for next use.
3443 */
3444function leaveView() {
3445 const oldLFrame = leaveViewLight();
3446 oldLFrame.isParent = true;
3447 oldLFrame.tView = null;
3448 oldLFrame.selectedIndex = -1;
3449 oldLFrame.contextLView = null;
3450 oldLFrame.elementDepthCount = 0;
3451 oldLFrame.currentDirectiveIndex = -1;
3452 oldLFrame.currentNamespace = null;
3453 oldLFrame.bindingRootIndex = -1;
3454 oldLFrame.bindingIndex = -1;
3455 oldLFrame.currentQueryIndex = 0;
3456}
3457function nextContextImpl(level) {
3458 const contextLView = instructionState.lFrame.contextLView =
3459 walkUpViews(level, instructionState.lFrame.contextLView);
3460 return contextLView[CONTEXT];
3461}
3462function walkUpViews(nestingLevel, currentView) {
3463 while (nestingLevel > 0) {
3464 ngDevMode &&
3465 assertDefined(currentView[DECLARATION_VIEW], 'Declaration view should be defined if nesting level is greater than 0.');
3466 currentView = currentView[DECLARATION_VIEW];
3467 nestingLevel--;
3468 }
3469 return currentView;
3470}
3471/**
3472 * Gets the currently selected element index.
3473 *
3474 * Used with {@link property} instruction (and more in the future) to identify the index in the
3475 * current `LView` to act on.
3476 */
3477function getSelectedIndex() {
3478 return instructionState.lFrame.selectedIndex;
3479}
3480/**
3481 * Sets the most recent index passed to {@link select}
3482 *
3483 * Used with {@link property} instruction (and more in the future) to identify the index in the
3484 * current `LView` to act on.
3485 *
3486 * (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
3487 * run if and when the provided `index` value is different from the current selected index value.)
3488 */
3489function setSelectedIndex(index) {
3490 ngDevMode && index !== -1 &&
3491 assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).');
3492 ngDevMode &&
3493 assertLessThan(index, instructionState.lFrame.lView.length, 'Can\'t set index passed end of LView');
3494 instructionState.lFrame.selectedIndex = index;
3495}
3496/**
3497 * Gets the `tNode` that represents currently selected element.
3498 */
3499function getSelectedTNode() {
3500 const lFrame = instructionState.lFrame;
3501 return getTNode(lFrame.tView, lFrame.selectedIndex);
3502}
3503/**
3504 * Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state.
3505 *
3506 * @codeGenApi
3507 */
3508function ɵɵnamespaceSVG() {
3509 instructionState.lFrame.currentNamespace = SVG_NAMESPACE;
3510}
3511/**
3512 * Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state.
3513 *
3514 * @codeGenApi
3515 */
3516function ɵɵnamespaceMathML() {
3517 instructionState.lFrame.currentNamespace = MATH_ML_NAMESPACE;
3518}
3519/**
3520 * Sets the namespace used to create elements to `null`, which forces element creation to use
3521 * `createElement` rather than `createElementNS`.
3522 *
3523 * @codeGenApi
3524 */
3525function ɵɵnamespaceHTML() {
3526 namespaceHTMLInternal();
3527}
3528/**
3529 * Sets the namespace used to create elements to `null`, which forces element creation to use
3530 * `createElement` rather than `createElementNS`.
3531 */
3532function namespaceHTMLInternal() {
3533 instructionState.lFrame.currentNamespace = null;
3534}
3535function getNamespace$1() {
3536 return instructionState.lFrame.currentNamespace;
3537}
3538
3539/**
3540 * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`.
3541 *
3542 * Must be run *only* on the first template pass.
3543 *
3544 * Sets up the pre-order hooks on the provided `tView`,
3545 * see {@link HookData} for details about the data structure.
3546 *
3547 * @param directiveIndex The index of the directive in LView
3548 * @param directiveDef The definition containing the hooks to setup in tView
3549 * @param tView The current TView
3550 */
3551function registerPreOrderHooks(directiveIndex, directiveDef, tView) {
3552 ngDevMode && assertFirstCreatePass(tView);
3553 const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype;
3554 if (ngOnChanges) {
3555 const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef);
3556 (tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, wrappedOnChanges);
3557 (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = []))
3558 .push(directiveIndex, wrappedOnChanges);
3559 }
3560 if (ngOnInit) {
3561 (tView.preOrderHooks || (tView.preOrderHooks = [])).push(0 - directiveIndex, ngOnInit);
3562 }
3563 if (ngDoCheck) {
3564 (tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, ngDoCheck);
3565 (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(directiveIndex, ngDoCheck);
3566 }
3567}
3568/**
3569 *
3570 * Loops through the directives on the provided `tNode` and queues hooks to be
3571 * run that are not initialization hooks.
3572 *
3573 * Should be executed during `elementEnd()` and similar to
3574 * preserve hook execution order. Content, view, and destroy hooks for projected
3575 * components and directives must be called *before* their hosts.
3576 *
3577 * Sets up the content, view, and destroy hooks on the provided `tView`,
3578 * see {@link HookData} for details about the data structure.
3579 *
3580 * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up
3581 * separately at `elementStart`.
3582 *
3583 * @param tView The current TView
3584 * @param tNode The TNode whose directives are to be searched for hooks to queue
3585 */
3586function registerPostOrderHooks(tView, tNode) {
3587 ngDevMode && assertFirstCreatePass(tView);
3588 // It's necessary to loop through the directives at elementEnd() (rather than processing in
3589 // directiveCreate) so we can preserve the current hook order. Content, view, and destroy
3590 // hooks for projected components and directives must be called *before* their hosts.
3591 for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) {
3592 const directiveDef = tView.data[i];
3593 ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef');
3594 const lifecycleHooks = directiveDef.type.prototype;
3595 const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy } = lifecycleHooks;
3596 if (ngAfterContentInit) {
3597 (tView.contentHooks || (tView.contentHooks = [])).push(-i, ngAfterContentInit);
3598 }
3599 if (ngAfterContentChecked) {
3600 (tView.contentHooks || (tView.contentHooks = [])).push(i, ngAfterContentChecked);
3601 (tView.contentCheckHooks || (tView.contentCheckHooks = [])).push(i, ngAfterContentChecked);
3602 }
3603 if (ngAfterViewInit) {
3604 (tView.viewHooks || (tView.viewHooks = [])).push(-i, ngAfterViewInit);
3605 }
3606 if (ngAfterViewChecked) {
3607 (tView.viewHooks || (tView.viewHooks = [])).push(i, ngAfterViewChecked);
3608 (tView.viewCheckHooks || (tView.viewCheckHooks = [])).push(i, ngAfterViewChecked);
3609 }
3610 if (ngOnDestroy != null) {
3611 (tView.destroyHooks || (tView.destroyHooks = [])).push(i, ngOnDestroy);
3612 }
3613 }
3614}
3615/**
3616 * Executing hooks requires complex logic as we need to deal with 2 constraints.
3617 *
3618 * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
3619 * once, across many change detection cycles. This must be true even if some hooks throw, or if
3620 * some recursively trigger a change detection cycle.
3621 * To solve that, it is required to track the state of the execution of these init hooks.
3622 * This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
3623 * and the index within that phase. They can be seen as a cursor in the following structure:
3624 * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
3625 * They are are stored as flags in LView[FLAGS].
3626 *
3627 * 2. Pre-order hooks can be executed in batches, because of the select instruction.
3628 * To be able to pause and resume their execution, we also need some state about the hook's array
3629 * that is being processed:
3630 * - the index of the next hook to be executed
3631 * - the number of init hooks already found in the processed part of the array
3632 * They are are stored as flags in LView[PREORDER_HOOK_FLAGS].
3633 */
3634/**
3635 * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were
3636 * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read
3637 * / write of the init-hooks related flags.
3638 * @param lView The LView where hooks are defined
3639 * @param hooks Hooks to be run
3640 * @param nodeIndex 3 cases depending on the value:
3641 * - undefined: all hooks from the array should be executed (post-order case)
3642 * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
3643 * flushing the remaining hooks)
3644 * - number: execute hooks only from the saved index until that node index exclusive (pre-order
3645 * case, when executing select(number))
3646 */
3647function executeCheckHooks(lView, hooks, nodeIndex) {
3648 callHooks(lView, hooks, 3 /* InitPhaseState.InitPhaseCompleted */, nodeIndex);
3649}
3650/**
3651 * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked,
3652 * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed.
3653 * @param lView The LView where hooks are defined
3654 * @param hooks Hooks to be run
3655 * @param initPhase A phase for which hooks should be run
3656 * @param nodeIndex 3 cases depending on the value:
3657 * - undefined: all hooks from the array should be executed (post-order case)
3658 * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
3659 * flushing the remaining hooks)
3660 * - number: execute hooks only from the saved index until that node index exclusive (pre-order
3661 * case, when executing select(number))
3662 */
3663function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) {
3664 ngDevMode &&
3665 assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once');
3666 if ((lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
3667 callHooks(lView, hooks, initPhase, nodeIndex);
3668 }
3669}
3670function incrementInitPhaseFlags(lView, initPhase) {
3671 ngDevMode &&
3672 assertNotEqual(initPhase, 3 /* InitPhaseState.InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.');
3673 let flags = lView[FLAGS];
3674 if ((flags & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
3675 flags &= 2047 /* LViewFlags.IndexWithinInitPhaseReset */;
3676 flags += 1 /* LViewFlags.InitPhaseStateIncrementer */;
3677 lView[FLAGS] = flags;
3678 }
3679}
3680/**
3681 * Calls lifecycle hooks with their contexts, skipping init hooks if it's not
3682 * the first LView pass
3683 *
3684 * @param currentView The current view
3685 * @param arr The array in which the hooks are found
3686 * @param initPhaseState the current state of the init phase
3687 * @param currentNodeIndex 3 cases depending on the value:
3688 * - undefined: all hooks from the array should be executed (post-order case)
3689 * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
3690 * flushing the remaining hooks)
3691 * - number: execute hooks only from the saved index until that node index exclusive (pre-order
3692 * case, when executing select(number))
3693 */
3694function callHooks(currentView, arr, initPhase, currentNodeIndex) {
3695 ngDevMode &&
3696 assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.');
3697 const startIndex = currentNodeIndex !== undefined ?
3698 (currentView[PREORDER_HOOK_FLAGS] & 65535 /* PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask */) :
3699 0;
3700 const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1;
3701 const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1
3702 let lastNodeIndexFound = 0;
3703 for (let i = startIndex; i < max; i++) {
3704 const hook = arr[i + 1];
3705 if (typeof hook === 'number') {
3706 lastNodeIndexFound = arr[i];
3707 if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) {
3708 break;
3709 }
3710 }
3711 else {
3712 const isInitHook = arr[i] < 0;
3713 if (isInitHook)
3714 currentView[PREORDER_HOOK_FLAGS] += 65536 /* PreOrderHookFlags.NumberOfInitHooksCalledIncrementer */;
3715 if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) {
3716 callHook(currentView, initPhase, arr, i);
3717 currentView[PREORDER_HOOK_FLAGS] =
3718 (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* PreOrderHookFlags.NumberOfInitHooksCalledMask */) + i +
3719 2;
3720 }
3721 i++;
3722 }
3723 }
3724}
3725/**
3726 * Execute one hook against the current `LView`.
3727 *
3728 * @param currentView The current view
3729 * @param initPhaseState the current state of the init phase
3730 * @param arr The array in which the hooks are found
3731 * @param i The current index within the hook data array
3732 */
3733function callHook(currentView, initPhase, arr, i) {
3734 const isInitHook = arr[i] < 0;
3735 const hook = arr[i + 1];
3736 const directiveIndex = isInitHook ? -arr[i] : arr[i];
3737 const directive = currentView[directiveIndex];
3738 if (isInitHook) {
3739 const indexWithintInitPhase = currentView[FLAGS] >> 11 /* LViewFlags.IndexWithinInitPhaseShift */;
3740 // The init phase state must be always checked here as it may have been recursively updated.
3741 if (indexWithintInitPhase <
3742 (currentView[PREORDER_HOOK_FLAGS] >> 16 /* PreOrderHookFlags.NumberOfInitHooksCalledShift */) &&
3743 (currentView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === initPhase) {
3744 currentView[FLAGS] += 2048 /* LViewFlags.IndexWithinInitPhaseIncrementer */;
3745 profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook);
3746 try {
3747 hook.call(directive);
3748 }
3749 finally {
3750 profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook);
3751 }
3752 }
3753 }
3754 else {
3755 profiler(4 /* ProfilerEvent.LifecycleHookStart */, directive, hook);
3756 try {
3757 hook.call(directive);
3758 }
3759 finally {
3760 profiler(5 /* ProfilerEvent.LifecycleHookEnd */, directive, hook);
3761 }
3762 }
3763}
3764
3765const NO_PARENT_INJECTOR = -1;
3766/**
3767 * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in
3768 * `TView.data`. This allows us to store information about the current node's tokens (which
3769 * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be
3770 * shared, so they live in `LView`).
3771 *
3772 * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter
3773 * determines whether a directive is available on the associated node or not. This prevents us
3774 * from searching the directives array at this level unless it's probable the directive is in it.
3775 *
3776 * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
3777 *
3778 * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed
3779 * using interfaces as they were previously. The start index of each `LInjector` and `TInjector`
3780 * will differ based on where it is flattened into the main array, so it's not possible to know
3781 * the indices ahead of time and save their types here. The interfaces are still included here
3782 * for documentation purposes.
3783 *
3784 * export interface LInjector extends Array<any> {
3785 *
3786 * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
3787 * [0]: number;
3788 *
3789 * // Cumulative bloom for directive IDs 32-63
3790 * [1]: number;
3791 *
3792 * // Cumulative bloom for directive IDs 64-95
3793 * [2]: number;
3794 *
3795 * // Cumulative bloom for directive IDs 96-127
3796 * [3]: number;
3797 *
3798 * // Cumulative bloom for directive IDs 128-159
3799 * [4]: number;
3800 *
3801 * // Cumulative bloom for directive IDs 160 - 191
3802 * [5]: number;
3803 *
3804 * // Cumulative bloom for directive IDs 192 - 223
3805 * [6]: number;
3806 *
3807 * // Cumulative bloom for directive IDs 224 - 255
3808 * [7]: number;
3809 *
3810 * // We need to store a reference to the injector's parent so DI can keep looking up
3811 * // the injector tree until it finds the dependency it's looking for.
3812 * [PARENT_INJECTOR]: number;
3813 * }
3814 *
3815 * export interface TInjector extends Array<any> {
3816 *
3817 * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
3818 * [0]: number;
3819 *
3820 * // Shared node bloom for directive IDs 32-63
3821 * [1]: number;
3822 *
3823 * // Shared node bloom for directive IDs 64-95
3824 * [2]: number;
3825 *
3826 * // Shared node bloom for directive IDs 96-127
3827 * [3]: number;
3828 *
3829 * // Shared node bloom for directive IDs 128-159
3830 * [4]: number;
3831 *
3832 * // Shared node bloom for directive IDs 160 - 191
3833 * [5]: number;
3834 *
3835 * // Shared node bloom for directive IDs 192 - 223
3836 * [6]: number;
3837 *
3838 * // Shared node bloom for directive IDs 224 - 255
3839 * [7]: number;
3840 *
3841 * // Necessary to find directive indices for a particular node.
3842 * [TNODE]: TElementNode|TElementContainerNode|TContainerNode;
3843 * }
3844 */
3845/**
3846 * Factory for creating instances of injectors in the NodeInjector.
3847 *
3848 * This factory is complicated by the fact that it can resolve `multi` factories as well.
3849 *
3850 * NOTE: Some of the fields are optional which means that this class has two hidden classes.
3851 * - One without `multi` support (most common)
3852 * - One with `multi` values, (rare).
3853 *
3854 * Since VMs can cache up to 4 inline hidden classes this is OK.
3855 *
3856 * - Single factory: Only `resolving` and `factory` is defined.
3857 * - `providers` factory: `componentProviders` is a number and `index = -1`.
3858 * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`.
3859 */
3860class NodeInjectorFactory {
3861 constructor(
3862 /**
3863 * Factory to invoke in order to create a new instance.
3864 */
3865 factory,
3866 /**
3867 * Set to `true` if the token is declared in `viewProviders` (or if it is component).
3868 */
3869 isViewProvider, injectImplementation) {
3870 this.factory = factory;
3871 /**
3872 * Marker set to true during factory invocation to see if we get into recursive loop.
3873 * Recursive loop causes an error to be displayed.
3874 */
3875 this.resolving = false;
3876 ngDevMode && assertDefined(factory, 'Factory not specified');
3877 ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.');
3878 this.canSeeViewProviders = isViewProvider;
3879 this.injectImpl = injectImplementation;
3880 }
3881}
3882function isFactory(obj) {
3883 return obj instanceof NodeInjectorFactory;
3884}
3885// Note: This hack is necessary so we don't erroneously get a circular dependency
3886// failure based on types.
3887const unusedValueExportToPlacateAjd$2 = 1;
3888
3889/**
3890 * Converts `TNodeType` into human readable text.
3891 * Make sure this matches with `TNodeType`
3892 */
3893function toTNodeTypeAsString(tNodeType) {
3894 let text = '';
3895 (tNodeType & 1 /* TNodeType.Text */) && (text += '|Text');
3896 (tNodeType & 2 /* TNodeType.Element */) && (text += '|Element');
3897 (tNodeType & 4 /* TNodeType.Container */) && (text += '|Container');
3898 (tNodeType & 8 /* TNodeType.ElementContainer */) && (text += '|ElementContainer');
3899 (tNodeType & 16 /* TNodeType.Projection */) && (text += '|Projection');
3900 (tNodeType & 32 /* TNodeType.Icu */) && (text += '|IcuContainer');
3901 (tNodeType & 64 /* TNodeType.Placeholder */) && (text += '|Placeholder');
3902 return text.length > 0 ? text.substring(1) : text;
3903}
3904// Note: This hack is necessary so we don't erroneously get a circular dependency
3905// failure based on types.
3906const unusedValueExportToPlacateAjd$1 = 1;
3907/**
3908 * Returns `true` if the `TNode` has a directive which has `@Input()` for `class` binding.
3909 *
3910 * ```
3911 * <div my-dir [class]="exp"></div>
3912 * ```
3913 * and
3914 * ```
3915 * @Directive({
3916 * })
3917 * class MyDirective {
3918 * @Input()
3919 * class: string;
3920 * }
3921 * ```
3922 *
3923 * In the above case it is necessary to write the reconciled styling information into the
3924 * directive's input.
3925 *
3926 * @param tNode
3927 */
3928function hasClassInput(tNode) {
3929 return (tNode.flags & 8 /* TNodeFlags.hasClassInput */) !== 0;
3930}
3931/**
3932 * Returns `true` if the `TNode` has a directive which has `@Input()` for `style` binding.
3933 *
3934 * ```
3935 * <div my-dir [style]="exp"></div>
3936 * ```
3937 * and
3938 * ```
3939 * @Directive({
3940 * })
3941 * class MyDirective {
3942 * @Input()
3943 * class: string;
3944 * }
3945 * ```
3946 *
3947 * In the above case it is necessary to write the reconciled styling information into the
3948 * directive's input.
3949 *
3950 * @param tNode
3951 */
3952function hasStyleInput(tNode) {
3953 return (tNode.flags & 16 /* TNodeFlags.hasStyleInput */) !== 0;
3954}
3955
3956function assertTNodeType(tNode, expectedTypes, message) {
3957 assertDefined(tNode, 'should be called with a TNode');
3958 if ((tNode.type & expectedTypes) === 0) {
3959 throwError(message ||
3960 `Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${toTNodeTypeAsString(tNode.type)}.`);
3961 }
3962}
3963function assertPureTNodeType(type) {
3964 if (!(type === 2 /* TNodeType.Element */ || //
3965 type === 1 /* TNodeType.Text */ || //
3966 type === 4 /* TNodeType.Container */ || //
3967 type === 8 /* TNodeType.ElementContainer */ || //
3968 type === 32 /* TNodeType.Icu */ || //
3969 type === 16 /* TNodeType.Projection */ || //
3970 type === 64 /* TNodeType.Placeholder */)) {
3971 throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`);
3972 }
3973}
3974
3975/**
3976 * Assigns all attribute values to the provided element via the inferred renderer.
3977 *
3978 * This function accepts two forms of attribute entries:
3979 *
3980 * default: (key, value):
3981 * attrs = [key1, value1, key2, value2]
3982 *
3983 * namespaced: (NAMESPACE_MARKER, uri, name, value)
3984 * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
3985 *
3986 * The `attrs` array can contain a mix of both the default and namespaced entries.
3987 * The "default" values are set without a marker, but if the function comes across
3988 * a marker value then it will attempt to set a namespaced value. If the marker is
3989 * not of a namespaced value then the function will quit and return the index value
3990 * where it stopped during the iteration of the attrs array.
3991 *
3992 * See [AttributeMarker] to understand what the namespace marker value is.
3993 *
3994 * Note that this instruction does not support assigning style and class values to
3995 * an element. See `elementStart` and `elementHostAttrs` to learn how styling values
3996 * are applied to an element.
3997 * @param renderer The renderer to be used
3998 * @param native The element that the attributes will be assigned to
3999 * @param attrs The attribute array of values that will be assigned to the element
4000 * @returns the index value that was last accessed in the attributes array
4001 */
4002function setUpAttributes(renderer, native, attrs) {
4003 let i = 0;
4004 while (i < attrs.length) {
4005 const value = attrs[i];
4006 if (typeof value === 'number') {
4007 // only namespaces are supported. Other value types (such as style/class
4008 // entries) are not supported in this function.
4009 if (value !== 0 /* AttributeMarker.NamespaceURI */) {
4010 break;
4011 }
4012 // we just landed on the marker value ... therefore
4013 // we should skip to the next entry
4014 i++;
4015 const namespaceURI = attrs[i++];
4016 const attrName = attrs[i++];
4017 const attrVal = attrs[i++];
4018 ngDevMode && ngDevMode.rendererSetAttribute++;
4019 renderer.setAttribute(native, attrName, attrVal, namespaceURI);
4020 }
4021 else {
4022 // attrName is string;
4023 const attrName = value;
4024 const attrVal = attrs[++i];
4025 // Standard attributes
4026 ngDevMode && ngDevMode.rendererSetAttribute++;
4027 if (isAnimationProp(attrName)) {
4028 renderer.setProperty(native, attrName, attrVal);
4029 }
4030 else {
4031 renderer.setAttribute(native, attrName, attrVal);
4032 }
4033 i++;
4034 }
4035 }
4036 // another piece of code may iterate over the same attributes array. Therefore
4037 // it may be helpful to return the exact spot where the attributes array exited
4038 // whether by running into an unsupported marker or if all the static values were
4039 // iterated over.
4040 return i;
4041}
4042/**
4043 * Test whether the given value is a marker that indicates that the following
4044 * attribute values in a `TAttributes` array are only the names of attributes,
4045 * and not name-value pairs.
4046 * @param marker The attribute marker to test.
4047 * @returns true if the marker is a "name-only" marker (e.g. `Bindings`, `Template` or `I18n`).
4048 */
4049function isNameOnlyAttributeMarker(marker) {
4050 return marker === 3 /* AttributeMarker.Bindings */ || marker === 4 /* AttributeMarker.Template */ ||
4051 marker === 6 /* AttributeMarker.I18n */;
4052}
4053function isAnimationProp(name) {
4054 // Perf note: accessing charCodeAt to check for the first character of a string is faster as
4055 // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
4056 // charCodeAt doesn't allocate memory to return a substring.
4057 return name.charCodeAt(0) === 64 /* CharCode.AT_SIGN */;
4058}
4059/**
4060 * Merges `src` `TAttributes` into `dst` `TAttributes` removing any duplicates in the process.
4061 *
4062 * This merge function keeps the order of attrs same.
4063 *
4064 * @param dst Location of where the merged `TAttributes` should end up.
4065 * @param src `TAttributes` which should be appended to `dst`
4066 */
4067function mergeHostAttrs(dst, src) {
4068 if (src === null || src.length === 0) {
4069 // do nothing
4070 }
4071 else if (dst === null || dst.length === 0) {
4072 // We have source, but dst is empty, just make a copy.
4073 dst = src.slice();
4074 }
4075 else {
4076 let srcMarker = -1 /* AttributeMarker.ImplicitAttributes */;
4077 for (let i = 0; i < src.length; i++) {
4078 const item = src[i];
4079 if (typeof item === 'number') {
4080 srcMarker = item;
4081 }
4082 else {
4083 if (srcMarker === 0 /* AttributeMarker.NamespaceURI */) {
4084 // Case where we need to consume `key1`, `key2`, `value` items.
4085 }
4086 else if (srcMarker === -1 /* AttributeMarker.ImplicitAttributes */ ||
4087 srcMarker === 2 /* AttributeMarker.Styles */) {
4088 // Case where we have to consume `key1` and `value` only.
4089 mergeHostAttribute(dst, srcMarker, item, null, src[++i]);
4090 }
4091 else {
4092 // Case where we have to consume `key1` only.
4093 mergeHostAttribute(dst, srcMarker, item, null, null);
4094 }
4095 }
4096 }
4097 }
4098 return dst;
4099}
4100/**
4101 * Append `key`/`value` to existing `TAttributes` taking region marker and duplicates into account.
4102 *
4103 * @param dst `TAttributes` to append to.
4104 * @param marker Region where the `key`/`value` should be added.
4105 * @param key1 Key to add to `TAttributes`
4106 * @param key2 Key to add to `TAttributes` (in case of `AttributeMarker.NamespaceURI`)
4107 * @param value Value to add or to overwrite to `TAttributes` Only used if `marker` is not Class.
4108 */
4109function mergeHostAttribute(dst, marker, key1, key2, value) {
4110 let i = 0;
4111 // Assume that new markers will be inserted at the end.
4112 let markerInsertPosition = dst.length;
4113 // scan until correct type.
4114 if (marker === -1 /* AttributeMarker.ImplicitAttributes */) {
4115 markerInsertPosition = -1;
4116 }
4117 else {
4118 while (i < dst.length) {
4119 const dstValue = dst[i++];
4120 if (typeof dstValue === 'number') {
4121 if (dstValue === marker) {
4122 markerInsertPosition = -1;
4123 break;
4124 }
4125 else if (dstValue > marker) {
4126 // We need to save this as we want the markers to be inserted in specific order.
4127 markerInsertPosition = i - 1;
4128 break;
4129 }
4130 }
4131 }
4132 }
4133 // search until you find place of insertion
4134 while (i < dst.length) {
4135 const item = dst[i];
4136 if (typeof item === 'number') {
4137 // since `i` started as the index after the marker, we did not find it if we are at the next
4138 // marker
4139 break;
4140 }
4141 else if (item === key1) {
4142 // We already have same token
4143 if (key2 === null) {
4144 if (value !== null) {
4145 dst[i + 1] = value;
4146 }
4147 return;
4148 }
4149 else if (key2 === dst[i + 1]) {
4150 dst[i + 2] = value;
4151 return;
4152 }
4153 }
4154 // Increment counter.
4155 i++;
4156 if (key2 !== null)
4157 i++;
4158 if (value !== null)
4159 i++;
4160 }
4161 // insert at location.
4162 if (markerInsertPosition !== -1) {
4163 dst.splice(markerInsertPosition, 0, marker);
4164 i = markerInsertPosition + 1;
4165 }
4166 dst.splice(i++, 0, key1);
4167 if (key2 !== null) {
4168 dst.splice(i++, 0, key2);
4169 }
4170 if (value !== null) {
4171 dst.splice(i++, 0, value);
4172 }
4173}
4174
4175/// Parent Injector Utils ///////////////////////////////////////////////////////////////
4176function hasParentInjector(parentLocation) {
4177 return parentLocation !== NO_PARENT_INJECTOR;
4178}
4179function getParentInjectorIndex(parentLocation) {
4180 ngDevMode && assertNumber(parentLocation, 'Number expected');
4181 ngDevMode && assertNotEqual(parentLocation, -1, 'Not a valid state.');
4182 const parentInjectorIndex = parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */;
4183 ngDevMode &&
4184 assertGreaterThan(parentInjectorIndex, HEADER_OFFSET, 'Parent injector must be pointing past HEADER_OFFSET.');
4185 return parentLocation & 32767 /* RelativeInjectorLocationFlags.InjectorIndexMask */;
4186}
4187function getParentInjectorViewOffset(parentLocation) {
4188 return parentLocation >> 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */;
4189}
4190/**
4191 * Unwraps a parent injector location number to find the view offset from the current injector,
4192 * then walks up the declaration view tree until the view is found that contains the parent
4193 * injector.
4194 *
4195 * @param location The location of the parent injector, which contains the view offset
4196 * @param startView The LView instance from which to start walking up the view tree
4197 * @returns The LView instance that contains the parent injector
4198 */
4199function getParentInjectorView(location, startView) {
4200 let viewOffset = getParentInjectorViewOffset(location);
4201 let parentView = startView;
4202 // For most cases, the parent injector can be found on the host node (e.g. for component
4203 // or container), but we must keep the loop here to support the rarer case of deeply nested
4204 // <ng-template> tags or inline views, where the parent injector might live many views
4205 // above the child injector.
4206 while (viewOffset > 0) {
4207 parentView = parentView[DECLARATION_VIEW];
4208 viewOffset--;
4209 }
4210 return parentView;
4211}
4212
4213/**
4214 * Defines if the call to `inject` should include `viewProviders` in its resolution.
4215 *
4216 * This is set to true when we try to instantiate a component. This value is reset in
4217 * `getNodeInjectable` to a value which matches the declaration location of the token about to be
4218 * instantiated. This is done so that if we are injecting a token which was declared outside of
4219 * `viewProviders` we don't accidentally pull `viewProviders` in.
4220 *
4221 * Example:
4222 *
4223 * ```
4224 * @Injectable()
4225 * class MyService {
4226 * constructor(public value: String) {}
4227 * }
4228 *
4229 * @Component({
4230 * providers: [
4231 * MyService,
4232 * {provide: String, value: 'providers' }
4233 * ]
4234 * viewProviders: [
4235 * {provide: String, value: 'viewProviders'}
4236 * ]
4237 * })
4238 * class MyComponent {
4239 * constructor(myService: MyService, value: String) {
4240 * // We expect that Component can see into `viewProviders`.
4241 * expect(value).toEqual('viewProviders');
4242 * // `MyService` was not declared in `viewProviders` hence it can't see it.
4243 * expect(myService.value).toEqual('providers');
4244 * }
4245 * }
4246 *
4247 * ```
4248 */
4249let includeViewProviders = true;
4250function setIncludeViewProviders(v) {
4251 const oldValue = includeViewProviders;
4252 includeViewProviders = v;
4253 return oldValue;
4254}
4255/**
4256 * The number of slots in each bloom filter (used by DI). The larger this number, the fewer
4257 * directives that will share slots, and thus, the fewer false positives when checking for
4258 * the existence of a directive.
4259 */
4260const BLOOM_SIZE = 256;
4261const BLOOM_MASK = BLOOM_SIZE - 1;
4262/**
4263 * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits,
4264 * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash
4265 * number.
4266 */
4267const BLOOM_BUCKET_BITS = 5;
4268/** Counter used to generate unique IDs for directives. */
4269let nextNgElementId = 0;
4270/** Value used when something wasn't found by an injector. */
4271const NOT_FOUND = {};
4272/**
4273 * Registers this directive as present in its node's injector by flipping the directive's
4274 * corresponding bit in the injector's bloom filter.
4275 *
4276 * @param injectorIndex The index of the node injector where this token should be registered
4277 * @param tView The TView for the injector's bloom filters
4278 * @param type The directive token to register
4279 */
4280function bloomAdd(injectorIndex, tView, type) {
4281 ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true');
4282 let id;
4283 if (typeof type === 'string') {
4284 id = type.charCodeAt(0) || 0;
4285 }
4286 else if (type.hasOwnProperty(NG_ELEMENT_ID)) {
4287 id = type[NG_ELEMENT_ID];
4288 }
4289 // Set a unique ID on the directive type, so if something tries to inject the directive,
4290 // we can easily retrieve the ID and hash it into the bloom bit that should be checked.
4291 if (id == null) {
4292 id = type[NG_ELEMENT_ID] = nextNgElementId++;
4293 }
4294 // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
4295 // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
4296 const bloomHash = id & BLOOM_MASK;
4297 // Create a mask that targets the specific bit associated with the directive.
4298 // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
4299 // to bit positions 0 - 31 in a 32 bit integer.
4300 const mask = 1 << bloomHash;
4301 // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`.
4302 // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask
4303 // should be written to.
4304 tView.data[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask;
4305}
4306/**
4307 * Creates (or gets an existing) injector for a given element or container.
4308 *
4309 * @param tNode for which an injector should be retrieved / created.
4310 * @param lView View where the node is stored
4311 * @returns Node injector
4312 */
4313function getOrCreateNodeInjectorForNode(tNode, lView) {
4314 const existingInjectorIndex = getInjectorIndex(tNode, lView);
4315 if (existingInjectorIndex !== -1) {
4316 return existingInjectorIndex;
4317 }
4318 const tView = lView[TVIEW];
4319 if (tView.firstCreatePass) {
4320 tNode.injectorIndex = lView.length;
4321 insertBloom(tView.data, tNode); // foundation for node bloom
4322 insertBloom(lView, null); // foundation for cumulative bloom
4323 insertBloom(tView.blueprint, null);
4324 }
4325 const parentLoc = getParentInjectorLocation(tNode, lView);
4326 const injectorIndex = tNode.injectorIndex;
4327 // If a parent injector can't be found, its location is set to -1.
4328 // In that case, we don't need to set up a cumulative bloom
4329 if (hasParentInjector(parentLoc)) {
4330 const parentIndex = getParentInjectorIndex(parentLoc);
4331 const parentLView = getParentInjectorView(parentLoc, lView);
4332 const parentData = parentLView[TVIEW].data;
4333 // Creates a cumulative bloom filter that merges the parent's bloom filter
4334 // and its own cumulative bloom (which contains tokens for all ancestors)
4335 for (let i = 0; i < 8 /* NodeInjectorOffset.BLOOM_SIZE */; i++) {
4336 lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i];
4337 }
4338 }
4339 lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */] = parentLoc;
4340 return injectorIndex;
4341}
4342function insertBloom(arr, footer) {
4343 arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer);
4344}
4345function getInjectorIndex(tNode, lView) {
4346 if (tNode.injectorIndex === -1 ||
4347 // If the injector index is the same as its parent's injector index, then the index has been
4348 // copied down from the parent node. No injector has been created yet on this node.
4349 (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) ||
4350 // After the first template pass, the injector index might exist but the parent values
4351 // might not have been calculated yet for this instance
4352 lView[tNode.injectorIndex + 8 /* NodeInjectorOffset.PARENT */] === null) {
4353 return -1;
4354 }
4355 else {
4356 ngDevMode && assertIndexInRange(lView, tNode.injectorIndex);
4357 return tNode.injectorIndex;
4358 }
4359}
4360/**
4361 * Finds the index of the parent injector, with a view offset if applicable. Used to set the
4362 * parent injector initially.
4363 *
4364 * @returns Returns a number that is the combination of the number of LViews that we have to go up
4365 * to find the LView containing the parent inject AND the index of the injector within that LView.
4366 */
4367function getParentInjectorLocation(tNode, lView) {
4368 if (tNode.parent && tNode.parent.injectorIndex !== -1) {
4369 // If we have a parent `TNode` and there is an injector associated with it we are done, because
4370 // the parent injector is within the current `LView`.
4371 return tNode.parent.injectorIndex; // ViewOffset is 0
4372 }
4373 // When parent injector location is computed it may be outside of the current view. (ie it could
4374 // be pointing to a declared parent location). This variable stores number of declaration parents
4375 // we need to walk up in order to find the parent injector location.
4376 let declarationViewOffset = 0;
4377 let parentTNode = null;
4378 let lViewCursor = lView;
4379 // The parent injector is not in the current `LView`. We will have to walk the declared parent
4380 // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent
4381 // `NodeInjector`.
4382 while (lViewCursor !== null) {
4383 parentTNode = getTNodeFromLView(lViewCursor);
4384 if (parentTNode === null) {
4385 // If we have no parent, than we are done.
4386 return NO_PARENT_INJECTOR;
4387 }
4388 ngDevMode && parentTNode && assertTNodeForLView(parentTNode, lViewCursor[DECLARATION_VIEW]);
4389 // Every iteration of the loop requires that we go to the declared parent.
4390 declarationViewOffset++;
4391 lViewCursor = lViewCursor[DECLARATION_VIEW];
4392 if (parentTNode.injectorIndex !== -1) {
4393 // We found a NodeInjector which points to something.
4394 return (parentTNode.injectorIndex |
4395 (declarationViewOffset << 16 /* RelativeInjectorLocationFlags.ViewOffsetShift */));
4396 }
4397 }
4398 return NO_PARENT_INJECTOR;
4399}
4400/**
4401 * Makes a type or an injection token public to the DI system by adding it to an
4402 * injector's bloom filter.
4403 *
4404 * @param di The node injector in which a directive will be added
4405 * @param token The type or the injection token to be made public
4406 */
4407function diPublicInInjector(injectorIndex, tView, token) {
4408 bloomAdd(injectorIndex, tView, token);
4409}
4410/**
4411 * Inject static attribute value into directive constructor.
4412 *
4413 * This method is used with `factory` functions which are generated as part of
4414 * `defineDirective` or `defineComponent`. The method retrieves the static value
4415 * of an attribute. (Dynamic attributes are not supported since they are not resolved
4416 * at the time of injection and can change over time.)
4417 *
4418 * # Example
4419 * Given:
4420 * ```
4421 * @Component(...)
4422 * class MyComponent {
4423 * constructor(@Attribute('title') title: string) { ... }
4424 * }
4425 * ```
4426 * When instantiated with
4427 * ```
4428 * <my-component title="Hello"></my-component>
4429 * ```
4430 *
4431 * Then factory method generated is:
4432 * ```
4433 * MyComponent.ɵcmp = defineComponent({
4434 * factory: () => new MyComponent(injectAttribute('title'))
4435 * ...
4436 * })
4437 * ```
4438 *
4439 * @publicApi
4440 */
4441function injectAttributeImpl(tNode, attrNameToInject) {
4442 ngDevMode && assertTNodeType(tNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
4443 ngDevMode && assertDefined(tNode, 'expecting tNode');
4444 if (attrNameToInject === 'class') {
4445 return tNode.classes;
4446 }
4447 if (attrNameToInject === 'style') {
4448 return tNode.styles;
4449 }
4450 const attrs = tNode.attrs;
4451 if (attrs) {
4452 const attrsLength = attrs.length;
4453 let i = 0;
4454 while (i < attrsLength) {
4455 const value = attrs[i];
4456 // If we hit a `Bindings` or `Template` marker then we are done.
4457 if (isNameOnlyAttributeMarker(value))
4458 break;
4459 // Skip namespaced attributes
4460 if (value === 0 /* AttributeMarker.NamespaceURI */) {
4461 // we skip the next two values
4462 // as namespaced attributes looks like
4463 // [..., AttributeMarker.NamespaceURI, 'http://someuri.com/test', 'test:exist',
4464 // 'existValue', ...]
4465 i = i + 2;
4466 }
4467 else if (typeof value === 'number') {
4468 // Skip to the first value of the marked attribute.
4469 i++;
4470 while (i < attrsLength && typeof attrs[i] === 'string') {
4471 i++;
4472 }
4473 }
4474 else if (value === attrNameToInject) {
4475 return attrs[i + 1];
4476 }
4477 else {
4478 i = i + 2;
4479 }
4480 }
4481 }
4482 return null;
4483}
4484function notFoundValueOrThrow(notFoundValue, token, flags) {
4485 if ((flags & InjectFlags.Optional) || notFoundValue !== undefined) {
4486 return notFoundValue;
4487 }
4488 else {
4489 throwProviderNotFoundError(token, 'NodeInjector');
4490 }
4491}
4492/**
4493 * Returns the value associated to the given token from the ModuleInjector or throws exception
4494 *
4495 * @param lView The `LView` that contains the `tNode`
4496 * @param token The token to look for
4497 * @param flags Injection flags
4498 * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
4499 * @returns the value from the injector or throws an exception
4500 */
4501function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) {
4502 if ((flags & InjectFlags.Optional) && notFoundValue === undefined) {
4503 // This must be set or the NullInjector will throw for optional deps
4504 notFoundValue = null;
4505 }
4506 if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) {
4507 const moduleInjector = lView[INJECTOR$1];
4508 // switch to `injectInjectorOnly` implementation for module injector, since module injector
4509 // should not have access to Component/Directive DI scope (that may happen through
4510 // `directiveInject` implementation)
4511 const previousInjectImplementation = setInjectImplementation(undefined);
4512 try {
4513 if (moduleInjector) {
4514 return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional);
4515 }
4516 else {
4517 return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional);
4518 }
4519 }
4520 finally {
4521 setInjectImplementation(previousInjectImplementation);
4522 }
4523 }
4524 return notFoundValueOrThrow(notFoundValue, token, flags);
4525}
4526/**
4527 * Returns the value associated to the given token from the NodeInjectors => ModuleInjector.
4528 *
4529 * Look for the injector providing the token by walking up the node injector tree and then
4530 * the module injector tree.
4531 *
4532 * This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom
4533 * filter. `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`)
4534 *
4535 * @param tNode The Node where the search for the injector should start
4536 * @param lView The `LView` that contains the `tNode`
4537 * @param token The token to look for
4538 * @param flags Injection flags
4539 * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
4540 * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
4541 */
4542function getOrCreateInjectable(tNode, lView, token, flags = InjectFlags.Default, notFoundValue) {
4543 if (tNode !== null) {
4544 // If the view or any of its ancestors have an embedded
4545 // view injector, we have to look it up there first.
4546 if (lView[FLAGS] & 1024 /* LViewFlags.HasEmbeddedViewInjector */) {
4547 const embeddedInjectorValue = lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, NOT_FOUND);
4548 if (embeddedInjectorValue !== NOT_FOUND) {
4549 return embeddedInjectorValue;
4550 }
4551 }
4552 // Otherwise try the node injector.
4553 const value = lookupTokenUsingNodeInjector(tNode, lView, token, flags, NOT_FOUND);
4554 if (value !== NOT_FOUND) {
4555 return value;
4556 }
4557 }
4558 // Finally, fall back to the module injector.
4559 return lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
4560}
4561/**
4562 * Returns the value associated to the given token from the node injector.
4563 *
4564 * @param tNode The Node where the search for the injector should start
4565 * @param lView The `LView` that contains the `tNode`
4566 * @param token The token to look for
4567 * @param flags Injection flags
4568 * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
4569 * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
4570 */
4571function lookupTokenUsingNodeInjector(tNode, lView, token, flags, notFoundValue) {
4572 const bloomHash = bloomHashBitOrFactory(token);
4573 // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
4574 // so just call the factory function to create it.
4575 if (typeof bloomHash === 'function') {
4576 if (!enterDI(lView, tNode, flags)) {
4577 // Failed to enter DI, try module injector instead. If a token is injected with the @Host
4578 // flag, the module injector is not searched for that token in Ivy.
4579 return (flags & InjectFlags.Host) ?
4580 notFoundValueOrThrow(notFoundValue, token, flags) :
4581 lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
4582 }
4583 try {
4584 const value = bloomHash(flags);
4585 if (value == null && !(flags & InjectFlags.Optional)) {
4586 throwProviderNotFoundError(token);
4587 }
4588 else {
4589 return value;
4590 }
4591 }
4592 finally {
4593 leaveDI();
4594 }
4595 }
4596 else if (typeof bloomHash === 'number') {
4597 // A reference to the previous injector TView that was found while climbing the element
4598 // injector tree. This is used to know if viewProviders can be accessed on the current
4599 // injector.
4600 let previousTView = null;
4601 let injectorIndex = getInjectorIndex(tNode, lView);
4602 let parentLocation = NO_PARENT_INJECTOR;
4603 let hostTElementNode = flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null;
4604 // If we should skip this injector, or if there is no injector on this node, start by
4605 // searching the parent injector.
4606 if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
4607 parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
4608 lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */];
4609 if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) {
4610 injectorIndex = -1;
4611 }
4612 else {
4613 previousTView = lView[TVIEW];
4614 injectorIndex = getParentInjectorIndex(parentLocation);
4615 lView = getParentInjectorView(parentLocation, lView);
4616 }
4617 }
4618 // Traverse up the injector tree until we find a potential match or until we know there
4619 // *isn't* a match.
4620 while (injectorIndex !== -1) {
4621 ngDevMode && assertNodeInjector(lView, injectorIndex);
4622 // Check the current injector. If it matches, see if it contains token.
4623 const tView = lView[TVIEW];
4624 ngDevMode &&
4625 assertTNodeForLView(tView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */], lView);
4626 if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
4627 // At this point, we have an injector which *may* contain the token, so we step through
4628 // the providers and directives associated with the injector's corresponding node to get
4629 // the instance.
4630 const instance = searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode);
4631 if (instance !== NOT_FOUND) {
4632 return instance;
4633 }
4634 }
4635 parentLocation = lView[injectorIndex + 8 /* NodeInjectorOffset.PARENT */];
4636 if (parentLocation !== NO_PARENT_INJECTOR &&
4637 shouldSearchParent(flags, lView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */] === hostTElementNode) &&
4638 bloomHasToken(bloomHash, injectorIndex, lView)) {
4639 // The def wasn't found anywhere on this node, so it was a false positive.
4640 // Traverse up the tree and continue searching.
4641 previousTView = tView;
4642 injectorIndex = getParentInjectorIndex(parentLocation);
4643 lView = getParentInjectorView(parentLocation, lView);
4644 }
4645 else {
4646 // If we should not search parent OR If the ancestor bloom filter value does not have the
4647 // bit corresponding to the directive we can give up on traversing up to find the specific
4648 // injector.
4649 injectorIndex = -1;
4650 }
4651 }
4652 }
4653 return notFoundValue;
4654}
4655function searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode) {
4656 const currentTView = lView[TVIEW];
4657 const tNode = currentTView.data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
4658 // First, we need to determine if view providers can be accessed by the starting element.
4659 // There are two possibilities
4660 const canAccessViewProviders = previousTView == null ?
4661 // 1) This is the first invocation `previousTView == null` which means that we are at the
4662 // `TNode` of where injector is starting to look. In such a case the only time we are allowed
4663 // to look into the ViewProviders is if:
4664 // - we are on a component
4665 // - AND the injector set `includeViewProviders` to true (implying that the token can see
4666 // ViewProviders because it is the Component or a Service which itself was declared in
4667 // ViewProviders)
4668 (isComponentHost(tNode) && includeViewProviders) :
4669 // 2) `previousTView != null` which means that we are now walking across the parent nodes.
4670 // In such a case we are only allowed to look into the ViewProviders if:
4671 // - We just crossed from child View to Parent View `previousTView != currentTView`
4672 // - AND the parent TNode is an Element.
4673 // This means that we just came from the Component's View and therefore are allowed to see
4674 // into the ViewProviders.
4675 (previousTView != currentTView && ((tNode.type & 3 /* TNodeType.AnyRNode */) !== 0));
4676 // This special case happens when there is a @host on the inject and when we are searching
4677 // on the host element node.
4678 const isHostSpecialCase = (flags & InjectFlags.Host) && hostTElementNode === tNode;
4679 const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase);
4680 if (injectableIdx !== null) {
4681 return getNodeInjectable(lView, currentTView, injectableIdx, tNode);
4682 }
4683 else {
4684 return NOT_FOUND;
4685 }
4686}
4687/**
4688 * Searches for the given token among the node's directives and providers.
4689 *
4690 * @param tNode TNode on which directives are present.
4691 * @param tView The tView we are currently processing
4692 * @param token Provider token or type of a directive to look for.
4693 * @param canAccessViewProviders Whether view providers should be considered.
4694 * @param isHostSpecialCase Whether the host special case applies.
4695 * @returns Index of a found directive or provider, or null when none found.
4696 */
4697function locateDirectiveOrProvider(tNode, tView, token, canAccessViewProviders, isHostSpecialCase) {
4698 const nodeProviderIndexes = tNode.providerIndexes;
4699 const tInjectables = tView.data;
4700 const injectablesStart = nodeProviderIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
4701 const directivesStart = tNode.directiveStart;
4702 const directiveEnd = tNode.directiveEnd;
4703 const cptViewProvidersCount = nodeProviderIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */;
4704 const startingIndex = canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
4705 // When the host special case applies, only the viewProviders and the component are visible
4706 const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd;
4707 for (let i = startingIndex; i < endIndex; i++) {
4708 const providerTokenOrDef = tInjectables[i];
4709 if (i < directivesStart && token === providerTokenOrDef ||
4710 i >= directivesStart && providerTokenOrDef.type === token) {
4711 return i;
4712 }
4713 }
4714 if (isHostSpecialCase) {
4715 const dirDef = tInjectables[directivesStart];
4716 if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
4717 return directivesStart;
4718 }
4719 }
4720 return null;
4721}
4722/**
4723 * Retrieve or instantiate the injectable from the `LView` at particular `index`.
4724 *
4725 * This function checks to see if the value has already been instantiated and if so returns the
4726 * cached `injectable`. Otherwise if it detects that the value is still a factory it
4727 * instantiates the `injectable` and caches the value.
4728 */
4729function getNodeInjectable(lView, tView, index, tNode) {
4730 let value = lView[index];
4731 const tData = tView.data;
4732 if (isFactory(value)) {
4733 const factory = value;
4734 if (factory.resolving) {
4735 throwCyclicDependencyError(stringifyForError(tData[index]));
4736 }
4737 const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
4738 factory.resolving = true;
4739 const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null;
4740 const success = enterDI(lView, tNode, InjectFlags.Default);
4741 ngDevMode &&
4742 assertEqual(success, true, 'Because flags do not contain \`SkipSelf\' we expect this to always succeed.');
4743 try {
4744 value = lView[index] = factory.factory(undefined, tData, lView, tNode);
4745 // This code path is hit for both directives and providers.
4746 // For perf reasons, we want to avoid searching for hooks on providers.
4747 // It does no harm to try (the hooks just won't exist), but the extra
4748 // checks are unnecessary and this is a hot path. So we check to see
4749 // if the index of the dependency is in the directive range for this
4750 // tNode. If it's not, we know it's a provider and skip hook registration.
4751 if (tView.firstCreatePass && index >= tNode.directiveStart) {
4752 ngDevMode && assertDirectiveDef(tData[index]);
4753 registerPreOrderHooks(index, tData[index], tView);
4754 }
4755 }
4756 finally {
4757 previousInjectImplementation !== null &&
4758 setInjectImplementation(previousInjectImplementation);
4759 setIncludeViewProviders(previousIncludeViewProviders);
4760 factory.resolving = false;
4761 leaveDI();
4762 }
4763 }
4764 return value;
4765}
4766/**
4767 * Returns the bit in an injector's bloom filter that should be used to determine whether or not
4768 * the directive might be provided by the injector.
4769 *
4770 * When a directive is public, it is added to the bloom filter and given a unique ID that can be
4771 * retrieved on the Type. When the directive isn't public or the token is not a directive `null`
4772 * is returned as the node injector can not possibly provide that token.
4773 *
4774 * @param token the injection token
4775 * @returns the matching bit to check in the bloom filter or `null` if the token is not known.
4776 * When the returned value is negative then it represents special values such as `Injector`.
4777 */
4778function bloomHashBitOrFactory(token) {
4779 ngDevMode && assertDefined(token, 'token must be defined');
4780 if (typeof token === 'string') {
4781 return token.charCodeAt(0) || 0;
4782 }
4783 const tokenId =
4784 // First check with `hasOwnProperty` so we don't get an inherited ID.
4785 token.hasOwnProperty(NG_ELEMENT_ID) ? token[NG_ELEMENT_ID] : undefined;
4786 // Negative token IDs are used for special objects such as `Injector`
4787 if (typeof tokenId === 'number') {
4788 if (tokenId >= 0) {
4789 return tokenId & BLOOM_MASK;
4790 }
4791 else {
4792 ngDevMode &&
4793 assertEqual(tokenId, -1 /* InjectorMarkers.Injector */, 'Expecting to get Special Injector Id');
4794 return createNodeInjector;
4795 }
4796 }
4797 else {
4798 return tokenId;
4799 }
4800}
4801function bloomHasToken(bloomHash, injectorIndex, injectorView) {
4802 // Create a mask that targets the specific bit associated with the directive we're looking for.
4803 // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
4804 // to bit positions 0 - 31 in a 32 bit integer.
4805 const mask = 1 << bloomHash;
4806 // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of
4807 // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset
4808 // that should be used.
4809 const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)];
4810 // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
4811 // this injector is a potential match.
4812 return !!(value & mask);
4813}
4814/** Returns true if flags prevent parent injector from being searched for tokens */
4815function shouldSearchParent(flags, isFirstHostTNode) {
4816 return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
4817}
4818class NodeInjector {
4819 constructor(_tNode, _lView) {
4820 this._tNode = _tNode;
4821 this._lView = _lView;
4822 }
4823 get(token, notFoundValue, flags) {
4824 return getOrCreateInjectable(this._tNode, this._lView, token, convertToBitFlags(flags), notFoundValue);
4825 }
4826}
4827/** Creates a `NodeInjector` for the current node. */
4828function createNodeInjector() {
4829 return new NodeInjector(getCurrentTNode(), getLView());
4830}
4831/**
4832 * @codeGenApi
4833 */
4834function ɵɵgetInheritedFactory(type) {
4835 return noSideEffects(() => {
4836 const ownConstructor = type.prototype.constructor;
4837 const ownFactory = ownConstructor[NG_FACTORY_DEF] || getFactoryOf(ownConstructor);
4838 const objectPrototype = Object.prototype;
4839 let parent = Object.getPrototypeOf(type.prototype).constructor;
4840 // Go up the prototype until we hit `Object`.
4841 while (parent && parent !== objectPrototype) {
4842 const factory = parent[NG_FACTORY_DEF] || getFactoryOf(parent);
4843 // If we hit something that has a factory and the factory isn't the same as the type,
4844 // we've found the inherited factory. Note the check that the factory isn't the type's
4845 // own factory is redundant in most cases, but if the user has custom decorators on the
4846 // class, this lookup will start one level down in the prototype chain, causing us to
4847 // find the own factory first and potentially triggering an infinite loop downstream.
4848 if (factory && factory !== ownFactory) {
4849 return factory;
4850 }
4851 parent = Object.getPrototypeOf(parent);
4852 }
4853 // There is no factory defined. Either this was improper usage of inheritance
4854 // (no Angular decorator on the superclass) or there is no constructor at all
4855 // in the inheritance chain. Since the two cases cannot be distinguished, the
4856 // latter has to be assumed.
4857 return t => new t();
4858 });
4859}
4860function getFactoryOf(type) {
4861 if (isForwardRef(type)) {
4862 return () => {
4863 const factory = getFactoryOf(resolveForwardRef(type));
4864 return factory && factory();
4865 };
4866 }
4867 return getFactoryDef(type);
4868}
4869/**
4870 * Returns a value from the closest embedded or node injector.
4871 *
4872 * @param tNode The Node where the search for the injector should start
4873 * @param lView The `LView` that contains the `tNode`
4874 * @param token The token to look for
4875 * @param flags Injection flags
4876 * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
4877 * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
4878 */
4879function lookupTokenUsingEmbeddedInjector(tNode, lView, token, flags, notFoundValue) {
4880 let currentTNode = tNode;
4881 let currentLView = lView;
4882 // When an LView with an embedded view injector is inserted, it'll likely be interlaced with
4883 // nodes who may have injectors (e.g. node injector -> embedded view injector -> node injector).
4884 // Since the bloom filters for the node injectors have already been constructed and we don't
4885 // have a way of extracting the records from an injector, the only way to maintain the correct
4886 // hierarchy when resolving the value is to walk it node-by-node while attempting to resolve
4887 // the token at each level.
4888 while (currentTNode !== null && currentLView !== null &&
4889 (currentLView[FLAGS] & 1024 /* LViewFlags.HasEmbeddedViewInjector */) &&
4890 !(currentLView[FLAGS] & 256 /* LViewFlags.IsRoot */)) {
4891 ngDevMode && assertTNodeForLView(currentTNode, currentLView);
4892 // Note that this lookup on the node injector is using the `Self` flag, because
4893 // we don't want the node injector to look at any parent injectors since we
4894 // may hit the embedded view injector first.
4895 const nodeInjectorValue = lookupTokenUsingNodeInjector(currentTNode, currentLView, token, flags | InjectFlags.Self, NOT_FOUND);
4896 if (nodeInjectorValue !== NOT_FOUND) {
4897 return nodeInjectorValue;
4898 }
4899 // Has an explicit type due to a TS bug: https://github.com/microsoft/TypeScript/issues/33191
4900 let parentTNode = currentTNode.parent;
4901 // `TNode.parent` includes the parent within the current view only. If it doesn't exist,
4902 // it means that we've hit the view boundary and we need to go up to the next view.
4903 if (!parentTNode) {
4904 // Before we go to the next LView, check if the token exists on the current embedded injector.
4905 const embeddedViewInjector = currentLView[EMBEDDED_VIEW_INJECTOR];
4906 if (embeddedViewInjector) {
4907 const embeddedViewInjectorValue = embeddedViewInjector.get(token, NOT_FOUND, flags);
4908 if (embeddedViewInjectorValue !== NOT_FOUND) {
4909 return embeddedViewInjectorValue;
4910 }
4911 }
4912 // Otherwise keep going up the tree.
4913 parentTNode = getTNodeFromLView(currentLView);
4914 currentLView = currentLView[DECLARATION_VIEW];
4915 }
4916 currentTNode = parentTNode;
4917 }
4918 return notFoundValue;
4919}
4920/** Gets the TNode associated with an LView inside of the declaration view. */
4921function getTNodeFromLView(lView) {
4922 const tView = lView[TVIEW];
4923 const tViewType = tView.type;
4924 // The parent pointer differs based on `TView.type`.
4925 if (tViewType === 2 /* TViewType.Embedded */) {
4926 ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
4927 return tView.declTNode;
4928 }
4929 else if (tViewType === 1 /* TViewType.Component */) {
4930 // Components don't have `TView.declTNode` because each instance of component could be
4931 // inserted in different location, hence `TView.declTNode` is meaningless.
4932 return lView[T_HOST];
4933 }
4934 return null;
4935}
4936
4937/**
4938 * Facade for the attribute injection from DI.
4939 *
4940 * @codeGenApi
4941 */
4942function ɵɵinjectAttribute(attrNameToInject) {
4943 return injectAttributeImpl(getCurrentTNode(), attrNameToInject);
4944}
4945
4946/**
4947 * Attribute decorator and metadata.
4948 *
4949 * @Annotation
4950 * @publicApi
4951 */
4952const Attribute = makeParamDecorator('Attribute', (attributeName) => ({ attributeName, __NG_ELEMENT_ID__: () => ɵɵinjectAttribute(attributeName) }));
4953
4954let _reflect = null;
4955function getReflect() {
4956 return (_reflect = _reflect || new ReflectionCapabilities());
4957}
4958function reflectDependencies(type) {
4959 return convertDependencies(getReflect().parameters(type));
4960}
4961function convertDependencies(deps) {
4962 return deps.map(dep => reflectDependency(dep));
4963}
4964function reflectDependency(dep) {
4965 const meta = {
4966 token: null,
4967 attribute: null,
4968 host: false,
4969 optional: false,
4970 self: false,
4971 skipSelf: false,
4972 };
4973 if (Array.isArray(dep) && dep.length > 0) {
4974 for (let j = 0; j < dep.length; j++) {
4975 const param = dep[j];
4976 if (param === undefined) {
4977 // param may be undefined if type of dep is not set by ngtsc
4978 continue;
4979 }
4980 const proto = Object.getPrototypeOf(param);
4981 if (param instanceof Optional || proto.ngMetadataName === 'Optional') {
4982 meta.optional = true;
4983 }
4984 else if (param instanceof SkipSelf || proto.ngMetadataName === 'SkipSelf') {
4985 meta.skipSelf = true;
4986 }
4987 else if (param instanceof Self || proto.ngMetadataName === 'Self') {
4988 meta.self = true;
4989 }
4990 else if (param instanceof Host || proto.ngMetadataName === 'Host') {
4991 meta.host = true;
4992 }
4993 else if (param instanceof Inject) {
4994 meta.token = param.token;
4995 }
4996 else if (param instanceof Attribute) {
4997 if (param.attributeName === undefined) {
4998 throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Attribute name must be defined.`);
4999 }
5000 meta.attribute = param.attributeName;
5001 }
5002 else {
5003 meta.token = param;
5004 }
5005 }
5006 }
5007 else if (dep === undefined || (Array.isArray(dep) && dep.length === 0)) {
5008 meta.token = null;
5009 }
5010 else {
5011 meta.token = dep;
5012 }
5013 return meta;
5014}
5015
5016/**
5017 * Map of module-id to the corresponding NgModule.
5018 */
5019const modules = new Map();
5020/**
5021 * Whether to check for duplicate NgModule registrations.
5022 *
5023 * This can be disabled for testing.
5024 */
5025let checkForDuplicateNgModules = true;
5026function assertSameOrNotExisting(id, type, incoming) {
5027 if (type && type !== incoming && checkForDuplicateNgModules) {
5028 throw new Error(`Duplicate module registered for ${id} - ${stringify(type)} vs ${stringify(type.name)}`);
5029 }
5030}
5031/**
5032 * Adds the given NgModule type to Angular's NgModule registry.
5033 *
5034 * This is generated as a side-effect of NgModule compilation. Note that the `id` is passed in
5035 * explicitly and not read from the NgModule definition. This is for two reasons: it avoids a
5036 * megamorphic read, and in JIT there's a chicken-and-egg problem where the NgModule may not be
5037 * fully resolved when it's registered.
5038 *
5039 * @codeGenApi
5040 */
5041function registerNgModuleType(ngModuleType, id) {
5042 const existing = modules.get(id) || null;
5043 assertSameOrNotExisting(id, existing, ngModuleType);
5044 modules.set(id, ngModuleType);
5045}
5046function clearModulesForTest() {
5047 modules.clear();
5048}
5049function getRegisteredNgModuleType(id) {
5050 return modules.get(id);
5051}
5052/**
5053 * Control whether the NgModule registration system enforces that each NgModule type registered has
5054 * a unique id.
5055 *
5056 * This is useful for testing as the NgModule registry cannot be properly reset between tests with
5057 * Angular's current API.
5058 */
5059function setAllowDuplicateNgModuleIdsForTest(allowDuplicates) {
5060 checkForDuplicateNgModules = !allowDuplicates;
5061}
5062
5063/**
5064 *
5065 * @codeGenApi
5066 */
5067function ɵɵresolveWindow(element) {
5068 return element.ownerDocument.defaultView;
5069}
5070/**
5071 *
5072 * @codeGenApi
5073 */
5074function ɵɵresolveDocument(element) {
5075 return element.ownerDocument;
5076}
5077/**
5078 *
5079 * @codeGenApi
5080 */
5081function ɵɵresolveBody(element) {
5082 return element.ownerDocument.body;
5083}
5084/**
5085 * The special delimiter we use to separate property names, prefixes, and suffixes
5086 * in property binding metadata. See storeBindingMetadata().
5087 *
5088 * We intentionally use the Unicode "REPLACEMENT CHARACTER" (U+FFFD) as a delimiter
5089 * because it is a very uncommon character that is unlikely to be part of a user's
5090 * property names or interpolation strings. If it is in fact used in a property
5091 * binding, DebugElement.properties will not return the correct value for that
5092 * binding. However, there should be no runtime effect for real applications.
5093 *
5094 * This character is typically rendered as a question mark inside of a diamond.
5095 * See https://en.wikipedia.org/wiki/Specials_(Unicode_block)
5096 *
5097 */
5098const INTERPOLATION_DELIMITER = `�`;
5099/**
5100 * Unwrap a value which might be behind a closure (for forward declaration reasons).
5101 */
5102function maybeUnwrapFn$1(value) {
5103 if (value instanceof Function) {
5104 return value();
5105 }
5106 else {
5107 return value;
5108 }
5109}
5110
5111/**
5112 * Defines a schema that allows an NgModule to contain the following:
5113 * - Non-Angular elements named with dash case (`-`).
5114 * - Element properties named with dash case (`-`).
5115 * Dash case is the naming convention for custom elements.
5116 *
5117 * @publicApi
5118 */
5119const CUSTOM_ELEMENTS_SCHEMA = {
5120 name: 'custom-elements'
5121};
5122/**
5123 * Defines a schema that allows any property on any element.
5124 *
5125 * This schema allows you to ignore the errors related to any unknown elements or properties in a
5126 * template. The usage of this schema is generally discouraged because it prevents useful validation
5127 * and may hide real errors in your template. Consider using the `CUSTOM_ELEMENTS_SCHEMA` instead.
5128 *
5129 * @publicApi
5130 */
5131const NO_ERRORS_SCHEMA = {
5132 name: 'no-errors-schema'
5133};
5134
5135let shouldThrowErrorOnUnknownElement = false;
5136/**
5137 * Sets a strict mode for JIT-compiled components to throw an error on unknown elements,
5138 * instead of just logging the error.
5139 * (for AOT-compiled ones this check happens at build time).
5140 */
5141function ɵsetUnknownElementStrictMode(shouldThrow) {
5142 shouldThrowErrorOnUnknownElement = shouldThrow;
5143}
5144/**
5145 * Gets the current value of the strict mode.
5146 */
5147function ɵgetUnknownElementStrictMode() {
5148 return shouldThrowErrorOnUnknownElement;
5149}
5150let shouldThrowErrorOnUnknownProperty = false;
5151/**
5152 * Sets a strict mode for JIT-compiled components to throw an error on unknown properties,
5153 * instead of just logging the error.
5154 * (for AOT-compiled ones this check happens at build time).
5155 */
5156function ɵsetUnknownPropertyStrictMode(shouldThrow) {
5157 shouldThrowErrorOnUnknownProperty = shouldThrow;
5158}
5159/**
5160 * Gets the current value of the strict mode.
5161 */
5162function ɵgetUnknownPropertyStrictMode() {
5163 return shouldThrowErrorOnUnknownProperty;
5164}
5165/**
5166 * Validates that the element is known at runtime and produces
5167 * an error if it's not the case.
5168 * This check is relevant for JIT-compiled components (for AOT-compiled
5169 * ones this check happens at build time).
5170 *
5171 * The element is considered known if either:
5172 * - it's a known HTML element
5173 * - it's a known custom element
5174 * - the element matches any directive
5175 * - the element is allowed by one of the schemas
5176 *
5177 * @param element Element to validate
5178 * @param lView An `LView` that represents a current component that is being rendered
5179 * @param tagName Name of the tag to check
5180 * @param schemas Array of schemas
5181 * @param hasDirectives Boolean indicating that the element matches any directive
5182 */
5183function validateElementIsKnown(element, lView, tagName, schemas, hasDirectives) {
5184 // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
5185 // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
5186 // defined as an array (as an empty array in case `schemas` field is not defined) and we should
5187 // execute the check below.
5188 if (schemas === null)
5189 return;
5190 // If the element matches any directive, it's considered as valid.
5191 if (!hasDirectives && tagName !== null) {
5192 // The element is unknown if it's an instance of HTMLUnknownElement, or it isn't registered
5193 // as a custom element. Note that unknown elements with a dash in their name won't be instances
5194 // of HTMLUnknownElement in browsers that support web components.
5195 const isUnknown =
5196 // Note that we can't check for `typeof HTMLUnknownElement === 'function'`,
5197 // because while most browsers return 'function', IE returns 'object'.
5198 (typeof HTMLUnknownElement !== 'undefined' && HTMLUnknownElement &&
5199 element instanceof HTMLUnknownElement) ||
5200 (typeof customElements !== 'undefined' && tagName.indexOf('-') > -1 &&
5201 !customElements.get(tagName));
5202 if (isUnknown && !matchingSchemas(schemas, tagName)) {
5203 const isHostStandalone = isHostComponentStandalone(lView);
5204 const templateLocation = getTemplateLocationDetails(lView);
5205 const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
5206 let message = `'${tagName}' is not a known element${templateLocation}:\n`;
5207 message += `1. If '${tagName}' is an Angular component, then verify that it is ${isHostStandalone ? 'included in the \'@Component.imports\' of this component' :
5208 'a part of an @NgModule where this component is declared'}.\n`;
5209 if (tagName && tagName.indexOf('-') > -1) {
5210 message +=
5211 `2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.`;
5212 }
5213 else {
5214 message +=
5215 `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
5216 }
5217 if (shouldThrowErrorOnUnknownElement) {
5218 throw new RuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message);
5219 }
5220 else {
5221 console.error(formatRuntimeError(304 /* RuntimeErrorCode.UNKNOWN_ELEMENT */, message));
5222 }
5223 }
5224 }
5225}
5226/**
5227 * Validates that the property of the element is known at runtime and returns
5228 * false if it's not the case.
5229 * This check is relevant for JIT-compiled components (for AOT-compiled
5230 * ones this check happens at build time).
5231 *
5232 * The property is considered known if either:
5233 * - it's a known property of the element
5234 * - the element is allowed by one of the schemas
5235 * - the property is used for animations
5236 *
5237 * @param element Element to validate
5238 * @param propName Name of the property to check
5239 * @param tagName Name of the tag hosting the property
5240 * @param schemas Array of schemas
5241 */
5242function isPropertyValid(element, propName, tagName, schemas) {
5243 // If `schemas` is set to `null`, that's an indication that this Component was compiled in AOT
5244 // mode where this check happens at compile time. In JIT mode, `schemas` is always present and
5245 // defined as an array (as an empty array in case `schemas` field is not defined) and we should
5246 // execute the check below.
5247 if (schemas === null)
5248 return true;
5249 // The property is considered valid if the element matches the schema, it exists on the element,
5250 // or it is synthetic, and we are in a browser context (web worker nodes should be skipped).
5251 if (matchingSchemas(schemas, tagName) || propName in element || isAnimationProp(propName)) {
5252 return true;
5253 }
5254 // Note: `typeof Node` returns 'function' in most browsers, but on IE it is 'object' so we
5255 // need to account for both here, while being careful with `typeof null` also returning 'object'.
5256 return typeof Node === 'undefined' || Node === null || !(element instanceof Node);
5257}
5258/**
5259 * Logs or throws an error that a property is not supported on an element.
5260 *
5261 * @param propName Name of the invalid property
5262 * @param tagName Name of the tag hosting the property
5263 * @param nodeType Type of the node hosting the property
5264 * @param lView An `LView` that represents a current component
5265 */
5266function handleUnknownPropertyError(propName, tagName, nodeType, lView) {
5267 // Special-case a situation when a structural directive is applied to
5268 // an `<ng-template>` element, for example: `<ng-template *ngIf="true">`.
5269 // In this case the compiler generates the `ɵɵtemplate` instruction with
5270 // the `null` as the tagName. The directive matching logic at runtime relies
5271 // on this effect (see `isInlineTemplate`), thus using the 'ng-template' as
5272 // a default value of the `tNode.value` is not feasible at this moment.
5273 if (!tagName && nodeType === 4 /* TNodeType.Container */) {
5274 tagName = 'ng-template';
5275 }
5276 const isHostStandalone = isHostComponentStandalone(lView);
5277 const templateLocation = getTemplateLocationDetails(lView);
5278 let message = `Can't bind to '${propName}' since it isn't a known property of '${tagName}'${templateLocation}.`;
5279 const schemas = `'${isHostStandalone ? '@Component' : '@NgModule'}.schemas'`;
5280 const importLocation = isHostStandalone ?
5281 'included in the \'@Component.imports\' of this component' :
5282 'a part of an @NgModule where this component is declared';
5283 if (KNOWN_CONTROL_FLOW_DIRECTIVES.has(propName)) {
5284 // Most likely this is a control flow directive (such as `*ngIf`) used in
5285 // a template, but the directive or the `CommonModule` is not imported.
5286 const correspondingImport = KNOWN_CONTROL_FLOW_DIRECTIVES.get(propName);
5287 message += `\nIf the '${propName}' is an Angular control flow directive, ` +
5288 `please make sure that either the '${correspondingImport}' directive or the 'CommonModule' is ${importLocation}.`;
5289 }
5290 else {
5291 // May be an Angular component, which is not imported/declared?
5292 message += `\n1. If '${tagName}' is an Angular component and it has the ` +
5293 `'${propName}' input, then verify that it is ${importLocation}.`;
5294 // May be a Web Component?
5295 if (tagName && tagName.indexOf('-') > -1) {
5296 message += `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' ` +
5297 `to the ${schemas} of this component to suppress this message.`;
5298 message += `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
5299 `the ${schemas} of this component.`;
5300 }
5301 else {
5302 // If it's expected, the error can be suppressed by the `NO_ERRORS_SCHEMA` schema.
5303 message += `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to ` +
5304 `the ${schemas} of this component.`;
5305 }
5306 }
5307 reportUnknownPropertyError(message);
5308}
5309function reportUnknownPropertyError(message) {
5310 if (shouldThrowErrorOnUnknownProperty) {
5311 throw new RuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message);
5312 }
5313 else {
5314 console.error(formatRuntimeError(303 /* RuntimeErrorCode.UNKNOWN_BINDING */, message));
5315 }
5316}
5317/**
5318 * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
5319 * and must **not** be used in production bundles. The function makes megamorphic reads, which might
5320 * be too slow for production mode and also it relies on the constructor function being available.
5321 *
5322 * Gets a reference to the host component def (where a current component is declared).
5323 *
5324 * @param lView An `LView` that represents a current component that is being rendered.
5325 */
5326function getDeclarationComponentDef(lView) {
5327 !ngDevMode && throwError('Must never be called in production mode');
5328 const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
5329 const context = declarationLView[CONTEXT];
5330 // Unable to obtain a context.
5331 if (!context)
5332 return null;
5333 return context.constructor ? getComponentDef$1(context.constructor) : null;
5334}
5335/**
5336 * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
5337 * and must **not** be used in production bundles. The function makes megamorphic reads, which might
5338 * be too slow for production mode.
5339 *
5340 * Checks if the current component is declared inside of a standalone component template.
5341 *
5342 * @param lView An `LView` that represents a current component that is being rendered.
5343 */
5344function isHostComponentStandalone(lView) {
5345 !ngDevMode && throwError('Must never be called in production mode');
5346 const componentDef = getDeclarationComponentDef(lView);
5347 // Treat host component as non-standalone if we can't obtain the def.
5348 return !!componentDef?.standalone;
5349}
5350/**
5351 * WARNING: this is a **dev-mode only** function (thus should always be guarded by the `ngDevMode`)
5352 * and must **not** be used in production bundles. The function makes megamorphic reads, which might
5353 * be too slow for production mode.
5354 *
5355 * Constructs a string describing the location of the host component template. The function is used
5356 * in dev mode to produce error messages.
5357 *
5358 * @param lView An `LView` that represents a current component that is being rendered.
5359 */
5360function getTemplateLocationDetails(lView) {
5361 !ngDevMode && throwError('Must never be called in production mode');
5362 const hostComponentDef = getDeclarationComponentDef(lView);
5363 const componentClassName = hostComponentDef?.type?.name;
5364 return componentClassName ? ` (used in the '${componentClassName}' component template)` : '';
5365}
5366/**
5367 * The set of known control flow directives and their corresponding imports.
5368 * We use this set to produce a more precises error message with a note
5369 * that the `CommonModule` should also be included.
5370 */
5371const KNOWN_CONTROL_FLOW_DIRECTIVES = new Map([
5372 ['ngIf', 'NgIf'], ['ngFor', 'NgFor'], ['ngSwitchCase', 'NgSwitchCase'],
5373 ['ngSwitchDefault', 'NgSwitchDefault']
5374]);
5375/**
5376 * Returns true if the tag name is allowed by specified schemas.
5377 * @param schemas Array of schemas
5378 * @param tagName Name of the tag
5379 */
5380function matchingSchemas(schemas, tagName) {
5381 if (schemas !== null) {
5382 for (let i = 0; i < schemas.length; i++) {
5383 const schema = schemas[i];
5384 if (schema === NO_ERRORS_SCHEMA ||
5385 schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
5386 return true;
5387 }
5388 }
5389 }
5390 return false;
5391}
5392
5393/**
5394 * Flags for renderer-specific style modifiers.
5395 * @publicApi
5396 */
5397var RendererStyleFlags2;
5398(function (RendererStyleFlags2) {
5399 // TODO(misko): This needs to be refactored into a separate file so that it can be imported from
5400 // `node_manipulation.ts` Currently doing the import cause resolution order to change and fails
5401 // the tests. The work around is to have hard coded value in `node_manipulation.ts` for now.
5402 /**
5403 * Marks a style as important.
5404 */
5405 RendererStyleFlags2[RendererStyleFlags2["Important"] = 1] = "Important";
5406 /**
5407 * Marks a style as using dash case naming (this-is-dash-case).
5408 */
5409 RendererStyleFlags2[RendererStyleFlags2["DashCase"] = 2] = "DashCase";
5410})(RendererStyleFlags2 || (RendererStyleFlags2 = {}));
5411
5412/**
5413 * Disallowed strings in the comment.
5414 *
5415 * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
5416 */
5417const COMMENT_DISALLOWED = /^>|^->|<!--|-->|--!>|<!-$/g;
5418/**
5419 * Delimiter in the disallowed strings which needs to be wrapped with zero with character.
5420 */
5421const COMMENT_DELIMITER = /(<|>)/;
5422const COMMENT_DELIMITER_ESCAPED = '\u200B$1\u200B';
5423/**
5424 * Escape the content of comment strings so that it can be safely inserted into a comment node.
5425 *
5426 * The issue is that HTML does not specify any way to escape comment end text inside the comment.
5427 * Consider: `<!-- The way you close a comment is with ">", and "->" at the beginning or by "-->" or
5428 * "--!>" at the end. -->`. Above the `"-->"` is meant to be text not an end to the comment. This
5429 * can be created programmatically through DOM APIs. (`<!--` are also disallowed.)
5430 *
5431 * see: https://html.spec.whatwg.org/multipage/syntax.html#comments
5432 *
5433 * ```
5434 * div.innerHTML = div.innerHTML
5435 * ```
5436 *
5437 * One would expect that the above code would be safe to do, but it turns out that because comment
5438 * text is not escaped, the comment may contain text which will prematurely close the comment
5439 * opening up the application for XSS attack. (In SSR we programmatically create comment nodes which
5440 * may contain such text and expect them to be safe.)
5441 *
5442 * This function escapes the comment text by looking for comment delimiters (`<` and `>`) and
5443 * surrounding them with `_>_` where the `_` is a zero width space `\u200B`. The result is that if a
5444 * comment contains any of the comment start/end delimiters (such as `<!--`, `-->` or `--!>`) the
5445 * text it will render normally but it will not cause the HTML parser to close/open the comment.
5446 *
5447 * @param value text to make safe for comment node by escaping the comment open/close character
5448 * sequence.
5449 */
5450function escapeCommentText(value) {
5451 return value.replace(COMMENT_DISALLOWED, (text) => text.replace(COMMENT_DELIMITER, COMMENT_DELIMITER_ESCAPED));
5452}
5453
5454// Keeps track of the currently-active LViews.
5455const TRACKED_LVIEWS = new Map();
5456// Used for generating unique IDs for LViews.
5457let uniqueIdCounter = 0;
5458/** Gets a unique ID that can be assigned to an LView. */
5459function getUniqueLViewId() {
5460 return uniqueIdCounter++;
5461}
5462/** Starts tracking an LView. */
5463function registerLView(lView) {
5464 ngDevMode && assertNumber(lView[ID], 'LView must have an ID in order to be registered');
5465 TRACKED_LVIEWS.set(lView[ID], lView);
5466}
5467/** Gets an LView by its unique ID. */
5468function getLViewById(id) {
5469 ngDevMode && assertNumber(id, 'ID used for LView lookup must be a number');
5470 return TRACKED_LVIEWS.get(id) || null;
5471}
5472/** Stops tracking an LView. */
5473function unregisterLView(lView) {
5474 ngDevMode && assertNumber(lView[ID], 'Cannot stop tracking an LView that does not have an ID');
5475 TRACKED_LVIEWS.delete(lView[ID]);
5476}
5477
5478/**
5479 * The internal view context which is specific to a given DOM element, directive or
5480 * component instance. Each value in here (besides the LView and element node details)
5481 * can be present, null or undefined. If undefined then it implies the value has not been
5482 * looked up yet, otherwise, if null, then a lookup was executed and nothing was found.
5483 *
5484 * Each value will get filled when the respective value is examined within the getContext
5485 * function. The component, element and each directive instance will share the same instance
5486 * of the context.
5487 */
5488class LContext {
5489 /** Component's parent view data. */
5490 get lView() {
5491 return getLViewById(this.lViewId);
5492 }
5493 constructor(
5494 /**
5495 * ID of the component's parent view data.
5496 */
5497 lViewId,
5498 /**
5499 * The index instance of the node.
5500 */
5501 nodeIndex,
5502 /**
5503 * The instance of the DOM node that is attached to the lNode.
5504 */
5505 native) {
5506 this.lViewId = lViewId;
5507 this.nodeIndex = nodeIndex;
5508 this.native = native;
5509 }
5510}
5511
5512/**
5513 * Returns the matching `LContext` data for a given DOM node, directive or component instance.
5514 *
5515 * This function will examine the provided DOM element, component, or directive instance\'s
5516 * monkey-patched property to derive the `LContext` data. Once called then the monkey-patched
5517 * value will be that of the newly created `LContext`.
5518 *
5519 * If the monkey-patched value is the `LView` instance then the context value for that
5520 * target will be created and the monkey-patch reference will be updated. Therefore when this
5521 * function is called it may mutate the provided element\'s, component\'s or any of the associated
5522 * directive\'s monkey-patch values.
5523 *
5524 * If the monkey-patch value is not detected then the code will walk up the DOM until an element
5525 * is found which contains a monkey-patch reference. When that occurs then the provided element
5526 * will be updated with a new context (which is then returned). If the monkey-patch value is not
5527 * detected for a component/directive instance then it will throw an error (all components and
5528 * directives should be automatically monkey-patched by ivy).
5529 *
5530 * @param target Component, Directive or DOM Node.
5531 */
5532function getLContext(target) {
5533 let mpValue = readPatchedData(target);
5534 if (mpValue) {
5535 // only when it's an array is it considered an LView instance
5536 // ... otherwise it's an already constructed LContext instance
5537 if (isLView(mpValue)) {
5538 const lView = mpValue;
5539 let nodeIndex;
5540 let component = undefined;
5541 let directives = undefined;
5542 if (isComponentInstance(target)) {
5543 nodeIndex = findViaComponent(lView, target);
5544 if (nodeIndex == -1) {
5545 throw new Error('The provided component was not found in the application');
5546 }
5547 component = target;
5548 }
5549 else if (isDirectiveInstance(target)) {
5550 nodeIndex = findViaDirective(lView, target);
5551 if (nodeIndex == -1) {
5552 throw new Error('The provided directive was not found in the application');
5553 }
5554 directives = getDirectivesAtNodeIndex(nodeIndex, lView);
5555 }
5556 else {
5557 nodeIndex = findViaNativeElement(lView, target);
5558 if (nodeIndex == -1) {
5559 return null;
5560 }
5561 }
5562 // the goal is not to fill the entire context full of data because the lookups
5563 // are expensive. Instead, only the target data (the element, component, container, ICU
5564 // expression or directive details) are filled into the context. If called multiple times
5565 // with different target values then the missing target data will be filled in.
5566 const native = unwrapRNode(lView[nodeIndex]);
5567 const existingCtx = readPatchedData(native);
5568 const context = (existingCtx && !Array.isArray(existingCtx)) ?
5569 existingCtx :
5570 createLContext(lView, nodeIndex, native);
5571 // only when the component has been discovered then update the monkey-patch
5572 if (component && context.component === undefined) {
5573 context.component = component;
5574 attachPatchData(context.component, context);
5575 }
5576 // only when the directives have been discovered then update the monkey-patch
5577 if (directives && context.directives === undefined) {
5578 context.directives = directives;
5579 for (let i = 0; i < directives.length; i++) {
5580 attachPatchData(directives[i], context);
5581 }
5582 }
5583 attachPatchData(context.native, context);
5584 mpValue = context;
5585 }
5586 }
5587 else {
5588 const rElement = target;
5589 ngDevMode && assertDomNode(rElement);
5590 // if the context is not found then we need to traverse upwards up the DOM
5591 // to find the nearest element that has already been monkey patched with data
5592 let parent = rElement;
5593 while (parent = parent.parentNode) {
5594 const parentContext = readPatchedData(parent);
5595 if (parentContext) {
5596 const lView = Array.isArray(parentContext) ? parentContext : parentContext.lView;
5597 // the edge of the app was also reached here through another means
5598 // (maybe because the DOM was changed manually).
5599 if (!lView) {
5600 return null;
5601 }
5602 const index = findViaNativeElement(lView, rElement);
5603 if (index >= 0) {
5604 const native = unwrapRNode(lView[index]);
5605 const context = createLContext(lView, index, native);
5606 attachPatchData(native, context);
5607 mpValue = context;
5608 break;
5609 }
5610 }
5611 }
5612 }
5613 return mpValue || null;
5614}
5615/**
5616 * Creates an empty instance of a `LContext` context
5617 */
5618function createLContext(lView, nodeIndex, native) {
5619 return new LContext(lView[ID], nodeIndex, native);
5620}
5621/**
5622 * Takes a component instance and returns the view for that component.
5623 *
5624 * @param componentInstance
5625 * @returns The component's view
5626 */
5627function getComponentViewByInstance(componentInstance) {
5628 let patchedData = readPatchedData(componentInstance);
5629 let lView;
5630 if (isLView(patchedData)) {
5631 const contextLView = patchedData;
5632 const nodeIndex = findViaComponent(contextLView, componentInstance);
5633 lView = getComponentLViewByIndex(nodeIndex, contextLView);
5634 const context = createLContext(contextLView, nodeIndex, lView[HOST]);
5635 context.component = componentInstance;
5636 attachPatchData(componentInstance, context);
5637 attachPatchData(context.native, context);
5638 }
5639 else {
5640 const context = patchedData;
5641 const contextLView = context.lView;
5642 ngDevMode && assertLView(contextLView);
5643 lView = getComponentLViewByIndex(context.nodeIndex, contextLView);
5644 }
5645 return lView;
5646}
5647/**
5648 * This property will be monkey-patched on elements, components and directives.
5649 */
5650const MONKEY_PATCH_KEY_NAME = '__ngContext__';
5651/**
5652 * Assigns the given data to the given target (which could be a component,
5653 * directive or DOM node instance) using monkey-patching.
5654 */
5655function attachPatchData(target, data) {
5656 ngDevMode && assertDefined(target, 'Target expected');
5657 // Only attach the ID of the view in order to avoid memory leaks (see #41047). We only do this
5658 // for `LView`, because we have control over when an `LView` is created and destroyed, whereas
5659 // we can't know when to remove an `LContext`.
5660 if (isLView(data)) {
5661 target[MONKEY_PATCH_KEY_NAME] = data[ID];
5662 registerLView(data);
5663 }
5664 else {
5665 target[MONKEY_PATCH_KEY_NAME] = data;
5666 }
5667}
5668/**
5669 * Returns the monkey-patch value data present on the target (which could be
5670 * a component, directive or a DOM node).
5671 */
5672function readPatchedData(target) {
5673 ngDevMode && assertDefined(target, 'Target expected');
5674 const data = target[MONKEY_PATCH_KEY_NAME];
5675 return (typeof data === 'number') ? getLViewById(data) : data || null;
5676}
5677function readPatchedLView(target) {
5678 const value = readPatchedData(target);
5679 if (value) {
5680 return (isLView(value) ? value : value.lView);
5681 }
5682 return null;
5683}
5684function isComponentInstance(instance) {
5685 return instance && instance.constructor && instance.constructor.ɵcmp;
5686}
5687function isDirectiveInstance(instance) {
5688 return instance && instance.constructor && instance.constructor.ɵdir;
5689}
5690/**
5691 * Locates the element within the given LView and returns the matching index
5692 */
5693function findViaNativeElement(lView, target) {
5694 const tView = lView[TVIEW];
5695 for (let i = HEADER_OFFSET; i < tView.bindingStartIndex; i++) {
5696 if (unwrapRNode(lView[i]) === target) {
5697 return i;
5698 }
5699 }
5700 return -1;
5701}
5702/**
5703 * Locates the next tNode (child, sibling or parent).
5704 */
5705function traverseNextElement(tNode) {
5706 if (tNode.child) {
5707 return tNode.child;
5708 }
5709 else if (tNode.next) {
5710 return tNode.next;
5711 }
5712 else {
5713 // Let's take the following template: <div><span>text</span></div><component/>
5714 // After checking the text node, we need to find the next parent that has a "next" TNode,
5715 // in this case the parent `div`, so that we can find the component.
5716 while (tNode.parent && !tNode.parent.next) {
5717 tNode = tNode.parent;
5718 }
5719 return tNode.parent && tNode.parent.next;
5720 }
5721}
5722/**
5723 * Locates the component within the given LView and returns the matching index
5724 */
5725function findViaComponent(lView, componentInstance) {
5726 const componentIndices = lView[TVIEW].components;
5727 if (componentIndices) {
5728 for (let i = 0; i < componentIndices.length; i++) {
5729 const elementComponentIndex = componentIndices[i];
5730 const componentView = getComponentLViewByIndex(elementComponentIndex, lView);
5731 if (componentView[CONTEXT] === componentInstance) {
5732 return elementComponentIndex;
5733 }
5734 }
5735 }
5736 else {
5737 const rootComponentView = getComponentLViewByIndex(HEADER_OFFSET, lView);
5738 const rootComponent = rootComponentView[CONTEXT];
5739 if (rootComponent === componentInstance) {
5740 // we are dealing with the root element here therefore we know that the
5741 // element is the very first element after the HEADER data in the lView
5742 return HEADER_OFFSET;
5743 }
5744 }
5745 return -1;
5746}
5747/**
5748 * Locates the directive within the given LView and returns the matching index
5749 */
5750function findViaDirective(lView, directiveInstance) {
5751 // if a directive is monkey patched then it will (by default)
5752 // have a reference to the LView of the current view. The
5753 // element bound to the directive being search lives somewhere
5754 // in the view data. We loop through the nodes and check their
5755 // list of directives for the instance.
5756 let tNode = lView[TVIEW].firstChild;
5757 while (tNode) {
5758 const directiveIndexStart = tNode.directiveStart;
5759 const directiveIndexEnd = tNode.directiveEnd;
5760 for (let i = directiveIndexStart; i < directiveIndexEnd; i++) {
5761 if (lView[i] === directiveInstance) {
5762 return tNode.index;
5763 }
5764 }
5765 tNode = traverseNextElement(tNode);
5766 }
5767 return -1;
5768}
5769/**
5770 * Returns a list of directives applied to a node at a specific index. The list includes
5771 * directives matched by selector and any host directives, but it excludes components.
5772 * Use `getComponentAtNodeIndex` to find the component applied to a node.
5773 *
5774 * @param nodeIndex The node index
5775 * @param lView The target view data
5776 */
5777function getDirectivesAtNodeIndex(nodeIndex, lView) {
5778 const tNode = lView[TVIEW].data[nodeIndex];
5779 if (tNode.directiveStart === 0)
5780 return EMPTY_ARRAY;
5781 const results = [];
5782 for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
5783 const directiveInstance = lView[i];
5784 if (!isComponentInstance(directiveInstance)) {
5785 results.push(directiveInstance);
5786 }
5787 }
5788 return results;
5789}
5790function getComponentAtNodeIndex(nodeIndex, lView) {
5791 const tNode = lView[TVIEW].data[nodeIndex];
5792 const { directiveStart, componentOffset } = tNode;
5793 return componentOffset > -1 ? lView[directiveStart + componentOffset] : null;
5794}
5795/**
5796 * Returns a map of local references (local reference name => element or directive instance) that
5797 * exist on a given element.
5798 */
5799function discoverLocalRefs(lView, nodeIndex) {
5800 const tNode = lView[TVIEW].data[nodeIndex];
5801 if (tNode && tNode.localNames) {
5802 const result = {};
5803 let localIndex = tNode.index + 1;
5804 for (let i = 0; i < tNode.localNames.length; i += 2) {
5805 result[tNode.localNames[i]] = lView[localIndex];
5806 localIndex++;
5807 }
5808 return result;
5809 }
5810 return null;
5811}
5812
5813let _icuContainerIterate;
5814/**
5815 * Iterator which provides ability to visit all of the `TIcuContainerNode` root `RNode`s.
5816 */
5817function icuContainerIterate(tIcuContainerNode, lView) {
5818 return _icuContainerIterate(tIcuContainerNode, lView);
5819}
5820/**
5821 * Ensures that `IcuContainerVisitor`'s implementation is present.
5822 *
5823 * This function is invoked when i18n instruction comes across an ICU. The purpose is to allow the
5824 * bundler to tree shake ICU logic and only load it if ICU instruction is executed.
5825 */
5826function ensureIcuContainerVisitorLoaded(loader) {
5827 if (_icuContainerIterate === undefined) {
5828 // Do not inline this function. We want to keep `ensureIcuContainerVisitorLoaded` light, so it
5829 // can be inlined into call-site.
5830 _icuContainerIterate = loader();
5831 }
5832}
5833
5834/**
5835 * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of
5836 * that LContainer, which is an LView
5837 * @param lView the lView whose parent to get
5838 */
5839function getLViewParent(lView) {
5840 ngDevMode && assertLView(lView);
5841 const parent = lView[PARENT];
5842 return isLContainer(parent) ? parent[PARENT] : parent;
5843}
5844/**
5845 * Retrieve the root view from any component or `LView` by walking the parent `LView` until
5846 * reaching the root `LView`.
5847 *
5848 * @param componentOrLView any component or `LView`
5849 */
5850function getRootView(componentOrLView) {
5851 ngDevMode && assertDefined(componentOrLView, 'component');
5852 let lView = isLView(componentOrLView) ? componentOrLView : readPatchedLView(componentOrLView);
5853 while (lView && !(lView[FLAGS] & 256 /* LViewFlags.IsRoot */)) {
5854 lView = getLViewParent(lView);
5855 }
5856 ngDevMode && assertLView(lView);
5857 return lView;
5858}
5859/**
5860 * Returns the context information associated with the application where the target is situated. It
5861 * does this by walking the parent views until it gets to the root view, then getting the context
5862 * off of that.
5863 *
5864 * @param viewOrComponent the `LView` or component to get the root context for.
5865 */
5866function getRootContext(viewOrComponent) {
5867 const rootView = getRootView(viewOrComponent);
5868 ngDevMode &&
5869 assertDefined(rootView[CONTEXT], 'Root view has no context. Perhaps it is disconnected?');
5870 return rootView[CONTEXT];
5871}
5872/**
5873 * Gets the first `LContainer` in the LView or `null` if none exists.
5874 */
5875function getFirstLContainer(lView) {
5876 return getNearestLContainer(lView[CHILD_HEAD]);
5877}
5878/**
5879 * Gets the next `LContainer` that is a sibling of the given container.
5880 */
5881function getNextLContainer(container) {
5882 return getNearestLContainer(container[NEXT]);
5883}
5884function getNearestLContainer(viewOrContainer) {
5885 while (viewOrContainer !== null && !isLContainer(viewOrContainer)) {
5886 viewOrContainer = viewOrContainer[NEXT];
5887 }
5888 return viewOrContainer;
5889}
5890
5891/**
5892 * NOTE: for performance reasons, the possible actions are inlined within the function instead of
5893 * being passed as an argument.
5894 */
5895function applyToElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) {
5896 // If this slot was allocated for a text node dynamically created by i18n, the text node itself
5897 // won't be created until i18nApply() in the update block, so this node should be skipped.
5898 // For more info, see "ICU expressions should work inside an ngTemplateOutlet inside an ngFor"
5899 // in `i18n_spec.ts`.
5900 if (lNodeToHandle != null) {
5901 let lContainer;
5902 let isComponent = false;
5903 // We are expecting an RNode, but in the case of a component or LContainer the `RNode` is
5904 // wrapped in an array which needs to be unwrapped. We need to know if it is a component and if
5905 // it has LContainer so that we can process all of those cases appropriately.
5906 if (isLContainer(lNodeToHandle)) {
5907 lContainer = lNodeToHandle;
5908 }
5909 else if (isLView(lNodeToHandle)) {
5910 isComponent = true;
5911 ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView');
5912 lNodeToHandle = lNodeToHandle[HOST];
5913 }
5914 const rNode = unwrapRNode(lNodeToHandle);
5915 if (action === 0 /* WalkTNodeTreeAction.Create */ && parent !== null) {
5916 if (beforeNode == null) {
5917 nativeAppendChild(renderer, parent, rNode);
5918 }
5919 else {
5920 nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
5921 }
5922 }
5923 else if (action === 1 /* WalkTNodeTreeAction.Insert */ && parent !== null) {
5924 nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
5925 }
5926 else if (action === 2 /* WalkTNodeTreeAction.Detach */) {
5927 nativeRemoveNode(renderer, rNode, isComponent);
5928 }
5929 else if (action === 3 /* WalkTNodeTreeAction.Destroy */) {
5930 ngDevMode && ngDevMode.rendererDestroyNode++;
5931 renderer.destroyNode(rNode);
5932 }
5933 if (lContainer != null) {
5934 applyContainer(renderer, action, lContainer, parent, beforeNode);
5935 }
5936 }
5937}
5938function createTextNode(renderer, value) {
5939 ngDevMode && ngDevMode.rendererCreateTextNode++;
5940 ngDevMode && ngDevMode.rendererSetText++;
5941 return renderer.createText(value);
5942}
5943function updateTextNode(renderer, rNode, value) {
5944 ngDevMode && ngDevMode.rendererSetText++;
5945 renderer.setValue(rNode, value);
5946}
5947function createCommentNode(renderer, value) {
5948 ngDevMode && ngDevMode.rendererCreateComment++;
5949 return renderer.createComment(escapeCommentText(value));
5950}
5951/**
5952 * Creates a native element from a tag name, using a renderer.
5953 * @param renderer A renderer to use
5954 * @param name the tag name
5955 * @param namespace Optional namespace for element.
5956 * @returns the element created
5957 */
5958function createElementNode(renderer, name, namespace) {
5959 ngDevMode && ngDevMode.rendererCreateElement++;
5960 return renderer.createElement(name, namespace);
5961}
5962/**
5963 * Removes all DOM elements associated with a view.
5964 *
5965 * Because some root nodes of the view may be containers, we sometimes need
5966 * to propagate deeply into the nested containers to remove all elements in the
5967 * views beneath it.
5968 *
5969 * @param tView The `TView' of the `LView` from which elements should be added or removed
5970 * @param lView The view from which elements should be added or removed
5971 */
5972function removeViewFromContainer(tView, lView) {
5973 const renderer = lView[RENDERER];
5974 applyView(tView, lView, renderer, 2 /* WalkTNodeTreeAction.Detach */, null, null);
5975 lView[HOST] = null;
5976 lView[T_HOST] = null;
5977}
5978/**
5979 * Adds all DOM elements associated with a view.
5980 *
5981 * Because some root nodes of the view may be containers, we sometimes need
5982 * to propagate deeply into the nested containers to add all elements in the
5983 * views beneath it.
5984 *
5985 * @param tView The `TView' of the `LView` from which elements should be added or removed
5986 * @param parentTNode The `TNode` where the `LView` should be attached to.
5987 * @param renderer Current renderer to use for DOM manipulations.
5988 * @param lView The view from which elements should be added or removed
5989 * @param parentNativeNode The parent `RElement` where it should be inserted into.
5990 * @param beforeNode The node before which elements should be added, if insert mode
5991 */
5992function addViewToContainer(tView, parentTNode, renderer, lView, parentNativeNode, beforeNode) {
5993 lView[HOST] = parentNativeNode;
5994 lView[T_HOST] = parentTNode;
5995 applyView(tView, lView, renderer, 1 /* WalkTNodeTreeAction.Insert */, parentNativeNode, beforeNode);
5996}
5997/**
5998 * Detach a `LView` from the DOM by detaching its nodes.
5999 *
6000 * @param tView The `TView' of the `LView` to be detached
6001 * @param lView the `LView` to be detached.
6002 */
6003function renderDetachView(tView, lView) {
6004 applyView(tView, lView, lView[RENDERER], 2 /* WalkTNodeTreeAction.Detach */, null, null);
6005}
6006/**
6007 * Traverses down and up the tree of views and containers to remove listeners and
6008 * call onDestroy callbacks.
6009 *
6010 * Notes:
6011 * - Because it's used for onDestroy calls, it needs to be bottom-up.
6012 * - Must process containers instead of their views to avoid splicing
6013 * when views are destroyed and re-added.
6014 * - Using a while loop because it's faster than recursion
6015 * - Destroy only called on movement to sibling or movement to parent (laterally or up)
6016 *
6017 * @param rootView The view to destroy
6018 */
6019function destroyViewTree(rootView) {
6020 // If the view has no children, we can clean it up and return early.
6021 let lViewOrLContainer = rootView[CHILD_HEAD];
6022 if (!lViewOrLContainer) {
6023 return cleanUpView(rootView[TVIEW], rootView);
6024 }
6025 while (lViewOrLContainer) {
6026 let next = null;
6027 if (isLView(lViewOrLContainer)) {
6028 // If LView, traverse down to child.
6029 next = lViewOrLContainer[CHILD_HEAD];
6030 }
6031 else {
6032 ngDevMode && assertLContainer(lViewOrLContainer);
6033 // If container, traverse down to its first LView.
6034 const firstView = lViewOrLContainer[CONTAINER_HEADER_OFFSET];
6035 if (firstView)
6036 next = firstView;
6037 }
6038 if (!next) {
6039 // Only clean up view when moving to the side or up, as destroy hooks
6040 // should be called in order from the bottom up.
6041 while (lViewOrLContainer && !lViewOrLContainer[NEXT] && lViewOrLContainer !== rootView) {
6042 if (isLView(lViewOrLContainer)) {
6043 cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
6044 }
6045 lViewOrLContainer = lViewOrLContainer[PARENT];
6046 }
6047 if (lViewOrLContainer === null)
6048 lViewOrLContainer = rootView;
6049 if (isLView(lViewOrLContainer)) {
6050 cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
6051 }
6052 next = lViewOrLContainer && lViewOrLContainer[NEXT];
6053 }
6054 lViewOrLContainer = next;
6055 }
6056}
6057/**
6058 * Inserts a view into a container.
6059 *
6060 * This adds the view to the container's array of active views in the correct
6061 * position. It also adds the view's elements to the DOM if the container isn't a
6062 * root node of another view (in that case, the view's elements will be added when
6063 * the container's parent view is added later).
6064 *
6065 * @param tView The `TView' of the `LView` to insert
6066 * @param lView The view to insert
6067 * @param lContainer The container into which the view should be inserted
6068 * @param index Which index in the container to insert the child view into
6069 */
6070function insertView(tView, lView, lContainer, index) {
6071 ngDevMode && assertLView(lView);
6072 ngDevMode && assertLContainer(lContainer);
6073 const indexInContainer = CONTAINER_HEADER_OFFSET + index;
6074 const containerLength = lContainer.length;
6075 if (index > 0) {
6076 // This is a new view, we need to add it to the children.
6077 lContainer[indexInContainer - 1][NEXT] = lView;
6078 }
6079 if (index < containerLength - CONTAINER_HEADER_OFFSET) {
6080 lView[NEXT] = lContainer[indexInContainer];
6081 addToArray(lContainer, CONTAINER_HEADER_OFFSET + index, lView);
6082 }
6083 else {
6084 lContainer.push(lView);
6085 lView[NEXT] = null;
6086 }
6087 lView[PARENT] = lContainer;
6088 // track views where declaration and insertion points are different
6089 const declarationLContainer = lView[DECLARATION_LCONTAINER];
6090 if (declarationLContainer !== null && lContainer !== declarationLContainer) {
6091 trackMovedView(declarationLContainer, lView);
6092 }
6093 // notify query that a new view has been added
6094 const lQueries = lView[QUERIES];
6095 if (lQueries !== null) {
6096 lQueries.insertView(tView);
6097 }
6098 // Sets the attached flag
6099 lView[FLAGS] |= 64 /* LViewFlags.Attached */;
6100}
6101/**
6102 * Track views created from the declaration container (TemplateRef) and inserted into a
6103 * different LContainer.
6104 */
6105function trackMovedView(declarationContainer, lView) {
6106 ngDevMode && assertDefined(lView, 'LView required');
6107 ngDevMode && assertLContainer(declarationContainer);
6108 const movedViews = declarationContainer[MOVED_VIEWS];
6109 const insertedLContainer = lView[PARENT];
6110 ngDevMode && assertLContainer(insertedLContainer);
6111 const insertedComponentLView = insertedLContainer[PARENT][DECLARATION_COMPONENT_VIEW];
6112 ngDevMode && assertDefined(insertedComponentLView, 'Missing insertedComponentLView');
6113 const declaredComponentLView = lView[DECLARATION_COMPONENT_VIEW];
6114 ngDevMode && assertDefined(declaredComponentLView, 'Missing declaredComponentLView');
6115 if (declaredComponentLView !== insertedComponentLView) {
6116 // At this point the declaration-component is not same as insertion-component; this means that
6117 // this is a transplanted view. Mark the declared lView as having transplanted views so that
6118 // those views can participate in CD.
6119 declarationContainer[HAS_TRANSPLANTED_VIEWS] = true;
6120 }
6121 if (movedViews === null) {
6122 declarationContainer[MOVED_VIEWS] = [lView];
6123 }
6124 else {
6125 movedViews.push(lView);
6126 }
6127}
6128function detachMovedView(declarationContainer, lView) {
6129 ngDevMode && assertLContainer(declarationContainer);
6130 ngDevMode &&
6131 assertDefined(declarationContainer[MOVED_VIEWS], 'A projected view should belong to a non-empty projected views collection');
6132 const movedViews = declarationContainer[MOVED_VIEWS];
6133 const declarationViewIndex = movedViews.indexOf(lView);
6134 const insertionLContainer = lView[PARENT];
6135 ngDevMode && assertLContainer(insertionLContainer);
6136 // If the view was marked for refresh but then detached before it was checked (where the flag
6137 // would be cleared and the counter decremented), we need to decrement the view counter here
6138 // instead.
6139 if (lView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) {
6140 lView[FLAGS] &= ~512 /* LViewFlags.RefreshTransplantedView */;
6141 updateTransplantedViewCount(insertionLContainer, -1);
6142 }
6143 movedViews.splice(declarationViewIndex, 1);
6144}
6145/**
6146 * Detaches a view from a container.
6147 *
6148 * This method removes the view from the container's array of active views. It also
6149 * removes the view's elements from the DOM.
6150 *
6151 * @param lContainer The container from which to detach a view
6152 * @param removeIndex The index of the view to detach
6153 * @returns Detached LView instance.
6154 */
6155function detachView(lContainer, removeIndex) {
6156 if (lContainer.length <= CONTAINER_HEADER_OFFSET)
6157 return;
6158 const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex;
6159 const viewToDetach = lContainer[indexInContainer];
6160 if (viewToDetach) {
6161 const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER];
6162 if (declarationLContainer !== null && declarationLContainer !== lContainer) {
6163 detachMovedView(declarationLContainer, viewToDetach);
6164 }
6165 if (removeIndex > 0) {
6166 lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT];
6167 }
6168 const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex);
6169 removeViewFromContainer(viewToDetach[TVIEW], viewToDetach);
6170 // notify query that a view has been removed
6171 const lQueries = removedLView[QUERIES];
6172 if (lQueries !== null) {
6173 lQueries.detachView(removedLView[TVIEW]);
6174 }
6175 viewToDetach[PARENT] = null;
6176 viewToDetach[NEXT] = null;
6177 // Unsets the attached flag
6178 viewToDetach[FLAGS] &= ~64 /* LViewFlags.Attached */;
6179 }
6180 return viewToDetach;
6181}
6182/**
6183 * A standalone function which destroys an LView,
6184 * conducting clean up (e.g. removing listeners, calling onDestroys).
6185 *
6186 * @param tView The `TView' of the `LView` to be destroyed
6187 * @param lView The view to be destroyed.
6188 */
6189function destroyLView(tView, lView) {
6190 if (!(lView[FLAGS] & 128 /* LViewFlags.Destroyed */)) {
6191 const renderer = lView[RENDERER];
6192 if (renderer.destroyNode) {
6193 applyView(tView, lView, renderer, 3 /* WalkTNodeTreeAction.Destroy */, null, null);
6194 }
6195 destroyViewTree(lView);
6196 }
6197}
6198/**
6199 * Calls onDestroys hooks for all directives and pipes in a given view and then removes all
6200 * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks
6201 * can be propagated to @Output listeners.
6202 *
6203 * @param tView `TView` for the `LView` to clean up.
6204 * @param lView The LView to clean up
6205 */
6206function cleanUpView(tView, lView) {
6207 if (!(lView[FLAGS] & 128 /* LViewFlags.Destroyed */)) {
6208 // Usually the Attached flag is removed when the view is detached from its parent, however
6209 // if it's a root view, the flag won't be unset hence why we're also removing on destroy.
6210 lView[FLAGS] &= ~64 /* LViewFlags.Attached */;
6211 // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook
6212 // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If
6213 // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop.
6214 // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is
6215 // really more of an "afterDestroy" hook if you think about it.
6216 lView[FLAGS] |= 128 /* LViewFlags.Destroyed */;
6217 executeOnDestroys(tView, lView);
6218 processCleanups(tView, lView);
6219 // For component views only, the local renderer is destroyed at clean up time.
6220 if (lView[TVIEW].type === 1 /* TViewType.Component */) {
6221 ngDevMode && ngDevMode.rendererDestroy++;
6222 lView[RENDERER].destroy();
6223 }
6224 const declarationContainer = lView[DECLARATION_LCONTAINER];
6225 // we are dealing with an embedded view that is still inserted into a container
6226 if (declarationContainer !== null && isLContainer(lView[PARENT])) {
6227 // and this is a projected view
6228 if (declarationContainer !== lView[PARENT]) {
6229 detachMovedView(declarationContainer, lView);
6230 }
6231 // For embedded views still attached to a container: remove query result from this view.
6232 const lQueries = lView[QUERIES];
6233 if (lQueries !== null) {
6234 lQueries.detachView(tView);
6235 }
6236 }
6237 // Unregister the view once everything else has been cleaned up.
6238 unregisterLView(lView);
6239 }
6240}
6241/** Removes listeners and unsubscribes from output subscriptions */
6242function processCleanups(tView, lView) {
6243 const tCleanup = tView.cleanup;
6244 const lCleanup = lView[CLEANUP];
6245 // `LCleanup` contains both share information with `TCleanup` as well as instance specific
6246 // information appended at the end. We need to know where the end of the `TCleanup` information
6247 // is, and we track this with `lastLCleanupIndex`.
6248 let lastLCleanupIndex = -1;
6249 if (tCleanup !== null) {
6250 for (let i = 0; i < tCleanup.length - 1; i += 2) {
6251 if (typeof tCleanup[i] === 'string') {
6252 // This is a native DOM listener. It will occupy 4 entries in the TCleanup array (hence i +=
6253 // 2 at the end of this block).
6254 const targetIdx = tCleanup[i + 3];
6255 ngDevMode && assertNumber(targetIdx, 'cleanup target must be a number');
6256 if (targetIdx >= 0) {
6257 // unregister
6258 lCleanup[lastLCleanupIndex = targetIdx]();
6259 }
6260 else {
6261 // Subscription
6262 lCleanup[lastLCleanupIndex = -targetIdx].unsubscribe();
6263 }
6264 i += 2;
6265 }
6266 else {
6267 // This is a cleanup function that is grouped with the index of its context
6268 const context = lCleanup[lastLCleanupIndex = tCleanup[i + 1]];
6269 tCleanup[i].call(context);
6270 }
6271 }
6272 }
6273 if (lCleanup !== null) {
6274 for (let i = lastLCleanupIndex + 1; i < lCleanup.length; i++) {
6275 const instanceCleanupFn = lCleanup[i];
6276 ngDevMode && assertFunction(instanceCleanupFn, 'Expecting instance cleanup function.');
6277 instanceCleanupFn();
6278 }
6279 lView[CLEANUP] = null;
6280 }
6281}
6282/** Calls onDestroy hooks for this view */
6283function executeOnDestroys(tView, lView) {
6284 let destroyHooks;
6285 if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
6286 for (let i = 0; i < destroyHooks.length; i += 2) {
6287 const context = lView[destroyHooks[i]];
6288 // Only call the destroy hook if the context has been requested.
6289 if (!(context instanceof NodeInjectorFactory)) {
6290 const toCall = destroyHooks[i + 1];
6291 if (Array.isArray(toCall)) {
6292 for (let j = 0; j < toCall.length; j += 2) {
6293 const callContext = context[toCall[j]];
6294 const hook = toCall[j + 1];
6295 profiler(4 /* ProfilerEvent.LifecycleHookStart */, callContext, hook);
6296 try {
6297 hook.call(callContext);
6298 }
6299 finally {
6300 profiler(5 /* ProfilerEvent.LifecycleHookEnd */, callContext, hook);
6301 }
6302 }
6303 }
6304 else {
6305 profiler(4 /* ProfilerEvent.LifecycleHookStart */, context, toCall);
6306 try {
6307 toCall.call(context);
6308 }
6309 finally {
6310 profiler(5 /* ProfilerEvent.LifecycleHookEnd */, context, toCall);
6311 }
6312 }
6313 }
6314 }
6315 }
6316}
6317/**
6318 * Returns a native element if a node can be inserted into the given parent.
6319 *
6320 * There are two reasons why we may not be able to insert a element immediately.
6321 * - Projection: When creating a child content element of a component, we have to skip the
6322 * insertion because the content of a component will be projected.
6323 * `<component><content>delayed due to projection</content></component>`
6324 * - Parent container is disconnected: This can happen when we are inserting a view into
6325 * parent container, which itself is disconnected. For example the parent container is part
6326 * of a View which has not be inserted or is made for projection but has not been inserted
6327 * into destination.
6328 *
6329 * @param tView: Current `TView`.
6330 * @param tNode: `TNode` for which we wish to retrieve render parent.
6331 * @param lView: Current `LView`.
6332 */
6333function getParentRElement(tView, tNode, lView) {
6334 return getClosestRElement(tView, tNode.parent, lView);
6335}
6336/**
6337 * Get closest `RElement` or `null` if it can't be found.
6338 *
6339 * If `TNode` is `TNodeType.Element` => return `RElement` at `LView[tNode.index]` location.
6340 * If `TNode` is `TNodeType.ElementContainer|IcuContain` => return the parent (recursively).
6341 * If `TNode` is `null` then return host `RElement`:
6342 * - return `null` if projection
6343 * - return `null` if parent container is disconnected (we have no parent.)
6344 *
6345 * @param tView: Current `TView`.
6346 * @param tNode: `TNode` for which we wish to retrieve `RElement` (or `null` if host element is
6347 * needed).
6348 * @param lView: Current `LView`.
6349 * @returns `null` if the `RElement` can't be determined at this time (no parent / projection)
6350 */
6351function getClosestRElement(tView, tNode, lView) {
6352 let parentTNode = tNode;
6353 // Skip over element and ICU containers as those are represented by a comment node and
6354 // can't be used as a render parent.
6355 while (parentTNode !== null &&
6356 (parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */))) {
6357 tNode = parentTNode;
6358 parentTNode = tNode.parent;
6359 }
6360 // If the parent tNode is null, then we are inserting across views: either into an embedded view
6361 // or a component view.
6362 if (parentTNode === null) {
6363 // We are inserting a root element of the component view into the component host element and
6364 // it should always be eager.
6365 return lView[HOST];
6366 }
6367 else {
6368 ngDevMode && assertTNodeType(parentTNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */);
6369 const { componentOffset } = parentTNode;
6370 if (componentOffset > -1) {
6371 ngDevMode && assertTNodeForLView(parentTNode, lView);
6372 const { encapsulation } = tView.data[parentTNode.directiveStart + componentOffset];
6373 // We've got a parent which is an element in the current view. We just need to verify if the
6374 // parent element is not a component. Component's content nodes are not inserted immediately
6375 // because they will be projected, and so doing insert at this point would be wasteful.
6376 // Since the projection would then move it to its final destination. Note that we can't
6377 // make this assumption when using the Shadow DOM, because the native projection placeholders
6378 // (<content> or <slot>) have to be in place as elements are being inserted.
6379 if (encapsulation === ViewEncapsulation.None ||
6380 encapsulation === ViewEncapsulation.Emulated) {
6381 return null;
6382 }
6383 }
6384 return getNativeByTNode(parentTNode, lView);
6385 }
6386}
6387/**
6388 * Inserts a native node before another native node for a given parent.
6389 * This is a utility function that can be used when native nodes were determined.
6390 */
6391function nativeInsertBefore(renderer, parent, child, beforeNode, isMove) {
6392 ngDevMode && ngDevMode.rendererInsertBefore++;
6393 renderer.insertBefore(parent, child, beforeNode, isMove);
6394}
6395function nativeAppendChild(renderer, parent, child) {
6396 ngDevMode && ngDevMode.rendererAppendChild++;
6397 ngDevMode && assertDefined(parent, 'parent node must be defined');
6398 renderer.appendChild(parent, child);
6399}
6400function nativeAppendOrInsertBefore(renderer, parent, child, beforeNode, isMove) {
6401 if (beforeNode !== null) {
6402 nativeInsertBefore(renderer, parent, child, beforeNode, isMove);
6403 }
6404 else {
6405 nativeAppendChild(renderer, parent, child);
6406 }
6407}
6408/** Removes a node from the DOM given its native parent. */
6409function nativeRemoveChild(renderer, parent, child, isHostElement) {
6410 renderer.removeChild(parent, child, isHostElement);
6411}
6412/** Checks if an element is a `<template>` node. */
6413function isTemplateNode(node) {
6414 return node.tagName === 'TEMPLATE' && node.content !== undefined;
6415}
6416/**
6417 * Returns a native parent of a given native node.
6418 */
6419function nativeParentNode(renderer, node) {
6420 return renderer.parentNode(node);
6421}
6422/**
6423 * Returns a native sibling of a given native node.
6424 */
6425function nativeNextSibling(renderer, node) {
6426 return renderer.nextSibling(node);
6427}
6428/**
6429 * Find a node in front of which `currentTNode` should be inserted.
6430 *
6431 * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
6432 * takes `TNode.insertBeforeIndex` into account if i18n code has been invoked.
6433 *
6434 * @param parentTNode parent `TNode`
6435 * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
6436 * @param lView current `LView`
6437 */
6438function getInsertInFrontOfRNode(parentTNode, currentTNode, lView) {
6439 return _getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView);
6440}
6441/**
6442 * Find a node in front of which `currentTNode` should be inserted. (Does not take i18n into
6443 * account)
6444 *
6445 * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
6446 * does not take `TNode.insertBeforeIndex` into account.
6447 *
6448 * @param parentTNode parent `TNode`
6449 * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
6450 * @param lView current `LView`
6451 */
6452function getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView) {
6453 if (parentTNode.type & (8 /* TNodeType.ElementContainer */ | 32 /* TNodeType.Icu */)) {
6454 return getNativeByTNode(parentTNode, lView);
6455 }
6456 return null;
6457}
6458/**
6459 * Tree shakable boundary for `getInsertInFrontOfRNodeWithI18n` function.
6460 *
6461 * This function will only be set if i18n code runs.
6462 */
6463let _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithNoI18n;
6464/**
6465 * Tree shakable boundary for `processI18nInsertBefore` function.
6466 *
6467 * This function will only be set if i18n code runs.
6468 */
6469let _processI18nInsertBefore;
6470function setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore) {
6471 _getInsertInFrontOfRNodeWithI18n = getInsertInFrontOfRNodeWithI18n;
6472 _processI18nInsertBefore = processI18nInsertBefore;
6473}
6474/**
6475 * Appends the `child` native node (or a collection of nodes) to the `parent`.
6476 *
6477 * @param tView The `TView' to be appended
6478 * @param lView The current LView
6479 * @param childRNode The native child (or children) that should be appended
6480 * @param childTNode The TNode of the child element
6481 */
6482function appendChild(tView, lView, childRNode, childTNode) {
6483 const parentRNode = getParentRElement(tView, childTNode, lView);
6484 const renderer = lView[RENDERER];
6485 const parentTNode = childTNode.parent || lView[T_HOST];
6486 const anchorNode = getInsertInFrontOfRNode(parentTNode, childTNode, lView);
6487 if (parentRNode != null) {
6488 if (Array.isArray(childRNode)) {
6489 for (let i = 0; i < childRNode.length; i++) {
6490 nativeAppendOrInsertBefore(renderer, parentRNode, childRNode[i], anchorNode, false);
6491 }
6492 }
6493 else {
6494 nativeAppendOrInsertBefore(renderer, parentRNode, childRNode, anchorNode, false);
6495 }
6496 }
6497 _processI18nInsertBefore !== undefined &&
6498 _processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRNode);
6499}
6500/**
6501 * Returns the first native node for a given LView, starting from the provided TNode.
6502 *
6503 * Native nodes are returned in the order in which those appear in the native tree (DOM).
6504 */
6505function getFirstNativeNode(lView, tNode) {
6506 if (tNode !== null) {
6507 ngDevMode &&
6508 assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 32 /* TNodeType.Icu */ | 16 /* TNodeType.Projection */);
6509 const tNodeType = tNode.type;
6510 if (tNodeType & 3 /* TNodeType.AnyRNode */) {
6511 return getNativeByTNode(tNode, lView);
6512 }
6513 else if (tNodeType & 4 /* TNodeType.Container */) {
6514 return getBeforeNodeForView(-1, lView[tNode.index]);
6515 }
6516 else if (tNodeType & 8 /* TNodeType.ElementContainer */) {
6517 const elIcuContainerChild = tNode.child;
6518 if (elIcuContainerChild !== null) {
6519 return getFirstNativeNode(lView, elIcuContainerChild);
6520 }
6521 else {
6522 const rNodeOrLContainer = lView[tNode.index];
6523 if (isLContainer(rNodeOrLContainer)) {
6524 return getBeforeNodeForView(-1, rNodeOrLContainer);
6525 }
6526 else {
6527 return unwrapRNode(rNodeOrLContainer);
6528 }
6529 }
6530 }
6531 else if (tNodeType & 32 /* TNodeType.Icu */) {
6532 let nextRNode = icuContainerIterate(tNode, lView);
6533 let rNode = nextRNode();
6534 // If the ICU container has no nodes, than we use the ICU anchor as the node.
6535 return rNode || unwrapRNode(lView[tNode.index]);
6536 }
6537 else {
6538 const projectionNodes = getProjectionNodes(lView, tNode);
6539 if (projectionNodes !== null) {
6540 if (Array.isArray(projectionNodes)) {
6541 return projectionNodes[0];
6542 }
6543 const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
6544 ngDevMode && assertParentView(parentView);
6545 return getFirstNativeNode(parentView, projectionNodes);
6546 }
6547 else {
6548 return getFirstNativeNode(lView, tNode.next);
6549 }
6550 }
6551 }
6552 return null;
6553}
6554function getProjectionNodes(lView, tNode) {
6555 if (tNode !== null) {
6556 const componentView = lView[DECLARATION_COMPONENT_VIEW];
6557 const componentHost = componentView[T_HOST];
6558 const slotIdx = tNode.projection;
6559 ngDevMode && assertProjectionSlots(lView);
6560 return componentHost.projection[slotIdx];
6561 }
6562 return null;
6563}
6564function getBeforeNodeForView(viewIndexInContainer, lContainer) {
6565 const nextViewIndex = CONTAINER_HEADER_OFFSET + viewIndexInContainer + 1;
6566 if (nextViewIndex < lContainer.length) {
6567 const lView = lContainer[nextViewIndex];
6568 const firstTNodeOfView = lView[TVIEW].firstChild;
6569 if (firstTNodeOfView !== null) {
6570 return getFirstNativeNode(lView, firstTNodeOfView);
6571 }
6572 }
6573 return lContainer[NATIVE];
6574}
6575/**
6576 * Removes a native node itself using a given renderer. To remove the node we are looking up its
6577 * parent from the native tree as not all platforms / browsers support the equivalent of
6578 * node.remove().
6579 *
6580 * @param renderer A renderer to be used
6581 * @param rNode The native node that should be removed
6582 * @param isHostElement A flag indicating if a node to be removed is a host of a component.
6583 */
6584function nativeRemoveNode(renderer, rNode, isHostElement) {
6585 ngDevMode && ngDevMode.rendererRemoveNode++;
6586 const nativeParent = nativeParentNode(renderer, rNode);
6587 if (nativeParent) {
6588 nativeRemoveChild(renderer, nativeParent, rNode, isHostElement);
6589 }
6590}
6591/**
6592 * Performs the operation of `action` on the node. Typically this involves inserting or removing
6593 * nodes on the LView or projection boundary.
6594 */
6595function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode, isProjection) {
6596 while (tNode != null) {
6597 ngDevMode && assertTNodeForLView(tNode, lView);
6598 ngDevMode &&
6599 assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
6600 const rawSlotValue = lView[tNode.index];
6601 const tNodeType = tNode.type;
6602 if (isProjection) {
6603 if (action === 0 /* WalkTNodeTreeAction.Create */) {
6604 rawSlotValue && attachPatchData(unwrapRNode(rawSlotValue), lView);
6605 tNode.flags |= 2 /* TNodeFlags.isProjected */;
6606 }
6607 }
6608 if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
6609 if (tNodeType & 8 /* TNodeType.ElementContainer */) {
6610 applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false);
6611 applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
6612 }
6613 else if (tNodeType & 32 /* TNodeType.Icu */) {
6614 const nextRNode = icuContainerIterate(tNode, lView);
6615 let rNode;
6616 while (rNode = nextRNode()) {
6617 applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
6618 }
6619 applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
6620 }
6621 else if (tNodeType & 16 /* TNodeType.Projection */) {
6622 applyProjectionRecursive(renderer, action, lView, tNode, parentRElement, beforeNode);
6623 }
6624 else {
6625 ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */);
6626 applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
6627 }
6628 }
6629 tNode = isProjection ? tNode.projectionNext : tNode.next;
6630 }
6631}
6632function applyView(tView, lView, renderer, action, parentRElement, beforeNode) {
6633 applyNodes(renderer, action, tView.firstChild, lView, parentRElement, beforeNode, false);
6634}
6635/**
6636 * `applyProjection` performs operation on the projection.
6637 *
6638 * Inserting a projection requires us to locate the projected nodes from the parent component. The
6639 * complication is that those nodes themselves could be re-projected from their parent component.
6640 *
6641 * @param tView The `TView` of `LView` which needs to be inserted, detached, destroyed
6642 * @param lView The `LView` which needs to be inserted, detached, destroyed.
6643 * @param tProjectionNode node to project
6644 */
6645function applyProjection(tView, lView, tProjectionNode) {
6646 const renderer = lView[RENDERER];
6647 const parentRNode = getParentRElement(tView, tProjectionNode, lView);
6648 const parentTNode = tProjectionNode.parent || lView[T_HOST];
6649 let beforeNode = getInsertInFrontOfRNode(parentTNode, tProjectionNode, lView);
6650 applyProjectionRecursive(renderer, 0 /* WalkTNodeTreeAction.Create */, lView, tProjectionNode, parentRNode, beforeNode);
6651}
6652/**
6653 * `applyProjectionRecursive` performs operation on the projection specified by `action` (insert,
6654 * detach, destroy)
6655 *
6656 * Inserting a projection requires us to locate the projected nodes from the parent component. The
6657 * complication is that those nodes themselves could be re-projected from their parent component.
6658 *
6659 * @param renderer Render to use
6660 * @param action action to perform (insert, detach, destroy)
6661 * @param lView The LView which needs to be inserted, detached, destroyed.
6662 * @param tProjectionNode node to project
6663 * @param parentRElement parent DOM element for insertion/removal.
6664 * @param beforeNode Before which node the insertions should happen.
6665 */
6666function applyProjectionRecursive(renderer, action, lView, tProjectionNode, parentRElement, beforeNode) {
6667 const componentLView = lView[DECLARATION_COMPONENT_VIEW];
6668 const componentNode = componentLView[T_HOST];
6669 ngDevMode &&
6670 assertEqual(typeof tProjectionNode.projection, 'number', 'expecting projection index');
6671 const nodeToProjectOrRNodes = componentNode.projection[tProjectionNode.projection];
6672 if (Array.isArray(nodeToProjectOrRNodes)) {
6673 // This should not exist, it is a bit of a hack. When we bootstrap a top level node and we
6674 // need to support passing projectable nodes, so we cheat and put them in the TNode
6675 // of the Host TView. (Yes we put instance info at the T Level). We can get away with it
6676 // because we know that that TView is not shared and therefore it will not be a problem.
6677 // This should be refactored and cleaned up.
6678 for (let i = 0; i < nodeToProjectOrRNodes.length; i++) {
6679 const rNode = nodeToProjectOrRNodes[i];
6680 applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
6681 }
6682 }
6683 else {
6684 let nodeToProject = nodeToProjectOrRNodes;
6685 const projectedComponentLView = componentLView[PARENT];
6686 applyNodes(renderer, action, nodeToProject, projectedComponentLView, parentRElement, beforeNode, true);
6687 }
6688}
6689/**
6690 * `applyContainer` performs an operation on the container and its views as specified by
6691 * `action` (insert, detach, destroy)
6692 *
6693 * Inserting a Container is complicated by the fact that the container may have Views which
6694 * themselves have containers or projections.
6695 *
6696 * @param renderer Renderer to use
6697 * @param action action to perform (insert, detach, destroy)
6698 * @param lContainer The LContainer which needs to be inserted, detached, destroyed.
6699 * @param parentRElement parent DOM element for insertion/removal.
6700 * @param beforeNode Before which node the insertions should happen.
6701 */
6702function applyContainer(renderer, action, lContainer, parentRElement, beforeNode) {
6703 ngDevMode && assertLContainer(lContainer);
6704 const anchor = lContainer[NATIVE]; // LContainer has its own before node.
6705 const native = unwrapRNode(lContainer);
6706 // An LContainer can be created dynamically on any node by injecting ViewContainerRef.
6707 // Asking for a ViewContainerRef on an element will result in a creation of a separate anchor
6708 // node (comment in the DOM) that will be different from the LContainer's host node. In this
6709 // particular case we need to execute action on 2 nodes:
6710 // - container's host node (this is done in the executeActionOnElementOrContainer)
6711 // - container's host node (this is done here)
6712 if (anchor !== native) {
6713 // This is very strange to me (Misko). I would expect that the native is same as anchor. I
6714 // don't see a reason why they should be different, but they are.
6715 //
6716 // If they are we need to process the second anchor as well.
6717 applyToElementOrContainer(action, renderer, parentRElement, anchor, beforeNode);
6718 }
6719 for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
6720 const lView = lContainer[i];
6721 applyView(lView[TVIEW], lView, renderer, action, parentRElement, anchor);
6722 }
6723}
6724/**
6725 * Writes class/style to element.
6726 *
6727 * @param renderer Renderer to use.
6728 * @param isClassBased `true` if it should be written to `class` (`false` to write to `style`)
6729 * @param rNode The Node to write to.
6730 * @param prop Property to write to. This would be the class/style name.
6731 * @param value Value to write. If `null`/`undefined`/`false` this is considered a remove (set/add
6732 * otherwise).
6733 */
6734function applyStyling(renderer, isClassBased, rNode, prop, value) {
6735 if (isClassBased) {
6736 // We actually want JS true/false here because any truthy value should add the class
6737 if (!value) {
6738 ngDevMode && ngDevMode.rendererRemoveClass++;
6739 renderer.removeClass(rNode, prop);
6740 }
6741 else {
6742 ngDevMode && ngDevMode.rendererAddClass++;
6743 renderer.addClass(rNode, prop);
6744 }
6745 }
6746 else {
6747 let flags = prop.indexOf('-') === -1 ? undefined : RendererStyleFlags2.DashCase;
6748 if (value == null /** || value === undefined */) {
6749 ngDevMode && ngDevMode.rendererRemoveStyle++;
6750 renderer.removeStyle(rNode, prop, flags);
6751 }
6752 else {
6753 // A value is important if it ends with `!important`. The style
6754 // parser strips any semicolons at the end of the value.
6755 const isImportant = typeof value === 'string' ? value.endsWith('!important') : false;
6756 if (isImportant) {
6757 // !important has to be stripped from the value for it to be valid.
6758 value = value.slice(0, -10);
6759 flags |= RendererStyleFlags2.Important;
6760 }
6761 ngDevMode && ngDevMode.rendererSetStyle++;
6762 renderer.setStyle(rNode, prop, value, flags);
6763 }
6764 }
6765}
6766/**
6767 * Write `cssText` to `RElement`.
6768 *
6769 * This function does direct write without any reconciliation. Used for writing initial values, so
6770 * that static styling values do not pull in the style parser.
6771 *
6772 * @param renderer Renderer to use
6773 * @param element The element which needs to be updated.
6774 * @param newValue The new class list to write.
6775 */
6776function writeDirectStyle(renderer, element, newValue) {
6777 ngDevMode && assertString(newValue, '\'newValue\' should be a string');
6778 renderer.setAttribute(element, 'style', newValue);
6779 ngDevMode && ngDevMode.rendererSetStyle++;
6780}
6781/**
6782 * Write `className` to `RElement`.
6783 *
6784 * This function does direct write without any reconciliation. Used for writing initial values, so
6785 * that static styling values do not pull in the style parser.
6786 *
6787 * @param renderer Renderer to use
6788 * @param element The element which needs to be updated.
6789 * @param newValue The new class list to write.
6790 */
6791function writeDirectClass(renderer, element, newValue) {
6792 ngDevMode && assertString(newValue, '\'newValue\' should be a string');
6793 if (newValue === '') {
6794 // There are tests in `google3` which expect `element.getAttribute('class')` to be `null`.
6795 renderer.removeAttribute(element, 'class');
6796 }
6797 else {
6798 renderer.setAttribute(element, 'class', newValue);
6799 }
6800 ngDevMode && ngDevMode.rendererSetClassName++;
6801}
6802/** Sets up the static DOM attributes on an `RNode`. */
6803function setupStaticAttributes(renderer, element, tNode) {
6804 const { mergedAttrs, classes, styles } = tNode;
6805 if (mergedAttrs !== null) {
6806 setUpAttributes(renderer, element, mergedAttrs);
6807 }
6808 if (classes !== null) {
6809 writeDirectClass(renderer, element, classes);
6810 }
6811 if (styles !== null) {
6812 writeDirectStyle(renderer, element, styles);
6813 }
6814}
6815
6816/**
6817 * @fileoverview
6818 * A module to facilitate use of a Trusted Types policy internally within
6819 * Angular. It lazily constructs the Trusted Types policy, providing helper
6820 * utilities for promoting strings to Trusted Types. When Trusted Types are not
6821 * available, strings are used as a fallback.
6822 * @security All use of this module is security-sensitive and should go through
6823 * security review.
6824 */
6825/**
6826 * The Trusted Types policy, or null if Trusted Types are not
6827 * enabled/supported, or undefined if the policy has not been created yet.
6828 */
6829let policy$1;
6830/**
6831 * Returns the Trusted Types policy, or null if Trusted Types are not
6832 * enabled/supported. The first call to this function will create the policy.
6833 */
6834function getPolicy$1() {
6835 if (policy$1 === undefined) {
6836 policy$1 = null;
6837 if (_global$1.trustedTypes) {
6838 try {
6839 policy$1 = _global$1.trustedTypes.createPolicy('angular', {
6840 createHTML: (s) => s,
6841 createScript: (s) => s,
6842 createScriptURL: (s) => s,
6843 });
6844 }
6845 catch {
6846 // trustedTypes.createPolicy throws if called with a name that is
6847 // already registered, even in report-only mode. Until the API changes,
6848 // catch the error not to break the applications functionally. In such
6849 // cases, the code will fall back to using strings.
6850 }
6851 }
6852 }
6853 return policy$1;
6854}
6855/**
6856 * Unsafely promote a string to a TrustedHTML, falling back to strings when
6857 * Trusted Types are not available.
6858 * @security This is a security-sensitive function; any use of this function
6859 * must go through security review. In particular, it must be assured that the
6860 * provided string will never cause an XSS vulnerability if used in a context
6861 * that will be interpreted as HTML by a browser, e.g. when assigning to
6862 * element.innerHTML.
6863 */
6864function trustedHTMLFromString(html) {
6865 return getPolicy$1()?.createHTML(html) || html;
6866}
6867/**
6868 * Unsafely promote a string to a TrustedScript, falling back to strings when
6869 * Trusted Types are not available.
6870 * @security In particular, it must be assured that the provided string will
6871 * never cause an XSS vulnerability if used in a context that will be
6872 * interpreted and executed as a script by a browser, e.g. when calling eval.
6873 */
6874function trustedScriptFromString(script) {
6875 return getPolicy$1()?.createScript(script) || script;
6876}
6877/**
6878 * Unsafely promote a string to a TrustedScriptURL, falling back to strings
6879 * when Trusted Types are not available.
6880 * @security This is a security-sensitive function; any use of this function
6881 * must go through security review. In particular, it must be assured that the
6882 * provided string will never cause an XSS vulnerability if used in a context
6883 * that will cause a browser to load and execute a resource, e.g. when
6884 * assigning to script.src.
6885 */
6886function trustedScriptURLFromString(url) {
6887 return getPolicy$1()?.createScriptURL(url) || url;
6888}
6889/**
6890 * Unsafely call the Function constructor with the given string arguments. It
6891 * is only available in development mode, and should be stripped out of
6892 * production code.
6893 * @security This is a security-sensitive function; any use of this function
6894 * must go through security review. In particular, it must be assured that it
6895 * is only called from development code, as use in production code can lead to
6896 * XSS vulnerabilities.
6897 */
6898function newTrustedFunctionForDev(...args) {
6899 if (typeof ngDevMode === 'undefined') {
6900 throw new Error('newTrustedFunctionForDev should never be called in production');
6901 }
6902 if (!_global$1.trustedTypes) {
6903 // In environments that don't support Trusted Types, fall back to the most
6904 // straightforward implementation:
6905 return new Function(...args);
6906 }
6907 // Chrome currently does not support passing TrustedScript to the Function
6908 // constructor. The following implements the workaround proposed on the page
6909 // below, where the Chromium bug is also referenced:
6910 // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
6911 const fnArgs = args.slice(0, -1).join(',');
6912 const fnBody = args[args.length - 1];
6913 const body = `(function anonymous(${fnArgs}
6914) { ${fnBody}
6915})`;
6916 // Using eval directly confuses the compiler and prevents this module from
6917 // being stripped out of JS binaries even if not used. The global['eval']
6918 // indirection fixes that.
6919 const fn = _global$1['eval'](trustedScriptFromString(body));
6920 if (fn.bind === undefined) {
6921 // Workaround for a browser bug that only exists in Chrome 83, where passing
6922 // a TrustedScript to eval just returns the TrustedScript back without
6923 // evaluating it. In that case, fall back to the most straightforward
6924 // implementation:
6925 return new Function(...args);
6926 }
6927 // To completely mimic the behavior of calling "new Function", two more
6928 // things need to happen:
6929 // 1. Stringifying the resulting function should return its source code
6930 fn.toString = () => body;
6931 // 2. When calling the resulting function, `this` should refer to `global`
6932 return fn.bind(_global$1);
6933 // When Trusted Types support in Function constructors is widely available,
6934 // the implementation of this function can be simplified to:
6935 // return new Function(...args.map(a => trustedScriptFromString(a)));
6936}
6937
6938/**
6939 * Validation function invoked at runtime for each binding that might potentially
6940 * represent a security-sensitive attribute of an <iframe>.
6941 * See `IFRAME_SECURITY_SENSITIVE_ATTRS` in the
6942 * `packages/compiler/src/schema/dom_security_schema.ts` script for the full list
6943 * of such attributes.
6944 *
6945 * @codeGenApi
6946 */
6947function ɵɵvalidateIframeAttribute(attrValue, tagName, attrName) {
6948 const lView = getLView();
6949 const tNode = getSelectedTNode();
6950 const element = getNativeByTNode(tNode, lView);
6951 // Restrict any dynamic bindings of security-sensitive attributes/properties
6952 // on an <iframe> for security reasons.
6953 if (tNode.type === 2 /* TNodeType.Element */ && tagName.toLowerCase() === 'iframe') {
6954 const iframe = element;
6955 // Unset previously applied `src` and `srcdoc` if we come across a situation when
6956 // a security-sensitive attribute is set later via an attribute/property binding.
6957 iframe.src = '';
6958 iframe.srcdoc = trustedHTMLFromString('');
6959 // Also remove the <iframe> from the document.
6960 nativeRemoveNode(lView[RENDERER], iframe);
6961 const errorMessage = ngDevMode &&
6962 `Angular has detected that the \`${attrName}\` was applied ` +
6963 `as a binding to an <iframe>${getTemplateLocationDetails(lView)}. ` +
6964 `For security reasons, the \`${attrName}\` can be set on an <iframe> ` +
6965 `as a static attribute only. \n` +
6966 `To fix this, switch the \`${attrName}\` binding to a static attribute ` +
6967 `in a template or in host bindings section.`;
6968 throw new RuntimeError(-910 /* RuntimeErrorCode.UNSAFE_IFRAME_ATTRS */, errorMessage);
6969 }
6970 return attrValue;
6971}
6972
6973/**
6974 * Most of the use of `document` in Angular is from within the DI system so it is possible to simply
6975 * inject the `DOCUMENT` token and are done.
6976 *
6977 * Ivy is special because it does not rely upon the DI and must get hold of the document some other
6978 * way.
6979 *
6980 * The solution is to define `getDocument()` and `setDocument()` top-level functions for ivy.
6981 * Wherever ivy needs the global document, it calls `getDocument()` instead.
6982 *
6983 * When running ivy outside of a browser environment, it is necessary to call `setDocument()` to
6984 * tell ivy what the global `document` is.
6985 *
6986 * Angular does this for us in each of the standard platforms (`Browser`, `Server`, and `WebWorker`)
6987 * by calling `setDocument()` when providing the `DOCUMENT` token.
6988 */
6989let DOCUMENT = undefined;
6990/**
6991 * Tell ivy what the `document` is for this platform.
6992 *
6993 * It is only necessary to call this if the current platform is not a browser.
6994 *
6995 * @param document The object representing the global `document` in this environment.
6996 */
6997function setDocument(document) {
6998 DOCUMENT = document;
6999}
7000/**
7001 * Access the object that represents the `document` for this platform.
7002 *
7003 * Ivy calls this whenever it needs to access the `document` object.
7004 * For example to create the renderer or to do sanitization.
7005 */
7006function getDocument() {
7007 if (DOCUMENT !== undefined) {
7008 return DOCUMENT;
7009 }
7010 else if (typeof document !== 'undefined') {
7011 return document;
7012 }
7013 // No "document" can be found. This should only happen if we are running ivy outside Angular and
7014 // the current platform is not a browser. Since this is not a supported scenario at the moment
7015 // this should not happen in Angular apps.
7016 // Once we support running ivy outside of Angular we will need to publish `setDocument()` as a
7017 // public API. Meanwhile we just return `undefined` and let the application fail.
7018 return undefined;
7019}
7020
7021/**
7022 * @fileoverview
7023 * A module to facilitate use of a Trusted Types policy internally within
7024 * Angular specifically for bypassSecurityTrust* and custom sanitizers. It
7025 * lazily constructs the Trusted Types policy, providing helper utilities for
7026 * promoting strings to Trusted Types. When Trusted Types are not available,
7027 * strings are used as a fallback.
7028 * @security All use of this module is security-sensitive and should go through
7029 * security review.
7030 */
7031/**
7032 * The Trusted Types policy, or null if Trusted Types are not
7033 * enabled/supported, or undefined if the policy has not been created yet.
7034 */
7035let policy;
7036/**
7037 * Returns the Trusted Types policy, or null if Trusted Types are not
7038 * enabled/supported. The first call to this function will create the policy.
7039 */
7040function getPolicy() {
7041 if (policy === undefined) {
7042 policy = null;
7043 if (_global$1.trustedTypes) {
7044 try {
7045 policy = _global$1.trustedTypes
7046 .createPolicy('angular#unsafe-bypass', {
7047 createHTML: (s) => s,
7048 createScript: (s) => s,
7049 createScriptURL: (s) => s,
7050 });
7051 }
7052 catch {
7053 // trustedTypes.createPolicy throws if called with a name that is
7054 // already registered, even in report-only mode. Until the API changes,
7055 // catch the error not to break the applications functionally. In such
7056 // cases, the code will fall back to using strings.
7057 }
7058 }
7059 }
7060 return policy;
7061}
7062/**
7063 * Unsafely promote a string to a TrustedHTML, falling back to strings when
7064 * Trusted Types are not available.
7065 * @security This is a security-sensitive function; any use of this function
7066 * must go through security review. In particular, it must be assured that it
7067 * is only passed strings that come directly from custom sanitizers or the
7068 * bypassSecurityTrust* functions.
7069 */
7070function trustedHTMLFromStringBypass(html) {
7071 return getPolicy()?.createHTML(html) || html;
7072}
7073/**
7074 * Unsafely promote a string to a TrustedScript, falling back to strings when
7075 * Trusted Types are not available.
7076 * @security This is a security-sensitive function; any use of this function
7077 * must go through security review. In particular, it must be assured that it
7078 * is only passed strings that come directly from custom sanitizers or the
7079 * bypassSecurityTrust* functions.
7080 */
7081function trustedScriptFromStringBypass(script) {
7082 return getPolicy()?.createScript(script) || script;
7083}
7084/**
7085 * Unsafely promote a string to a TrustedScriptURL, falling back to strings
7086 * when Trusted Types are not available.
7087 * @security This is a security-sensitive function; any use of this function
7088 * must go through security review. In particular, it must be assured that it
7089 * is only passed strings that come directly from custom sanitizers or the
7090 * bypassSecurityTrust* functions.
7091 */
7092function trustedScriptURLFromStringBypass(url) {
7093 return getPolicy()?.createScriptURL(url) || url;
7094}
7095
7096class SafeValueImpl {
7097 constructor(changingThisBreaksApplicationSecurity) {
7098 this.changingThisBreaksApplicationSecurity = changingThisBreaksApplicationSecurity;
7099 }
7100 toString() {
7101 return `SafeValue must use [property]=binding: ${this.changingThisBreaksApplicationSecurity}` +
7102 ` (see ${XSS_SECURITY_URL})`;
7103 }
7104}
7105class SafeHtmlImpl extends SafeValueImpl {
7106 getTypeName() {
7107 return "HTML" /* BypassType.Html */;
7108 }
7109}
7110class SafeStyleImpl extends SafeValueImpl {
7111 getTypeName() {
7112 return "Style" /* BypassType.Style */;
7113 }
7114}
7115class SafeScriptImpl extends SafeValueImpl {
7116 getTypeName() {
7117 return "Script" /* BypassType.Script */;
7118 }
7119}
7120class SafeUrlImpl extends SafeValueImpl {
7121 getTypeName() {
7122 return "URL" /* BypassType.Url */;
7123 }
7124}
7125class SafeResourceUrlImpl extends SafeValueImpl {
7126 getTypeName() {
7127 return "ResourceURL" /* BypassType.ResourceUrl */;
7128 }
7129}
7130function unwrapSafeValue(value) {
7131 return value instanceof SafeValueImpl ? value.changingThisBreaksApplicationSecurity :
7132 value;
7133}
7134function allowSanitizationBypassAndThrow(value, type) {
7135 const actualType = getSanitizationBypassType(value);
7136 if (actualType != null && actualType !== type) {
7137 // Allow ResourceURLs in URL contexts, they are strictly more trusted.
7138 if (actualType === "ResourceURL" /* BypassType.ResourceUrl */ && type === "URL" /* BypassType.Url */)
7139 return true;
7140 throw new Error(`Required a safe ${type}, got a ${actualType} (see ${XSS_SECURITY_URL})`);
7141 }
7142 return actualType === type;
7143}
7144function getSanitizationBypassType(value) {
7145 return value instanceof SafeValueImpl && value.getTypeName() || null;
7146}
7147/**
7148 * Mark `html` string as trusted.
7149 *
7150 * This function wraps the trusted string in `String` and brands it in a way which makes it
7151 * recognizable to {@link htmlSanitizer} to be trusted implicitly.
7152 *
7153 * @param trustedHtml `html` string which needs to be implicitly trusted.
7154 * @returns a `html` which has been branded to be implicitly trusted.
7155 */
7156function bypassSanitizationTrustHtml(trustedHtml) {
7157 return new SafeHtmlImpl(trustedHtml);
7158}
7159/**
7160 * Mark `style` string as trusted.
7161 *
7162 * This function wraps the trusted string in `String` and brands it in a way which makes it
7163 * recognizable to {@link styleSanitizer} to be trusted implicitly.
7164 *
7165 * @param trustedStyle `style` string which needs to be implicitly trusted.
7166 * @returns a `style` hich has been branded to be implicitly trusted.
7167 */
7168function bypassSanitizationTrustStyle(trustedStyle) {
7169 return new SafeStyleImpl(trustedStyle);
7170}
7171/**
7172 * Mark `script` string as trusted.
7173 *
7174 * This function wraps the trusted string in `String` and brands it in a way which makes it
7175 * recognizable to {@link scriptSanitizer} to be trusted implicitly.
7176 *
7177 * @param trustedScript `script` string which needs to be implicitly trusted.
7178 * @returns a `script` which has been branded to be implicitly trusted.
7179 */
7180function bypassSanitizationTrustScript(trustedScript) {
7181 return new SafeScriptImpl(trustedScript);
7182}
7183/**
7184 * Mark `url` string as trusted.
7185 *
7186 * This function wraps the trusted string in `String` and brands it in a way which makes it
7187 * recognizable to {@link urlSanitizer} to be trusted implicitly.
7188 *
7189 * @param trustedUrl `url` string which needs to be implicitly trusted.
7190 * @returns a `url` which has been branded to be implicitly trusted.
7191 */
7192function bypassSanitizationTrustUrl(trustedUrl) {
7193 return new SafeUrlImpl(trustedUrl);
7194}
7195/**
7196 * Mark `url` string as trusted.
7197 *
7198 * This function wraps the trusted string in `String` and brands it in a way which makes it
7199 * recognizable to {@link resourceUrlSanitizer} to be trusted implicitly.
7200 *
7201 * @param trustedResourceUrl `url` string which needs to be implicitly trusted.
7202 * @returns a `url` which has been branded to be implicitly trusted.
7203 */
7204function bypassSanitizationTrustResourceUrl(trustedResourceUrl) {
7205 return new SafeResourceUrlImpl(trustedResourceUrl);
7206}
7207
7208/**
7209 * This helper is used to get hold of an inert tree of DOM elements containing dirty HTML
7210 * that needs sanitizing.
7211 * Depending upon browser support we use one of two strategies for doing this.
7212 * Default: DOMParser strategy
7213 * Fallback: InertDocument strategy
7214 */
7215function getInertBodyHelper(defaultDoc) {
7216 const inertDocumentHelper = new InertDocumentHelper(defaultDoc);
7217 return isDOMParserAvailable() ? new DOMParserHelper(inertDocumentHelper) : inertDocumentHelper;
7218}
7219/**
7220 * Uses DOMParser to create and fill an inert body element.
7221 * This is the default strategy used in browsers that support it.
7222 */
7223class DOMParserHelper {
7224 constructor(inertDocumentHelper) {
7225 this.inertDocumentHelper = inertDocumentHelper;
7226 }
7227 getInertBodyElement(html) {
7228 // We add these extra elements to ensure that the rest of the content is parsed as expected
7229 // e.g. leading whitespace is maintained and tags like `<meta>` do not get hoisted to the
7230 // `<head>` tag. Note that the `<body>` tag is closed implicitly to prevent unclosed tags
7231 // in `html` from consuming the otherwise explicit `</body>` tag.
7232 html = '<body><remove></remove>' + html;
7233 try {
7234 const body = new window.DOMParser()
7235 .parseFromString(trustedHTMLFromString(html), 'text/html')
7236 .body;
7237 if (body === null) {
7238 // In some browsers (e.g. Mozilla/5.0 iPad AppleWebKit Mobile) the `body` property only
7239 // becomes available in the following tick of the JS engine. In that case we fall back to
7240 // the `inertDocumentHelper` instead.
7241 return this.inertDocumentHelper.getInertBodyElement(html);
7242 }
7243 body.removeChild(body.firstChild);
7244 return body;
7245 }
7246 catch {
7247 return null;
7248 }
7249 }
7250}
7251/**
7252 * Use an HTML5 `template` element, if supported, or an inert body element created via
7253 * `createHtmlDocument` to create and fill an inert DOM element.
7254 * This is the fallback strategy if the browser does not support DOMParser.
7255 */
7256class InertDocumentHelper {
7257 constructor(defaultDoc) {
7258 this.defaultDoc = defaultDoc;
7259 this.inertDocument = this.defaultDoc.implementation.createHTMLDocument('sanitization-inert');
7260 if (this.inertDocument.body == null) {
7261 // usually there should be only one body element in the document, but IE doesn't have any, so
7262 // we need to create one.
7263 const inertHtml = this.inertDocument.createElement('html');
7264 this.inertDocument.appendChild(inertHtml);
7265 const inertBodyElement = this.inertDocument.createElement('body');
7266 inertHtml.appendChild(inertBodyElement);
7267 }
7268 }
7269 getInertBodyElement(html) {
7270 // Prefer using <template> element if supported.
7271 const templateEl = this.inertDocument.createElement('template');
7272 if ('content' in templateEl) {
7273 templateEl.innerHTML = trustedHTMLFromString(html);
7274 return templateEl;
7275 }
7276 // Note that previously we used to do something like `this.inertDocument.body.innerHTML = html`
7277 // and we returned the inert `body` node. This was changed, because IE seems to treat setting
7278 // `innerHTML` on an inserted element differently, compared to one that hasn't been inserted
7279 // yet. In particular, IE appears to split some of the text into multiple text nodes rather
7280 // than keeping them in a single one which ends up messing with Ivy's i18n parsing further
7281 // down the line. This has been worked around by creating a new inert `body` and using it as
7282 // the root node in which we insert the HTML.
7283 const inertBody = this.inertDocument.createElement('body');
7284 inertBody.innerHTML = trustedHTMLFromString(html);
7285 // Support: IE 11 only
7286 // strip custom-namespaced attributes on IE<=11
7287 if (this.defaultDoc.documentMode) {
7288 this.stripCustomNsAttrs(inertBody);
7289 }
7290 return inertBody;
7291 }
7292 /**
7293 * When IE11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1'
7294 * attribute to declare ns1 namespace and prefixes the attribute with 'ns1' (e.g.
7295 * 'ns1:xlink:foo').
7296 *
7297 * This is undesirable since we don't want to allow any of these custom attributes. This method
7298 * strips them all.
7299 */
7300 stripCustomNsAttrs(el) {
7301 const elAttrs = el.attributes;
7302 // loop backwards so that we can support removals.
7303 for (let i = elAttrs.length - 1; 0 < i; i--) {
7304 const attrib = elAttrs.item(i);
7305 const attrName = attrib.name;
7306 if (attrName === 'xmlns:ns1' || attrName.indexOf('ns1:') === 0) {
7307 el.removeAttribute(attrName);
7308 }
7309 }
7310 let childNode = el.firstChild;
7311 while (childNode) {
7312 if (childNode.nodeType === Node.ELEMENT_NODE)
7313 this.stripCustomNsAttrs(childNode);
7314 childNode = childNode.nextSibling;
7315 }
7316 }
7317}
7318/**
7319 * We need to determine whether the DOMParser exists in the global context and
7320 * supports parsing HTML; HTML parsing support is not as wide as other formats, see
7321 * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Browser_compatibility.
7322 *
7323 * @suppress {uselessCode}
7324 */
7325function isDOMParserAvailable() {
7326 try {
7327 return !!new window.DOMParser().parseFromString(trustedHTMLFromString(''), 'text/html');
7328 }
7329 catch {
7330 return false;
7331 }
7332}
7333
7334/**
7335 * A pattern that recognizes a commonly useful subset of URLs that are safe.
7336 *
7337 * This regular expression matches a subset of URLs that will not cause script
7338 * execution if used in URL context within a HTML document. Specifically, this
7339 * regular expression matches if (comment from here on and regex copied from
7340 * Soy's EscapingConventions):
7341 * (1) Either an allowed protocol (http, https, mailto or ftp).
7342 * (2) or no protocol. A protocol must be followed by a colon. The below
7343 * allows that by allowing colons only after one of the characters [/?#].
7344 * A colon after a hash (#) must be in the fragment.
7345 * Otherwise, a colon after a (?) must be in a query.
7346 * Otherwise, a colon after a single solidus (/) must be in a path.
7347 * Otherwise, a colon after a double solidus (//) must be in the authority
7348 * (before port).
7349 *
7350 * The pattern disallows &, used in HTML entity declarations before
7351 * one of the characters in [/?#]. This disallows HTML entities used in the
7352 * protocol name, which should never happen, e.g. "h&#116;tp" for "http".
7353 * It also disallows HTML entities in the first path part of a relative path,
7354 * e.g. "foo&lt;bar/baz". Our existing escaping functions should not produce
7355 * that. More importantly, it disallows masking of a colon,
7356 * e.g. "javascript&#58;...".
7357 *
7358 * This regular expression was taken from the Closure sanitization library.
7359 */
7360const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|data|ftp|tel|file|sms):|[^&:/?#]*(?:[/?#]|$))/gi;
7361function _sanitizeUrl(url) {
7362 url = String(url);
7363 if (url.match(SAFE_URL_PATTERN))
7364 return url;
7365 if (typeof ngDevMode === 'undefined' || ngDevMode) {
7366 console.warn(`WARNING: sanitizing unsafe URL value ${url} (see ${XSS_SECURITY_URL})`);
7367 }
7368 return 'unsafe:' + url;
7369}
7370
7371function tagSet(tags) {
7372 const res = {};
7373 for (const t of tags.split(','))
7374 res[t] = true;
7375 return res;
7376}
7377function merge(...sets) {
7378 const res = {};
7379 for (const s of sets) {
7380 for (const v in s) {
7381 if (s.hasOwnProperty(v))
7382 res[v] = true;
7383 }
7384 }
7385 return res;
7386}
7387// Good source of info about elements and attributes
7388// https://html.spec.whatwg.org/#semantics
7389// https://simon.html5.org/html-elements
7390// Safe Void Elements - HTML5
7391// https://html.spec.whatwg.org/#void-elements
7392const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr');
7393// Elements that you can, intentionally, leave open (and which close themselves)
7394// https://html.spec.whatwg.org/#optional-tags
7395const OPTIONAL_END_TAG_BLOCK_ELEMENTS = tagSet('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr');
7396const OPTIONAL_END_TAG_INLINE_ELEMENTS = tagSet('rp,rt');
7397const OPTIONAL_END_TAG_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, OPTIONAL_END_TAG_BLOCK_ELEMENTS);
7398// Safe Block Elements - HTML5
7399const BLOCK_ELEMENTS = merge(OPTIONAL_END_TAG_BLOCK_ELEMENTS, tagSet('address,article,' +
7400 'aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
7401 'h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul'));
7402// Inline Elements - HTML5
7403const INLINE_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, tagSet('a,abbr,acronym,audio,b,' +
7404 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' +
7405 'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video'));
7406const VALID_ELEMENTS = merge(VOID_ELEMENTS, BLOCK_ELEMENTS, INLINE_ELEMENTS, OPTIONAL_END_TAG_ELEMENTS);
7407// Attributes that have href and hence need to be sanitized
7408const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href');
7409const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' +
7410 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' +
7411 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' +
7412 'scope,scrolling,shape,size,sizes,span,srclang,srcset,start,summary,tabindex,target,title,translate,type,usemap,' +
7413 'valign,value,vspace,width');
7414// Accessibility attributes as per WAI-ARIA 1.1 (W3C Working Draft 14 December 2018)
7415const ARIA_ATTRS = tagSet('aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,' +
7416 'aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,' +
7417 'aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,' +
7418 'aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,' +
7419 'aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,' +
7420 'aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,' +
7421 'aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext');
7422// NB: This currently consciously doesn't support SVG. SVG sanitization has had several security
7423// issues in the past, so it seems safer to leave it out if possible. If support for binding SVG via
7424// innerHTML is required, SVG attributes should be added here.
7425// NB: Sanitization does not allow <form> elements or other active elements (<button> etc). Those
7426// can be sanitized, but they increase security surface area without a legitimate use case, so they
7427// are left out here.
7428const VALID_ATTRS = merge(URI_ATTRS, HTML_ATTRS, ARIA_ATTRS);
7429// Elements whose content should not be traversed/preserved, if the elements themselves are invalid.
7430//
7431// Typically, `<invalid>Some content</invalid>` would traverse (and in this case preserve)
7432// `Some content`, but strip `invalid-element` opening/closing tags. For some elements, though, we
7433// don't want to preserve the content, if the elements themselves are going to be removed.
7434const SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS = tagSet('script,style,template');
7435/**
7436 * SanitizingHtmlSerializer serializes a DOM fragment, stripping out any unsafe elements and unsafe
7437 * attributes.
7438 */
7439class SanitizingHtmlSerializer {
7440 constructor() {
7441 // Explicitly track if something was stripped, to avoid accidentally warning of sanitization just
7442 // because characters were re-encoded.
7443 this.sanitizedSomething = false;
7444 this.buf = [];
7445 }
7446 sanitizeChildren(el) {
7447 // This cannot use a TreeWalker, as it has to run on Angular's various DOM adapters.
7448 // However this code never accesses properties off of `document` before deleting its contents
7449 // again, so it shouldn't be vulnerable to DOM clobbering.
7450 let current = el.firstChild;
7451 let traverseContent = true;
7452 while (current) {
7453 if (current.nodeType === Node.ELEMENT_NODE) {
7454 traverseContent = this.startElement(current);
7455 }
7456 else if (current.nodeType === Node.TEXT_NODE) {
7457 this.chars(current.nodeValue);
7458 }
7459 else {
7460 // Strip non-element, non-text nodes.
7461 this.sanitizedSomething = true;
7462 }
7463 if (traverseContent && current.firstChild) {
7464 current = current.firstChild;
7465 continue;
7466 }
7467 while (current) {
7468 // Leaving the element. Walk up and to the right, closing tags as we go.
7469 if (current.nodeType === Node.ELEMENT_NODE) {
7470 this.endElement(current);
7471 }
7472 let next = this.checkClobberedElement(current, current.nextSibling);
7473 if (next) {
7474 current = next;
7475 break;
7476 }
7477 current = this.checkClobberedElement(current, current.parentNode);
7478 }
7479 }
7480 return this.buf.join('');
7481 }
7482 /**
7483 * Sanitizes an opening element tag (if valid) and returns whether the element's contents should
7484 * be traversed. Element content must always be traversed (even if the element itself is not
7485 * valid/safe), unless the element is one of `SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS`.
7486 *
7487 * @param element The element to sanitize.
7488 * @return True if the element's contents should be traversed.
7489 */
7490 startElement(element) {
7491 const tagName = element.nodeName.toLowerCase();
7492 if (!VALID_ELEMENTS.hasOwnProperty(tagName)) {
7493 this.sanitizedSomething = true;
7494 return !SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS.hasOwnProperty(tagName);
7495 }
7496 this.buf.push('<');
7497 this.buf.push(tagName);
7498 const elAttrs = element.attributes;
7499 for (let i = 0; i < elAttrs.length; i++) {
7500 const elAttr = elAttrs.item(i);
7501 const attrName = elAttr.name;
7502 const lower = attrName.toLowerCase();
7503 if (!VALID_ATTRS.hasOwnProperty(lower)) {
7504 this.sanitizedSomething = true;
7505 continue;
7506 }
7507 let value = elAttr.value;
7508 // TODO(martinprobst): Special case image URIs for data:image/...
7509 if (URI_ATTRS[lower])
7510 value = _sanitizeUrl(value);
7511 this.buf.push(' ', attrName, '="', encodeEntities(value), '"');
7512 }
7513 this.buf.push('>');
7514 return true;
7515 }
7516 endElement(current) {
7517 const tagName = current.nodeName.toLowerCase();
7518 if (VALID_ELEMENTS.hasOwnProperty(tagName) && !VOID_ELEMENTS.hasOwnProperty(tagName)) {
7519 this.buf.push('</');
7520 this.buf.push(tagName);
7521 this.buf.push('>');
7522 }
7523 }
7524 chars(chars) {
7525 this.buf.push(encodeEntities(chars));
7526 }
7527 checkClobberedElement(node, nextNode) {
7528 if (nextNode &&
7529 (node.compareDocumentPosition(nextNode) &
7530 Node.DOCUMENT_POSITION_CONTAINED_BY) === Node.DOCUMENT_POSITION_CONTAINED_BY) {
7531 throw new Error(`Failed to sanitize html because the element is clobbered: ${node.outerHTML}`);
7532 }
7533 return nextNode;
7534 }
7535}
7536// Regular Expressions for parsing tags and attributes
7537const SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
7538// ! to ~ is the ASCII range.
7539const NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
7540/**
7541 * Escapes all potentially dangerous characters, so that the
7542 * resulting string can be safely inserted into attribute or
7543 * element text.
7544 * @param value
7545 */
7546function encodeEntities(value) {
7547 return value.replace(/&/g, '&amp;')
7548 .replace(SURROGATE_PAIR_REGEXP, function (match) {
7549 const hi = match.charCodeAt(0);
7550 const low = match.charCodeAt(1);
7551 return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
7552 })
7553 .replace(NON_ALPHANUMERIC_REGEXP, function (match) {
7554 return '&#' + match.charCodeAt(0) + ';';
7555 })
7556 .replace(/</g, '&lt;')
7557 .replace(/>/g, '&gt;');
7558}
7559let inertBodyHelper;
7560/**
7561 * Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to
7562 * the DOM in a browser environment.
7563 */
7564function _sanitizeHtml(defaultDoc, unsafeHtmlInput) {
7565 let inertBodyElement = null;
7566 try {
7567 inertBodyHelper = inertBodyHelper || getInertBodyHelper(defaultDoc);
7568 // Make sure unsafeHtml is actually a string (TypeScript types are not enforced at runtime).
7569 let unsafeHtml = unsafeHtmlInput ? String(unsafeHtmlInput) : '';
7570 inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
7571 // mXSS protection. Repeatedly parse the document to make sure it stabilizes, so that a browser
7572 // trying to auto-correct incorrect HTML cannot cause formerly inert HTML to become dangerous.
7573 let mXSSAttempts = 5;
7574 let parsedHtml = unsafeHtml;
7575 do {
7576 if (mXSSAttempts === 0) {
7577 throw new Error('Failed to sanitize html because the input is unstable');
7578 }
7579 mXSSAttempts--;
7580 unsafeHtml = parsedHtml;
7581 parsedHtml = inertBodyElement.innerHTML;
7582 inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeHtml);
7583 } while (unsafeHtml !== parsedHtml);
7584 const sanitizer = new SanitizingHtmlSerializer();
7585 const safeHtml = sanitizer.sanitizeChildren(getTemplateContent(inertBodyElement) || inertBodyElement);
7586 if ((typeof ngDevMode === 'undefined' || ngDevMode) && sanitizer.sanitizedSomething) {
7587 console.warn(`WARNING: sanitizing HTML stripped some content, see ${XSS_SECURITY_URL}`);
7588 }
7589 return trustedHTMLFromString(safeHtml);
7590 }
7591 finally {
7592 // In case anything goes wrong, clear out inertElement to reset the entire DOM structure.
7593 if (inertBodyElement) {
7594 const parent = getTemplateContent(inertBodyElement) || inertBodyElement;
7595 while (parent.firstChild) {
7596 parent.removeChild(parent.firstChild);
7597 }
7598 }
7599 }
7600}
7601function getTemplateContent(el) {
7602 return 'content' in el /** Microsoft/TypeScript#21517 */ && isTemplateElement(el) ?
7603 el.content :
7604 null;
7605}
7606function isTemplateElement(el) {
7607 return el.nodeType === Node.ELEMENT_NODE && el.nodeName === 'TEMPLATE';
7608}
7609
7610/**
7611 * A SecurityContext marks a location that has dangerous security implications, e.g. a DOM property
7612 * like `innerHTML` that could cause Cross Site Scripting (XSS) security bugs when improperly
7613 * handled.
7614 *
7615 * See DomSanitizer for more details on security in Angular applications.
7616 *
7617 * @publicApi
7618 */
7619var SecurityContext;
7620(function (SecurityContext) {
7621 SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
7622 SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
7623 SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
7624 SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
7625 SecurityContext[SecurityContext["URL"] = 4] = "URL";
7626 SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
7627})(SecurityContext || (SecurityContext = {}));
7628
7629/**
7630 * An `html` sanitizer which converts untrusted `html` **string** into trusted string by removing
7631 * dangerous content.
7632 *
7633 * This method parses the `html` and locates potentially dangerous content (such as urls and
7634 * javascript) and removes it.
7635 *
7636 * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustHtml}.
7637 *
7638 * @param unsafeHtml untrusted `html`, typically from the user.
7639 * @returns `html` string which is safe to display to user, because all of the dangerous javascript
7640 * and urls have been removed.
7641 *
7642 * @codeGenApi
7643 */
7644function ɵɵsanitizeHtml(unsafeHtml) {
7645 const sanitizer = getSanitizer();
7646 if (sanitizer) {
7647 return trustedHTMLFromStringBypass(sanitizer.sanitize(SecurityContext.HTML, unsafeHtml) || '');
7648 }
7649 if (allowSanitizationBypassAndThrow(unsafeHtml, "HTML" /* BypassType.Html */)) {
7650 return trustedHTMLFromStringBypass(unwrapSafeValue(unsafeHtml));
7651 }
7652 return _sanitizeHtml(getDocument(), renderStringify(unsafeHtml));
7653}
7654/**
7655 * A `style` sanitizer which converts untrusted `style` **string** into trusted string by removing
7656 * dangerous content.
7657 *
7658 * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustStyle}.
7659 *
7660 * @param unsafeStyle untrusted `style`, typically from the user.
7661 * @returns `style` string which is safe to bind to the `style` properties.
7662 *
7663 * @codeGenApi
7664 */
7665function ɵɵsanitizeStyle(unsafeStyle) {
7666 const sanitizer = getSanitizer();
7667 if (sanitizer) {
7668 return sanitizer.sanitize(SecurityContext.STYLE, unsafeStyle) || '';
7669 }
7670 if (allowSanitizationBypassAndThrow(unsafeStyle, "Style" /* BypassType.Style */)) {
7671 return unwrapSafeValue(unsafeStyle);
7672 }
7673 return renderStringify(unsafeStyle);
7674}
7675/**
7676 * A `url` sanitizer which converts untrusted `url` **string** into trusted string by removing
7677 * dangerous
7678 * content.
7679 *
7680 * This method parses the `url` and locates potentially dangerous content (such as javascript) and
7681 * removes it.
7682 *
7683 * It is possible to mark a string as trusted by calling {@link bypassSanitizationTrustUrl}.
7684 *
7685 * @param unsafeUrl untrusted `url`, typically from the user.
7686 * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
7687 * all of the dangerous javascript has been removed.
7688 *
7689 * @codeGenApi
7690 */
7691function ɵɵsanitizeUrl(unsafeUrl) {
7692 const sanitizer = getSanitizer();
7693 if (sanitizer) {
7694 return sanitizer.sanitize(SecurityContext.URL, unsafeUrl) || '';
7695 }
7696 if (allowSanitizationBypassAndThrow(unsafeUrl, "URL" /* BypassType.Url */)) {
7697 return unwrapSafeValue(unsafeUrl);
7698 }
7699 return _sanitizeUrl(renderStringify(unsafeUrl));
7700}
7701/**
7702 * A `url` sanitizer which only lets trusted `url`s through.
7703 *
7704 * This passes only `url`s marked trusted by calling {@link bypassSanitizationTrustResourceUrl}.
7705 *
7706 * @param unsafeResourceUrl untrusted `url`, typically from the user.
7707 * @returns `url` string which is safe to bind to the `src` properties such as `<img src>`, because
7708 * only trusted `url`s have been allowed to pass.
7709 *
7710 * @codeGenApi
7711 */
7712function ɵɵsanitizeResourceUrl(unsafeResourceUrl) {
7713 const sanitizer = getSanitizer();
7714 if (sanitizer) {
7715 return trustedScriptURLFromStringBypass(sanitizer.sanitize(SecurityContext.RESOURCE_URL, unsafeResourceUrl) || '');
7716 }
7717 if (allowSanitizationBypassAndThrow(unsafeResourceUrl, "ResourceURL" /* BypassType.ResourceUrl */)) {
7718 return trustedScriptURLFromStringBypass(unwrapSafeValue(unsafeResourceUrl));
7719 }
7720 throw new RuntimeError(904 /* RuntimeErrorCode.UNSAFE_VALUE_IN_RESOURCE_URL */, ngDevMode && `unsafe value used in a resource URL context (see ${XSS_SECURITY_URL})`);
7721}
7722/**
7723 * A `script` sanitizer which only lets trusted javascript through.
7724 *
7725 * This passes only `script`s marked trusted by calling {@link
7726 * bypassSanitizationTrustScript}.
7727 *
7728 * @param unsafeScript untrusted `script`, typically from the user.
7729 * @returns `url` string which is safe to bind to the `<script>` element such as `<img src>`,
7730 * because only trusted `scripts` have been allowed to pass.
7731 *
7732 * @codeGenApi
7733 */
7734function ɵɵsanitizeScript(unsafeScript) {
7735 const sanitizer = getSanitizer();
7736 if (sanitizer) {
7737 return trustedScriptFromStringBypass(sanitizer.sanitize(SecurityContext.SCRIPT, unsafeScript) || '');
7738 }
7739 if (allowSanitizationBypassAndThrow(unsafeScript, "Script" /* BypassType.Script */)) {
7740 return trustedScriptFromStringBypass(unwrapSafeValue(unsafeScript));
7741 }
7742 throw new RuntimeError(905 /* RuntimeErrorCode.UNSAFE_VALUE_IN_SCRIPT */, ngDevMode && 'unsafe value used in a script context');
7743}
7744/**
7745 * A template tag function for promoting the associated constant literal to a
7746 * TrustedHTML. Interpolation is explicitly not allowed.
7747 *
7748 * @param html constant template literal containing trusted HTML.
7749 * @returns TrustedHTML wrapping `html`.
7750 *
7751 * @security This is a security-sensitive function and should only be used to
7752 * convert constant values of attributes and properties found in
7753 * application-provided Angular templates to TrustedHTML.
7754 *
7755 * @codeGenApi
7756 */
7757function ɵɵtrustConstantHtml(html) {
7758 // The following runtime check ensures that the function was called as a
7759 // template tag (e.g. ɵɵtrustConstantHtml`content`), without any interpolation
7760 // (e.g. not ɵɵtrustConstantHtml`content ${variable}`). A TemplateStringsArray
7761 // is an array with a `raw` property that is also an array. The associated
7762 // template literal has no interpolation if and only if the length of the
7763 // TemplateStringsArray is 1.
7764 if (ngDevMode && (!Array.isArray(html) || !Array.isArray(html.raw) || html.length !== 1)) {
7765 throw new Error(`Unexpected interpolation in trusted HTML constant: ${html.join('?')}`);
7766 }
7767 return trustedHTMLFromString(html[0]);
7768}
7769/**
7770 * A template tag function for promoting the associated constant literal to a
7771 * TrustedScriptURL. Interpolation is explicitly not allowed.
7772 *
7773 * @param url constant template literal containing a trusted script URL.
7774 * @returns TrustedScriptURL wrapping `url`.
7775 *
7776 * @security This is a security-sensitive function and should only be used to
7777 * convert constant values of attributes and properties found in
7778 * application-provided Angular templates to TrustedScriptURL.
7779 *
7780 * @codeGenApi
7781 */
7782function ɵɵtrustConstantResourceUrl(url) {
7783 // The following runtime check ensures that the function was called as a
7784 // template tag (e.g. ɵɵtrustConstantResourceUrl`content`), without any
7785 // interpolation (e.g. not ɵɵtrustConstantResourceUrl`content ${variable}`). A
7786 // TemplateStringsArray is an array with a `raw` property that is also an
7787 // array. The associated template literal has no interpolation if and only if
7788 // the length of the TemplateStringsArray is 1.
7789 if (ngDevMode && (!Array.isArray(url) || !Array.isArray(url.raw) || url.length !== 1)) {
7790 throw new Error(`Unexpected interpolation in trusted URL constant: ${url.join('?')}`);
7791 }
7792 return trustedScriptURLFromString(url[0]);
7793}
7794/**
7795 * Detects which sanitizer to use for URL property, based on tag name and prop name.
7796 *
7797 * The rules are based on the RESOURCE_URL context config from
7798 * `packages/compiler/src/schema/dom_security_schema.ts`.
7799 * If tag and prop names don't match Resource URL schema, use URL sanitizer.
7800 */
7801function getUrlSanitizer(tag, prop) {
7802 if ((prop === 'src' &&
7803 (tag === 'embed' || tag === 'frame' || tag === 'iframe' || tag === 'media' ||
7804 tag === 'script')) ||
7805 (prop === 'href' && (tag === 'base' || tag === 'link'))) {
7806 return ɵɵsanitizeResourceUrl;
7807 }
7808 return ɵɵsanitizeUrl;
7809}
7810/**
7811 * Sanitizes URL, selecting sanitizer function based on tag and property names.
7812 *
7813 * This function is used in case we can't define security context at compile time, when only prop
7814 * name is available. This happens when we generate host bindings for Directives/Components. The
7815 * host element is unknown at compile time, so we defer calculation of specific sanitizer to
7816 * runtime.
7817 *
7818 * @param unsafeUrl untrusted `url`, typically from the user.
7819 * @param tag target element tag name.
7820 * @param prop name of the property that contains the value.
7821 * @returns `url` string which is safe to bind.
7822 *
7823 * @codeGenApi
7824 */
7825function ɵɵsanitizeUrlOrResourceUrl(unsafeUrl, tag, prop) {
7826 return getUrlSanitizer(tag, prop)(unsafeUrl);
7827}
7828function validateAgainstEventProperties(name) {
7829 if (name.toLowerCase().startsWith('on')) {
7830 const errorMessage = `Binding to event property '${name}' is disallowed for security reasons, ` +
7831 `please use (${name.slice(2)})=...` +
7832 `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
7833 ` current module.`;
7834 throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage);
7835 }
7836}
7837function validateAgainstEventAttributes(name) {
7838 if (name.toLowerCase().startsWith('on')) {
7839 const errorMessage = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
7840 `please use (${name.slice(2)})=...`;
7841 throw new RuntimeError(306 /* RuntimeErrorCode.INVALID_EVENT_BINDING */, errorMessage);
7842 }
7843}
7844function getSanitizer() {
7845 const lView = getLView();
7846 return lView && lView[SANITIZER];
7847}
7848
7849/**
7850 * Creates a token that can be used in a DI Provider.
7851 *
7852 * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
7853 * runtime representation) such as when injecting an interface, callable type, array or
7854 * parameterized type.
7855 *
7856 * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
7857 * the `Injector`. This provides an additional level of type safety.
7858 *
7859 * ```
7860 * interface MyInterface {...}
7861 * const myInterface = injector.get(new InjectionToken<MyInterface>('SomeToken'));
7862 * // myInterface is inferred to be MyInterface.
7863 * ```
7864 *
7865 * When creating an `InjectionToken`, you can optionally specify a factory function which returns
7866 * (possibly by creating) a default value of the parameterized type `T`. This sets up the
7867 * `InjectionToken` using this factory as a provider as if it was defined explicitly in the
7868 * application's root injector. If the factory function, which takes zero arguments, needs to inject
7869 * dependencies, it can do so using the `inject` function.
7870 * As you can see in the Tree-shakable InjectionToken example below.
7871 *
7872 * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
7873 * overrides the above behavior and marks the token as belonging to a particular `@NgModule` (note:
7874 * this option is now deprecated). As mentioned above, `'root'` is the default value for
7875 * `providedIn`.
7876 *
7877 * The `providedIn: NgModule` and `providedIn: 'any'` options are deprecated.
7878 *
7879 * @usageNotes
7880 * ### Basic Examples
7881 *
7882 * ### Plain InjectionToken
7883 *
7884 * {@example core/di/ts/injector_spec.ts region='InjectionToken'}
7885 *
7886 * ### Tree-shakable InjectionToken
7887 *
7888 * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
7889 *
7890 *
7891 * @publicApi
7892 */
7893class InjectionToken {
7894 /**
7895 * @param _desc Description for the token,
7896 * used only for debugging purposes,
7897 * it should but does not need to be unique
7898 * @param options Options for the token's usage, as described above
7899 */
7900 constructor(_desc, options) {
7901 this._desc = _desc;
7902 /** @internal */
7903 this.ngMetadataName = 'InjectionToken';
7904 this.ɵprov = undefined;
7905 if (typeof options == 'number') {
7906 (typeof ngDevMode === 'undefined' || ngDevMode) &&
7907 assertLessThan(options, 0, 'Only negative numbers are supported here');
7908 // This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
7909 // See `InjectorMarkers`
7910 this.__NG_ELEMENT_ID__ = options;
7911 }
7912 else if (options !== undefined) {
7913 this.ɵprov = ɵɵdefineInjectable({
7914 token: this,
7915 providedIn: options.providedIn || 'root',
7916 factory: options.factory,
7917 });
7918 }
7919 }
7920 /**
7921 * @internal
7922 */
7923 get multi() {
7924 return this;
7925 }
7926 toString() {
7927 return `InjectionToken ${this._desc}`;
7928 }
7929}
7930
7931/**
7932 * A multi-provider token for initialization functions that will run upon construction of an
7933 * environment injector.
7934 *
7935 * @publicApi
7936 */
7937const ENVIRONMENT_INITIALIZER = new InjectionToken('ENVIRONMENT_INITIALIZER');
7938
7939/**
7940 * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
7941 *
7942 * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
7943 * project.
7944 *
7945 * @publicApi
7946 */
7947const INJECTOR = new InjectionToken('INJECTOR',
7948// Disable tslint because this is const enum which gets inlined not top level prop access.
7949// tslint:disable-next-line: no-toplevel-property-access
7950-1 /* InjectorMarkers.Injector */);
7951
7952const INJECTOR_DEF_TYPES = new InjectionToken('INJECTOR_DEF_TYPES');
7953
7954class NullInjector {
7955 get(token, notFoundValue = THROW_IF_NOT_FOUND) {
7956 if (notFoundValue === THROW_IF_NOT_FOUND) {
7957 const error = new Error(`NullInjectorError: No provider for ${stringify(token)}!`);
7958 error.name = 'NullInjectorError';
7959 throw error;
7960 }
7961 return notFoundValue;
7962 }
7963}
7964
7965/**
7966 * Wrap an array of `Provider`s into `EnvironmentProviders`, preventing them from being accidentally
7967 * referenced in `@Component in a component injector.
7968 */
7969function makeEnvironmentProviders(providers) {
7970 return {
7971 ɵproviders: providers,
7972 };
7973}
7974/**
7975 * Collects providers from all NgModules and standalone components, including transitively imported
7976 * ones.
7977 *
7978 * Providers extracted via `importProvidersFrom` are only usable in an application injector or
7979 * another environment injector (such as a route injector). They should not be used in component
7980 * providers.
7981 *
7982 * More information about standalone components can be found in [this
7983 * guide](guide/standalone-components).
7984 *
7985 * @usageNotes
7986 * The results of the `importProvidersFrom` call can be used in the `bootstrapApplication` call:
7987 *
7988 * ```typescript
7989 * await bootstrapApplication(RootComponent, {
7990 * providers: [
7991 * importProvidersFrom(NgModuleOne, NgModuleTwo)
7992 * ]
7993 * });
7994 * ```
7995 *
7996 * You can also use the `importProvidersFrom` results in the `providers` field of a route, when a
7997 * standalone component is used:
7998 *
7999 * ```typescript
8000 * export const ROUTES: Route[] = [
8001 * {
8002 * path: 'foo',
8003 * providers: [
8004 * importProvidersFrom(NgModuleOne, NgModuleTwo)
8005 * ],
8006 * component: YourStandaloneComponent
8007 * }
8008 * ];
8009 * ```
8010 *
8011 * @returns Collected providers from the specified list of types.
8012 * @publicApi
8013 */
8014function importProvidersFrom(...sources) {
8015 return {
8016 ɵproviders: internalImportProvidersFrom(true, sources),
8017 ɵfromNgModule: true,
8018 };
8019}
8020function internalImportProvidersFrom(checkForStandaloneCmp, ...sources) {
8021 const providersOut = [];
8022 const dedup = new Set(); // already seen types
8023 let injectorTypesWithProviders;
8024 deepForEach(sources, source => {
8025 if ((typeof ngDevMode === 'undefined' || ngDevMode) && checkForStandaloneCmp) {
8026 const cmpDef = getComponentDef$1(source);
8027 if (cmpDef?.standalone) {
8028 throw new RuntimeError(800 /* RuntimeErrorCode.IMPORT_PROVIDERS_FROM_STANDALONE */, `Importing providers supports NgModule or ModuleWithProviders but got a standalone component "${stringifyForError(source)}"`);
8029 }
8030 }
8031 // Narrow `source` to access the internal type analogue for `ModuleWithProviders`.
8032 const internalSource = source;
8033 if (walkProviderTree(internalSource, providersOut, [], dedup)) {
8034 injectorTypesWithProviders || (injectorTypesWithProviders = []);
8035 injectorTypesWithProviders.push(internalSource);
8036 }
8037 });
8038 // Collect all providers from `ModuleWithProviders` types.
8039 if (injectorTypesWithProviders !== undefined) {
8040 processInjectorTypesWithProviders(injectorTypesWithProviders, providersOut);
8041 }
8042 return providersOut;
8043}
8044/**
8045 * Collects all providers from the list of `ModuleWithProviders` and appends them to the provided
8046 * array.
8047 */
8048function processInjectorTypesWithProviders(typesWithProviders, providersOut) {
8049 for (let i = 0; i < typesWithProviders.length; i++) {
8050 const { ngModule, providers } = typesWithProviders[i];
8051 deepForEachProvider(providers, provider => {
8052 ngDevMode && validateProvider(provider, providers || EMPTY_ARRAY, ngModule);
8053 providersOut.push(provider);
8054 });
8055 }
8056}
8057/**
8058 * The logic visits an `InjectorType`, an `InjectorTypeWithProviders`, or a standalone
8059 * `ComponentType`, and all of its transitive providers and collects providers.
8060 *
8061 * If an `InjectorTypeWithProviders` that declares providers besides the type is specified,
8062 * the function will return "true" to indicate that the providers of the type definition need
8063 * to be processed. This allows us to process providers of injector types after all imports of
8064 * an injector definition are processed. (following View Engine semantics: see FW-1349)
8065 */
8066function walkProviderTree(container, providersOut, parents, dedup) {
8067 container = resolveForwardRef(container);
8068 if (!container)
8069 return false;
8070 // The actual type which had the definition. Usually `container`, but may be an unwrapped type
8071 // from `InjectorTypeWithProviders`.
8072 let defType = null;
8073 let injDef = getInjectorDef(container);
8074 const cmpDef = !injDef && getComponentDef$1(container);
8075 if (!injDef && !cmpDef) {
8076 // `container` is not an injector type or a component type. It might be:
8077 // * An `InjectorTypeWithProviders` that wraps an injector type.
8078 // * A standalone directive or pipe that got pulled in from a standalone component's
8079 // dependencies.
8080 // Try to unwrap it as an `InjectorTypeWithProviders` first.
8081 const ngModule = container.ngModule;
8082 injDef = getInjectorDef(ngModule);
8083 if (injDef) {
8084 defType = ngModule;
8085 }
8086 else {
8087 // Not a component or injector type, so ignore it.
8088 return false;
8089 }
8090 }
8091 else if (cmpDef && !cmpDef.standalone) {
8092 return false;
8093 }
8094 else {
8095 defType = container;
8096 }
8097 // Check for circular dependencies.
8098 if (ngDevMode && parents.indexOf(defType) !== -1) {
8099 const defName = stringify(defType);
8100 const path = parents.map(stringify);
8101 throwCyclicDependencyError(defName, path);
8102 }
8103 // Check for multiple imports of the same module
8104 const isDuplicate = dedup.has(defType);
8105 if (cmpDef) {
8106 if (isDuplicate) {
8107 // This component definition has already been processed.
8108 return false;
8109 }
8110 dedup.add(defType);
8111 if (cmpDef.dependencies) {
8112 const deps = typeof cmpDef.dependencies === 'function' ? cmpDef.dependencies() : cmpDef.dependencies;
8113 for (const dep of deps) {
8114 walkProviderTree(dep, providersOut, parents, dedup);
8115 }
8116 }
8117 }
8118 else if (injDef) {
8119 // First, include providers from any imports.
8120 if (injDef.imports != null && !isDuplicate) {
8121 // Before processing defType's imports, add it to the set of parents. This way, if it ends
8122 // up deeply importing itself, this can be detected.
8123 ngDevMode && parents.push(defType);
8124 // Add it to the set of dedups. This way we can detect multiple imports of the same module
8125 dedup.add(defType);
8126 let importTypesWithProviders;
8127 try {
8128 deepForEach(injDef.imports, imported => {
8129 if (walkProviderTree(imported, providersOut, parents, dedup)) {
8130 importTypesWithProviders || (importTypesWithProviders = []);
8131 // If the processed import is an injector type with providers, we store it in the
8132 // list of import types with providers, so that we can process those afterwards.
8133 importTypesWithProviders.push(imported);
8134 }
8135 });
8136 }
8137 finally {
8138 // Remove it from the parents set when finished.
8139 ngDevMode && parents.pop();
8140 }
8141 // Imports which are declared with providers (TypeWithProviders) need to be processed
8142 // after all imported modules are processed. This is similar to how View Engine
8143 // processes/merges module imports in the metadata resolver. See: FW-1349.
8144 if (importTypesWithProviders !== undefined) {
8145 processInjectorTypesWithProviders(importTypesWithProviders, providersOut);
8146 }
8147 }
8148 if (!isDuplicate) {
8149 // Track the InjectorType and add a provider for it.
8150 // It's important that this is done after the def's imports.
8151 const factory = getFactoryDef(defType) || (() => new defType());
8152 // Append extra providers to make more info available for consumers (to retrieve an injector
8153 // type), as well as internally (to calculate an injection scope correctly and eagerly
8154 // instantiate a `defType` when an injector is created).
8155 providersOut.push(
8156 // Provider to create `defType` using its factory.
8157 { provide: defType, useFactory: factory, deps: EMPTY_ARRAY },
8158 // Make this `defType` available to an internal logic that calculates injector scope.
8159 { provide: INJECTOR_DEF_TYPES, useValue: defType, multi: true },
8160 // Provider to eagerly instantiate `defType` via `ENVIRONMENT_INITIALIZER`.
8161 { provide: ENVIRONMENT_INITIALIZER, useValue: () => ɵɵinject(defType), multi: true } //
8162 );
8163 }
8164 // Next, include providers listed on the definition itself.
8165 const defProviders = injDef.providers;
8166 if (defProviders != null && !isDuplicate) {
8167 const injectorType = container;
8168 deepForEachProvider(defProviders, provider => {
8169 ngDevMode && validateProvider(provider, defProviders, injectorType);
8170 providersOut.push(provider);
8171 });
8172 }
8173 }
8174 else {
8175 // Should not happen, but just in case.
8176 return false;
8177 }
8178 return (defType !== container &&
8179 container.providers !== undefined);
8180}
8181function validateProvider(provider, providers, containerType) {
8182 if (isTypeProvider(provider) || isValueProvider(provider) || isFactoryProvider(provider) ||
8183 isExistingProvider(provider)) {
8184 return;
8185 }
8186 // Here we expect the provider to be a `useClass` provider (by elimination).
8187 const classRef = resolveForwardRef(provider && (provider.useClass || provider.provide));
8188 if (!classRef) {
8189 throwInvalidProviderError(containerType, providers, provider);
8190 }
8191}
8192function deepForEachProvider(providers, fn) {
8193 for (let provider of providers) {
8194 if (isEnvironmentProviders(provider)) {
8195 provider = provider.ɵproviders;
8196 }
8197 if (Array.isArray(provider)) {
8198 deepForEachProvider(provider, fn);
8199 }
8200 else {
8201 fn(provider);
8202 }
8203 }
8204}
8205const USE_VALUE$1 = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty });
8206function isValueProvider(value) {
8207 return value !== null && typeof value == 'object' && USE_VALUE$1 in value;
8208}
8209function isExistingProvider(value) {
8210 return !!(value && value.useExisting);
8211}
8212function isFactoryProvider(value) {
8213 return !!(value && value.useFactory);
8214}
8215function isTypeProvider(value) {
8216 return typeof value === 'function';
8217}
8218function isClassProvider(value) {
8219 return !!value.useClass;
8220}
8221
8222/**
8223 * An internal token whose presence in an injector indicates that the injector should treat itself
8224 * as a root scoped injector when processing requests for unknown tokens which may indicate
8225 * they are provided in the root scope.
8226 */
8227const INJECTOR_SCOPE = new InjectionToken('Set Injector scope.');
8228
8229/**
8230 * Marker which indicates that a value has not yet been created from the factory function.
8231 */
8232const NOT_YET = {};
8233/**
8234 * Marker which indicates that the factory function for a token is in the process of being called.
8235 *
8236 * If the injector is asked to inject a token with its value set to CIRCULAR, that indicates
8237 * injection of a dependency has recursively attempted to inject the original token, and there is
8238 * a circular dependency among the providers.
8239 */
8240const CIRCULAR = {};
8241/**
8242 * A lazily initialized NullInjector.
8243 */
8244let NULL_INJECTOR$1 = undefined;
8245function getNullInjector() {
8246 if (NULL_INJECTOR$1 === undefined) {
8247 NULL_INJECTOR$1 = new NullInjector();
8248 }
8249 return NULL_INJECTOR$1;
8250}
8251/**
8252 * An `Injector` that's part of the environment injector hierarchy, which exists outside of the
8253 * component tree.
8254 */
8255class EnvironmentInjector {
8256}
8257class R3Injector extends EnvironmentInjector {
8258 /**
8259 * Flag indicating that this injector was previously destroyed.
8260 */
8261 get destroyed() {
8262 return this._destroyed;
8263 }
8264 constructor(providers, parent, source, scopes) {
8265 super();
8266 this.parent = parent;
8267 this.source = source;
8268 this.scopes = scopes;
8269 /**
8270 * Map of tokens to records which contain the instances of those tokens.
8271 * - `null` value implies that we don't have the record. Used by tree-shakable injectors
8272 * to prevent further searches.
8273 */
8274 this.records = new Map();
8275 /**
8276 * Set of values instantiated by this injector which contain `ngOnDestroy` lifecycle hooks.
8277 */
8278 this._ngOnDestroyHooks = new Set();
8279 this._onDestroyHooks = [];
8280 this._destroyed = false;
8281 // Start off by creating Records for every provider.
8282 forEachSingleProvider(providers, provider => this.processProvider(provider));
8283 // Make sure the INJECTOR token provides this injector.
8284 this.records.set(INJECTOR, makeRecord(undefined, this));
8285 // And `EnvironmentInjector` if the current injector is supposed to be env-scoped.
8286 if (scopes.has('environment')) {
8287 this.records.set(EnvironmentInjector, makeRecord(undefined, this));
8288 }
8289 // Detect whether this injector has the APP_ROOT_SCOPE token and thus should provide
8290 // any injectable scoped to APP_ROOT_SCOPE.
8291 const record = this.records.get(INJECTOR_SCOPE);
8292 if (record != null && typeof record.value === 'string') {
8293 this.scopes.add(record.value);
8294 }
8295 this.injectorDefTypes =
8296 new Set(this.get(INJECTOR_DEF_TYPES.multi, EMPTY_ARRAY, InjectFlags.Self));
8297 }
8298 /**
8299 * Destroy the injector and release references to every instance or provider associated with it.
8300 *
8301 * Also calls the `OnDestroy` lifecycle hooks of every instance that was created for which a
8302 * hook was found.
8303 */
8304 destroy() {
8305 this.assertNotDestroyed();
8306 // Set destroyed = true first, in case lifecycle hooks re-enter destroy().
8307 this._destroyed = true;
8308 try {
8309 // Call all the lifecycle hooks.
8310 for (const service of this._ngOnDestroyHooks) {
8311 service.ngOnDestroy();
8312 }
8313 for (const hook of this._onDestroyHooks) {
8314 hook();
8315 }
8316 }
8317 finally {
8318 // Release all references.
8319 this.records.clear();
8320 this._ngOnDestroyHooks.clear();
8321 this.injectorDefTypes.clear();
8322 this._onDestroyHooks.length = 0;
8323 }
8324 }
8325 onDestroy(callback) {
8326 this._onDestroyHooks.push(callback);
8327 }
8328 runInContext(fn) {
8329 this.assertNotDestroyed();
8330 const previousInjector = setCurrentInjector(this);
8331 const previousInjectImplementation = setInjectImplementation(undefined);
8332 try {
8333 return fn();
8334 }
8335 finally {
8336 setCurrentInjector(previousInjector);
8337 setInjectImplementation(previousInjectImplementation);
8338 }
8339 }
8340 get(token, notFoundValue = THROW_IF_NOT_FOUND, flags = InjectFlags.Default) {
8341 this.assertNotDestroyed();
8342 flags = convertToBitFlags(flags);
8343 // Set the injection context.
8344 const previousInjector = setCurrentInjector(this);
8345 const previousInjectImplementation = setInjectImplementation(undefined);
8346 try {
8347 // Check for the SkipSelf flag.
8348 if (!(flags & InjectFlags.SkipSelf)) {
8349 // SkipSelf isn't set, check if the record belongs to this injector.
8350 let record = this.records.get(token);
8351 if (record === undefined) {
8352 // No record, but maybe the token is scoped to this injector. Look for an injectable
8353 // def with a scope matching this injector.
8354 const def = couldBeInjectableType(token) && getInjectableDef(token);
8355 if (def && this.injectableDefInScope(def)) {
8356 // Found an injectable def and it's scoped to this injector. Pretend as if it was here
8357 // all along.
8358 record = makeRecord(injectableDefOrInjectorDefFactory(token), NOT_YET);
8359 }
8360 else {
8361 record = null;
8362 }
8363 this.records.set(token, record);
8364 }
8365 // If a record was found, get the instance for it and return it.
8366 if (record != null /* NOT null || undefined */) {
8367 return this.hydrate(token, record);
8368 }
8369 }
8370 // Select the next injector based on the Self flag - if self is set, the next injector is
8371 // the NullInjector, otherwise it's the parent.
8372 const nextInjector = !(flags & InjectFlags.Self) ? this.parent : getNullInjector();
8373 // Set the notFoundValue based on the Optional flag - if optional is set and notFoundValue
8374 // is undefined, the value is null, otherwise it's the notFoundValue.
8375 notFoundValue = (flags & InjectFlags.Optional) && notFoundValue === THROW_IF_NOT_FOUND ?
8376 null :
8377 notFoundValue;
8378 return nextInjector.get(token, notFoundValue);
8379 }
8380 catch (e) {
8381 if (e.name === 'NullInjectorError') {
8382 const path = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
8383 path.unshift(stringify(token));
8384 if (previousInjector) {
8385 // We still have a parent injector, keep throwing
8386 throw e;
8387 }
8388 else {
8389 // Format & throw the final error message when we don't have any previous injector
8390 return catchInjectorError(e, token, 'R3InjectorError', this.source);
8391 }
8392 }
8393 else {
8394 throw e;
8395 }
8396 }
8397 finally {
8398 // Lastly, restore the previous injection context.
8399 setInjectImplementation(previousInjectImplementation);
8400 setCurrentInjector(previousInjector);
8401 }
8402 }
8403 /** @internal */
8404 resolveInjectorInitializers() {
8405 const previousInjector = setCurrentInjector(this);
8406 const previousInjectImplementation = setInjectImplementation(undefined);
8407 try {
8408 const initializers = this.get(ENVIRONMENT_INITIALIZER.multi, EMPTY_ARRAY, InjectFlags.Self);
8409 if (ngDevMode && !Array.isArray(initializers)) {
8410 throw new RuntimeError(-209 /* RuntimeErrorCode.INVALID_MULTI_PROVIDER */, 'Unexpected type of the `ENVIRONMENT_INITIALIZER` token value ' +
8411 `(expected an array, but got ${typeof initializers}). ` +
8412 'Please check that the `ENVIRONMENT_INITIALIZER` token is configured as a ' +
8413 '`multi: true` provider.');
8414 }
8415 for (const initializer of initializers) {
8416 initializer();
8417 }
8418 }
8419 finally {
8420 setCurrentInjector(previousInjector);
8421 setInjectImplementation(previousInjectImplementation);
8422 }
8423 }
8424 toString() {
8425 const tokens = [];
8426 const records = this.records;
8427 for (const token of records.keys()) {
8428 tokens.push(stringify(token));
8429 }
8430 return `R3Injector[${tokens.join(', ')}]`;
8431 }
8432 assertNotDestroyed() {
8433 if (this._destroyed) {
8434 throw new RuntimeError(205 /* RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED */, ngDevMode && 'Injector has already been destroyed.');
8435 }
8436 }
8437 /**
8438 * Process a `SingleProvider` and add it.
8439 */
8440 processProvider(provider) {
8441 // Determine the token from the provider. Either it's its own token, or has a {provide: ...}
8442 // property.
8443 provider = resolveForwardRef(provider);
8444 let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider && provider.provide);
8445 // Construct a `Record` for the provider.
8446 const record = providerToRecord(provider);
8447 if (!isTypeProvider(provider) && provider.multi === true) {
8448 // If the provider indicates that it's a multi-provider, process it specially.
8449 // First check whether it's been defined already.
8450 let multiRecord = this.records.get(token);
8451 if (multiRecord) {
8452 // It has. Throw a nice error if
8453 if (ngDevMode && multiRecord.multi === undefined) {
8454 throwMixedMultiProviderError();
8455 }
8456 }
8457 else {
8458 multiRecord = makeRecord(undefined, NOT_YET, true);
8459 multiRecord.factory = () => injectArgs(multiRecord.multi);
8460 this.records.set(token, multiRecord);
8461 }
8462 token = provider;
8463 multiRecord.multi.push(provider);
8464 }
8465 else {
8466 const existing = this.records.get(token);
8467 if (ngDevMode && existing && existing.multi !== undefined) {
8468 throwMixedMultiProviderError();
8469 }
8470 }
8471 this.records.set(token, record);
8472 }
8473 hydrate(token, record) {
8474 if (ngDevMode && record.value === CIRCULAR) {
8475 throwCyclicDependencyError(stringify(token));
8476 }
8477 else if (record.value === NOT_YET) {
8478 record.value = CIRCULAR;
8479 record.value = record.factory();
8480 }
8481 if (typeof record.value === 'object' && record.value && hasOnDestroy(record.value)) {
8482 this._ngOnDestroyHooks.add(record.value);
8483 }
8484 return record.value;
8485 }
8486 injectableDefInScope(def) {
8487 if (!def.providedIn) {
8488 return false;
8489 }
8490 const providedIn = resolveForwardRef(def.providedIn);
8491 if (typeof providedIn === 'string') {
8492 return providedIn === 'any' || (this.scopes.has(providedIn));
8493 }
8494 else {
8495 return this.injectorDefTypes.has(providedIn);
8496 }
8497 }
8498}
8499function injectableDefOrInjectorDefFactory(token) {
8500 // Most tokens will have an injectable def directly on them, which specifies a factory directly.
8501 const injectableDef = getInjectableDef(token);
8502 const factory = injectableDef !== null ? injectableDef.factory : getFactoryDef(token);
8503 if (factory !== null) {
8504 return factory;
8505 }
8506 // InjectionTokens should have an injectable def (ɵprov) and thus should be handled above.
8507 // If it's missing that, it's an error.
8508 if (token instanceof InjectionToken) {
8509 throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Token ${stringify(token)} is missing a ɵprov definition.`);
8510 }
8511 // Undecorated types can sometimes be created if they have no constructor arguments.
8512 if (token instanceof Function) {
8513 return getUndecoratedInjectableFactory(token);
8514 }
8515 // There was no way to resolve a factory for this token.
8516 throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && 'unreachable');
8517}
8518function getUndecoratedInjectableFactory(token) {
8519 // If the token has parameters then it has dependencies that we cannot resolve implicitly.
8520 const paramLength = token.length;
8521 if (paramLength > 0) {
8522 const args = newArray(paramLength, '?');
8523 throw new RuntimeError(204 /* RuntimeErrorCode.INVALID_INJECTION_TOKEN */, ngDevMode && `Can't resolve all parameters for ${stringify(token)}: (${args.join(', ')}).`);
8524 }
8525 // The constructor function appears to have no parameters.
8526 // This might be because it inherits from a super-class. In which case, use an injectable
8527 // def from an ancestor if there is one.
8528 // Otherwise this really is a simple class with no dependencies, so return a factory that
8529 // just instantiates the zero-arg constructor.
8530 const inheritedInjectableDef = getInheritedInjectableDef(token);
8531 if (inheritedInjectableDef !== null) {
8532 return () => inheritedInjectableDef.factory(token);
8533 }
8534 else {
8535 return () => new token();
8536 }
8537}
8538function providerToRecord(provider) {
8539 if (isValueProvider(provider)) {
8540 return makeRecord(undefined, provider.useValue);
8541 }
8542 else {
8543 const factory = providerToFactory(provider);
8544 return makeRecord(factory, NOT_YET);
8545 }
8546}
8547/**
8548 * Converts a `SingleProvider` into a factory function.
8549 *
8550 * @param provider provider to convert to factory
8551 */
8552function providerToFactory(provider, ngModuleType, providers) {
8553 let factory = undefined;
8554 if (ngDevMode && isEnvironmentProviders(provider)) {
8555 throwInvalidProviderError(undefined, providers, provider);
8556 }
8557 if (isTypeProvider(provider)) {
8558 const unwrappedProvider = resolveForwardRef(provider);
8559 return getFactoryDef(unwrappedProvider) || injectableDefOrInjectorDefFactory(unwrappedProvider);
8560 }
8561 else {
8562 if (isValueProvider(provider)) {
8563 factory = () => resolveForwardRef(provider.useValue);
8564 }
8565 else if (isFactoryProvider(provider)) {
8566 factory = () => provider.useFactory(...injectArgs(provider.deps || []));
8567 }
8568 else if (isExistingProvider(provider)) {
8569 factory = () => ɵɵinject(resolveForwardRef(provider.useExisting));
8570 }
8571 else {
8572 const classRef = resolveForwardRef(provider &&
8573 (provider.useClass || provider.provide));
8574 if (ngDevMode && !classRef) {
8575 throwInvalidProviderError(ngModuleType, providers, provider);
8576 }
8577 if (hasDeps(provider)) {
8578 factory = () => new (classRef)(...injectArgs(provider.deps));
8579 }
8580 else {
8581 return getFactoryDef(classRef) || injectableDefOrInjectorDefFactory(classRef);
8582 }
8583 }
8584 }
8585 return factory;
8586}
8587function makeRecord(factory, value, multi = false) {
8588 return {
8589 factory: factory,
8590 value: value,
8591 multi: multi ? [] : undefined,
8592 };
8593}
8594function hasDeps(value) {
8595 return !!value.deps;
8596}
8597function hasOnDestroy(value) {
8598 return value !== null && typeof value === 'object' &&
8599 typeof value.ngOnDestroy === 'function';
8600}
8601function couldBeInjectableType(value) {
8602 return (typeof value === 'function') ||
8603 (typeof value === 'object' && value instanceof InjectionToken);
8604}
8605function forEachSingleProvider(providers, fn) {
8606 for (const provider of providers) {
8607 if (Array.isArray(provider)) {
8608 forEachSingleProvider(provider, fn);
8609 }
8610 else if (provider && isEnvironmentProviders(provider)) {
8611 forEachSingleProvider(provider.ɵproviders, fn);
8612 }
8613 else {
8614 fn(provider);
8615 }
8616 }
8617}
8618
8619/**
8620 * Represents a component created by a `ComponentFactory`.
8621 * Provides access to the component instance and related objects,
8622 * and provides the means of destroying the instance.
8623 *
8624 * @publicApi
8625 */
8626class ComponentRef$1 {
8627}
8628/**
8629 * Base class for a factory that can create a component dynamically.
8630 * Instantiate a factory for a given type of component with `resolveComponentFactory()`.
8631 * Use the resulting `ComponentFactory.create()` method to create a component of that type.
8632 *
8633 * @see [Dynamic Components](guide/dynamic-component-loader)
8634 *
8635 * @publicApi
8636 *
8637 * @deprecated Angular no longer requires Component factories. Please use other APIs where
8638 * Component class can be used directly.
8639 */
8640class ComponentFactory$1 {
8641}
8642
8643function noComponentFactoryError(component) {
8644 const error = Error(`No component factory found for ${stringify(component)}. Did you add it to @NgModule.entryComponents?`);
8645 error[ERROR_COMPONENT] = component;
8646 return error;
8647}
8648const ERROR_COMPONENT = 'ngComponent';
8649function getComponent$1(error) {
8650 return error[ERROR_COMPONENT];
8651}
8652class _NullComponentFactoryResolver {
8653 resolveComponentFactory(component) {
8654 throw noComponentFactoryError(component);
8655 }
8656}
8657/**
8658 * A simple registry that maps `Components` to generated `ComponentFactory` classes
8659 * that can be used to create instances of components.
8660 * Use to obtain the factory for a given component type,
8661 * then use the factory's `create()` method to create a component of that type.
8662 *
8663 * Note: since v13, dynamic component creation via
8664 * [`ViewContainerRef.createComponent`](api/core/ViewContainerRef#createComponent)
8665 * does **not** require resolving component factory: component class can be used directly.
8666 *
8667 * @publicApi
8668 *
8669 * @deprecated Angular no longer requires Component factories. Please use other APIs where
8670 * Component class can be used directly.
8671 */
8672class ComponentFactoryResolver$1 {
8673}
8674ComponentFactoryResolver$1.NULL = ( /* @__PURE__ */new _NullComponentFactoryResolver());
8675
8676/**
8677 * Creates an ElementRef from the most recent node.
8678 *
8679 * @returns The ElementRef instance to use
8680 */
8681function injectElementRef() {
8682 return createElementRef(getCurrentTNode(), getLView());
8683}
8684/**
8685 * Creates an ElementRef given a node.
8686 *
8687 * @param tNode The node for which you'd like an ElementRef
8688 * @param lView The view to which the node belongs
8689 * @returns The ElementRef instance to use
8690 */
8691function createElementRef(tNode, lView) {
8692 return new ElementRef(getNativeByTNode(tNode, lView));
8693}
8694/**
8695 * A wrapper around a native element inside of a View.
8696 *
8697 * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
8698 * element.
8699 *
8700 * @security Permitting direct access to the DOM can make your application more vulnerable to
8701 * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
8702 * [Security Guide](https://g.co/ng/security).
8703 *
8704 * @publicApi
8705 */
8706// Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
8707// i.e. users have to ask for what they need. With that, we can build better analysis tools
8708// and could do better codegen in the future.
8709class ElementRef {
8710 constructor(nativeElement) {
8711 this.nativeElement = nativeElement;
8712 }
8713}
8714/**
8715 * @internal
8716 * @nocollapse
8717 */
8718ElementRef.__NG_ELEMENT_ID__ = injectElementRef;
8719/**
8720 * Unwraps `ElementRef` and return the `nativeElement`.
8721 *
8722 * @param value value to unwrap
8723 * @returns `nativeElement` if `ElementRef` otherwise returns value as is.
8724 */
8725function unwrapElementRef(value) {
8726 return value instanceof ElementRef ? value.nativeElement : value;
8727}
8728
8729/**
8730 * Creates and initializes a custom renderer that implements the `Renderer2` base class.
8731 *
8732 * @publicApi
8733 */
8734class RendererFactory2 {
8735}
8736/**
8737 * Extend this base class to implement custom rendering. By default, Angular
8738 * renders a template into DOM. You can use custom rendering to intercept
8739 * rendering calls, or to render to something other than DOM.
8740 *
8741 * Create your custom renderer using `RendererFactory2`.
8742 *
8743 * Use a custom renderer to bypass Angular's templating and
8744 * make custom UI changes that can't be expressed declaratively.
8745 * For example if you need to set a property or an attribute whose name is
8746 * not statically known, use the `setProperty()` or
8747 * `setAttribute()` method.
8748 *
8749 * @publicApi
8750 */
8751class Renderer2 {
8752}
8753/**
8754 * @internal
8755 * @nocollapse
8756 */
8757Renderer2.__NG_ELEMENT_ID__ = () => injectRenderer2();
8758/** Injects a Renderer2 for the current component. */
8759function injectRenderer2() {
8760 // We need the Renderer to be based on the component that it's being injected into, however since
8761 // DI happens before we've entered its view, `getLView` will return the parent view instead.
8762 const lView = getLView();
8763 const tNode = getCurrentTNode();
8764 const nodeAtIndex = getComponentLViewByIndex(tNode.index, lView);
8765 return (isLView(nodeAtIndex) ? nodeAtIndex : lView)[RENDERER];
8766}
8767
8768/**
8769 * Sanitizer is used by the views to sanitize potentially dangerous values.
8770 *
8771 * @publicApi
8772 */
8773class Sanitizer {
8774}
8775/** @nocollapse */
8776Sanitizer.ɵprov = ɵɵdefineInjectable({
8777 token: Sanitizer,
8778 providedIn: 'root',
8779 factory: () => null,
8780});
8781
8782/**
8783 * @description Represents the version of Angular
8784 *
8785 * @publicApi
8786 */
8787class Version {
8788 constructor(full) {
8789 this.full = full;
8790 this.major = full.split('.')[0];
8791 this.minor = full.split('.')[1];
8792 this.patch = full.split('.').slice(2).join('.');
8793 }
8794}
8795/**
8796 * @publicApi
8797 */
8798const VERSION = new Version('15.1.5');
8799
8800// This default value is when checking the hierarchy for a token.
8801//
8802// It means both:
8803// - the token is not provided by the current injector,
8804// - only the element injectors should be checked (ie do not check module injectors
8805//
8806// mod1
8807// /
8808// el1 mod2
8809// \ /
8810// el2
8811//
8812// When requesting el2.injector.get(token), we should check in the following order and return the
8813// first found value:
8814// - el2.injector.get(token, default)
8815// - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
8816// - mod2.injector.get(token, default)
8817const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
8818
8819const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
8820function wrappedError(message, originalError) {
8821 const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
8822 const error = Error(msg);
8823 error[ERROR_ORIGINAL_ERROR] = originalError;
8824 return error;
8825}
8826function getOriginalError(error) {
8827 return error[ERROR_ORIGINAL_ERROR];
8828}
8829
8830/**
8831 * Provides a hook for centralized exception handling.
8832 *
8833 * The default implementation of `ErrorHandler` prints error messages to the `console`. To
8834 * intercept error handling, write a custom exception handler that replaces this default as
8835 * appropriate for your app.
8836 *
8837 * @usageNotes
8838 * ### Example
8839 *
8840 * ```
8841 * class MyErrorHandler implements ErrorHandler {
8842 * handleError(error) {
8843 * // do something with the exception
8844 * }
8845 * }
8846 *
8847 * @NgModule({
8848 * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
8849 * })
8850 * class MyModule {}
8851 * ```
8852 *
8853 * @publicApi
8854 */
8855class ErrorHandler {
8856 constructor() {
8857 /**
8858 * @internal
8859 */
8860 this._console = console;
8861 }
8862 handleError(error) {
8863 const originalError = this._findOriginalError(error);
8864 this._console.error('ERROR', error);
8865 if (originalError) {
8866 this._console.error('ORIGINAL ERROR', originalError);
8867 }
8868 }
8869 /** @internal */
8870 _findOriginalError(error) {
8871 let e = error && getOriginalError(error);
8872 while (e && getOriginalError(e)) {
8873 e = getOriginalError(e);
8874 }
8875 return e || null;
8876 }
8877}
8878
8879function normalizeDebugBindingName(name) {
8880 // Attribute names with `$` (eg `x-y$`) are valid per spec, but unsupported by some browsers
8881 name = camelCaseToDashCase(name.replace(/[$@]/g, '_'));
8882 return `ng-reflect-${name}`;
8883}
8884const CAMEL_CASE_REGEXP = /([A-Z])/g;
8885function camelCaseToDashCase(input) {
8886 return input.replace(CAMEL_CASE_REGEXP, (...m) => '-' + m[1].toLowerCase());
8887}
8888function normalizeDebugBindingValue(value) {
8889 try {
8890 // Limit the size of the value as otherwise the DOM just gets polluted.
8891 return value != null ? value.toString().slice(0, 30) : value;
8892 }
8893 catch (e) {
8894 return '[ERROR] Exception while trying to serialize the value';
8895 }
8896}
8897
8898/** Verifies that a given type is a Standalone Component. */
8899function assertStandaloneComponentType(type) {
8900 assertComponentDef(type);
8901 const componentDef = getComponentDef$1(type);
8902 if (!componentDef.standalone) {
8903 throw new RuntimeError(907 /* RuntimeErrorCode.TYPE_IS_NOT_STANDALONE */, `The ${stringifyForError(type)} component is not marked as standalone, ` +
8904 `but Angular expects to have a standalone component here. ` +
8905 `Please make sure the ${stringifyForError(type)} component has ` +
8906 `the \`standalone: true\` flag in the decorator.`);
8907 }
8908}
8909/** Verifies whether a given type is a component */
8910function assertComponentDef(type) {
8911 if (!getComponentDef$1(type)) {
8912 throw new RuntimeError(906 /* RuntimeErrorCode.MISSING_GENERATED_DEF */, `The ${stringifyForError(type)} is not an Angular component, ` +
8913 `make sure it has the \`@Component\` decorator.`);
8914 }
8915}
8916/** Called when there are multiple component selectors that match a given node */
8917function throwMultipleComponentError(tNode, first, second) {
8918 throw new RuntimeError(-300 /* RuntimeErrorCode.MULTIPLE_COMPONENTS_MATCH */, `Multiple components match node with tagname ${tNode.value}: ` +
8919 `${stringifyForError(first)} and ` +
8920 `${stringifyForError(second)}`);
8921}
8922/** Throws an ExpressionChangedAfterChecked error if checkNoChanges mode is on. */
8923function throwErrorIfNoChangesMode(creationMode, oldValue, currValue, propName) {
8924 const field = propName ? ` for '${propName}'` : '';
8925 let msg = `ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value${field}: '${oldValue}'. Current value: '${currValue}'.`;
8926 if (creationMode) {
8927 msg +=
8928 ` It seems like the view has been created after its parent and its children have been dirty checked.` +
8929 ` Has it been created in a change detection hook?`;
8930 }
8931 throw new RuntimeError(-100 /* RuntimeErrorCode.EXPRESSION_CHANGED_AFTER_CHECKED */, msg);
8932}
8933function constructDetailsForInterpolation(lView, rootIndex, expressionIndex, meta, changedValue) {
8934 const [propName, prefix, ...chunks] = meta.split(INTERPOLATION_DELIMITER);
8935 let oldValue = prefix, newValue = prefix;
8936 for (let i = 0; i < chunks.length; i++) {
8937 const slotIdx = rootIndex + i;
8938 oldValue += `${lView[slotIdx]}${chunks[i]}`;
8939 newValue += `${slotIdx === expressionIndex ? changedValue : lView[slotIdx]}${chunks[i]}`;
8940 }
8941 return { propName, oldValue, newValue };
8942}
8943/**
8944 * Constructs an object that contains details for the ExpressionChangedAfterItHasBeenCheckedError:
8945 * - property name (for property bindings or interpolations)
8946 * - old and new values, enriched using information from metadata
8947 *
8948 * More information on the metadata storage format can be found in `storePropertyBindingMetadata`
8949 * function description.
8950 */
8951function getExpressionChangedErrorDetails(lView, bindingIndex, oldValue, newValue) {
8952 const tData = lView[TVIEW].data;
8953 const metadata = tData[bindingIndex];
8954 if (typeof metadata === 'string') {
8955 // metadata for property interpolation
8956 if (metadata.indexOf(INTERPOLATION_DELIMITER) > -1) {
8957 return constructDetailsForInterpolation(lView, bindingIndex, bindingIndex, metadata, newValue);
8958 }
8959 // metadata for property binding
8960 return { propName: metadata, oldValue, newValue };
8961 }
8962 // metadata is not available for this expression, check if this expression is a part of the
8963 // property interpolation by going from the current binding index left and look for a string that
8964 // contains INTERPOLATION_DELIMITER, the layout in tView.data for this case will look like this:
8965 // [..., 'id�Prefix � and � suffix', null, null, null, ...]
8966 if (metadata === null) {
8967 let idx = bindingIndex - 1;
8968 while (typeof tData[idx] !== 'string' && tData[idx + 1] === null) {
8969 idx--;
8970 }
8971 const meta = tData[idx];
8972 if (typeof meta === 'string') {
8973 const matches = meta.match(new RegExp(INTERPOLATION_DELIMITER, 'g'));
8974 // first interpolation delimiter separates property name from interpolation parts (in case of
8975 // property interpolations), so we subtract one from total number of found delimiters
8976 if (matches && (matches.length - 1) > bindingIndex - idx) {
8977 return constructDetailsForInterpolation(lView, idx, bindingIndex, meta, newValue);
8978 }
8979 }
8980 }
8981 return { propName: undefined, oldValue, newValue };
8982}
8983
8984/**
8985 * Returns an index of `classToSearch` in `className` taking token boundaries into account.
8986 *
8987 * `classIndexOf('AB A', 'A', 0)` will be 3 (not 0 since `AB!==A`)
8988 *
8989 * @param className A string containing classes (whitespace separated)
8990 * @param classToSearch A class name to locate
8991 * @param startingIndex Starting location of search
8992 * @returns an index of the located class (or -1 if not found)
8993 */
8994function classIndexOf(className, classToSearch, startingIndex) {
8995 ngDevMode && assertNotEqual(classToSearch, '', 'can not look for "" string.');
8996 let end = className.length;
8997 while (true) {
8998 const foundIndex = className.indexOf(classToSearch, startingIndex);
8999 if (foundIndex === -1)
9000 return foundIndex;
9001 if (foundIndex === 0 || className.charCodeAt(foundIndex - 1) <= 32 /* CharCode.SPACE */) {
9002 // Ensure that it has leading whitespace
9003 const length = classToSearch.length;
9004 if (foundIndex + length === end ||
9005 className.charCodeAt(foundIndex + length) <= 32 /* CharCode.SPACE */) {
9006 // Ensure that it has trailing whitespace
9007 return foundIndex;
9008 }
9009 }
9010 // False positive, keep searching from where we left off.
9011 startingIndex = foundIndex + 1;
9012 }
9013}
9014
9015const NG_TEMPLATE_SELECTOR = 'ng-template';
9016/**
9017 * Search the `TAttributes` to see if it contains `cssClassToMatch` (case insensitive)
9018 *
9019 * @param attrs `TAttributes` to search through.
9020 * @param cssClassToMatch class to match (lowercase)
9021 * @param isProjectionMode Whether or not class matching should look into the attribute `class` in
9022 * addition to the `AttributeMarker.Classes`.
9023 */
9024function isCssClassMatching(attrs, cssClassToMatch, isProjectionMode) {
9025 // TODO(misko): The fact that this function needs to know about `isProjectionMode` seems suspect.
9026 // It is strange to me that sometimes the class information comes in form of `class` attribute
9027 // and sometimes in form of `AttributeMarker.Classes`. Some investigation is needed to determine
9028 // if that is the right behavior.
9029 ngDevMode &&
9030 assertEqual(cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
9031 let i = 0;
9032 while (i < attrs.length) {
9033 let item = attrs[i++];
9034 if (isProjectionMode && item === 'class') {
9035 item = attrs[i];
9036 if (classIndexOf(item.toLowerCase(), cssClassToMatch, 0) !== -1) {
9037 return true;
9038 }
9039 }
9040 else if (item === 1 /* AttributeMarker.Classes */) {
9041 // We found the classes section. Start searching for the class.
9042 while (i < attrs.length && typeof (item = attrs[i++]) == 'string') {
9043 // while we have strings
9044 if (item.toLowerCase() === cssClassToMatch)
9045 return true;
9046 }
9047 return false;
9048 }
9049 }
9050 return false;
9051}
9052/**
9053 * Checks whether the `tNode` represents an inline template (e.g. `*ngFor`).
9054 *
9055 * @param tNode current TNode
9056 */
9057function isInlineTemplate(tNode) {
9058 return tNode.type === 4 /* TNodeType.Container */ && tNode.value !== NG_TEMPLATE_SELECTOR;
9059}
9060/**
9061 * Function that checks whether a given tNode matches tag-based selector and has a valid type.
9062 *
9063 * Matching can be performed in 2 modes: projection mode (when we project nodes) and regular
9064 * directive matching mode:
9065 * - in the "directive matching" mode we do _not_ take TContainer's tagName into account if it is
9066 * different from NG_TEMPLATE_SELECTOR (value different from NG_TEMPLATE_SELECTOR indicates that a
9067 * tag name was extracted from * syntax so we would match the same directive twice);
9068 * - in the "projection" mode, we use a tag name potentially extracted from the * syntax processing
9069 * (applicable to TNodeType.Container only).
9070 */
9071function hasTagAndTypeMatch(tNode, currentSelector, isProjectionMode) {
9072 const tagNameToCompare = tNode.type === 4 /* TNodeType.Container */ && !isProjectionMode ? NG_TEMPLATE_SELECTOR : tNode.value;
9073 return currentSelector === tagNameToCompare;
9074}
9075/**
9076 * A utility function to match an Ivy node static data against a simple CSS selector
9077 *
9078 * @param node static data of the node to match
9079 * @param selector The selector to try matching against the node.
9080 * @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing
9081 * directive matching.
9082 * @returns true if node matches the selector.
9083 */
9084function isNodeMatchingSelector(tNode, selector, isProjectionMode) {
9085 ngDevMode && assertDefined(selector[0], 'Selector should have a tag name');
9086 let mode = 4 /* SelectorFlags.ELEMENT */;
9087 const nodeAttrs = tNode.attrs || [];
9088 // Find the index of first attribute that has no value, only a name.
9089 const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs);
9090 // When processing ":not" selectors, we skip to the next ":not" if the
9091 // current one doesn't match
9092 let skipToNextSelector = false;
9093 for (let i = 0; i < selector.length; i++) {
9094 const current = selector[i];
9095 if (typeof current === 'number') {
9096 // If we finish processing a :not selector and it hasn't failed, return false
9097 if (!skipToNextSelector && !isPositive(mode) && !isPositive(current)) {
9098 return false;
9099 }
9100 // If we are skipping to the next :not() and this mode flag is positive,
9101 // it's a part of the current :not() selector, and we should keep skipping
9102 if (skipToNextSelector && isPositive(current))
9103 continue;
9104 skipToNextSelector = false;
9105 mode = current | (mode & 1 /* SelectorFlags.NOT */);
9106 continue;
9107 }
9108 if (skipToNextSelector)
9109 continue;
9110 if (mode & 4 /* SelectorFlags.ELEMENT */) {
9111 mode = 2 /* SelectorFlags.ATTRIBUTE */ | mode & 1 /* SelectorFlags.NOT */;
9112 if (current !== '' && !hasTagAndTypeMatch(tNode, current, isProjectionMode) ||
9113 current === '' && selector.length === 1) {
9114 if (isPositive(mode))
9115 return false;
9116 skipToNextSelector = true;
9117 }
9118 }
9119 else {
9120 const selectorAttrValue = mode & 8 /* SelectorFlags.CLASS */ ? current : selector[++i];
9121 // special case for matching against classes when a tNode has been instantiated with
9122 // class and style values as separate attribute values (e.g. ['title', CLASS, 'foo'])
9123 if ((mode & 8 /* SelectorFlags.CLASS */) && tNode.attrs !== null) {
9124 if (!isCssClassMatching(tNode.attrs, selectorAttrValue, isProjectionMode)) {
9125 if (isPositive(mode))
9126 return false;
9127 skipToNextSelector = true;
9128 }
9129 continue;
9130 }
9131 const attrName = (mode & 8 /* SelectorFlags.CLASS */) ? 'class' : current;
9132 const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate(tNode), isProjectionMode);
9133 if (attrIndexInNode === -1) {
9134 if (isPositive(mode))
9135 return false;
9136 skipToNextSelector = true;
9137 continue;
9138 }
9139 if (selectorAttrValue !== '') {
9140 let nodeAttrValue;
9141 if (attrIndexInNode > nameOnlyMarkerIdx) {
9142 nodeAttrValue = '';
9143 }
9144 else {
9145 ngDevMode &&
9146 assertNotEqual(nodeAttrs[attrIndexInNode], 0 /* AttributeMarker.NamespaceURI */, 'We do not match directives on namespaced attributes');
9147 // we lowercase the attribute value to be able to match
9148 // selectors without case-sensitivity
9149 // (selectors are already in lowercase when generated)
9150 nodeAttrValue = nodeAttrs[attrIndexInNode + 1].toLowerCase();
9151 }
9152 const compareAgainstClassName = mode & 8 /* SelectorFlags.CLASS */ ? nodeAttrValue : null;
9153 if (compareAgainstClassName &&
9154 classIndexOf(compareAgainstClassName, selectorAttrValue, 0) !== -1 ||
9155 mode & 2 /* SelectorFlags.ATTRIBUTE */ && selectorAttrValue !== nodeAttrValue) {
9156 if (isPositive(mode))
9157 return false;
9158 skipToNextSelector = true;
9159 }
9160 }
9161 }
9162 }
9163 return isPositive(mode) || skipToNextSelector;
9164}
9165function isPositive(mode) {
9166 return (mode & 1 /* SelectorFlags.NOT */) === 0;
9167}
9168/**
9169 * Examines the attribute's definition array for a node to find the index of the
9170 * attribute that matches the given `name`.
9171 *
9172 * NOTE: This will not match namespaced attributes.
9173 *
9174 * Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`.
9175 * The following table summarizes which types of attributes we attempt to match:
9176 *
9177 * ===========================================================================================================
9178 * Modes | Normal Attributes | Bindings Attributes | Template Attributes | I18n
9179 * Attributes
9180 * ===========================================================================================================
9181 * Inline + Projection | YES | YES | NO | YES
9182 * -----------------------------------------------------------------------------------------------------------
9183 * Inline + Directive | NO | NO | YES | NO
9184 * -----------------------------------------------------------------------------------------------------------
9185 * Non-inline + Projection | YES | YES | NO | YES
9186 * -----------------------------------------------------------------------------------------------------------
9187 * Non-inline + Directive | YES | YES | NO | YES
9188 * ===========================================================================================================
9189 *
9190 * @param name the name of the attribute to find
9191 * @param attrs the attribute array to examine
9192 * @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`)
9193 * rather than a manually expanded template node (e.g `<ng-template>`).
9194 * @param isProjectionMode true if we are matching against content projection otherwise we are
9195 * matching against directives.
9196 */
9197function findAttrIndexInNode(name, attrs, isInlineTemplate, isProjectionMode) {
9198 if (attrs === null)
9199 return -1;
9200 let i = 0;
9201 if (isProjectionMode || !isInlineTemplate) {
9202 let bindingsMode = false;
9203 while (i < attrs.length) {
9204 const maybeAttrName = attrs[i];
9205 if (maybeAttrName === name) {
9206 return i;
9207 }
9208 else if (maybeAttrName === 3 /* AttributeMarker.Bindings */ || maybeAttrName === 6 /* AttributeMarker.I18n */) {
9209 bindingsMode = true;
9210 }
9211 else if (maybeAttrName === 1 /* AttributeMarker.Classes */ || maybeAttrName === 2 /* AttributeMarker.Styles */) {
9212 let value = attrs[++i];
9213 // We should skip classes here because we have a separate mechanism for
9214 // matching classes in projection mode.
9215 while (typeof value === 'string') {
9216 value = attrs[++i];
9217 }
9218 continue;
9219 }
9220 else if (maybeAttrName === 4 /* AttributeMarker.Template */) {
9221 // We do not care about Template attributes in this scenario.
9222 break;
9223 }
9224 else if (maybeAttrName === 0 /* AttributeMarker.NamespaceURI */) {
9225 // Skip the whole namespaced attribute and value. This is by design.
9226 i += 4;
9227 continue;
9228 }
9229 // In binding mode there are only names, rather than name-value pairs.
9230 i += bindingsMode ? 1 : 2;
9231 }
9232 // We did not match the attribute
9233 return -1;
9234 }
9235 else {
9236 return matchTemplateAttribute(attrs, name);
9237 }
9238}
9239function isNodeMatchingSelectorList(tNode, selector, isProjectionMode = false) {
9240 for (let i = 0; i < selector.length; i++) {
9241 if (isNodeMatchingSelector(tNode, selector[i], isProjectionMode)) {
9242 return true;
9243 }
9244 }
9245 return false;
9246}
9247function getProjectAsAttrValue(tNode) {
9248 const nodeAttrs = tNode.attrs;
9249 if (nodeAttrs != null) {
9250 const ngProjectAsAttrIdx = nodeAttrs.indexOf(5 /* AttributeMarker.ProjectAs */);
9251 // only check for ngProjectAs in attribute names, don't accidentally match attribute's value
9252 // (attribute names are stored at even indexes)
9253 if ((ngProjectAsAttrIdx & 1) === 0) {
9254 return nodeAttrs[ngProjectAsAttrIdx + 1];
9255 }
9256 }
9257 return null;
9258}
9259function getNameOnlyMarkerIndex(nodeAttrs) {
9260 for (let i = 0; i < nodeAttrs.length; i++) {
9261 const nodeAttr = nodeAttrs[i];
9262 if (isNameOnlyAttributeMarker(nodeAttr)) {
9263 return i;
9264 }
9265 }
9266 return nodeAttrs.length;
9267}
9268function matchTemplateAttribute(attrs, name) {
9269 let i = attrs.indexOf(4 /* AttributeMarker.Template */);
9270 if (i > -1) {
9271 i++;
9272 while (i < attrs.length) {
9273 const attr = attrs[i];
9274 // Return in case we checked all template attrs and are switching to the next section in the
9275 // attrs array (that starts with a number that represents an attribute marker).
9276 if (typeof attr === 'number')
9277 return -1;
9278 if (attr === name)
9279 return i;
9280 i++;
9281 }
9282 }
9283 return -1;
9284}
9285/**
9286 * Checks whether a selector is inside a CssSelectorList
9287 * @param selector Selector to be checked.
9288 * @param list List in which to look for the selector.
9289 */
9290function isSelectorInSelectorList(selector, list) {
9291 selectorListLoop: for (let i = 0; i < list.length; i++) {
9292 const currentSelectorInList = list[i];
9293 if (selector.length !== currentSelectorInList.length) {
9294 continue;
9295 }
9296 for (let j = 0; j < selector.length; j++) {
9297 if (selector[j] !== currentSelectorInList[j]) {
9298 continue selectorListLoop;
9299 }
9300 }
9301 return true;
9302 }
9303 return false;
9304}
9305function maybeWrapInNotSelector(isNegativeMode, chunk) {
9306 return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
9307}
9308function stringifyCSSSelector(selector) {
9309 let result = selector[0];
9310 let i = 1;
9311 let mode = 2 /* SelectorFlags.ATTRIBUTE */;
9312 let currentChunk = '';
9313 let isNegativeMode = false;
9314 while (i < selector.length) {
9315 let valueOrMarker = selector[i];
9316 if (typeof valueOrMarker === 'string') {
9317 if (mode & 2 /* SelectorFlags.ATTRIBUTE */) {
9318 const attrValue = selector[++i];
9319 currentChunk +=
9320 '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
9321 }
9322 else if (mode & 8 /* SelectorFlags.CLASS */) {
9323 currentChunk += '.' + valueOrMarker;
9324 }
9325 else if (mode & 4 /* SelectorFlags.ELEMENT */) {
9326 currentChunk += ' ' + valueOrMarker;
9327 }
9328 }
9329 else {
9330 //
9331 // Append current chunk to the final result in case we come across SelectorFlag, which
9332 // indicates that the previous section of a selector is over. We need to accumulate content
9333 // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
9334 // ```
9335 // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
9336 // ```
9337 // should be transformed to `.classA :not(.classB .classC)`.
9338 //
9339 // Note: for negative selector part, we accumulate content between flags until we find the
9340 // next negative flag. This is needed to support a case where `:not()` rule contains more than
9341 // one chunk, e.g. the following selector:
9342 // ```
9343 // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
9344 // ```
9345 // should be stringified to `:not(p.foo) :not(.bar)`
9346 //
9347 if (currentChunk !== '' && !isPositive(valueOrMarker)) {
9348 result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
9349 currentChunk = '';
9350 }
9351 mode = valueOrMarker;
9352 // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
9353 // mode is maintained for remaining chunks of a selector.
9354 isNegativeMode = isNegativeMode || !isPositive(mode);
9355 }
9356 i++;
9357 }
9358 if (currentChunk !== '') {
9359 result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
9360 }
9361 return result;
9362}
9363/**
9364 * Generates string representation of CSS selector in parsed form.
9365 *
9366 * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
9367 * additional parsing at runtime (for example, for directive matching). However in some cases (for
9368 * example, while bootstrapping a component), a string version of the selector is required to query
9369 * for the host element on the page. This function takes the parsed form of a selector and returns
9370 * its string representation.
9371 *
9372 * @param selectorList selector in parsed form
9373 * @returns string representation of a given selector
9374 */
9375function stringifyCSSSelectorList(selectorList) {
9376 return selectorList.map(stringifyCSSSelector).join(',');
9377}
9378/**
9379 * Extracts attributes and classes information from a given CSS selector.
9380 *
9381 * This function is used while creating a component dynamically. In this case, the host element
9382 * (that is created dynamically) should contain attributes and classes specified in component's CSS
9383 * selector.
9384 *
9385 * @param selector CSS selector in parsed form (in a form of array)
9386 * @returns object with `attrs` and `classes` fields that contain extracted information
9387 */
9388function extractAttrsAndClassesFromSelector(selector) {
9389 const attrs = [];
9390 const classes = [];
9391 let i = 1;
9392 let mode = 2 /* SelectorFlags.ATTRIBUTE */;
9393 while (i < selector.length) {
9394 let valueOrMarker = selector[i];
9395 if (typeof valueOrMarker === 'string') {
9396 if (mode === 2 /* SelectorFlags.ATTRIBUTE */) {
9397 if (valueOrMarker !== '') {
9398 attrs.push(valueOrMarker, selector[++i]);
9399 }
9400 }
9401 else if (mode === 8 /* SelectorFlags.CLASS */) {
9402 classes.push(valueOrMarker);
9403 }
9404 }
9405 else {
9406 // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
9407 // mode is maintained for remaining chunks of a selector. Since attributes and classes are
9408 // extracted only for "positive" part of the selector, we can stop here.
9409 if (!isPositive(mode))
9410 break;
9411 mode = valueOrMarker;
9412 }
9413 i++;
9414 }
9415 return { attrs, classes };
9416}
9417
9418/** A special value which designates that a value has not changed. */
9419const NO_CHANGE = (typeof ngDevMode === 'undefined' || ngDevMode) ? { __brand__: 'NO_CHANGE' } : {};
9420
9421/**
9422 * Advances to an element for later binding instructions.
9423 *
9424 * Used in conjunction with instructions like {@link property} to act on elements with specified
9425 * indices, for example those created with {@link element} or {@link elementStart}.
9426 *
9427 * ```ts
9428 * (rf: RenderFlags, ctx: any) => {
9429 * if (rf & 1) {
9430 * text(0, 'Hello');
9431 * text(1, 'Goodbye')
9432 * element(2, 'div');
9433 * }
9434 * if (rf & 2) {
9435 * advance(2); // Advance twice to the <div>.
9436 * property('title', 'test');
9437 * }
9438 * }
9439 * ```
9440 * @param delta Number of elements to advance forwards by.
9441 *
9442 * @codeGenApi
9443 */
9444function ɵɵadvance(delta) {
9445 ngDevMode && assertGreaterThan(delta, 0, 'Can only advance forward');
9446 selectIndexInternal(getTView(), getLView(), getSelectedIndex() + delta, !!ngDevMode && isInCheckNoChangesMode());
9447}
9448function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
9449 ngDevMode && assertIndexInDeclRange(lView, index);
9450 // Flush the initial hooks for elements in the view that have been added up to this point.
9451 // PERF WARNING: do NOT extract this to a separate function without running benchmarks
9452 if (!checkNoChangesMode) {
9453 const hooksInitPhaseCompleted = (lView[FLAGS] & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
9454 if (hooksInitPhaseCompleted) {
9455 const preOrderCheckHooks = tView.preOrderCheckHooks;
9456 if (preOrderCheckHooks !== null) {
9457 executeCheckHooks(lView, preOrderCheckHooks, index);
9458 }
9459 }
9460 else {
9461 const preOrderHooks = tView.preOrderHooks;
9462 if (preOrderHooks !== null) {
9463 executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, index);
9464 }
9465 }
9466 }
9467 // We must set the selected index *after* running the hooks, because hooks may have side-effects
9468 // that cause other template functions to run, thus updating the selected index, which is global
9469 // state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
9470 // will be altered by the time we leave the `ɵɵadvance` instruction.
9471 setSelectedIndex(index);
9472}
9473
9474/**
9475 * A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
9476 *
9477 * This should be kept up to date with the public exports of @angular/core.
9478 */
9479const angularCoreDiEnv = {
9480 'ɵɵdefineInjectable': ɵɵdefineInjectable,
9481 'ɵɵdefineInjector': ɵɵdefineInjector,
9482 'ɵɵinject': ɵɵinject,
9483 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep,
9484 'resolveForwardRef': resolveForwardRef,
9485};
9486
9487/**
9488 * Compile an Angular injectable according to its `Injectable` metadata, and patch the resulting
9489 * injectable def (`ɵprov`) onto the injectable type.
9490 */
9491function compileInjectable(type, meta) {
9492 let ngInjectableDef = null;
9493 let ngFactoryDef = null;
9494 // if NG_PROV_DEF is already defined on this class then don't overwrite it
9495 if (!type.hasOwnProperty(NG_PROV_DEF)) {
9496 Object.defineProperty(type, NG_PROV_DEF, {
9497 get: () => {
9498 if (ngInjectableDef === null) {
9499 const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type });
9500 ngInjectableDef = compiler.compileInjectable(angularCoreDiEnv, `ng:///${type.name}/ɵprov.js`, getInjectableMetadata(type, meta));
9501 }
9502 return ngInjectableDef;
9503 },
9504 });
9505 }
9506 // if NG_FACTORY_DEF is already defined on this class then don't overwrite it
9507 if (!type.hasOwnProperty(NG_FACTORY_DEF)) {
9508 Object.defineProperty(type, NG_FACTORY_DEF, {
9509 get: () => {
9510 if (ngFactoryDef === null) {
9511 const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'injectable', type });
9512 ngFactoryDef = compiler.compileFactory(angularCoreDiEnv, `ng:///${type.name}/ɵfac.js`, {
9513 name: type.name,
9514 type,
9515 typeArgumentCount: 0,
9516 deps: reflectDependencies(type),
9517 target: compiler.FactoryTarget.Injectable
9518 });
9519 }
9520 return ngFactoryDef;
9521 },
9522 // Leave this configurable so that the factories from directives or pipes can take precedence.
9523 configurable: true
9524 });
9525 }
9526}
9527const USE_VALUE = getClosureSafeProperty({ provide: String, useValue: getClosureSafeProperty });
9528function isUseClassProvider(meta) {
9529 return meta.useClass !== undefined;
9530}
9531function isUseValueProvider(meta) {
9532 return USE_VALUE in meta;
9533}
9534function isUseFactoryProvider(meta) {
9535 return meta.useFactory !== undefined;
9536}
9537function isUseExistingProvider(meta) {
9538 return meta.useExisting !== undefined;
9539}
9540function getInjectableMetadata(type, srcMeta) {
9541 // Allow the compilation of a class with a `@Injectable()` decorator without parameters
9542 const meta = srcMeta || { providedIn: null };
9543 const compilerMeta = {
9544 name: type.name,
9545 type: type,
9546 typeArgumentCount: 0,
9547 providedIn: meta.providedIn,
9548 };
9549 if ((isUseClassProvider(meta) || isUseFactoryProvider(meta)) && meta.deps !== undefined) {
9550 compilerMeta.deps = convertDependencies(meta.deps);
9551 }
9552 // Check to see if the user explicitly provided a `useXxxx` property.
9553 if (isUseClassProvider(meta)) {
9554 compilerMeta.useClass = meta.useClass;
9555 }
9556 else if (isUseValueProvider(meta)) {
9557 compilerMeta.useValue = meta.useValue;
9558 }
9559 else if (isUseFactoryProvider(meta)) {
9560 compilerMeta.useFactory = meta.useFactory;
9561 }
9562 else if (isUseExistingProvider(meta)) {
9563 compilerMeta.useExisting = meta.useExisting;
9564 }
9565 return compilerMeta;
9566}
9567
9568/**
9569 * Injectable decorator and metadata.
9570 *
9571 * @Annotation
9572 * @publicApi
9573 */
9574const Injectable = makeDecorator('Injectable', undefined, undefined, undefined, (type, meta) => compileInjectable(type, meta));
9575
9576/**
9577 * Create a new `Injector` which is configured using a `defType` of `InjectorType<any>`s.
9578 *
9579 * @publicApi
9580 */
9581function createInjector(defType, parent = null, additionalProviders = null, name) {
9582 const injector = createInjectorWithoutInjectorInstances(defType, parent, additionalProviders, name);
9583 injector.resolveInjectorInitializers();
9584 return injector;
9585}
9586/**
9587 * Creates a new injector without eagerly resolving its injector types. Can be used in places
9588 * where resolving the injector types immediately can lead to an infinite loop. The injector types
9589 * should be resolved at a later point by calling `_resolveInjectorDefTypes`.
9590 */
9591function createInjectorWithoutInjectorInstances(defType, parent = null, additionalProviders = null, name, scopes = new Set()) {
9592 const providers = [
9593 additionalProviders || EMPTY_ARRAY,
9594 importProvidersFrom(defType),
9595 ];
9596 name = name || (typeof defType === 'object' ? undefined : stringify(defType));
9597 return new R3Injector(providers, parent || getNullInjector(), name || null, scopes);
9598}
9599
9600/**
9601 * Concrete injectors implement this interface. Injectors are configured
9602 * with [providers](guide/glossary#provider) that associate
9603 * dependencies of various types with [injection tokens](guide/glossary#di-token).
9604 *
9605 * @see ["DI Providers"](guide/dependency-injection-providers).
9606 * @see `StaticProvider`
9607 *
9608 * @usageNotes
9609 *
9610 * The following example creates a service injector instance.
9611 *
9612 * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
9613 *
9614 * ### Usage example
9615 *
9616 * {@example core/di/ts/injector_spec.ts region='Injector'}
9617 *
9618 * `Injector` returns itself when given `Injector` as a token:
9619 *
9620 * {@example core/di/ts/injector_spec.ts region='injectInjector'}
9621 *
9622 * @publicApi
9623 */
9624class Injector {
9625 static create(options, parent) {
9626 if (Array.isArray(options)) {
9627 return createInjector({ name: '' }, parent, options, '');
9628 }
9629 else {
9630 const name = options.name ?? '';
9631 return createInjector({ name }, options.parent, options.providers, name);
9632 }
9633 }
9634}
9635Injector.THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
9636Injector.NULL = ( /* @__PURE__ */new NullInjector());
9637/** @nocollapse */
9638Injector.ɵprov = ɵɵdefineInjectable({
9639 token: Injector,
9640 providedIn: 'any',
9641 factory: () => ɵɵinject(INJECTOR),
9642});
9643/**
9644 * @internal
9645 * @nocollapse
9646 */
9647Injector.__NG_ELEMENT_ID__ = -1 /* InjectorMarkers.Injector */;
9648
9649function findFirstClosedCycle(keys) {
9650 const res = [];
9651 for (let i = 0; i < keys.length; ++i) {
9652 if (res.indexOf(keys[i]) > -1) {
9653 res.push(keys[i]);
9654 return res;
9655 }
9656 res.push(keys[i]);
9657 }
9658 return res;
9659}
9660function constructResolvingPath(keys) {
9661 if (keys.length > 1) {
9662 const reversed = findFirstClosedCycle(keys.slice().reverse());
9663 const tokenStrs = reversed.map(k => stringify(k.token));
9664 return ' (' + tokenStrs.join(' -> ') + ')';
9665 }
9666 return '';
9667}
9668function injectionError(injector, key, constructResolvingMessage, originalError) {
9669 const keys = [key];
9670 const errMsg = constructResolvingMessage(keys);
9671 const error = (originalError ? wrappedError(errMsg, originalError) : Error(errMsg));
9672 error.addKey = addKey;
9673 error.keys = keys;
9674 error.injectors = [injector];
9675 error.constructResolvingMessage = constructResolvingMessage;
9676 error[ERROR_ORIGINAL_ERROR] = originalError;
9677 return error;
9678}
9679function addKey(injector, key) {
9680 this.injectors.push(injector);
9681 this.keys.push(key);
9682 // Note: This updated message won't be reflected in the `.stack` property
9683 this.message = this.constructResolvingMessage(this.keys);
9684}
9685/**
9686 * Thrown when trying to retrieve a dependency by key from {@link Injector}, but the
9687 * {@link Injector} does not have a {@link Provider} for the given key.
9688 *
9689 * @usageNotes
9690 * ### Example
9691 *
9692 * ```typescript
9693 * class A {
9694 * constructor(b:B) {}
9695 * }
9696 *
9697 * expect(() => Injector.resolveAndCreate([A])).toThrowError();
9698 * ```
9699 */
9700function noProviderError(injector, key) {
9701 return injectionError(injector, key, function (keys) {
9702 const first = stringify(keys[0].token);
9703 return `No provider for ${first}!${constructResolvingPath(keys)}`;
9704 });
9705}
9706/**
9707 * Thrown when dependencies form a cycle.
9708 *
9709 * @usageNotes
9710 * ### Example
9711 *
9712 * ```typescript
9713 * var injector = Injector.resolveAndCreate([
9714 * {provide: "one", useFactory: (two) => "two", deps: [[new Inject("two")]]},
9715 * {provide: "two", useFactory: (one) => "one", deps: [[new Inject("one")]]}
9716 * ]);
9717 *
9718 * expect(() => injector.get("one")).toThrowError();
9719 * ```
9720 *
9721 * Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
9722 */
9723function cyclicDependencyError(injector, key) {
9724 return injectionError(injector, key, function (keys) {
9725 return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
9726 });
9727}
9728/**
9729 * Thrown when a constructing type returns with an Error.
9730 *
9731 * The `InstantiationError` class contains the original error plus the dependency graph which caused
9732 * this object to be instantiated.
9733 *
9734 * @usageNotes
9735 * ### Example
9736 *
9737 * ```typescript
9738 * class A {
9739 * constructor() {
9740 * throw new Error('message');
9741 * }
9742 * }
9743 *
9744 * var injector = Injector.resolveAndCreate([A]);
9745
9746 * try {
9747 * injector.get(A);
9748 * } catch (e) {
9749 * expect(e instanceof InstantiationError).toBe(true);
9750 * expect(e.originalException.message).toEqual("message");
9751 * expect(e.originalStack).toBeDefined();
9752 * }
9753 * ```
9754 */
9755function instantiationError(injector, originalException, originalStack, key) {
9756 return injectionError(injector, key, function (keys) {
9757 const first = stringify(keys[0].token);
9758 return `${originalException.message}: Error during instantiation of ${first}!${constructResolvingPath(keys)}.`;
9759 }, originalException);
9760}
9761/**
9762 * Thrown when an object other then {@link Provider} (or `Type`) is passed to {@link Injector}
9763 * creation.
9764 *
9765 * @usageNotes
9766 * ### Example
9767 *
9768 * ```typescript
9769 * expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError();
9770 * ```
9771 */
9772function invalidProviderError(provider) {
9773 return Error(`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`);
9774}
9775/**
9776 * Thrown when the class has no annotation information.
9777 *
9778 * Lack of annotation information prevents the {@link Injector} from determining which dependencies
9779 * need to be injected into the constructor.
9780 *
9781 * @usageNotes
9782 * ### Example
9783 *
9784 * ```typescript
9785 * class A {
9786 * constructor(b) {}
9787 * }
9788 *
9789 * expect(() => Injector.resolveAndCreate([A])).toThrowError();
9790 * ```
9791 *
9792 * This error is also thrown when the class not marked with {@link Injectable} has parameter types.
9793 *
9794 * ```typescript
9795 * class B {}
9796 *
9797 * class A {
9798 * constructor(b:B) {} // no information about the parameter types of A is available at runtime.
9799 * }
9800 *
9801 * expect(() => Injector.resolveAndCreate([A,B])).toThrowError();
9802 * ```
9803 *
9804 */
9805function noAnnotationError(typeOrFunc, params) {
9806 const signature = [];
9807 for (let i = 0, ii = params.length; i < ii; i++) {
9808 const parameter = params[i];
9809 if (!parameter || parameter.length == 0) {
9810 signature.push('?');
9811 }
9812 else {
9813 signature.push(parameter.map(stringify).join(' '));
9814 }
9815 }
9816 return Error('Cannot resolve all parameters for \'' + stringify(typeOrFunc) + '\'(' +
9817 signature.join(', ') + '). ' +
9818 'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' +
9819 stringify(typeOrFunc) + '\' is decorated with Injectable.');
9820}
9821/**
9822 * Thrown when getting an object by index.
9823 *
9824 * @usageNotes
9825 * ### Example
9826 *
9827 * ```typescript
9828 * class A {}
9829 *
9830 * var injector = Injector.resolveAndCreate([A]);
9831 *
9832 * expect(() => injector.getAt(100)).toThrowError();
9833 * ```
9834 *
9835 */
9836function outOfBoundsError(index) {
9837 return Error(`Index ${index} is out-of-bounds.`);
9838}
9839// TODO: add a working example after alpha38 is released
9840/**
9841 * Thrown when a multi provider and a regular provider are bound to the same token.
9842 *
9843 * @usageNotes
9844 * ### Example
9845 *
9846 * ```typescript
9847 * expect(() => Injector.resolveAndCreate([
9848 * { provide: "Strings", useValue: "string1", multi: true},
9849 * { provide: "Strings", useValue: "string2", multi: false}
9850 * ])).toThrowError();
9851 * ```
9852 */
9853function mixingMultiProvidersWithRegularProvidersError(provider1, provider2) {
9854 return Error(`Cannot mix multi providers and regular providers, got: ${provider1} ${provider2}`);
9855}
9856
9857/**
9858 * A unique object used for retrieving items from the {@link ReflectiveInjector}.
9859 *
9860 * Keys have:
9861 * - a system-wide unique `id`.
9862 * - a `token`.
9863 *
9864 * `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows
9865 * the
9866 * injector to store created objects in a more efficient way.
9867 *
9868 * `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when
9869 * resolving
9870 * providers.
9871 *
9872 * @deprecated No replacement
9873 * @publicApi
9874 */
9875class ReflectiveKey {
9876 /**
9877 * Private
9878 */
9879 constructor(token, id) {
9880 this.token = token;
9881 this.id = id;
9882 if (!token) {
9883 throw new RuntimeError(208 /* RuntimeErrorCode.MISSING_INJECTION_TOKEN */, ngDevMode && 'Token must be defined!');
9884 }
9885 this.displayName = stringify(this.token);
9886 }
9887 /**
9888 * Retrieves a `Key` for a token.
9889 */
9890 static get(token) {
9891 return _globalKeyRegistry.get(resolveForwardRef(token));
9892 }
9893 /**
9894 * @returns the number of keys registered in the system.
9895 */
9896 static get numberOfKeys() {
9897 return _globalKeyRegistry.numberOfKeys;
9898 }
9899}
9900class KeyRegistry {
9901 constructor() {
9902 this._allKeys = new Map();
9903 }
9904 get(token) {
9905 if (token instanceof ReflectiveKey)
9906 return token;
9907 if (this._allKeys.has(token)) {
9908 return this._allKeys.get(token);
9909 }
9910 const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
9911 this._allKeys.set(token, newKey);
9912 return newKey;
9913 }
9914 get numberOfKeys() {
9915 return this._allKeys.size;
9916 }
9917}
9918const _globalKeyRegistry = new KeyRegistry();
9919
9920/**
9921 * `Dependency` is used by the framework to extend DI.
9922 * This is internal to Angular and should not be used directly.
9923 */
9924class ReflectiveDependency {
9925 constructor(key, optional, visibility) {
9926 this.key = key;
9927 this.optional = optional;
9928 this.visibility = visibility;
9929 }
9930 static fromKey(key) {
9931 return new ReflectiveDependency(key, false, null);
9932 }
9933}
9934const _EMPTY_LIST = [];
9935class ResolvedReflectiveProvider_ {
9936 constructor(key, resolvedFactories, multiProvider) {
9937 this.key = key;
9938 this.resolvedFactories = resolvedFactories;
9939 this.multiProvider = multiProvider;
9940 this.resolvedFactory = this.resolvedFactories[0];
9941 }
9942}
9943/**
9944 * An internal resolved representation of a factory function created by resolving `Provider`.
9945 * @publicApi
9946 */
9947class ResolvedReflectiveFactory {
9948 constructor(
9949 /**
9950 * Factory function which can return an instance of an object represented by a key.
9951 */
9952 factory,
9953 /**
9954 * Arguments (dependencies) to the `factory` function.
9955 */
9956 dependencies) {
9957 this.factory = factory;
9958 this.dependencies = dependencies;
9959 }
9960}
9961/**
9962 * Resolve a single provider.
9963 */
9964function resolveReflectiveFactory(provider) {
9965 let factoryFn;
9966 let resolvedDeps;
9967 if (provider.useClass) {
9968 const useClass = resolveForwardRef(provider.useClass);
9969 factoryFn = getReflect().factory(useClass);
9970 resolvedDeps = _dependenciesFor(useClass);
9971 }
9972 else if (provider.useExisting) {
9973 factoryFn = (aliasInstance) => aliasInstance;
9974 resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
9975 }
9976 else if (provider.useFactory) {
9977 factoryFn = provider.useFactory;
9978 resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
9979 }
9980 else {
9981 factoryFn = () => provider.useValue;
9982 resolvedDeps = _EMPTY_LIST;
9983 }
9984 return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
9985}
9986/**
9987 * Converts the `Provider` into `ResolvedProvider`.
9988 *
9989 * `Injector` internally only uses `ResolvedProvider`, `Provider` contains convenience provider
9990 * syntax.
9991 */
9992function resolveReflectiveProvider(provider) {
9993 return new ResolvedReflectiveProvider_(ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)], provider.multi || false);
9994}
9995/**
9996 * Resolve a list of Providers.
9997 */
9998function resolveReflectiveProviders(providers) {
9999 const normalized = _normalizeProviders(providers, []);
10000 const resolved = normalized.map(resolveReflectiveProvider);
10001 const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map());
10002 return Array.from(resolvedProviderMap.values());
10003}
10004/**
10005 * Merges a list of ResolvedProviders into a list where each key is contained exactly once and
10006 * multi providers have been merged.
10007 */
10008function mergeResolvedReflectiveProviders(providers, normalizedProvidersMap) {
10009 for (let i = 0; i < providers.length; i++) {
10010 const provider = providers[i];
10011 const existing = normalizedProvidersMap.get(provider.key.id);
10012 if (existing) {
10013 if (provider.multiProvider !== existing.multiProvider) {
10014 throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
10015 }
10016 if (provider.multiProvider) {
10017 for (let j = 0; j < provider.resolvedFactories.length; j++) {
10018 existing.resolvedFactories.push(provider.resolvedFactories[j]);
10019 }
10020 }
10021 else {
10022 normalizedProvidersMap.set(provider.key.id, provider);
10023 }
10024 }
10025 else {
10026 let resolvedProvider;
10027 if (provider.multiProvider) {
10028 resolvedProvider = new ResolvedReflectiveProvider_(provider.key, provider.resolvedFactories.slice(), provider.multiProvider);
10029 }
10030 else {
10031 resolvedProvider = provider;
10032 }
10033 normalizedProvidersMap.set(provider.key.id, resolvedProvider);
10034 }
10035 }
10036 return normalizedProvidersMap;
10037}
10038function _normalizeProviders(providers, res) {
10039 providers.forEach(b => {
10040 if (b instanceof Type) {
10041 res.push({ provide: b, useClass: b });
10042 }
10043 else if (b && typeof b == 'object' && b.provide !== undefined) {
10044 res.push(b);
10045 }
10046 else if (Array.isArray(b)) {
10047 _normalizeProviders(b, res);
10048 }
10049 else {
10050 throw invalidProviderError(b);
10051 }
10052 });
10053 return res;
10054}
10055function constructDependencies(typeOrFunc, dependencies) {
10056 if (!dependencies) {
10057 return _dependenciesFor(typeOrFunc);
10058 }
10059 else {
10060 const params = dependencies.map(t => [t]);
10061 return dependencies.map(t => _extractToken(typeOrFunc, t, params));
10062 }
10063}
10064function _dependenciesFor(typeOrFunc) {
10065 const params = getReflect().parameters(typeOrFunc);
10066 if (!params)
10067 return [];
10068 if (params.some(p => p == null)) {
10069 throw noAnnotationError(typeOrFunc, params);
10070 }
10071 return params.map(p => _extractToken(typeOrFunc, p, params));
10072}
10073function _extractToken(typeOrFunc, metadata, params) {
10074 let token = null;
10075 let optional = false;
10076 if (!Array.isArray(metadata)) {
10077 if (metadata instanceof Inject) {
10078 return _createDependency(metadata.token, optional, null);
10079 }
10080 else {
10081 return _createDependency(metadata, optional, null);
10082 }
10083 }
10084 let visibility = null;
10085 for (let i = 0; i < metadata.length; ++i) {
10086 const paramMetadata = metadata[i];
10087 if (paramMetadata instanceof Type) {
10088 token = paramMetadata;
10089 }
10090 else if (paramMetadata instanceof Inject) {
10091 token = paramMetadata.token;
10092 }
10093 else if (paramMetadata instanceof Optional) {
10094 optional = true;
10095 }
10096 else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
10097 visibility = paramMetadata;
10098 }
10099 else if (paramMetadata instanceof InjectionToken) {
10100 token = paramMetadata;
10101 }
10102 }
10103 token = resolveForwardRef(token);
10104 if (token != null) {
10105 return _createDependency(token, optional, visibility);
10106 }
10107 else {
10108 throw noAnnotationError(typeOrFunc, params);
10109 }
10110}
10111function _createDependency(token, optional, visibility) {
10112 return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
10113}
10114
10115// Threshold for the dynamic version
10116const UNDEFINED = {};
10117/**
10118 * A ReflectiveDependency injection container used for instantiating objects and resolving
10119 * dependencies.
10120 *
10121 * An `Injector` is a replacement for a `new` operator, which can automatically resolve the
10122 * constructor dependencies.
10123 *
10124 * In typical use, application code asks for the dependencies in the constructor and they are
10125 * resolved by the `Injector`.
10126 *
10127 * @usageNotes
10128 * ### Example
10129 *
10130 * The following example creates an `Injector` configured to create `Engine` and `Car`.
10131 *
10132 * ```typescript
10133 * @Injectable()
10134 * class Engine {
10135 * }
10136 *
10137 * @Injectable()
10138 * class Car {
10139 * constructor(public engine:Engine) {}
10140 * }
10141 *
10142 * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
10143 * var car = injector.get(Car);
10144 * expect(car instanceof Car).toBe(true);
10145 * expect(car.engine instanceof Engine).toBe(true);
10146 * ```
10147 *
10148 * Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
10149 * resolve all of the object's dependencies automatically.
10150 *
10151 * TODO: delete in v14.
10152 *
10153 * @deprecated from v5 - slow and brings in a lot of code, Use `Injector.create` instead.
10154 * @publicApi
10155 */
10156class ReflectiveInjector {
10157 /**
10158 * Turns an array of provider definitions into an array of resolved providers.
10159 *
10160 * A resolution is a process of flattening multiple nested arrays and converting individual
10161 * providers into an array of `ResolvedReflectiveProvider`s.
10162 *
10163 * @usageNotes
10164 * ### Example
10165 *
10166 * ```typescript
10167 * @Injectable()
10168 * class Engine {
10169 * }
10170 *
10171 * @Injectable()
10172 * class Car {
10173 * constructor(public engine:Engine) {}
10174 * }
10175 *
10176 * var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
10177 *
10178 * expect(providers.length).toEqual(2);
10179 *
10180 * expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
10181 * expect(providers[0].key.displayName).toBe("Car");
10182 * expect(providers[0].dependencies.length).toEqual(1);
10183 * expect(providers[0].factory).toBeDefined();
10184 *
10185 * expect(providers[1].key.displayName).toBe("Engine");
10186 * });
10187 * ```
10188 *
10189 */
10190 static resolve(providers) {
10191 return resolveReflectiveProviders(providers);
10192 }
10193 /**
10194 * Resolves an array of providers and creates an injector from those providers.
10195 *
10196 * The passed-in providers can be an array of `Type`, `Provider`,
10197 * or a recursive array of more providers.
10198 *
10199 * @usageNotes
10200 * ### Example
10201 *
10202 * ```typescript
10203 * @Injectable()
10204 * class Engine {
10205 * }
10206 *
10207 * @Injectable()
10208 * class Car {
10209 * constructor(public engine:Engine) {}
10210 * }
10211 *
10212 * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
10213 * expect(injector.get(Car) instanceof Car).toBe(true);
10214 * ```
10215 */
10216 static resolveAndCreate(providers, parent) {
10217 const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
10218 return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
10219 }
10220 /**
10221 * Creates an injector from previously resolved providers.
10222 *
10223 * This API is the recommended way to construct injectors in performance-sensitive parts.
10224 *
10225 * @usageNotes
10226 * ### Example
10227 *
10228 * ```typescript
10229 * @Injectable()
10230 * class Engine {
10231 * }
10232 *
10233 * @Injectable()
10234 * class Car {
10235 * constructor(public engine:Engine) {}
10236 * }
10237 *
10238 * var providers = ReflectiveInjector.resolve([Car, Engine]);
10239 * var injector = ReflectiveInjector.fromResolvedProviders(providers);
10240 * expect(injector.get(Car) instanceof Car).toBe(true);
10241 * ```
10242 */
10243 static fromResolvedProviders(providers, parent) {
10244 return new ReflectiveInjector_(providers, parent);
10245 }
10246}
10247class ReflectiveInjector_ {
10248 /**
10249 * Private
10250 */
10251 constructor(_providers, _parent) {
10252 /** @internal */
10253 this._constructionCounter = 0;
10254 this._providers = _providers;
10255 this.parent = _parent || null;
10256 const len = _providers.length;
10257 this.keyIds = [];
10258 this.objs = [];
10259 for (let i = 0; i < len; i++) {
10260 this.keyIds[i] = _providers[i].key.id;
10261 this.objs[i] = UNDEFINED;
10262 }
10263 }
10264 get(token, notFoundValue = THROW_IF_NOT_FOUND) {
10265 return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
10266 }
10267 resolveAndCreateChild(providers) {
10268 const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
10269 return this.createChildFromResolved(ResolvedReflectiveProviders);
10270 }
10271 createChildFromResolved(providers) {
10272 const inj = new ReflectiveInjector_(providers);
10273 inj.parent = this;
10274 return inj;
10275 }
10276 resolveAndInstantiate(provider) {
10277 return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]);
10278 }
10279 instantiateResolved(provider) {
10280 return this._instantiateProvider(provider);
10281 }
10282 getProviderAtIndex(index) {
10283 if (index < 0 || index >= this._providers.length) {
10284 throw outOfBoundsError(index);
10285 }
10286 return this._providers[index];
10287 }
10288 /** @internal */
10289 _new(provider) {
10290 if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
10291 throw cyclicDependencyError(this, provider.key);
10292 }
10293 return this._instantiateProvider(provider);
10294 }
10295 _getMaxNumberOfObjects() {
10296 return this.objs.length;
10297 }
10298 _instantiateProvider(provider) {
10299 if (provider.multiProvider) {
10300 const res = [];
10301 for (let i = 0; i < provider.resolvedFactories.length; ++i) {
10302 res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
10303 }
10304 return res;
10305 }
10306 else {
10307 return this._instantiate(provider, provider.resolvedFactories[0]);
10308 }
10309 }
10310 _instantiate(provider, ResolvedReflectiveFactory) {
10311 const factory = ResolvedReflectiveFactory.factory;
10312 let deps;
10313 try {
10314 deps =
10315 ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
10316 }
10317 catch (e) {
10318 if (e.addKey) {
10319 e.addKey(this, provider.key);
10320 }
10321 throw e;
10322 }
10323 let obj;
10324 try {
10325 obj = factory(...deps);
10326 }
10327 catch (e) {
10328 throw instantiationError(this, e, e.stack, provider.key);
10329 }
10330 return obj;
10331 }
10332 _getByReflectiveDependency(dep) {
10333 return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
10334 }
10335 _getByKey(key, visibility, notFoundValue) {
10336 if (key === ReflectiveInjector_.INJECTOR_KEY) {
10337 return this;
10338 }
10339 if (visibility instanceof Self) {
10340 return this._getByKeySelf(key, notFoundValue);
10341 }
10342 else {
10343 return this._getByKeyDefault(key, notFoundValue, visibility);
10344 }
10345 }
10346 _getObjByKeyId(keyId) {
10347 for (let i = 0; i < this.keyIds.length; i++) {
10348 if (this.keyIds[i] === keyId) {
10349 if (this.objs[i] === UNDEFINED) {
10350 this.objs[i] = this._new(this._providers[i]);
10351 }
10352 return this.objs[i];
10353 }
10354 }
10355 return UNDEFINED;
10356 }
10357 /** @internal */
10358 _throwOrNull(key, notFoundValue) {
10359 if (notFoundValue !== THROW_IF_NOT_FOUND) {
10360 return notFoundValue;
10361 }
10362 else {
10363 throw noProviderError(this, key);
10364 }
10365 }
10366 /** @internal */
10367 _getByKeySelf(key, notFoundValue) {
10368 const obj = this._getObjByKeyId(key.id);
10369 return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
10370 }
10371 /** @internal */
10372 _getByKeyDefault(key, notFoundValue, visibility) {
10373 let inj;
10374 if (visibility instanceof SkipSelf) {
10375 inj = this.parent;
10376 }
10377 else {
10378 inj = this;
10379 }
10380 while (inj instanceof ReflectiveInjector_) {
10381 const inj_ = inj;
10382 const obj = inj_._getObjByKeyId(key.id);
10383 if (obj !== UNDEFINED)
10384 return obj;
10385 inj = inj_.parent;
10386 }
10387 if (inj !== null) {
10388 return inj.get(key.token, notFoundValue);
10389 }
10390 else {
10391 return this._throwOrNull(key, notFoundValue);
10392 }
10393 }
10394 get displayName() {
10395 const providers = _mapProviders(this, (b) => ' "' + b.key.displayName + '" ')
10396 .join(', ');
10397 return `ReflectiveInjector(providers: [${providers}])`;
10398 }
10399 toString() {
10400 return this.displayName;
10401 }
10402}
10403ReflectiveInjector_.INJECTOR_KEY = ( /* @__PURE__ */ReflectiveKey.get(Injector));
10404function _mapProviders(injector, fn) {
10405 const res = [];
10406 for (let i = 0; i < injector._providers.length; ++i) {
10407 res[i] = fn(injector.getProviderAtIndex(i));
10408 }
10409 return res;
10410}
10411
10412/**
10413 * @module
10414 * @description
10415 * The `di` module provides dependency injection container services.
10416 */
10417
10418/**
10419 * This file should not be necessary because node resolution should just default to `./di/index`!
10420 *
10421 * However it does not seem to work and it breaks:
10422 * - //packages/animations/browser/test:test_web_chromium-local
10423 * - //packages/compiler-cli/test:extract_i18n
10424 * - //packages/compiler-cli/test:ngc
10425 * - //packages/compiler-cli/test:perform_watch
10426 * - //packages/compiler-cli/test/diagnostics:check_types
10427 * - //packages/compiler-cli/test/transformers:test
10428 * - //packages/compiler/test:test
10429 * - //tools/public_api_guard:core_api
10430 *
10431 * Remove this file once the above is solved or wait until `ngc` is deleted and then it should be
10432 * safe to delete this file.
10433 */
10434
10435function ɵɵdirectiveInject(token, flags = InjectFlags.Default) {
10436 const lView = getLView();
10437 // Fall back to inject() if view hasn't been created. This situation can happen in tests
10438 // if inject utilities are used before bootstrapping.
10439 if (lView === null) {
10440 // Verify that we will not get into infinite loop.
10441 ngDevMode && assertInjectImplementationNotEqual(ɵɵdirectiveInject);
10442 return ɵɵinject(token, flags);
10443 }
10444 const tNode = getCurrentTNode();
10445 return getOrCreateInjectable(tNode, lView, resolveForwardRef(token), flags);
10446}
10447/**
10448 * Throws an error indicating that a factory function could not be generated by the compiler for a
10449 * particular class.
10450 *
10451 * This instruction allows the actual error message to be optimized away when ngDevMode is turned
10452 * off, saving bytes of generated code while still providing a good experience in dev mode.
10453 *
10454 * The name of the class is not mentioned here, but will be in the generated factory function name
10455 * and thus in the stack trace.
10456 *
10457 * @codeGenApi
10458 */
10459function ɵɵinvalidFactory() {
10460 const msg = ngDevMode ? `This constructor was not compatible with Dependency Injection.` : 'invalid';
10461 throw new Error(msg);
10462}
10463
10464/**
10465 * Invoke `HostBindingsFunction`s for view.
10466 *
10467 * This methods executes `TView.hostBindingOpCodes`. It is used to execute the
10468 * `HostBindingsFunction`s associated with the current `LView`.
10469 *
10470 * @param tView Current `TView`.
10471 * @param lView Current `LView`.
10472 */
10473function processHostBindingOpCodes(tView, lView) {
10474 const hostBindingOpCodes = tView.hostBindingOpCodes;
10475 if (hostBindingOpCodes === null)
10476 return;
10477 try {
10478 for (let i = 0; i < hostBindingOpCodes.length; i++) {
10479 const opCode = hostBindingOpCodes[i];
10480 if (opCode < 0) {
10481 // Negative numbers are element indexes.
10482 setSelectedIndex(~opCode);
10483 }
10484 else {
10485 // Positive numbers are NumberTuple which store bindingRootIndex and directiveIndex.
10486 const directiveIdx = opCode;
10487 const bindingRootIndx = hostBindingOpCodes[++i];
10488 const hostBindingFn = hostBindingOpCodes[++i];
10489 setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
10490 const context = lView[directiveIdx];
10491 hostBindingFn(2 /* RenderFlags.Update */, context);
10492 }
10493 }
10494 }
10495 finally {
10496 setSelectedIndex(-1);
10497 }
10498}
10499/** Refreshes all content queries declared by directives in a given view */
10500function refreshContentQueries(tView, lView) {
10501 const contentQueries = tView.contentQueries;
10502 if (contentQueries !== null) {
10503 for (let i = 0; i < contentQueries.length; i += 2) {
10504 const queryStartIdx = contentQueries[i];
10505 const directiveDefIdx = contentQueries[i + 1];
10506 if (directiveDefIdx !== -1) {
10507 const directiveDef = tView.data[directiveDefIdx];
10508 ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.');
10509 ngDevMode &&
10510 assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
10511 setCurrentQueryIndex(queryStartIdx);
10512 directiveDef.contentQueries(2 /* RenderFlags.Update */, lView[directiveDefIdx], directiveDefIdx);
10513 }
10514 }
10515 }
10516}
10517/** Refreshes child components in the current view (update mode). */
10518function refreshChildComponents(hostLView, components) {
10519 for (let i = 0; i < components.length; i++) {
10520 refreshComponent(hostLView, components[i]);
10521 }
10522}
10523/** Renders child components in the current view (creation mode). */
10524function renderChildComponents(hostLView, components) {
10525 for (let i = 0; i < components.length; i++) {
10526 renderComponent(hostLView, components[i]);
10527 }
10528}
10529function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector, embeddedViewInjector) {
10530 const lView = tView.blueprint.slice();
10531 lView[HOST] = host;
10532 lView[FLAGS] = flags | 4 /* LViewFlags.CreationMode */ | 64 /* LViewFlags.Attached */ | 8 /* LViewFlags.FirstLViewPass */;
10533 if (embeddedViewInjector !== null ||
10534 (parentLView && (parentLView[FLAGS] & 1024 /* LViewFlags.HasEmbeddedViewInjector */))) {
10535 lView[FLAGS] |= 1024 /* LViewFlags.HasEmbeddedViewInjector */;
10536 }
10537 resetPreOrderHookFlags(lView);
10538 ngDevMode && tView.declTNode && parentLView && assertTNodeForLView(tView.declTNode, parentLView);
10539 lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
10540 lView[CONTEXT] = context;
10541 lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]);
10542 ngDevMode && assertDefined(lView[RENDERER_FACTORY], 'RendererFactory is required');
10543 lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER]);
10544 ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required');
10545 lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || null;
10546 lView[INJECTOR$1] = injector || parentLView && parentLView[INJECTOR$1] || null;
10547 lView[T_HOST] = tHostNode;
10548 lView[ID] = getUniqueLViewId();
10549 lView[EMBEDDED_VIEW_INJECTOR] = embeddedViewInjector;
10550 ngDevMode &&
10551 assertEqual(tView.type == 2 /* TViewType.Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView');
10552 lView[DECLARATION_COMPONENT_VIEW] =
10553 tView.type == 2 /* TViewType.Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView;
10554 return lView;
10555}
10556function getOrCreateTNode(tView, index, type, name, attrs) {
10557 ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
10558 // `view_engine_compatibility` for additional context.
10559 assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
10560 // Keep this function short, so that the VM will inline it.
10561 ngDevMode && assertPureTNodeType(type);
10562 let tNode = tView.data[index];
10563 if (tNode === null) {
10564 tNode = createTNodeAtIndex(tView, index, type, name, attrs);
10565 if (isInI18nBlock()) {
10566 // If we are in i18n block then all elements should be pre declared through `Placeholder`
10567 // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
10568 // If the `TNode` was not pre-declared than it means it was not mentioned which means it was
10569 // removed, so we mark it as detached.
10570 tNode.flags |= 32 /* TNodeFlags.isDetached */;
10571 }
10572 }
10573 else if (tNode.type & 64 /* TNodeType.Placeholder */) {
10574 tNode.type = type;
10575 tNode.value = name;
10576 tNode.attrs = attrs;
10577 const parent = getCurrentParentTNode();
10578 tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex;
10579 ngDevMode && assertTNodeForTView(tNode, tView);
10580 ngDevMode && assertEqual(index, tNode.index, 'Expecting same index');
10581 }
10582 setCurrentTNode(tNode, true);
10583 return tNode;
10584}
10585function createTNodeAtIndex(tView, index, type, name, attrs) {
10586 const currentTNode = getCurrentTNodePlaceholderOk();
10587 const isParent = isCurrentTNodeParent();
10588 const parent = isParent ? currentTNode : currentTNode && currentTNode.parent;
10589 // Parents cannot cross component boundaries because components will be used in multiple places.
10590 const tNode = tView.data[index] =
10591 createTNode(tView, parent, type, index, name, attrs);
10592 // Assign a pointer to the first child node of a given view. The first node is not always the one
10593 // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has
10594 // the index 1 or more, so we can't just check node index.
10595 if (tView.firstChild === null) {
10596 tView.firstChild = tNode;
10597 }
10598 if (currentTNode !== null) {
10599 if (isParent) {
10600 // FIXME(misko): This logic looks unnecessarily complicated. Could we simplify?
10601 if (currentTNode.child == null && tNode.parent !== null) {
10602 // We are in the same view, which means we are adding content node to the parent view.
10603 currentTNode.child = tNode;
10604 }
10605 }
10606 else {
10607 if (currentTNode.next === null) {
10608 // In the case of i18n the `currentTNode` may already be linked, in which case we don't want
10609 // to break the links which i18n created.
10610 currentTNode.next = tNode;
10611 }
10612 }
10613 }
10614 return tNode;
10615}
10616/**
10617 * When elements are created dynamically after a view blueprint is created (e.g. through
10618 * i18nApply()), we need to adjust the blueprint for future
10619 * template passes.
10620 *
10621 * @param tView `TView` associated with `LView`
10622 * @param lView The `LView` containing the blueprint to adjust
10623 * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
10624 * @param initialValue Initial value to store in blueprint
10625 */
10626function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
10627 if (numSlotsToAlloc === 0)
10628 return -1;
10629 if (ngDevMode) {
10630 assertFirstCreatePass(tView);
10631 assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
10632 assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
10633 assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
10634 assertFirstUpdatePass(tView);
10635 }
10636 const allocIdx = lView.length;
10637 for (let i = 0; i < numSlotsToAlloc; i++) {
10638 lView.push(initialValue);
10639 tView.blueprint.push(initialValue);
10640 tView.data.push(null);
10641 }
10642 return allocIdx;
10643}
10644//////////////////////////
10645//// Render
10646//////////////////////////
10647/**
10648 * Processes a view in the creation mode. This includes a number of steps in a specific order:
10649 * - creating view query functions (if any);
10650 * - executing a template function in the creation mode;
10651 * - updating static queries (if any);
10652 * - creating child components defined in a given view.
10653 */
10654function renderView(tView, lView, context) {
10655 ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
10656 enterView(lView);
10657 try {
10658 const viewQuery = tView.viewQuery;
10659 if (viewQuery !== null) {
10660 executeViewQueryFn(1 /* RenderFlags.Create */, viewQuery, context);
10661 }
10662 // Execute a template associated with this view, if it exists. A template function might not be
10663 // defined for the root component views.
10664 const templateFn = tView.template;
10665 if (templateFn !== null) {
10666 executeTemplate(tView, lView, templateFn, 1 /* RenderFlags.Create */, context);
10667 }
10668 // This needs to be set before children are processed to support recursive components.
10669 // This must be set to false immediately after the first creation run because in an
10670 // ngFor loop, all the views will be created together before update mode runs and turns
10671 // off firstCreatePass. If we don't set it here, instances will perform directive
10672 // matching, etc again and again.
10673 if (tView.firstCreatePass) {
10674 tView.firstCreatePass = false;
10675 }
10676 // We resolve content queries specifically marked as `static` in creation mode. Dynamic
10677 // content queries are resolved during change detection (i.e. update mode), after embedded
10678 // views are refreshed (see block above).
10679 if (tView.staticContentQueries) {
10680 refreshContentQueries(tView, lView);
10681 }
10682 // We must materialize query results before child components are processed
10683 // in case a child component has projected a container. The LContainer needs
10684 // to exist so the embedded views are properly attached by the container.
10685 if (tView.staticViewQueries) {
10686 executeViewQueryFn(2 /* RenderFlags.Update */, tView.viewQuery, context);
10687 }
10688 // Render child component views.
10689 const components = tView.components;
10690 if (components !== null) {
10691 renderChildComponents(lView, components);
10692 }
10693 }
10694 catch (error) {
10695 // If we didn't manage to get past the first template pass due to
10696 // an error, mark the view as corrupted so we can try to recover.
10697 if (tView.firstCreatePass) {
10698 tView.incompleteFirstPass = true;
10699 tView.firstCreatePass = false;
10700 }
10701 throw error;
10702 }
10703 finally {
10704 lView[FLAGS] &= ~4 /* LViewFlags.CreationMode */;
10705 leaveView();
10706 }
10707}
10708/**
10709 * Processes a view in update mode. This includes a number of steps in a specific order:
10710 * - executing a template function in update mode;
10711 * - executing hooks;
10712 * - refreshing queries;
10713 * - setting host bindings;
10714 * - refreshing child (embedded and component) views.
10715 */
10716function refreshView(tView, lView, templateFn, context) {
10717 ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
10718 const flags = lView[FLAGS];
10719 if ((flags & 128 /* LViewFlags.Destroyed */) === 128 /* LViewFlags.Destroyed */)
10720 return;
10721 enterView(lView);
10722 // Check no changes mode is a dev only mode used to verify that bindings have not changed
10723 // since they were assigned. We do not want to execute lifecycle hooks in that mode.
10724 const isInCheckNoChangesPass = ngDevMode && isInCheckNoChangesMode();
10725 try {
10726 resetPreOrderHookFlags(lView);
10727 setBindingIndex(tView.bindingStartIndex);
10728 if (templateFn !== null) {
10729 executeTemplate(tView, lView, templateFn, 2 /* RenderFlags.Update */, context);
10730 }
10731 const hooksInitPhaseCompleted = (flags & 3 /* LViewFlags.InitPhaseStateMask */) === 3 /* InitPhaseState.InitPhaseCompleted */;
10732 // execute pre-order hooks (OnInit, OnChanges, DoCheck)
10733 // PERF WARNING: do NOT extract this to a separate function without running benchmarks
10734 if (!isInCheckNoChangesPass) {
10735 if (hooksInitPhaseCompleted) {
10736 const preOrderCheckHooks = tView.preOrderCheckHooks;
10737 if (preOrderCheckHooks !== null) {
10738 executeCheckHooks(lView, preOrderCheckHooks, null);
10739 }
10740 }
10741 else {
10742 const preOrderHooks = tView.preOrderHooks;
10743 if (preOrderHooks !== null) {
10744 executeInitAndCheckHooks(lView, preOrderHooks, 0 /* InitPhaseState.OnInitHooksToBeRun */, null);
10745 }
10746 incrementInitPhaseFlags(lView, 0 /* InitPhaseState.OnInitHooksToBeRun */);
10747 }
10748 }
10749 // First mark transplanted views that are declared in this lView as needing a refresh at their
10750 // insertion points. This is needed to avoid the situation where the template is defined in this
10751 // `LView` but its declaration appears after the insertion component.
10752 markTransplantedViewsForRefresh(lView);
10753 refreshEmbeddedViews(lView);
10754 // Content query results must be refreshed before content hooks are called.
10755 if (tView.contentQueries !== null) {
10756 refreshContentQueries(tView, lView);
10757 }
10758 // execute content hooks (AfterContentInit, AfterContentChecked)
10759 // PERF WARNING: do NOT extract this to a separate function without running benchmarks
10760 if (!isInCheckNoChangesPass) {
10761 if (hooksInitPhaseCompleted) {
10762 const contentCheckHooks = tView.contentCheckHooks;
10763 if (contentCheckHooks !== null) {
10764 executeCheckHooks(lView, contentCheckHooks);
10765 }
10766 }
10767 else {
10768 const contentHooks = tView.contentHooks;
10769 if (contentHooks !== null) {
10770 executeInitAndCheckHooks(lView, contentHooks, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
10771 }
10772 incrementInitPhaseFlags(lView, 1 /* InitPhaseState.AfterContentInitHooksToBeRun */);
10773 }
10774 }
10775 processHostBindingOpCodes(tView, lView);
10776 // Refresh child component views.
10777 const components = tView.components;
10778 if (components !== null) {
10779 refreshChildComponents(lView, components);
10780 }
10781 // View queries must execute after refreshing child components because a template in this view
10782 // could be inserted in a child component. If the view query executes before child component
10783 // refresh, the template might not yet be inserted.
10784 const viewQuery = tView.viewQuery;
10785 if (viewQuery !== null) {
10786 executeViewQueryFn(2 /* RenderFlags.Update */, viewQuery, context);
10787 }
10788 // execute view hooks (AfterViewInit, AfterViewChecked)
10789 // PERF WARNING: do NOT extract this to a separate function without running benchmarks
10790 if (!isInCheckNoChangesPass) {
10791 if (hooksInitPhaseCompleted) {
10792 const viewCheckHooks = tView.viewCheckHooks;
10793 if (viewCheckHooks !== null) {
10794 executeCheckHooks(lView, viewCheckHooks);
10795 }
10796 }
10797 else {
10798 const viewHooks = tView.viewHooks;
10799 if (viewHooks !== null) {
10800 executeInitAndCheckHooks(lView, viewHooks, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
10801 }
10802 incrementInitPhaseFlags(lView, 2 /* InitPhaseState.AfterViewInitHooksToBeRun */);
10803 }
10804 }
10805 if (tView.firstUpdatePass === true) {
10806 // We need to make sure that we only flip the flag on successful `refreshView` only
10807 // Don't do this in `finally` block.
10808 // If we did this in `finally` block then an exception could block the execution of styling
10809 // instructions which in turn would be unable to insert themselves into the styling linked
10810 // list. The result of this would be that if the exception would not be throw on subsequent CD
10811 // the styling would be unable to process it data and reflect to the DOM.
10812 tView.firstUpdatePass = false;
10813 }
10814 // Do not reset the dirty state when running in check no changes mode. We don't want components
10815 // to behave differently depending on whether check no changes is enabled or not. For example:
10816 // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to
10817 // refresh a `NgClass` binding should work. If we would reset the dirty state in the check
10818 // no changes cycle, the component would be not be dirty for the next update pass. This would
10819 // be different in production mode where the component dirty state is not reset.
10820 if (!isInCheckNoChangesPass) {
10821 lView[FLAGS] &= ~(32 /* LViewFlags.Dirty */ | 8 /* LViewFlags.FirstLViewPass */);
10822 }
10823 if (lView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) {
10824 lView[FLAGS] &= ~512 /* LViewFlags.RefreshTransplantedView */;
10825 updateTransplantedViewCount(lView[PARENT], -1);
10826 }
10827 }
10828 finally {
10829 leaveView();
10830 }
10831}
10832function executeTemplate(tView, lView, templateFn, rf, context) {
10833 const prevSelectedIndex = getSelectedIndex();
10834 const isUpdatePhase = rf & 2 /* RenderFlags.Update */;
10835 try {
10836 setSelectedIndex(-1);
10837 if (isUpdatePhase && lView.length > HEADER_OFFSET) {
10838 // When we're updating, inherently select 0 so we don't
10839 // have to generate that instruction for most update blocks.
10840 selectIndexInternal(tView, lView, HEADER_OFFSET, !!ngDevMode && isInCheckNoChangesMode());
10841 }
10842 const preHookType = isUpdatePhase ? 2 /* ProfilerEvent.TemplateUpdateStart */ : 0 /* ProfilerEvent.TemplateCreateStart */;
10843 profiler(preHookType, context);
10844 templateFn(rf, context);
10845 }
10846 finally {
10847 setSelectedIndex(prevSelectedIndex);
10848 const postHookType = isUpdatePhase ? 3 /* ProfilerEvent.TemplateUpdateEnd */ : 1 /* ProfilerEvent.TemplateCreateEnd */;
10849 profiler(postHookType, context);
10850 }
10851}
10852//////////////////////////
10853//// Element
10854//////////////////////////
10855function executeContentQueries(tView, tNode, lView) {
10856 if (isContentQueryHost(tNode)) {
10857 const start = tNode.directiveStart;
10858 const end = tNode.directiveEnd;
10859 for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
10860 const def = tView.data[directiveIndex];
10861 if (def.contentQueries) {
10862 def.contentQueries(1 /* RenderFlags.Create */, lView[directiveIndex], directiveIndex);
10863 }
10864 }
10865 }
10866}
10867/**
10868 * Creates directive instances.
10869 */
10870function createDirectivesInstances(tView, lView, tNode) {
10871 if (!getBindingsEnabled())
10872 return;
10873 instantiateAllDirectives(tView, lView, tNode, getNativeByTNode(tNode, lView));
10874 if ((tNode.flags & 64 /* TNodeFlags.hasHostBindings */) === 64 /* TNodeFlags.hasHostBindings */) {
10875 invokeDirectivesHostBindings(tView, lView, tNode);
10876 }
10877}
10878/**
10879 * Takes a list of local names and indices and pushes the resolved local variable values
10880 * to LView in the same order as they are loaded in the template with load().
10881 */
10882function saveResolvedLocalsInData(viewData, tNode, localRefExtractor = getNativeByTNode) {
10883 const localNames = tNode.localNames;
10884 if (localNames !== null) {
10885 let localIndex = tNode.index + 1;
10886 for (let i = 0; i < localNames.length; i += 2) {
10887 const index = localNames[i + 1];
10888 const value = index === -1 ?
10889 localRefExtractor(tNode, viewData) :
10890 viewData[index];
10891 viewData[localIndex++] = value;
10892 }
10893 }
10894}
10895/**
10896 * Gets TView from a template function or creates a new TView
10897 * if it doesn't already exist.
10898 *
10899 * @param def ComponentDef
10900 * @returns TView
10901 */
10902function getOrCreateComponentTView(def) {
10903 const tView = def.tView;
10904 // Create a TView if there isn't one, or recreate it if the first create pass didn't
10905 // complete successfully since we can't know for sure whether it's in a usable shape.
10906 if (tView === null || tView.incompleteFirstPass) {
10907 // Declaration node here is null since this function is called when we dynamically create a
10908 // component and hence there is no declaration.
10909 const declTNode = null;
10910 return def.tView = createTView(1 /* TViewType.Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts);
10911 }
10912 return tView;
10913}
10914/**
10915 * Creates a TView instance
10916 *
10917 * @param type Type of `TView`.
10918 * @param declTNode Declaration location of this `TView`.
10919 * @param templateFn Template function
10920 * @param decls The number of nodes, local refs, and pipes in this template
10921 * @param directives Registry of directives for this view
10922 * @param pipes Registry of pipes for this view
10923 * @param viewQuery View queries for this view
10924 * @param schemas Schemas for this view
10925 * @param consts Constants for this view
10926 */
10927function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory) {
10928 ngDevMode && ngDevMode.tView++;
10929 const bindingStartIndex = HEADER_OFFSET + decls;
10930 // This length does not yet contain host bindings from child directives because at this point,
10931 // we don't know which directives are active on this template. As soon as a directive is matched
10932 // that has a host binding, we will update the blueprint with that def's hostVars count.
10933 const initialViewLength = bindingStartIndex + vars;
10934 const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength);
10935 const consts = typeof constsOrFactory === 'function' ? constsOrFactory() : constsOrFactory;
10936 const tView = blueprint[TVIEW] = {
10937 type: type,
10938 blueprint: blueprint,
10939 template: templateFn,
10940 queries: null,
10941 viewQuery: viewQuery,
10942 declTNode: declTNode,
10943 data: blueprint.slice().fill(null, bindingStartIndex),
10944 bindingStartIndex: bindingStartIndex,
10945 expandoStartIndex: initialViewLength,
10946 hostBindingOpCodes: null,
10947 firstCreatePass: true,
10948 firstUpdatePass: true,
10949 staticViewQueries: false,
10950 staticContentQueries: false,
10951 preOrderHooks: null,
10952 preOrderCheckHooks: null,
10953 contentHooks: null,
10954 contentCheckHooks: null,
10955 viewHooks: null,
10956 viewCheckHooks: null,
10957 destroyHooks: null,
10958 cleanup: null,
10959 contentQueries: null,
10960 components: null,
10961 directiveRegistry: typeof directives === 'function' ? directives() : directives,
10962 pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
10963 firstChild: null,
10964 schemas: schemas,
10965 consts: consts,
10966 incompleteFirstPass: false
10967 };
10968 if (ngDevMode) {
10969 // For performance reasons it is important that the tView retains the same shape during runtime.
10970 // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
10971 // prevent class transitions.
10972 Object.seal(tView);
10973 }
10974 return tView;
10975}
10976function createViewBlueprint(bindingStartIndex, initialViewLength) {
10977 const blueprint = [];
10978 for (let i = 0; i < initialViewLength; i++) {
10979 blueprint.push(i < bindingStartIndex ? null : NO_CHANGE);
10980 }
10981 return blueprint;
10982}
10983/**
10984 * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
10985 *
10986 * @param rendererFactory Factory function to create renderer instance.
10987 * @param elementOrSelector Render element or CSS selector to locate the element.
10988 * @param encapsulation View Encapsulation defined for component that requests host element.
10989 */
10990function locateHostElement(renderer, elementOrSelector, encapsulation) {
10991 // When using native Shadow DOM, do not clear host element to allow native slot projection
10992 const preserveContent = encapsulation === ViewEncapsulation.ShadowDom;
10993 return renderer.selectRootElement(elementOrSelector, preserveContent);
10994}
10995/**
10996 * Saves context for this cleanup function in LView.cleanupInstances.
10997 *
10998 * On the first template pass, saves in TView:
10999 * - Cleanup function
11000 * - Index of context we just saved in LView.cleanupInstances
11001 *
11002 * This function can also be used to store instance specific cleanup fns. In that case the `context`
11003 * is `null` and the function is store in `LView` (rather than it `TView`).
11004 */
11005function storeCleanupWithContext(tView, lView, context, cleanupFn) {
11006 const lCleanup = getOrCreateLViewCleanup(lView);
11007 if (context === null) {
11008 // If context is null that this is instance specific callback. These callbacks can only be
11009 // inserted after template shared instances. For this reason in ngDevMode we freeze the TView.
11010 if (ngDevMode) {
11011 Object.freeze(getOrCreateTViewCleanup(tView));
11012 }
11013 lCleanup.push(cleanupFn);
11014 }
11015 else {
11016 lCleanup.push(context);
11017 if (tView.firstCreatePass) {
11018 getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1);
11019 }
11020 }
11021}
11022function createTNode(tView, tParent, type, index, value, attrs) {
11023 ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
11024 // `view_engine_compatibility` for additional context.
11025 assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
11026 ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\'');
11027 ngDevMode && ngDevMode.tNode++;
11028 ngDevMode && tParent && assertTNodeForTView(tParent, tView);
11029 let injectorIndex = tParent ? tParent.injectorIndex : -1;
11030 const tNode = {
11031 type,
11032 index,
11033 insertBeforeIndex: null,
11034 injectorIndex,
11035 directiveStart: -1,
11036 directiveEnd: -1,
11037 directiveStylingLast: -1,
11038 componentOffset: -1,
11039 propertyBindings: null,
11040 flags: 0,
11041 providerIndexes: 0,
11042 value: value,
11043 attrs: attrs,
11044 mergedAttrs: null,
11045 localNames: null,
11046 initialInputs: undefined,
11047 inputs: null,
11048 outputs: null,
11049 tViews: null,
11050 next: null,
11051 projectionNext: null,
11052 child: null,
11053 parent: tParent,
11054 projection: null,
11055 styles: null,
11056 stylesWithoutHost: null,
11057 residualStyles: undefined,
11058 classes: null,
11059 classesWithoutHost: null,
11060 residualClasses: undefined,
11061 classBindings: 0,
11062 styleBindings: 0,
11063 };
11064 if (ngDevMode) {
11065 // For performance reasons it is important that the tNode retains the same shape during runtime.
11066 // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
11067 // prevent class transitions.
11068 Object.seal(tNode);
11069 }
11070 return tNode;
11071}
11072/**
11073 * Generates the `PropertyAliases` data structure from the provided input/output mapping.
11074 * @param aliasMap Input/output mapping from the directive definition.
11075 * @param directiveIndex Index of the directive.
11076 * @param propertyAliases Object in which to store the results.
11077 * @param hostDirectiveAliasMap Object used to alias or filter out properties for host directives.
11078 * If the mapping is provided, it'll act as an allowlist, as well as a mapping of what public
11079 * name inputs/outputs should be exposed under.
11080 */
11081function generatePropertyAliases(aliasMap, directiveIndex, propertyAliases, hostDirectiveAliasMap) {
11082 for (let publicName in aliasMap) {
11083 if (aliasMap.hasOwnProperty(publicName)) {
11084 propertyAliases = propertyAliases === null ? {} : propertyAliases;
11085 const internalName = aliasMap[publicName];
11086 // If there are no host directive mappings, we want to remap using the alias map from the
11087 // definition itself. If there is an alias map, it has two functions:
11088 // 1. It serves as an allowlist of bindings that are exposed by the host directives. Only the
11089 // ones inside the host directive map will be exposed on the host.
11090 // 2. The public name of the property is aliased using the host directive alias map, rather
11091 // than the alias map from the definition.
11092 if (hostDirectiveAliasMap === null) {
11093 addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName);
11094 }
11095 else if (hostDirectiveAliasMap.hasOwnProperty(publicName)) {
11096 addPropertyAlias(propertyAliases, directiveIndex, hostDirectiveAliasMap[publicName], internalName);
11097 }
11098 }
11099 }
11100 return propertyAliases;
11101}
11102function addPropertyAlias(propertyAliases, directiveIndex, publicName, internalName) {
11103 if (propertyAliases.hasOwnProperty(publicName)) {
11104 propertyAliases[publicName].push(directiveIndex, internalName);
11105 }
11106 else {
11107 propertyAliases[publicName] = [directiveIndex, internalName];
11108 }
11109}
11110/**
11111 * Initializes data structures required to work with directive inputs and outputs.
11112 * Initialization is done for all directives matched on a given TNode.
11113 */
11114function initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefinitionMap) {
11115 ngDevMode && assertFirstCreatePass(tView);
11116 const start = tNode.directiveStart;
11117 const end = tNode.directiveEnd;
11118 const tViewData = tView.data;
11119 const tNodeAttrs = tNode.attrs;
11120 const inputsFromAttrs = [];
11121 let inputsStore = null;
11122 let outputsStore = null;
11123 for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
11124 const directiveDef = tViewData[directiveIndex];
11125 const aliasData = hostDirectiveDefinitionMap ? hostDirectiveDefinitionMap.get(directiveDef) : null;
11126 const aliasedInputs = aliasData ? aliasData.inputs : null;
11127 const aliasedOutputs = aliasData ? aliasData.outputs : null;
11128 inputsStore =
11129 generatePropertyAliases(directiveDef.inputs, directiveIndex, inputsStore, aliasedInputs);
11130 outputsStore =
11131 generatePropertyAliases(directiveDef.outputs, directiveIndex, outputsStore, aliasedOutputs);
11132 // Do not use unbound attributes as inputs to structural directives, since structural
11133 // directive inputs can only be set using microsyntax (e.g. `<div *dir="exp">`).
11134 // TODO(FW-1930): microsyntax expressions may also contain unbound/static attributes, which
11135 // should be set for inline templates.
11136 const initialInputs = (inputsStore !== null && tNodeAttrs !== null && !isInlineTemplate(tNode)) ?
11137 generateInitialInputs(inputsStore, directiveIndex, tNodeAttrs) :
11138 null;
11139 inputsFromAttrs.push(initialInputs);
11140 }
11141 if (inputsStore !== null) {
11142 if (inputsStore.hasOwnProperty('class')) {
11143 tNode.flags |= 8 /* TNodeFlags.hasClassInput */;
11144 }
11145 if (inputsStore.hasOwnProperty('style')) {
11146 tNode.flags |= 16 /* TNodeFlags.hasStyleInput */;
11147 }
11148 }
11149 tNode.initialInputs = inputsFromAttrs;
11150 tNode.inputs = inputsStore;
11151 tNode.outputs = outputsStore;
11152}
11153/**
11154 * Mapping between attributes names that don't correspond to their element property names.
11155 *
11156 * Performance note: this function is written as a series of if checks (instead of, say, a property
11157 * object lookup) for performance reasons - the series of `if` checks seems to be the fastest way of
11158 * mapping property names. Do NOT change without benchmarking.
11159 *
11160 * Note: this mapping has to be kept in sync with the equally named mapping in the template
11161 * type-checking machinery of ngtsc.
11162 */
11163function mapPropName(name) {
11164 if (name === 'class')
11165 return 'className';
11166 if (name === 'for')
11167 return 'htmlFor';
11168 if (name === 'formaction')
11169 return 'formAction';
11170 if (name === 'innerHtml')
11171 return 'innerHTML';
11172 if (name === 'readonly')
11173 return 'readOnly';
11174 if (name === 'tabindex')
11175 return 'tabIndex';
11176 return name;
11177}
11178function elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, nativeOnly) {
11179 ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
11180 const element = getNativeByTNode(tNode, lView);
11181 let inputData = tNode.inputs;
11182 let dataValue;
11183 if (!nativeOnly && inputData != null && (dataValue = inputData[propName])) {
11184 setInputsForProperty(tView, lView, dataValue, propName, value);
11185 if (isComponentHost(tNode))
11186 markDirtyIfOnPush(lView, tNode.index);
11187 if (ngDevMode) {
11188 setNgReflectProperties(lView, element, tNode.type, dataValue, value);
11189 }
11190 }
11191 else if (tNode.type & 3 /* TNodeType.AnyRNode */) {
11192 propName = mapPropName(propName);
11193 if (ngDevMode) {
11194 validateAgainstEventProperties(propName);
11195 if (!isPropertyValid(element, propName, tNode.value, tView.schemas)) {
11196 handleUnknownPropertyError(propName, tNode.value, tNode.type, lView);
11197 }
11198 ngDevMode.rendererSetProperty++;
11199 }
11200 // It is assumed that the sanitizer is only added when the compiler determines that the
11201 // property is risky, so sanitization can be done without further checks.
11202 value = sanitizer != null ? sanitizer(value, tNode.value || '', propName) : value;
11203 renderer.setProperty(element, propName, value);
11204 }
11205 else if (tNode.type & 12 /* TNodeType.AnyContainer */) {
11206 // If the node is a container and the property didn't
11207 // match any of the inputs or schemas we should throw.
11208 if (ngDevMode && !matchingSchemas(tView.schemas, tNode.value)) {
11209 handleUnknownPropertyError(propName, tNode.value, tNode.type, lView);
11210 }
11211 }
11212}
11213/** If node is an OnPush component, marks its LView dirty. */
11214function markDirtyIfOnPush(lView, viewIndex) {
11215 ngDevMode && assertLView(lView);
11216 const childComponentLView = getComponentLViewByIndex(viewIndex, lView);
11217 if (!(childComponentLView[FLAGS] & 16 /* LViewFlags.CheckAlways */)) {
11218 childComponentLView[FLAGS] |= 32 /* LViewFlags.Dirty */;
11219 }
11220}
11221function setNgReflectProperty(lView, element, type, attrName, value) {
11222 const renderer = lView[RENDERER];
11223 attrName = normalizeDebugBindingName(attrName);
11224 const debugValue = normalizeDebugBindingValue(value);
11225 if (type & 3 /* TNodeType.AnyRNode */) {
11226 if (value == null) {
11227 renderer.removeAttribute(element, attrName);
11228 }
11229 else {
11230 renderer.setAttribute(element, attrName, debugValue);
11231 }
11232 }
11233 else {
11234 const textContent = escapeCommentText(`bindings=${JSON.stringify({ [attrName]: debugValue }, null, 2)}`);
11235 renderer.setValue(element, textContent);
11236 }
11237}
11238function setNgReflectProperties(lView, element, type, dataValue, value) {
11239 if (type & (3 /* TNodeType.AnyRNode */ | 4 /* TNodeType.Container */)) {
11240 /**
11241 * dataValue is an array containing runtime input or output names for the directives:
11242 * i+0: directive instance index
11243 * i+1: privateName
11244 *
11245 * e.g. [0, 'change', 'change-minified']
11246 * we want to set the reflected property with the privateName: dataValue[i+1]
11247 */
11248 for (let i = 0; i < dataValue.length; i += 2) {
11249 setNgReflectProperty(lView, element, type, dataValue[i + 1], value);
11250 }
11251 }
11252}
11253/**
11254 * Resolve the matched directives on a node.
11255 */
11256function resolveDirectives(tView, lView, tNode, localRefs) {
11257 // Please make sure to have explicit type for `exportsMap`. Inferred type triggers bug in
11258 // tsickle.
11259 ngDevMode && assertFirstCreatePass(tView);
11260 let hasDirectives = false;
11261 if (getBindingsEnabled()) {
11262 const exportsMap = localRefs === null ? null : { '': -1 };
11263 const matchResult = findDirectiveDefMatches(tView, tNode);
11264 let directiveDefs;
11265 let hostDirectiveDefs;
11266 if (matchResult === null) {
11267 directiveDefs = hostDirectiveDefs = null;
11268 }
11269 else {
11270 [directiveDefs, hostDirectiveDefs] = matchResult;
11271 }
11272 if (directiveDefs !== null) {
11273 hasDirectives = true;
11274 initializeDirectives(tView, lView, tNode, directiveDefs, exportsMap, hostDirectiveDefs);
11275 }
11276 if (exportsMap)
11277 cacheMatchingLocalNames(tNode, localRefs, exportsMap);
11278 }
11279 // Merge the template attrs last so that they have the highest priority.
11280 tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, tNode.attrs);
11281 return hasDirectives;
11282}
11283/** Initializes the data structures necessary for a list of directives to be instantiated. */
11284function initializeDirectives(tView, lView, tNode, directives, exportsMap, hostDirectiveDefs) {
11285 ngDevMode && assertFirstCreatePass(tView);
11286 // Publishes the directive types to DI so they can be injected. Needs to
11287 // happen in a separate pass before the TNode flags have been initialized.
11288 for (let i = 0; i < directives.length; i++) {
11289 diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, directives[i].type);
11290 }
11291 initTNodeFlags(tNode, tView.data.length, directives.length);
11292 // When the same token is provided by several directives on the same node, some rules apply in
11293 // the viewEngine:
11294 // - viewProviders have priority over providers
11295 // - the last directive in NgModule.declarations has priority over the previous one
11296 // So to match these rules, the order in which providers are added in the arrays is very
11297 // important.
11298 for (let i = 0; i < directives.length; i++) {
11299 const def = directives[i];
11300 if (def.providersResolver)
11301 def.providersResolver(def);
11302 }
11303 let preOrderHooksFound = false;
11304 let preOrderCheckHooksFound = false;
11305 let directiveIdx = allocExpando(tView, lView, directives.length, null);
11306 ngDevMode &&
11307 assertSame(directiveIdx, tNode.directiveStart, 'TNode.directiveStart should point to just allocated space');
11308 for (let i = 0; i < directives.length; i++) {
11309 const def = directives[i];
11310 // Merge the attrs in the order of matches. This assumes that the first directive is the
11311 // component itself, so that the component has the least priority.
11312 tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
11313 configureViewWithDirective(tView, tNode, lView, directiveIdx, def);
11314 saveNameToExportMap(directiveIdx, def, exportsMap);
11315 if (def.contentQueries !== null)
11316 tNode.flags |= 4 /* TNodeFlags.hasContentQuery */;
11317 if (def.hostBindings !== null || def.hostAttrs !== null || def.hostVars !== 0)
11318 tNode.flags |= 64 /* TNodeFlags.hasHostBindings */;
11319 const lifeCycleHooks = def.type.prototype;
11320 // Only push a node index into the preOrderHooks array if this is the first
11321 // pre-order hook found on this node.
11322 if (!preOrderHooksFound &&
11323 (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngOnInit || lifeCycleHooks.ngDoCheck)) {
11324 // We will push the actual hook function into this array later during dir instantiation.
11325 // We cannot do it now because we must ensure hooks are registered in the same
11326 // order that directives are created (i.e. injection order).
11327 (tView.preOrderHooks || (tView.preOrderHooks = [])).push(tNode.index);
11328 preOrderHooksFound = true;
11329 }
11330 if (!preOrderCheckHooksFound && (lifeCycleHooks.ngOnChanges || lifeCycleHooks.ngDoCheck)) {
11331 (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(tNode.index);
11332 preOrderCheckHooksFound = true;
11333 }
11334 directiveIdx++;
11335 }
11336 initializeInputAndOutputAliases(tView, tNode, hostDirectiveDefs);
11337}
11338/**
11339 * Add `hostBindings` to the `TView.hostBindingOpCodes`.
11340 *
11341 * @param tView `TView` to which the `hostBindings` should be added.
11342 * @param tNode `TNode` the element which contains the directive
11343 * @param directiveIdx Directive index in view.
11344 * @param directiveVarsIdx Where will the directive's vars be stored
11345 * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
11346 */
11347function registerHostBindingOpCodes(tView, tNode, directiveIdx, directiveVarsIdx, def) {
11348 ngDevMode && assertFirstCreatePass(tView);
11349 const hostBindings = def.hostBindings;
11350 if (hostBindings) {
11351 let hostBindingOpCodes = tView.hostBindingOpCodes;
11352 if (hostBindingOpCodes === null) {
11353 hostBindingOpCodes = tView.hostBindingOpCodes = [];
11354 }
11355 const elementIndx = ~tNode.index;
11356 if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
11357 // Conditionally add select element so that we are more efficient in execution.
11358 // NOTE: this is strictly not necessary and it trades code size for runtime perf.
11359 // (We could just always add it.)
11360 hostBindingOpCodes.push(elementIndx);
11361 }
11362 hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
11363 }
11364}
11365/**
11366 * Returns the last selected element index in the `HostBindingOpCodes`
11367 *
11368 * For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
11369 * if it changes. This method returns the last index (or '0' if not found.)
11370 *
11371 * Selected element index are only the ones which are negative.
11372 */
11373function lastSelectedElementIdx(hostBindingOpCodes) {
11374 let i = hostBindingOpCodes.length;
11375 while (i > 0) {
11376 const value = hostBindingOpCodes[--i];
11377 if (typeof value === 'number' && value < 0) {
11378 return value;
11379 }
11380 }
11381 return 0;
11382}
11383/**
11384 * Instantiate all the directives that were previously resolved on the current node.
11385 */
11386function instantiateAllDirectives(tView, lView, tNode, native) {
11387 const start = tNode.directiveStart;
11388 const end = tNode.directiveEnd;
11389 // The component view needs to be created before creating the node injector
11390 // since it is used to inject some special symbols like `ChangeDetectorRef`.
11391 if (isComponentHost(tNode)) {
11392 ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
11393 addComponentLogic(lView, tNode, tView.data[start + tNode.componentOffset]);
11394 }
11395 if (!tView.firstCreatePass) {
11396 getOrCreateNodeInjectorForNode(tNode, lView);
11397 }
11398 attachPatchData(native, lView);
11399 const initialInputs = tNode.initialInputs;
11400 for (let i = start; i < end; i++) {
11401 const def = tView.data[i];
11402 const directive = getNodeInjectable(lView, tView, i, tNode);
11403 attachPatchData(directive, lView);
11404 if (initialInputs !== null) {
11405 setInputsFromAttrs(lView, i - start, directive, def, tNode, initialInputs);
11406 }
11407 if (isComponentDef(def)) {
11408 const componentView = getComponentLViewByIndex(tNode.index, lView);
11409 componentView[CONTEXT] = getNodeInjectable(lView, tView, i, tNode);
11410 }
11411 }
11412}
11413function invokeDirectivesHostBindings(tView, lView, tNode) {
11414 const start = tNode.directiveStart;
11415 const end = tNode.directiveEnd;
11416 const elementIndex = tNode.index;
11417 const currentDirectiveIndex = getCurrentDirectiveIndex();
11418 try {
11419 setSelectedIndex(elementIndex);
11420 for (let dirIndex = start; dirIndex < end; dirIndex++) {
11421 const def = tView.data[dirIndex];
11422 const directive = lView[dirIndex];
11423 setCurrentDirectiveIndex(dirIndex);
11424 if (def.hostBindings !== null || def.hostVars !== 0 || def.hostAttrs !== null) {
11425 invokeHostBindingsInCreationMode(def, directive);
11426 }
11427 }
11428 }
11429 finally {
11430 setSelectedIndex(-1);
11431 setCurrentDirectiveIndex(currentDirectiveIndex);
11432 }
11433}
11434/**
11435 * Invoke the host bindings in creation mode.
11436 *
11437 * @param def `DirectiveDef` which may contain the `hostBindings` function.
11438 * @param directive Instance of directive.
11439 */
11440function invokeHostBindingsInCreationMode(def, directive) {
11441 if (def.hostBindings !== null) {
11442 def.hostBindings(1 /* RenderFlags.Create */, directive);
11443 }
11444}
11445/**
11446 * Matches the current node against all available selectors.
11447 * If a component is matched (at most one), it is returned in first position in the array.
11448 */
11449function findDirectiveDefMatches(tView, tNode) {
11450 ngDevMode && assertFirstCreatePass(tView);
11451 ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
11452 const registry = tView.directiveRegistry;
11453 let matches = null;
11454 let hostDirectiveDefs = null;
11455 if (registry) {
11456 for (let i = 0; i < registry.length; i++) {
11457 const def = registry[i];
11458 if (isNodeMatchingSelectorList(tNode, def.selectors, /* isProjectionMode */ false)) {
11459 matches || (matches = []);
11460 if (isComponentDef(def)) {
11461 if (ngDevMode) {
11462 assertTNodeType(tNode, 2 /* TNodeType.Element */, `"${tNode.value}" tags cannot be used as component hosts. ` +
11463 `Please use a different tag to activate the ${stringify(def.type)} component.`);
11464 if (isComponentHost(tNode)) {
11465 throwMultipleComponentError(tNode, matches.find(isComponentDef).type, def.type);
11466 }
11467 }
11468 // Components are inserted at the front of the matches array so that their lifecycle
11469 // hooks run before any directive lifecycle hooks. This appears to be for ViewEngine
11470 // compatibility. This logic doesn't make sense with host directives, because it
11471 // would allow the host directives to undo any overrides the host may have made.
11472 // To handle this case, the host directives of components are inserted at the beginning
11473 // of the array, followed by the component. As such, the insertion order is as follows:
11474 // 1. Host directives belonging to the selector-matched component.
11475 // 2. Selector-matched component.
11476 // 3. Host directives belonging to selector-matched directives.
11477 // 4. Selector-matched directives.
11478 if (def.findHostDirectiveDefs !== null) {
11479 const hostDirectiveMatches = [];
11480 hostDirectiveDefs = hostDirectiveDefs || new Map();
11481 def.findHostDirectiveDefs(def, hostDirectiveMatches, hostDirectiveDefs);
11482 // Add all host directives declared on this component, followed by the component itself.
11483 // Host directives should execute first so the host has a chance to override changes
11484 // to the DOM made by them.
11485 matches.unshift(...hostDirectiveMatches, def);
11486 // Component is offset starting from the beginning of the host directives array.
11487 const componentOffset = hostDirectiveMatches.length;
11488 markAsComponentHost(tView, tNode, componentOffset);
11489 }
11490 else {
11491 // No host directives on this component, just add the
11492 // component def to the beginning of the matches.
11493 matches.unshift(def);
11494 markAsComponentHost(tView, tNode, 0);
11495 }
11496 }
11497 else {
11498 // Append any host directives to the matches first.
11499 hostDirectiveDefs = hostDirectiveDefs || new Map();
11500 def.findHostDirectiveDefs?.(def, matches, hostDirectiveDefs);
11501 matches.push(def);
11502 }
11503 }
11504 }
11505 }
11506 return matches === null ? null : [matches, hostDirectiveDefs];
11507}
11508/**
11509 * Marks a given TNode as a component's host. This consists of:
11510 * - setting the component offset on the TNode.
11511 * - storing index of component's host element so it will be queued for view refresh during CD.
11512 */
11513function markAsComponentHost(tView, hostTNode, componentOffset) {
11514 ngDevMode && assertFirstCreatePass(tView);
11515 ngDevMode && assertGreaterThan(componentOffset, -1, 'componentOffset must be great than -1');
11516 hostTNode.componentOffset = componentOffset;
11517 (tView.components || (tView.components = [])).push(hostTNode.index);
11518}
11519/** Caches local names and their matching directive indices for query and template lookups. */
11520function cacheMatchingLocalNames(tNode, localRefs, exportsMap) {
11521 if (localRefs) {
11522 const localNames = tNode.localNames = [];
11523 // Local names must be stored in tNode in the same order that localRefs are defined
11524 // in the template to ensure the data is loaded in the same slots as their refs
11525 // in the template (for template queries).
11526 for (let i = 0; i < localRefs.length; i += 2) {
11527 const index = exportsMap[localRefs[i + 1]];
11528 if (index == null)
11529 throw new RuntimeError(-301 /* RuntimeErrorCode.EXPORT_NOT_FOUND */, ngDevMode && `Export of name '${localRefs[i + 1]}' not found!`);
11530 localNames.push(localRefs[i], index);
11531 }
11532 }
11533}
11534/**
11535 * Builds up an export map as directives are created, so local refs can be quickly mapped
11536 * to their directive instances.
11537 */
11538function saveNameToExportMap(directiveIdx, def, exportsMap) {
11539 if (exportsMap) {
11540 if (def.exportAs) {
11541 for (let i = 0; i < def.exportAs.length; i++) {
11542 exportsMap[def.exportAs[i]] = directiveIdx;
11543 }
11544 }
11545 if (isComponentDef(def))
11546 exportsMap[''] = directiveIdx;
11547 }
11548}
11549/**
11550 * Initializes the flags on the current node, setting all indices to the initial index,
11551 * the directive count to 0, and adding the isComponent flag.
11552 * @param index the initial index
11553 */
11554function initTNodeFlags(tNode, index, numberOfDirectives) {
11555 ngDevMode &&
11556 assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
11557 tNode.flags |= 1 /* TNodeFlags.isDirectiveHost */;
11558 // When the first directive is created on a node, save the index
11559 tNode.directiveStart = index;
11560 tNode.directiveEnd = index + numberOfDirectives;
11561 tNode.providerIndexes = index;
11562}
11563/**
11564 * Setup directive for instantiation.
11565 *
11566 * We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
11567 * as `LView`. `TView` gets the `DirectiveDef`.
11568 *
11569 * @param tView `TView`
11570 * @param tNode `TNode`
11571 * @param lView `LView`
11572 * @param directiveIndex Index where the directive will be stored in the Expando.
11573 * @param def `DirectiveDef`
11574 */
11575function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
11576 ngDevMode &&
11577 assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
11578 tView.data[directiveIndex] = def;
11579 const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
11580 // Even though `directiveFactory` will already be using `ɵɵdirectiveInject` in its generated code,
11581 // we also want to support `inject()` directly from the directive constructor context so we set
11582 // `ɵɵdirectiveInject` as the inject implementation here too.
11583 const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), ɵɵdirectiveInject);
11584 tView.blueprint[directiveIndex] = nodeInjectorFactory;
11585 lView[directiveIndex] = nodeInjectorFactory;
11586 registerHostBindingOpCodes(tView, tNode, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
11587}
11588function addComponentLogic(lView, hostTNode, def) {
11589 const native = getNativeByTNode(hostTNode, lView);
11590 const tView = getOrCreateComponentTView(def);
11591 // Only component views should be added to the view tree directly. Embedded views are
11592 // accessed through their containers because they may be removed / re-added later.
11593 const rendererFactory = lView[RENDERER_FACTORY];
11594 const componentView = addToViewTree(lView, createLView(lView, tView, null, def.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, native, hostTNode, rendererFactory, rendererFactory.createRenderer(native, def), null, null, null));
11595 // Component view will always be created before any injected LContainers,
11596 // so this is a regular element, wrap it with the component view
11597 lView[hostTNode.index] = componentView;
11598}
11599function elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace) {
11600 if (ngDevMode) {
11601 assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
11602 validateAgainstEventAttributes(name);
11603 assertTNodeType(tNode, 2 /* TNodeType.Element */, `Attempted to set attribute \`${name}\` on a container node. ` +
11604 `Host bindings are not valid on ng-container or ng-template.`);
11605 }
11606 const element = getNativeByTNode(tNode, lView);
11607 setElementAttribute(lView[RENDERER], element, namespace, tNode.value, name, value, sanitizer);
11608}
11609function setElementAttribute(renderer, element, namespace, tagName, name, value, sanitizer) {
11610 if (value == null) {
11611 ngDevMode && ngDevMode.rendererRemoveAttribute++;
11612 renderer.removeAttribute(element, name, namespace);
11613 }
11614 else {
11615 ngDevMode && ngDevMode.rendererSetAttribute++;
11616 const strValue = sanitizer == null ? renderStringify(value) : sanitizer(value, tagName || '', name);
11617 renderer.setAttribute(element, name, strValue, namespace);
11618 }
11619}
11620/**
11621 * Sets initial input properties on directive instances from attribute data
11622 *
11623 * @param lView Current LView that is being processed.
11624 * @param directiveIndex Index of the directive in directives array
11625 * @param instance Instance of the directive on which to set the initial inputs
11626 * @param def The directive def that contains the list of inputs
11627 * @param tNode The static data for this node
11628 */
11629function setInputsFromAttrs(lView, directiveIndex, instance, def, tNode, initialInputData) {
11630 const initialInputs = initialInputData[directiveIndex];
11631 if (initialInputs !== null) {
11632 const setInput = def.setInput;
11633 for (let i = 0; i < initialInputs.length;) {
11634 const publicName = initialInputs[i++];
11635 const privateName = initialInputs[i++];
11636 const value = initialInputs[i++];
11637 if (setInput !== null) {
11638 def.setInput(instance, value, publicName, privateName);
11639 }
11640 else {
11641 instance[privateName] = value;
11642 }
11643 if (ngDevMode) {
11644 const nativeElement = getNativeByTNode(tNode, lView);
11645 setNgReflectProperty(lView, nativeElement, tNode.type, privateName, value);
11646 }
11647 }
11648 }
11649}
11650/**
11651 * Generates initialInputData for a node and stores it in the template's static storage
11652 * so subsequent template invocations don't have to recalculate it.
11653 *
11654 * initialInputData is an array containing values that need to be set as input properties
11655 * for directives on this node, but only once on creation. We need this array to support
11656 * the case where you set an @Input property of a directive using attribute-like syntax.
11657 * e.g. if you have a `name` @Input, you can set it once like this:
11658 *
11659 * <my-component name="Bess"></my-component>
11660 *
11661 * @param inputs Input alias map that was generated from the directive def inputs.
11662 * @param directiveIndex Index of the directive that is currently being processed.
11663 * @param attrs Static attrs on this node.
11664 */
11665function generateInitialInputs(inputs, directiveIndex, attrs) {
11666 let inputsToStore = null;
11667 let i = 0;
11668 while (i < attrs.length) {
11669 const attrName = attrs[i];
11670 if (attrName === 0 /* AttributeMarker.NamespaceURI */) {
11671 // We do not allow inputs on namespaced attributes.
11672 i += 4;
11673 continue;
11674 }
11675 else if (attrName === 5 /* AttributeMarker.ProjectAs */) {
11676 // Skip over the `ngProjectAs` value.
11677 i += 2;
11678 continue;
11679 }
11680 // If we hit any other attribute markers, we're done anyway. None of those are valid inputs.
11681 if (typeof attrName === 'number')
11682 break;
11683 if (inputs.hasOwnProperty(attrName)) {
11684 if (inputsToStore === null)
11685 inputsToStore = [];
11686 // Find the input's public name from the input store. Note that we can be found easier
11687 // through the directive def, but we want to do it using the inputs store so that it can
11688 // account for host directive aliases.
11689 const inputConfig = inputs[attrName];
11690 for (let j = 0; j < inputConfig.length; j += 2) {
11691 if (inputConfig[j] === directiveIndex) {
11692 inputsToStore.push(attrName, inputConfig[j + 1], attrs[i + 1]);
11693 // A directive can't have multiple inputs with the same name so we can break here.
11694 break;
11695 }
11696 }
11697 }
11698 i += 2;
11699 }
11700 return inputsToStore;
11701}
11702//////////////////////////
11703//// ViewContainer & View
11704//////////////////////////
11705/**
11706 * Creates a LContainer, either from a container instruction, or for a ViewContainerRef.
11707 *
11708 * @param hostNative The host element for the LContainer
11709 * @param hostTNode The host TNode for the LContainer
11710 * @param currentView The parent view of the LContainer
11711 * @param native The native comment element
11712 * @param isForViewContainerRef Optional a flag indicating the ViewContainerRef case
11713 * @returns LContainer
11714 */
11715function createLContainer(hostNative, currentView, native, tNode) {
11716 ngDevMode && assertLView(currentView);
11717 const lContainer = [
11718 hostNative,
11719 true,
11720 false,
11721 currentView,
11722 null,
11723 0,
11724 tNode,
11725 native,
11726 null,
11727 null, // moved views
11728 ];
11729 ngDevMode &&
11730 assertEqual(lContainer.length, CONTAINER_HEADER_OFFSET, 'Should allocate correct number of slots for LContainer header.');
11731 return lContainer;
11732}
11733/**
11734 * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
11735 * them by executing an associated template function.
11736 */
11737function refreshEmbeddedViews(lView) {
11738 for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
11739 for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
11740 const embeddedLView = lContainer[i];
11741 const embeddedTView = embeddedLView[TVIEW];
11742 ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
11743 if (viewAttachedToChangeDetector(embeddedLView)) {
11744 refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
11745 }
11746 }
11747 }
11748}
11749/**
11750 * Mark transplanted views as needing to be refreshed at their insertion points.
11751 *
11752 * @param lView The `LView` that may have transplanted views.
11753 */
11754function markTransplantedViewsForRefresh(lView) {
11755 for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
11756 if (!lContainer[HAS_TRANSPLANTED_VIEWS])
11757 continue;
11758 const movedViews = lContainer[MOVED_VIEWS];
11759 ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS');
11760 for (let i = 0; i < movedViews.length; i++) {
11761 const movedLView = movedViews[i];
11762 const insertionLContainer = movedLView[PARENT];
11763 ngDevMode && assertLContainer(insertionLContainer);
11764 // We don't want to increment the counter if the moved LView was already marked for
11765 // refresh.
11766 if ((movedLView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) === 0) {
11767 updateTransplantedViewCount(insertionLContainer, 1);
11768 }
11769 // Note, it is possible that the `movedViews` is tracking views that are transplanted *and*
11770 // those that aren't (declaration component === insertion component). In the latter case,
11771 // it's fine to add the flag, as we will clear it immediately in
11772 // `refreshEmbeddedViews` for the view currently being refreshed.
11773 movedLView[FLAGS] |= 512 /* LViewFlags.RefreshTransplantedView */;
11774 }
11775 }
11776}
11777/////////////
11778/**
11779 * Refreshes components by entering the component view and processing its bindings, queries, etc.
11780 *
11781 * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
11782 */
11783function refreshComponent(hostLView, componentHostIdx) {
11784 ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
11785 const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
11786 // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
11787 if (viewAttachedToChangeDetector(componentView)) {
11788 const tView = componentView[TVIEW];
11789 if (componentView[FLAGS] & (16 /* LViewFlags.CheckAlways */ | 32 /* LViewFlags.Dirty */)) {
11790 refreshView(tView, componentView, tView.template, componentView[CONTEXT]);
11791 }
11792 else if (componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
11793 // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
11794 refreshContainsDirtyView(componentView);
11795 }
11796 }
11797}
11798/**
11799 * Refreshes all transplanted views marked with `LViewFlags.RefreshTransplantedView` that are
11800 * children or descendants of the given lView.
11801 *
11802 * @param lView The lView which contains descendant transplanted views that need to be refreshed.
11803 */
11804function refreshContainsDirtyView(lView) {
11805 for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
11806 for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
11807 const embeddedLView = lContainer[i];
11808 if (viewAttachedToChangeDetector(embeddedLView)) {
11809 if (embeddedLView[FLAGS] & 512 /* LViewFlags.RefreshTransplantedView */) {
11810 const embeddedTView = embeddedLView[TVIEW];
11811 ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
11812 refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
11813 }
11814 else if (embeddedLView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
11815 refreshContainsDirtyView(embeddedLView);
11816 }
11817 }
11818 }
11819 }
11820 const tView = lView[TVIEW];
11821 // Refresh child component views.
11822 const components = tView.components;
11823 if (components !== null) {
11824 for (let i = 0; i < components.length; i++) {
11825 const componentView = getComponentLViewByIndex(components[i], lView);
11826 // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
11827 if (viewAttachedToChangeDetector(componentView) &&
11828 componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
11829 refreshContainsDirtyView(componentView);
11830 }
11831 }
11832 }
11833}
11834function renderComponent(hostLView, componentHostIdx) {
11835 ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
11836 const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
11837 const componentTView = componentView[TVIEW];
11838 syncViewWithBlueprint(componentTView, componentView);
11839 renderView(componentTView, componentView, componentView[CONTEXT]);
11840}
11841/**
11842 * Syncs an LView instance with its blueprint if they have gotten out of sync.
11843 *
11844 * Typically, blueprints and their view instances should always be in sync, so the loop here
11845 * will be skipped. However, consider this case of two components side-by-side:
11846 *
11847 * App template:
11848 * ```
11849 * <comp></comp>
11850 * <comp></comp>
11851 * ```
11852 *
11853 * The following will happen:
11854 * 1. App template begins processing.
11855 * 2. First <comp> is matched as a component and its LView is created.
11856 * 3. Second <comp> is matched as a component and its LView is created.
11857 * 4. App template completes processing, so it's time to check child templates.
11858 * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
11859 * 6. Second <comp> template is checked. Its blueprint has been updated by the first
11860 * <comp> template, but its LView was created before this update, so it is out of sync.
11861 *
11862 * Note that embedded views inside ngFor loops will never be out of sync because these views
11863 * are processed as soon as they are created.
11864 *
11865 * @param tView The `TView` that contains the blueprint for syncing
11866 * @param lView The view to sync
11867 */
11868function syncViewWithBlueprint(tView, lView) {
11869 for (let i = lView.length; i < tView.blueprint.length; i++) {
11870 lView.push(tView.blueprint[i]);
11871 }
11872}
11873/**
11874 * Adds LView or LContainer to the end of the current view tree.
11875 *
11876 * This structure will be used to traverse through nested views to remove listeners
11877 * and call onDestroy callbacks.
11878 *
11879 * @param lView The view where LView or LContainer should be added
11880 * @param adjustedHostIndex Index of the view's host node in LView[], adjusted for header
11881 * @param lViewOrLContainer The LView or LContainer to add to the view tree
11882 * @returns The state passed in
11883 */
11884function addToViewTree(lView, lViewOrLContainer) {
11885 // TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer
11886 // to the end of the queue, which means if the developer retrieves the LContainers from RNodes out
11887 // of order, the change detection will run out of order, as the act of retrieving the the
11888 // LContainer from the RNode is what adds it to the queue.
11889 if (lView[CHILD_HEAD]) {
11890 lView[CHILD_TAIL][NEXT] = lViewOrLContainer;
11891 }
11892 else {
11893 lView[CHILD_HEAD] = lViewOrLContainer;
11894 }
11895 lView[CHILD_TAIL] = lViewOrLContainer;
11896 return lViewOrLContainer;
11897}
11898///////////////////////////////
11899//// Change detection
11900///////////////////////////////
11901/**
11902 * Marks current view and all ancestors dirty.
11903 *
11904 * Returns the root view because it is found as a byproduct of marking the view tree
11905 * dirty, and can be used by methods that consume markViewDirty() to easily schedule
11906 * change detection. Otherwise, such methods would need to traverse up the view tree
11907 * an additional time to get the root view and schedule a tick on it.
11908 *
11909 * @param lView The starting LView to mark dirty
11910 * @returns the root LView
11911 */
11912function markViewDirty(lView) {
11913 while (lView) {
11914 lView[FLAGS] |= 32 /* LViewFlags.Dirty */;
11915 const parent = getLViewParent(lView);
11916 // Stop traversing up as soon as you find a root view that wasn't attached to any container
11917 if (isRootView(lView) && !parent) {
11918 return lView;
11919 }
11920 // continue otherwise
11921 lView = parent;
11922 }
11923 return null;
11924}
11925function detectChangesInternal(tView, lView, context, notifyErrorHandler = true) {
11926 const rendererFactory = lView[RENDERER_FACTORY];
11927 // Check no changes mode is a dev only mode used to verify that bindings have not changed
11928 // since they were assigned. We do not want to invoke renderer factory functions in that mode
11929 // to avoid any possible side-effects.
11930 const checkNoChangesMode = !!ngDevMode && isInCheckNoChangesMode();
11931 if (!checkNoChangesMode && rendererFactory.begin)
11932 rendererFactory.begin();
11933 try {
11934 refreshView(tView, lView, tView.template, context);
11935 }
11936 catch (error) {
11937 if (notifyErrorHandler) {
11938 handleError(lView, error);
11939 }
11940 throw error;
11941 }
11942 finally {
11943 if (!checkNoChangesMode && rendererFactory.end)
11944 rendererFactory.end();
11945 }
11946}
11947function checkNoChangesInternal(tView, lView, context, notifyErrorHandler = true) {
11948 setIsInCheckNoChangesMode(true);
11949 try {
11950 detectChangesInternal(tView, lView, context, notifyErrorHandler);
11951 }
11952 finally {
11953 setIsInCheckNoChangesMode(false);
11954 }
11955}
11956function executeViewQueryFn(flags, viewQueryFn, component) {
11957 ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');
11958 setCurrentQueryIndex(0);
11959 viewQueryFn(flags, component);
11960}
11961///////////////////////////////
11962//// Bindings & interpolations
11963///////////////////////////////
11964/**
11965 * Stores meta-data for a property binding to be used by TestBed's `DebugElement.properties`.
11966 *
11967 * In order to support TestBed's `DebugElement.properties` we need to save, for each binding:
11968 * - a bound property name;
11969 * - a static parts of interpolated strings;
11970 *
11971 * A given property metadata is saved at the binding's index in the `TView.data` (in other words, a
11972 * property binding metadata will be stored in `TView.data` at the same index as a bound value in
11973 * `LView`). Metadata are represented as `INTERPOLATION_DELIMITER`-delimited string with the
11974 * following format:
11975 * - `propertyName` for bound properties;
11976 * - `propertyName�prefix�interpolation_static_part1�..interpolation_static_partN�suffix` for
11977 * interpolated properties.
11978 *
11979 * @param tData `TData` where meta-data will be saved;
11980 * @param tNode `TNode` that is a target of the binding;
11981 * @param propertyName bound property name;
11982 * @param bindingIndex binding index in `LView`
11983 * @param interpolationParts static interpolation parts (for property interpolations)
11984 */
11985function storePropertyBindingMetadata(tData, tNode, propertyName, bindingIndex, ...interpolationParts) {
11986 // Binding meta-data are stored only the first time a given property instruction is processed.
11987 // Since we don't have a concept of the "first update pass" we need to check for presence of the
11988 // binding meta-data to decide if one should be stored (or if was stored already).
11989 if (tData[bindingIndex] === null) {
11990 if (tNode.inputs == null || !tNode.inputs[propertyName]) {
11991 const propBindingIdxs = tNode.propertyBindings || (tNode.propertyBindings = []);
11992 propBindingIdxs.push(bindingIndex);
11993 let bindingMetadata = propertyName;
11994 if (interpolationParts.length > 0) {
11995 bindingMetadata +=
11996 INTERPOLATION_DELIMITER + interpolationParts.join(INTERPOLATION_DELIMITER);
11997 }
11998 tData[bindingIndex] = bindingMetadata;
11999 }
12000 }
12001}
12002function getOrCreateLViewCleanup(view) {
12003 // top level variables should not be exported for performance reasons (PERF_NOTES.md)
12004 return view[CLEANUP] || (view[CLEANUP] = []);
12005}
12006function getOrCreateTViewCleanup(tView) {
12007 return tView.cleanup || (tView.cleanup = []);
12008}
12009/**
12010 * There are cases where the sub component's renderer needs to be included
12011 * instead of the current renderer (see the componentSyntheticHost* instructions).
12012 */
12013function loadComponentRenderer(currentDef, tNode, lView) {
12014 // TODO(FW-2043): the `currentDef` is null when host bindings are invoked while creating root
12015 // component (see packages/core/src/render3/component.ts). This is not consistent with the process
12016 // of creating inner components, when current directive index is available in the state. In order
12017 // to avoid relying on current def being `null` (thus special-casing root component creation), the
12018 // process of creating root component should be unified with the process of creating inner
12019 // components.
12020 if (currentDef === null || isComponentDef(currentDef)) {
12021 lView = unwrapLView(lView[tNode.index]);
12022 }
12023 return lView[RENDERER];
12024}
12025/** Handles an error thrown in an LView. */
12026function handleError(lView, error) {
12027 const injector = lView[INJECTOR$1];
12028 const errorHandler = injector ? injector.get(ErrorHandler, null) : null;
12029 errorHandler && errorHandler.handleError(error);
12030}
12031/**
12032 * Set the inputs of directives at the current node to corresponding value.
12033 *
12034 * @param tView The current TView
12035 * @param lView the `LView` which contains the directives.
12036 * @param inputs mapping between the public "input" name and privately-known,
12037 * possibly minified, property names to write to.
12038 * @param value Value to set.
12039 */
12040function setInputsForProperty(tView, lView, inputs, publicName, value) {
12041 for (let i = 0; i < inputs.length;) {
12042 const index = inputs[i++];
12043 const privateName = inputs[i++];
12044 const instance = lView[index];
12045 ngDevMode && assertIndexInRange(lView, index);
12046 const def = tView.data[index];
12047 if (def.setInput !== null) {
12048 def.setInput(instance, value, publicName, privateName);
12049 }
12050 else {
12051 instance[privateName] = value;
12052 }
12053 }
12054}
12055/**
12056 * Updates a text binding at a given index in a given LView.
12057 */
12058function textBindingInternal(lView, index, value) {
12059 ngDevMode && assertString(value, 'Value should be a string');
12060 ngDevMode && assertNotSame(value, NO_CHANGE, 'value should not be NO_CHANGE');
12061 ngDevMode && assertIndexInRange(lView, index);
12062 const element = getNativeByIndex(index, lView);
12063 ngDevMode && assertDefined(element, 'native element should exist');
12064 updateTextNode(lView[RENDERER], element, value);
12065}
12066
12067/**
12068 * Compute the static styling (class/style) from `TAttributes`.
12069 *
12070 * This function should be called during `firstCreatePass` only.
12071 *
12072 * @param tNode The `TNode` into which the styling information should be loaded.
12073 * @param attrs `TAttributes` containing the styling information.
12074 * @param writeToHost Where should the resulting static styles be written?
12075 * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost`
12076 * - `true` Write to `TNode.styles` / `TNode.classes`
12077 */
12078function computeStaticStyling(tNode, attrs, writeToHost) {
12079 ngDevMode &&
12080 assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only');
12081 let styles = writeToHost ? tNode.styles : null;
12082 let classes = writeToHost ? tNode.classes : null;
12083 let mode = 0;
12084 if (attrs !== null) {
12085 for (let i = 0; i < attrs.length; i++) {
12086 const value = attrs[i];
12087 if (typeof value === 'number') {
12088 mode = value;
12089 }
12090 else if (mode == 1 /* AttributeMarker.Classes */) {
12091 classes = concatStringsWithSpace(classes, value);
12092 }
12093 else if (mode == 2 /* AttributeMarker.Styles */) {
12094 const style = value;
12095 const styleValue = attrs[++i];
12096 styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';');
12097 }
12098 }
12099 }
12100 writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles;
12101 writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes;
12102}
12103
12104function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
12105 while (tNode !== null) {
12106 ngDevMode &&
12107 assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */ | 16 /* TNodeType.Projection */ | 32 /* TNodeType.Icu */);
12108 const lNode = lView[tNode.index];
12109 if (lNode !== null) {
12110 result.push(unwrapRNode(lNode));
12111 }
12112 // A given lNode can represent either a native node or a LContainer (when it is a host of a
12113 // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
12114 // from the views in this container.
12115 if (isLContainer(lNode)) {
12116 for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) {
12117 const lViewInAContainer = lNode[i];
12118 const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
12119 if (lViewFirstChildTNode !== null) {
12120 collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
12121 }
12122 }
12123 }
12124 const tNodeType = tNode.type;
12125 if (tNodeType & 8 /* TNodeType.ElementContainer */) {
12126 collectNativeNodes(tView, lView, tNode.child, result);
12127 }
12128 else if (tNodeType & 32 /* TNodeType.Icu */) {
12129 const nextRNode = icuContainerIterate(tNode, lView);
12130 let rNode;
12131 while (rNode = nextRNode()) {
12132 result.push(rNode);
12133 }
12134 }
12135 else if (tNodeType & 16 /* TNodeType.Projection */) {
12136 const nodesInSlot = getProjectionNodes(lView, tNode);
12137 if (Array.isArray(nodesInSlot)) {
12138 result.push(...nodesInSlot);
12139 }
12140 else {
12141 const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
12142 ngDevMode && assertParentView(parentView);
12143 collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
12144 }
12145 }
12146 tNode = isProjection ? tNode.projectionNext : tNode.next;
12147 }
12148 return result;
12149}
12150
12151class ViewRef {
12152 get rootNodes() {
12153 const lView = this._lView;
12154 const tView = lView[TVIEW];
12155 return collectNativeNodes(tView, lView, tView.firstChild, []);
12156 }
12157 constructor(
12158 /**
12159 * This represents `LView` associated with the component when ViewRef is a ChangeDetectorRef.
12160 *
12161 * When ViewRef is created for a dynamic component, this also represents the `LView` for the
12162 * component.
12163 *
12164 * For a "regular" ViewRef created for an embedded view, this is the `LView` for the embedded
12165 * view.
12166 *
12167 * @internal
12168 */
12169 _lView,
12170 /**
12171 * This represents the `LView` associated with the point where `ChangeDetectorRef` was
12172 * requested.
12173 *
12174 * This may be different from `_lView` if the `_cdRefInjectingView` is an embedded view.
12175 */
12176 _cdRefInjectingView) {
12177 this._lView = _lView;
12178 this._cdRefInjectingView = _cdRefInjectingView;
12179 this._appRef = null;
12180 this._attachedToViewContainer = false;
12181 }
12182 get context() {
12183 return this._lView[CONTEXT];
12184 }
12185 set context(value) {
12186 this._lView[CONTEXT] = value;
12187 }
12188 get destroyed() {
12189 return (this._lView[FLAGS] & 128 /* LViewFlags.Destroyed */) === 128 /* LViewFlags.Destroyed */;
12190 }
12191 destroy() {
12192 if (this._appRef) {
12193 this._appRef.detachView(this);
12194 }
12195 else if (this._attachedToViewContainer) {
12196 const parent = this._lView[PARENT];
12197 if (isLContainer(parent)) {
12198 const viewRefs = parent[VIEW_REFS];
12199 const index = viewRefs ? viewRefs.indexOf(this) : -1;
12200 if (index > -1) {
12201 ngDevMode &&
12202 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.');
12203 detachView(parent, index);
12204 removeFromArray(viewRefs, index);
12205 }
12206 }
12207 this._attachedToViewContainer = false;
12208 }
12209 destroyLView(this._lView[TVIEW], this._lView);
12210 }
12211 onDestroy(callback) {
12212 storeCleanupWithContext(this._lView[TVIEW], this._lView, null, callback);
12213 }
12214 /**
12215 * Marks a view and all of its ancestors dirty.
12216 *
12217 * This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is
12218 * checked when it needs to be re-rendered but the two normal triggers haven't marked it
12219 * dirty (i.e. inputs haven't changed and events haven't fired in the view).
12220 *
12221 * <!-- TODO: Add a link to a chapter on OnPush components -->
12222 *
12223 * @usageNotes
12224 * ### Example
12225 *
12226 * ```typescript
12227 * @Component({
12228 * selector: 'app-root',
12229 * template: `Number of ticks: {{numberOfTicks}}`
12230 * changeDetection: ChangeDetectionStrategy.OnPush,
12231 * })
12232 * class AppComponent {
12233 * numberOfTicks = 0;
12234 *
12235 * constructor(private ref: ChangeDetectorRef) {
12236 * setInterval(() => {
12237 * this.numberOfTicks++;
12238 * // the following is required, otherwise the view will not be updated
12239 * this.ref.markForCheck();
12240 * }, 1000);
12241 * }
12242 * }
12243 * ```
12244 */
12245 markForCheck() {
12246 markViewDirty(this._cdRefInjectingView || this._lView);
12247 }
12248 /**
12249 * Detaches the view from the change detection tree.
12250 *
12251 * Detached views will not be checked during change detection runs until they are
12252 * re-attached, even if they are dirty. `detach` can be used in combination with
12253 * {@link ChangeDetectorRef#detectChanges detectChanges} to implement local change
12254 * detection checks.
12255 *
12256 * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
12257 * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
12258 *
12259 * @usageNotes
12260 * ### Example
12261 *
12262 * The following example defines a component with a large list of readonly data.
12263 * Imagine the data changes constantly, many times per second. For performance reasons,
12264 * we want to check and update the list every five seconds. We can do that by detaching
12265 * the component's change detector and doing a local check every five seconds.
12266 *
12267 * ```typescript
12268 * class DataProvider {
12269 * // in a real application the returned data will be different every time
12270 * get data() {
12271 * return [1,2,3,4,5];
12272 * }
12273 * }
12274 *
12275 * @Component({
12276 * selector: 'giant-list',
12277 * template: `
12278 * <li *ngFor="let d of dataProvider.data">Data {{d}}</li>
12279 * `,
12280 * })
12281 * class GiantList {
12282 * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
12283 * ref.detach();
12284 * setInterval(() => {
12285 * this.ref.detectChanges();
12286 * }, 5000);
12287 * }
12288 * }
12289 *
12290 * @Component({
12291 * selector: 'app',
12292 * providers: [DataProvider],
12293 * template: `
12294 * <giant-list><giant-list>
12295 * `,
12296 * })
12297 * class App {
12298 * }
12299 * ```
12300 */
12301 detach() {
12302 this._lView[FLAGS] &= ~64 /* LViewFlags.Attached */;
12303 }
12304 /**
12305 * Re-attaches a view to the change detection tree.
12306 *
12307 * This can be used to re-attach views that were previously detached from the tree
12308 * using {@link ChangeDetectorRef#detach detach}. Views are attached to the tree by default.
12309 *
12310 * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
12311 *
12312 * @usageNotes
12313 * ### Example
12314 *
12315 * The following example creates a component displaying `live` data. The component will detach
12316 * its change detector from the main change detector tree when the component's live property
12317 * is set to false.
12318 *
12319 * ```typescript
12320 * class DataProvider {
12321 * data = 1;
12322 *
12323 * constructor() {
12324 * setInterval(() => {
12325 * this.data = this.data * 2;
12326 * }, 500);
12327 * }
12328 * }
12329 *
12330 * @Component({
12331 * selector: 'live-data',
12332 * inputs: ['live'],
12333 * template: 'Data: {{dataProvider.data}}'
12334 * })
12335 * class LiveData {
12336 * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
12337 *
12338 * set live(value) {
12339 * if (value) {
12340 * this.ref.reattach();
12341 * } else {
12342 * this.ref.detach();
12343 * }
12344 * }
12345 * }
12346 *
12347 * @Component({
12348 * selector: 'app-root',
12349 * providers: [DataProvider],
12350 * template: `
12351 * Live Update: <input type="checkbox" [(ngModel)]="live">
12352 * <live-data [live]="live"><live-data>
12353 * `,
12354 * })
12355 * class AppComponent {
12356 * live = true;
12357 * }
12358 * ```
12359 */
12360 reattach() {
12361 this._lView[FLAGS] |= 64 /* LViewFlags.Attached */;
12362 }
12363 /**
12364 * Checks the view and its children.
12365 *
12366 * This can also be used in combination with {@link ChangeDetectorRef#detach detach} to implement
12367 * local change detection checks.
12368 *
12369 * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
12370 * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
12371 *
12372 * @usageNotes
12373 * ### Example
12374 *
12375 * The following example defines a component with a large list of readonly data.
12376 * Imagine, the data changes constantly, many times per second. For performance reasons,
12377 * we want to check and update the list every five seconds.
12378 *
12379 * We can do that by detaching the component's change detector and doing a local change detection
12380 * check every five seconds.
12381 *
12382 * See {@link ChangeDetectorRef#detach detach} for more information.
12383 */
12384 detectChanges() {
12385 detectChangesInternal(this._lView[TVIEW], this._lView, this.context);
12386 }
12387 /**
12388 * Checks the change detector and its children, and throws if any changes are detected.
12389 *
12390 * This is used in development mode to verify that running change detection doesn't
12391 * introduce other changes.
12392 */
12393 checkNoChanges() {
12394 if (ngDevMode) {
12395 checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context);
12396 }
12397 }
12398 attachToViewContainerRef() {
12399 if (this._appRef) {
12400 throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached directly to the ApplicationRef!');
12401 }
12402 this._attachedToViewContainer = true;
12403 }
12404 detachFromAppRef() {
12405 this._appRef = null;
12406 renderDetachView(this._lView[TVIEW], this._lView);
12407 }
12408 attachToAppRef(appRef) {
12409 if (this._attachedToViewContainer) {
12410 throw new RuntimeError(902 /* RuntimeErrorCode.VIEW_ALREADY_ATTACHED */, ngDevMode && 'This view is already attached to a ViewContainer!');
12411 }
12412 this._appRef = appRef;
12413 }
12414}
12415/** @internal */
12416class RootViewRef extends ViewRef {
12417 constructor(_view) {
12418 super(_view);
12419 this._view = _view;
12420 }
12421 detectChanges() {
12422 const lView = this._view;
12423 const tView = lView[TVIEW];
12424 const context = lView[CONTEXT];
12425 detectChangesInternal(tView, lView, context, false);
12426 }
12427 checkNoChanges() {
12428 if (ngDevMode) {
12429 const lView = this._view;
12430 const tView = lView[TVIEW];
12431 const context = lView[CONTEXT];
12432 checkNoChangesInternal(tView, lView, context, false);
12433 }
12434 }
12435 get context() {
12436 return null;
12437 }
12438}
12439
12440class ComponentFactoryResolver extends ComponentFactoryResolver$1 {
12441 /**
12442 * @param ngModule The NgModuleRef to which all resolved factories are bound.
12443 */
12444 constructor(ngModule) {
12445 super();
12446 this.ngModule = ngModule;
12447 }
12448 resolveComponentFactory(component) {
12449 ngDevMode && assertComponentType(component);
12450 const componentDef = getComponentDef$1(component);
12451 return new ComponentFactory(componentDef, this.ngModule);
12452 }
12453}
12454function toRefArray(map) {
12455 const array = [];
12456 for (let nonMinified in map) {
12457 if (map.hasOwnProperty(nonMinified)) {
12458 const minified = map[nonMinified];
12459 array.push({ propName: minified, templateName: nonMinified });
12460 }
12461 }
12462 return array;
12463}
12464function getNamespace(elementName) {
12465 const name = elementName.toLowerCase();
12466 return name === 'svg' ? SVG_NAMESPACE : (name === 'math' ? MATH_ML_NAMESPACE : null);
12467}
12468/**
12469 * Injector that looks up a value using a specific injector, before falling back to the module
12470 * injector. Used primarily when creating components or embedded views dynamically.
12471 */
12472class ChainedInjector {
12473 constructor(injector, parentInjector) {
12474 this.injector = injector;
12475 this.parentInjector = parentInjector;
12476 }
12477 get(token, notFoundValue, flags) {
12478 flags = convertToBitFlags(flags);
12479 const value = this.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
12480 if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
12481 notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
12482 // Return the value from the root element injector when
12483 // - it provides it
12484 // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
12485 // - the module injector should not be checked
12486 // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
12487 return value;
12488 }
12489 return this.parentInjector.get(token, notFoundValue, flags);
12490 }
12491}
12492/**
12493 * ComponentFactory interface implementation.
12494 */
12495class ComponentFactory extends ComponentFactory$1 {
12496 get inputs() {
12497 return toRefArray(this.componentDef.inputs);
12498 }
12499 get outputs() {
12500 return toRefArray(this.componentDef.outputs);
12501 }
12502 /**
12503 * @param componentDef The component definition.
12504 * @param ngModule The NgModuleRef to which the factory is bound.
12505 */
12506 constructor(componentDef, ngModule) {
12507 super();
12508 this.componentDef = componentDef;
12509 this.ngModule = ngModule;
12510 this.componentType = componentDef.type;
12511 this.selector = stringifyCSSSelectorList(componentDef.selectors);
12512 this.ngContentSelectors =
12513 componentDef.ngContentSelectors ? componentDef.ngContentSelectors : [];
12514 this.isBoundToModule = !!ngModule;
12515 }
12516 create(injector, projectableNodes, rootSelectorOrNode, environmentInjector) {
12517 environmentInjector = environmentInjector || this.ngModule;
12518 let realEnvironmentInjector = environmentInjector instanceof EnvironmentInjector ?
12519 environmentInjector :
12520 environmentInjector?.injector;
12521 if (realEnvironmentInjector && this.componentDef.getStandaloneInjector !== null) {
12522 realEnvironmentInjector = this.componentDef.getStandaloneInjector(realEnvironmentInjector) ||
12523 realEnvironmentInjector;
12524 }
12525 const rootViewInjector = realEnvironmentInjector ? new ChainedInjector(injector, realEnvironmentInjector) : injector;
12526 const rendererFactory = rootViewInjector.get(RendererFactory2, null);
12527 if (rendererFactory === null) {
12528 throw new RuntimeError(407 /* RuntimeErrorCode.RENDERER_NOT_FOUND */, ngDevMode &&
12529 'Angular was not able to inject a renderer (RendererFactory2). ' +
12530 'Likely this is due to a broken DI hierarchy. ' +
12531 'Make sure that any injector used to create this component has a correct parent.');
12532 }
12533 const sanitizer = rootViewInjector.get(Sanitizer, null);
12534 const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
12535 // Determine a tag name used for creating host elements when this component is created
12536 // dynamically. Default to 'div' if this component did not specify any tag name in its selector.
12537 const elementName = this.componentDef.selectors[0][0] || 'div';
12538 const hostRNode = rootSelectorOrNode ?
12539 locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation) :
12540 createElementNode(hostRenderer, elementName, getNamespace(elementName));
12541 const rootFlags = this.componentDef.onPush ? 32 /* LViewFlags.Dirty */ | 256 /* LViewFlags.IsRoot */ :
12542 16 /* LViewFlags.CheckAlways */ | 256 /* LViewFlags.IsRoot */;
12543 // Create the root view. Uses empty TView and ContentTemplate.
12544 const rootTView = createTView(0 /* TViewType.Root */, null, null, 1, 0, null, null, null, null, null);
12545 const rootLView = createLView(null, rootTView, null, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector, null);
12546 // rootView is the parent when bootstrapping
12547 // TODO(misko): it looks like we are entering view here but we don't really need to as
12548 // `renderView` does that. However as the code is written it is needed because
12549 // `createRootComponentView` and `createRootComponent` both read global state. Fixing those
12550 // issues would allow us to drop this.
12551 enterView(rootLView);
12552 let component;
12553 let tElementNode;
12554 try {
12555 const rootComponentDef = this.componentDef;
12556 let rootDirectives;
12557 let hostDirectiveDefs = null;
12558 if (rootComponentDef.findHostDirectiveDefs) {
12559 rootDirectives = [];
12560 hostDirectiveDefs = new Map();
12561 rootComponentDef.findHostDirectiveDefs(rootComponentDef, rootDirectives, hostDirectiveDefs);
12562 rootDirectives.push(rootComponentDef);
12563 }
12564 else {
12565 rootDirectives = [rootComponentDef];
12566 }
12567 const hostTNode = createRootComponentTNode(rootLView, hostRNode);
12568 const componentView = createRootComponentView(hostTNode, hostRNode, rootComponentDef, rootDirectives, rootLView, rendererFactory, hostRenderer);
12569 tElementNode = getTNode(rootTView, HEADER_OFFSET);
12570 // TODO(crisbeto): in practice `hostRNode` should always be defined, but there are some tests
12571 // where the renderer is mocked out and `undefined` is returned. We should update the tests so
12572 // that this check can be removed.
12573 if (hostRNode) {
12574 setRootNodeAttributes(hostRenderer, rootComponentDef, hostRNode, rootSelectorOrNode);
12575 }
12576 if (projectableNodes !== undefined) {
12577 projectNodes(tElementNode, this.ngContentSelectors, projectableNodes);
12578 }
12579 // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
12580 // executed here?
12581 // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
12582 component = createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, [LifecycleHooksFeature]);
12583 renderView(rootTView, rootLView, null);
12584 }
12585 finally {
12586 leaveView();
12587 }
12588 return new ComponentRef(this.componentType, component, createElementRef(tElementNode, rootLView), rootLView, tElementNode);
12589 }
12590}
12591/**
12592 * Represents an instance of a Component created via a {@link ComponentFactory}.
12593 *
12594 * `ComponentRef` provides access to the Component Instance as well other objects related to this
12595 * Component Instance and allows you to destroy the Component Instance via the {@link #destroy}
12596 * method.
12597 *
12598 */
12599class ComponentRef extends ComponentRef$1 {
12600 constructor(componentType, instance, location, _rootLView, _tNode) {
12601 super();
12602 this.location = location;
12603 this._rootLView = _rootLView;
12604 this._tNode = _tNode;
12605 this.instance = instance;
12606 this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView);
12607 this.componentType = componentType;
12608 }
12609 setInput(name, value) {
12610 const inputData = this._tNode.inputs;
12611 let dataValue;
12612 if (inputData !== null && (dataValue = inputData[name])) {
12613 const lView = this._rootLView;
12614 setInputsForProperty(lView[TVIEW], lView, dataValue, name, value);
12615 markDirtyIfOnPush(lView, this._tNode.index);
12616 }
12617 else {
12618 if (ngDevMode) {
12619 const cmpNameForError = stringifyForError(this.componentType);
12620 let message = `Can't set value of the '${name}' input on the '${cmpNameForError}' component. `;
12621 message += `Make sure that the '${name}' property is annotated with @Input() or a mapped @Input('${name}') exists.`;
12622 reportUnknownPropertyError(message);
12623 }
12624 }
12625 }
12626 get injector() {
12627 return new NodeInjector(this._tNode, this._rootLView);
12628 }
12629 destroy() {
12630 this.hostView.destroy();
12631 }
12632 onDestroy(callback) {
12633 this.hostView.onDestroy(callback);
12634 }
12635}
12636// TODO: A hack to not pull in the NullInjector from @angular/core.
12637const NULL_INJECTOR = {
12638 get: (token, notFoundValue) => {
12639 throwProviderNotFoundError(token, 'NullInjector');
12640 }
12641};
12642/** Creates a TNode that can be used to instantiate a root component. */
12643function createRootComponentTNode(lView, rNode) {
12644 const tView = lView[TVIEW];
12645 const index = HEADER_OFFSET;
12646 ngDevMode && assertIndexInRange(lView, index);
12647 lView[index] = rNode;
12648 // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
12649 // the same time we want to communicate the debug `TNode` that this is a special `TNode`
12650 // representing a host element.
12651 return getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, '#host', null);
12652}
12653/**
12654 * Creates the root component view and the root component node.
12655 *
12656 * @param rNode Render host element.
12657 * @param rootComponentDef ComponentDef
12658 * @param rootView The parent view where the host node is stored
12659 * @param rendererFactory Factory to be used for creating child renderers.
12660 * @param hostRenderer The current renderer
12661 * @param sanitizer The sanitizer, if provided
12662 *
12663 * @returns Component view created
12664 */
12665function createRootComponentView(tNode, rNode, rootComponentDef, rootDirectives, rootView, rendererFactory, hostRenderer, sanitizer) {
12666 const tView = rootView[TVIEW];
12667 applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer);
12668 const viewRenderer = rendererFactory.createRenderer(rNode, rootComponentDef);
12669 const componentView = createLView(rootView, getOrCreateComponentTView(rootComponentDef), null, rootComponentDef.onPush ? 32 /* LViewFlags.Dirty */ : 16 /* LViewFlags.CheckAlways */, rootView[tNode.index], tNode, rendererFactory, viewRenderer, sanitizer || null, null, null);
12670 if (tView.firstCreatePass) {
12671 markAsComponentHost(tView, tNode, rootDirectives.length - 1);
12672 }
12673 addToViewTree(rootView, componentView);
12674 // Store component view at node index, with node as the HOST
12675 return rootView[tNode.index] = componentView;
12676}
12677/** Sets up the styling information on a root component. */
12678function applyRootComponentStyling(rootDirectives, tNode, rNode, hostRenderer) {
12679 for (const def of rootDirectives) {
12680 tNode.mergedAttrs = mergeHostAttrs(tNode.mergedAttrs, def.hostAttrs);
12681 }
12682 if (tNode.mergedAttrs !== null) {
12683 computeStaticStyling(tNode, tNode.mergedAttrs, true);
12684 if (rNode !== null) {
12685 setupStaticAttributes(hostRenderer, rNode, tNode);
12686 }
12687 }
12688}
12689/**
12690 * Creates a root component and sets it up with features and host bindings.Shared by
12691 * renderComponent() and ViewContainerRef.createComponent().
12692 */
12693function createRootComponent(componentView, rootComponentDef, rootDirectives, hostDirectiveDefs, rootLView, hostFeatures) {
12694 const rootTNode = getCurrentTNode();
12695 ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
12696 const tView = rootLView[TVIEW];
12697 const native = getNativeByTNode(rootTNode, rootLView);
12698 initializeDirectives(tView, rootLView, rootTNode, rootDirectives, null, hostDirectiveDefs);
12699 for (let i = 0; i < rootDirectives.length; i++) {
12700 const directiveIndex = rootTNode.directiveStart + i;
12701 const directiveInstance = getNodeInjectable(rootLView, tView, directiveIndex, rootTNode);
12702 attachPatchData(directiveInstance, rootLView);
12703 }
12704 invokeDirectivesHostBindings(tView, rootLView, rootTNode);
12705 if (native) {
12706 attachPatchData(native, rootLView);
12707 }
12708 // We're guaranteed for the `componentOffset` to be positive here
12709 // since a root component always matches a component def.
12710 ngDevMode &&
12711 assertGreaterThan(rootTNode.componentOffset, -1, 'componentOffset must be great than -1');
12712 const component = getNodeInjectable(rootLView, tView, rootTNode.directiveStart + rootTNode.componentOffset, rootTNode);
12713 componentView[CONTEXT] = rootLView[CONTEXT] = component;
12714 if (hostFeatures !== null) {
12715 for (const feature of hostFeatures) {
12716 feature(component, rootComponentDef);
12717 }
12718 }
12719 // We want to generate an empty QueryList for root content queries for backwards
12720 // compatibility with ViewEngine.
12721 executeContentQueries(tView, rootTNode, componentView);
12722 return component;
12723}
12724/** Sets the static attributes on a root component. */
12725function setRootNodeAttributes(hostRenderer, componentDef, hostRNode, rootSelectorOrNode) {
12726 if (rootSelectorOrNode) {
12727 setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
12728 }
12729 else {
12730 // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
12731 // is not defined), also apply attributes and classes extracted from component selector.
12732 // Extract attributes and classes from the first selector only to match VE behavior.
12733 const { attrs, classes } = extractAttrsAndClassesFromSelector(componentDef.selectors[0]);
12734 if (attrs) {
12735 setUpAttributes(hostRenderer, hostRNode, attrs);
12736 }
12737 if (classes && classes.length > 0) {
12738 writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
12739 }
12740 }
12741}
12742/** Projects the `projectableNodes` that were specified when creating a root component. */
12743function projectNodes(tNode, ngContentSelectors, projectableNodes) {
12744 const projection = tNode.projection = [];
12745 for (let i = 0; i < ngContentSelectors.length; i++) {
12746 const nodesforSlot = projectableNodes[i];
12747 // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
12748 // case). Here we do normalize passed data structure to be an array of arrays to avoid
12749 // complex checks down the line.
12750 // We also normalize the length of the passed in projectable nodes (to match the number of
12751 // <ng-container> slots defined by a component).
12752 projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
12753 }
12754}
12755/**
12756 * Used to enable lifecycle hooks on the root component.
12757 *
12758 * Include this feature when calling `renderComponent` if the root component
12759 * you are rendering has lifecycle hooks defined. Otherwise, the hooks won't
12760 * be called properly.
12761 *
12762 * Example:
12763 *
12764 * ```
12765 * renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]});
12766 * ```
12767 */
12768function LifecycleHooksFeature() {
12769 const tNode = getCurrentTNode();
12770 ngDevMode && assertDefined(tNode, 'TNode is required');
12771 registerPostOrderHooks(getLView()[TVIEW], tNode);
12772}
12773
12774function getSuperType(type) {
12775 return Object.getPrototypeOf(type.prototype).constructor;
12776}
12777/**
12778 * Merges the definition from a super class to a sub class.
12779 * @param definition The definition that is a SubClass of another directive of component
12780 *
12781 * @codeGenApi
12782 */
12783function ɵɵInheritDefinitionFeature(definition) {
12784 let superType = getSuperType(definition.type);
12785 let shouldInheritFields = true;
12786 const inheritanceChain = [definition];
12787 while (superType) {
12788 let superDef = undefined;
12789 if (isComponentDef(definition)) {
12790 // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
12791 superDef = superType.ɵcmp || superType.ɵdir;
12792 }
12793 else {
12794 if (superType.ɵcmp) {
12795 throw new RuntimeError(903 /* RuntimeErrorCode.INVALID_INHERITANCE */, ngDevMode &&
12796 `Directives cannot inherit Components. Directive ${stringifyForError(definition.type)} is attempting to extend component ${stringifyForError(superType)}`);
12797 }
12798 // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
12799 superDef = superType.ɵdir;
12800 }
12801 if (superDef) {
12802 if (shouldInheritFields) {
12803 inheritanceChain.push(superDef);
12804 // Some fields in the definition may be empty, if there were no values to put in them that
12805 // would've justified object creation. Unwrap them if necessary.
12806 const writeableDef = definition;
12807 writeableDef.inputs = maybeUnwrapEmpty(definition.inputs);
12808 writeableDef.declaredInputs = maybeUnwrapEmpty(definition.declaredInputs);
12809 writeableDef.outputs = maybeUnwrapEmpty(definition.outputs);
12810 // Merge hostBindings
12811 const superHostBindings = superDef.hostBindings;
12812 superHostBindings && inheritHostBindings(definition, superHostBindings);
12813 // Merge queries
12814 const superViewQuery = superDef.viewQuery;
12815 const superContentQueries = superDef.contentQueries;
12816 superViewQuery && inheritViewQuery(definition, superViewQuery);
12817 superContentQueries && inheritContentQueries(definition, superContentQueries);
12818 // Merge inputs and outputs
12819 fillProperties(definition.inputs, superDef.inputs);
12820 fillProperties(definition.declaredInputs, superDef.declaredInputs);
12821 fillProperties(definition.outputs, superDef.outputs);
12822 // Merge animations metadata.
12823 // If `superDef` is a Component, the `data` field is present (defaults to an empty object).
12824 if (isComponentDef(superDef) && superDef.data.animation) {
12825 // If super def is a Component, the `definition` is also a Component, since Directives can
12826 // not inherit Components (we throw an error above and cannot reach this code).
12827 const defData = definition.data;
12828 defData.animation = (defData.animation || []).concat(superDef.data.animation);
12829 }
12830 }
12831 // Run parent features
12832 const features = superDef.features;
12833 if (features) {
12834 for (let i = 0; i < features.length; i++) {
12835 const feature = features[i];
12836 if (feature && feature.ngInherit) {
12837 feature(definition);
12838 }
12839 // If `InheritDefinitionFeature` is a part of the current `superDef`, it means that this
12840 // def already has all the necessary information inherited from its super class(es), so we
12841 // can stop merging fields from super classes. However we need to iterate through the
12842 // prototype chain to look for classes that might contain other "features" (like
12843 // NgOnChanges), which we should invoke for the original `definition`. We set the
12844 // `shouldInheritFields` flag to indicate that, essentially skipping fields inheritance
12845 // logic and only invoking functions from the "features" list.
12846 if (feature === ɵɵInheritDefinitionFeature) {
12847 shouldInheritFields = false;
12848 }
12849 }
12850 }
12851 }
12852 superType = Object.getPrototypeOf(superType);
12853 }
12854 mergeHostAttrsAcrossInheritance(inheritanceChain);
12855}
12856/**
12857 * Merge the `hostAttrs` and `hostVars` from the inherited parent to the base class.
12858 *
12859 * @param inheritanceChain A list of `WritableDefs` starting at the top most type and listing
12860 * sub-types in order. For each type take the `hostAttrs` and `hostVars` and merge it with the child
12861 * type.
12862 */
12863function mergeHostAttrsAcrossInheritance(inheritanceChain) {
12864 let hostVars = 0;
12865 let hostAttrs = null;
12866 // We process the inheritance order from the base to the leaves here.
12867 for (let i = inheritanceChain.length - 1; i >= 0; i--) {
12868 const def = inheritanceChain[i];
12869 // For each `hostVars`, we need to add the superclass amount.
12870 def.hostVars = (hostVars += def.hostVars);
12871 // for each `hostAttrs` we need to merge it with superclass.
12872 def.hostAttrs =
12873 mergeHostAttrs(def.hostAttrs, hostAttrs = mergeHostAttrs(hostAttrs, def.hostAttrs));
12874 }
12875}
12876function maybeUnwrapEmpty(value) {
12877 if (value === EMPTY_OBJ) {
12878 return {};
12879 }
12880 else if (value === EMPTY_ARRAY) {
12881 return [];
12882 }
12883 else {
12884 return value;
12885 }
12886}
12887function inheritViewQuery(definition, superViewQuery) {
12888 const prevViewQuery = definition.viewQuery;
12889 if (prevViewQuery) {
12890 definition.viewQuery = (rf, ctx) => {
12891 superViewQuery(rf, ctx);
12892 prevViewQuery(rf, ctx);
12893 };
12894 }
12895 else {
12896 definition.viewQuery = superViewQuery;
12897 }
12898}
12899function inheritContentQueries(definition, superContentQueries) {
12900 const prevContentQueries = definition.contentQueries;
12901 if (prevContentQueries) {
12902 definition.contentQueries = (rf, ctx, directiveIndex) => {
12903 superContentQueries(rf, ctx, directiveIndex);
12904 prevContentQueries(rf, ctx, directiveIndex);
12905 };
12906 }
12907 else {
12908 definition.contentQueries = superContentQueries;
12909 }
12910}
12911function inheritHostBindings(definition, superHostBindings) {
12912 const prevHostBindings = definition.hostBindings;
12913 if (prevHostBindings) {
12914 definition.hostBindings = (rf, ctx) => {
12915 superHostBindings(rf, ctx);
12916 prevHostBindings(rf, ctx);
12917 };
12918 }
12919 else {
12920 definition.hostBindings = superHostBindings;
12921 }
12922}
12923
12924/**
12925 * Fields which exist on either directive or component definitions, and need to be copied from
12926 * parent to child classes by the `ɵɵCopyDefinitionFeature`.
12927 */
12928const COPY_DIRECTIVE_FIELDS = [
12929 // The child class should use the providers of its parent.
12930 'providersResolver',
12931 // Not listed here are any fields which are handled by the `ɵɵInheritDefinitionFeature`, such
12932 // as inputs, outputs, and host binding functions.
12933];
12934/**
12935 * Fields which exist only on component definitions, and need to be copied from parent to child
12936 * classes by the `ɵɵCopyDefinitionFeature`.
12937 *
12938 * The type here allows any field of `ComponentDef` which is not also a property of `DirectiveDef`,
12939 * since those should go in `COPY_DIRECTIVE_FIELDS` above.
12940 */
12941const COPY_COMPONENT_FIELDS = [
12942 // The child class should use the template function of its parent, including all template
12943 // semantics.
12944 'template',
12945 'decls',
12946 'consts',
12947 'vars',
12948 'onPush',
12949 'ngContentSelectors',
12950 // The child class should use the CSS styles of its parent, including all styling semantics.
12951 'styles',
12952 'encapsulation',
12953 // The child class should be checked by the runtime in the same way as its parent.
12954 'schemas',
12955];
12956/**
12957 * Copies the fields not handled by the `ɵɵInheritDefinitionFeature` from the supertype of a
12958 * definition.
12959 *
12960 * This exists primarily to support ngcc migration of an existing View Engine pattern, where an
12961 * entire decorator is inherited from a parent to a child class. When ngcc detects this case, it
12962 * generates a skeleton definition on the child class, and applies this feature.
12963 *
12964 * The `ɵɵCopyDefinitionFeature` then copies any needed fields from the parent class' definition,
12965 * including things like the component template function.
12966 *
12967 * @param definition The definition of a child class which inherits from a parent class with its
12968 * own definition.
12969 *
12970 * @codeGenApi
12971 */
12972function ɵɵCopyDefinitionFeature(definition) {
12973 let superType = getSuperType(definition.type);
12974 let superDef = undefined;
12975 if (isComponentDef(definition)) {
12976 // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
12977 superDef = superType.ɵcmp;
12978 }
12979 else {
12980 // Don't use getComponentDef/getDirectiveDef. This logic relies on inheritance.
12981 superDef = superType.ɵdir;
12982 }
12983 // Needed because `definition` fields are readonly.
12984 const defAny = definition;
12985 // Copy over any fields that apply to either directives or components.
12986 for (const field of COPY_DIRECTIVE_FIELDS) {
12987 defAny[field] = superDef[field];
12988 }
12989 if (isComponentDef(superDef)) {
12990 // Copy over any component-specific fields.
12991 for (const field of COPY_COMPONENT_FIELDS) {
12992 defAny[field] = superDef[field];
12993 }
12994 }
12995}
12996
12997/**
12998 * This feature adds the host directives behavior to a directive definition by patching a
12999 * function onto it. The expectation is that the runtime will invoke the function during
13000 * directive matching.
13001 *
13002 * For example:
13003 * ```ts
13004 * class ComponentWithHostDirective {
13005 * static ɵcmp = defineComponent({
13006 * type: ComponentWithHostDirective,
13007 * features: [ɵɵHostDirectivesFeature([
13008 * SimpleHostDirective,
13009 * {directive: AdvancedHostDirective, inputs: ['foo: alias'], outputs: ['bar']},
13010 * ])]
13011 * });
13012 * }
13013 * ```
13014 *
13015 * @codeGenApi
13016 */
13017function ɵɵHostDirectivesFeature(rawHostDirectives) {
13018 return (definition) => {
13019 definition.findHostDirectiveDefs = findHostDirectiveDefs;
13020 definition.hostDirectives =
13021 (Array.isArray(rawHostDirectives) ? rawHostDirectives : rawHostDirectives()).map(dir => {
13022 return typeof dir === 'function' ?
13023 { directive: resolveForwardRef(dir), inputs: EMPTY_OBJ, outputs: EMPTY_OBJ } :
13024 {
13025 directive: resolveForwardRef(dir.directive),
13026 inputs: bindingArrayToMap(dir.inputs),
13027 outputs: bindingArrayToMap(dir.outputs)
13028 };
13029 });
13030 };
13031}
13032function findHostDirectiveDefs(currentDef, matchedDefs, hostDirectiveDefs) {
13033 if (currentDef.hostDirectives !== null) {
13034 for (const hostDirectiveConfig of currentDef.hostDirectives) {
13035 const hostDirectiveDef = getDirectiveDef(hostDirectiveConfig.directive);
13036 if (typeof ngDevMode === 'undefined' || ngDevMode) {
13037 validateHostDirective(hostDirectiveConfig, hostDirectiveDef, matchedDefs);
13038 }
13039 // We need to patch the `declaredInputs` so that
13040 // `ngOnChanges` can map the properties correctly.
13041 patchDeclaredInputs(hostDirectiveDef.declaredInputs, hostDirectiveConfig.inputs);
13042 // Host directives execute before the host so that its host bindings can be overwritten.
13043 findHostDirectiveDefs(hostDirectiveDef, matchedDefs, hostDirectiveDefs);
13044 hostDirectiveDefs.set(hostDirectiveDef, hostDirectiveConfig);
13045 matchedDefs.push(hostDirectiveDef);
13046 }
13047 }
13048}
13049/**
13050 * Converts an array in the form of `['publicName', 'alias', 'otherPublicName', 'otherAlias']` into
13051 * a map in the form of `{publicName: 'alias', otherPublicName: 'otherAlias'}`.
13052 */
13053function bindingArrayToMap(bindings) {
13054 if (bindings === undefined || bindings.length === 0) {
13055 return EMPTY_OBJ;
13056 }
13057 const result = {};
13058 for (let i = 0; i < bindings.length; i += 2) {
13059 result[bindings[i]] = bindings[i + 1];
13060 }
13061 return result;
13062}
13063/**
13064 * `ngOnChanges` has some leftover legacy ViewEngine behavior where the keys inside the
13065 * `SimpleChanges` event refer to the *declared* name of the input, not its public name or its
13066 * minified name. E.g. in `@Input('alias') foo: string`, the name in the `SimpleChanges` object
13067 * will always be `foo`, and not `alias` or the minified name of `foo` in apps using property
13068 * minification.
13069 *
13070 * This is achieved through the `DirectiveDef.declaredInputs` map that is constructed when the
13071 * definition is declared. When a property is written to the directive instance, the
13072 * `NgOnChangesFeature` will try to remap the property name being written to using the
13073 * `declaredInputs`.
13074 *
13075 * Since the host directive input remapping happens during directive matching, `declaredInputs`
13076 * won't contain the new alias that the input is available under. This function addresses the
13077 * issue by patching the host directive aliases to the `declaredInputs`. There is *not* a risk of
13078 * this patching accidentally introducing new inputs to the host directive, because `declaredInputs`
13079 * is used *only* by the `NgOnChangesFeature` when determining what name is used in the
13080 * `SimpleChanges` object which won't be reached if an input doesn't exist.
13081 */
13082function patchDeclaredInputs(declaredInputs, exposedInputs) {
13083 for (const publicName in exposedInputs) {
13084 if (exposedInputs.hasOwnProperty(publicName)) {
13085 const remappedPublicName = exposedInputs[publicName];
13086 const privateName = declaredInputs[publicName];
13087 // We *technically* shouldn't be able to hit this case because we can't have multiple
13088 // inputs on the same property and we have validations against conflicting aliases in
13089 // `validateMappings`. If we somehow did, it would lead to `ngOnChanges` being invoked
13090 // with the wrong name so we have a non-user-friendly assertion here just in case.
13091 if ((typeof ngDevMode === 'undefined' || ngDevMode) &&
13092 declaredInputs.hasOwnProperty(remappedPublicName)) {
13093 assertEqual(declaredInputs[remappedPublicName], declaredInputs[publicName], `Conflicting host directive input alias ${publicName}.`);
13094 }
13095 declaredInputs[remappedPublicName] = privateName;
13096 }
13097 }
13098}
13099/**
13100 * Verifies that the host directive has been configured correctly.
13101 * @param hostDirectiveConfig Host directive configuration object.
13102 * @param directiveDef Directive definition of the host directive.
13103 * @param matchedDefs Directives that have been matched so far.
13104 */
13105function validateHostDirective(hostDirectiveConfig, directiveDef, matchedDefs) {
13106 const type = hostDirectiveConfig.directive;
13107 if (directiveDef === null) {
13108 if (getComponentDef$1(type) !== null) {
13109 throw new RuntimeError(310 /* RuntimeErrorCode.HOST_DIRECTIVE_COMPONENT */, `Host directive ${type.name} cannot be a component.`);
13110 }
13111 throw new RuntimeError(307 /* RuntimeErrorCode.HOST_DIRECTIVE_UNRESOLVABLE */, `Could not resolve metadata for host directive ${type.name}. ` +
13112 `Make sure that the ${type.name} class is annotated with an @Directive decorator.`);
13113 }
13114 if (!directiveDef.standalone) {
13115 throw new RuntimeError(308 /* RuntimeErrorCode.HOST_DIRECTIVE_NOT_STANDALONE */, `Host directive ${directiveDef.type.name} must be standalone.`);
13116 }
13117 if (matchedDefs.indexOf(directiveDef) > -1) {
13118 throw new RuntimeError(309 /* RuntimeErrorCode.DUPLICATE_DIRECTITVE */, `Directive ${directiveDef.type.name} matches multiple times on the same element. ` +
13119 `Directives can only match an element once.`);
13120 }
13121 validateMappings('input', directiveDef, hostDirectiveConfig.inputs);
13122 validateMappings('output', directiveDef, hostDirectiveConfig.outputs);
13123}
13124/**
13125 * Checks that the host directive inputs/outputs configuration is valid.
13126 * @param bindingType Kind of binding that is being validated. Used in the error message.
13127 * @param def Definition of the host directive that is being validated against.
13128 * @param hostDirectiveBindings Host directive mapping object that shold be validated.
13129 */
13130function validateMappings(bindingType, def, hostDirectiveBindings) {
13131 const className = def.type.name;
13132 const bindings = bindingType === 'input' ? def.inputs : def.outputs;
13133 for (const publicName in hostDirectiveBindings) {
13134 if (hostDirectiveBindings.hasOwnProperty(publicName)) {
13135 if (!bindings.hasOwnProperty(publicName)) {
13136 throw new RuntimeError(311 /* RuntimeErrorCode.HOST_DIRECTIVE_UNDEFINED_BINDING */, `Directive ${className} does not have an ${bindingType} with a public name of ${publicName}.`);
13137 }
13138 const remappedPublicName = hostDirectiveBindings[publicName];
13139 if (bindings.hasOwnProperty(remappedPublicName) &&
13140 bindings[remappedPublicName] !== publicName) {
13141 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.`);
13142 }
13143 }
13144 }
13145}
13146
13147let _symbolIterator = null;
13148function getSymbolIterator() {
13149 if (!_symbolIterator) {
13150 const Symbol = _global$1['Symbol'];
13151 if (Symbol && Symbol.iterator) {
13152 _symbolIterator = Symbol.iterator;
13153 }
13154 else {
13155 // es6-shim specific logic
13156 const keys = Object.getOwnPropertyNames(Map.prototype);
13157 for (let i = 0; i < keys.length; ++i) {
13158 const key = keys[i];
13159 if (key !== 'entries' && key !== 'size' &&
13160 Map.prototype[key] === Map.prototype['entries']) {
13161 _symbolIterator = key;
13162 }
13163 }
13164 }
13165 }
13166 return _symbolIterator;
13167}
13168
13169function isIterable(obj) {
13170 return obj !== null && typeof obj === 'object' && obj[getSymbolIterator()] !== undefined;
13171}
13172function isListLikeIterable(obj) {
13173 if (!isJsObject(obj))
13174 return false;
13175 return Array.isArray(obj) ||
13176 (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
13177 getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop
13178}
13179function areIterablesEqual(a, b, comparator) {
13180 const iterator1 = a[getSymbolIterator()]();
13181 const iterator2 = b[getSymbolIterator()]();
13182 while (true) {
13183 const item1 = iterator1.next();
13184 const item2 = iterator2.next();
13185 if (item1.done && item2.done)
13186 return true;
13187 if (item1.done || item2.done)
13188 return false;
13189 if (!comparator(item1.value, item2.value))
13190 return false;
13191 }
13192}
13193function iterateListLike(obj, fn) {
13194 if (Array.isArray(obj)) {
13195 for (let i = 0; i < obj.length; i++) {
13196 fn(obj[i]);
13197 }
13198 }
13199 else {
13200 const iterator = obj[getSymbolIterator()]();
13201 let item;
13202 while (!((item = iterator.next()).done)) {
13203 fn(item.value);
13204 }
13205 }
13206}
13207function isJsObject(o) {
13208 return o !== null && (typeof o === 'function' || typeof o === 'object');
13209}
13210
13211function devModeEqual(a, b) {
13212 const isListLikeIterableA = isListLikeIterable(a);
13213 const isListLikeIterableB = isListLikeIterable(b);
13214 if (isListLikeIterableA && isListLikeIterableB) {
13215 return areIterablesEqual(a, b, devModeEqual);
13216 }
13217 else {
13218 const isAObject = a && (typeof a === 'object' || typeof a === 'function');
13219 const isBObject = b && (typeof b === 'object' || typeof b === 'function');
13220 if (!isListLikeIterableA && isAObject && !isListLikeIterableB && isBObject) {
13221 return true;
13222 }
13223 else {
13224 return Object.is(a, b);
13225 }
13226 }
13227}
13228
13229// TODO(misko): consider inlining
13230/** Updates binding and returns the value. */
13231function updateBinding(lView, bindingIndex, value) {
13232 return lView[bindingIndex] = value;
13233}
13234/** Gets the current binding value. */
13235function getBinding(lView, bindingIndex) {
13236 ngDevMode && assertIndexInRange(lView, bindingIndex);
13237 ngDevMode &&
13238 assertNotSame(lView[bindingIndex], NO_CHANGE, 'Stored value should never be NO_CHANGE.');
13239 return lView[bindingIndex];
13240}
13241/**
13242 * Updates binding if changed, then returns whether it was updated.
13243 *
13244 * This function also checks the `CheckNoChangesMode` and throws if changes are made.
13245 * Some changes (Objects/iterables) during `CheckNoChangesMode` are exempt to comply with VE
13246 * behavior.
13247 *
13248 * @param lView current `LView`
13249 * @param bindingIndex The binding in the `LView` to check
13250 * @param value New value to check against `lView[bindingIndex]`
13251 * @returns `true` if the bindings has changed. (Throws if binding has changed during
13252 * `CheckNoChangesMode`)
13253 */
13254function bindingUpdated(lView, bindingIndex, value) {
13255 ngDevMode && assertNotSame(value, NO_CHANGE, 'Incoming value should never be NO_CHANGE.');
13256 ngDevMode &&
13257 assertLessThan(bindingIndex, lView.length, `Slot should have been initialized to NO_CHANGE`);
13258 const oldValue = lView[bindingIndex];
13259 if (Object.is(oldValue, value)) {
13260 return false;
13261 }
13262 else {
13263 if (ngDevMode && isInCheckNoChangesMode()) {
13264 // View engine didn't report undefined values as changed on the first checkNoChanges pass
13265 // (before the change detection was run).
13266 const oldValueToCompare = oldValue !== NO_CHANGE ? oldValue : undefined;
13267 if (!devModeEqual(oldValueToCompare, value)) {
13268 const details = getExpressionChangedErrorDetails(lView, bindingIndex, oldValueToCompare, value);
13269 throwErrorIfNoChangesMode(oldValue === NO_CHANGE, details.oldValue, details.newValue, details.propName);
13270 }
13271 // There was a change, but the `devModeEqual` decided that the change is exempt from an error.
13272 // For this reason we exit as if no change. The early exit is needed to prevent the changed
13273 // value to be written into `LView` (If we would write the new value that we would not see it
13274 // as change on next CD.)
13275 return false;
13276 }
13277 lView[bindingIndex] = value;
13278 return true;
13279 }
13280}
13281/** Updates 2 bindings if changed, then returns whether either was updated. */
13282function bindingUpdated2(lView, bindingIndex, exp1, exp2) {
13283 const different = bindingUpdated(lView, bindingIndex, exp1);
13284 return bindingUpdated(lView, bindingIndex + 1, exp2) || different;
13285}
13286/** Updates 3 bindings if changed, then returns whether any was updated. */
13287function bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) {
13288 const different = bindingUpdated2(lView, bindingIndex, exp1, exp2);
13289 return bindingUpdated(lView, bindingIndex + 2, exp3) || different;
13290}
13291/** Updates 4 bindings if changed, then returns whether any was updated. */
13292function bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) {
13293 const different = bindingUpdated2(lView, bindingIndex, exp1, exp2);
13294 return bindingUpdated2(lView, bindingIndex + 2, exp3, exp4) || different;
13295}
13296
13297/**
13298 * Updates the value of or removes a bound attribute on an Element.
13299 *
13300 * Used in the case of `[attr.title]="value"`
13301 *
13302 * @param name name The name of the attribute.
13303 * @param value value The attribute is removed when value is `null` or `undefined`.
13304 * Otherwise the attribute value is set to the stringified value.
13305 * @param sanitizer An optional function used to sanitize the value.
13306 * @param namespace Optional namespace to use when setting the attribute.
13307 *
13308 * @codeGenApi
13309 */
13310function ɵɵattribute(name, value, sanitizer, namespace) {
13311 const lView = getLView();
13312 const bindingIndex = nextBindingIndex();
13313 if (bindingUpdated(lView, bindingIndex, value)) {
13314 const tView = getTView();
13315 const tNode = getSelectedTNode();
13316 elementAttributeInternal(tNode, lView, name, value, sanitizer, namespace);
13317 ngDevMode && storePropertyBindingMetadata(tView.data, tNode, 'attr.' + name, bindingIndex);
13318 }
13319 return ɵɵattribute;
13320}
13321
13322/**
13323 * Create interpolation bindings with a variable number of expressions.
13324 *
13325 * If there are 1 to 8 expressions `interpolation1()` to `interpolation8()` should be used instead.
13326 * Those are faster because there is no need to create an array of expressions and iterate over it.
13327 *
13328 * `values`:
13329 * - has static text at even indexes,
13330 * - has evaluated expressions at odd indexes.
13331 *
13332 * Returns the concatenated string when any of the arguments changes, `NO_CHANGE` otherwise.
13333 */
13334function interpolationV(lView, values) {
13335 ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values');
13336 ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values');
13337 let isBindingUpdated = false;
13338 let bindingIndex = getBindingIndex();
13339 for (let i = 1; i < values.length; i += 2) {
13340 // Check if bindings (odd indexes) have changed
13341 isBindingUpdated = bindingUpdated(lView, bindingIndex++, values[i]) || isBindingUpdated;
13342 }
13343 setBindingIndex(bindingIndex);
13344 if (!isBindingUpdated) {
13345 return NO_CHANGE;
13346 }
13347 // Build the updated content
13348 let content = values[0];
13349 for (let i = 1; i < values.length; i += 2) {
13350 content += renderStringify(values[i]) + values[i + 1];
13351 }
13352 return content;
13353}
13354/**
13355 * Creates an interpolation binding with 1 expression.
13356 *
13357 * @param prefix static value used for concatenation only.
13358 * @param v0 value checked for change.
13359 * @param suffix static value used for concatenation only.
13360 */
13361function interpolation1(lView, prefix, v0, suffix) {
13362 const different = bindingUpdated(lView, nextBindingIndex(), v0);
13363 return different ? prefix + renderStringify(v0) + suffix : NO_CHANGE;
13364}
13365/**
13366 * Creates an interpolation binding with 2 expressions.
13367 */
13368function interpolation2(lView, prefix, v0, i0, v1, suffix) {
13369 const bindingIndex = getBindingIndex();
13370 const different = bindingUpdated2(lView, bindingIndex, v0, v1);
13371 incrementBindingIndex(2);
13372 return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + suffix : NO_CHANGE;
13373}
13374/**
13375 * Creates an interpolation binding with 3 expressions.
13376 */
13377function interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix) {
13378 const bindingIndex = getBindingIndex();
13379 const different = bindingUpdated3(lView, bindingIndex, v0, v1, v2);
13380 incrementBindingIndex(3);
13381 return different ?
13382 prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + suffix :
13383 NO_CHANGE;
13384}
13385/**
13386 * Create an interpolation binding with 4 expressions.
13387 */
13388function interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
13389 const bindingIndex = getBindingIndex();
13390 const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
13391 incrementBindingIndex(4);
13392 return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
13393 renderStringify(v2) + i2 + renderStringify(v3) + suffix :
13394 NO_CHANGE;
13395}
13396/**
13397 * Creates an interpolation binding with 5 expressions.
13398 */
13399function interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
13400 const bindingIndex = getBindingIndex();
13401 let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
13402 different = bindingUpdated(lView, bindingIndex + 4, v4) || different;
13403 incrementBindingIndex(5);
13404 return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
13405 renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + suffix :
13406 NO_CHANGE;
13407}
13408/**
13409 * Creates an interpolation binding with 6 expressions.
13410 */
13411function interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
13412 const bindingIndex = getBindingIndex();
13413 let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
13414 different = bindingUpdated2(lView, bindingIndex + 4, v4, v5) || different;
13415 incrementBindingIndex(6);
13416 return different ?
13417 prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
13418 renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + suffix :
13419 NO_CHANGE;
13420}
13421/**
13422 * Creates an interpolation binding with 7 expressions.
13423 */
13424function interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
13425 const bindingIndex = getBindingIndex();
13426 let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
13427 different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different;
13428 incrementBindingIndex(7);
13429 return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
13430 renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 +
13431 renderStringify(v5) + i5 + renderStringify(v6) + suffix :
13432 NO_CHANGE;
13433}
13434/**
13435 * Creates an interpolation binding with 8 expressions.
13436 */
13437function interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
13438 const bindingIndex = getBindingIndex();
13439 let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
13440 different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different;
13441 incrementBindingIndex(8);
13442 return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 +
13443 renderStringify(v2) + i2 + renderStringify(v3) + i3 + renderStringify(v4) + i4 +
13444 renderStringify(v5) + i5 + renderStringify(v6) + i6 + renderStringify(v7) + suffix :
13445 NO_CHANGE;
13446}
13447
13448/**
13449 *
13450 * Update an interpolated attribute on an element with single bound value surrounded by text.
13451 *
13452 * Used when the value passed to a property has 1 interpolated value in it:
13453 *
13454 * ```html
13455 * <div attr.title="prefix{{v0}}suffix"></div>
13456 * ```
13457 *
13458 * Its compiled representation is::
13459 *
13460 * ```ts
13461 * ɵɵattributeInterpolate1('title', 'prefix', v0, 'suffix');
13462 * ```
13463 *
13464 * @param attrName The name of the attribute to update
13465 * @param prefix Static value used for concatenation only.
13466 * @param v0 Value checked for change.
13467 * @param suffix Static value used for concatenation only.
13468 * @param sanitizer An optional sanitizer function
13469 * @returns itself, so that it may be chained.
13470 * @codeGenApi
13471 */
13472function ɵɵattributeInterpolate1(attrName, prefix, v0, suffix, sanitizer, namespace) {
13473 const lView = getLView();
13474 const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
13475 if (interpolatedValue !== NO_CHANGE) {
13476 const tNode = getSelectedTNode();
13477 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13478 ngDevMode &&
13479 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 1, prefix, suffix);
13480 }
13481 return ɵɵattributeInterpolate1;
13482}
13483/**
13484 *
13485 * Update an interpolated attribute on an element with 2 bound values surrounded by text.
13486 *
13487 * Used when the value passed to a property has 2 interpolated values in it:
13488 *
13489 * ```html
13490 * <div attr.title="prefix{{v0}}-{{v1}}suffix"></div>
13491 * ```
13492 *
13493 * Its compiled representation is::
13494 *
13495 * ```ts
13496 * ɵɵattributeInterpolate2('title', 'prefix', v0, '-', v1, 'suffix');
13497 * ```
13498 *
13499 * @param attrName The name of the attribute to update
13500 * @param prefix Static value used for concatenation only.
13501 * @param v0 Value checked for change.
13502 * @param i0 Static value used for concatenation only.
13503 * @param v1 Value checked for change.
13504 * @param suffix Static value used for concatenation only.
13505 * @param sanitizer An optional sanitizer function
13506 * @returns itself, so that it may be chained.
13507 * @codeGenApi
13508 */
13509function ɵɵattributeInterpolate2(attrName, prefix, v0, i0, v1, suffix, sanitizer, namespace) {
13510 const lView = getLView();
13511 const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
13512 if (interpolatedValue !== NO_CHANGE) {
13513 const tNode = getSelectedTNode();
13514 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13515 ngDevMode &&
13516 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 2, prefix, i0, suffix);
13517 }
13518 return ɵɵattributeInterpolate2;
13519}
13520/**
13521 *
13522 * Update an interpolated attribute on an element with 3 bound values surrounded by text.
13523 *
13524 * Used when the value passed to a property has 3 interpolated values in it:
13525 *
13526 * ```html
13527 * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
13528 * ```
13529 *
13530 * Its compiled representation is::
13531 *
13532 * ```ts
13533 * ɵɵattributeInterpolate3(
13534 * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix');
13535 * ```
13536 *
13537 * @param attrName The name of the attribute to update
13538 * @param prefix Static value used for concatenation only.
13539 * @param v0 Value checked for change.
13540 * @param i0 Static value used for concatenation only.
13541 * @param v1 Value checked for change.
13542 * @param i1 Static value used for concatenation only.
13543 * @param v2 Value checked for change.
13544 * @param suffix Static value used for concatenation only.
13545 * @param sanitizer An optional sanitizer function
13546 * @returns itself, so that it may be chained.
13547 * @codeGenApi
13548 */
13549function ɵɵattributeInterpolate3(attrName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer, namespace) {
13550 const lView = getLView();
13551 const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
13552 if (interpolatedValue !== NO_CHANGE) {
13553 const tNode = getSelectedTNode();
13554 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13555 ngDevMode &&
13556 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 3, prefix, i0, i1, suffix);
13557 }
13558 return ɵɵattributeInterpolate3;
13559}
13560/**
13561 *
13562 * Update an interpolated attribute on an element with 4 bound values surrounded by text.
13563 *
13564 * Used when the value passed to a property has 4 interpolated values in it:
13565 *
13566 * ```html
13567 * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
13568 * ```
13569 *
13570 * Its compiled representation is::
13571 *
13572 * ```ts
13573 * ɵɵattributeInterpolate4(
13574 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
13575 * ```
13576 *
13577 * @param attrName The name of the attribute to update
13578 * @param prefix Static value used for concatenation only.
13579 * @param v0 Value checked for change.
13580 * @param i0 Static value used for concatenation only.
13581 * @param v1 Value checked for change.
13582 * @param i1 Static value used for concatenation only.
13583 * @param v2 Value checked for change.
13584 * @param i2 Static value used for concatenation only.
13585 * @param v3 Value checked for change.
13586 * @param suffix Static value used for concatenation only.
13587 * @param sanitizer An optional sanitizer function
13588 * @returns itself, so that it may be chained.
13589 * @codeGenApi
13590 */
13591function ɵɵattributeInterpolate4(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer, namespace) {
13592 const lView = getLView();
13593 const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
13594 if (interpolatedValue !== NO_CHANGE) {
13595 const tNode = getSelectedTNode();
13596 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13597 ngDevMode &&
13598 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix);
13599 }
13600 return ɵɵattributeInterpolate4;
13601}
13602/**
13603 *
13604 * Update an interpolated attribute on an element with 5 bound values surrounded by text.
13605 *
13606 * Used when the value passed to a property has 5 interpolated values in it:
13607 *
13608 * ```html
13609 * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
13610 * ```
13611 *
13612 * Its compiled representation is::
13613 *
13614 * ```ts
13615 * ɵɵattributeInterpolate5(
13616 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
13617 * ```
13618 *
13619 * @param attrName The name of the attribute to update
13620 * @param prefix Static value used for concatenation only.
13621 * @param v0 Value checked for change.
13622 * @param i0 Static value used for concatenation only.
13623 * @param v1 Value checked for change.
13624 * @param i1 Static value used for concatenation only.
13625 * @param v2 Value checked for change.
13626 * @param i2 Static value used for concatenation only.
13627 * @param v3 Value checked for change.
13628 * @param i3 Static value used for concatenation only.
13629 * @param v4 Value checked for change.
13630 * @param suffix Static value used for concatenation only.
13631 * @param sanitizer An optional sanitizer function
13632 * @returns itself, so that it may be chained.
13633 * @codeGenApi
13634 */
13635function ɵɵattributeInterpolate5(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer, namespace) {
13636 const lView = getLView();
13637 const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
13638 if (interpolatedValue !== NO_CHANGE) {
13639 const tNode = getSelectedTNode();
13640 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13641 ngDevMode &&
13642 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix);
13643 }
13644 return ɵɵattributeInterpolate5;
13645}
13646/**
13647 *
13648 * Update an interpolated attribute on an element with 6 bound values surrounded by text.
13649 *
13650 * Used when the value passed to a property has 6 interpolated values in it:
13651 *
13652 * ```html
13653 * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
13654 * ```
13655 *
13656 * Its compiled representation is::
13657 *
13658 * ```ts
13659 * ɵɵattributeInterpolate6(
13660 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
13661 * ```
13662 *
13663 * @param attrName The name of the attribute to update
13664 * @param prefix Static value used for concatenation only.
13665 * @param v0 Value checked for change.
13666 * @param i0 Static value used for concatenation only.
13667 * @param v1 Value checked for change.
13668 * @param i1 Static value used for concatenation only.
13669 * @param v2 Value checked for change.
13670 * @param i2 Static value used for concatenation only.
13671 * @param v3 Value checked for change.
13672 * @param i3 Static value used for concatenation only.
13673 * @param v4 Value checked for change.
13674 * @param i4 Static value used for concatenation only.
13675 * @param v5 Value checked for change.
13676 * @param suffix Static value used for concatenation only.
13677 * @param sanitizer An optional sanitizer function
13678 * @returns itself, so that it may be chained.
13679 * @codeGenApi
13680 */
13681function ɵɵattributeInterpolate6(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer, namespace) {
13682 const lView = getLView();
13683 const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
13684 if (interpolatedValue !== NO_CHANGE) {
13685 const tNode = getSelectedTNode();
13686 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13687 ngDevMode &&
13688 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix);
13689 }
13690 return ɵɵattributeInterpolate6;
13691}
13692/**
13693 *
13694 * Update an interpolated attribute on an element with 7 bound values surrounded by text.
13695 *
13696 * Used when the value passed to a property has 7 interpolated values in it:
13697 *
13698 * ```html
13699 * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
13700 * ```
13701 *
13702 * Its compiled representation is::
13703 *
13704 * ```ts
13705 * ɵɵattributeInterpolate7(
13706 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
13707 * ```
13708 *
13709 * @param attrName The name of the attribute to update
13710 * @param prefix Static value used for concatenation only.
13711 * @param v0 Value checked for change.
13712 * @param i0 Static value used for concatenation only.
13713 * @param v1 Value checked for change.
13714 * @param i1 Static value used for concatenation only.
13715 * @param v2 Value checked for change.
13716 * @param i2 Static value used for concatenation only.
13717 * @param v3 Value checked for change.
13718 * @param i3 Static value used for concatenation only.
13719 * @param v4 Value checked for change.
13720 * @param i4 Static value used for concatenation only.
13721 * @param v5 Value checked for change.
13722 * @param i5 Static value used for concatenation only.
13723 * @param v6 Value checked for change.
13724 * @param suffix Static value used for concatenation only.
13725 * @param sanitizer An optional sanitizer function
13726 * @returns itself, so that it may be chained.
13727 * @codeGenApi
13728 */
13729function ɵɵattributeInterpolate7(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer, namespace) {
13730 const lView = getLView();
13731 const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
13732 if (interpolatedValue !== NO_CHANGE) {
13733 const tNode = getSelectedTNode();
13734 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13735 ngDevMode &&
13736 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix);
13737 }
13738 return ɵɵattributeInterpolate7;
13739}
13740/**
13741 *
13742 * Update an interpolated attribute on an element with 8 bound values surrounded by text.
13743 *
13744 * Used when the value passed to a property has 8 interpolated values in it:
13745 *
13746 * ```html
13747 * <div attr.title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
13748 * ```
13749 *
13750 * Its compiled representation is::
13751 *
13752 * ```ts
13753 * ɵɵattributeInterpolate8(
13754 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
13755 * ```
13756 *
13757 * @param attrName The name of the attribute to update
13758 * @param prefix Static value used for concatenation only.
13759 * @param v0 Value checked for change.
13760 * @param i0 Static value used for concatenation only.
13761 * @param v1 Value checked for change.
13762 * @param i1 Static value used for concatenation only.
13763 * @param v2 Value checked for change.
13764 * @param i2 Static value used for concatenation only.
13765 * @param v3 Value checked for change.
13766 * @param i3 Static value used for concatenation only.
13767 * @param v4 Value checked for change.
13768 * @param i4 Static value used for concatenation only.
13769 * @param v5 Value checked for change.
13770 * @param i5 Static value used for concatenation only.
13771 * @param v6 Value checked for change.
13772 * @param i6 Static value used for concatenation only.
13773 * @param v7 Value checked for change.
13774 * @param suffix Static value used for concatenation only.
13775 * @param sanitizer An optional sanitizer function
13776 * @returns itself, so that it may be chained.
13777 * @codeGenApi
13778 */
13779function ɵɵattributeInterpolate8(attrName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer, namespace) {
13780 const lView = getLView();
13781 const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
13782 if (interpolatedValue !== NO_CHANGE) {
13783 const tNode = getSelectedTNode();
13784 elementAttributeInternal(tNode, lView, attrName, interpolatedValue, sanitizer, namespace);
13785 ngDevMode &&
13786 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix);
13787 }
13788 return ɵɵattributeInterpolate8;
13789}
13790/**
13791 * Update an interpolated attribute on an element with 9 or more bound values surrounded by text.
13792 *
13793 * Used when the number of interpolated values exceeds 8.
13794 *
13795 * ```html
13796 * <div
13797 * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
13798 * ```
13799 *
13800 * Its compiled representation is::
13801 *
13802 * ```ts
13803 * ɵɵattributeInterpolateV(
13804 * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
13805 * 'suffix']);
13806 * ```
13807 *
13808 * @param attrName The name of the attribute to update.
13809 * @param values The collection of values and the strings in-between those values, beginning with
13810 * a string prefix and ending with a string suffix.
13811 * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
13812 * @param sanitizer An optional sanitizer function
13813 * @returns itself, so that it may be chained.
13814 * @codeGenApi
13815 */
13816function ɵɵattributeInterpolateV(attrName, values, sanitizer, namespace) {
13817 const lView = getLView();
13818 const interpolated = interpolationV(lView, values);
13819 if (interpolated !== NO_CHANGE) {
13820 const tNode = getSelectedTNode();
13821 elementAttributeInternal(tNode, lView, attrName, interpolated, sanitizer, namespace);
13822 if (ngDevMode) {
13823 const interpolationInBetween = [values[0]]; // prefix
13824 for (let i = 2; i < values.length; i += 2) {
13825 interpolationInBetween.push(values[i]);
13826 }
13827 storePropertyBindingMetadata(getTView().data, tNode, 'attr.' + attrName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
13828 }
13829 }
13830 return ɵɵattributeInterpolateV;
13831}
13832
13833/**
13834 * Synchronously perform change detection on a component (and possibly its sub-components).
13835 *
13836 * This function triggers change detection in a synchronous way on a component.
13837 *
13838 * @param component The component which the change detection should be performed on.
13839 */
13840function detectChanges(component) {
13841 const view = getComponentViewByInstance(component);
13842 detectChangesInternal(view[TVIEW], view, component);
13843}
13844
13845function templateFirstCreatePass(index, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) {
13846 ngDevMode && assertFirstCreatePass(tView);
13847 ngDevMode && ngDevMode.firstCreatePass++;
13848 const tViewConsts = tView.consts;
13849 // TODO(pk): refactor getOrCreateTNode to have the "create" only version
13850 const tNode = getOrCreateTNode(tView, index, 4 /* TNodeType.Container */, tagName || null, getConstant(tViewConsts, attrsIndex));
13851 resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
13852 registerPostOrderHooks(tView, tNode);
13853 const embeddedTView = tNode.tViews = createTView(2 /* TViewType.Embedded */, tNode, templateFn, decls, vars, tView.directiveRegistry, tView.pipeRegistry, null, tView.schemas, tViewConsts);
13854 if (tView.queries !== null) {
13855 tView.queries.template(tView, tNode);
13856 embeddedTView.queries = tView.queries.embeddedTView(tNode);
13857 }
13858 return tNode;
13859}
13860/**
13861 * Creates an LContainer for an ng-template (dynamically-inserted view), e.g.
13862 *
13863 * <ng-template #foo>
13864 * <div></div>
13865 * </ng-template>
13866 *
13867 * @param index The index of the container in the data array
13868 * @param templateFn Inline template
13869 * @param decls The number of nodes, local refs, and pipes for this template
13870 * @param vars The number of bindings for this template
13871 * @param tagName The name of the container element, if applicable
13872 * @param attrsIndex Index of template attributes in the `consts` array.
13873 * @param localRefs Index of the local references in the `consts` array.
13874 * @param localRefExtractor A function which extracts local-refs values from the template.
13875 * Defaults to the current element associated with the local-ref.
13876 *
13877 * @codeGenApi
13878 */
13879function ɵɵtemplate(index, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex, localRefExtractor) {
13880 const lView = getLView();
13881 const tView = getTView();
13882 const adjustedIndex = index + HEADER_OFFSET;
13883 const tNode = tView.firstCreatePass ? templateFirstCreatePass(adjustedIndex, tView, lView, templateFn, decls, vars, tagName, attrsIndex, localRefsIndex) :
13884 tView.data[adjustedIndex];
13885 setCurrentTNode(tNode, false);
13886 const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : '');
13887 appendChild(tView, lView, comment, tNode);
13888 attachPatchData(comment, lView);
13889 addToViewTree(lView, lView[adjustedIndex] = createLContainer(comment, lView, comment, tNode));
13890 if (isDirectiveHost(tNode)) {
13891 createDirectivesInstances(tView, lView, tNode);
13892 }
13893 if (localRefsIndex != null) {
13894 saveResolvedLocalsInData(lView, tNode, localRefExtractor);
13895 }
13896}
13897
13898/** Store a value in the `data` at a given `index`. */
13899function store(tView, lView, index, value) {
13900 // We don't store any static data for local variables, so the first time
13901 // we see the template, we should store as null to avoid a sparse array
13902 if (index >= tView.data.length) {
13903 tView.data[index] = null;
13904 tView.blueprint[index] = null;
13905 }
13906 lView[index] = value;
13907}
13908/**
13909 * Retrieves a local reference from the current contextViewData.
13910 *
13911 * If the reference to retrieve is in a parent view, this instruction is used in conjunction
13912 * with a nextContext() call, which walks up the tree and updates the contextViewData instance.
13913 *
13914 * @param index The index of the local ref in contextViewData.
13915 *
13916 * @codeGenApi
13917 */
13918function ɵɵreference(index) {
13919 const contextLView = getContextLView();
13920 return load(contextLView, HEADER_OFFSET + index);
13921}
13922
13923/**
13924 * Update a property on a selected element.
13925 *
13926 * Operates on the element selected by index via the {@link select} instruction.
13927 *
13928 * If the property name also exists as an input property on one of the element's directives,
13929 * the component property will be set instead of the element property. This check must
13930 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled
13931 *
13932 * @param propName Name of property. Because it is going to DOM, this is not subject to
13933 * renaming as part of minification.
13934 * @param value New value to write.
13935 * @param sanitizer An optional function used to sanitize the value.
13936 * @returns This function returns itself so that it may be chained
13937 * (e.g. `property('name', ctx.name)('title', ctx.title)`)
13938 *
13939 * @codeGenApi
13940 */
13941function ɵɵproperty(propName, value, sanitizer) {
13942 const lView = getLView();
13943 const bindingIndex = nextBindingIndex();
13944 if (bindingUpdated(lView, bindingIndex, value)) {
13945 const tView = getTView();
13946 const tNode = getSelectedTNode();
13947 elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, false);
13948 ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
13949 }
13950 return ɵɵproperty;
13951}
13952/**
13953 * Given `<div style="..." my-dir>` and `MyDir` with `@Input('style')` we need to write to
13954 * directive input.
13955 */
13956function setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased) {
13957 const inputs = tNode.inputs;
13958 const property = isClassBased ? 'class' : 'style';
13959 // We support both 'class' and `className` hence the fallback.
13960 setInputsForProperty(tView, lView, inputs[property], property, value);
13961}
13962
13963function elementStartFirstCreatePass(index, tView, lView, native, name, attrsIndex, localRefsIndex) {
13964 ngDevMode && assertFirstCreatePass(tView);
13965 ngDevMode && ngDevMode.firstCreatePass++;
13966 const tViewConsts = tView.consts;
13967 const attrs = getConstant(tViewConsts, attrsIndex);
13968 const tNode = getOrCreateTNode(tView, index, 2 /* TNodeType.Element */, name, attrs);
13969 const hasDirectives = resolveDirectives(tView, lView, tNode, getConstant(tViewConsts, localRefsIndex));
13970 if (ngDevMode) {
13971 validateElementIsKnown(native, lView, tNode.value, tView.schemas, hasDirectives);
13972 }
13973 if (tNode.attrs !== null) {
13974 computeStaticStyling(tNode, tNode.attrs, false);
13975 }
13976 if (tNode.mergedAttrs !== null) {
13977 computeStaticStyling(tNode, tNode.mergedAttrs, true);
13978 }
13979 if (tView.queries !== null) {
13980 tView.queries.elementStart(tView, tNode);
13981 }
13982 return tNode;
13983}
13984/**
13985 * Create DOM element. The instruction must later be followed by `elementEnd()` call.
13986 *
13987 * @param index Index of the element in the LView array
13988 * @param name Name of the DOM Node
13989 * @param attrsIndex Index of the element's attributes in the `consts` array.
13990 * @param localRefsIndex Index of the element's local references in the `consts` array.
13991 * @returns This function returns itself so that it may be chained.
13992 *
13993 * Attributes and localRefs are passed as an array of strings where elements with an even index
13994 * hold an attribute name and elements with an odd index hold an attribute value, ex.:
13995 * ['id', 'warning5', 'class', 'alert']
13996 *
13997 * @codeGenApi
13998 */
13999function ɵɵelementStart(index, name, attrsIndex, localRefsIndex) {
14000 const lView = getLView();
14001 const tView = getTView();
14002 const adjustedIndex = HEADER_OFFSET + index;
14003 ngDevMode &&
14004 assertEqual(getBindingIndex(), tView.bindingStartIndex, 'elements should be created before any bindings');
14005 ngDevMode && assertIndexInRange(lView, adjustedIndex);
14006 const renderer = lView[RENDERER];
14007 const native = lView[adjustedIndex] = createElementNode(renderer, name, getNamespace$1());
14008 const tNode = tView.firstCreatePass ?
14009 elementStartFirstCreatePass(adjustedIndex, tView, lView, native, name, attrsIndex, localRefsIndex) :
14010 tView.data[adjustedIndex];
14011 setCurrentTNode(tNode, true);
14012 setupStaticAttributes(renderer, native, tNode);
14013 if ((tNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
14014 // In the i18n case, the translation may have removed this element, so only add it if it is not
14015 // detached. See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
14016 appendChild(tView, lView, native, tNode);
14017 }
14018 // any immediate children of a component or template container must be pre-emptively
14019 // monkey-patched with the component view data so that the element can be inspected
14020 // later on using any element discovery utility methods (see `element_discovery.ts`)
14021 if (getElementDepthCount() === 0) {
14022 attachPatchData(native, lView);
14023 }
14024 increaseElementDepthCount();
14025 if (isDirectiveHost(tNode)) {
14026 createDirectivesInstances(tView, lView, tNode);
14027 executeContentQueries(tView, tNode, lView);
14028 }
14029 if (localRefsIndex !== null) {
14030 saveResolvedLocalsInData(lView, tNode);
14031 }
14032 return ɵɵelementStart;
14033}
14034/**
14035 * Mark the end of the element.
14036 * @returns This function returns itself so that it may be chained.
14037 *
14038 * @codeGenApi
14039 */
14040function ɵɵelementEnd() {
14041 let currentTNode = getCurrentTNode();
14042 ngDevMode && assertDefined(currentTNode, 'No parent node to close.');
14043 if (isCurrentTNodeParent()) {
14044 setCurrentTNodeAsNotParent();
14045 }
14046 else {
14047 ngDevMode && assertHasParent(getCurrentTNode());
14048 currentTNode = currentTNode.parent;
14049 setCurrentTNode(currentTNode, false);
14050 }
14051 const tNode = currentTNode;
14052 ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */);
14053 decreaseElementDepthCount();
14054 const tView = getTView();
14055 if (tView.firstCreatePass) {
14056 registerPostOrderHooks(tView, currentTNode);
14057 if (isContentQueryHost(currentTNode)) {
14058 tView.queries.elementEnd(currentTNode);
14059 }
14060 }
14061 if (tNode.classesWithoutHost != null && hasClassInput(tNode)) {
14062 setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.classesWithoutHost, true);
14063 }
14064 if (tNode.stylesWithoutHost != null && hasStyleInput(tNode)) {
14065 setDirectiveInputsWhichShadowsStyling(tView, tNode, getLView(), tNode.stylesWithoutHost, false);
14066 }
14067 return ɵɵelementEnd;
14068}
14069/**
14070 * Creates an empty element using {@link elementStart} and {@link elementEnd}
14071 *
14072 * @param index Index of the element in the data array
14073 * @param name Name of the DOM Node
14074 * @param attrsIndex Index of the element's attributes in the `consts` array.
14075 * @param localRefsIndex Index of the element's local references in the `consts` array.
14076 * @returns This function returns itself so that it may be chained.
14077 *
14078 * @codeGenApi
14079 */
14080function ɵɵelement(index, name, attrsIndex, localRefsIndex) {
14081 ɵɵelementStart(index, name, attrsIndex, localRefsIndex);
14082 ɵɵelementEnd();
14083 return ɵɵelement;
14084}
14085
14086function elementContainerStartFirstCreatePass(index, tView, lView, attrsIndex, localRefsIndex) {
14087 ngDevMode && ngDevMode.firstCreatePass++;
14088 const tViewConsts = tView.consts;
14089 const attrs = getConstant(tViewConsts, attrsIndex);
14090 const tNode = getOrCreateTNode(tView, index, 8 /* TNodeType.ElementContainer */, 'ng-container', attrs);
14091 // While ng-container doesn't necessarily support styling, we use the style context to identify
14092 // and execute directives on the ng-container.
14093 if (attrs !== null) {
14094 computeStaticStyling(tNode, attrs, true);
14095 }
14096 const localRefs = getConstant(tViewConsts, localRefsIndex);
14097 resolveDirectives(tView, lView, tNode, localRefs);
14098 if (tView.queries !== null) {
14099 tView.queries.elementStart(tView, tNode);
14100 }
14101 return tNode;
14102}
14103/**
14104 * Creates a logical container for other nodes (<ng-container>) backed by a comment node in the DOM.
14105 * The instruction must later be followed by `elementContainerEnd()` call.
14106 *
14107 * @param index Index of the element in the LView array
14108 * @param attrsIndex Index of the container attributes in the `consts` array.
14109 * @param localRefsIndex Index of the container's local references in the `consts` array.
14110 * @returns This function returns itself so that it may be chained.
14111 *
14112 * Even if this instruction accepts a set of attributes no actual attribute values are propagated to
14113 * the DOM (as a comment node can't have attributes). Attributes are here only for directive
14114 * matching purposes and setting initial inputs of directives.
14115 *
14116 * @codeGenApi
14117 */
14118function ɵɵelementContainerStart(index, attrsIndex, localRefsIndex) {
14119 const lView = getLView();
14120 const tView = getTView();
14121 const adjustedIndex = index + HEADER_OFFSET;
14122 ngDevMode && assertIndexInRange(lView, adjustedIndex);
14123 ngDevMode &&
14124 assertEqual(getBindingIndex(), tView.bindingStartIndex, 'element containers should be created before any bindings');
14125 const tNode = tView.firstCreatePass ?
14126 elementContainerStartFirstCreatePass(adjustedIndex, tView, lView, attrsIndex, localRefsIndex) :
14127 tView.data[adjustedIndex];
14128 setCurrentTNode(tNode, true);
14129 ngDevMode && ngDevMode.rendererCreateComment++;
14130 const native = lView[adjustedIndex] =
14131 lView[RENDERER].createComment(ngDevMode ? 'ng-container' : '');
14132 appendChild(tView, lView, native, tNode);
14133 attachPatchData(native, lView);
14134 if (isDirectiveHost(tNode)) {
14135 createDirectivesInstances(tView, lView, tNode);
14136 executeContentQueries(tView, tNode, lView);
14137 }
14138 if (localRefsIndex != null) {
14139 saveResolvedLocalsInData(lView, tNode);
14140 }
14141 return ɵɵelementContainerStart;
14142}
14143/**
14144 * Mark the end of the <ng-container>.
14145 * @returns This function returns itself so that it may be chained.
14146 *
14147 * @codeGenApi
14148 */
14149function ɵɵelementContainerEnd() {
14150 let currentTNode = getCurrentTNode();
14151 const tView = getTView();
14152 if (isCurrentTNodeParent()) {
14153 setCurrentTNodeAsNotParent();
14154 }
14155 else {
14156 ngDevMode && assertHasParent(currentTNode);
14157 currentTNode = currentTNode.parent;
14158 setCurrentTNode(currentTNode, false);
14159 }
14160 ngDevMode && assertTNodeType(currentTNode, 8 /* TNodeType.ElementContainer */);
14161 if (tView.firstCreatePass) {
14162 registerPostOrderHooks(tView, currentTNode);
14163 if (isContentQueryHost(currentTNode)) {
14164 tView.queries.elementEnd(currentTNode);
14165 }
14166 }
14167 return ɵɵelementContainerEnd;
14168}
14169/**
14170 * Creates an empty logical container using {@link elementContainerStart}
14171 * and {@link elementContainerEnd}
14172 *
14173 * @param index Index of the element in the LView array
14174 * @param attrsIndex Index of the container attributes in the `consts` array.
14175 * @param localRefsIndex Index of the container's local references in the `consts` array.
14176 * @returns This function returns itself so that it may be chained.
14177 *
14178 * @codeGenApi
14179 */
14180function ɵɵelementContainer(index, attrsIndex, localRefsIndex) {
14181 ɵɵelementContainerStart(index, attrsIndex, localRefsIndex);
14182 ɵɵelementContainerEnd();
14183 return ɵɵelementContainer;
14184}
14185
14186/**
14187 * Returns the current OpaqueViewState instance.
14188 *
14189 * Used in conjunction with the restoreView() instruction to save a snapshot
14190 * of the current view and restore it when listeners are invoked. This allows
14191 * walking the declaration view tree in listeners to get vars from parent views.
14192 *
14193 * @codeGenApi
14194 */
14195function ɵɵgetCurrentView() {
14196 return getLView();
14197}
14198
14199/**
14200 * Determine if the argument is shaped like a Promise
14201 */
14202function isPromise(obj) {
14203 // allow any Promise/A+ compliant thenable.
14204 // It's up to the caller to ensure that obj.then conforms to the spec
14205 return !!obj && typeof obj.then === 'function';
14206}
14207/**
14208 * Determine if the argument is a Subscribable
14209 */
14210function isSubscribable(obj) {
14211 return !!obj && typeof obj.subscribe === 'function';
14212}
14213/**
14214 * Determine if the argument is an Observable
14215 *
14216 * Strictly this tests that the `obj` is `Subscribable`, since `Observable`
14217 * types need additional methods, such as `lift()`. But it is adequate for our
14218 * needs since within the Angular framework code we only ever need to use the
14219 * `subscribe()` method, and RxJS has mechanisms to wrap `Subscribable` objects
14220 * into `Observable` as needed.
14221 */
14222const isObservable = isSubscribable;
14223
14224/**
14225 * Adds an event listener to the current node.
14226 *
14227 * If an output exists on one of the node's directives, it also subscribes to the output
14228 * and saves the subscription for later cleanup.
14229 *
14230 * @param eventName Name of the event
14231 * @param listenerFn The function to be called when event emits
14232 * @param useCapture Whether or not to use capture in event listener - this argument is a reminder
14233 * from the Renderer3 infrastructure and should be removed from the instruction arguments
14234 * @param eventTargetResolver Function that returns global target information in case this listener
14235 * should be attached to a global object like window, document or body
14236 *
14237 * @codeGenApi
14238 */
14239function ɵɵlistener(eventName, listenerFn, useCapture, eventTargetResolver) {
14240 const lView = getLView();
14241 const tView = getTView();
14242 const tNode = getCurrentTNode();
14243 listenerInternal(tView, lView, lView[RENDERER], tNode, eventName, listenerFn, eventTargetResolver);
14244 return ɵɵlistener;
14245}
14246/**
14247 * Registers a synthetic host listener (e.g. `(@foo.start)`) on a component or directive.
14248 *
14249 * This instruction is for compatibility purposes and is designed to ensure that a
14250 * synthetic host listener (e.g. `@HostListener('@foo.start')`) properly gets rendered
14251 * in the component's renderer. Normally all host listeners are evaluated with the
14252 * parent component's renderer, but, in the case of animation @triggers, they need
14253 * to be evaluated with the sub component's renderer (because that's where the
14254 * animation triggers are defined).
14255 *
14256 * Do not use this instruction as a replacement for `listener`. This instruction
14257 * only exists to ensure compatibility with the ViewEngine's host binding behavior.
14258 *
14259 * @param eventName Name of the event
14260 * @param listenerFn The function to be called when event emits
14261 * @param useCapture Whether or not to use capture in event listener
14262 * @param eventTargetResolver Function that returns global target information in case this listener
14263 * should be attached to a global object like window, document or body
14264 *
14265 * @codeGenApi
14266 */
14267function ɵɵsyntheticHostListener(eventName, listenerFn) {
14268 const tNode = getCurrentTNode();
14269 const lView = getLView();
14270 const tView = getTView();
14271 const currentDef = getCurrentDirectiveDef(tView.data);
14272 const renderer = loadComponentRenderer(currentDef, tNode, lView);
14273 listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn);
14274 return ɵɵsyntheticHostListener;
14275}
14276/**
14277 * A utility function that checks if a given element has already an event handler registered for an
14278 * event with a specified name. The TView.cleanup data structure is used to find out which events
14279 * are registered for a given element.
14280 */
14281function findExistingListener(tView, lView, eventName, tNodeIdx) {
14282 const tCleanup = tView.cleanup;
14283 if (tCleanup != null) {
14284 for (let i = 0; i < tCleanup.length - 1; i += 2) {
14285 const cleanupEventName = tCleanup[i];
14286 if (cleanupEventName === eventName && tCleanup[i + 1] === tNodeIdx) {
14287 // We have found a matching event name on the same node but it might not have been
14288 // registered yet, so we must explicitly verify entries in the LView cleanup data
14289 // structures.
14290 const lCleanup = lView[CLEANUP];
14291 const listenerIdxInLCleanup = tCleanup[i + 2];
14292 return lCleanup.length > listenerIdxInLCleanup ? lCleanup[listenerIdxInLCleanup] : null;
14293 }
14294 // TView.cleanup can have a mix of 4-elements entries (for event handler cleanups) or
14295 // 2-element entries (for directive and queries destroy hooks). As such we can encounter
14296 // blocks of 4 or 2 items in the tView.cleanup and this is why we iterate over 2 elements
14297 // first and jump another 2 elements if we detect listeners cleanup (4 elements). Also check
14298 // documentation of TView.cleanup for more details of this data structure layout.
14299 if (typeof cleanupEventName === 'string') {
14300 i += 2;
14301 }
14302 }
14303 }
14304 return null;
14305}
14306function listenerInternal(tView, lView, renderer, tNode, eventName, listenerFn, eventTargetResolver) {
14307 const isTNodeDirectiveHost = isDirectiveHost(tNode);
14308 const firstCreatePass = tView.firstCreatePass;
14309 const tCleanup = firstCreatePass && getOrCreateTViewCleanup(tView);
14310 const context = lView[CONTEXT];
14311 // When the ɵɵlistener instruction was generated and is executed we know that there is either a
14312 // native listener or a directive output on this element. As such we we know that we will have to
14313 // register a listener and store its cleanup function on LView.
14314 const lCleanup = getOrCreateLViewCleanup(lView);
14315 ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
14316 let processOutputs = true;
14317 // Adding a native event listener is applicable when:
14318 // - The corresponding TNode represents a DOM element.
14319 // - The event target has a resolver (usually resulting in a global object,
14320 // such as `window` or `document`).
14321 if ((tNode.type & 3 /* TNodeType.AnyRNode */) || eventTargetResolver) {
14322 const native = getNativeByTNode(tNode, lView);
14323 const target = eventTargetResolver ? eventTargetResolver(native) : native;
14324 const lCleanupIndex = lCleanup.length;
14325 const idxOrTargetGetter = eventTargetResolver ?
14326 (_lView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])) :
14327 tNode.index;
14328 // In order to match current behavior, native DOM event listeners must be added for all
14329 // events (including outputs).
14330 // There might be cases where multiple directives on the same element try to register an event
14331 // handler function for the same event. In this situation we want to avoid registration of
14332 // several native listeners as each registration would be intercepted by NgZone and
14333 // trigger change detection. This would mean that a single user action would result in several
14334 // change detections being invoked. To avoid this situation we want to have only one call to
14335 // native handler registration (for the same element and same type of event).
14336 //
14337 // In order to have just one native event handler in presence of multiple handler functions,
14338 // we just register a first handler function as a native event listener and then chain
14339 // (coalesce) other handler functions on top of the first native handler function.
14340 let existingListener = null;
14341 // Please note that the coalescing described here doesn't happen for events specifying an
14342 // alternative target (ex. (document:click)) - this is to keep backward compatibility with the
14343 // view engine.
14344 // Also, we don't have to search for existing listeners is there are no directives
14345 // matching on a given node as we can't register multiple event handlers for the same event in
14346 // a template (this would mean having duplicate attributes).
14347 if (!eventTargetResolver && isTNodeDirectiveHost) {
14348 existingListener = findExistingListener(tView, lView, eventName, tNode.index);
14349 }
14350 if (existingListener !== null) {
14351 // Attach a new listener to coalesced listeners list, maintaining the order in which
14352 // listeners are registered. For performance reasons, we keep a reference to the last
14353 // listener in that list (in `__ngLastListenerFn__` field), so we can avoid going through
14354 // the entire set each time we need to add a new listener.
14355 const lastListenerFn = existingListener.__ngLastListenerFn__ || existingListener;
14356 lastListenerFn.__ngNextListenerFn__ = listenerFn;
14357 existingListener.__ngLastListenerFn__ = listenerFn;
14358 processOutputs = false;
14359 }
14360 else {
14361 listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */);
14362 const cleanupFn = renderer.listen(target, eventName, listenerFn);
14363 ngDevMode && ngDevMode.rendererAddEventListener++;
14364 lCleanup.push(listenerFn, cleanupFn);
14365 tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, lCleanupIndex + 1);
14366 }
14367 }
14368 else {
14369 // Even if there is no native listener to add, we still need to wrap the listener so that OnPush
14370 // ancestors are marked dirty when an event occurs.
14371 listenerFn = wrapListener(tNode, lView, context, listenerFn, false /** preventDefault */);
14372 }
14373 // subscribe to directive outputs
14374 const outputs = tNode.outputs;
14375 let props;
14376 if (processOutputs && outputs !== null && (props = outputs[eventName])) {
14377 const propsLength = props.length;
14378 if (propsLength) {
14379 for (let i = 0; i < propsLength; i += 2) {
14380 const index = props[i];
14381 ngDevMode && assertIndexInRange(lView, index);
14382 const minifiedName = props[i + 1];
14383 const directiveInstance = lView[index];
14384 const output = directiveInstance[minifiedName];
14385 if (ngDevMode && !isObservable(output)) {
14386 throw new Error(`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`);
14387 }
14388 const subscription = output.subscribe(listenerFn);
14389 const idx = lCleanup.length;
14390 lCleanup.push(listenerFn, subscription);
14391 tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
14392 }
14393 }
14394 }
14395}
14396function executeListenerWithErrorHandling(lView, context, listenerFn, e) {
14397 try {
14398 profiler(6 /* ProfilerEvent.OutputStart */, context, listenerFn);
14399 // Only explicitly returning false from a listener should preventDefault
14400 return listenerFn(e) !== false;
14401 }
14402 catch (error) {
14403 handleError(lView, error);
14404 return false;
14405 }
14406 finally {
14407 profiler(7 /* ProfilerEvent.OutputEnd */, context, listenerFn);
14408 }
14409}
14410/**
14411 * Wraps an event listener with a function that marks ancestors dirty and prevents default behavior,
14412 * if applicable.
14413 *
14414 * @param tNode The TNode associated with this listener
14415 * @param lView The LView that contains this listener
14416 * @param listenerFn The listener function to call
14417 * @param wrapWithPreventDefault Whether or not to prevent default behavior
14418 * (the procedural renderer does this already, so in those cases, we should skip)
14419 */
14420function wrapListener(tNode, lView, context, listenerFn, wrapWithPreventDefault) {
14421 // Note: we are performing most of the work in the listener function itself
14422 // to optimize listener registration.
14423 return function wrapListenerIn_markDirtyAndPreventDefault(e) {
14424 // Ivy uses `Function` as a special token that allows us to unwrap the function
14425 // so that it can be invoked programmatically by `DebugNode.triggerEventHandler`.
14426 if (e === Function) {
14427 return listenerFn;
14428 }
14429 // In order to be backwards compatible with View Engine, events on component host nodes
14430 // must also mark the component view itself dirty (i.e. the view that it owns).
14431 const startView = tNode.componentOffset > -1 ? getComponentLViewByIndex(tNode.index, lView) : lView;
14432 markViewDirty(startView);
14433 let result = executeListenerWithErrorHandling(lView, context, listenerFn, e);
14434 // A just-invoked listener function might have coalesced listeners so we need to check for
14435 // their presence and invoke as needed.
14436 let nextListenerFn = wrapListenerIn_markDirtyAndPreventDefault.__ngNextListenerFn__;
14437 while (nextListenerFn) {
14438 // We should prevent default if any of the listeners explicitly return false
14439 result = executeListenerWithErrorHandling(lView, context, nextListenerFn, e) && result;
14440 nextListenerFn = nextListenerFn.__ngNextListenerFn__;
14441 }
14442 if (wrapWithPreventDefault && result === false) {
14443 e.preventDefault();
14444 // Necessary for legacy browsers that don't support preventDefault (e.g. IE)
14445 e.returnValue = false;
14446 }
14447 return result;
14448 };
14449}
14450
14451/**
14452 * Retrieves a context at the level specified and saves it as the global, contextViewData.
14453 * Will get the next level up if level is not specified.
14454 *
14455 * This is used to save contexts of parent views so they can be bound in embedded views, or
14456 * in conjunction with reference() to bind a ref from a parent view.
14457 *
14458 * @param level The relative level of the view from which to grab context compared to contextVewData
14459 * @returns context
14460 *
14461 * @codeGenApi
14462 */
14463function ɵɵnextContext(level = 1) {
14464 return nextContextImpl(level);
14465}
14466
14467/**
14468 * Checks a given node against matching projection slots and returns the
14469 * determined slot index. Returns "null" if no slot matched the given node.
14470 *
14471 * This function takes into account the parsed ngProjectAs selector from the
14472 * node's attributes. If present, it will check whether the ngProjectAs selector
14473 * matches any of the projection slot selectors.
14474 */
14475function matchingProjectionSlotIndex(tNode, projectionSlots) {
14476 let wildcardNgContentIndex = null;
14477 const ngProjectAsAttrVal = getProjectAsAttrValue(tNode);
14478 for (let i = 0; i < projectionSlots.length; i++) {
14479 const slotValue = projectionSlots[i];
14480 // The last wildcard projection slot should match all nodes which aren't matching
14481 // any selector. This is necessary to be backwards compatible with view engine.
14482 if (slotValue === '*') {
14483 wildcardNgContentIndex = i;
14484 continue;
14485 }
14486 // If we ran into an `ngProjectAs` attribute, we should match its parsed selector
14487 // to the list of selectors, otherwise we fall back to matching against the node.
14488 if (ngProjectAsAttrVal === null ?
14489 isNodeMatchingSelectorList(tNode, slotValue, /* isProjectionMode */ true) :
14490 isSelectorInSelectorList(ngProjectAsAttrVal, slotValue)) {
14491 return i; // first matching selector "captures" a given node
14492 }
14493 }
14494 return wildcardNgContentIndex;
14495}
14496/**
14497 * Instruction to distribute projectable nodes among <ng-content> occurrences in a given template.
14498 * It takes all the selectors from the entire component's template and decides where
14499 * each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is
14500 * backed by a selector).
14501 *
14502 * This function requires CSS selectors to be provided in 2 forms: parsed (by a compiler) and text,
14503 * un-parsed form.
14504 *
14505 * The parsed form is needed for efficient matching of a node against a given CSS selector.
14506 * The un-parsed, textual form is needed for support of the ngProjectAs attribute.
14507 *
14508 * Having a CSS selector in 2 different formats is not ideal, but alternatives have even more
14509 * drawbacks:
14510 * - having only a textual form would require runtime parsing of CSS selectors;
14511 * - we can't have only a parsed as we can't re-construct textual form from it (as entered by a
14512 * template author).
14513 *
14514 * @param projectionSlots? A collection of projection slots. A projection slot can be based
14515 * on a parsed CSS selectors or set to the wildcard selector ("*") in order to match
14516 * all nodes which do not match any selector. If not specified, a single wildcard
14517 * selector projection slot will be defined.
14518 *
14519 * @codeGenApi
14520 */
14521function ɵɵprojectionDef(projectionSlots) {
14522 const componentNode = getLView()[DECLARATION_COMPONENT_VIEW][T_HOST];
14523 if (!componentNode.projection) {
14524 // If no explicit projection slots are defined, fall back to a single
14525 // projection slot with the wildcard selector.
14526 const numProjectionSlots = projectionSlots ? projectionSlots.length : 1;
14527 const projectionHeads = componentNode.projection =
14528 newArray(numProjectionSlots, null);
14529 const tails = projectionHeads.slice();
14530 let componentChild = componentNode.child;
14531 while (componentChild !== null) {
14532 const slotIndex = projectionSlots ? matchingProjectionSlotIndex(componentChild, projectionSlots) : 0;
14533 if (slotIndex !== null) {
14534 if (tails[slotIndex]) {
14535 tails[slotIndex].projectionNext = componentChild;
14536 }
14537 else {
14538 projectionHeads[slotIndex] = componentChild;
14539 }
14540 tails[slotIndex] = componentChild;
14541 }
14542 componentChild = componentChild.next;
14543 }
14544 }
14545}
14546/**
14547 * Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
14548 * to the projectionDef instruction.
14549 *
14550 * @param nodeIndex
14551 * @param selectorIndex:
14552 * - 0 when the selector is `*` (or unspecified as this is the default value),
14553 * - 1 based index of the selector from the {@link projectionDef}
14554 *
14555 * @codeGenApi
14556 */
14557function ɵɵprojection(nodeIndex, selectorIndex = 0, attrs) {
14558 const lView = getLView();
14559 const tView = getTView();
14560 const tProjectionNode = getOrCreateTNode(tView, HEADER_OFFSET + nodeIndex, 16 /* TNodeType.Projection */, null, attrs || null);
14561 // We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
14562 if (tProjectionNode.projection === null)
14563 tProjectionNode.projection = selectorIndex;
14564 // `<ng-content>` has no content
14565 setCurrentTNodeAsNotParent();
14566 if ((tProjectionNode.flags & 32 /* TNodeFlags.isDetached */) !== 32 /* TNodeFlags.isDetached */) {
14567 // re-distribution of projectable nodes is stored on a component's view level
14568 applyProjection(tView, lView, tProjectionNode);
14569 }
14570}
14571
14572/**
14573 *
14574 * Update an interpolated property on an element with a lone bound value
14575 *
14576 * Used when the value passed to a property has 1 interpolated value in it, an no additional text
14577 * surrounds that interpolated value:
14578 *
14579 * ```html
14580 * <div title="{{v0}}"></div>
14581 * ```
14582 *
14583 * Its compiled representation is::
14584 *
14585 * ```ts
14586 * ɵɵpropertyInterpolate('title', v0);
14587 * ```
14588 *
14589 * If the property name also exists as an input property on one of the element's directives,
14590 * the component property will be set instead of the element property. This check must
14591 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14592 *
14593 * @param propName The name of the property to update
14594 * @param prefix Static value used for concatenation only.
14595 * @param v0 Value checked for change.
14596 * @param suffix Static value used for concatenation only.
14597 * @param sanitizer An optional sanitizer function
14598 * @returns itself, so that it may be chained.
14599 * @codeGenApi
14600 */
14601function ɵɵpropertyInterpolate(propName, v0, sanitizer) {
14602 ɵɵpropertyInterpolate1(propName, '', v0, '', sanitizer);
14603 return ɵɵpropertyInterpolate;
14604}
14605/**
14606 *
14607 * Update an interpolated property on an element with single bound value surrounded by text.
14608 *
14609 * Used when the value passed to a property has 1 interpolated value in it:
14610 *
14611 * ```html
14612 * <div title="prefix{{v0}}suffix"></div>
14613 * ```
14614 *
14615 * Its compiled representation is::
14616 *
14617 * ```ts
14618 * ɵɵpropertyInterpolate1('title', 'prefix', v0, 'suffix');
14619 * ```
14620 *
14621 * If the property name also exists as an input property on one of the element's directives,
14622 * the component property will be set instead of the element property. This check must
14623 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14624 *
14625 * @param propName The name of the property to update
14626 * @param prefix Static value used for concatenation only.
14627 * @param v0 Value checked for change.
14628 * @param suffix Static value used for concatenation only.
14629 * @param sanitizer An optional sanitizer function
14630 * @returns itself, so that it may be chained.
14631 * @codeGenApi
14632 */
14633function ɵɵpropertyInterpolate1(propName, prefix, v0, suffix, sanitizer) {
14634 const lView = getLView();
14635 const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
14636 if (interpolatedValue !== NO_CHANGE) {
14637 const tView = getTView();
14638 const tNode = getSelectedTNode();
14639 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14640 ngDevMode &&
14641 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 1, prefix, suffix);
14642 }
14643 return ɵɵpropertyInterpolate1;
14644}
14645/**
14646 *
14647 * Update an interpolated property on an element with 2 bound values surrounded by text.
14648 *
14649 * Used when the value passed to a property has 2 interpolated values in it:
14650 *
14651 * ```html
14652 * <div title="prefix{{v0}}-{{v1}}suffix"></div>
14653 * ```
14654 *
14655 * Its compiled representation is::
14656 *
14657 * ```ts
14658 * ɵɵpropertyInterpolate2('title', 'prefix', v0, '-', v1, 'suffix');
14659 * ```
14660 *
14661 * If the property name also exists as an input property on one of the element's directives,
14662 * the component property will be set instead of the element property. This check must
14663 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14664 *
14665 * @param propName The name of the property to update
14666 * @param prefix Static value used for concatenation only.
14667 * @param v0 Value checked for change.
14668 * @param i0 Static value used for concatenation only.
14669 * @param v1 Value checked for change.
14670 * @param suffix Static value used for concatenation only.
14671 * @param sanitizer An optional sanitizer function
14672 * @returns itself, so that it may be chained.
14673 * @codeGenApi
14674 */
14675function ɵɵpropertyInterpolate2(propName, prefix, v0, i0, v1, suffix, sanitizer) {
14676 const lView = getLView();
14677 const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
14678 if (interpolatedValue !== NO_CHANGE) {
14679 const tView = getTView();
14680 const tNode = getSelectedTNode();
14681 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14682 ngDevMode &&
14683 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 2, prefix, i0, suffix);
14684 }
14685 return ɵɵpropertyInterpolate2;
14686}
14687/**
14688 *
14689 * Update an interpolated property on an element with 3 bound values surrounded by text.
14690 *
14691 * Used when the value passed to a property has 3 interpolated values in it:
14692 *
14693 * ```html
14694 * <div title="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
14695 * ```
14696 *
14697 * Its compiled representation is::
14698 *
14699 * ```ts
14700 * ɵɵpropertyInterpolate3(
14701 * 'title', 'prefix', v0, '-', v1, '-', v2, 'suffix');
14702 * ```
14703 *
14704 * If the property name also exists as an input property on one of the element's directives,
14705 * the component property will be set instead of the element property. This check must
14706 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14707 *
14708 * @param propName The name of the property to update
14709 * @param prefix Static value used for concatenation only.
14710 * @param v0 Value checked for change.
14711 * @param i0 Static value used for concatenation only.
14712 * @param v1 Value checked for change.
14713 * @param i1 Static value used for concatenation only.
14714 * @param v2 Value checked for change.
14715 * @param suffix Static value used for concatenation only.
14716 * @param sanitizer An optional sanitizer function
14717 * @returns itself, so that it may be chained.
14718 * @codeGenApi
14719 */
14720function ɵɵpropertyInterpolate3(propName, prefix, v0, i0, v1, i1, v2, suffix, sanitizer) {
14721 const lView = getLView();
14722 const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
14723 if (interpolatedValue !== NO_CHANGE) {
14724 const tView = getTView();
14725 const tNode = getSelectedTNode();
14726 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14727 ngDevMode &&
14728 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 3, prefix, i0, i1, suffix);
14729 }
14730 return ɵɵpropertyInterpolate3;
14731}
14732/**
14733 *
14734 * Update an interpolated property on an element with 4 bound values surrounded by text.
14735 *
14736 * Used when the value passed to a property has 4 interpolated values in it:
14737 *
14738 * ```html
14739 * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
14740 * ```
14741 *
14742 * Its compiled representation is::
14743 *
14744 * ```ts
14745 * ɵɵpropertyInterpolate4(
14746 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
14747 * ```
14748 *
14749 * If the property name also exists as an input property on one of the element's directives,
14750 * the component property will be set instead of the element property. This check must
14751 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14752 *
14753 * @param propName The name of the property to update
14754 * @param prefix Static value used for concatenation only.
14755 * @param v0 Value checked for change.
14756 * @param i0 Static value used for concatenation only.
14757 * @param v1 Value checked for change.
14758 * @param i1 Static value used for concatenation only.
14759 * @param v2 Value checked for change.
14760 * @param i2 Static value used for concatenation only.
14761 * @param v3 Value checked for change.
14762 * @param suffix Static value used for concatenation only.
14763 * @param sanitizer An optional sanitizer function
14764 * @returns itself, so that it may be chained.
14765 * @codeGenApi
14766 */
14767function ɵɵpropertyInterpolate4(propName, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, sanitizer) {
14768 const lView = getLView();
14769 const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
14770 if (interpolatedValue !== NO_CHANGE) {
14771 const tView = getTView();
14772 const tNode = getSelectedTNode();
14773 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14774 ngDevMode &&
14775 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 4, prefix, i0, i1, i2, suffix);
14776 }
14777 return ɵɵpropertyInterpolate4;
14778}
14779/**
14780 *
14781 * Update an interpolated property on an element with 5 bound values surrounded by text.
14782 *
14783 * Used when the value passed to a property has 5 interpolated values in it:
14784 *
14785 * ```html
14786 * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
14787 * ```
14788 *
14789 * Its compiled representation is::
14790 *
14791 * ```ts
14792 * ɵɵpropertyInterpolate5(
14793 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
14794 * ```
14795 *
14796 * If the property name also exists as an input property on one of the element's directives,
14797 * the component property will be set instead of the element property. This check must
14798 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14799 *
14800 * @param propName The name of the property to update
14801 * @param prefix Static value used for concatenation only.
14802 * @param v0 Value checked for change.
14803 * @param i0 Static value used for concatenation only.
14804 * @param v1 Value checked for change.
14805 * @param i1 Static value used for concatenation only.
14806 * @param v2 Value checked for change.
14807 * @param i2 Static value used for concatenation only.
14808 * @param v3 Value checked for change.
14809 * @param i3 Static value used for concatenation only.
14810 * @param v4 Value checked for change.
14811 * @param suffix Static value used for concatenation only.
14812 * @param sanitizer An optional sanitizer function
14813 * @returns itself, so that it may be chained.
14814 * @codeGenApi
14815 */
14816function ɵɵpropertyInterpolate5(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, sanitizer) {
14817 const lView = getLView();
14818 const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
14819 if (interpolatedValue !== NO_CHANGE) {
14820 const tView = getTView();
14821 const tNode = getSelectedTNode();
14822 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14823 ngDevMode &&
14824 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 5, prefix, i0, i1, i2, i3, suffix);
14825 }
14826 return ɵɵpropertyInterpolate5;
14827}
14828/**
14829 *
14830 * Update an interpolated property on an element with 6 bound values surrounded by text.
14831 *
14832 * Used when the value passed to a property has 6 interpolated values in it:
14833 *
14834 * ```html
14835 * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
14836 * ```
14837 *
14838 * Its compiled representation is::
14839 *
14840 * ```ts
14841 * ɵɵpropertyInterpolate6(
14842 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
14843 * ```
14844 *
14845 * If the property name also exists as an input property on one of the element's directives,
14846 * the component property will be set instead of the element property. This check must
14847 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14848 *
14849 * @param propName The name of the property to update
14850 * @param prefix Static value used for concatenation only.
14851 * @param v0 Value checked for change.
14852 * @param i0 Static value used for concatenation only.
14853 * @param v1 Value checked for change.
14854 * @param i1 Static value used for concatenation only.
14855 * @param v2 Value checked for change.
14856 * @param i2 Static value used for concatenation only.
14857 * @param v3 Value checked for change.
14858 * @param i3 Static value used for concatenation only.
14859 * @param v4 Value checked for change.
14860 * @param i4 Static value used for concatenation only.
14861 * @param v5 Value checked for change.
14862 * @param suffix Static value used for concatenation only.
14863 * @param sanitizer An optional sanitizer function
14864 * @returns itself, so that it may be chained.
14865 * @codeGenApi
14866 */
14867function ɵɵpropertyInterpolate6(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, sanitizer) {
14868 const lView = getLView();
14869 const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
14870 if (interpolatedValue !== NO_CHANGE) {
14871 const tView = getTView();
14872 const tNode = getSelectedTNode();
14873 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14874 ngDevMode &&
14875 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 6, prefix, i0, i1, i2, i3, i4, suffix);
14876 }
14877 return ɵɵpropertyInterpolate6;
14878}
14879/**
14880 *
14881 * Update an interpolated property on an element with 7 bound values surrounded by text.
14882 *
14883 * Used when the value passed to a property has 7 interpolated values in it:
14884 *
14885 * ```html
14886 * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
14887 * ```
14888 *
14889 * Its compiled representation is::
14890 *
14891 * ```ts
14892 * ɵɵpropertyInterpolate7(
14893 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
14894 * ```
14895 *
14896 * If the property name also exists as an input property on one of the element's directives,
14897 * the component property will be set instead of the element property. This check must
14898 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14899 *
14900 * @param propName The name of the property to update
14901 * @param prefix Static value used for concatenation only.
14902 * @param v0 Value checked for change.
14903 * @param i0 Static value used for concatenation only.
14904 * @param v1 Value checked for change.
14905 * @param i1 Static value used for concatenation only.
14906 * @param v2 Value checked for change.
14907 * @param i2 Static value used for concatenation only.
14908 * @param v3 Value checked for change.
14909 * @param i3 Static value used for concatenation only.
14910 * @param v4 Value checked for change.
14911 * @param i4 Static value used for concatenation only.
14912 * @param v5 Value checked for change.
14913 * @param i5 Static value used for concatenation only.
14914 * @param v6 Value checked for change.
14915 * @param suffix Static value used for concatenation only.
14916 * @param sanitizer An optional sanitizer function
14917 * @returns itself, so that it may be chained.
14918 * @codeGenApi
14919 */
14920function ɵɵpropertyInterpolate7(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, sanitizer) {
14921 const lView = getLView();
14922 const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
14923 if (interpolatedValue !== NO_CHANGE) {
14924 const tView = getTView();
14925 const tNode = getSelectedTNode();
14926 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14927 ngDevMode &&
14928 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 7, prefix, i0, i1, i2, i3, i4, i5, suffix);
14929 }
14930 return ɵɵpropertyInterpolate7;
14931}
14932/**
14933 *
14934 * Update an interpolated property on an element with 8 bound values surrounded by text.
14935 *
14936 * Used when the value passed to a property has 8 interpolated values in it:
14937 *
14938 * ```html
14939 * <div title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
14940 * ```
14941 *
14942 * Its compiled representation is::
14943 *
14944 * ```ts
14945 * ɵɵpropertyInterpolate8(
14946 * 'title', 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
14947 * ```
14948 *
14949 * If the property name also exists as an input property on one of the element's directives,
14950 * the component property will be set instead of the element property. This check must
14951 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
14952 *
14953 * @param propName The name of the property to update
14954 * @param prefix Static value used for concatenation only.
14955 * @param v0 Value checked for change.
14956 * @param i0 Static value used for concatenation only.
14957 * @param v1 Value checked for change.
14958 * @param i1 Static value used for concatenation only.
14959 * @param v2 Value checked for change.
14960 * @param i2 Static value used for concatenation only.
14961 * @param v3 Value checked for change.
14962 * @param i3 Static value used for concatenation only.
14963 * @param v4 Value checked for change.
14964 * @param i4 Static value used for concatenation only.
14965 * @param v5 Value checked for change.
14966 * @param i5 Static value used for concatenation only.
14967 * @param v6 Value checked for change.
14968 * @param i6 Static value used for concatenation only.
14969 * @param v7 Value checked for change.
14970 * @param suffix Static value used for concatenation only.
14971 * @param sanitizer An optional sanitizer function
14972 * @returns itself, so that it may be chained.
14973 * @codeGenApi
14974 */
14975function ɵɵpropertyInterpolate8(propName, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, sanitizer) {
14976 const lView = getLView();
14977 const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
14978 if (interpolatedValue !== NO_CHANGE) {
14979 const tView = getTView();
14980 const tNode = getSelectedTNode();
14981 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
14982 ngDevMode &&
14983 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - 8, prefix, i0, i1, i2, i3, i4, i5, i6, suffix);
14984 }
14985 return ɵɵpropertyInterpolate8;
14986}
14987/**
14988 * Update an interpolated property on an element with 9 or more bound values surrounded by text.
14989 *
14990 * Used when the number of interpolated values exceeds 8.
14991 *
14992 * ```html
14993 * <div
14994 * title="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
14995 * ```
14996 *
14997 * Its compiled representation is::
14998 *
14999 * ```ts
15000 * ɵɵpropertyInterpolateV(
15001 * 'title', ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
15002 * 'suffix']);
15003 * ```
15004 *
15005 * If the property name also exists as an input property on one of the element's directives,
15006 * the component property will be set instead of the element property. This check must
15007 * be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled.
15008 *
15009 * @param propName The name of the property to update.
15010 * @param values The collection of values and the strings in between those values, beginning with a
15011 * string prefix and ending with a string suffix.
15012 * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
15013 * @param sanitizer An optional sanitizer function
15014 * @returns itself, so that it may be chained.
15015 * @codeGenApi
15016 */
15017function ɵɵpropertyInterpolateV(propName, values, sanitizer) {
15018 const lView = getLView();
15019 const interpolatedValue = interpolationV(lView, values);
15020 if (interpolatedValue !== NO_CHANGE) {
15021 const tView = getTView();
15022 const tNode = getSelectedTNode();
15023 elementPropertyInternal(tView, tNode, lView, propName, interpolatedValue, lView[RENDERER], sanitizer, false);
15024 if (ngDevMode) {
15025 const interpolationInBetween = [values[0]]; // prefix
15026 for (let i = 2; i < values.length; i += 2) {
15027 interpolationInBetween.push(values[i]);
15028 }
15029 storePropertyBindingMetadata(tView.data, tNode, propName, getBindingIndex() - interpolationInBetween.length + 1, ...interpolationInBetween);
15030 }
15031 }
15032 return ɵɵpropertyInterpolateV;
15033}
15034
15035function toTStylingRange(prev, next) {
15036 ngDevMode && assertNumberInRange(prev, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
15037 ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
15038 return (prev << 17 /* StylingRange.PREV_SHIFT */ | next << 2 /* StylingRange.NEXT_SHIFT */);
15039}
15040function getTStylingRangePrev(tStylingRange) {
15041 ngDevMode && assertNumber(tStylingRange, 'expected number');
15042 return (tStylingRange >> 17 /* StylingRange.PREV_SHIFT */) & 32767 /* StylingRange.UNSIGNED_MASK */;
15043}
15044function getTStylingRangePrevDuplicate(tStylingRange) {
15045 ngDevMode && assertNumber(tStylingRange, 'expected number');
15046 return (tStylingRange & 2 /* StylingRange.PREV_DUPLICATE */) ==
15047 2 /* StylingRange.PREV_DUPLICATE */;
15048}
15049function setTStylingRangePrev(tStylingRange, previous) {
15050 ngDevMode && assertNumber(tStylingRange, 'expected number');
15051 ngDevMode && assertNumberInRange(previous, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
15052 return ((tStylingRange & ~4294836224 /* StylingRange.PREV_MASK */) |
15053 (previous << 17 /* StylingRange.PREV_SHIFT */));
15054}
15055function setTStylingRangePrevDuplicate(tStylingRange) {
15056 ngDevMode && assertNumber(tStylingRange, 'expected number');
15057 return (tStylingRange | 2 /* StylingRange.PREV_DUPLICATE */);
15058}
15059function getTStylingRangeNext(tStylingRange) {
15060 ngDevMode && assertNumber(tStylingRange, 'expected number');
15061 return (tStylingRange & 131068 /* StylingRange.NEXT_MASK */) >> 2 /* StylingRange.NEXT_SHIFT */;
15062}
15063function setTStylingRangeNext(tStylingRange, next) {
15064 ngDevMode && assertNumber(tStylingRange, 'expected number');
15065 ngDevMode && assertNumberInRange(next, 0, 32767 /* StylingRange.UNSIGNED_MASK */);
15066 return ((tStylingRange & ~131068 /* StylingRange.NEXT_MASK */) | //
15067 next << 2 /* StylingRange.NEXT_SHIFT */);
15068}
15069function getTStylingRangeNextDuplicate(tStylingRange) {
15070 ngDevMode && assertNumber(tStylingRange, 'expected number');
15071 return (tStylingRange & 1 /* StylingRange.NEXT_DUPLICATE */) ===
15072 1 /* StylingRange.NEXT_DUPLICATE */;
15073}
15074function setTStylingRangeNextDuplicate(tStylingRange) {
15075 ngDevMode && assertNumber(tStylingRange, 'expected number');
15076 return (tStylingRange | 1 /* StylingRange.NEXT_DUPLICATE */);
15077}
15078function getTStylingRangeTail(tStylingRange) {
15079 ngDevMode && assertNumber(tStylingRange, 'expected number');
15080 const next = getTStylingRangeNext(tStylingRange);
15081 return next === 0 ? getTStylingRangePrev(tStylingRange) : next;
15082}
15083
15084/**
15085 * NOTE: The word `styling` is used interchangeably as style or class styling.
15086 *
15087 * This file contains code to link styling instructions together so that they can be replayed in
15088 * priority order. The file exists because Ivy styling instruction execution order does not match
15089 * that of the priority order. The purpose of this code is to create a linked list so that the
15090 * instructions can be traversed in priority order when computing the styles.
15091 *
15092 * Assume we are dealing with the following code:
15093 * ```
15094 * @Component({
15095 * template: `
15096 * <my-cmp [style]=" {color: '#001'} "
15097 * [style.color]=" #002 "
15098 * dir-style-color-1
15099 * dir-style-color-2> `
15100 * })
15101 * class ExampleComponent {
15102 * static ngComp = ... {
15103 * ...
15104 * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
15105 * ɵɵstyleMap({color: '#001'});
15106 * ɵɵstyleProp('color', '#002');
15107 * ...
15108 * }
15109 * }
15110 *
15111 * @Directive({
15112 * selector: `[dir-style-color-1]',
15113 * })
15114 * class Style1Directive {
15115 * @HostBinding('style') style = {color: '#005'};
15116 * @HostBinding('style.color') color = '#006';
15117 *
15118 * static ngDir = ... {
15119 * ...
15120 * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
15121 * ɵɵstyleMap({color: '#005'});
15122 * ɵɵstyleProp('color', '#006');
15123 * ...
15124 * }
15125 * }
15126 *
15127 * @Directive({
15128 * selector: `[dir-style-color-2]',
15129 * })
15130 * class Style2Directive {
15131 * @HostBinding('style') style = {color: '#007'};
15132 * @HostBinding('style.color') color = '#008';
15133 *
15134 * static ngDir = ... {
15135 * ...
15136 * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
15137 * ɵɵstyleMap({color: '#007'});
15138 * ɵɵstyleProp('color', '#008');
15139 * ...
15140 * }
15141 * }
15142 *
15143 * @Directive({
15144 * selector: `my-cmp',
15145 * })
15146 * class MyComponent {
15147 * @HostBinding('style') style = {color: '#003'};
15148 * @HostBinding('style.color') color = '#004';
15149 *
15150 * static ngComp = ... {
15151 * ...
15152 * // Compiler ensures that `ɵɵstyleProp` is after `ɵɵstyleMap`
15153 * ɵɵstyleMap({color: '#003'});
15154 * ɵɵstyleProp('color', '#004');
15155 * ...
15156 * }
15157 * }
15158 * ```
15159 *
15160 * The Order of instruction execution is:
15161 *
15162 * NOTE: the comment binding location is for illustrative purposes only.
15163 *
15164 * ```
15165 * // Template: (ExampleComponent)
15166 * ɵɵstyleMap({color: '#001'}); // Binding index: 10
15167 * ɵɵstyleProp('color', '#002'); // Binding index: 12
15168 * // MyComponent
15169 * ɵɵstyleMap({color: '#003'}); // Binding index: 20
15170 * ɵɵstyleProp('color', '#004'); // Binding index: 22
15171 * // Style1Directive
15172 * ɵɵstyleMap({color: '#005'}); // Binding index: 24
15173 * ɵɵstyleProp('color', '#006'); // Binding index: 26
15174 * // Style2Directive
15175 * ɵɵstyleMap({color: '#007'}); // Binding index: 28
15176 * ɵɵstyleProp('color', '#008'); // Binding index: 30
15177 * ```
15178 *
15179 * The correct priority order of concatenation is:
15180 *
15181 * ```
15182 * // MyComponent
15183 * ɵɵstyleMap({color: '#003'}); // Binding index: 20
15184 * ɵɵstyleProp('color', '#004'); // Binding index: 22
15185 * // Style1Directive
15186 * ɵɵstyleMap({color: '#005'}); // Binding index: 24
15187 * ɵɵstyleProp('color', '#006'); // Binding index: 26
15188 * // Style2Directive
15189 * ɵɵstyleMap({color: '#007'}); // Binding index: 28
15190 * ɵɵstyleProp('color', '#008'); // Binding index: 30
15191 * // Template: (ExampleComponent)
15192 * ɵɵstyleMap({color: '#001'}); // Binding index: 10
15193 * ɵɵstyleProp('color', '#002'); // Binding index: 12
15194 * ```
15195 *
15196 * What color should be rendered?
15197 *
15198 * Once the items are correctly sorted in the list, the answer is simply the last item in the
15199 * concatenation list which is `#002`.
15200 *
15201 * To do so we keep a linked list of all of the bindings which pertain to this element.
15202 * Notice that the bindings are inserted in the order of execution, but the `TView.data` allows
15203 * us to traverse them in the order of priority.
15204 *
15205 * |Idx|`TView.data`|`LView` | Notes
15206 * |---|------------|-----------------|--------------
15207 * |...| | |
15208 * |10 |`null` |`{color: '#001'}`| `ɵɵstyleMap('color', {color: '#001'})`
15209 * |11 |`30 | 12` | ... |
15210 * |12 |`color` |`'#002'` | `ɵɵstyleProp('color', '#002')`
15211 * |13 |`10 | 0` | ... |
15212 * |...| | |
15213 * |20 |`null` |`{color: '#003'}`| `ɵɵstyleMap('color', {color: '#003'})`
15214 * |21 |`0 | 22` | ... |
15215 * |22 |`color` |`'#004'` | `ɵɵstyleProp('color', '#004')`
15216 * |23 |`20 | 24` | ... |
15217 * |24 |`null` |`{color: '#005'}`| `ɵɵstyleMap('color', {color: '#005'})`
15218 * |25 |`22 | 26` | ... |
15219 * |26 |`color` |`'#006'` | `ɵɵstyleProp('color', '#006')`
15220 * |27 |`24 | 28` | ... |
15221 * |28 |`null` |`{color: '#007'}`| `ɵɵstyleMap('color', {color: '#007'})`
15222 * |29 |`26 | 30` | ... |
15223 * |30 |`color` |`'#008'` | `ɵɵstyleProp('color', '#008')`
15224 * |31 |`28 | 10` | ... |
15225 *
15226 * The above data structure allows us to re-concatenate the styling no matter which data binding
15227 * changes.
15228 *
15229 * NOTE: in addition to keeping track of next/previous index the `TView.data` also stores prev/next
15230 * duplicate bit. The duplicate bit if true says there either is a binding with the same name or
15231 * there is a map (which may contain the name). This information is useful in knowing if other
15232 * styles with higher priority need to be searched for overwrites.
15233 *
15234 * NOTE: See `should support example in 'tnode_linked_list.ts' documentation` in
15235 * `tnode_linked_list_spec.ts` for working example.
15236 */
15237let __unused_const_as_closure_does_not_like_standalone_comment_blocks__;
15238/**
15239 * Insert new `tStyleValue` at `TData` and link existing style bindings such that we maintain linked
15240 * list of styles and compute the duplicate flag.
15241 *
15242 * Note: this function is executed during `firstUpdatePass` only to populate the `TView.data`.
15243 *
15244 * The function works by keeping track of `tStylingRange` which contains two pointers pointing to
15245 * the head/tail of the template portion of the styles.
15246 * - if `isHost === false` (we are template) then insertion is at tail of `TStylingRange`
15247 * - if `isHost === true` (we are host binding) then insertion is at head of `TStylingRange`
15248 *
15249 * @param tData The `TData` to insert into.
15250 * @param tNode `TNode` associated with the styling element.
15251 * @param tStylingKey See `TStylingKey`.
15252 * @param index location of where `tStyleValue` should be stored (and linked into list.)
15253 * @param isHostBinding `true` if the insertion is for a `hostBinding`. (insertion is in front of
15254 * template.)
15255 * @param isClassBinding True if the associated `tStylingKey` as a `class` styling.
15256 * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.)
15257 */
15258function insertTStylingBinding(tData, tNode, tStylingKeyWithStatic, index, isHostBinding, isClassBinding) {
15259 ngDevMode && assertFirstUpdatePass(getTView());
15260 let tBindings = isClassBinding ? tNode.classBindings : tNode.styleBindings;
15261 let tmplHead = getTStylingRangePrev(tBindings);
15262 let tmplTail = getTStylingRangeNext(tBindings);
15263 tData[index] = tStylingKeyWithStatic;
15264 let isKeyDuplicateOfStatic = false;
15265 let tStylingKey;
15266 if (Array.isArray(tStylingKeyWithStatic)) {
15267 // We are case when the `TStylingKey` contains static fields as well.
15268 const staticKeyValueArray = tStylingKeyWithStatic;
15269 tStylingKey = staticKeyValueArray[1]; // unwrap.
15270 // We need to check if our key is present in the static so that we can mark it as duplicate.
15271 if (tStylingKey === null ||
15272 keyValueArrayIndexOf(staticKeyValueArray, tStylingKey) > 0) {
15273 // tStylingKey is present in the statics, need to mark it as duplicate.
15274 isKeyDuplicateOfStatic = true;
15275 }
15276 }
15277 else {
15278 tStylingKey = tStylingKeyWithStatic;
15279 }
15280 if (isHostBinding) {
15281 // We are inserting host bindings
15282 // If we don't have template bindings then `tail` is 0.
15283 const hasTemplateBindings = tmplTail !== 0;
15284 // This is important to know because that means that the `head` can't point to the first
15285 // template bindings (there are none.) Instead the head points to the tail of the template.
15286 if (hasTemplateBindings) {
15287 // template head's "prev" will point to last host binding or to 0 if no host bindings yet
15288 const previousNode = getTStylingRangePrev(tData[tmplHead + 1]);
15289 tData[index + 1] = toTStylingRange(previousNode, tmplHead);
15290 // if a host binding has already been registered, we need to update the next of that host
15291 // binding to point to this one
15292 if (previousNode !== 0) {
15293 // We need to update the template-tail value to point to us.
15294 tData[previousNode + 1] =
15295 setTStylingRangeNext(tData[previousNode + 1], index);
15296 }
15297 // The "previous" of the template binding head should point to this host binding
15298 tData[tmplHead + 1] = setTStylingRangePrev(tData[tmplHead + 1], index);
15299 }
15300 else {
15301 tData[index + 1] = toTStylingRange(tmplHead, 0);
15302 // if a host binding has already been registered, we need to update the next of that host
15303 // binding to point to this one
15304 if (tmplHead !== 0) {
15305 // We need to update the template-tail value to point to us.
15306 tData[tmplHead + 1] = setTStylingRangeNext(tData[tmplHead + 1], index);
15307 }
15308 // if we don't have template, the head points to template-tail, and needs to be advanced.
15309 tmplHead = index;
15310 }
15311 }
15312 else {
15313 // We are inserting in template section.
15314 // We need to set this binding's "previous" to the current template tail
15315 tData[index + 1] = toTStylingRange(tmplTail, 0);
15316 ngDevMode &&
15317 assertEqual(tmplHead !== 0 && tmplTail === 0, false, 'Adding template bindings after hostBindings is not allowed.');
15318 if (tmplHead === 0) {
15319 tmplHead = index;
15320 }
15321 else {
15322 // We need to update the previous value "next" to point to this binding
15323 tData[tmplTail + 1] = setTStylingRangeNext(tData[tmplTail + 1], index);
15324 }
15325 tmplTail = index;
15326 }
15327 // Now we need to update / compute the duplicates.
15328 // Starting with our location search towards head (least priority)
15329 if (isKeyDuplicateOfStatic) {
15330 tData[index + 1] = setTStylingRangePrevDuplicate(tData[index + 1]);
15331 }
15332 markDuplicates(tData, tStylingKey, index, true, isClassBinding);
15333 markDuplicates(tData, tStylingKey, index, false, isClassBinding);
15334 markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding);
15335 tBindings = toTStylingRange(tmplHead, tmplTail);
15336 if (isClassBinding) {
15337 tNode.classBindings = tBindings;
15338 }
15339 else {
15340 tNode.styleBindings = tBindings;
15341 }
15342}
15343/**
15344 * Look into the residual styling to see if the current `tStylingKey` is duplicate of residual.
15345 *
15346 * @param tNode `TNode` where the residual is stored.
15347 * @param tStylingKey `TStylingKey` to store.
15348 * @param tData `TData` associated with the current `LView`.
15349 * @param index location of where `tStyleValue` should be stored (and linked into list.)
15350 * @param isClassBinding True if the associated `tStylingKey` as a `class` styling.
15351 * `tNode.classBindings` should be used (or `tNode.styleBindings` otherwise.)
15352 */
15353function markDuplicateOfResidualStyling(tNode, tStylingKey, tData, index, isClassBinding) {
15354 const residual = isClassBinding ? tNode.residualClasses : tNode.residualStyles;
15355 if (residual != null /* or undefined */ && typeof tStylingKey == 'string' &&
15356 keyValueArrayIndexOf(residual, tStylingKey) >= 0) {
15357 // We have duplicate in the residual so mark ourselves as duplicate.
15358 tData[index + 1] = setTStylingRangeNextDuplicate(tData[index + 1]);
15359 }
15360}
15361/**
15362 * Marks `TStyleValue`s as duplicates if another style binding in the list has the same
15363 * `TStyleValue`.
15364 *
15365 * NOTE: this function is intended to be called twice once with `isPrevDir` set to `true` and once
15366 * with it set to `false` to search both the previous as well as next items in the list.
15367 *
15368 * No duplicate case
15369 * ```
15370 * [style.color]
15371 * [style.width.px] <<- index
15372 * [style.height.px]
15373 * ```
15374 *
15375 * In the above case adding `[style.width.px]` to the existing `[style.color]` produces no
15376 * duplicates because `width` is not found in any other part of the linked list.
15377 *
15378 * Duplicate case
15379 * ```
15380 * [style.color]
15381 * [style.width.em]
15382 * [style.width.px] <<- index
15383 * ```
15384 * In the above case adding `[style.width.px]` will produce a duplicate with `[style.width.em]`
15385 * because `width` is found in the chain.
15386 *
15387 * Map case 1
15388 * ```
15389 * [style.width.px]
15390 * [style.color]
15391 * [style] <<- index
15392 * ```
15393 * In the above case adding `[style]` will produce a duplicate with any other bindings because
15394 * `[style]` is a Map and as such is fully dynamic and could produce `color` or `width`.
15395 *
15396 * Map case 2
15397 * ```
15398 * [style]
15399 * [style.width.px]
15400 * [style.color] <<- index
15401 * ```
15402 * In the above case adding `[style.color]` will produce a duplicate because there is already a
15403 * `[style]` binding which is a Map and as such is fully dynamic and could produce `color` or
15404 * `width`.
15405 *
15406 * NOTE: Once `[style]` (Map) is added into the system all things are mapped as duplicates.
15407 * NOTE: We use `style` as example, but same logic is applied to `class`es as well.
15408 *
15409 * @param tData `TData` where the linked list is stored.
15410 * @param tStylingKey `TStylingKeyPrimitive` which contains the value to compare to other keys in
15411 * the linked list.
15412 * @param index Starting location in the linked list to search from
15413 * @param isPrevDir Direction.
15414 * - `true` for previous (lower priority);
15415 * - `false` for next (higher priority).
15416 */
15417function markDuplicates(tData, tStylingKey, index, isPrevDir, isClassBinding) {
15418 const tStylingAtIndex = tData[index + 1];
15419 const isMap = tStylingKey === null;
15420 let cursor = isPrevDir ? getTStylingRangePrev(tStylingAtIndex) : getTStylingRangeNext(tStylingAtIndex);
15421 let foundDuplicate = false;
15422 // We keep iterating as long as we have a cursor
15423 // AND either:
15424 // - we found what we are looking for, OR
15425 // - we are a map in which case we have to continue searching even after we find what we were
15426 // looking for since we are a wild card and everything needs to be flipped to duplicate.
15427 while (cursor !== 0 && (foundDuplicate === false || isMap)) {
15428 ngDevMode && assertIndexInRange(tData, cursor);
15429 const tStylingValueAtCursor = tData[cursor];
15430 const tStyleRangeAtCursor = tData[cursor + 1];
15431 if (isStylingMatch(tStylingValueAtCursor, tStylingKey)) {
15432 foundDuplicate = true;
15433 tData[cursor + 1] = isPrevDir ? setTStylingRangeNextDuplicate(tStyleRangeAtCursor) :
15434 setTStylingRangePrevDuplicate(tStyleRangeAtCursor);
15435 }
15436 cursor = isPrevDir ? getTStylingRangePrev(tStyleRangeAtCursor) :
15437 getTStylingRangeNext(tStyleRangeAtCursor);
15438 }
15439 if (foundDuplicate) {
15440 // if we found a duplicate, than mark ourselves.
15441 tData[index + 1] = isPrevDir ? setTStylingRangePrevDuplicate(tStylingAtIndex) :
15442 setTStylingRangeNextDuplicate(tStylingAtIndex);
15443 }
15444}
15445/**
15446 * Determines if two `TStylingKey`s are a match.
15447 *
15448 * When computing whether a binding contains a duplicate, we need to compare if the instruction
15449 * `TStylingKey` has a match.
15450 *
15451 * Here are examples of `TStylingKey`s which match given `tStylingKeyCursor` is:
15452 * - `color`
15453 * - `color` // Match another color
15454 * - `null` // That means that `tStylingKey` is a `classMap`/`styleMap` instruction
15455 * - `['', 'color', 'other', true]` // wrapped `color` so match
15456 * - `['', null, 'other', true]` // wrapped `null` so match
15457 * - `['', 'width', 'color', 'value']` // wrapped static value contains a match on `'color'`
15458 * - `null` // `tStylingKeyCursor` always match as it is `classMap`/`styleMap` instruction
15459 *
15460 * @param tStylingKeyCursor
15461 * @param tStylingKey
15462 */
15463function isStylingMatch(tStylingKeyCursor, tStylingKey) {
15464 ngDevMode &&
15465 assertNotEqual(Array.isArray(tStylingKey), true, 'Expected that \'tStylingKey\' has been unwrapped');
15466 if (tStylingKeyCursor === null || // If the cursor is `null` it means that we have map at that
15467 // location so we must assume that we have a match.
15468 tStylingKey == null || // If `tStylingKey` is `null` then it is a map therefor assume that it
15469 // contains a match.
15470 (Array.isArray(tStylingKeyCursor) ? tStylingKeyCursor[1] : tStylingKeyCursor) ===
15471 tStylingKey // If the keys match explicitly than we are a match.
15472 ) {
15473 return true;
15474 }
15475 else if (Array.isArray(tStylingKeyCursor) && typeof tStylingKey === 'string') {
15476 // if we did not find a match, but `tStylingKeyCursor` is `KeyValueArray` that means cursor has
15477 // statics and we need to check those as well.
15478 return keyValueArrayIndexOf(tStylingKeyCursor, tStylingKey) >=
15479 0; // see if we are matching the key
15480 }
15481 return false;
15482}
15483
15484// Global state of the parser. (This makes parser non-reentrant, but that is not an issue)
15485const parserState = {
15486 textEnd: 0,
15487 key: 0,
15488 keyEnd: 0,
15489 value: 0,
15490 valueEnd: 0,
15491};
15492/**
15493 * Retrieves the last parsed `key` of style.
15494 * @param text the text to substring the key from.
15495 */
15496function getLastParsedKey(text) {
15497 return text.substring(parserState.key, parserState.keyEnd);
15498}
15499/**
15500 * Retrieves the last parsed `value` of style.
15501 * @param text the text to substring the key from.
15502 */
15503function getLastParsedValue(text) {
15504 return text.substring(parserState.value, parserState.valueEnd);
15505}
15506/**
15507 * Initializes `className` string for parsing and parses the first token.
15508 *
15509 * This function is intended to be used in this format:
15510 * ```
15511 * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
15512 * const key = getLastParsedKey();
15513 * ...
15514 * }
15515 * ```
15516 * @param text `className` to parse
15517 * @returns index where the next invocation of `parseClassNameNext` should resume.
15518 */
15519function parseClassName(text) {
15520 resetParserState(text);
15521 return parseClassNameNext(text, consumeWhitespace(text, 0, parserState.textEnd));
15522}
15523/**
15524 * Parses next `className` token.
15525 *
15526 * This function is intended to be used in this format:
15527 * ```
15528 * for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
15529 * const key = getLastParsedKey();
15530 * ...
15531 * }
15532 * ```
15533 *
15534 * @param text `className` to parse
15535 * @param index where the parsing should resume.
15536 * @returns index where the next invocation of `parseClassNameNext` should resume.
15537 */
15538function parseClassNameNext(text, index) {
15539 const end = parserState.textEnd;
15540 if (end === index) {
15541 return -1;
15542 }
15543 index = parserState.keyEnd = consumeClassToken(text, parserState.key = index, end);
15544 return consumeWhitespace(text, index, end);
15545}
15546/**
15547 * Initializes `cssText` string for parsing and parses the first key/values.
15548 *
15549 * This function is intended to be used in this format:
15550 * ```
15551 * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) {
15552 * const key = getLastParsedKey();
15553 * const value = getLastParsedValue();
15554 * ...
15555 * }
15556 * ```
15557 * @param text `cssText` to parse
15558 * @returns index where the next invocation of `parseStyleNext` should resume.
15559 */
15560function parseStyle(text) {
15561 resetParserState(text);
15562 return parseStyleNext(text, consumeWhitespace(text, 0, parserState.textEnd));
15563}
15564/**
15565 * Parses the next `cssText` key/values.
15566 *
15567 * This function is intended to be used in this format:
15568 * ```
15569 * for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i))) {
15570 * const key = getLastParsedKey();
15571 * const value = getLastParsedValue();
15572 * ...
15573 * }
15574 *
15575 * @param text `cssText` to parse
15576 * @param index where the parsing should resume.
15577 * @returns index where the next invocation of `parseStyleNext` should resume.
15578 */
15579function parseStyleNext(text, startIndex) {
15580 const end = parserState.textEnd;
15581 let index = parserState.key = consumeWhitespace(text, startIndex, end);
15582 if (end === index) {
15583 // we reached an end so just quit
15584 return -1;
15585 }
15586 index = parserState.keyEnd = consumeStyleKey(text, index, end);
15587 index = consumeSeparator(text, index, end, 58 /* CharCode.COLON */);
15588 index = parserState.value = consumeWhitespace(text, index, end);
15589 index = parserState.valueEnd = consumeStyleValue(text, index, end);
15590 return consumeSeparator(text, index, end, 59 /* CharCode.SEMI_COLON */);
15591}
15592/**
15593 * Reset the global state of the styling parser.
15594 * @param text The styling text to parse.
15595 */
15596function resetParserState(text) {
15597 parserState.key = 0;
15598 parserState.keyEnd = 0;
15599 parserState.value = 0;
15600 parserState.valueEnd = 0;
15601 parserState.textEnd = text.length;
15602}
15603/**
15604 * Returns index of next non-whitespace character.
15605 *
15606 * @param text Text to scan
15607 * @param startIndex Starting index of character where the scan should start.
15608 * @param endIndex Ending index of character where the scan should end.
15609 * @returns Index of next non-whitespace character (May be the same as `start` if no whitespace at
15610 * that location.)
15611 */
15612function consumeWhitespace(text, startIndex, endIndex) {
15613 while (startIndex < endIndex && text.charCodeAt(startIndex) <= 32 /* CharCode.SPACE */) {
15614 startIndex++;
15615 }
15616 return startIndex;
15617}
15618/**
15619 * Returns index of last char in class token.
15620 *
15621 * @param text Text to scan
15622 * @param startIndex Starting index of character where the scan should start.
15623 * @param endIndex Ending index of character where the scan should end.
15624 * @returns Index after last char in class token.
15625 */
15626function consumeClassToken(text, startIndex, endIndex) {
15627 while (startIndex < endIndex && text.charCodeAt(startIndex) > 32 /* CharCode.SPACE */) {
15628 startIndex++;
15629 }
15630 return startIndex;
15631}
15632/**
15633 * Consumes all of the characters belonging to style key and token.
15634 *
15635 * @param text Text to scan
15636 * @param startIndex Starting index of character where the scan should start.
15637 * @param endIndex Ending index of character where the scan should end.
15638 * @returns Index after last style key character.
15639 */
15640function consumeStyleKey(text, startIndex, endIndex) {
15641 let ch;
15642 while (startIndex < endIndex &&
15643 ((ch = text.charCodeAt(startIndex)) === 45 /* CharCode.DASH */ || ch === 95 /* CharCode.UNDERSCORE */ ||
15644 ((ch & -33 /* CharCode.UPPER_CASE */) >= 65 /* CharCode.A */ && (ch & -33 /* CharCode.UPPER_CASE */) <= 90 /* CharCode.Z */) ||
15645 (ch >= 48 /* CharCode.ZERO */ && ch <= 57 /* CharCode.NINE */))) {
15646 startIndex++;
15647 }
15648 return startIndex;
15649}
15650/**
15651 * Consumes all whitespace and the separator `:` after the style key.
15652 *
15653 * @param text Text to scan
15654 * @param startIndex Starting index of character where the scan should start.
15655 * @param endIndex Ending index of character where the scan should end.
15656 * @returns Index after separator and surrounding whitespace.
15657 */
15658function consumeSeparator(text, startIndex, endIndex, separator) {
15659 startIndex = consumeWhitespace(text, startIndex, endIndex);
15660 if (startIndex < endIndex) {
15661 if (ngDevMode && text.charCodeAt(startIndex) !== separator) {
15662 malformedStyleError(text, String.fromCharCode(separator), startIndex);
15663 }
15664 startIndex++;
15665 }
15666 return startIndex;
15667}
15668/**
15669 * Consumes style value honoring `url()` and `""` text.
15670 *
15671 * @param text Text to scan
15672 * @param startIndex Starting index of character where the scan should start.
15673 * @param endIndex Ending index of character where the scan should end.
15674 * @returns Index after last style value character.
15675 */
15676function consumeStyleValue(text, startIndex, endIndex) {
15677 let ch1 = -1; // 1st previous character
15678 let ch2 = -1; // 2nd previous character
15679 let ch3 = -1; // 3rd previous character
15680 let i = startIndex;
15681 let lastChIndex = i;
15682 while (i < endIndex) {
15683 const ch = text.charCodeAt(i++);
15684 if (ch === 59 /* CharCode.SEMI_COLON */) {
15685 return lastChIndex;
15686 }
15687 else if (ch === 34 /* CharCode.DOUBLE_QUOTE */ || ch === 39 /* CharCode.SINGLE_QUOTE */) {
15688 lastChIndex = i = consumeQuotedText(text, ch, i, endIndex);
15689 }
15690 else if (startIndex ===
15691 i - 4 && // We have seen only 4 characters so far "URL(" (Ignore "foo_URL()")
15692 ch3 === 85 /* CharCode.U */ &&
15693 ch2 === 82 /* CharCode.R */ && ch1 === 76 /* CharCode.L */ && ch === 40 /* CharCode.OPEN_PAREN */) {
15694 lastChIndex = i = consumeQuotedText(text, 41 /* CharCode.CLOSE_PAREN */, i, endIndex);
15695 }
15696 else if (ch > 32 /* CharCode.SPACE */) {
15697 // if we have a non-whitespace character then capture its location
15698 lastChIndex = i;
15699 }
15700 ch3 = ch2;
15701 ch2 = ch1;
15702 ch1 = ch & -33 /* CharCode.UPPER_CASE */;
15703 }
15704 return lastChIndex;
15705}
15706/**
15707 * Consumes all of the quoted characters.
15708 *
15709 * @param text Text to scan
15710 * @param quoteCharCode CharCode of either `"` or `'` quote or `)` for `url(...)`.
15711 * @param startIndex Starting index of character where the scan should start.
15712 * @param endIndex Ending index of character where the scan should end.
15713 * @returns Index after quoted characters.
15714 */
15715function consumeQuotedText(text, quoteCharCode, startIndex, endIndex) {
15716 let ch1 = -1; // 1st previous character
15717 let index = startIndex;
15718 while (index < endIndex) {
15719 const ch = text.charCodeAt(index++);
15720 if (ch == quoteCharCode && ch1 !== 92 /* CharCode.BACK_SLASH */) {
15721 return index;
15722 }
15723 if (ch == 92 /* CharCode.BACK_SLASH */ && ch1 === 92 /* CharCode.BACK_SLASH */) {
15724 // two back slashes cancel each other out. For example `"\\"` should properly end the
15725 // quotation. (It should not assume that the last `"` is escaped.)
15726 ch1 = 0;
15727 }
15728 else {
15729 ch1 = ch;
15730 }
15731 }
15732 throw ngDevMode ? malformedStyleError(text, String.fromCharCode(quoteCharCode), endIndex) :
15733 new Error();
15734}
15735function malformedStyleError(text, expecting, index) {
15736 ngDevMode && assertEqual(typeof text === 'string', true, 'String expected here');
15737 throw throwError(`Malformed style at location ${index} in string '` + text.substring(0, index) + '[>>' +
15738 text.substring(index, index + 1) + '<<]' + text.slice(index + 1) +
15739 `'. Expecting '${expecting}'.`);
15740}
15741
15742/**
15743 * Update a style binding on an element with the provided value.
15744 *
15745 * If the style value is falsy then it will be removed from the element
15746 * (or assigned a different value depending if there are any styles placed
15747 * on the element with `styleMap` or any static styles that are
15748 * present from when the element was created with `styling`).
15749 *
15750 * Note that the styling element is updated as part of `stylingApply`.
15751 *
15752 * @param prop A valid CSS property.
15753 * @param value New value to write (`null` or an empty string to remove).
15754 * @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
15755 *
15756 * Note that this will apply the provided style value to the host element if this function is called
15757 * within a host binding function.
15758 *
15759 * @codeGenApi
15760 */
15761function ɵɵstyleProp(prop, value, suffix) {
15762 checkStylingProperty(prop, value, suffix, false);
15763 return ɵɵstyleProp;
15764}
15765/**
15766 * Update a class binding on an element with the provided value.
15767 *
15768 * This instruction is meant to handle the `[class.foo]="exp"` case and,
15769 * therefore, the class binding itself must already be allocated using
15770 * `styling` within the creation block.
15771 *
15772 * @param prop A valid CSS class (only one).
15773 * @param value A true/false value which will turn the class on or off.
15774 *
15775 * Note that this will apply the provided class value to the host element if this function
15776 * is called within a host binding function.
15777 *
15778 * @codeGenApi
15779 */
15780function ɵɵclassProp(className, value) {
15781 checkStylingProperty(className, value, null, true);
15782 return ɵɵclassProp;
15783}
15784/**
15785 * Update style bindings using an object literal on an element.
15786 *
15787 * This instruction is meant to apply styling via the `[style]="exp"` template bindings.
15788 * When styles are applied to the element they will then be updated with respect to
15789 * any styles/classes set via `styleProp`. If any styles are set to falsy
15790 * then they will be removed from the element.
15791 *
15792 * Note that the styling instruction will not be applied until `stylingApply` is called.
15793 *
15794 * @param styles A key/value style map of the styles that will be applied to the given element.
15795 * Any missing styles (that have already been applied to the element beforehand) will be
15796 * removed (unset) from the element's styling.
15797 *
15798 * Note that this will apply the provided styleMap value to the host element if this function
15799 * is called within a host binding.
15800 *
15801 * @codeGenApi
15802 */
15803function ɵɵstyleMap(styles) {
15804 checkStylingMap(styleKeyValueArraySet, styleStringParser, styles, false);
15805}
15806/**
15807 * Parse text as style and add values to KeyValueArray.
15808 *
15809 * This code is pulled out to a separate function so that it can be tree shaken away if it is not
15810 * needed. It is only referenced from `ɵɵstyleMap`.
15811 *
15812 * @param keyValueArray KeyValueArray to add parsed values to.
15813 * @param text text to parse.
15814 */
15815function styleStringParser(keyValueArray, text) {
15816 for (let i = parseStyle(text); i >= 0; i = parseStyleNext(text, i)) {
15817 styleKeyValueArraySet(keyValueArray, getLastParsedKey(text), getLastParsedValue(text));
15818 }
15819}
15820/**
15821 * Update class bindings using an object literal or class-string on an element.
15822 *
15823 * This instruction is meant to apply styling via the `[class]="exp"` template bindings.
15824 * When classes are applied to the element they will then be updated with
15825 * respect to any styles/classes set via `classProp`. If any
15826 * classes are set to falsy then they will be removed from the element.
15827 *
15828 * Note that the styling instruction will not be applied until `stylingApply` is called.
15829 * Note that this will the provided classMap value to the host element if this function is called
15830 * within a host binding.
15831 *
15832 * @param classes A key/value map or string of CSS classes that will be added to the
15833 * given element. Any missing classes (that have already been applied to the element
15834 * beforehand) will be removed (unset) from the element's list of CSS classes.
15835 *
15836 * @codeGenApi
15837 */
15838function ɵɵclassMap(classes) {
15839 checkStylingMap(keyValueArraySet, classStringParser, classes, true);
15840}
15841/**
15842 * Parse text as class and add values to KeyValueArray.
15843 *
15844 * This code is pulled out to a separate function so that it can be tree shaken away if it is not
15845 * needed. It is only referenced from `ɵɵclassMap`.
15846 *
15847 * @param keyValueArray KeyValueArray to add parsed values to.
15848 * @param text text to parse.
15849 */
15850function classStringParser(keyValueArray, text) {
15851 for (let i = parseClassName(text); i >= 0; i = parseClassNameNext(text, i)) {
15852 keyValueArraySet(keyValueArray, getLastParsedKey(text), true);
15853 }
15854}
15855/**
15856 * Common code between `ɵɵclassProp` and `ɵɵstyleProp`.
15857 *
15858 * @param prop property name.
15859 * @param value binding value.
15860 * @param suffix suffix for the property (e.g. `em` or `px`)
15861 * @param isClassBased `true` if `class` change (`false` if `style`)
15862 */
15863function checkStylingProperty(prop, value, suffix, isClassBased) {
15864 const lView = getLView();
15865 const tView = getTView();
15866 // Styling instructions use 2 slots per binding.
15867 // 1. one for the value / TStylingKey
15868 // 2. one for the intermittent-value / TStylingRange
15869 const bindingIndex = incrementBindingIndex(2);
15870 if (tView.firstUpdatePass) {
15871 stylingFirstUpdatePass(tView, prop, bindingIndex, isClassBased);
15872 }
15873 if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
15874 const tNode = tView.data[getSelectedIndex()];
15875 updateStyling(tView, tNode, lView, lView[RENDERER], prop, lView[bindingIndex + 1] = normalizeSuffix(value, suffix), isClassBased, bindingIndex);
15876 }
15877}
15878/**
15879 * Common code between `ɵɵclassMap` and `ɵɵstyleMap`.
15880 *
15881 * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
15882 * function so that `style` can be processed. This is done for tree shaking purposes.
15883 * @param stringParser Parser used to parse `value` if `string`. (Passed in as `style` and `class`
15884 * have different parsers.)
15885 * @param value bound value from application
15886 * @param isClassBased `true` if `class` change (`false` if `style`)
15887 */
15888function checkStylingMap(keyValueArraySet, stringParser, value, isClassBased) {
15889 const tView = getTView();
15890 const bindingIndex = incrementBindingIndex(2);
15891 if (tView.firstUpdatePass) {
15892 stylingFirstUpdatePass(tView, null, bindingIndex, isClassBased);
15893 }
15894 const lView = getLView();
15895 if (value !== NO_CHANGE && bindingUpdated(lView, bindingIndex, value)) {
15896 // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
15897 // if so as not to read unnecessarily.
15898 const tNode = tView.data[getSelectedIndex()];
15899 if (hasStylingInputShadow(tNode, isClassBased) && !isInHostBindings(tView, bindingIndex)) {
15900 if (ngDevMode) {
15901 // verify that if we are shadowing then `TData` is appropriately marked so that we skip
15902 // processing this binding in styling resolution.
15903 const tStylingKey = tView.data[bindingIndex];
15904 assertEqual(Array.isArray(tStylingKey) ? tStylingKey[1] : tStylingKey, false, 'Styling linked list shadow input should be marked as \'false\'');
15905 }
15906 // VE does not concatenate the static portion like we are doing here.
15907 // Instead VE just ignores the static completely if dynamic binding is present.
15908 // Because of locality we have already set the static portion because we don't know if there
15909 // is a dynamic portion until later. If we would ignore the static portion it would look like
15910 // the binding has removed it. This would confuse `[ngStyle]`/`[ngClass]` to do the wrong
15911 // thing as it would think that the static portion was removed. For this reason we
15912 // concatenate it so that `[ngStyle]`/`[ngClass]` can continue to work on changed.
15913 let staticPrefix = isClassBased ? tNode.classesWithoutHost : tNode.stylesWithoutHost;
15914 ngDevMode && isClassBased === false && staticPrefix !== null &&
15915 assertEqual(staticPrefix.endsWith(';'), true, 'Expecting static portion to end with \';\'');
15916 if (staticPrefix !== null) {
15917 // We want to make sure that falsy values of `value` become empty strings.
15918 value = concatStringsWithSpace(staticPrefix, value ? value : '');
15919 }
15920 // Given `<div [style] my-dir>` such that `my-dir` has `@Input('style')`.
15921 // This takes over the `[style]` binding. (Same for `[class]`)
15922 setDirectiveInputsWhichShadowsStyling(tView, tNode, lView, value, isClassBased);
15923 }
15924 else {
15925 updateStylingMap(tView, tNode, lView, lView[RENDERER], lView[bindingIndex + 1], lView[bindingIndex + 1] = toStylingKeyValueArray(keyValueArraySet, stringParser, value), isClassBased, bindingIndex);
15926 }
15927 }
15928}
15929/**
15930 * Determines when the binding is in `hostBindings` section
15931 *
15932 * @param tView Current `TView`
15933 * @param bindingIndex index of binding which we would like if it is in `hostBindings`
15934 */
15935function isInHostBindings(tView, bindingIndex) {
15936 // All host bindings are placed after the expando section.
15937 return bindingIndex >= tView.expandoStartIndex;
15938}
15939/**
15940 * Collects the necessary information to insert the binding into a linked list of style bindings
15941 * using `insertTStylingBinding`.
15942 *
15943 * @param tView `TView` where the binding linked list will be stored.
15944 * @param tStylingKey Property/key of the binding.
15945 * @param bindingIndex Index of binding associated with the `prop`
15946 * @param isClassBased `true` if `class` change (`false` if `style`)
15947 */
15948function stylingFirstUpdatePass(tView, tStylingKey, bindingIndex, isClassBased) {
15949 ngDevMode && assertFirstUpdatePass(tView);
15950 const tData = tView.data;
15951 if (tData[bindingIndex + 1] === null) {
15952 // The above check is necessary because we don't clear first update pass until first successful
15953 // (no exception) template execution. This prevents the styling instruction from double adding
15954 // itself to the list.
15955 // `getSelectedIndex()` should be here (rather than in instruction) so that it is guarded by the
15956 // if so as not to read unnecessarily.
15957 const tNode = tData[getSelectedIndex()];
15958 ngDevMode && assertDefined(tNode, 'TNode expected');
15959 const isHostBindings = isInHostBindings(tView, bindingIndex);
15960 if (hasStylingInputShadow(tNode, isClassBased) && tStylingKey === null && !isHostBindings) {
15961 // `tStylingKey === null` implies that we are either `[style]` or `[class]` binding.
15962 // If there is a directive which uses `@Input('style')` or `@Input('class')` than
15963 // we need to neutralize this binding since that directive is shadowing it.
15964 // We turn this into a noop by setting the key to `false`
15965 tStylingKey = false;
15966 }
15967 tStylingKey = wrapInStaticStylingKey(tData, tNode, tStylingKey, isClassBased);
15968 insertTStylingBinding(tData, tNode, tStylingKey, bindingIndex, isHostBindings, isClassBased);
15969 }
15970}
15971/**
15972 * Adds static styling information to the binding if applicable.
15973 *
15974 * The linked list of styles not only stores the list and keys, but also stores static styling
15975 * information on some of the keys. This function determines if the key should contain the styling
15976 * information and computes it.
15977 *
15978 * See `TStylingStatic` for more details.
15979 *
15980 * @param tData `TData` where the linked list is stored.
15981 * @param tNode `TNode` for which the styling is being computed.
15982 * @param stylingKey `TStylingKeyPrimitive` which may need to be wrapped into `TStylingKey`
15983 * @param isClassBased `true` if `class` (`false` if `style`)
15984 */
15985function wrapInStaticStylingKey(tData, tNode, stylingKey, isClassBased) {
15986 const hostDirectiveDef = getCurrentDirectiveDef(tData);
15987 let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles;
15988 if (hostDirectiveDef === null) {
15989 // We are in template node.
15990 // If template node already had styling instruction then it has already collected the static
15991 // styling and there is no need to collect them again. We know that we are the first styling
15992 // instruction because the `TNode.*Bindings` points to 0 (nothing has been inserted yet).
15993 const isFirstStylingInstructionInTemplate = (isClassBased ? tNode.classBindings : tNode.styleBindings) === 0;
15994 if (isFirstStylingInstructionInTemplate) {
15995 // It would be nice to be able to get the statics from `mergeAttrs`, however, at this point
15996 // they are already merged and it would not be possible to figure which property belongs where
15997 // in the priority.
15998 stylingKey = collectStylingFromDirectives(null, tData, tNode, stylingKey, isClassBased);
15999 stylingKey = collectStylingFromTAttrs(stylingKey, tNode.attrs, isClassBased);
16000 // We know that if we have styling binding in template we can't have residual.
16001 residual = null;
16002 }
16003 }
16004 else {
16005 // We are in host binding node and there was no binding instruction in template node.
16006 // This means that we need to compute the residual.
16007 const directiveStylingLast = tNode.directiveStylingLast;
16008 const isFirstStylingInstructionInHostBinding = directiveStylingLast === -1 || tData[directiveStylingLast] !== hostDirectiveDef;
16009 if (isFirstStylingInstructionInHostBinding) {
16010 stylingKey =
16011 collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased);
16012 if (residual === null) {
16013 // - If `null` than either:
16014 // - Template styling instruction already ran and it has consumed the static
16015 // styling into its `TStylingKey` and so there is no need to update residual. Instead
16016 // we need to update the `TStylingKey` associated with the first template node
16017 // instruction. OR
16018 // - Some other styling instruction ran and determined that there are no residuals
16019 let templateStylingKey = getTemplateHeadTStylingKey(tData, tNode, isClassBased);
16020 if (templateStylingKey !== undefined && Array.isArray(templateStylingKey)) {
16021 // Only recompute if `templateStylingKey` had static values. (If no static value found
16022 // then there is nothing to do since this operation can only produce less static keys, not
16023 // more.)
16024 templateStylingKey = collectStylingFromDirectives(null, tData, tNode, templateStylingKey[1] /* unwrap previous statics */, isClassBased);
16025 templateStylingKey =
16026 collectStylingFromTAttrs(templateStylingKey, tNode.attrs, isClassBased);
16027 setTemplateHeadTStylingKey(tData, tNode, isClassBased, templateStylingKey);
16028 }
16029 }
16030 else {
16031 // We only need to recompute residual if it is not `null`.
16032 // - If existing residual (implies there was no template styling). This means that some of
16033 // the statics may have moved from the residual to the `stylingKey` and so we have to
16034 // recompute.
16035 // - If `undefined` this is the first time we are running.
16036 residual = collectResidual(tData, tNode, isClassBased);
16037 }
16038 }
16039 }
16040 if (residual !== undefined) {
16041 isClassBased ? (tNode.residualClasses = residual) : (tNode.residualStyles = residual);
16042 }
16043 return stylingKey;
16044}
16045/**
16046 * Retrieve the `TStylingKey` for the template styling instruction.
16047 *
16048 * This is needed since `hostBinding` styling instructions are inserted after the template
16049 * instruction. While the template instruction needs to update the residual in `TNode` the
16050 * `hostBinding` instructions need to update the `TStylingKey` of the template instruction because
16051 * the template instruction is downstream from the `hostBindings` instructions.
16052 *
16053 * @param tData `TData` where the linked list is stored.
16054 * @param tNode `TNode` for which the styling is being computed.
16055 * @param isClassBased `true` if `class` (`false` if `style`)
16056 * @return `TStylingKey` if found or `undefined` if not found.
16057 */
16058function getTemplateHeadTStylingKey(tData, tNode, isClassBased) {
16059 const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings;
16060 if (getTStylingRangeNext(bindings) === 0) {
16061 // There does not seem to be a styling instruction in the `template`.
16062 return undefined;
16063 }
16064 return tData[getTStylingRangePrev(bindings)];
16065}
16066/**
16067 * Update the `TStylingKey` of the first template instruction in `TNode`.
16068 *
16069 * Logically `hostBindings` styling instructions are of lower priority than that of the template.
16070 * However, they execute after the template styling instructions. This means that they get inserted
16071 * in front of the template styling instructions.
16072 *
16073 * If we have a template styling instruction and a new `hostBindings` styling instruction is
16074 * executed it means that it may need to steal static fields from the template instruction. This
16075 * method allows us to update the first template instruction `TStylingKey` with a new value.
16076 *
16077 * Assume:
16078 * ```
16079 * <div my-dir style="color: red" [style.color]="tmplExp"></div>
16080 *
16081 * @Directive({
16082 * host: {
16083 * 'style': 'width: 100px',
16084 * '[style.color]': 'dirExp',
16085 * }
16086 * })
16087 * class MyDir {}
16088 * ```
16089 *
16090 * when `[style.color]="tmplExp"` executes it creates this data structure.
16091 * ```
16092 * ['', 'color', 'color', 'red', 'width', '100px'],
16093 * ```
16094 *
16095 * The reason for this is that the template instruction does not know if there are styling
16096 * instructions and must assume that there are none and must collect all of the static styling.
16097 * (both
16098 * `color' and 'width`)
16099 *
16100 * When `'[style.color]': 'dirExp',` executes we need to insert a new data into the linked list.
16101 * ```
16102 * ['', 'color', 'width', '100px'], // newly inserted
16103 * ['', 'color', 'color', 'red', 'width', '100px'], // this is wrong
16104 * ```
16105 *
16106 * Notice that the template statics is now wrong as it incorrectly contains `width` so we need to
16107 * update it like so:
16108 * ```
16109 * ['', 'color', 'width', '100px'],
16110 * ['', 'color', 'color', 'red'], // UPDATE
16111 * ```
16112 *
16113 * @param tData `TData` where the linked list is stored.
16114 * @param tNode `TNode` for which the styling is being computed.
16115 * @param isClassBased `true` if `class` (`false` if `style`)
16116 * @param tStylingKey New `TStylingKey` which is replacing the old one.
16117 */
16118function setTemplateHeadTStylingKey(tData, tNode, isClassBased, tStylingKey) {
16119 const bindings = isClassBased ? tNode.classBindings : tNode.styleBindings;
16120 ngDevMode &&
16121 assertNotEqual(getTStylingRangeNext(bindings), 0, 'Expecting to have at least one template styling binding.');
16122 tData[getTStylingRangePrev(bindings)] = tStylingKey;
16123}
16124/**
16125 * Collect all static values after the current `TNode.directiveStylingLast` index.
16126 *
16127 * Collect the remaining styling information which has not yet been collected by an existing
16128 * styling instruction.
16129 *
16130 * @param tData `TData` where the `DirectiveDefs` are stored.
16131 * @param tNode `TNode` which contains the directive range.
16132 * @param isClassBased `true` if `class` (`false` if `style`)
16133 */
16134function collectResidual(tData, tNode, isClassBased) {
16135 let residual = undefined;
16136 const directiveEnd = tNode.directiveEnd;
16137 ngDevMode &&
16138 assertNotEqual(tNode.directiveStylingLast, -1, 'By the time this function gets called at least one hostBindings-node styling instruction must have executed.');
16139 // We add `1 + tNode.directiveStart` because we need to skip the current directive (as we are
16140 // collecting things after the last `hostBindings` directive which had a styling instruction.)
16141 for (let i = 1 + tNode.directiveStylingLast; i < directiveEnd; i++) {
16142 const attrs = tData[i].hostAttrs;
16143 residual = collectStylingFromTAttrs(residual, attrs, isClassBased);
16144 }
16145 return collectStylingFromTAttrs(residual, tNode.attrs, isClassBased);
16146}
16147/**
16148 * Collect the static styling information with lower priority than `hostDirectiveDef`.
16149 *
16150 * (This is opposite of residual styling.)
16151 *
16152 * @param hostDirectiveDef `DirectiveDef` for which we want to collect lower priority static
16153 * styling. (Or `null` if template styling)
16154 * @param tData `TData` where the linked list is stored.
16155 * @param tNode `TNode` for which the styling is being computed.
16156 * @param stylingKey Existing `TStylingKey` to update or wrap.
16157 * @param isClassBased `true` if `class` (`false` if `style`)
16158 */
16159function collectStylingFromDirectives(hostDirectiveDef, tData, tNode, stylingKey, isClassBased) {
16160 // We need to loop because there can be directives which have `hostAttrs` but don't have
16161 // `hostBindings` so this loop catches up to the current directive..
16162 let currentDirective = null;
16163 const directiveEnd = tNode.directiveEnd;
16164 let directiveStylingLast = tNode.directiveStylingLast;
16165 if (directiveStylingLast === -1) {
16166 directiveStylingLast = tNode.directiveStart;
16167 }
16168 else {
16169 directiveStylingLast++;
16170 }
16171 while (directiveStylingLast < directiveEnd) {
16172 currentDirective = tData[directiveStylingLast];
16173 ngDevMode && assertDefined(currentDirective, 'expected to be defined');
16174 stylingKey = collectStylingFromTAttrs(stylingKey, currentDirective.hostAttrs, isClassBased);
16175 if (currentDirective === hostDirectiveDef)
16176 break;
16177 directiveStylingLast++;
16178 }
16179 if (hostDirectiveDef !== null) {
16180 // we only advance the styling cursor if we are collecting data from host bindings.
16181 // Template executes before host bindings and so if we would update the index,
16182 // host bindings would not get their statics.
16183 tNode.directiveStylingLast = directiveStylingLast;
16184 }
16185 return stylingKey;
16186}
16187/**
16188 * Convert `TAttrs` into `TStylingStatic`.
16189 *
16190 * @param stylingKey existing `TStylingKey` to update or wrap.
16191 * @param attrs `TAttributes` to process.
16192 * @param isClassBased `true` if `class` (`false` if `style`)
16193 */
16194function collectStylingFromTAttrs(stylingKey, attrs, isClassBased) {
16195 const desiredMarker = isClassBased ? 1 /* AttributeMarker.Classes */ : 2 /* AttributeMarker.Styles */;
16196 let currentMarker = -1 /* AttributeMarker.ImplicitAttributes */;
16197 if (attrs !== null) {
16198 for (let i = 0; i < attrs.length; i++) {
16199 const item = attrs[i];
16200 if (typeof item === 'number') {
16201 currentMarker = item;
16202 }
16203 else {
16204 if (currentMarker === desiredMarker) {
16205 if (!Array.isArray(stylingKey)) {
16206 stylingKey = stylingKey === undefined ? [] : ['', stylingKey];
16207 }
16208 keyValueArraySet(stylingKey, item, isClassBased ? true : attrs[++i]);
16209 }
16210 }
16211 }
16212 }
16213 return stylingKey === undefined ? null : stylingKey;
16214}
16215/**
16216 * Convert user input to `KeyValueArray`.
16217 *
16218 * This function takes user input which could be `string`, Object literal, or iterable and converts
16219 * it into a consistent representation. The output of this is `KeyValueArray` (which is an array
16220 * where
16221 * even indexes contain keys and odd indexes contain values for those keys).
16222 *
16223 * The advantage of converting to `KeyValueArray` is that we can perform diff in an input
16224 * independent
16225 * way.
16226 * (ie we can compare `foo bar` to `['bar', 'baz'] and determine a set of changes which need to be
16227 * applied)
16228 *
16229 * The fact that `KeyValueArray` is sorted is very important because it allows us to compute the
16230 * difference in linear fashion without the need to allocate any additional data.
16231 *
16232 * For example if we kept this as a `Map` we would have to iterate over previous `Map` to determine
16233 * which values need to be deleted, over the new `Map` to determine additions, and we would have to
16234 * keep additional `Map` to keep track of duplicates or items which have not yet been visited.
16235 *
16236 * @param keyValueArraySet (See `keyValueArraySet` in "util/array_utils") Gets passed in as a
16237 * function so that `style` can be processed. This is done
16238 * for tree shaking purposes.
16239 * @param stringParser The parser is passed in so that it will be tree shakable. See
16240 * `styleStringParser` and `classStringParser`
16241 * @param value The value to parse/convert to `KeyValueArray`
16242 */
16243function toStylingKeyValueArray(keyValueArraySet, stringParser, value) {
16244 if (value == null /*|| value === undefined */ || value === '')
16245 return EMPTY_ARRAY;
16246 const styleKeyValueArray = [];
16247 const unwrappedValue = unwrapSafeValue(value);
16248 if (Array.isArray(unwrappedValue)) {
16249 for (let i = 0; i < unwrappedValue.length; i++) {
16250 keyValueArraySet(styleKeyValueArray, unwrappedValue[i], true);
16251 }
16252 }
16253 else if (typeof unwrappedValue === 'object') {
16254 for (const key in unwrappedValue) {
16255 if (unwrappedValue.hasOwnProperty(key)) {
16256 keyValueArraySet(styleKeyValueArray, key, unwrappedValue[key]);
16257 }
16258 }
16259 }
16260 else if (typeof unwrappedValue === 'string') {
16261 stringParser(styleKeyValueArray, unwrappedValue);
16262 }
16263 else {
16264 ngDevMode &&
16265 throwError('Unsupported styling type ' + typeof unwrappedValue + ': ' + unwrappedValue);
16266 }
16267 return styleKeyValueArray;
16268}
16269/**
16270 * Set a `value` for a `key`.
16271 *
16272 * See: `keyValueArraySet` for details
16273 *
16274 * @param keyValueArray KeyValueArray to add to.
16275 * @param key Style key to add.
16276 * @param value The value to set.
16277 */
16278function styleKeyValueArraySet(keyValueArray, key, value) {
16279 keyValueArraySet(keyValueArray, key, unwrapSafeValue(value));
16280}
16281/**
16282 * Update map based styling.
16283 *
16284 * Map based styling could be anything which contains more than one binding. For example `string`,
16285 * or object literal. Dealing with all of these types would complicate the logic so
16286 * instead this function expects that the complex input is first converted into normalized
16287 * `KeyValueArray`. The advantage of normalization is that we get the values sorted, which makes it
16288 * very cheap to compute deltas between the previous and current value.
16289 *
16290 * @param tView Associated `TView.data` contains the linked list of binding priorities.
16291 * @param tNode `TNode` where the binding is located.
16292 * @param lView `LView` contains the values associated with other styling binding at this `TNode`.
16293 * @param renderer Renderer to use if any updates.
16294 * @param oldKeyValueArray Previous value represented as `KeyValueArray`
16295 * @param newKeyValueArray Current value represented as `KeyValueArray`
16296 * @param isClassBased `true` if `class` (`false` if `style`)
16297 * @param bindingIndex Binding index of the binding.
16298 */
16299function updateStylingMap(tView, tNode, lView, renderer, oldKeyValueArray, newKeyValueArray, isClassBased, bindingIndex) {
16300 if (oldKeyValueArray === NO_CHANGE) {
16301 // On first execution the oldKeyValueArray is NO_CHANGE => treat it as empty KeyValueArray.
16302 oldKeyValueArray = EMPTY_ARRAY;
16303 }
16304 let oldIndex = 0;
16305 let newIndex = 0;
16306 let oldKey = 0 < oldKeyValueArray.length ? oldKeyValueArray[0] : null;
16307 let newKey = 0 < newKeyValueArray.length ? newKeyValueArray[0] : null;
16308 while (oldKey !== null || newKey !== null) {
16309 ngDevMode && assertLessThan(oldIndex, 999, 'Are we stuck in infinite loop?');
16310 ngDevMode && assertLessThan(newIndex, 999, 'Are we stuck in infinite loop?');
16311 const oldValue = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex + 1] : undefined;
16312 const newValue = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex + 1] : undefined;
16313 let setKey = null;
16314 let setValue = undefined;
16315 if (oldKey === newKey) {
16316 // UPDATE: Keys are equal => new value is overwriting old value.
16317 oldIndex += 2;
16318 newIndex += 2;
16319 if (oldValue !== newValue) {
16320 setKey = newKey;
16321 setValue = newValue;
16322 }
16323 }
16324 else if (newKey === null || oldKey !== null && oldKey < newKey) {
16325 // DELETE: oldKey key is missing or we did not find the oldKey in the newValue
16326 // (because the keyValueArray is sorted and `newKey` is found later alphabetically).
16327 // `"background" < "color"` so we need to delete `"background"` because it is not found in the
16328 // new array.
16329 oldIndex += 2;
16330 setKey = oldKey;
16331 }
16332 else {
16333 // CREATE: newKey's is earlier alphabetically than oldKey's (or no oldKey) => we have new key.
16334 // `"color" > "background"` so we need to add `color` because it is in new array but not in
16335 // old array.
16336 ngDevMode && assertDefined(newKey, 'Expecting to have a valid key');
16337 newIndex += 2;
16338 setKey = newKey;
16339 setValue = newValue;
16340 }
16341 if (setKey !== null) {
16342 updateStyling(tView, tNode, lView, renderer, setKey, setValue, isClassBased, bindingIndex);
16343 }
16344 oldKey = oldIndex < oldKeyValueArray.length ? oldKeyValueArray[oldIndex] : null;
16345 newKey = newIndex < newKeyValueArray.length ? newKeyValueArray[newIndex] : null;
16346 }
16347}
16348/**
16349 * Update a simple (property name) styling.
16350 *
16351 * This function takes `prop` and updates the DOM to that value. The function takes the binding
16352 * value as well as binding priority into consideration to determine which value should be written
16353 * to DOM. (For example it may be determined that there is a higher priority overwrite which blocks
16354 * the DOM write, or if the value goes to `undefined` a lower priority overwrite may be consulted.)
16355 *
16356 * @param tView Associated `TView.data` contains the linked list of binding priorities.
16357 * @param tNode `TNode` where the binding is located.
16358 * @param lView `LView` contains the values associated with other styling binding at this `TNode`.
16359 * @param renderer Renderer to use if any updates.
16360 * @param prop Either style property name or a class name.
16361 * @param value Either style value for `prop` or `true`/`false` if `prop` is class.
16362 * @param isClassBased `true` if `class` (`false` if `style`)
16363 * @param bindingIndex Binding index of the binding.
16364 */
16365function updateStyling(tView, tNode, lView, renderer, prop, value, isClassBased, bindingIndex) {
16366 if (!(tNode.type & 3 /* TNodeType.AnyRNode */)) {
16367 // It is possible to have styling on non-elements (such as ng-container).
16368 // This is rare, but it does happen. In such a case, just ignore the binding.
16369 return;
16370 }
16371 const tData = tView.data;
16372 const tRange = tData[bindingIndex + 1];
16373 const higherPriorityValue = getTStylingRangeNextDuplicate(tRange) ?
16374 findStylingValue(tData, tNode, lView, prop, getTStylingRangeNext(tRange), isClassBased) :
16375 undefined;
16376 if (!isStylingValuePresent(higherPriorityValue)) {
16377 // We don't have a next duplicate, or we did not find a duplicate value.
16378 if (!isStylingValuePresent(value)) {
16379 // We should delete current value or restore to lower priority value.
16380 if (getTStylingRangePrevDuplicate(tRange)) {
16381 // We have a possible prev duplicate, let's retrieve it.
16382 value = findStylingValue(tData, null, lView, prop, bindingIndex, isClassBased);
16383 }
16384 }
16385 const rNode = getNativeByIndex(getSelectedIndex(), lView);
16386 applyStyling(renderer, isClassBased, rNode, prop, value);
16387 }
16388}
16389/**
16390 * Search for styling value with higher priority which is overwriting current value, or a
16391 * value of lower priority to which we should fall back if the value is `undefined`.
16392 *
16393 * When value is being applied at a location, related values need to be consulted.
16394 * - If there is a higher priority binding, we should be using that one instead.
16395 * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp1`
16396 * requires that we check `exp2` to see if it is set to value other than `undefined`.
16397 * - If there is a lower priority binding and we are changing to `undefined`
16398 * For example `<div [style]="{color:exp1}" [style.color]="exp2">` change to `exp2` to
16399 * `undefined` requires that we check `exp1` (and static values) and use that as new value.
16400 *
16401 * NOTE: The styling stores two values.
16402 * 1. The raw value which came from the application is stored at `index + 0` location. (This value
16403 * is used for dirty checking).
16404 * 2. The normalized value is stored at `index + 1`.
16405 *
16406 * @param tData `TData` used for traversing the priority.
16407 * @param tNode `TNode` to use for resolving static styling. Also controls search direction.
16408 * - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true.
16409 * If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value.
16410 * - `null` search prev and go all the way to end. Return last value where
16411 * `isStylingValuePresent(value)` is true.
16412 * @param lView `LView` used for retrieving the actual values.
16413 * @param prop Property which we are interested in.
16414 * @param index Starting index in the linked list of styling bindings where the search should start.
16415 * @param isClassBased `true` if `class` (`false` if `style`)
16416 */
16417function findStylingValue(tData, tNode, lView, prop, index, isClassBased) {
16418 // `TNode` to use for resolving static styling. Also controls search direction.
16419 // - `TNode` search next and quit as soon as `isStylingValuePresent(value)` is true.
16420 // If no value found consult `tNode.residualStyle`/`tNode.residualClass` for default value.
16421 // - `null` search prev and go all the way to end. Return last value where
16422 // `isStylingValuePresent(value)` is true.
16423 const isPrevDirection = tNode === null;
16424 let value = undefined;
16425 while (index > 0) {
16426 const rawKey = tData[index];
16427 const containsStatics = Array.isArray(rawKey);
16428 // Unwrap the key if we contain static values.
16429 const key = containsStatics ? rawKey[1] : rawKey;
16430 const isStylingMap = key === null;
16431 let valueAtLViewIndex = lView[index + 1];
16432 if (valueAtLViewIndex === NO_CHANGE) {
16433 // In firstUpdatePass the styling instructions create a linked list of styling.
16434 // On subsequent passes it is possible for a styling instruction to try to read a binding
16435 // which
16436 // has not yet executed. In that case we will find `NO_CHANGE` and we should assume that
16437 // we have `undefined` (or empty array in case of styling-map instruction) instead. This
16438 // allows the resolution to apply the value (which may later be overwritten when the
16439 // binding actually executes.)
16440 valueAtLViewIndex = isStylingMap ? EMPTY_ARRAY : undefined;
16441 }
16442 let currentValue = isStylingMap ? keyValueArrayGet(valueAtLViewIndex, prop) :
16443 (key === prop ? valueAtLViewIndex : undefined);
16444 if (containsStatics && !isStylingValuePresent(currentValue)) {
16445 currentValue = keyValueArrayGet(rawKey, prop);
16446 }
16447 if (isStylingValuePresent(currentValue)) {
16448 value = currentValue;
16449 if (isPrevDirection) {
16450 return value;
16451 }
16452 }
16453 const tRange = tData[index + 1];
16454 index = isPrevDirection ? getTStylingRangePrev(tRange) : getTStylingRangeNext(tRange);
16455 }
16456 if (tNode !== null) {
16457 // in case where we are going in next direction AND we did not find anything, we need to
16458 // consult residual styling
16459 let residual = isClassBased ? tNode.residualClasses : tNode.residualStyles;
16460 if (residual != null /** OR residual !=== undefined */) {
16461 value = keyValueArrayGet(residual, prop);
16462 }
16463 }
16464 return value;
16465}
16466/**
16467 * Determines if the binding value should be used (or if the value is 'undefined' and hence priority
16468 * resolution should be used.)
16469 *
16470 * @param value Binding style value.
16471 */
16472function isStylingValuePresent(value) {
16473 // Currently only `undefined` value is considered non-binding. That is `undefined` says I don't
16474 // have an opinion as to what this binding should be and you should consult other bindings by
16475 // priority to determine the valid value.
16476 // This is extracted into a single function so that we have a single place to control this.
16477 return value !== undefined;
16478}
16479/**
16480 * Normalizes and/or adds a suffix to the value.
16481 *
16482 * If value is `null`/`undefined` no suffix is added
16483 * @param value
16484 * @param suffix
16485 */
16486function normalizeSuffix(value, suffix) {
16487 if (value == null /** || value === undefined */) {
16488 // do nothing
16489 }
16490 else if (typeof suffix === 'string') {
16491 value = value + suffix;
16492 }
16493 else if (typeof value === 'object') {
16494 value = stringify(unwrapSafeValue(value));
16495 }
16496 return value;
16497}
16498/**
16499 * Tests if the `TNode` has input shadow.
16500 *
16501 * An input shadow is when a directive steals (shadows) the input by using `@Input('style')` or
16502 * `@Input('class')` as input.
16503 *
16504 * @param tNode `TNode` which we would like to see if it has shadow.
16505 * @param isClassBased `true` if `class` (`false` if `style`)
16506 */
16507function hasStylingInputShadow(tNode, isClassBased) {
16508 return (tNode.flags & (isClassBased ? 8 /* TNodeFlags.hasClassInput */ : 16 /* TNodeFlags.hasStyleInput */)) !== 0;
16509}
16510
16511/**
16512 * Create static text node
16513 *
16514 * @param index Index of the node in the data array
16515 * @param value Static string value to write.
16516 *
16517 * @codeGenApi
16518 */
16519function ɵɵtext(index, value = '') {
16520 const lView = getLView();
16521 const tView = getTView();
16522 const adjustedIndex = index + HEADER_OFFSET;
16523 ngDevMode &&
16524 assertEqual(getBindingIndex(), tView.bindingStartIndex, 'text nodes should be created before any bindings');
16525 ngDevMode && assertIndexInRange(lView, adjustedIndex);
16526 const tNode = tView.firstCreatePass ?
16527 getOrCreateTNode(tView, adjustedIndex, 1 /* TNodeType.Text */, value, null) :
16528 tView.data[adjustedIndex];
16529 const textNative = lView[adjustedIndex] = createTextNode(lView[RENDERER], value);
16530 appendChild(tView, lView, textNative, tNode);
16531 // Text nodes are self closing.
16532 setCurrentTNode(tNode, false);
16533}
16534
16535/**
16536 *
16537 * Update text content with a lone bound value
16538 *
16539 * Used when a text node has 1 interpolated value in it, an no additional text
16540 * surrounds that interpolated value:
16541 *
16542 * ```html
16543 * <div>{{v0}}</div>
16544 * ```
16545 *
16546 * Its compiled representation is:
16547 *
16548 * ```ts
16549 * ɵɵtextInterpolate(v0);
16550 * ```
16551 * @returns itself, so that it may be chained.
16552 * @see textInterpolateV
16553 * @codeGenApi
16554 */
16555function ɵɵtextInterpolate(v0) {
16556 ɵɵtextInterpolate1('', v0, '');
16557 return ɵɵtextInterpolate;
16558}
16559/**
16560 *
16561 * Update text content with single bound value surrounded by other text.
16562 *
16563 * Used when a text node has 1 interpolated value in it:
16564 *
16565 * ```html
16566 * <div>prefix{{v0}}suffix</div>
16567 * ```
16568 *
16569 * Its compiled representation is:
16570 *
16571 * ```ts
16572 * ɵɵtextInterpolate1('prefix', v0, 'suffix');
16573 * ```
16574 * @returns itself, so that it may be chained.
16575 * @see textInterpolateV
16576 * @codeGenApi
16577 */
16578function ɵɵtextInterpolate1(prefix, v0, suffix) {
16579 const lView = getLView();
16580 const interpolated = interpolation1(lView, prefix, v0, suffix);
16581 if (interpolated !== NO_CHANGE) {
16582 textBindingInternal(lView, getSelectedIndex(), interpolated);
16583 }
16584 return ɵɵtextInterpolate1;
16585}
16586/**
16587 *
16588 * Update text content with 2 bound values surrounded by other text.
16589 *
16590 * Used when a text node has 2 interpolated values in it:
16591 *
16592 * ```html
16593 * <div>prefix{{v0}}-{{v1}}suffix</div>
16594 * ```
16595 *
16596 * Its compiled representation is:
16597 *
16598 * ```ts
16599 * ɵɵtextInterpolate2('prefix', v0, '-', v1, 'suffix');
16600 * ```
16601 * @returns itself, so that it may be chained.
16602 * @see textInterpolateV
16603 * @codeGenApi
16604 */
16605function ɵɵtextInterpolate2(prefix, v0, i0, v1, suffix) {
16606 const lView = getLView();
16607 const interpolated = interpolation2(lView, prefix, v0, i0, v1, suffix);
16608 if (interpolated !== NO_CHANGE) {
16609 textBindingInternal(lView, getSelectedIndex(), interpolated);
16610 }
16611 return ɵɵtextInterpolate2;
16612}
16613/**
16614 *
16615 * Update text content with 3 bound values surrounded by other text.
16616 *
16617 * Used when a text node has 3 interpolated values in it:
16618 *
16619 * ```html
16620 * <div>prefix{{v0}}-{{v1}}-{{v2}}suffix</div>
16621 * ```
16622 *
16623 * Its compiled representation is:
16624 *
16625 * ```ts
16626 * ɵɵtextInterpolate3(
16627 * 'prefix', v0, '-', v1, '-', v2, 'suffix');
16628 * ```
16629 * @returns itself, so that it may be chained.
16630 * @see textInterpolateV
16631 * @codeGenApi
16632 */
16633function ɵɵtextInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
16634 const lView = getLView();
16635 const interpolated = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
16636 if (interpolated !== NO_CHANGE) {
16637 textBindingInternal(lView, getSelectedIndex(), interpolated);
16638 }
16639 return ɵɵtextInterpolate3;
16640}
16641/**
16642 *
16643 * Update text content with 4 bound values surrounded by other text.
16644 *
16645 * Used when a text node has 4 interpolated values in it:
16646 *
16647 * ```html
16648 * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix</div>
16649 * ```
16650 *
16651 * Its compiled representation is:
16652 *
16653 * ```ts
16654 * ɵɵtextInterpolate4(
16655 * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
16656 * ```
16657 * @returns itself, so that it may be chained.
16658 * @see ɵɵtextInterpolateV
16659 * @codeGenApi
16660 */
16661function ɵɵtextInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
16662 const lView = getLView();
16663 const interpolated = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
16664 if (interpolated !== NO_CHANGE) {
16665 textBindingInternal(lView, getSelectedIndex(), interpolated);
16666 }
16667 return ɵɵtextInterpolate4;
16668}
16669/**
16670 *
16671 * Update text content with 5 bound values surrounded by other text.
16672 *
16673 * Used when a text node has 5 interpolated values in it:
16674 *
16675 * ```html
16676 * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix</div>
16677 * ```
16678 *
16679 * Its compiled representation is:
16680 *
16681 * ```ts
16682 * ɵɵtextInterpolate5(
16683 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
16684 * ```
16685 * @returns itself, so that it may be chained.
16686 * @see textInterpolateV
16687 * @codeGenApi
16688 */
16689function ɵɵtextInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
16690 const lView = getLView();
16691 const interpolated = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
16692 if (interpolated !== NO_CHANGE) {
16693 textBindingInternal(lView, getSelectedIndex(), interpolated);
16694 }
16695 return ɵɵtextInterpolate5;
16696}
16697/**
16698 *
16699 * Update text content with 6 bound values surrounded by other text.
16700 *
16701 * Used when a text node has 6 interpolated values in it:
16702 *
16703 * ```html
16704 * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix</div>
16705 * ```
16706 *
16707 * Its compiled representation is:
16708 *
16709 * ```ts
16710 * ɵɵtextInterpolate6(
16711 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
16712 * ```
16713 *
16714 * @param i4 Static value used for concatenation only.
16715 * @param v5 Value checked for change. @returns itself, so that it may be chained.
16716 * @see textInterpolateV
16717 * @codeGenApi
16718 */
16719function ɵɵtextInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
16720 const lView = getLView();
16721 const interpolated = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
16722 if (interpolated !== NO_CHANGE) {
16723 textBindingInternal(lView, getSelectedIndex(), interpolated);
16724 }
16725 return ɵɵtextInterpolate6;
16726}
16727/**
16728 *
16729 * Update text content with 7 bound values surrounded by other text.
16730 *
16731 * Used when a text node has 7 interpolated values in it:
16732 *
16733 * ```html
16734 * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix</div>
16735 * ```
16736 *
16737 * Its compiled representation is:
16738 *
16739 * ```ts
16740 * ɵɵtextInterpolate7(
16741 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
16742 * ```
16743 * @returns itself, so that it may be chained.
16744 * @see textInterpolateV
16745 * @codeGenApi
16746 */
16747function ɵɵtextInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
16748 const lView = getLView();
16749 const interpolated = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
16750 if (interpolated !== NO_CHANGE) {
16751 textBindingInternal(lView, getSelectedIndex(), interpolated);
16752 }
16753 return ɵɵtextInterpolate7;
16754}
16755/**
16756 *
16757 * Update text content with 8 bound values surrounded by other text.
16758 *
16759 * Used when a text node has 8 interpolated values in it:
16760 *
16761 * ```html
16762 * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix</div>
16763 * ```
16764 *
16765 * Its compiled representation is:
16766 *
16767 * ```ts
16768 * ɵɵtextInterpolate8(
16769 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
16770 * ```
16771 * @returns itself, so that it may be chained.
16772 * @see textInterpolateV
16773 * @codeGenApi
16774 */
16775function ɵɵtextInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
16776 const lView = getLView();
16777 const interpolated = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
16778 if (interpolated !== NO_CHANGE) {
16779 textBindingInternal(lView, getSelectedIndex(), interpolated);
16780 }
16781 return ɵɵtextInterpolate8;
16782}
16783/**
16784 * Update text content with 9 or more bound values other surrounded by text.
16785 *
16786 * Used when the number of interpolated values exceeds 8.
16787 *
16788 * ```html
16789 * <div>prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix</div>
16790 * ```
16791 *
16792 * Its compiled representation is:
16793 *
16794 * ```ts
16795 * ɵɵtextInterpolateV(
16796 * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
16797 * 'suffix']);
16798 * ```
16799 *.
16800 * @param values The collection of values and the strings in between those values, beginning with
16801 * a string prefix and ending with a string suffix.
16802 * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
16803 *
16804 * @returns itself, so that it may be chained.
16805 * @codeGenApi
16806 */
16807function ɵɵtextInterpolateV(values) {
16808 const lView = getLView();
16809 const interpolated = interpolationV(lView, values);
16810 if (interpolated !== NO_CHANGE) {
16811 textBindingInternal(lView, getSelectedIndex(), interpolated);
16812 }
16813 return ɵɵtextInterpolateV;
16814}
16815
16816/**
16817 *
16818 * Update an interpolated class on an element with single bound value surrounded by text.
16819 *
16820 * Used when the value passed to a property has 1 interpolated value in it:
16821 *
16822 * ```html
16823 * <div class="prefix{{v0}}suffix"></div>
16824 * ```
16825 *
16826 * Its compiled representation is:
16827 *
16828 * ```ts
16829 * ɵɵclassMapInterpolate1('prefix', v0, 'suffix');
16830 * ```
16831 *
16832 * @param prefix Static value used for concatenation only.
16833 * @param v0 Value checked for change.
16834 * @param suffix Static value used for concatenation only.
16835 * @codeGenApi
16836 */
16837function ɵɵclassMapInterpolate1(prefix, v0, suffix) {
16838 const lView = getLView();
16839 const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
16840 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
16841}
16842/**
16843 *
16844 * Update an interpolated class on an element with 2 bound values surrounded by text.
16845 *
16846 * Used when the value passed to a property has 2 interpolated values in it:
16847 *
16848 * ```html
16849 * <div class="prefix{{v0}}-{{v1}}suffix"></div>
16850 * ```
16851 *
16852 * Its compiled representation is:
16853 *
16854 * ```ts
16855 * ɵɵclassMapInterpolate2('prefix', v0, '-', v1, 'suffix');
16856 * ```
16857 *
16858 * @param prefix Static value used for concatenation only.
16859 * @param v0 Value checked for change.
16860 * @param i0 Static value used for concatenation only.
16861 * @param v1 Value checked for change.
16862 * @param suffix Static value used for concatenation only.
16863 * @codeGenApi
16864 */
16865function ɵɵclassMapInterpolate2(prefix, v0, i0, v1, suffix) {
16866 const lView = getLView();
16867 const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
16868 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
16869}
16870/**
16871 *
16872 * Update an interpolated class on an element with 3 bound values surrounded by text.
16873 *
16874 * Used when the value passed to a property has 3 interpolated values in it:
16875 *
16876 * ```html
16877 * <div class="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
16878 * ```
16879 *
16880 * Its compiled representation is:
16881 *
16882 * ```ts
16883 * ɵɵclassMapInterpolate3(
16884 * 'prefix', v0, '-', v1, '-', v2, 'suffix');
16885 * ```
16886 *
16887 * @param prefix Static value used for concatenation only.
16888 * @param v0 Value checked for change.
16889 * @param i0 Static value used for concatenation only.
16890 * @param v1 Value checked for change.
16891 * @param i1 Static value used for concatenation only.
16892 * @param v2 Value checked for change.
16893 * @param suffix Static value used for concatenation only.
16894 * @codeGenApi
16895 */
16896function ɵɵclassMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
16897 const lView = getLView();
16898 const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
16899 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
16900}
16901/**
16902 *
16903 * Update an interpolated class on an element with 4 bound values surrounded by text.
16904 *
16905 * Used when the value passed to a property has 4 interpolated values in it:
16906 *
16907 * ```html
16908 * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
16909 * ```
16910 *
16911 * Its compiled representation is:
16912 *
16913 * ```ts
16914 * ɵɵclassMapInterpolate4(
16915 * 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
16916 * ```
16917 *
16918 * @param prefix Static value used for concatenation only.
16919 * @param v0 Value checked for change.
16920 * @param i0 Static value used for concatenation only.
16921 * @param v1 Value checked for change.
16922 * @param i1 Static value used for concatenation only.
16923 * @param v2 Value checked for change.
16924 * @param i2 Static value used for concatenation only.
16925 * @param v3 Value checked for change.
16926 * @param suffix Static value used for concatenation only.
16927 * @codeGenApi
16928 */
16929function ɵɵclassMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
16930 const lView = getLView();
16931 const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
16932 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
16933}
16934/**
16935 *
16936 * Update an interpolated class on an element with 5 bound values surrounded by text.
16937 *
16938 * Used when the value passed to a property has 5 interpolated values in it:
16939 *
16940 * ```html
16941 * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
16942 * ```
16943 *
16944 * Its compiled representation is:
16945 *
16946 * ```ts
16947 * ɵɵclassMapInterpolate5(
16948 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
16949 * ```
16950 *
16951 * @param prefix Static value used for concatenation only.
16952 * @param v0 Value checked for change.
16953 * @param i0 Static value used for concatenation only.
16954 * @param v1 Value checked for change.
16955 * @param i1 Static value used for concatenation only.
16956 * @param v2 Value checked for change.
16957 * @param i2 Static value used for concatenation only.
16958 * @param v3 Value checked for change.
16959 * @param i3 Static value used for concatenation only.
16960 * @param v4 Value checked for change.
16961 * @param suffix Static value used for concatenation only.
16962 * @codeGenApi
16963 */
16964function ɵɵclassMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
16965 const lView = getLView();
16966 const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
16967 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
16968}
16969/**
16970 *
16971 * Update an interpolated class on an element with 6 bound values surrounded by text.
16972 *
16973 * Used when the value passed to a property has 6 interpolated values in it:
16974 *
16975 * ```html
16976 * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
16977 * ```
16978 *
16979 * Its compiled representation is:
16980 *
16981 * ```ts
16982 * ɵɵclassMapInterpolate6(
16983 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
16984 * ```
16985 *
16986 * @param prefix Static value used for concatenation only.
16987 * @param v0 Value checked for change.
16988 * @param i0 Static value used for concatenation only.
16989 * @param v1 Value checked for change.
16990 * @param i1 Static value used for concatenation only.
16991 * @param v2 Value checked for change.
16992 * @param i2 Static value used for concatenation only.
16993 * @param v3 Value checked for change.
16994 * @param i3 Static value used for concatenation only.
16995 * @param v4 Value checked for change.
16996 * @param i4 Static value used for concatenation only.
16997 * @param v5 Value checked for change.
16998 * @param suffix Static value used for concatenation only.
16999 * @codeGenApi
17000 */
17001function ɵɵclassMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
17002 const lView = getLView();
17003 const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
17004 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
17005}
17006/**
17007 *
17008 * Update an interpolated class on an element with 7 bound values surrounded by text.
17009 *
17010 * Used when the value passed to a property has 7 interpolated values in it:
17011 *
17012 * ```html
17013 * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
17014 * ```
17015 *
17016 * Its compiled representation is:
17017 *
17018 * ```ts
17019 * ɵɵclassMapInterpolate7(
17020 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
17021 * ```
17022 *
17023 * @param prefix Static value used for concatenation only.
17024 * @param v0 Value checked for change.
17025 * @param i0 Static value used for concatenation only.
17026 * @param v1 Value checked for change.
17027 * @param i1 Static value used for concatenation only.
17028 * @param v2 Value checked for change.
17029 * @param i2 Static value used for concatenation only.
17030 * @param v3 Value checked for change.
17031 * @param i3 Static value used for concatenation only.
17032 * @param v4 Value checked for change.
17033 * @param i4 Static value used for concatenation only.
17034 * @param v5 Value checked for change.
17035 * @param i5 Static value used for concatenation only.
17036 * @param v6 Value checked for change.
17037 * @param suffix Static value used for concatenation only.
17038 * @codeGenApi
17039 */
17040function ɵɵclassMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
17041 const lView = getLView();
17042 const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
17043 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
17044}
17045/**
17046 *
17047 * Update an interpolated class on an element with 8 bound values surrounded by text.
17048 *
17049 * Used when the value passed to a property has 8 interpolated values in it:
17050 *
17051 * ```html
17052 * <div class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
17053 * ```
17054 *
17055 * Its compiled representation is:
17056 *
17057 * ```ts
17058 * ɵɵclassMapInterpolate8(
17059 * 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, 'suffix');
17060 * ```
17061 *
17062 * @param prefix Static value used for concatenation only.
17063 * @param v0 Value checked for change.
17064 * @param i0 Static value used for concatenation only.
17065 * @param v1 Value checked for change.
17066 * @param i1 Static value used for concatenation only.
17067 * @param v2 Value checked for change.
17068 * @param i2 Static value used for concatenation only.
17069 * @param v3 Value checked for change.
17070 * @param i3 Static value used for concatenation only.
17071 * @param v4 Value checked for change.
17072 * @param i4 Static value used for concatenation only.
17073 * @param v5 Value checked for change.
17074 * @param i5 Static value used for concatenation only.
17075 * @param v6 Value checked for change.
17076 * @param i6 Static value used for concatenation only.
17077 * @param v7 Value checked for change.
17078 * @param suffix Static value used for concatenation only.
17079 * @codeGenApi
17080 */
17081function ɵɵclassMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
17082 const lView = getLView();
17083 const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
17084 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
17085}
17086/**
17087 * Update an interpolated class on an element with 9 or more bound values surrounded by text.
17088 *
17089 * Used when the number of interpolated values exceeds 8.
17090 *
17091 * ```html
17092 * <div
17093 * class="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix"></div>
17094 * ```
17095 *
17096 * Its compiled representation is:
17097 *
17098 * ```ts
17099 * ɵɵclassMapInterpolateV(
17100 * ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
17101 * 'suffix']);
17102 * ```
17103 *.
17104 * @param values The collection of values and the strings in-between those values, beginning with
17105 * a string prefix and ending with a string suffix.
17106 * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
17107 * @codeGenApi
17108 */
17109function ɵɵclassMapInterpolateV(values) {
17110 const lView = getLView();
17111 const interpolatedValue = interpolationV(lView, values);
17112 checkStylingMap(keyValueArraySet, classStringParser, interpolatedValue, true);
17113}
17114
17115/**
17116 *
17117 * Update an interpolated style on an element with single bound value surrounded by text.
17118 *
17119 * Used when the value passed to a property has 1 interpolated value in it:
17120 *
17121 * ```html
17122 * <div style="key: {{v0}}suffix"></div>
17123 * ```
17124 *
17125 * Its compiled representation is:
17126 *
17127 * ```ts
17128 * ɵɵstyleMapInterpolate1('key: ', v0, 'suffix');
17129 * ```
17130 *
17131 * @param prefix Static value used for concatenation only.
17132 * @param v0 Value checked for change.
17133 * @param suffix Static value used for concatenation only.
17134 * @codeGenApi
17135 */
17136function ɵɵstyleMapInterpolate1(prefix, v0, suffix) {
17137 const lView = getLView();
17138 const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
17139 ɵɵstyleMap(interpolatedValue);
17140}
17141/**
17142 *
17143 * Update an interpolated style on an element with 2 bound values surrounded by text.
17144 *
17145 * Used when the value passed to a property has 2 interpolated values in it:
17146 *
17147 * ```html
17148 * <div style="key: {{v0}}; key1: {{v1}}suffix"></div>
17149 * ```
17150 *
17151 * Its compiled representation is:
17152 *
17153 * ```ts
17154 * ɵɵstyleMapInterpolate2('key: ', v0, '; key1: ', v1, 'suffix');
17155 * ```
17156 *
17157 * @param prefix Static value used for concatenation only.
17158 * @param v0 Value checked for change.
17159 * @param i0 Static value used for concatenation only.
17160 * @param v1 Value checked for change.
17161 * @param suffix Static value used for concatenation only.
17162 * @codeGenApi
17163 */
17164function ɵɵstyleMapInterpolate2(prefix, v0, i0, v1, suffix) {
17165 const lView = getLView();
17166 const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
17167 ɵɵstyleMap(interpolatedValue);
17168}
17169/**
17170 *
17171 * Update an interpolated style on an element with 3 bound values surrounded by text.
17172 *
17173 * Used when the value passed to a property has 3 interpolated values in it:
17174 *
17175 * ```html
17176 * <div style="key: {{v0}}; key2: {{v1}}; key2: {{v2}}suffix"></div>
17177 * ```
17178 *
17179 * Its compiled representation is:
17180 *
17181 * ```ts
17182 * ɵɵstyleMapInterpolate3(
17183 * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, 'suffix');
17184 * ```
17185 *
17186 * @param prefix Static value used for concatenation only.
17187 * @param v0 Value checked for change.
17188 * @param i0 Static value used for concatenation only.
17189 * @param v1 Value checked for change.
17190 * @param i1 Static value used for concatenation only.
17191 * @param v2 Value checked for change.
17192 * @param suffix Static value used for concatenation only.
17193 * @codeGenApi
17194 */
17195function ɵɵstyleMapInterpolate3(prefix, v0, i0, v1, i1, v2, suffix) {
17196 const lView = getLView();
17197 const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
17198 ɵɵstyleMap(interpolatedValue);
17199}
17200/**
17201 *
17202 * Update an interpolated style on an element with 4 bound values surrounded by text.
17203 *
17204 * Used when the value passed to a property has 4 interpolated values in it:
17205 *
17206 * ```html
17207 * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}suffix"></div>
17208 * ```
17209 *
17210 * Its compiled representation is:
17211 *
17212 * ```ts
17213 * ɵɵstyleMapInterpolate4(
17214 * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, 'suffix');
17215 * ```
17216 *
17217 * @param prefix Static value used for concatenation only.
17218 * @param v0 Value checked for change.
17219 * @param i0 Static value used for concatenation only.
17220 * @param v1 Value checked for change.
17221 * @param i1 Static value used for concatenation only.
17222 * @param v2 Value checked for change.
17223 * @param i2 Static value used for concatenation only.
17224 * @param v3 Value checked for change.
17225 * @param suffix Static value used for concatenation only.
17226 * @codeGenApi
17227 */
17228function ɵɵstyleMapInterpolate4(prefix, v0, i0, v1, i1, v2, i2, v3, suffix) {
17229 const lView = getLView();
17230 const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
17231 ɵɵstyleMap(interpolatedValue);
17232}
17233/**
17234 *
17235 * Update an interpolated style on an element with 5 bound values surrounded by text.
17236 *
17237 * Used when the value passed to a property has 5 interpolated values in it:
17238 *
17239 * ```html
17240 * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}suffix"></div>
17241 * ```
17242 *
17243 * Its compiled representation is:
17244 *
17245 * ```ts
17246 * ɵɵstyleMapInterpolate5(
17247 * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, 'suffix');
17248 * ```
17249 *
17250 * @param prefix Static value used for concatenation only.
17251 * @param v0 Value checked for change.
17252 * @param i0 Static value used for concatenation only.
17253 * @param v1 Value checked for change.
17254 * @param i1 Static value used for concatenation only.
17255 * @param v2 Value checked for change.
17256 * @param i2 Static value used for concatenation only.
17257 * @param v3 Value checked for change.
17258 * @param i3 Static value used for concatenation only.
17259 * @param v4 Value checked for change.
17260 * @param suffix Static value used for concatenation only.
17261 * @codeGenApi
17262 */
17263function ɵɵstyleMapInterpolate5(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix) {
17264 const lView = getLView();
17265 const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
17266 ɵɵstyleMap(interpolatedValue);
17267}
17268/**
17269 *
17270 * Update an interpolated style on an element with 6 bound values surrounded by text.
17271 *
17272 * Used when the value passed to a property has 6 interpolated values in it:
17273 *
17274 * ```html
17275 * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}};
17276 * key5: {{v5}}suffix"></div>
17277 * ```
17278 *
17279 * Its compiled representation is:
17280 *
17281 * ```ts
17282 * ɵɵstyleMapInterpolate6(
17283 * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
17284 * 'suffix');
17285 * ```
17286 *
17287 * @param prefix Static value used for concatenation only.
17288 * @param v0 Value checked for change.
17289 * @param i0 Static value used for concatenation only.
17290 * @param v1 Value checked for change.
17291 * @param i1 Static value used for concatenation only.
17292 * @param v2 Value checked for change.
17293 * @param i2 Static value used for concatenation only.
17294 * @param v3 Value checked for change.
17295 * @param i3 Static value used for concatenation only.
17296 * @param v4 Value checked for change.
17297 * @param i4 Static value used for concatenation only.
17298 * @param v5 Value checked for change.
17299 * @param suffix Static value used for concatenation only.
17300 * @codeGenApi
17301 */
17302function ɵɵstyleMapInterpolate6(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix) {
17303 const lView = getLView();
17304 const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
17305 ɵɵstyleMap(interpolatedValue);
17306}
17307/**
17308 *
17309 * Update an interpolated style on an element with 7 bound values surrounded by text.
17310 *
17311 * Used when the value passed to a property has 7 interpolated values in it:
17312 *
17313 * ```html
17314 * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
17315 * key6: {{v6}}suffix"></div>
17316 * ```
17317 *
17318 * Its compiled representation is:
17319 *
17320 * ```ts
17321 * ɵɵstyleMapInterpolate7(
17322 * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
17323 * '; key6: ', v6, 'suffix');
17324 * ```
17325 *
17326 * @param prefix Static value used for concatenation only.
17327 * @param v0 Value checked for change.
17328 * @param i0 Static value used for concatenation only.
17329 * @param v1 Value checked for change.
17330 * @param i1 Static value used for concatenation only.
17331 * @param v2 Value checked for change.
17332 * @param i2 Static value used for concatenation only.
17333 * @param v3 Value checked for change.
17334 * @param i3 Static value used for concatenation only.
17335 * @param v4 Value checked for change.
17336 * @param i4 Static value used for concatenation only.
17337 * @param v5 Value checked for change.
17338 * @param i5 Static value used for concatenation only.
17339 * @param v6 Value checked for change.
17340 * @param suffix Static value used for concatenation only.
17341 * @codeGenApi
17342 */
17343function ɵɵstyleMapInterpolate7(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix) {
17344 const lView = getLView();
17345 const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
17346 ɵɵstyleMap(interpolatedValue);
17347}
17348/**
17349 *
17350 * Update an interpolated style on an element with 8 bound values surrounded by text.
17351 *
17352 * Used when the value passed to a property has 8 interpolated values in it:
17353 *
17354 * ```html
17355 * <div style="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
17356 * key6: {{v6}}; key7: {{v7}}suffix"></div>
17357 * ```
17358 *
17359 * Its compiled representation is:
17360 *
17361 * ```ts
17362 * ɵɵstyleMapInterpolate8(
17363 * 'key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
17364 * '; key6: ', v6, '; key7: ', v7, 'suffix');
17365 * ```
17366 *
17367 * @param prefix Static value used for concatenation only.
17368 * @param v0 Value checked for change.
17369 * @param i0 Static value used for concatenation only.
17370 * @param v1 Value checked for change.
17371 * @param i1 Static value used for concatenation only.
17372 * @param v2 Value checked for change.
17373 * @param i2 Static value used for concatenation only.
17374 * @param v3 Value checked for change.
17375 * @param i3 Static value used for concatenation only.
17376 * @param v4 Value checked for change.
17377 * @param i4 Static value used for concatenation only.
17378 * @param v5 Value checked for change.
17379 * @param i5 Static value used for concatenation only.
17380 * @param v6 Value checked for change.
17381 * @param i6 Static value used for concatenation only.
17382 * @param v7 Value checked for change.
17383 * @param suffix Static value used for concatenation only.
17384 * @codeGenApi
17385 */
17386function ɵɵstyleMapInterpolate8(prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix) {
17387 const lView = getLView();
17388 const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
17389 ɵɵstyleMap(interpolatedValue);
17390}
17391/**
17392 * Update an interpolated style on an element with 9 or more bound values surrounded by text.
17393 *
17394 * Used when the number of interpolated values exceeds 8.
17395 *
17396 * ```html
17397 * <div
17398 * class="key: {{v0}}; key1: {{v1}}; key2: {{v2}}; key3: {{v3}}; key4: {{v4}}; key5: {{v5}};
17399 * key6: {{v6}}; key7: {{v7}}; key8: {{v8}}; key9: {{v9}}suffix"></div>
17400 * ```
17401 *
17402 * Its compiled representation is:
17403 *
17404 * ```ts
17405 * ɵɵstyleMapInterpolateV(
17406 * ['key: ', v0, '; key1: ', v1, '; key2: ', v2, '; key3: ', v3, '; key4: ', v4, '; key5: ', v5,
17407 * '; key6: ', v6, '; key7: ', v7, '; key8: ', v8, '; key9: ', v9, 'suffix']);
17408 * ```
17409 *.
17410 * @param values The collection of values and the strings in-between those values, beginning with
17411 * a string prefix and ending with a string suffix.
17412 * (e.g. `['prefix', value0, '; key2: ', value1, '; key2: ', value2, ..., value99, 'suffix']`)
17413 * @codeGenApi
17414 */
17415function ɵɵstyleMapInterpolateV(values) {
17416 const lView = getLView();
17417 const interpolatedValue = interpolationV(lView, values);
17418 ɵɵstyleMap(interpolatedValue);
17419}
17420
17421/**
17422 *
17423 * Update an interpolated style property on an element with single bound value surrounded by text.
17424 *
17425 * Used when the value passed to a property has 1 interpolated value in it:
17426 *
17427 * ```html
17428 * <div style.color="prefix{{v0}}suffix"></div>
17429 * ```
17430 *
17431 * Its compiled representation is:
17432 *
17433 * ```ts
17434 * ɵɵstylePropInterpolate1(0, 'prefix', v0, 'suffix');
17435 * ```
17436 *
17437 * @param styleIndex Index of style to update. This index value refers to the
17438 * index of the style in the style bindings array that was passed into
17439 * `styling`.
17440 * @param prefix Static value used for concatenation only.
17441 * @param v0 Value checked for change.
17442 * @param suffix Static value used for concatenation only.
17443 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17444 * @returns itself, so that it may be chained.
17445 * @codeGenApi
17446 */
17447function ɵɵstylePropInterpolate1(prop, prefix, v0, suffix, valueSuffix) {
17448 const lView = getLView();
17449 const interpolatedValue = interpolation1(lView, prefix, v0, suffix);
17450 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17451 return ɵɵstylePropInterpolate1;
17452}
17453/**
17454 *
17455 * Update an interpolated style property on an element with 2 bound values surrounded by text.
17456 *
17457 * Used when the value passed to a property has 2 interpolated values in it:
17458 *
17459 * ```html
17460 * <div style.color="prefix{{v0}}-{{v1}}suffix"></div>
17461 * ```
17462 *
17463 * Its compiled representation is:
17464 *
17465 * ```ts
17466 * ɵɵstylePropInterpolate2(0, 'prefix', v0, '-', v1, 'suffix');
17467 * ```
17468 *
17469 * @param styleIndex Index of style to update. This index value refers to the
17470 * index of the style in the style bindings array that was passed into
17471 * `styling`.
17472 * @param prefix Static value used for concatenation only.
17473 * @param v0 Value checked for change.
17474 * @param i0 Static value used for concatenation only.
17475 * @param v1 Value checked for change.
17476 * @param suffix Static value used for concatenation only.
17477 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17478 * @returns itself, so that it may be chained.
17479 * @codeGenApi
17480 */
17481function ɵɵstylePropInterpolate2(prop, prefix, v0, i0, v1, suffix, valueSuffix) {
17482 const lView = getLView();
17483 const interpolatedValue = interpolation2(lView, prefix, v0, i0, v1, suffix);
17484 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17485 return ɵɵstylePropInterpolate2;
17486}
17487/**
17488 *
17489 * Update an interpolated style property on an element with 3 bound values surrounded by text.
17490 *
17491 * Used when the value passed to a property has 3 interpolated values in it:
17492 *
17493 * ```html
17494 * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}suffix"></div>
17495 * ```
17496 *
17497 * Its compiled representation is:
17498 *
17499 * ```ts
17500 * ɵɵstylePropInterpolate3(0, 'prefix', v0, '-', v1, '-', v2, 'suffix');
17501 * ```
17502 *
17503 * @param styleIndex Index of style to update. This index value refers to the
17504 * index of the style in the style bindings array that was passed into
17505 * `styling`.
17506 * @param prefix Static value used for concatenation only.
17507 * @param v0 Value checked for change.
17508 * @param i0 Static value used for concatenation only.
17509 * @param v1 Value checked for change.
17510 * @param i1 Static value used for concatenation only.
17511 * @param v2 Value checked for change.
17512 * @param suffix Static value used for concatenation only.
17513 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17514 * @returns itself, so that it may be chained.
17515 * @codeGenApi
17516 */
17517function ɵɵstylePropInterpolate3(prop, prefix, v0, i0, v1, i1, v2, suffix, valueSuffix) {
17518 const lView = getLView();
17519 const interpolatedValue = interpolation3(lView, prefix, v0, i0, v1, i1, v2, suffix);
17520 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17521 return ɵɵstylePropInterpolate3;
17522}
17523/**
17524 *
17525 * Update an interpolated style property on an element with 4 bound values surrounded by text.
17526 *
17527 * Used when the value passed to a property has 4 interpolated values in it:
17528 *
17529 * ```html
17530 * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}suffix"></div>
17531 * ```
17532 *
17533 * Its compiled representation is:
17534 *
17535 * ```ts
17536 * ɵɵstylePropInterpolate4(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, 'suffix');
17537 * ```
17538 *
17539 * @param styleIndex Index of style to update. This index value refers to the
17540 * index of the style in the style bindings array that was passed into
17541 * `styling`.
17542 * @param prefix Static value used for concatenation only.
17543 * @param v0 Value checked for change.
17544 * @param i0 Static value used for concatenation only.
17545 * @param v1 Value checked for change.
17546 * @param i1 Static value used for concatenation only.
17547 * @param v2 Value checked for change.
17548 * @param i2 Static value used for concatenation only.
17549 * @param v3 Value checked for change.
17550 * @param suffix Static value used for concatenation only.
17551 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17552 * @returns itself, so that it may be chained.
17553 * @codeGenApi
17554 */
17555function ɵɵstylePropInterpolate4(prop, prefix, v0, i0, v1, i1, v2, i2, v3, suffix, valueSuffix) {
17556 const lView = getLView();
17557 const interpolatedValue = interpolation4(lView, prefix, v0, i0, v1, i1, v2, i2, v3, suffix);
17558 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17559 return ɵɵstylePropInterpolate4;
17560}
17561/**
17562 *
17563 * Update an interpolated style property on an element with 5 bound values surrounded by text.
17564 *
17565 * Used when the value passed to a property has 5 interpolated values in it:
17566 *
17567 * ```html
17568 * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}suffix"></div>
17569 * ```
17570 *
17571 * Its compiled representation is:
17572 *
17573 * ```ts
17574 * ɵɵstylePropInterpolate5(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, 'suffix');
17575 * ```
17576 *
17577 * @param styleIndex Index of style to update. This index value refers to the
17578 * index of the style in the style bindings array that was passed into
17579 * `styling`.
17580 * @param prefix Static value used for concatenation only.
17581 * @param v0 Value checked for change.
17582 * @param i0 Static value used for concatenation only.
17583 * @param v1 Value checked for change.
17584 * @param i1 Static value used for concatenation only.
17585 * @param v2 Value checked for change.
17586 * @param i2 Static value used for concatenation only.
17587 * @param v3 Value checked for change.
17588 * @param i3 Static value used for concatenation only.
17589 * @param v4 Value checked for change.
17590 * @param suffix Static value used for concatenation only.
17591 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17592 * @returns itself, so that it may be chained.
17593 * @codeGenApi
17594 */
17595function ɵɵstylePropInterpolate5(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix, valueSuffix) {
17596 const lView = getLView();
17597 const interpolatedValue = interpolation5(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, suffix);
17598 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17599 return ɵɵstylePropInterpolate5;
17600}
17601/**
17602 *
17603 * Update an interpolated style property on an element with 6 bound values surrounded by text.
17604 *
17605 * Used when the value passed to a property has 6 interpolated values in it:
17606 *
17607 * ```html
17608 * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}suffix"></div>
17609 * ```
17610 *
17611 * Its compiled representation is:
17612 *
17613 * ```ts
17614 * ɵɵstylePropInterpolate6(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, 'suffix');
17615 * ```
17616 *
17617 * @param styleIndex Index of style to update. This index value refers to the
17618 * index of the style in the style bindings array that was passed into
17619 * `styling`.
17620 * @param prefix Static value used for concatenation only.
17621 * @param v0 Value checked for change.
17622 * @param i0 Static value used for concatenation only.
17623 * @param v1 Value checked for change.
17624 * @param i1 Static value used for concatenation only.
17625 * @param v2 Value checked for change.
17626 * @param i2 Static value used for concatenation only.
17627 * @param v3 Value checked for change.
17628 * @param i3 Static value used for concatenation only.
17629 * @param v4 Value checked for change.
17630 * @param i4 Static value used for concatenation only.
17631 * @param v5 Value checked for change.
17632 * @param suffix Static value used for concatenation only.
17633 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17634 * @returns itself, so that it may be chained.
17635 * @codeGenApi
17636 */
17637function ɵɵstylePropInterpolate6(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix, valueSuffix) {
17638 const lView = getLView();
17639 const interpolatedValue = interpolation6(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, suffix);
17640 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17641 return ɵɵstylePropInterpolate6;
17642}
17643/**
17644 *
17645 * Update an interpolated style property on an element with 7 bound values surrounded by text.
17646 *
17647 * Used when the value passed to a property has 7 interpolated values in it:
17648 *
17649 * ```html
17650 * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}suffix"></div>
17651 * ```
17652 *
17653 * Its compiled representation is:
17654 *
17655 * ```ts
17656 * ɵɵstylePropInterpolate7(
17657 * 0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, 'suffix');
17658 * ```
17659 *
17660 * @param styleIndex Index of style to update. This index value refers to the
17661 * index of the style in the style bindings array that was passed into
17662 * `styling`.
17663 * @param prefix Static value used for concatenation only.
17664 * @param v0 Value checked for change.
17665 * @param i0 Static value used for concatenation only.
17666 * @param v1 Value checked for change.
17667 * @param i1 Static value used for concatenation only.
17668 * @param v2 Value checked for change.
17669 * @param i2 Static value used for concatenation only.
17670 * @param v3 Value checked for change.
17671 * @param i3 Static value used for concatenation only.
17672 * @param v4 Value checked for change.
17673 * @param i4 Static value used for concatenation only.
17674 * @param v5 Value checked for change.
17675 * @param i5 Static value used for concatenation only.
17676 * @param v6 Value checked for change.
17677 * @param suffix Static value used for concatenation only.
17678 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17679 * @returns itself, so that it may be chained.
17680 * @codeGenApi
17681 */
17682function ɵɵstylePropInterpolate7(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix, valueSuffix) {
17683 const lView = getLView();
17684 const interpolatedValue = interpolation7(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, suffix);
17685 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17686 return ɵɵstylePropInterpolate7;
17687}
17688/**
17689 *
17690 * Update an interpolated style property on an element with 8 bound values surrounded by text.
17691 *
17692 * Used when the value passed to a property has 8 interpolated values in it:
17693 *
17694 * ```html
17695 * <div style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}suffix"></div>
17696 * ```
17697 *
17698 * Its compiled representation is:
17699 *
17700 * ```ts
17701 * ɵɵstylePropInterpolate8(0, 'prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6,
17702 * '-', v7, 'suffix');
17703 * ```
17704 *
17705 * @param styleIndex Index of style to update. This index value refers to the
17706 * index of the style in the style bindings array that was passed into
17707 * `styling`.
17708 * @param prefix Static value used for concatenation only.
17709 * @param v0 Value checked for change.
17710 * @param i0 Static value used for concatenation only.
17711 * @param v1 Value checked for change.
17712 * @param i1 Static value used for concatenation only.
17713 * @param v2 Value checked for change.
17714 * @param i2 Static value used for concatenation only.
17715 * @param v3 Value checked for change.
17716 * @param i3 Static value used for concatenation only.
17717 * @param v4 Value checked for change.
17718 * @param i4 Static value used for concatenation only.
17719 * @param v5 Value checked for change.
17720 * @param i5 Static value used for concatenation only.
17721 * @param v6 Value checked for change.
17722 * @param i6 Static value used for concatenation only.
17723 * @param v7 Value checked for change.
17724 * @param suffix Static value used for concatenation only.
17725 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17726 * @returns itself, so that it may be chained.
17727 * @codeGenApi
17728 */
17729function ɵɵstylePropInterpolate8(prop, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix, valueSuffix) {
17730 const lView = getLView();
17731 const interpolatedValue = interpolation8(lView, prefix, v0, i0, v1, i1, v2, i2, v3, i3, v4, i4, v5, i5, v6, i6, v7, suffix);
17732 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17733 return ɵɵstylePropInterpolate8;
17734}
17735/**
17736 * Update an interpolated style property on an element with 9 or more bound values surrounded by
17737 * text.
17738 *
17739 * Used when the number of interpolated values exceeds 8.
17740 *
17741 * ```html
17742 * <div
17743 * style.color="prefix{{v0}}-{{v1}}-{{v2}}-{{v3}}-{{v4}}-{{v5}}-{{v6}}-{{v7}}-{{v8}}-{{v9}}suffix">
17744 * </div>
17745 * ```
17746 *
17747 * Its compiled representation is:
17748 *
17749 * ```ts
17750 * ɵɵstylePropInterpolateV(
17751 * 0, ['prefix', v0, '-', v1, '-', v2, '-', v3, '-', v4, '-', v5, '-', v6, '-', v7, '-', v9,
17752 * 'suffix']);
17753 * ```
17754 *
17755 * @param styleIndex Index of style to update. This index value refers to the
17756 * index of the style in the style bindings array that was passed into
17757 * `styling`..
17758 * @param values The collection of values and the strings in-between those values, beginning with
17759 * a string prefix and ending with a string suffix.
17760 * (e.g. `['prefix', value0, '-', value1, '-', value2, ..., value99, 'suffix']`)
17761 * @param valueSuffix Optional suffix. Used with scalar values to add unit such as `px`.
17762 * @returns itself, so that it may be chained.
17763 * @codeGenApi
17764 */
17765function ɵɵstylePropInterpolateV(prop, values, valueSuffix) {
17766 const lView = getLView();
17767 const interpolatedValue = interpolationV(lView, values);
17768 checkStylingProperty(prop, interpolatedValue, valueSuffix, false);
17769 return ɵɵstylePropInterpolateV;
17770}
17771
17772/**
17773 * Update a property on a host element. Only applies to native node properties, not inputs.
17774 *
17775 * Operates on the element selected by index via the {@link select} instruction.
17776 *
17777 * @param propName Name of property. Because it is going to DOM, this is not subject to
17778 * renaming as part of minification.
17779 * @param value New value to write.
17780 * @param sanitizer An optional function used to sanitize the value.
17781 * @returns This function returns itself so that it may be chained
17782 * (e.g. `property('name', ctx.name)('title', ctx.title)`)
17783 *
17784 * @codeGenApi
17785 */
17786function ɵɵhostProperty(propName, value, sanitizer) {
17787 const lView = getLView();
17788 const bindingIndex = nextBindingIndex();
17789 if (bindingUpdated(lView, bindingIndex, value)) {
17790 const tView = getTView();
17791 const tNode = getSelectedTNode();
17792 elementPropertyInternal(tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, true);
17793 ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
17794 }
17795 return ɵɵhostProperty;
17796}
17797/**
17798 * Updates a synthetic host binding (e.g. `[@foo]`) on a component or directive.
17799 *
17800 * This instruction is for compatibility purposes and is designed to ensure that a
17801 * synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
17802 * the component's renderer. Normally all host bindings are evaluated with the parent
17803 * component's renderer, but, in the case of animation @triggers, they need to be
17804 * evaluated with the sub component's renderer (because that's where the animation
17805 * triggers are defined).
17806 *
17807 * Do not use this instruction as a replacement for `elementProperty`. This instruction
17808 * only exists to ensure compatibility with the ViewEngine's host binding behavior.
17809 *
17810 * @param index The index of the element to update in the data array
17811 * @param propName Name of property. Because it is going to DOM, this is not subject to
17812 * renaming as part of minification.
17813 * @param value New value to write.
17814 * @param sanitizer An optional function used to sanitize the value.
17815 *
17816 * @codeGenApi
17817 */
17818function ɵɵsyntheticHostProperty(propName, value, sanitizer) {
17819 const lView = getLView();
17820 const bindingIndex = nextBindingIndex();
17821 if (bindingUpdated(lView, bindingIndex, value)) {
17822 const tView = getTView();
17823 const tNode = getSelectedTNode();
17824 const currentDef = getCurrentDirectiveDef(tView.data);
17825 const renderer = loadComponentRenderer(currentDef, tNode, lView);
17826 elementPropertyInternal(tView, tNode, lView, propName, value, renderer, sanitizer, true);
17827 ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
17828 }
17829 return ɵɵsyntheticHostProperty;
17830}
17831
17832/**
17833 * NOTE: changes to the `ngI18nClosureMode` name must be synced with `compiler-cli/src/tooling.ts`.
17834 */
17835if (typeof ngI18nClosureMode === 'undefined') {
17836 // These property accesses can be ignored because ngI18nClosureMode will be set to false
17837 // when optimizing code and the whole if statement will be dropped.
17838 // Make sure to refer to ngI18nClosureMode as ['ngI18nClosureMode'] for closure.
17839 // NOTE: we need to have it in IIFE so that the tree-shaker is happy.
17840 (function () {
17841 // tslint:disable-next-line:no-toplevel-property-access
17842 _global$1['ngI18nClosureMode'] =
17843 // TODO(FW-1250): validate that this actually, you know, works.
17844 // tslint:disable-next-line:no-toplevel-property-access
17845 typeof goog !== 'undefined' && typeof goog.getMsg === 'function';
17846 })();
17847}
17848
17849// THIS CODE IS GENERATED - DO NOT MODIFY.
17850const u = undefined;
17851function plural(val) {
17852 const n = val, i = Math.floor(Math.abs(val)), v = val.toString().replace(/^[^.]*\.?/, '').length;
17853 if (i === 1 && v === 0)
17854 return 1;
17855 return 5;
17856}
17857var 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];
17858
17859/**
17860 * This const is used to store the locale data registered with `registerLocaleData`
17861 */
17862let LOCALE_DATA = {};
17863/**
17864 * Register locale data to be used internally by Angular. See the
17865 * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale
17866 * data.
17867 *
17868 * The signature `registerLocaleData(data: any, extraData?: any)` is deprecated since v5.1
17869 */
17870function registerLocaleData(data, localeId, extraData) {
17871 if (typeof localeId !== 'string') {
17872 extraData = localeId;
17873 localeId = data[LocaleDataIndex.LocaleId];
17874 }
17875 localeId = localeId.toLowerCase().replace(/_/g, '-');
17876 LOCALE_DATA[localeId] = data;
17877 if (extraData) {
17878 LOCALE_DATA[localeId][LocaleDataIndex.ExtraData] = extraData;
17879 }
17880}
17881/**
17882 * Finds the locale data for a given locale.
17883 *
17884 * @param locale The locale code.
17885 * @returns The locale data.
17886 * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
17887 */
17888function findLocaleData(locale) {
17889 const normalizedLocale = normalizeLocale(locale);
17890 let match = getLocaleData(normalizedLocale);
17891 if (match) {
17892 return match;
17893 }
17894 // let's try to find a parent locale
17895 const parentLocale = normalizedLocale.split('-')[0];
17896 match = getLocaleData(parentLocale);
17897 if (match) {
17898 return match;
17899 }
17900 if (parentLocale === 'en') {
17901 return localeEn;
17902 }
17903 throw new RuntimeError(701 /* RuntimeErrorCode.MISSING_LOCALE_DATA */, ngDevMode && `Missing locale data for the locale "${locale}".`);
17904}
17905/**
17906 * Retrieves the default currency code for the given locale.
17907 *
17908 * The default is defined as the first currency which is still in use.
17909 *
17910 * @param locale The code of the locale whose currency code we want.
17911 * @returns The code of the default currency for the given locale.
17912 *
17913 */
17914function getLocaleCurrencyCode(locale) {
17915 const data = findLocaleData(locale);
17916 return data[LocaleDataIndex.CurrencyCode] || null;
17917}
17918/**
17919 * Retrieves the plural function used by ICU expressions to determine the plural case to use
17920 * for a given locale.
17921 * @param locale A locale code for the locale format rules to use.
17922 * @returns The plural function for the locale.
17923 * @see `NgPlural`
17924 * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
17925 */
17926function getLocalePluralCase(locale) {
17927 const data = findLocaleData(locale);
17928 return data[LocaleDataIndex.PluralCase];
17929}
17930/**
17931 * Helper function to get the given `normalizedLocale` from `LOCALE_DATA`
17932 * or from the global `ng.common.locale`.
17933 */
17934function getLocaleData(normalizedLocale) {
17935 if (!(normalizedLocale in LOCALE_DATA)) {
17936 LOCALE_DATA[normalizedLocale] = _global$1.ng && _global$1.ng.common && _global$1.ng.common.locales &&
17937 _global$1.ng.common.locales[normalizedLocale];
17938 }
17939 return LOCALE_DATA[normalizedLocale];
17940}
17941/**
17942 * Helper function to remove all the locale data from `LOCALE_DATA`.
17943 */
17944function unregisterAllLocaleData() {
17945 LOCALE_DATA = {};
17946}
17947/**
17948 * Index of each type of locale data from the locale data array
17949 */
17950var LocaleDataIndex;
17951(function (LocaleDataIndex) {
17952 LocaleDataIndex[LocaleDataIndex["LocaleId"] = 0] = "LocaleId";
17953 LocaleDataIndex[LocaleDataIndex["DayPeriodsFormat"] = 1] = "DayPeriodsFormat";
17954 LocaleDataIndex[LocaleDataIndex["DayPeriodsStandalone"] = 2] = "DayPeriodsStandalone";
17955 LocaleDataIndex[LocaleDataIndex["DaysFormat"] = 3] = "DaysFormat";
17956 LocaleDataIndex[LocaleDataIndex["DaysStandalone"] = 4] = "DaysStandalone";
17957 LocaleDataIndex[LocaleDataIndex["MonthsFormat"] = 5] = "MonthsFormat";
17958 LocaleDataIndex[LocaleDataIndex["MonthsStandalone"] = 6] = "MonthsStandalone";
17959 LocaleDataIndex[LocaleDataIndex["Eras"] = 7] = "Eras";
17960 LocaleDataIndex[LocaleDataIndex["FirstDayOfWeek"] = 8] = "FirstDayOfWeek";
17961 LocaleDataIndex[LocaleDataIndex["WeekendRange"] = 9] = "WeekendRange";
17962 LocaleDataIndex[LocaleDataIndex["DateFormat"] = 10] = "DateFormat";
17963 LocaleDataIndex[LocaleDataIndex["TimeFormat"] = 11] = "TimeFormat";
17964 LocaleDataIndex[LocaleDataIndex["DateTimeFormat"] = 12] = "DateTimeFormat";
17965 LocaleDataIndex[LocaleDataIndex["NumberSymbols"] = 13] = "NumberSymbols";
17966 LocaleDataIndex[LocaleDataIndex["NumberFormats"] = 14] = "NumberFormats";
17967 LocaleDataIndex[LocaleDataIndex["CurrencyCode"] = 15] = "CurrencyCode";
17968 LocaleDataIndex[LocaleDataIndex["CurrencySymbol"] = 16] = "CurrencySymbol";
17969 LocaleDataIndex[LocaleDataIndex["CurrencyName"] = 17] = "CurrencyName";
17970 LocaleDataIndex[LocaleDataIndex["Currencies"] = 18] = "Currencies";
17971 LocaleDataIndex[LocaleDataIndex["Directionality"] = 19] = "Directionality";
17972 LocaleDataIndex[LocaleDataIndex["PluralCase"] = 20] = "PluralCase";
17973 LocaleDataIndex[LocaleDataIndex["ExtraData"] = 21] = "ExtraData";
17974})(LocaleDataIndex || (LocaleDataIndex = {}));
17975/**
17976 * Returns the canonical form of a locale name - lowercase with `_` replaced with `-`.
17977 */
17978function normalizeLocale(locale) {
17979 return locale.toLowerCase().replace(/_/g, '-');
17980}
17981
17982const pluralMapping = ['zero', 'one', 'two', 'few', 'many'];
17983/**
17984 * Returns the plural case based on the locale
17985 */
17986function getPluralCase(value, locale) {
17987 const plural = getLocalePluralCase(locale)(parseInt(value, 10));
17988 const result = pluralMapping[plural];
17989 return (result !== undefined) ? result : 'other';
17990}
17991/**
17992 * The locale id that the application is using by default (for translations and ICU expressions).
17993 */
17994const DEFAULT_LOCALE_ID = 'en-US';
17995/**
17996 * USD currency code that the application uses by default for CurrencyPipe when no
17997 * DEFAULT_CURRENCY_CODE is provided.
17998 */
17999const USD_CURRENCY_CODE = 'USD';
18000
18001/**
18002 * Marks that the next string is an element name.
18003 *
18004 * See `I18nMutateOpCodes` documentation.
18005 */
18006const ELEMENT_MARKER = {
18007 marker: 'element'
18008};
18009/**
18010 * Marks that the next string is comment text need for ICU.
18011 *
18012 * See `I18nMutateOpCodes` documentation.
18013 */
18014const ICU_MARKER = {
18015 marker: 'ICU'
18016};
18017/**
18018 * See `I18nCreateOpCodes`
18019 */
18020var I18nCreateOpCode;
18021(function (I18nCreateOpCode) {
18022 /**
18023 * Number of bits to shift index so that it can be combined with the `APPEND_EAGERLY` and
18024 * `COMMENT`.
18025 */
18026 I18nCreateOpCode[I18nCreateOpCode["SHIFT"] = 2] = "SHIFT";
18027 /**
18028 * Should the node be appended to parent immediately after creation.
18029 */
18030 I18nCreateOpCode[I18nCreateOpCode["APPEND_EAGERLY"] = 1] = "APPEND_EAGERLY";
18031 /**
18032 * If set the node should be comment (rather than a text) node.
18033 */
18034 I18nCreateOpCode[I18nCreateOpCode["COMMENT"] = 2] = "COMMENT";
18035})(I18nCreateOpCode || (I18nCreateOpCode = {}));
18036// Note: This hack is necessary so we don't erroneously get a circular dependency
18037// failure based on types.
18038const unusedValueExportToPlacateAjd = 1;
18039
18040/**
18041 * The locale id that the application is currently using (for translations and ICU expressions).
18042 * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
18043 * but is now defined as a global value.
18044 */
18045let LOCALE_ID = DEFAULT_LOCALE_ID;
18046/**
18047 * Sets the locale id that will be used for translations and ICU expressions.
18048 * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
18049 * but is now defined as a global value.
18050 *
18051 * @param localeId
18052 */
18053function setLocaleId(localeId) {
18054 assertDefined(localeId, `Expected localeId to be defined`);
18055 if (typeof localeId === 'string') {
18056 LOCALE_ID = localeId.toLowerCase().replace(/_/g, '-');
18057 }
18058}
18059/**
18060 * Gets the locale id that will be used for translations and ICU expressions.
18061 * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
18062 * but is now defined as a global value.
18063 */
18064function getLocaleId() {
18065 return LOCALE_ID;
18066}
18067
18068/**
18069 * Find a node in front of which `currentTNode` should be inserted (takes i18n into account).
18070 *
18071 * This method determines the `RNode` in front of which we should insert the `currentRNode`. This
18072 * takes `TNode.insertBeforeIndex` into account.
18073 *
18074 * @param parentTNode parent `TNode`
18075 * @param currentTNode current `TNode` (The node which we would like to insert into the DOM)
18076 * @param lView current `LView`
18077 */
18078function getInsertInFrontOfRNodeWithI18n(parentTNode, currentTNode, lView) {
18079 const tNodeInsertBeforeIndex = currentTNode.insertBeforeIndex;
18080 const insertBeforeIndex = Array.isArray(tNodeInsertBeforeIndex) ? tNodeInsertBeforeIndex[0] : tNodeInsertBeforeIndex;
18081 if (insertBeforeIndex === null) {
18082 return getInsertInFrontOfRNodeWithNoI18n(parentTNode, currentTNode, lView);
18083 }
18084 else {
18085 ngDevMode && assertIndexInRange(lView, insertBeforeIndex);
18086 return unwrapRNode(lView[insertBeforeIndex]);
18087 }
18088}
18089/**
18090 * Process `TNode.insertBeforeIndex` by adding i18n text nodes.
18091 *
18092 * See `TNode.insertBeforeIndex`
18093 */
18094function processI18nInsertBefore(renderer, childTNode, lView, childRNode, parentRElement) {
18095 const tNodeInsertBeforeIndex = childTNode.insertBeforeIndex;
18096 if (Array.isArray(tNodeInsertBeforeIndex)) {
18097 // An array indicates that there are i18n nodes that need to be added as children of this
18098 // `childRNode`. These i18n nodes were created before this `childRNode` was available and so
18099 // only now can be added. The first element of the array is the normal index where we should
18100 // insert the `childRNode`. Additional elements are the extra nodes to be added as children of
18101 // `childRNode`.
18102 ngDevMode && assertDomNode(childRNode);
18103 let i18nParent = childRNode;
18104 let anchorRNode = null;
18105 if (!(childTNode.type & 3 /* TNodeType.AnyRNode */)) {
18106 anchorRNode = i18nParent;
18107 i18nParent = parentRElement;
18108 }
18109 if (i18nParent !== null && childTNode.componentOffset === -1) {
18110 for (let i = 1; i < tNodeInsertBeforeIndex.length; i++) {
18111 // No need to `unwrapRNode` because all of the indexes point to i18n text nodes.
18112 // see `assertDomNode` below.
18113 const i18nChild = lView[tNodeInsertBeforeIndex[i]];
18114 nativeInsertBefore(renderer, i18nParent, i18nChild, anchorRNode, false);
18115 }
18116 }
18117 }
18118}
18119
18120/**
18121 * Add `tNode` to `previousTNodes` list and update relevant `TNode`s in `previousTNodes` list
18122 * `tNode.insertBeforeIndex`.
18123 *
18124 * Things to keep in mind:
18125 * 1. All i18n text nodes are encoded as `TNodeType.Element` and are created eagerly by the
18126 * `ɵɵi18nStart` instruction.
18127 * 2. All `TNodeType.Placeholder` `TNodes` are elements which will be created later by
18128 * `ɵɵelementStart` instruction.
18129 * 3. `ɵɵelementStart` instruction will create `TNode`s in the ascending `TNode.index` order. (So a
18130 * smaller index `TNode` is guaranteed to be created before a larger one)
18131 *
18132 * We use the above three invariants to determine `TNode.insertBeforeIndex`.
18133 *
18134 * In an ideal world `TNode.insertBeforeIndex` would always be `TNode.next.index`. However,
18135 * this will not work because `TNode.next.index` may be larger than `TNode.index` which means that
18136 * the next node is not yet created and therefore we can't insert in front of it.
18137 *
18138 * Rule1: `TNode.insertBeforeIndex = null` if `TNode.next === null` (Initial condition, as we don't
18139 * know if there will be further `TNode`s inserted after.)
18140 * Rule2: If `previousTNode` is created after the `tNode` being inserted, then
18141 * `previousTNode.insertBeforeNode = tNode.index` (So when a new `tNode` is added we check
18142 * previous to see if we can update its `insertBeforeTNode`)
18143 *
18144 * See `TNode.insertBeforeIndex` for more context.
18145 *
18146 * @param previousTNodes A list of previous TNodes so that we can easily traverse `TNode`s in
18147 * reverse order. (If `TNode` would have `previous` this would not be necessary.)
18148 * @param newTNode A TNode to add to the `previousTNodes` list.
18149 */
18150function addTNodeAndUpdateInsertBeforeIndex(previousTNodes, newTNode) {
18151 // Start with Rule1
18152 ngDevMode &&
18153 assertEqual(newTNode.insertBeforeIndex, null, 'We expect that insertBeforeIndex is not set');
18154 previousTNodes.push(newTNode);
18155 if (previousTNodes.length > 1) {
18156 for (let i = previousTNodes.length - 2; i >= 0; i--) {
18157 const existingTNode = previousTNodes[i];
18158 // Text nodes are created eagerly and so they don't need their `indexBeforeIndex` updated.
18159 // It is safe to ignore them.
18160 if (!isI18nText(existingTNode)) {
18161 if (isNewTNodeCreatedBefore(existingTNode, newTNode) &&
18162 getInsertBeforeIndex(existingTNode) === null) {
18163 // If it was created before us in time, (and it does not yet have `insertBeforeIndex`)
18164 // then add the `insertBeforeIndex`.
18165 setInsertBeforeIndex(existingTNode, newTNode.index);
18166 }
18167 }
18168 }
18169 }
18170}
18171function isI18nText(tNode) {
18172 return !(tNode.type & 64 /* TNodeType.Placeholder */);
18173}
18174function isNewTNodeCreatedBefore(existingTNode, newTNode) {
18175 return isI18nText(newTNode) || existingTNode.index > newTNode.index;
18176}
18177function getInsertBeforeIndex(tNode) {
18178 const index = tNode.insertBeforeIndex;
18179 return Array.isArray(index) ? index[0] : index;
18180}
18181function setInsertBeforeIndex(tNode, value) {
18182 const index = tNode.insertBeforeIndex;
18183 if (Array.isArray(index)) {
18184 // Array is stored if we have to insert child nodes. See `TNode.insertBeforeIndex`
18185 index[0] = value;
18186 }
18187 else {
18188 setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
18189 tNode.insertBeforeIndex = value;
18190 }
18191}
18192
18193/**
18194 * Retrieve `TIcu` at a given `index`.
18195 *
18196 * The `TIcu` can be stored either directly (if it is nested ICU) OR
18197 * it is stored inside tho `TIcuContainer` if it is top level ICU.
18198 *
18199 * The reason for this is that the top level ICU need a `TNode` so that they are part of the render
18200 * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
18201 * expressed (parent ICU may have selected a case which does not contain it.)
18202 *
18203 * @param tView Current `TView`.
18204 * @param index Index where the value should be read from.
18205 */
18206function getTIcu(tView, index) {
18207 const value = tView.data[index];
18208 if (value === null || typeof value === 'string')
18209 return null;
18210 if (ngDevMode &&
18211 !(value.hasOwnProperty('tViews') || value.hasOwnProperty('currentCaseLViewIndex'))) {
18212 throwError('We expect to get \'null\'|\'TIcu\'|\'TIcuContainer\', but got: ' + value);
18213 }
18214 // Here the `value.hasOwnProperty('currentCaseLViewIndex')` is a polymorphic read as it can be
18215 // either TIcu or TIcuContainerNode. This is not ideal, but we still think it is OK because it
18216 // will be just two cases which fits into the browser inline cache (inline cache can take up to
18217 // 4)
18218 const tIcu = value.hasOwnProperty('currentCaseLViewIndex') ? value :
18219 value.value;
18220 ngDevMode && assertTIcu(tIcu);
18221 return tIcu;
18222}
18223/**
18224 * Store `TIcu` at a give `index`.
18225 *
18226 * The `TIcu` can be stored either directly (if it is nested ICU) OR
18227 * it is stored inside tho `TIcuContainer` if it is top level ICU.
18228 *
18229 * The reason for this is that the top level ICU need a `TNode` so that they are part of the render
18230 * tree, but nested ICU's have no TNode, because we don't know ahead of time if the nested ICU is
18231 * expressed (parent ICU may have selected a case which does not contain it.)
18232 *
18233 * @param tView Current `TView`.
18234 * @param index Index where the value should be stored at in `Tview.data`
18235 * @param tIcu The TIcu to store.
18236 */
18237function setTIcu(tView, index, tIcu) {
18238 const tNode = tView.data[index];
18239 ngDevMode &&
18240 assertEqual(tNode === null || tNode.hasOwnProperty('tViews'), true, 'We expect to get \'null\'|\'TIcuContainer\'');
18241 if (tNode === null) {
18242 tView.data[index] = tIcu;
18243 }
18244 else {
18245 ngDevMode && assertTNodeType(tNode, 32 /* TNodeType.Icu */);
18246 tNode.value = tIcu;
18247 }
18248}
18249/**
18250 * Set `TNode.insertBeforeIndex` taking the `Array` into account.
18251 *
18252 * See `TNode.insertBeforeIndex`
18253 */
18254function setTNodeInsertBeforeIndex(tNode, index) {
18255 ngDevMode && assertTNode(tNode);
18256 let insertBeforeIndex = tNode.insertBeforeIndex;
18257 if (insertBeforeIndex === null) {
18258 setI18nHandling(getInsertInFrontOfRNodeWithI18n, processI18nInsertBefore);
18259 insertBeforeIndex = tNode.insertBeforeIndex =
18260 [null /* may be updated to number later */, index];
18261 }
18262 else {
18263 assertEqual(Array.isArray(insertBeforeIndex), true, 'Expecting array here');
18264 insertBeforeIndex.push(index);
18265 }
18266}
18267/**
18268 * Create `TNode.type=TNodeType.Placeholder` node.
18269 *
18270 * See `TNodeType.Placeholder` for more information.
18271 */
18272function createTNodePlaceholder(tView, previousTNodes, index) {
18273 const tNode = createTNodeAtIndex(tView, index, 64 /* TNodeType.Placeholder */, null, null);
18274 addTNodeAndUpdateInsertBeforeIndex(previousTNodes, tNode);
18275 return tNode;
18276}
18277/**
18278 * Returns current ICU case.
18279 *
18280 * ICU cases are stored as index into the `TIcu.cases`.
18281 * At times it is necessary to communicate that the ICU case just switched and that next ICU update
18282 * should update all bindings regardless of the mask. In such a case the we store negative numbers
18283 * for cases which have just been switched. This function removes the negative flag.
18284 */
18285function getCurrentICUCaseIndex(tIcu, lView) {
18286 const currentCase = lView[tIcu.currentCaseLViewIndex];
18287 return currentCase === null ? currentCase : (currentCase < 0 ? ~currentCase : currentCase);
18288}
18289function getParentFromIcuCreateOpCode(mergedCode) {
18290 return mergedCode >>> 17 /* IcuCreateOpCode.SHIFT_PARENT */;
18291}
18292function getRefFromIcuCreateOpCode(mergedCode) {
18293 return (mergedCode & 131070 /* IcuCreateOpCode.MASK_REF */) >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
18294}
18295function getInstructionFromIcuCreateOpCode(mergedCode) {
18296 return mergedCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */;
18297}
18298function icuCreateOpCode(opCode, parentIdx, refIdx) {
18299 ngDevMode && assertGreaterThanOrEqual(parentIdx, 0, 'Missing parent index');
18300 ngDevMode && assertGreaterThan(refIdx, 0, 'Missing ref index');
18301 return opCode | parentIdx << 17 /* IcuCreateOpCode.SHIFT_PARENT */ | refIdx << 1 /* IcuCreateOpCode.SHIFT_REF */;
18302}
18303
18304/**
18305 * Keep track of which input bindings in `ɵɵi18nExp` have changed.
18306 *
18307 * This is used to efficiently update expressions in i18n only when the corresponding input has
18308 * changed.
18309 *
18310 * 1) Each bit represents which of the `ɵɵi18nExp` has changed.
18311 * 2) There are 32 bits allowed in JS.
18312 * 3) Bit 32 is special as it is shared for all changes past 32. (In other words if you have more
18313 * than 32 `ɵɵi18nExp` then all changes past 32nd `ɵɵi18nExp` will be mapped to same bit. This means
18314 * that we may end up changing more than we need to. But i18n expressions with 32 bindings is rare
18315 * so in practice it should not be an issue.)
18316 */
18317let changeMask = 0b0;
18318/**
18319 * Keeps track of which bit needs to be updated in `changeMask`
18320 *
18321 * This value gets incremented on every call to `ɵɵi18nExp`
18322 */
18323let changeMaskCounter = 0;
18324/**
18325 * Keep track of which input bindings in `ɵɵi18nExp` have changed.
18326 *
18327 * `setMaskBit` gets invoked by each call to `ɵɵi18nExp`.
18328 *
18329 * @param hasChange did `ɵɵi18nExp` detect a change.
18330 */
18331function setMaskBit(hasChange) {
18332 if (hasChange) {
18333 changeMask = changeMask | (1 << Math.min(changeMaskCounter, 31));
18334 }
18335 changeMaskCounter++;
18336}
18337function applyI18n(tView, lView, index) {
18338 if (changeMaskCounter > 0) {
18339 ngDevMode && assertDefined(tView, `tView should be defined`);
18340 const tI18n = tView.data[index];
18341 // When `index` points to an `ɵɵi18nAttributes` then we have an array otherwise `TI18n`
18342 const updateOpCodes = Array.isArray(tI18n) ? tI18n : tI18n.update;
18343 const bindingsStartIndex = getBindingIndex() - changeMaskCounter - 1;
18344 applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask);
18345 }
18346 // Reset changeMask & maskBit to default for the next update cycle
18347 changeMask = 0b0;
18348 changeMaskCounter = 0;
18349}
18350/**
18351 * Apply `I18nCreateOpCodes` op-codes as stored in `TI18n.create`.
18352 *
18353 * Creates text (and comment) nodes which are internationalized.
18354 *
18355 * @param lView Current lView
18356 * @param createOpCodes Set of op-codes to apply
18357 * @param parentRNode Parent node (so that direct children can be added eagerly) or `null` if it is
18358 * a root node.
18359 * @param insertInFrontOf DOM node that should be used as an anchor.
18360 */
18361function applyCreateOpCodes(lView, createOpCodes, parentRNode, insertInFrontOf) {
18362 const renderer = lView[RENDERER];
18363 for (let i = 0; i < createOpCodes.length; i++) {
18364 const opCode = createOpCodes[i++];
18365 const text = createOpCodes[i];
18366 const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT;
18367 const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY;
18368 const index = opCode >>> I18nCreateOpCode.SHIFT;
18369 let rNode = lView[index];
18370 if (rNode === null) {
18371 // We only create new DOM nodes if they don't already exist: If ICU switches case back to a
18372 // case which was already instantiated, no need to create new DOM nodes.
18373 rNode = lView[index] =
18374 isComment ? renderer.createComment(text) : createTextNode(renderer, text);
18375 }
18376 if (appendNow && parentRNode !== null) {
18377 nativeInsertBefore(renderer, parentRNode, rNode, insertInFrontOf, false);
18378 }
18379 }
18380}
18381/**
18382 * Apply `I18nMutateOpCodes` OpCodes.
18383 *
18384 * @param tView Current `TView`
18385 * @param mutableOpCodes Mutable OpCodes to process
18386 * @param lView Current `LView`
18387 * @param anchorRNode place where the i18n node should be inserted.
18388 */
18389function applyMutableOpCodes(tView, mutableOpCodes, lView, anchorRNode) {
18390 ngDevMode && assertDomNode(anchorRNode);
18391 const renderer = lView[RENDERER];
18392 // `rootIdx` represents the node into which all inserts happen.
18393 let rootIdx = null;
18394 // `rootRNode` represents the real node into which we insert. This can be different from
18395 // `lView[rootIdx]` if we have projection.
18396 // - null we don't have a parent (as can be the case in when we are inserting into a root of
18397 // LView which has no parent.)
18398 // - `RElement` The element representing the root after taking projection into account.
18399 let rootRNode;
18400 for (let i = 0; i < mutableOpCodes.length; i++) {
18401 const opCode = mutableOpCodes[i];
18402 if (typeof opCode == 'string') {
18403 const textNodeIndex = mutableOpCodes[++i];
18404 if (lView[textNodeIndex] === null) {
18405 ngDevMode && ngDevMode.rendererCreateTextNode++;
18406 ngDevMode && assertIndexInRange(lView, textNodeIndex);
18407 lView[textNodeIndex] = createTextNode(renderer, opCode);
18408 }
18409 }
18410 else if (typeof opCode == 'number') {
18411 switch (opCode & 1 /* IcuCreateOpCode.MASK_INSTRUCTION */) {
18412 case 0 /* IcuCreateOpCode.AppendChild */:
18413 const parentIdx = getParentFromIcuCreateOpCode(opCode);
18414 if (rootIdx === null) {
18415 // The first operation should save the `rootIdx` because the first operation
18416 // must insert into the root. (Only subsequent operations can insert into a dynamic
18417 // parent)
18418 rootIdx = parentIdx;
18419 rootRNode = nativeParentNode(renderer, anchorRNode);
18420 }
18421 let insertInFrontOf;
18422 let parentRNode;
18423 if (parentIdx === rootIdx) {
18424 insertInFrontOf = anchorRNode;
18425 parentRNode = rootRNode;
18426 }
18427 else {
18428 insertInFrontOf = null;
18429 parentRNode = unwrapRNode(lView[parentIdx]);
18430 }
18431 // FIXME(misko): Refactor with `processI18nText`
18432 if (parentRNode !== null) {
18433 // This can happen if the `LView` we are adding to is not attached to a parent `LView`.
18434 // In such a case there is no "root" we can attach to. This is fine, as we still need to
18435 // create the elements. When the `LView` gets later added to a parent these "root" nodes
18436 // get picked up and added.
18437 ngDevMode && assertDomNode(parentRNode);
18438 const refIdx = getRefFromIcuCreateOpCode(opCode);
18439 ngDevMode && assertGreaterThan(refIdx, HEADER_OFFSET, 'Missing ref');
18440 // `unwrapRNode` is not needed here as all of these point to RNodes as part of the i18n
18441 // which can't have components.
18442 const child = lView[refIdx];
18443 ngDevMode && assertDomNode(child);
18444 nativeInsertBefore(renderer, parentRNode, child, insertInFrontOf, false);
18445 const tIcu = getTIcu(tView, refIdx);
18446 if (tIcu !== null && typeof tIcu === 'object') {
18447 // If we just added a comment node which has ICU then that ICU may have already been
18448 // rendered and therefore we need to re-add it here.
18449 ngDevMode && assertTIcu(tIcu);
18450 const caseIndex = getCurrentICUCaseIndex(tIcu, lView);
18451 if (caseIndex !== null) {
18452 applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, lView[tIcu.anchorIdx]);
18453 }
18454 }
18455 }
18456 break;
18457 case 1 /* IcuCreateOpCode.Attr */:
18458 const elementNodeIndex = opCode >>> 1 /* IcuCreateOpCode.SHIFT_REF */;
18459 const attrName = mutableOpCodes[++i];
18460 const attrValue = mutableOpCodes[++i];
18461 // This code is used for ICU expressions only, since we don't support
18462 // directives/components in ICUs, we don't need to worry about inputs here
18463 setElementAttribute(renderer, getNativeByIndex(elementNodeIndex, lView), null, null, attrName, attrValue, null);
18464 break;
18465 default:
18466 if (ngDevMode) {
18467 throw new RuntimeError(700 /* RuntimeErrorCode.INVALID_I18N_STRUCTURE */, `Unable to determine the type of mutate operation for "${opCode}"`);
18468 }
18469 }
18470 }
18471 else {
18472 switch (opCode) {
18473 case ICU_MARKER:
18474 const commentValue = mutableOpCodes[++i];
18475 const commentNodeIndex = mutableOpCodes[++i];
18476 if (lView[commentNodeIndex] === null) {
18477 ngDevMode &&
18478 assertEqual(typeof commentValue, 'string', `Expected "${commentValue}" to be a comment node value`);
18479 ngDevMode && ngDevMode.rendererCreateComment++;
18480 ngDevMode && assertIndexInExpandoRange(lView, commentNodeIndex);
18481 const commentRNode = lView[commentNodeIndex] =
18482 createCommentNode(renderer, commentValue);
18483 // FIXME(misko): Attaching patch data is only needed for the root (Also add tests)
18484 attachPatchData(commentRNode, lView);
18485 }
18486 break;
18487 case ELEMENT_MARKER:
18488 const tagName = mutableOpCodes[++i];
18489 const elementNodeIndex = mutableOpCodes[++i];
18490 if (lView[elementNodeIndex] === null) {
18491 ngDevMode &&
18492 assertEqual(typeof tagName, 'string', `Expected "${tagName}" to be an element node tag name`);
18493 ngDevMode && ngDevMode.rendererCreateElement++;
18494 ngDevMode && assertIndexInExpandoRange(lView, elementNodeIndex);
18495 const elementRNode = lView[elementNodeIndex] =
18496 createElementNode(renderer, tagName, null);
18497 // FIXME(misko): Attaching patch data is only needed for the root (Also add tests)
18498 attachPatchData(elementRNode, lView);
18499 }
18500 break;
18501 default:
18502 ngDevMode &&
18503 throwError(`Unable to determine the type of mutate operation for "${opCode}"`);
18504 }
18505 }
18506 }
18507}
18508/**
18509 * Apply `I18nUpdateOpCodes` OpCodes
18510 *
18511 * @param tView Current `TView`
18512 * @param lView Current `LView`
18513 * @param updateOpCodes OpCodes to process
18514 * @param bindingsStartIndex Location of the first `ɵɵi18nApply`
18515 * @param changeMask Each bit corresponds to a `ɵɵi18nExp` (Counting backwards from
18516 * `bindingsStartIndex`)
18517 */
18518function applyUpdateOpCodes(tView, lView, updateOpCodes, bindingsStartIndex, changeMask) {
18519 for (let i = 0; i < updateOpCodes.length; i++) {
18520 // bit code to check if we should apply the next update
18521 const checkBit = updateOpCodes[i];
18522 // Number of opCodes to skip until next set of update codes
18523 const skipCodes = updateOpCodes[++i];
18524 if (checkBit & changeMask) {
18525 // The value has been updated since last checked
18526 let value = '';
18527 for (let j = i + 1; j <= (i + skipCodes); j++) {
18528 const opCode = updateOpCodes[j];
18529 if (typeof opCode == 'string') {
18530 value += opCode;
18531 }
18532 else if (typeof opCode == 'number') {
18533 if (opCode < 0) {
18534 // Negative opCode represent `i18nExp` values offset.
18535 value += renderStringify(lView[bindingsStartIndex - opCode]);
18536 }
18537 else {
18538 const nodeIndex = (opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */);
18539 switch (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) {
18540 case 1 /* I18nUpdateOpCode.Attr */:
18541 const propName = updateOpCodes[++j];
18542 const sanitizeFn = updateOpCodes[++j];
18543 const tNodeOrTagName = tView.data[nodeIndex];
18544 ngDevMode && assertDefined(tNodeOrTagName, 'Experting TNode or string');
18545 if (typeof tNodeOrTagName === 'string') {
18546 // IF we don't have a `TNode`, then we are an element in ICU (as ICU content does
18547 // not have TNode), in which case we know that there are no directives, and hence
18548 // we use attribute setting.
18549 setElementAttribute(lView[RENDERER], lView[nodeIndex], null, tNodeOrTagName, propName, value, sanitizeFn);
18550 }
18551 else {
18552 elementPropertyInternal(tView, tNodeOrTagName, lView, propName, value, lView[RENDERER], sanitizeFn, false);
18553 }
18554 break;
18555 case 0 /* I18nUpdateOpCode.Text */:
18556 const rText = lView[nodeIndex];
18557 rText !== null && updateTextNode(lView[RENDERER], rText, value);
18558 break;
18559 case 2 /* I18nUpdateOpCode.IcuSwitch */:
18560 applyIcuSwitchCase(tView, getTIcu(tView, nodeIndex), lView, value);
18561 break;
18562 case 3 /* I18nUpdateOpCode.IcuUpdate */:
18563 applyIcuUpdateCase(tView, getTIcu(tView, nodeIndex), bindingsStartIndex, lView);
18564 break;
18565 }
18566 }
18567 }
18568 }
18569 }
18570 else {
18571 const opCode = updateOpCodes[i + 1];
18572 if (opCode > 0 && (opCode & 3 /* I18nUpdateOpCode.MASK_OPCODE */) === 3 /* I18nUpdateOpCode.IcuUpdate */) {
18573 // Special case for the `icuUpdateCase`. It could be that the mask did not match, but
18574 // we still need to execute `icuUpdateCase` because the case has changed recently due to
18575 // previous `icuSwitchCase` instruction. (`icuSwitchCase` and `icuUpdateCase` always come in
18576 // pairs.)
18577 const nodeIndex = (opCode >>> 2 /* I18nUpdateOpCode.SHIFT_REF */);
18578 const tIcu = getTIcu(tView, nodeIndex);
18579 const currentIndex = lView[tIcu.currentCaseLViewIndex];
18580 if (currentIndex < 0) {
18581 applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView);
18582 }
18583 }
18584 }
18585 i += skipCodes;
18586 }
18587}
18588/**
18589 * Apply OpCodes associated with updating an existing ICU.
18590 *
18591 * @param tView Current `TView`
18592 * @param tIcu Current `TIcu`
18593 * @param bindingsStartIndex Location of the first `ɵɵi18nApply`
18594 * @param lView Current `LView`
18595 */
18596function applyIcuUpdateCase(tView, tIcu, bindingsStartIndex, lView) {
18597 ngDevMode && assertIndexInRange(lView, tIcu.currentCaseLViewIndex);
18598 let activeCaseIndex = lView[tIcu.currentCaseLViewIndex];
18599 if (activeCaseIndex !== null) {
18600 let mask = changeMask;
18601 if (activeCaseIndex < 0) {
18602 // Clear the flag.
18603 // Negative number means that the ICU was freshly created and we need to force the update.
18604 activeCaseIndex = lView[tIcu.currentCaseLViewIndex] = ~activeCaseIndex;
18605 // -1 is same as all bits on, which simulates creation since it marks all bits dirty
18606 mask = -1;
18607 }
18608 applyUpdateOpCodes(tView, lView, tIcu.update[activeCaseIndex], bindingsStartIndex, mask);
18609 }
18610}
18611/**
18612 * Apply OpCodes associated with switching a case on ICU.
18613 *
18614 * This involves tearing down existing case and than building up a new case.
18615 *
18616 * @param tView Current `TView`
18617 * @param tIcu Current `TIcu`
18618 * @param lView Current `LView`
18619 * @param value Value of the case to update to.
18620 */
18621function applyIcuSwitchCase(tView, tIcu, lView, value) {
18622 // Rebuild a new case for this ICU
18623 const caseIndex = getCaseIndex(tIcu, value);
18624 let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView);
18625 if (activeCaseIndex !== caseIndex) {
18626 applyIcuSwitchCaseRemove(tView, tIcu, lView);
18627 lView[tIcu.currentCaseLViewIndex] = caseIndex === null ? null : ~caseIndex;
18628 if (caseIndex !== null) {
18629 // Add the nodes for the new case
18630 const anchorRNode = lView[tIcu.anchorIdx];
18631 if (anchorRNode) {
18632 ngDevMode && assertDomNode(anchorRNode);
18633 applyMutableOpCodes(tView, tIcu.create[caseIndex], lView, anchorRNode);
18634 }
18635 }
18636 }
18637}
18638/**
18639 * Apply OpCodes associated with tearing ICU case.
18640 *
18641 * This involves tearing down existing case and than building up a new case.
18642 *
18643 * @param tView Current `TView`
18644 * @param tIcu Current `TIcu`
18645 * @param lView Current `LView`
18646 */
18647function applyIcuSwitchCaseRemove(tView, tIcu, lView) {
18648 let activeCaseIndex = getCurrentICUCaseIndex(tIcu, lView);
18649 if (activeCaseIndex !== null) {
18650 const removeCodes = tIcu.remove[activeCaseIndex];
18651 for (let i = 0; i < removeCodes.length; i++) {
18652 const nodeOrIcuIndex = removeCodes[i];
18653 if (nodeOrIcuIndex > 0) {
18654 // Positive numbers are `RNode`s.
18655 const rNode = getNativeByIndex(nodeOrIcuIndex, lView);
18656 rNode !== null && nativeRemoveNode(lView[RENDERER], rNode);
18657 }
18658 else {
18659 // Negative numbers are ICUs
18660 applyIcuSwitchCaseRemove(tView, getTIcu(tView, ~nodeOrIcuIndex), lView);
18661 }
18662 }
18663 }
18664}
18665/**
18666 * Returns the index of the current case of an ICU expression depending on the main binding value
18667 *
18668 * @param icuExpression
18669 * @param bindingValue The value of the main binding used by this ICU expression
18670 */
18671function getCaseIndex(icuExpression, bindingValue) {
18672 let index = icuExpression.cases.indexOf(bindingValue);
18673 if (index === -1) {
18674 switch (icuExpression.type) {
18675 case 1 /* IcuType.plural */: {
18676 const resolvedCase = getPluralCase(bindingValue, getLocaleId());
18677 index = icuExpression.cases.indexOf(resolvedCase);
18678 if (index === -1 && resolvedCase !== 'other') {
18679 index = icuExpression.cases.indexOf('other');
18680 }
18681 break;
18682 }
18683 case 0 /* IcuType.select */: {
18684 index = icuExpression.cases.indexOf('other');
18685 break;
18686 }
18687 }
18688 }
18689 return index === -1 ? null : index;
18690}
18691
18692function loadIcuContainerVisitor() {
18693 const _stack = [];
18694 let _index = -1;
18695 let _lView;
18696 let _removes;
18697 /**
18698 * Retrieves a set of root nodes from `TIcu.remove`. Used by `TNodeType.ICUContainer`
18699 * to determine which root belong to the ICU.
18700 *
18701 * Example of usage.
18702 * ```
18703 * const nextRNode = icuContainerIteratorStart(tIcuContainerNode, lView);
18704 * let rNode: RNode|null;
18705 * while(rNode = nextRNode()) {
18706 * console.log(rNode);
18707 * }
18708 * ```
18709 *
18710 * @param tIcuContainerNode Current `TIcuContainerNode`
18711 * @param lView `LView` where the `RNode`s should be looked up.
18712 */
18713 function icuContainerIteratorStart(tIcuContainerNode, lView) {
18714 _lView = lView;
18715 while (_stack.length)
18716 _stack.pop();
18717 ngDevMode && assertTNodeForLView(tIcuContainerNode, lView);
18718 enterIcu(tIcuContainerNode.value, lView);
18719 return icuContainerIteratorNext;
18720 }
18721 function enterIcu(tIcu, lView) {
18722 _index = 0;
18723 const currentCase = getCurrentICUCaseIndex(tIcu, lView);
18724 if (currentCase !== null) {
18725 ngDevMode && assertNumberInRange(currentCase, 0, tIcu.cases.length - 1);
18726 _removes = tIcu.remove[currentCase];
18727 }
18728 else {
18729 _removes = EMPTY_ARRAY;
18730 }
18731 }
18732 function icuContainerIteratorNext() {
18733 if (_index < _removes.length) {
18734 const removeOpCode = _removes[_index++];
18735 ngDevMode && assertNumber(removeOpCode, 'Expecting OpCode number');
18736 if (removeOpCode > 0) {
18737 const rNode = _lView[removeOpCode];
18738 ngDevMode && assertDomNode(rNode);
18739 return rNode;
18740 }
18741 else {
18742 _stack.push(_index, _removes);
18743 // ICUs are represented by negative indices
18744 const tIcuIndex = ~removeOpCode;
18745 const tIcu = _lView[TVIEW].data[tIcuIndex];
18746 ngDevMode && assertTIcu(tIcu);
18747 enterIcu(tIcu, _lView);
18748 return icuContainerIteratorNext();
18749 }
18750 }
18751 else {
18752 if (_stack.length === 0) {
18753 return null;
18754 }
18755 else {
18756 _removes = _stack.pop();
18757 _index = _stack.pop();
18758 return icuContainerIteratorNext();
18759 }
18760 }
18761 }
18762 return icuContainerIteratorStart;
18763}
18764
18765/**
18766 * Converts `I18nCreateOpCodes` array into a human readable format.
18767 *
18768 * This function is attached to the `I18nCreateOpCodes.debug` property if `ngDevMode` is enabled.
18769 * This function provides a human readable view of the opcodes. This is useful when debugging the
18770 * application as well as writing more readable tests.
18771 *
18772 * @param this `I18nCreateOpCodes` if attached as a method.
18773 * @param opcodes `I18nCreateOpCodes` if invoked as a function.
18774 */
18775function i18nCreateOpCodesToString(opcodes) {
18776 const createOpCodes = opcodes || (Array.isArray(this) ? this : []);
18777 let lines = [];
18778 for (let i = 0; i < createOpCodes.length; i++) {
18779 const opCode = createOpCodes[i++];
18780 const text = createOpCodes[i];
18781 const isComment = (opCode & I18nCreateOpCode.COMMENT) === I18nCreateOpCode.COMMENT;
18782 const appendNow = (opCode & I18nCreateOpCode.APPEND_EAGERLY) === I18nCreateOpCode.APPEND_EAGERLY;
18783 const index = opCode >>> I18nCreateOpCode.SHIFT;
18784 lines.push(`lView[${index}] = document.${isComment ? 'createComment' : 'createText'}(${JSON.stringify(text)});`);
18785 if (appendNow) {
18786 lines.push(`parent.appendChild(lView[${index}]);`);
18787 }
18788 }
18789 return lines;
18790}
18791/**
18792 * Converts `I18nUpdateOpCodes` array into a human readable format.
18793 *
18794 * This function is attached to the `I18nUpdateOpCodes.debug` property if `ngDevMode` is enabled.
18795 * This function provides a human readable view of the opcodes. This is useful when debugging the
18796 * application as well as writing more readable tests.
18797 *
18798 * @param this `I18nUpdateOpCodes` if attached as a method.
18799 * @param opcodes `I18nUpdateOpCodes` if invoked as a function.
18800 */
18801function i18nUpdateOpCodesToString(opcodes) {
18802 const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : []));
18803 let lines = [];
18804 function consumeOpCode(value) {
18805 const ref = value >>> 2 /* I18nUpdateOpCode.SHIFT_REF */;
18806 const opCode = value & 3 /* I18nUpdateOpCode.MASK_OPCODE */;
18807 switch (opCode) {
18808 case 0 /* I18nUpdateOpCode.Text */:
18809 return `(lView[${ref}] as Text).textContent = $$$`;
18810 case 1 /* I18nUpdateOpCode.Attr */:
18811 const attrName = parser.consumeString();
18812 const sanitizationFn = parser.consumeFunction();
18813 const value = sanitizationFn ? `(${sanitizationFn})($$$)` : '$$$';
18814 return `(lView[${ref}] as Element).setAttribute('${attrName}', ${value})`;
18815 case 2 /* I18nUpdateOpCode.IcuSwitch */:
18816 return `icuSwitchCase(${ref}, $$$)`;
18817 case 3 /* I18nUpdateOpCode.IcuUpdate */:
18818 return `icuUpdateCase(${ref})`;
18819 }
18820 throw new Error('unexpected OpCode');
18821 }
18822 while (parser.hasMore()) {
18823 let mask = parser.consumeNumber();
18824 let size = parser.consumeNumber();
18825 const end = parser.i + size;
18826 const statements = [];
18827 let statement = '';
18828 while (parser.i < end) {
18829 let value = parser.consumeNumberOrString();
18830 if (typeof value === 'string') {
18831 statement += value;
18832 }
18833 else if (value < 0) {
18834 // Negative numbers are ref indexes
18835 // Here `i` refers to current binding index. It is to signify that the value is relative,
18836 // rather than absolute.
18837 statement += '${lView[i' + value + ']}';
18838 }
18839 else {
18840 // Positive numbers are operations.
18841 const opCodeText = consumeOpCode(value);
18842 statements.push(opCodeText.replace('$$$', '`' + statement + '`') + ';');
18843 statement = '';
18844 }
18845 }
18846 lines.push(`if (mask & 0b${mask.toString(2)}) { ${statements.join(' ')} }`);
18847 }
18848 return lines;
18849}
18850/**
18851 * Converts `I18nCreateOpCodes` array into a human readable format.
18852 *
18853 * This function is attached to the `I18nCreateOpCodes.debug` if `ngDevMode` is enabled. This
18854 * function provides a human readable view of the opcodes. This is useful when debugging the
18855 * application as well as writing more readable tests.
18856 *
18857 * @param this `I18nCreateOpCodes` if attached as a method.
18858 * @param opcodes `I18nCreateOpCodes` if invoked as a function.
18859 */
18860function icuCreateOpCodesToString(opcodes) {
18861 const parser = new OpCodeParser(opcodes || (Array.isArray(this) ? this : []));
18862 let lines = [];
18863 function consumeOpCode(opCode) {
18864 const parent = getParentFromIcuCreateOpCode(opCode);
18865 const ref = getRefFromIcuCreateOpCode(opCode);
18866 switch (getInstructionFromIcuCreateOpCode(opCode)) {
18867 case 0 /* IcuCreateOpCode.AppendChild */:
18868 return `(lView[${parent}] as Element).appendChild(lView[${lastRef}])`;
18869 case 1 /* IcuCreateOpCode.Attr */:
18870 return `(lView[${ref}] as Element).setAttribute("${parser.consumeString()}", "${parser.consumeString()}")`;
18871 }
18872 throw new Error('Unexpected OpCode: ' + getInstructionFromIcuCreateOpCode(opCode));
18873 }
18874 let lastRef = -1;
18875 while (parser.hasMore()) {
18876 let value = parser.consumeNumberStringOrMarker();
18877 if (value === ICU_MARKER) {
18878 const text = parser.consumeString();
18879 lastRef = parser.consumeNumber();
18880 lines.push(`lView[${lastRef}] = document.createComment("${text}")`);
18881 }
18882 else if (value === ELEMENT_MARKER) {
18883 const text = parser.consumeString();
18884 lastRef = parser.consumeNumber();
18885 lines.push(`lView[${lastRef}] = document.createElement("${text}")`);
18886 }
18887 else if (typeof value === 'string') {
18888 lastRef = parser.consumeNumber();
18889 lines.push(`lView[${lastRef}] = document.createTextNode("${value}")`);
18890 }
18891 else if (typeof value === 'number') {
18892 const line = consumeOpCode(value);
18893 line && lines.push(line);
18894 }
18895 else {
18896 throw new Error('Unexpected value');
18897 }
18898 }
18899 return lines;
18900}
18901/**
18902 * Converts `I18nRemoveOpCodes` array into a human readable format.
18903 *
18904 * This function is attached to the `I18nRemoveOpCodes.debug` if `ngDevMode` is enabled. This
18905 * function provides a human readable view of the opcodes. This is useful when debugging the
18906 * application as well as writing more readable tests.
18907 *
18908 * @param this `I18nRemoveOpCodes` if attached as a method.
18909 * @param opcodes `I18nRemoveOpCodes` if invoked as a function.
18910 */
18911function i18nRemoveOpCodesToString(opcodes) {
18912 const removeCodes = opcodes || (Array.isArray(this) ? this : []);
18913 let lines = [];
18914 for (let i = 0; i < removeCodes.length; i++) {
18915 const nodeOrIcuIndex = removeCodes[i];
18916 if (nodeOrIcuIndex > 0) {
18917 // Positive numbers are `RNode`s.
18918 lines.push(`remove(lView[${nodeOrIcuIndex}])`);
18919 }
18920 else {
18921 // Negative numbers are ICUs
18922 lines.push(`removeNestedICU(${~nodeOrIcuIndex})`);
18923 }
18924 }
18925 return lines;
18926}
18927class OpCodeParser {
18928 constructor(codes) {
18929 this.i = 0;
18930 this.codes = codes;
18931 }
18932 hasMore() {
18933 return this.i < this.codes.length;
18934 }
18935 consumeNumber() {
18936 let value = this.codes[this.i++];
18937 assertNumber(value, 'expecting number in OpCode');
18938 return value;
18939 }
18940 consumeString() {
18941 let value = this.codes[this.i++];
18942 assertString(value, 'expecting string in OpCode');
18943 return value;
18944 }
18945 consumeFunction() {
18946 let value = this.codes[this.i++];
18947 if (value === null || typeof value === 'function') {
18948 return value;
18949 }
18950 throw new Error('expecting function in OpCode');
18951 }
18952 consumeNumberOrString() {
18953 let value = this.codes[this.i++];
18954 if (typeof value === 'string') {
18955 return value;
18956 }
18957 assertNumber(value, 'expecting number or string in OpCode');
18958 return value;
18959 }
18960 consumeNumberStringOrMarker() {
18961 let value = this.codes[this.i++];
18962 if (typeof value === 'string' || typeof value === 'number' || value == ICU_MARKER ||
18963 value == ELEMENT_MARKER) {
18964 return value;
18965 }
18966 assertNumber(value, 'expecting number, string, ICU_MARKER or ELEMENT_MARKER in OpCode');
18967 return value;
18968 }
18969}
18970
18971const BINDING_REGEXP = /�(\d+):?\d*�/gi;
18972const ICU_REGEXP = /({\s*�\d+:?\d*�\s*,\s*\S{6}\s*,[\s\S]*})/gi;
18973const NESTED_ICU = /�(\d+)�/;
18974const ICU_BLOCK_REGEXP = /^\s*(�\d+:?\d*�)\s*,\s*(select|plural)\s*,/;
18975const MARKER = `�`;
18976const SUBTEMPLATE_REGEXP = /�\/?\*(\d+:\d+)�/gi;
18977const PH_REGEXP = /�(\/?[#*]\d+):?\d*�/gi;
18978/**
18979 * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
18980 * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
18981 * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
18982 * and later on replaced by a space. We are re-implementing the same idea here, since translations
18983 * might contain this special character.
18984 */
18985const NGSP_UNICODE_REGEXP = /\uE500/g;
18986function replaceNgsp(value) {
18987 return value.replace(NGSP_UNICODE_REGEXP, ' ');
18988}
18989/**
18990 * Patch a `debug` property getter on top of the existing object.
18991 *
18992 * NOTE: always call this method with `ngDevMode && attachDebugObject(...)`
18993 *
18994 * @param obj Object to patch
18995 * @param debugGetter Getter returning a value to patch
18996 */
18997function attachDebugGetter(obj, debugGetter) {
18998 if (ngDevMode) {
18999 Object.defineProperty(obj, 'debug', { get: debugGetter, enumerable: false });
19000 }
19001 else {
19002 throw new Error('This method should be guarded with `ngDevMode` so that it can be tree shaken in production!');
19003 }
19004}
19005/**
19006 * Create dynamic nodes from i18n translation block.
19007 *
19008 * - Text nodes are created synchronously
19009 * - TNodes are linked into tree lazily
19010 *
19011 * @param tView Current `TView`
19012 * @parentTNodeIndex index to the parent TNode of this i18n block
19013 * @param lView Current `LView`
19014 * @param index Index of `ɵɵi18nStart` instruction.
19015 * @param message Message to translate.
19016 * @param subTemplateIndex Index into the sub template of message translation. (ie in case of
19017 * `ngIf`) (-1 otherwise)
19018 */
19019function i18nStartFirstCreatePass(tView, parentTNodeIndex, lView, index, message, subTemplateIndex) {
19020 const rootTNode = getCurrentParentTNode();
19021 const createOpCodes = [];
19022 const updateOpCodes = [];
19023 const existingTNodeStack = [[]];
19024 if (ngDevMode) {
19025 attachDebugGetter(createOpCodes, i18nCreateOpCodesToString);
19026 attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
19027 }
19028 message = getTranslationForTemplate(message, subTemplateIndex);
19029 const msgParts = replaceNgsp(message).split(PH_REGEXP);
19030 for (let i = 0; i < msgParts.length; i++) {
19031 let value = msgParts[i];
19032 if ((i & 1) === 0) {
19033 // Even indexes are text (including bindings & ICU expressions)
19034 const parts = i18nParseTextIntoPartsAndICU(value);
19035 for (let j = 0; j < parts.length; j++) {
19036 let part = parts[j];
19037 if ((j & 1) === 0) {
19038 // `j` is odd therefore `part` is string
19039 const text = part;
19040 ngDevMode && assertString(text, 'Parsed ICU part should be string');
19041 if (text !== '') {
19042 i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodeStack[0], createOpCodes, updateOpCodes, lView, text);
19043 }
19044 }
19045 else {
19046 // `j` is Even therefor `part` is an `ICUExpression`
19047 const icuExpression = part;
19048 // Verify that ICU expression has the right shape. Translations might contain invalid
19049 // constructions (while original messages were correct), so ICU parsing at runtime may
19050 // not succeed (thus `icuExpression` remains a string).
19051 // Note: we intentionally retain the error here by not using `ngDevMode`, because
19052 // the value can change based on the locale and users aren't guaranteed to hit
19053 // an invalid string while they're developing.
19054 if (typeof icuExpression !== 'object') {
19055 throw new Error(`Unable to parse ICU expression in "${message}" message.`);
19056 }
19057 const icuContainerTNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodeStack[0], lView, createOpCodes, ngDevMode ? `ICU ${index}:${icuExpression.mainBinding}` : '', true);
19058 const icuNodeIndex = icuContainerTNode.index;
19059 ngDevMode &&
19060 assertGreaterThanOrEqual(icuNodeIndex, HEADER_OFFSET, 'Index must be in absolute LView offset');
19061 icuStart(tView, lView, updateOpCodes, parentTNodeIndex, icuExpression, icuNodeIndex);
19062 }
19063 }
19064 }
19065 else {
19066 // Odd indexes are placeholders (elements and sub-templates)
19067 // At this point value is something like: '/#1:2' (originally coming from '�/#1:2�')
19068 const isClosing = value.charCodeAt(0) === 47 /* CharCode.SLASH */;
19069 const type = value.charCodeAt(isClosing ? 1 : 0);
19070 ngDevMode && assertOneOf(type, 42 /* CharCode.STAR */, 35 /* CharCode.HASH */);
19071 const index = HEADER_OFFSET + Number.parseInt(value.substring((isClosing ? 2 : 1)));
19072 if (isClosing) {
19073 existingTNodeStack.shift();
19074 setCurrentTNode(getCurrentParentTNode(), false);
19075 }
19076 else {
19077 const tNode = createTNodePlaceholder(tView, existingTNodeStack[0], index);
19078 existingTNodeStack.unshift([]);
19079 setCurrentTNode(tNode, true);
19080 }
19081 }
19082 }
19083 tView.data[index] = {
19084 create: createOpCodes,
19085 update: updateOpCodes,
19086 };
19087}
19088/**
19089 * Allocate space in i18n Range add create OpCode instruction to create a text or comment node.
19090 *
19091 * @param tView Current `TView` needed to allocate space in i18n range.
19092 * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will be
19093 * added as part of the `i18nStart` instruction or as part of the `TNode.insertBeforeIndex`.
19094 * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
19095 * @param lView Current `LView` needed to allocate space in i18n range.
19096 * @param createOpCodes Array storing `I18nCreateOpCodes` where new opCodes will be added.
19097 * @param text Text to be added when the `Text` or `Comment` node will be created.
19098 * @param isICU true if a `Comment` node for ICU (instead of `Text`) node should be created.
19099 */
19100function createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, text, isICU) {
19101 const i18nNodeIdx = allocExpando(tView, lView, 1, null);
19102 let opCode = i18nNodeIdx << I18nCreateOpCode.SHIFT;
19103 let parentTNode = getCurrentParentTNode();
19104 if (rootTNode === parentTNode) {
19105 // FIXME(misko): A null `parentTNode` should represent when we fall of the `LView` boundary.
19106 // (there is no parent), but in some circumstances (because we are inconsistent about how we set
19107 // `previousOrParentTNode`) it could point to `rootTNode` So this is a work around.
19108 parentTNode = null;
19109 }
19110 if (parentTNode === null) {
19111 // If we don't have a parent that means that we can eagerly add nodes.
19112 // If we have a parent than these nodes can't be added now (as the parent has not been created
19113 // yet) and instead the `parentTNode` is responsible for adding it. See
19114 // `TNode.insertBeforeIndex`
19115 opCode |= I18nCreateOpCode.APPEND_EAGERLY;
19116 }
19117 if (isICU) {
19118 opCode |= I18nCreateOpCode.COMMENT;
19119 ensureIcuContainerVisitorLoaded(loadIcuContainerVisitor);
19120 }
19121 createOpCodes.push(opCode, text === null ? '' : text);
19122 // We store `{{?}}` so that when looking at debug `TNodeType.template` we can see where the
19123 // bindings are.
19124 const tNode = createTNodeAtIndex(tView, i18nNodeIdx, isICU ? 32 /* TNodeType.Icu */ : 1 /* TNodeType.Text */, text === null ? (ngDevMode ? '{{?}}' : '') : text, null);
19125 addTNodeAndUpdateInsertBeforeIndex(existingTNodes, tNode);
19126 const tNodeIdx = tNode.index;
19127 setCurrentTNode(tNode, false /* Text nodes are self closing */);
19128 if (parentTNode !== null && rootTNode !== parentTNode) {
19129 // We are a child of deeper node (rather than a direct child of `i18nStart` instruction.)
19130 // We have to make sure to add ourselves to the parent.
19131 setTNodeInsertBeforeIndex(parentTNode, tNodeIdx);
19132 }
19133 return tNode;
19134}
19135/**
19136 * Processes text node in i18n block.
19137 *
19138 * Text nodes can have:
19139 * - Create instruction in `createOpCodes` for creating the text node.
19140 * - Allocate spec for text node in i18n range of `LView`
19141 * - If contains binding:
19142 * - bindings => allocate space in i18n range of `LView` to store the binding value.
19143 * - populate `updateOpCodes` with update instructions.
19144 *
19145 * @param tView Current `TView`
19146 * @param rootTNode Root `TNode` of the i18n block. This node determines if the new TNode will
19147 * be added as part of the `i18nStart` instruction or as part of the
19148 * `TNode.insertBeforeIndex`.
19149 * @param existingTNodes internal state for `addTNodeAndUpdateInsertBeforeIndex`.
19150 * @param createOpCodes Location where the creation OpCodes will be stored.
19151 * @param lView Current `LView`
19152 * @param text The translated text (which may contain binding)
19153 */
19154function i18nStartFirstCreatePassProcessTextNode(tView, rootTNode, existingTNodes, createOpCodes, updateOpCodes, lView, text) {
19155 const hasBinding = text.match(BINDING_REGEXP);
19156 const tNode = createTNodeAndAddOpCode(tView, rootTNode, existingTNodes, lView, createOpCodes, hasBinding ? null : text, false);
19157 if (hasBinding) {
19158 generateBindingUpdateOpCodes(updateOpCodes, text, tNode.index, null, 0, null);
19159 }
19160}
19161/**
19162 * See `i18nAttributes` above.
19163 */
19164function i18nAttributesFirstPass(tView, index, values) {
19165 const previousElement = getCurrentTNode();
19166 const previousElementIndex = previousElement.index;
19167 const updateOpCodes = [];
19168 if (ngDevMode) {
19169 attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
19170 }
19171 if (tView.firstCreatePass && tView.data[index] === null) {
19172 for (let i = 0; i < values.length; i += 2) {
19173 const attrName = values[i];
19174 const message = values[i + 1];
19175 if (message !== '') {
19176 // Check if attribute value contains an ICU and throw an error if that's the case.
19177 // ICUs in element attributes are not supported.
19178 // Note: we intentionally retain the error here by not using `ngDevMode`, because
19179 // the `value` can change based on the locale and users aren't guaranteed to hit
19180 // an invalid string while they're developing.
19181 if (ICU_REGEXP.test(message)) {
19182 throw new Error(`ICU expressions are not supported in attributes. Message: "${message}".`);
19183 }
19184 // i18n attributes that hit this code path are guaranteed to have bindings, because
19185 // the compiler treats static i18n attributes as regular attribute bindings.
19186 // Since this may not be the first i18n attribute on this element we need to pass in how
19187 // many previous bindings there have already been.
19188 generateBindingUpdateOpCodes(updateOpCodes, message, previousElementIndex, attrName, countBindings(updateOpCodes), null);
19189 }
19190 }
19191 tView.data[index] = updateOpCodes;
19192 }
19193}
19194/**
19195 * Generate the OpCodes to update the bindings of a string.
19196 *
19197 * @param updateOpCodes Place where the update opcodes will be stored.
19198 * @param str The string containing the bindings.
19199 * @param destinationNode Index of the destination node which will receive the binding.
19200 * @param attrName Name of the attribute, if the string belongs to an attribute.
19201 * @param sanitizeFn Sanitization function used to sanitize the string after update, if necessary.
19202 * @param bindingStart The lView index of the next expression that can be bound via an opCode.
19203 * @returns The mask value for these bindings
19204 */
19205function generateBindingUpdateOpCodes(updateOpCodes, str, destinationNode, attrName, bindingStart, sanitizeFn) {
19206 ngDevMode &&
19207 assertGreaterThanOrEqual(destinationNode, HEADER_OFFSET, 'Index must be in absolute LView offset');
19208 const maskIndex = updateOpCodes.length; // Location of mask
19209 const sizeIndex = maskIndex + 1; // location of size for skipping
19210 updateOpCodes.push(null, null); // Alloc space for mask and size
19211 const startIndex = maskIndex + 2; // location of first allocation.
19212 if (ngDevMode) {
19213 attachDebugGetter(updateOpCodes, i18nUpdateOpCodesToString);
19214 }
19215 const textParts = str.split(BINDING_REGEXP);
19216 let mask = 0;
19217 for (let j = 0; j < textParts.length; j++) {
19218 const textValue = textParts[j];
19219 if (j & 1) {
19220 // Odd indexes are bindings
19221 const bindingIndex = bindingStart + parseInt(textValue, 10);
19222 updateOpCodes.push(-1 - bindingIndex);
19223 mask = mask | toMaskBit(bindingIndex);
19224 }
19225 else if (textValue !== '') {
19226 // Even indexes are text
19227 updateOpCodes.push(textValue);
19228 }
19229 }
19230 updateOpCodes.push(destinationNode << 2 /* I18nUpdateOpCode.SHIFT_REF */ |
19231 (attrName ? 1 /* I18nUpdateOpCode.Attr */ : 0 /* I18nUpdateOpCode.Text */));
19232 if (attrName) {
19233 updateOpCodes.push(attrName, sanitizeFn);
19234 }
19235 updateOpCodes[maskIndex] = mask;
19236 updateOpCodes[sizeIndex] = updateOpCodes.length - startIndex;
19237 return mask;
19238}
19239/**
19240 * Count the number of bindings in the given `opCodes`.
19241 *
19242 * It could be possible to speed this up, by passing the number of bindings found back from
19243 * `generateBindingUpdateOpCodes()` to `i18nAttributesFirstPass()` but this would then require more
19244 * complexity in the code and/or transient objects to be created.
19245 *
19246 * Since this function is only called once when the template is instantiated, is trivial in the
19247 * first instance (since `opCodes` will be an empty array), and it is not common for elements to
19248 * contain multiple i18n bound attributes, it seems like this is a reasonable compromise.
19249 */
19250function countBindings(opCodes) {
19251 let count = 0;
19252 for (let i = 0; i < opCodes.length; i++) {
19253 const opCode = opCodes[i];
19254 // Bindings are negative numbers.
19255 if (typeof opCode === 'number' && opCode < 0) {
19256 count++;
19257 }
19258 }
19259 return count;
19260}
19261/**
19262 * Convert binding index to mask bit.
19263 *
19264 * Each index represents a single bit on the bit-mask. Because bit-mask only has 32 bits, we make
19265 * the 32nd bit share all masks for all bindings higher than 32. Since it is extremely rare to
19266 * have more than 32 bindings this will be hit very rarely. The downside of hitting this corner
19267 * case is that we will execute binding code more often than necessary. (penalty of performance)
19268 */
19269function toMaskBit(bindingIndex) {
19270 return 1 << Math.min(bindingIndex, 31);
19271}
19272function isRootTemplateMessage(subTemplateIndex) {
19273 return subTemplateIndex === -1;
19274}
19275/**
19276 * Removes everything inside the sub-templates of a message.
19277 */
19278function removeInnerTemplateTranslation(message) {
19279 let match;
19280 let res = '';
19281 let index = 0;
19282 let inTemplate = false;
19283 let tagMatched;
19284 while ((match = SUBTEMPLATE_REGEXP.exec(message)) !== null) {
19285 if (!inTemplate) {
19286 res += message.substring(index, match.index + match[0].length);
19287 tagMatched = match[1];
19288 inTemplate = true;
19289 }
19290 else {
19291 if (match[0] === `${MARKER}/*${tagMatched}${MARKER}`) {
19292 index = match.index;
19293 inTemplate = false;
19294 }
19295 }
19296 }
19297 ngDevMode &&
19298 assertEqual(inTemplate, false, `Tag mismatch: unable to find the end of the sub-template in the translation "${message}"`);
19299 res += message.slice(index);
19300 return res;
19301}
19302/**
19303 * Extracts a part of a message and removes the rest.
19304 *
19305 * This method is used for extracting a part of the message associated with a template. A
19306 * translated message can span multiple templates.
19307 *
19308 * Example:
19309 * ```
19310 * <div i18n>Translate <span *ngIf>me</span>!</div>
19311 * ```
19312 *
19313 * @param message The message to crop
19314 * @param subTemplateIndex Index of the sub-template to extract. If undefined it returns the
19315 * external template and removes all sub-templates.
19316 */
19317function getTranslationForTemplate(message, subTemplateIndex) {
19318 if (isRootTemplateMessage(subTemplateIndex)) {
19319 // We want the root template message, ignore all sub-templates
19320 return removeInnerTemplateTranslation(message);
19321 }
19322 else {
19323 // We want a specific sub-template
19324 const start = message.indexOf(`:${subTemplateIndex}${MARKER}`) + 2 + subTemplateIndex.toString().length;
19325 const end = message.search(new RegExp(`${MARKER}\\/\\*\\d+:${subTemplateIndex}${MARKER}`));
19326 return removeInnerTemplateTranslation(message.substring(start, end));
19327 }
19328}
19329/**
19330 * Generate the OpCodes for ICU expressions.
19331 *
19332 * @param icuExpression
19333 * @param index Index where the anchor is stored and an optional `TIcuContainerNode`
19334 * - `lView[anchorIdx]` points to a `Comment` node representing the anchor for the ICU.
19335 * - `tView.data[anchorIdx]` points to the `TIcuContainerNode` if ICU is root (`null` otherwise)
19336 */
19337function icuStart(tView, lView, updateOpCodes, parentIdx, icuExpression, anchorIdx) {
19338 ngDevMode && assertDefined(icuExpression, 'ICU expression must be defined');
19339 let bindingMask = 0;
19340 const tIcu = {
19341 type: icuExpression.type,
19342 currentCaseLViewIndex: allocExpando(tView, lView, 1, null),
19343 anchorIdx,
19344 cases: [],
19345 create: [],
19346 remove: [],
19347 update: []
19348 };
19349 addUpdateIcuSwitch(updateOpCodes, icuExpression, anchorIdx);
19350 setTIcu(tView, anchorIdx, tIcu);
19351 const values = icuExpression.values;
19352 for (let i = 0; i < values.length; i++) {
19353 // Each value is an array of strings & other ICU expressions
19354 const valueArr = values[i];
19355 const nestedIcus = [];
19356 for (let j = 0; j < valueArr.length; j++) {
19357 const value = valueArr[j];
19358 if (typeof value !== 'string') {
19359 // It is an nested ICU expression
19360 const icuIndex = nestedIcus.push(value) - 1;
19361 // Replace nested ICU expression by a comment node
19362 valueArr[j] = `<!--�${icuIndex}�-->`;
19363 }
19364 }
19365 bindingMask = parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, icuExpression.cases[i], valueArr.join(''), nestedIcus) |
19366 bindingMask;
19367 }
19368 if (bindingMask) {
19369 addUpdateIcuUpdate(updateOpCodes, bindingMask, anchorIdx);
19370 }
19371}
19372/**
19373 * Parses text containing an ICU expression and produces a JSON object for it.
19374 * Original code from closure library, modified for Angular.
19375 *
19376 * @param pattern Text containing an ICU expression that needs to be parsed.
19377 *
19378 */
19379function parseICUBlock(pattern) {
19380 const cases = [];
19381 const values = [];
19382 let icuType = 1 /* IcuType.plural */;
19383 let mainBinding = 0;
19384 pattern = pattern.replace(ICU_BLOCK_REGEXP, function (str, binding, type) {
19385 if (type === 'select') {
19386 icuType = 0 /* IcuType.select */;
19387 }
19388 else {
19389 icuType = 1 /* IcuType.plural */;
19390 }
19391 mainBinding = parseInt(binding.slice(1), 10);
19392 return '';
19393 });
19394 const parts = i18nParseTextIntoPartsAndICU(pattern);
19395 // Looking for (key block)+ sequence. One of the keys has to be "other".
19396 for (let pos = 0; pos < parts.length;) {
19397 let key = parts[pos++].trim();
19398 if (icuType === 1 /* IcuType.plural */) {
19399 // Key can be "=x", we just want "x"
19400 key = key.replace(/\s*(?:=)?(\w+)\s*/, '$1');
19401 }
19402 if (key.length) {
19403 cases.push(key);
19404 }
19405 const blocks = i18nParseTextIntoPartsAndICU(parts[pos++]);
19406 if (cases.length > values.length) {
19407 values.push(blocks);
19408 }
19409 }
19410 // TODO(ocombe): support ICU expressions in attributes, see #21615
19411 return { type: icuType, mainBinding: mainBinding, cases, values };
19412}
19413/**
19414 * Breaks pattern into strings and top level {...} blocks.
19415 * Can be used to break a message into text and ICU expressions, or to break an ICU expression
19416 * into keys and cases. Original code from closure library, modified for Angular.
19417 *
19418 * @param pattern (sub)Pattern to be broken.
19419 * @returns An `Array<string|IcuExpression>` where:
19420 * - odd positions: `string` => text between ICU expressions
19421 * - even positions: `ICUExpression` => ICU expression parsed into `ICUExpression` record.
19422 */
19423function i18nParseTextIntoPartsAndICU(pattern) {
19424 if (!pattern) {
19425 return [];
19426 }
19427 let prevPos = 0;
19428 const braceStack = [];
19429 const results = [];
19430 const braces = /[{}]/g;
19431 // lastIndex doesn't get set to 0 so we have to.
19432 braces.lastIndex = 0;
19433 let match;
19434 while (match = braces.exec(pattern)) {
19435 const pos = match.index;
19436 if (match[0] == '}') {
19437 braceStack.pop();
19438 if (braceStack.length == 0) {
19439 // End of the block.
19440 const block = pattern.substring(prevPos, pos);
19441 if (ICU_BLOCK_REGEXP.test(block)) {
19442 results.push(parseICUBlock(block));
19443 }
19444 else {
19445 results.push(block);
19446 }
19447 prevPos = pos + 1;
19448 }
19449 }
19450 else {
19451 if (braceStack.length == 0) {
19452 const substring = pattern.substring(prevPos, pos);
19453 results.push(substring);
19454 prevPos = pos + 1;
19455 }
19456 braceStack.push('{');
19457 }
19458 }
19459 const substring = pattern.substring(prevPos);
19460 results.push(substring);
19461 return results;
19462}
19463/**
19464 * Parses a node, its children and its siblings, and generates the mutate & update OpCodes.
19465 *
19466 */
19467function parseIcuCase(tView, tIcu, lView, updateOpCodes, parentIdx, caseName, unsafeCaseHtml, nestedIcus) {
19468 const create = [];
19469 const remove = [];
19470 const update = [];
19471 if (ngDevMode) {
19472 attachDebugGetter(create, icuCreateOpCodesToString);
19473 attachDebugGetter(remove, i18nRemoveOpCodesToString);
19474 attachDebugGetter(update, i18nUpdateOpCodesToString);
19475 }
19476 tIcu.cases.push(caseName);
19477 tIcu.create.push(create);
19478 tIcu.remove.push(remove);
19479 tIcu.update.push(update);
19480 const inertBodyHelper = getInertBodyHelper(getDocument());
19481 const inertBodyElement = inertBodyHelper.getInertBodyElement(unsafeCaseHtml);
19482 ngDevMode && assertDefined(inertBodyElement, 'Unable to generate inert body element');
19483 const inertRootNode = getTemplateContent(inertBodyElement) || inertBodyElement;
19484 if (inertRootNode) {
19485 return walkIcuTree(tView, tIcu, lView, updateOpCodes, create, remove, update, inertRootNode, parentIdx, nestedIcus, 0);
19486 }
19487 else {
19488 return 0;
19489 }
19490}
19491function walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, parentNode, parentIdx, nestedIcus, depth) {
19492 let bindingMask = 0;
19493 let currentNode = parentNode.firstChild;
19494 while (currentNode) {
19495 const newIndex = allocExpando(tView, lView, 1, null);
19496 switch (currentNode.nodeType) {
19497 case Node.ELEMENT_NODE:
19498 const element = currentNode;
19499 const tagName = element.tagName.toLowerCase();
19500 if (VALID_ELEMENTS.hasOwnProperty(tagName)) {
19501 addCreateNodeAndAppend(create, ELEMENT_MARKER, tagName, parentIdx, newIndex);
19502 tView.data[newIndex] = tagName;
19503 const elAttrs = element.attributes;
19504 for (let i = 0; i < elAttrs.length; i++) {
19505 const attr = elAttrs.item(i);
19506 const lowerAttrName = attr.name.toLowerCase();
19507 const hasBinding = !!attr.value.match(BINDING_REGEXP);
19508 // we assume the input string is safe, unless it's using a binding
19509 if (hasBinding) {
19510 if (VALID_ATTRS.hasOwnProperty(lowerAttrName)) {
19511 if (URI_ATTRS[lowerAttrName]) {
19512 generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, _sanitizeUrl);
19513 }
19514 else {
19515 generateBindingUpdateOpCodes(update, attr.value, newIndex, attr.name, 0, null);
19516 }
19517 }
19518 else {
19519 ngDevMode &&
19520 console.warn(`WARNING: ignoring unsafe attribute value ` +
19521 `${lowerAttrName} on element ${tagName} ` +
19522 `(see ${XSS_SECURITY_URL})`);
19523 }
19524 }
19525 else {
19526 addCreateAttribute(create, newIndex, attr);
19527 }
19528 }
19529 // Parse the children of this node (if any)
19530 bindingMask = walkIcuTree(tView, tIcu, lView, sharedUpdateOpCodes, create, remove, update, currentNode, newIndex, nestedIcus, depth + 1) |
19531 bindingMask;
19532 addRemoveNode(remove, newIndex, depth);
19533 }
19534 break;
19535 case Node.TEXT_NODE:
19536 const value = currentNode.textContent || '';
19537 const hasBinding = value.match(BINDING_REGEXP);
19538 addCreateNodeAndAppend(create, null, hasBinding ? '' : value, parentIdx, newIndex);
19539 addRemoveNode(remove, newIndex, depth);
19540 if (hasBinding) {
19541 bindingMask =
19542 generateBindingUpdateOpCodes(update, value, newIndex, null, 0, null) | bindingMask;
19543 }
19544 break;
19545 case Node.COMMENT_NODE:
19546 // Check if the comment node is a placeholder for a nested ICU
19547 const isNestedIcu = NESTED_ICU.exec(currentNode.textContent || '');
19548 if (isNestedIcu) {
19549 const nestedIcuIndex = parseInt(isNestedIcu[1], 10);
19550 const icuExpression = nestedIcus[nestedIcuIndex];
19551 // Create the comment node that will anchor the ICU expression
19552 addCreateNodeAndAppend(create, ICU_MARKER, ngDevMode ? `nested ICU ${nestedIcuIndex}` : '', parentIdx, newIndex);
19553 icuStart(tView, lView, sharedUpdateOpCodes, parentIdx, icuExpression, newIndex);
19554 addRemoveNestedIcu(remove, newIndex, depth);
19555 }
19556 break;
19557 }
19558 currentNode = currentNode.nextSibling;
19559 }
19560 return bindingMask;
19561}
19562function addRemoveNode(remove, index, depth) {
19563 if (depth === 0) {
19564 remove.push(index);
19565 }
19566}
19567function addRemoveNestedIcu(remove, index, depth) {
19568 if (depth === 0) {
19569 remove.push(~index); // remove ICU at `index`
19570 remove.push(index); // remove ICU comment at `index`
19571 }
19572}
19573function addUpdateIcuSwitch(update, icuExpression, index) {
19574 update.push(toMaskBit(icuExpression.mainBinding), 2, -1 - icuExpression.mainBinding, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 2 /* I18nUpdateOpCode.IcuSwitch */);
19575}
19576function addUpdateIcuUpdate(update, bindingMask, index) {
19577 update.push(bindingMask, 1, index << 2 /* I18nUpdateOpCode.SHIFT_REF */ | 3 /* I18nUpdateOpCode.IcuUpdate */);
19578}
19579function addCreateNodeAndAppend(create, marker, text, appendToParentIdx, createAtIdx) {
19580 if (marker !== null) {
19581 create.push(marker);
19582 }
19583 create.push(text, createAtIdx, icuCreateOpCode(0 /* IcuCreateOpCode.AppendChild */, appendToParentIdx, createAtIdx));
19584}
19585function addCreateAttribute(create, newIndex, attr) {
19586 create.push(newIndex << 1 /* IcuCreateOpCode.SHIFT_REF */ | 1 /* IcuCreateOpCode.Attr */, attr.name, attr.value);
19587}
19588
19589// i18nPostprocess consts
19590const ROOT_TEMPLATE_ID = 0;
19591const PP_MULTI_VALUE_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]/;
19592const PP_PLACEHOLDERS_REGEXP = /\[(�.+?�?)\]|(�\/?\*\d+:\d+�)/g;
19593const PP_ICU_VARS_REGEXP = /({\s*)(VAR_(PLURAL|SELECT)(_\d+)?)(\s*,)/g;
19594const PP_ICU_PLACEHOLDERS_REGEXP = /{([A-Z0-9_]+)}/g;
19595const PP_ICUS_REGEXP = /�I18N_EXP_(ICU(_\d+)?)�/g;
19596const PP_CLOSE_TEMPLATE_REGEXP = /\/\*/;
19597const PP_TEMPLATE_ID_REGEXP = /\d+\:(\d+)/;
19598/**
19599 * Handles message string post-processing for internationalization.
19600 *
19601 * Handles message string post-processing by transforming it from intermediate
19602 * format (that might contain some markers that we need to replace) to the final
19603 * form, consumable by i18nStart instruction. Post processing steps include:
19604 *
19605 * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�])
19606 * 2. Replace all ICU vars (like "VAR_PLURAL")
19607 * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
19608 * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�)
19609 * in case multiple ICUs have the same placeholder name
19610 *
19611 * @param message Raw translation string for post processing
19612 * @param replacements Set of replacements that should be applied
19613 *
19614 * @returns Transformed string that can be consumed by i18nStart instruction
19615 *
19616 * @codeGenApi
19617 */
19618function i18nPostprocess(message, replacements = {}) {
19619 /**
19620 * Step 1: resolve all multi-value placeholders like [�#5�|�*1:1��#2:1�|�#4:1�]
19621 *
19622 * Note: due to the way we process nested templates (BFS), multi-value placeholders are typically
19623 * grouped by templates, for example: [�#5�|�#6�|�#1:1�|�#3:2�] where �#5� and �#6� belong to root
19624 * template, �#1:1� belong to nested template with index 1 and �#1:2� - nested template with index
19625 * 3. However in real templates the order might be different: i.e. �#1:1� and/or �#3:2� may go in
19626 * front of �#6�. The post processing step restores the right order by keeping track of the
19627 * template id stack and looks for placeholders that belong to the currently active template.
19628 */
19629 let result = message;
19630 if (PP_MULTI_VALUE_PLACEHOLDERS_REGEXP.test(message)) {
19631 const matches = {};
19632 const templateIdsStack = [ROOT_TEMPLATE_ID];
19633 result = result.replace(PP_PLACEHOLDERS_REGEXP, (m, phs, tmpl) => {
19634 const content = phs || tmpl;
19635 const placeholders = matches[content] || [];
19636 if (!placeholders.length) {
19637 content.split('|').forEach((placeholder) => {
19638 const match = placeholder.match(PP_TEMPLATE_ID_REGEXP);
19639 const templateId = match ? parseInt(match[1], 10) : ROOT_TEMPLATE_ID;
19640 const isCloseTemplateTag = PP_CLOSE_TEMPLATE_REGEXP.test(placeholder);
19641 placeholders.push([templateId, isCloseTemplateTag, placeholder]);
19642 });
19643 matches[content] = placeholders;
19644 }
19645 if (!placeholders.length) {
19646 throw new Error(`i18n postprocess: unmatched placeholder - ${content}`);
19647 }
19648 const currentTemplateId = templateIdsStack[templateIdsStack.length - 1];
19649 let idx = 0;
19650 // find placeholder index that matches current template id
19651 for (let i = 0; i < placeholders.length; i++) {
19652 if (placeholders[i][0] === currentTemplateId) {
19653 idx = i;
19654 break;
19655 }
19656 }
19657 // update template id stack based on the current tag extracted
19658 const [templateId, isCloseTemplateTag, placeholder] = placeholders[idx];
19659 if (isCloseTemplateTag) {
19660 templateIdsStack.pop();
19661 }
19662 else if (currentTemplateId !== templateId) {
19663 templateIdsStack.push(templateId);
19664 }
19665 // remove processed tag from the list
19666 placeholders.splice(idx, 1);
19667 return placeholder;
19668 });
19669 }
19670 // return current result if no replacements specified
19671 if (!Object.keys(replacements).length) {
19672 return result;
19673 }
19674 /**
19675 * Step 2: replace all ICU vars (like "VAR_PLURAL")
19676 */
19677 result = result.replace(PP_ICU_VARS_REGEXP, (match, start, key, _type, _idx, end) => {
19678 return replacements.hasOwnProperty(key) ? `${start}${replacements[key]}${end}` : match;
19679 });
19680 /**
19681 * Step 3: replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
19682 */
19683 result = result.replace(PP_ICU_PLACEHOLDERS_REGEXP, (match, key) => {
19684 return replacements.hasOwnProperty(key) ? replacements[key] : match;
19685 });
19686 /**
19687 * Step 4: replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�) in case
19688 * multiple ICUs have the same placeholder name
19689 */
19690 result = result.replace(PP_ICUS_REGEXP, (match, key) => {
19691 if (replacements.hasOwnProperty(key)) {
19692 const list = replacements[key];
19693 if (!list.length) {
19694 throw new Error(`i18n postprocess: unmatched ICU - ${match} with key: ${key}`);
19695 }
19696 return list.shift();
19697 }
19698 return match;
19699 });
19700 return result;
19701}
19702
19703/**
19704 * Marks a block of text as translatable.
19705 *
19706 * The instructions `i18nStart` and `i18nEnd` mark the translation block in the template.
19707 * The translation `message` is the value which is locale specific. The translation string may
19708 * contain placeholders which associate inner elements and sub-templates within the translation.
19709 *
19710 * The translation `message` placeholders are:
19711 * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be
19712 * interpolated into. The placeholder `index` points to the expression binding index. An optional
19713 * `block` that matches the sub-template in which it was declared.
19714 * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning
19715 * and end of DOM element that were embedded in the original translation block. The placeholder
19716 * `index` points to the element index in the template instructions set. An optional `block` that
19717 * matches the sub-template in which it was declared.
19718 * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be
19719 * split up and translated separately in each angular template function. The `index` points to the
19720 * `template` instruction index. A `block` that matches the sub-template in which it was declared.
19721 *
19722 * @param index A unique index of the translation in the static block.
19723 * @param messageIndex An index of the translation message from the `def.consts` array.
19724 * @param subTemplateIndex Optional sub-template index in the `message`.
19725 *
19726 * @codeGenApi
19727 */
19728function ɵɵi18nStart(index, messageIndex, subTemplateIndex = -1) {
19729 const tView = getTView();
19730 const lView = getLView();
19731 const adjustedIndex = HEADER_OFFSET + index;
19732 ngDevMode && assertDefined(tView, `tView should be defined`);
19733 const message = getConstant(tView.consts, messageIndex);
19734 const parentTNode = getCurrentParentTNode();
19735 if (tView.firstCreatePass) {
19736 i18nStartFirstCreatePass(tView, parentTNode === null ? 0 : parentTNode.index, lView, adjustedIndex, message, subTemplateIndex);
19737 }
19738 const tI18n = tView.data[adjustedIndex];
19739 const sameViewParentTNode = parentTNode === lView[T_HOST] ? null : parentTNode;
19740 const parentRNode = getClosestRElement(tView, sameViewParentTNode, lView);
19741 // If `parentTNode` is an `ElementContainer` than it has `<!--ng-container--->`.
19742 // When we do inserts we have to make sure to insert in front of `<!--ng-container--->`.
19743 const insertInFrontOf = parentTNode && (parentTNode.type & 8 /* TNodeType.ElementContainer */) ?
19744 lView[parentTNode.index] :
19745 null;
19746 applyCreateOpCodes(lView, tI18n.create, parentRNode, insertInFrontOf);
19747 setInI18nBlock(true);
19748}
19749/**
19750 * Translates a translation block marked by `i18nStart` and `i18nEnd`. It inserts the text/ICU nodes
19751 * into the render tree, moves the placeholder nodes and removes the deleted nodes.
19752 *
19753 * @codeGenApi
19754 */
19755function ɵɵi18nEnd() {
19756 setInI18nBlock(false);
19757}
19758/**
19759 *
19760 * Use this instruction to create a translation block that doesn't contain any placeholder.
19761 * It calls both {@link i18nStart} and {@link i18nEnd} in one instruction.
19762 *
19763 * The translation `message` is the value which is locale specific. The translation string may
19764 * contain placeholders which associate inner elements and sub-templates within the translation.
19765 *
19766 * The translation `message` placeholders are:
19767 * - `�{index}(:{block})�`: *Binding Placeholder*: Marks a location where an expression will be
19768 * interpolated into. The placeholder `index` points to the expression binding index. An optional
19769 * `block` that matches the sub-template in which it was declared.
19770 * - `�#{index}(:{block})�`/`�/#{index}(:{block})�`: *Element Placeholder*: Marks the beginning
19771 * and end of DOM element that were embedded in the original translation block. The placeholder
19772 * `index` points to the element index in the template instructions set. An optional `block` that
19773 * matches the sub-template in which it was declared.
19774 * - `�*{index}:{block}�`/`�/*{index}:{block}�`: *Sub-template Placeholder*: Sub-templates must be
19775 * split up and translated separately in each angular template function. The `index` points to the
19776 * `template` instruction index. A `block` that matches the sub-template in which it was declared.
19777 *
19778 * @param index A unique index of the translation in the static block.
19779 * @param messageIndex An index of the translation message from the `def.consts` array.
19780 * @param subTemplateIndex Optional sub-template index in the `message`.
19781 *
19782 * @codeGenApi
19783 */
19784function ɵɵi18n(index, messageIndex, subTemplateIndex) {
19785 ɵɵi18nStart(index, messageIndex, subTemplateIndex);
19786 ɵɵi18nEnd();
19787}
19788/**
19789 * Marks a list of attributes as translatable.
19790 *
19791 * @param index A unique index in the static block
19792 * @param values
19793 *
19794 * @codeGenApi
19795 */
19796function ɵɵi18nAttributes(index, attrsIndex) {
19797 const tView = getTView();
19798 ngDevMode && assertDefined(tView, `tView should be defined`);
19799 const attrs = getConstant(tView.consts, attrsIndex);
19800 i18nAttributesFirstPass(tView, index + HEADER_OFFSET, attrs);
19801}
19802/**
19803 * Stores the values of the bindings during each update cycle in order to determine if we need to
19804 * update the translated nodes.
19805 *
19806 * @param value The binding's value
19807 * @returns This function returns itself so that it may be chained
19808 * (e.g. `i18nExp(ctx.name)(ctx.title)`)
19809 *
19810 * @codeGenApi
19811 */
19812function ɵɵi18nExp(value) {
19813 const lView = getLView();
19814 setMaskBit(bindingUpdated(lView, nextBindingIndex(), value));
19815 return ɵɵi18nExp;
19816}
19817/**
19818 * Updates a translation block or an i18n attribute when the bindings have changed.
19819 *
19820 * @param index Index of either {@link i18nStart} (translation block) or {@link i18nAttributes}
19821 * (i18n attribute) on which it should update the content.
19822 *
19823 * @codeGenApi
19824 */
19825function ɵɵi18nApply(index) {
19826 applyI18n(getTView(), getLView(), index + HEADER_OFFSET);
19827}
19828/**
19829 * Handles message string post-processing for internationalization.
19830 *
19831 * Handles message string post-processing by transforming it from intermediate
19832 * format (that might contain some markers that we need to replace) to the final
19833 * form, consumable by i18nStart instruction. Post processing steps include:
19834 *
19835 * 1. Resolve all multi-value cases (like [�*1:1��#2:1�|�#4:1�|�5�])
19836 * 2. Replace all ICU vars (like "VAR_PLURAL")
19837 * 3. Replace all placeholders used inside ICUs in a form of {PLACEHOLDER}
19838 * 4. Replace all ICU references with corresponding values (like �ICU_EXP_ICU_1�)
19839 * in case multiple ICUs have the same placeholder name
19840 *
19841 * @param message Raw translation string for post processing
19842 * @param replacements Set of replacements that should be applied
19843 *
19844 * @returns Transformed string that can be consumed by i18nStart instruction
19845 *
19846 * @codeGenApi
19847 */
19848function ɵɵi18nPostprocess(message, replacements = {}) {
19849 return i18nPostprocess(message, replacements);
19850}
19851
19852/*
19853 * This file re-exports all symbols contained in this directory.
19854 *
19855 * Why is this file not `index.ts`?
19856 *
19857 * There seems to be an inconsistent path resolution of an `index.ts` file
19858 * when only the parent directory is referenced. This could be due to the
19859 * node module resolution configuration differing from rollup and/or typescript.
19860 *
19861 * With commit
19862 * https://github.com/angular/angular/commit/d5e3f2c64bd13ce83e7c70788b7fc514ca4a9918
19863 * the `instructions.ts` file was moved to `instructions/instructions.ts` and an
19864 * `index.ts` file was used to re-export everything. Having had file names that were
19865 * importing from `instructions' directly (not the from the sub file or the `index.ts`
19866 * file) caused strange CI issues. `index.ts` had to be renamed to `all.ts` for this
19867 * to work.
19868 *
19869 * Jira Issue = FW-1184
19870 */
19871
19872/**
19873 * Resolves the providers which are defined in the DirectiveDef.
19874 *
19875 * When inserting the tokens and the factories in their respective arrays, we can assume that
19876 * this method is called first for the component (if any), and then for other directives on the same
19877 * node.
19878 * As a consequence,the providers are always processed in that order:
19879 * 1) The view providers of the component
19880 * 2) The providers of the component
19881 * 3) The providers of the other directives
19882 * This matches the structure of the injectables arrays of a view (for each node).
19883 * So the tokens and the factories can be pushed at the end of the arrays, except
19884 * in one case for multi providers.
19885 *
19886 * @param def the directive definition
19887 * @param providers: Array of `providers`.
19888 * @param viewProviders: Array of `viewProviders`.
19889 */
19890function providersResolver(def, providers, viewProviders) {
19891 const tView = getTView();
19892 if (tView.firstCreatePass) {
19893 const isComponent = isComponentDef(def);
19894 // The list of view providers is processed first, and the flags are updated
19895 resolveProvider(viewProviders, tView.data, tView.blueprint, isComponent, true);
19896 // Then, the list of providers is processed, and the flags are updated
19897 resolveProvider(providers, tView.data, tView.blueprint, isComponent, false);
19898 }
19899}
19900/**
19901 * Resolves a provider and publishes it to the DI system.
19902 */
19903function resolveProvider(provider, tInjectables, lInjectablesBlueprint, isComponent, isViewProvider) {
19904 provider = resolveForwardRef(provider);
19905 if (Array.isArray(provider)) {
19906 // Recursively call `resolveProvider`
19907 // Recursion is OK in this case because this code will not be in hot-path once we implement
19908 // cloning of the initial state.
19909 for (let i = 0; i < provider.length; i++) {
19910 resolveProvider(provider[i], tInjectables, lInjectablesBlueprint, isComponent, isViewProvider);
19911 }
19912 }
19913 else {
19914 const tView = getTView();
19915 const lView = getLView();
19916 let token = isTypeProvider(provider) ? provider : resolveForwardRef(provider.provide);
19917 let providerFactory = providerToFactory(provider);
19918 const tNode = getCurrentTNode();
19919 const beginIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
19920 const endIndex = tNode.directiveStart;
19921 const cptViewProvidersCount = tNode.providerIndexes >> 20 /* TNodeProviderIndexes.CptViewProvidersCountShift */;
19922 if (isTypeProvider(provider) || !provider.multi) {
19923 // Single provider case: the factory is created and pushed immediately
19924 const factory = new NodeInjectorFactory(providerFactory, isViewProvider, ɵɵdirectiveInject);
19925 const existingFactoryIndex = indexOf(token, tInjectables, isViewProvider ? beginIndex : beginIndex + cptViewProvidersCount, endIndex);
19926 if (existingFactoryIndex === -1) {
19927 diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token);
19928 registerDestroyHooksIfSupported(tView, provider, tInjectables.length);
19929 tInjectables.push(token);
19930 tNode.directiveStart++;
19931 tNode.directiveEnd++;
19932 if (isViewProvider) {
19933 tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */;
19934 }
19935 lInjectablesBlueprint.push(factory);
19936 lView.push(factory);
19937 }
19938 else {
19939 lInjectablesBlueprint[existingFactoryIndex] = factory;
19940 lView[existingFactoryIndex] = factory;
19941 }
19942 }
19943 else {
19944 // Multi provider case:
19945 // We create a multi factory which is going to aggregate all the values.
19946 // Since the output of such a factory depends on content or view injection,
19947 // we create two of them, which are linked together.
19948 //
19949 // The first one (for view providers) is always in the first block of the injectables array,
19950 // and the second one (for providers) is always in the second block.
19951 // This is important because view providers have higher priority. When a multi token
19952 // is being looked up, the view providers should be found first.
19953 // Note that it is not possible to have a multi factory in the third block (directive block).
19954 //
19955 // The algorithm to process multi providers is as follows:
19956 // 1) If the multi provider comes from the `viewProviders` of the component:
19957 // a) If the special view providers factory doesn't exist, it is created and pushed.
19958 // b) Else, the multi provider is added to the existing multi factory.
19959 // 2) If the multi provider comes from the `providers` of the component or of another
19960 // directive:
19961 // a) If the multi factory doesn't exist, it is created and provider pushed into it.
19962 // It is also linked to the multi factory for view providers, if it exists.
19963 // b) Else, the multi provider is added to the existing multi factory.
19964 const existingProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex + cptViewProvidersCount, endIndex);
19965 const existingViewProvidersFactoryIndex = indexOf(token, tInjectables, beginIndex, beginIndex + cptViewProvidersCount);
19966 const doesProvidersFactoryExist = existingProvidersFactoryIndex >= 0 &&
19967 lInjectablesBlueprint[existingProvidersFactoryIndex];
19968 const doesViewProvidersFactoryExist = existingViewProvidersFactoryIndex >= 0 &&
19969 lInjectablesBlueprint[existingViewProvidersFactoryIndex];
19970 if (isViewProvider && !doesViewProvidersFactoryExist ||
19971 !isViewProvider && !doesProvidersFactoryExist) {
19972 // Cases 1.a and 2.a
19973 diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, lView), tView, token);
19974 const factory = multiFactory(isViewProvider ? multiViewProvidersFactoryResolver : multiProvidersFactoryResolver, lInjectablesBlueprint.length, isViewProvider, isComponent, providerFactory);
19975 if (!isViewProvider && doesViewProvidersFactoryExist) {
19976 lInjectablesBlueprint[existingViewProvidersFactoryIndex].providerFactory = factory;
19977 }
19978 registerDestroyHooksIfSupported(tView, provider, tInjectables.length, 0);
19979 tInjectables.push(token);
19980 tNode.directiveStart++;
19981 tNode.directiveEnd++;
19982 if (isViewProvider) {
19983 tNode.providerIndexes += 1048576 /* TNodeProviderIndexes.CptViewProvidersCountShifter */;
19984 }
19985 lInjectablesBlueprint.push(factory);
19986 lView.push(factory);
19987 }
19988 else {
19989 // Cases 1.b and 2.b
19990 const indexInFactory = multiFactoryAdd(lInjectablesBlueprint[isViewProvider ? existingViewProvidersFactoryIndex :
19991 existingProvidersFactoryIndex], providerFactory, !isViewProvider && isComponent);
19992 registerDestroyHooksIfSupported(tView, provider, existingProvidersFactoryIndex > -1 ? existingProvidersFactoryIndex :
19993 existingViewProvidersFactoryIndex, indexInFactory);
19994 }
19995 if (!isViewProvider && isComponent && doesViewProvidersFactoryExist) {
19996 lInjectablesBlueprint[existingViewProvidersFactoryIndex].componentProviders++;
19997 }
19998 }
19999 }
20000}
20001/**
20002 * Registers the `ngOnDestroy` hook of a provider, if the provider supports destroy hooks.
20003 * @param tView `TView` in which to register the hook.
20004 * @param provider Provider whose hook should be registered.
20005 * @param contextIndex Index under which to find the context for the hook when it's being invoked.
20006 * @param indexInFactory Only required for `multi` providers. Index of the provider in the multi
20007 * provider factory.
20008 */
20009function registerDestroyHooksIfSupported(tView, provider, contextIndex, indexInFactory) {
20010 const providerIsTypeProvider = isTypeProvider(provider);
20011 const providerIsClassProvider = isClassProvider(provider);
20012 if (providerIsTypeProvider || providerIsClassProvider) {
20013 // Resolve forward references as `useClass` can hold a forward reference.
20014 const classToken = providerIsClassProvider ? resolveForwardRef(provider.useClass) : provider;
20015 const prototype = classToken.prototype;
20016 const ngOnDestroy = prototype.ngOnDestroy;
20017 if (ngOnDestroy) {
20018 const hooks = tView.destroyHooks || (tView.destroyHooks = []);
20019 if (!providerIsTypeProvider && provider.multi) {
20020 ngDevMode &&
20021 assertDefined(indexInFactory, 'indexInFactory when registering multi factory destroy hook');
20022 const existingCallbacksIndex = hooks.indexOf(contextIndex);
20023 if (existingCallbacksIndex === -1) {
20024 hooks.push(contextIndex, [indexInFactory, ngOnDestroy]);
20025 }
20026 else {
20027 hooks[existingCallbacksIndex + 1].push(indexInFactory, ngOnDestroy);
20028 }
20029 }
20030 else {
20031 hooks.push(contextIndex, ngOnDestroy);
20032 }
20033 }
20034 }
20035}
20036/**
20037 * Add a factory in a multi factory.
20038 * @returns Index at which the factory was inserted.
20039 */
20040function multiFactoryAdd(multiFactory, factory, isComponentProvider) {
20041 if (isComponentProvider) {
20042 multiFactory.componentProviders++;
20043 }
20044 return multiFactory.multi.push(factory) - 1;
20045}
20046/**
20047 * Returns the index of item in the array, but only in the begin to end range.
20048 */
20049function indexOf(item, arr, begin, end) {
20050 for (let i = begin; i < end; i++) {
20051 if (arr[i] === item)
20052 return i;
20053 }
20054 return -1;
20055}
20056/**
20057 * Use this with `multi` `providers`.
20058 */
20059function multiProvidersFactoryResolver(_, tData, lData, tNode) {
20060 return multiResolve(this.multi, []);
20061}
20062/**
20063 * Use this with `multi` `viewProviders`.
20064 *
20065 * This factory knows how to concatenate itself with the existing `multi` `providers`.
20066 */
20067function multiViewProvidersFactoryResolver(_, tData, lView, tNode) {
20068 const factories = this.multi;
20069 let result;
20070 if (this.providerFactory) {
20071 const componentCount = this.providerFactory.componentProviders;
20072 const multiProviders = getNodeInjectable(lView, lView[TVIEW], this.providerFactory.index, tNode);
20073 // Copy the section of the array which contains `multi` `providers` from the component
20074 result = multiProviders.slice(0, componentCount);
20075 // Insert the `viewProvider` instances.
20076 multiResolve(factories, result);
20077 // Copy the section of the array which contains `multi` `providers` from other directives
20078 for (let i = componentCount; i < multiProviders.length; i++) {
20079 result.push(multiProviders[i]);
20080 }
20081 }
20082 else {
20083 result = [];
20084 // Insert the `viewProvider` instances.
20085 multiResolve(factories, result);
20086 }
20087 return result;
20088}
20089/**
20090 * Maps an array of factories into an array of values.
20091 */
20092function multiResolve(factories, result) {
20093 for (let i = 0; i < factories.length; i++) {
20094 const factory = factories[i];
20095 result.push(factory());
20096 }
20097 return result;
20098}
20099/**
20100 * Creates a multi factory.
20101 */
20102function multiFactory(factoryFn, index, isViewProvider, isComponent, f) {
20103 const factory = new NodeInjectorFactory(factoryFn, isViewProvider, ɵɵdirectiveInject);
20104 factory.multi = [];
20105 factory.index = index;
20106 factory.componentProviders = 0;
20107 multiFactoryAdd(factory, f, isComponent && !isViewProvider);
20108 return factory;
20109}
20110
20111/**
20112 * This feature resolves the providers of a directive (or component),
20113 * and publish them into the DI system, making it visible to others for injection.
20114 *
20115 * For example:
20116 * ```ts
20117 * class ComponentWithProviders {
20118 * constructor(private greeter: GreeterDE) {}
20119 *
20120 * static ɵcmp = defineComponent({
20121 * type: ComponentWithProviders,
20122 * selectors: [['component-with-providers']],
20123 * factory: () => new ComponentWithProviders(directiveInject(GreeterDE as any)),
20124 * decls: 1,
20125 * vars: 1,
20126 * template: function(fs: RenderFlags, ctx: ComponentWithProviders) {
20127 * if (fs & RenderFlags.Create) {
20128 * ɵɵtext(0);
20129 * }
20130 * if (fs & RenderFlags.Update) {
20131 * ɵɵtextInterpolate(ctx.greeter.greet());
20132 * }
20133 * },
20134 * features: [ɵɵProvidersFeature([GreeterDE])]
20135 * });
20136 * }
20137 * ```
20138 *
20139 * @param definition
20140 *
20141 * @codeGenApi
20142 */
20143function ɵɵProvidersFeature(providers, viewProviders = []) {
20144 return (definition) => {
20145 definition.providersResolver =
20146 (def, processProvidersFn) => {
20147 return providersResolver(def, //
20148 processProvidersFn ? processProvidersFn(providers) : providers, //
20149 viewProviders);
20150 };
20151 };
20152}
20153
20154/**
20155 * Represents an instance of an `NgModule` created by an `NgModuleFactory`.
20156 * Provides access to the `NgModule` instance and related objects.
20157 *
20158 * @publicApi
20159 */
20160class NgModuleRef$1 {
20161}
20162/**
20163 * @publicApi
20164 *
20165 * @deprecated
20166 * This class was mostly used as a part of ViewEngine-based JIT API and is no longer needed in Ivy
20167 * JIT mode. See [JIT API changes due to ViewEngine deprecation](guide/deprecations#jit-api-changes)
20168 * for additional context. Angular provides APIs that accept NgModule classes directly (such as
20169 * [PlatformRef.bootstrapModule](api/core/PlatformRef#bootstrapModule) and
20170 * [createNgModule](api/core/createNgModule)), consider switching to those APIs instead of
20171 * using factory-based ones.
20172 */
20173class NgModuleFactory$1 {
20174}
20175
20176/**
20177 * Returns a new NgModuleRef instance based on the NgModule class and parent injector provided.
20178 *
20179 * @param ngModule NgModule class.
20180 * @param parentInjector Optional injector instance to use as a parent for the module injector. If
20181 * not provided, `NullInjector` will be used instead.
20182 * @returns NgModuleRef that represents an NgModule instance.
20183 *
20184 * @publicApi
20185 */
20186function createNgModule(ngModule, parentInjector) {
20187 return new NgModuleRef(ngModule, parentInjector ?? null);
20188}
20189/**
20190 * The `createNgModule` function alias for backwards-compatibility.
20191 * Please avoid using it directly and use `createNgModule` instead.
20192 *
20193 * @deprecated Use `createNgModule` instead.
20194 */
20195const createNgModuleRef = createNgModule;
20196class NgModuleRef extends NgModuleRef$1 {
20197 constructor(ngModuleType, _parent) {
20198 super();
20199 this._parent = _parent;
20200 // tslint:disable-next-line:require-internal-with-underscore
20201 this._bootstrapComponents = [];
20202 this.destroyCbs = [];
20203 // When bootstrapping a module we have a dependency graph that looks like this:
20204 // ApplicationRef -> ComponentFactoryResolver -> NgModuleRef. The problem is that if the
20205 // module being resolved tries to inject the ComponentFactoryResolver, it'll create a
20206 // circular dependency which will result in a runtime error, because the injector doesn't
20207 // exist yet. We work around the issue by creating the ComponentFactoryResolver ourselves
20208 // and providing it, rather than letting the injector resolve it.
20209 this.componentFactoryResolver = new ComponentFactoryResolver(this);
20210 const ngModuleDef = getNgModuleDef(ngModuleType);
20211 ngDevMode &&
20212 assertDefined(ngModuleDef, `NgModule '${stringify(ngModuleType)}' is not a subtype of 'NgModuleType'.`);
20213 this._bootstrapComponents = maybeUnwrapFn$1(ngModuleDef.bootstrap);
20214 this._r3Injector = createInjectorWithoutInjectorInstances(ngModuleType, _parent, [
20215 { provide: NgModuleRef$1, useValue: this }, {
20216 provide: ComponentFactoryResolver$1,
20217 useValue: this.componentFactoryResolver
20218 }
20219 ], stringify(ngModuleType), new Set(['environment']));
20220 // We need to resolve the injector types separately from the injector creation, because
20221 // the module might be trying to use this ref in its constructor for DI which will cause a
20222 // circular error that will eventually error out, because the injector isn't created yet.
20223 this._r3Injector.resolveInjectorInitializers();
20224 this.instance = this._r3Injector.get(ngModuleType);
20225 }
20226 get injector() {
20227 return this._r3Injector;
20228 }
20229 destroy() {
20230 ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
20231 const injector = this._r3Injector;
20232 !injector.destroyed && injector.destroy();
20233 this.destroyCbs.forEach(fn => fn());
20234 this.destroyCbs = null;
20235 }
20236 onDestroy(callback) {
20237 ngDevMode && assertDefined(this.destroyCbs, 'NgModule already destroyed');
20238 this.destroyCbs.push(callback);
20239 }
20240}
20241class NgModuleFactory extends NgModuleFactory$1 {
20242 constructor(moduleType) {
20243 super();
20244 this.moduleType = moduleType;
20245 }
20246 create(parentInjector) {
20247 return new NgModuleRef(this.moduleType, parentInjector);
20248 }
20249}
20250class EnvironmentNgModuleRefAdapter extends NgModuleRef$1 {
20251 constructor(providers, parent, source) {
20252 super();
20253 this.componentFactoryResolver = new ComponentFactoryResolver(this);
20254 this.instance = null;
20255 const injector = new R3Injector([
20256 ...providers,
20257 { provide: NgModuleRef$1, useValue: this },
20258 { provide: ComponentFactoryResolver$1, useValue: this.componentFactoryResolver },
20259 ], parent || getNullInjector(), source, new Set(['environment']));
20260 this.injector = injector;
20261 injector.resolveInjectorInitializers();
20262 }
20263 destroy() {
20264 this.injector.destroy();
20265 }
20266 onDestroy(callback) {
20267 this.injector.onDestroy(callback);
20268 }
20269}
20270/**
20271 * Create a new environment injector.
20272 *
20273 * Learn more about environment injectors in
20274 * [this guide](guide/standalone-components#environment-injectors).
20275 *
20276 * @param providers An array of providers.
20277 * @param parent A parent environment injector.
20278 * @param debugName An optional name for this injector instance, which will be used in error
20279 * messages.
20280 *
20281 * @publicApi
20282 */
20283function createEnvironmentInjector(providers, parent, debugName = null) {
20284 const adapter = new EnvironmentNgModuleRefAdapter(providers, parent, debugName);
20285 return adapter.injector;
20286}
20287
20288/**
20289 * A service used by the framework to create instances of standalone injectors. Those injectors are
20290 * created on demand in case of dynamic component instantiation and contain ambient providers
20291 * collected from the imports graph rooted at a given standalone component.
20292 */
20293class StandaloneService {
20294 constructor(_injector) {
20295 this._injector = _injector;
20296 this.cachedInjectors = new Map();
20297 }
20298 getOrCreateStandaloneInjector(componentDef) {
20299 if (!componentDef.standalone) {
20300 return null;
20301 }
20302 if (!this.cachedInjectors.has(componentDef.id)) {
20303 const providers = internalImportProvidersFrom(false, componentDef.type);
20304 const standaloneInjector = providers.length > 0 ?
20305 createEnvironmentInjector([providers], this._injector, `Standalone[${componentDef.type.name}]`) :
20306 null;
20307 this.cachedInjectors.set(componentDef.id, standaloneInjector);
20308 }
20309 return this.cachedInjectors.get(componentDef.id);
20310 }
20311 ngOnDestroy() {
20312 try {
20313 for (const injector of this.cachedInjectors.values()) {
20314 if (injector !== null) {
20315 injector.destroy();
20316 }
20317 }
20318 }
20319 finally {
20320 this.cachedInjectors.clear();
20321 }
20322 }
20323}
20324/** @nocollapse */
20325StandaloneService.ɵprov = ɵɵdefineInjectable({
20326 token: StandaloneService,
20327 providedIn: 'environment',
20328 factory: () => new StandaloneService(ɵɵinject(EnvironmentInjector)),
20329});
20330/**
20331 * A feature that acts as a setup code for the {@link StandaloneService}.
20332 *
20333 * The most important responsibility of this feature is to expose the "getStandaloneInjector"
20334 * function (an entry points to a standalone injector creation) on a component definition object. We
20335 * go through the features infrastructure to make sure that the standalone injector creation logic
20336 * is tree-shakable and not included in applications that don't use standalone components.
20337 *
20338 * @codeGenApi
20339 */
20340function ɵɵStandaloneFeature(definition) {
20341 definition.getStandaloneInjector = (parentInjector) => {
20342 return parentInjector.get(StandaloneService).getOrCreateStandaloneInjector(definition);
20343 };
20344}
20345
20346/**
20347 * Retrieves the component instance associated with a given DOM element.
20348 *
20349 * @usageNotes
20350 * Given the following DOM structure:
20351 *
20352 * ```html
20353 * <app-root>
20354 * <div>
20355 * <child-comp></child-comp>
20356 * </div>
20357 * </app-root>
20358 * ```
20359 *
20360 * Calling `getComponent` on `<child-comp>` will return the instance of `ChildComponent`
20361 * associated with this DOM element.
20362 *
20363 * Calling the function on `<app-root>` will return the `MyApp` instance.
20364 *
20365 *
20366 * @param element DOM element from which the component should be retrieved.
20367 * @returns Component instance associated with the element or `null` if there
20368 * is no component associated with it.
20369 *
20370 * @publicApi
20371 * @globalApi ng
20372 */
20373function getComponent(element) {
20374 ngDevMode && assertDomElement(element);
20375 const context = getLContext(element);
20376 if (context === null)
20377 return null;
20378 if (context.component === undefined) {
20379 const lView = context.lView;
20380 if (lView === null) {
20381 return null;
20382 }
20383 context.component = getComponentAtNodeIndex(context.nodeIndex, lView);
20384 }
20385 return context.component;
20386}
20387/**
20388 * If inside an embedded view (e.g. `*ngIf` or `*ngFor`), retrieves the context of the embedded
20389 * view that the element is part of. Otherwise retrieves the instance of the component whose view
20390 * owns the element (in this case, the result is the same as calling `getOwningComponent`).
20391 *
20392 * @param element Element for which to get the surrounding component instance.
20393 * @returns Instance of the component that is around the element or null if the element isn't
20394 * inside any component.
20395 *
20396 * @publicApi
20397 * @globalApi ng
20398 */
20399function getContext(element) {
20400 assertDomElement(element);
20401 const context = getLContext(element);
20402 const lView = context ? context.lView : null;
20403 return lView === null ? null : lView[CONTEXT];
20404}
20405/**
20406 * Retrieves the component instance whose view contains the DOM element.
20407 *
20408 * For example, if `<child-comp>` is used in the template of `<app-comp>`
20409 * (i.e. a `ViewChild` of `<app-comp>`), calling `getOwningComponent` on `<child-comp>`
20410 * would return `<app-comp>`.
20411 *
20412 * @param elementOrDir DOM element, component or directive instance
20413 * for which to retrieve the root components.
20414 * @returns Component instance whose view owns the DOM element or null if the element is not
20415 * part of a component view.
20416 *
20417 * @publicApi
20418 * @globalApi ng
20419 */
20420function getOwningComponent(elementOrDir) {
20421 const context = getLContext(elementOrDir);
20422 let lView = context ? context.lView : null;
20423 if (lView === null)
20424 return null;
20425 let parent;
20426 while (lView[TVIEW].type === 2 /* TViewType.Embedded */ && (parent = getLViewParent(lView))) {
20427 lView = parent;
20428 }
20429 return lView[FLAGS] & 256 /* LViewFlags.IsRoot */ ? null : lView[CONTEXT];
20430}
20431/**
20432 * Retrieves all root components associated with a DOM element, directive or component instance.
20433 * Root components are those which have been bootstrapped by Angular.
20434 *
20435 * @param elementOrDir DOM element, component or directive instance
20436 * for which to retrieve the root components.
20437 * @returns Root components associated with the target object.
20438 *
20439 * @publicApi
20440 * @globalApi ng
20441 */
20442function getRootComponents(elementOrDir) {
20443 const lView = readPatchedLView(elementOrDir);
20444 return lView !== null ? [getRootContext(lView)] : [];
20445}
20446/**
20447 * Retrieves an `Injector` associated with an element, component or directive instance.
20448 *
20449 * @param elementOrDir DOM element, component or directive instance for which to
20450 * retrieve the injector.
20451 * @returns Injector associated with the element, component or directive instance.
20452 *
20453 * @publicApi
20454 * @globalApi ng
20455 */
20456function getInjector(elementOrDir) {
20457 const context = getLContext(elementOrDir);
20458 const lView = context ? context.lView : null;
20459 if (lView === null)
20460 return Injector.NULL;
20461 const tNode = lView[TVIEW].data[context.nodeIndex];
20462 return new NodeInjector(tNode, lView);
20463}
20464/**
20465 * Retrieve a set of injection tokens at a given DOM node.
20466 *
20467 * @param element Element for which the injection tokens should be retrieved.
20468 */
20469function getInjectionTokens(element) {
20470 const context = getLContext(element);
20471 const lView = context ? context.lView : null;
20472 if (lView === null)
20473 return [];
20474 const tView = lView[TVIEW];
20475 const tNode = tView.data[context.nodeIndex];
20476 const providerTokens = [];
20477 const startIndex = tNode.providerIndexes & 1048575 /* TNodeProviderIndexes.ProvidersStartIndexMask */;
20478 const endIndex = tNode.directiveEnd;
20479 for (let i = startIndex; i < endIndex; i++) {
20480 let value = tView.data[i];
20481 if (isDirectiveDefHack(value)) {
20482 // The fact that we sometimes store Type and sometimes DirectiveDef in this location is a
20483 // design flaw. We should always store same type so that we can be monomorphic. The issue
20484 // is that for Components/Directives we store the def instead the type. The correct behavior
20485 // is that we should always be storing injectable type in this location.
20486 value = value.type;
20487 }
20488 providerTokens.push(value);
20489 }
20490 return providerTokens;
20491}
20492/**
20493 * Retrieves directive instances associated with a given DOM node. Does not include
20494 * component instances.
20495 *
20496 * @usageNotes
20497 * Given the following DOM structure:
20498 *
20499 * ```html
20500 * <app-root>
20501 * <button my-button></button>
20502 * <my-comp></my-comp>
20503 * </app-root>
20504 * ```
20505 *
20506 * Calling `getDirectives` on `<button>` will return an array with an instance of the `MyButton`
20507 * directive that is associated with the DOM node.
20508 *
20509 * Calling `getDirectives` on `<my-comp>` will return an empty array.
20510 *
20511 * @param node DOM node for which to get the directives.
20512 * @returns Array of directives associated with the node.
20513 *
20514 * @publicApi
20515 * @globalApi ng
20516 */
20517function getDirectives(node) {
20518 // Skip text nodes because we can't have directives associated with them.
20519 if (node instanceof Text) {
20520 return [];
20521 }
20522 const context = getLContext(node);
20523 const lView = context ? context.lView : null;
20524 if (lView === null) {
20525 return [];
20526 }
20527 const tView = lView[TVIEW];
20528 const nodeIndex = context.nodeIndex;
20529 if (!tView?.data[nodeIndex]) {
20530 return [];
20531 }
20532 if (context.directives === undefined) {
20533 context.directives = getDirectivesAtNodeIndex(nodeIndex, lView);
20534 }
20535 // The `directives` in this case are a named array called `LComponentView`. Clone the
20536 // result so we don't expose an internal data structure in the user's console.
20537 return context.directives === null ? [] : [...context.directives];
20538}
20539/**
20540 * Returns the debug (partial) metadata for a particular directive or component instance.
20541 * The function accepts an instance of a directive or component and returns the corresponding
20542 * metadata.
20543 *
20544 * @param directiveOrComponentInstance Instance of a directive or component
20545 * @returns metadata of the passed directive or component
20546 *
20547 * @publicApi
20548 * @globalApi ng
20549 */
20550function getDirectiveMetadata(directiveOrComponentInstance) {
20551 const { constructor } = directiveOrComponentInstance;
20552 if (!constructor) {
20553 throw new Error('Unable to find the instance constructor');
20554 }
20555 // In case a component inherits from a directive, we may have component and directive metadata
20556 // To ensure we don't get the metadata of the directive, we want to call `getComponentDef` first.
20557 const componentDef = getComponentDef$1(constructor);
20558 if (componentDef) {
20559 return {
20560 inputs: componentDef.inputs,
20561 outputs: componentDef.outputs,
20562 encapsulation: componentDef.encapsulation,
20563 changeDetection: componentDef.onPush ? ChangeDetectionStrategy.OnPush :
20564 ChangeDetectionStrategy.Default
20565 };
20566 }
20567 const directiveDef = getDirectiveDef(constructor);
20568 if (directiveDef) {
20569 return { inputs: directiveDef.inputs, outputs: directiveDef.outputs };
20570 }
20571 return null;
20572}
20573/**
20574 * Retrieve map of local references.
20575 *
20576 * The references are retrieved as a map of local reference name to element or directive instance.
20577 *
20578 * @param target DOM element, component or directive instance for which to retrieve
20579 * the local references.
20580 */
20581function getLocalRefs(target) {
20582 const context = getLContext(target);
20583 if (context === null)
20584 return {};
20585 if (context.localRefs === undefined) {
20586 const lView = context.lView;
20587 if (lView === null) {
20588 return {};
20589 }
20590 context.localRefs = discoverLocalRefs(lView, context.nodeIndex);
20591 }
20592 return context.localRefs || {};
20593}
20594/**
20595 * Retrieves the host element of a component or directive instance.
20596 * The host element is the DOM element that matched the selector of the directive.
20597 *
20598 * @param componentOrDirective Component or directive instance for which the host
20599 * element should be retrieved.
20600 * @returns Host element of the target.
20601 *
20602 * @publicApi
20603 * @globalApi ng
20604 */
20605function getHostElement(componentOrDirective) {
20606 return getLContext(componentOrDirective).native;
20607}
20608/**
20609 * Retrieves the rendered text for a given component.
20610 *
20611 * This function retrieves the host element of a component and
20612 * and then returns the `textContent` for that element. This implies
20613 * that the text returned will include re-projected content of
20614 * the component as well.
20615 *
20616 * @param component The component to return the content text for.
20617 */
20618function getRenderedText(component) {
20619 const hostElement = getHostElement(component);
20620 return hostElement.textContent || '';
20621}
20622/**
20623 * Retrieves a list of event listeners associated with a DOM element. The list does include host
20624 * listeners, but it does not include event listeners defined outside of the Angular context
20625 * (e.g. through `addEventListener`).
20626 *
20627 * @usageNotes
20628 * Given the following DOM structure:
20629 *
20630 * ```html
20631 * <app-root>
20632 * <div (click)="doSomething()"></div>
20633 * </app-root>
20634 * ```
20635 *
20636 * Calling `getListeners` on `<div>` will return an object that looks as follows:
20637 *
20638 * ```ts
20639 * {
20640 * name: 'click',
20641 * element: <div>,
20642 * callback: () => doSomething(),
20643 * useCapture: false
20644 * }
20645 * ```
20646 *
20647 * @param element Element for which the DOM listeners should be retrieved.
20648 * @returns Array of event listeners on the DOM element.
20649 *
20650 * @publicApi
20651 * @globalApi ng
20652 */
20653function getListeners(element) {
20654 ngDevMode && assertDomElement(element);
20655 const lContext = getLContext(element);
20656 const lView = lContext === null ? null : lContext.lView;
20657 if (lView === null)
20658 return [];
20659 const tView = lView[TVIEW];
20660 const lCleanup = lView[CLEANUP];
20661 const tCleanup = tView.cleanup;
20662 const listeners = [];
20663 if (tCleanup && lCleanup) {
20664 for (let i = 0; i < tCleanup.length;) {
20665 const firstParam = tCleanup[i++];
20666 const secondParam = tCleanup[i++];
20667 if (typeof firstParam === 'string') {
20668 const name = firstParam;
20669 const listenerElement = unwrapRNode(lView[secondParam]);
20670 const callback = lCleanup[tCleanup[i++]];
20671 const useCaptureOrIndx = tCleanup[i++];
20672 // if useCaptureOrIndx is boolean then report it as is.
20673 // if useCaptureOrIndx is positive number then it in unsubscribe method
20674 // if useCaptureOrIndx is negative number then it is a Subscription
20675 const type = (typeof useCaptureOrIndx === 'boolean' || useCaptureOrIndx >= 0) ? 'dom' : 'output';
20676 const useCapture = typeof useCaptureOrIndx === 'boolean' ? useCaptureOrIndx : false;
20677 if (element == listenerElement) {
20678 listeners.push({ element, name, callback, useCapture, type });
20679 }
20680 }
20681 }
20682 }
20683 listeners.sort(sortListeners);
20684 return listeners;
20685}
20686function sortListeners(a, b) {
20687 if (a.name == b.name)
20688 return 0;
20689 return a.name < b.name ? -1 : 1;
20690}
20691/**
20692 * This function should not exist because it is megamorphic and only mostly correct.
20693 *
20694 * See call site for more info.
20695 */
20696function isDirectiveDefHack(obj) {
20697 return obj.type !== undefined && obj.template !== undefined && obj.declaredInputs !== undefined;
20698}
20699/**
20700 * Retrieve the component `LView` from component/element.
20701 *
20702 * NOTE: `LView` is a private and should not be leaked outside.
20703 * Don't export this method to `ng.*` on window.
20704 *
20705 * @param target DOM element or component instance for which to retrieve the LView.
20706 */
20707function getComponentLView(target) {
20708 const lContext = getLContext(target);
20709 const nodeIndx = lContext.nodeIndex;
20710 const lView = lContext.lView;
20711 ngDevMode && assertLView(lView);
20712 const componentLView = lView[nodeIndx];
20713 ngDevMode && assertLView(componentLView);
20714 return componentLView;
20715}
20716/** Asserts that a value is a DOM Element. */
20717function assertDomElement(value) {
20718 if (typeof Element !== 'undefined' && !(value instanceof Element)) {
20719 throw new Error('Expecting instance of DOM Element');
20720 }
20721}
20722
20723/**
20724 * Adds decorator, constructor, and property metadata to a given type via static metadata fields
20725 * on the type.
20726 *
20727 * These metadata fields can later be read with Angular's `ReflectionCapabilities` API.
20728 *
20729 * Calls to `setClassMetadata` can be guarded by ngDevMode, resulting in the metadata assignments
20730 * being tree-shaken away during production builds.
20731 */
20732function setClassMetadata(type, decorators, ctorParameters, propDecorators) {
20733 return noSideEffects(() => {
20734 const clazz = type;
20735 if (decorators !== null) {
20736 if (clazz.hasOwnProperty('decorators') && clazz.decorators !== undefined) {
20737 clazz.decorators.push(...decorators);
20738 }
20739 else {
20740 clazz.decorators = decorators;
20741 }
20742 }
20743 if (ctorParameters !== null) {
20744 // Rather than merging, clobber the existing parameters. If other projects exist which
20745 // use tsickle-style annotations and reflect over them in the same way, this could
20746 // cause issues, but that is vanishingly unlikely.
20747 clazz.ctorParameters = ctorParameters;
20748 }
20749 if (propDecorators !== null) {
20750 // The property decorator objects are merged as it is possible different fields have
20751 // different decorator types. Decorators on individual fields are not merged, as it's
20752 // also incredibly unlikely that a field will be decorated both with an Angular
20753 // decorator and a non-Angular decorator that's also been downleveled.
20754 if (clazz.hasOwnProperty('propDecorators') && clazz.propDecorators !== undefined) {
20755 clazz.propDecorators = { ...clazz.propDecorators, ...propDecorators };
20756 }
20757 else {
20758 clazz.propDecorators = propDecorators;
20759 }
20760 }
20761 });
20762}
20763
20764/**
20765 * Bindings for pure functions are stored after regular bindings.
20766 *
20767 * |-------decls------|---------vars---------| |----- hostVars (dir1) ------|
20768 * ------------------------------------------------------------------------------------------
20769 * | nodes/refs/pipes | bindings | fn slots | injector | dir1 | host bindings | host slots |
20770 * ------------------------------------------------------------------------------------------
20771 * ^ ^
20772 * TView.bindingStartIndex TView.expandoStartIndex
20773 *
20774 * Pure function instructions are given an offset from the binding root. Adding the offset to the
20775 * binding root gives the first index where the bindings are stored. In component views, the binding
20776 * root is the bindingStartIndex. In host bindings, the binding root is the expandoStartIndex +
20777 * any directive instances + any hostVars in directives evaluated before it.
20778 *
20779 * See VIEW_DATA.md for more information about host binding resolution.
20780 */
20781/**
20782 * If the value hasn't been saved, calls the pure function to store and return the
20783 * value. If it has been saved, returns the saved value.
20784 *
20785 * @param slotOffset the offset from binding root to the reserved slot
20786 * @param pureFn Function that returns a value
20787 * @param thisArg Optional calling context of pureFn
20788 * @returns value
20789 *
20790 * @codeGenApi
20791 */
20792function ɵɵpureFunction0(slotOffset, pureFn, thisArg) {
20793 const bindingIndex = getBindingRoot() + slotOffset;
20794 const lView = getLView();
20795 return lView[bindingIndex] === NO_CHANGE ?
20796 updateBinding(lView, bindingIndex, thisArg ? pureFn.call(thisArg) : pureFn()) :
20797 getBinding(lView, bindingIndex);
20798}
20799/**
20800 * If the value of the provided exp has changed, calls the pure function to return
20801 * an updated value. Or if the value has not changed, returns cached value.
20802 *
20803 * @param slotOffset the offset from binding root to the reserved slot
20804 * @param pureFn Function that returns an updated value
20805 * @param exp Updated expression value
20806 * @param thisArg Optional calling context of pureFn
20807 * @returns Updated or cached value
20808 *
20809 * @codeGenApi
20810 */
20811function ɵɵpureFunction1(slotOffset, pureFn, exp, thisArg) {
20812 return pureFunction1Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp, thisArg);
20813}
20814/**
20815 * If the value of any provided exp has changed, calls the pure function to return
20816 * an updated value. Or if no values have changed, returns cached value.
20817 *
20818 * @param slotOffset the offset from binding root to the reserved slot
20819 * @param pureFn
20820 * @param exp1
20821 * @param exp2
20822 * @param thisArg Optional calling context of pureFn
20823 * @returns Updated or cached value
20824 *
20825 * @codeGenApi
20826 */
20827function ɵɵpureFunction2(slotOffset, pureFn, exp1, exp2, thisArg) {
20828 return pureFunction2Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, thisArg);
20829}
20830/**
20831 * If the value of any provided exp has changed, calls the pure function to return
20832 * an updated value. Or if no values have changed, returns cached value.
20833 *
20834 * @param slotOffset the offset from binding root to the reserved slot
20835 * @param pureFn
20836 * @param exp1
20837 * @param exp2
20838 * @param exp3
20839 * @param thisArg Optional calling context of pureFn
20840 * @returns Updated or cached value
20841 *
20842 * @codeGenApi
20843 */
20844function ɵɵpureFunction3(slotOffset, pureFn, exp1, exp2, exp3, thisArg) {
20845 return pureFunction3Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, thisArg);
20846}
20847/**
20848 * If the value of any provided exp has changed, calls the pure function to return
20849 * an updated value. Or if no values have changed, returns cached value.
20850 *
20851 * @param slotOffset the offset from binding root to the reserved slot
20852 * @param pureFn
20853 * @param exp1
20854 * @param exp2
20855 * @param exp3
20856 * @param exp4
20857 * @param thisArg Optional calling context of pureFn
20858 * @returns Updated or cached value
20859 *
20860 * @codeGenApi
20861 */
20862function ɵɵpureFunction4(slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) {
20863 return pureFunction4Internal(getLView(), getBindingRoot(), slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg);
20864}
20865/**
20866 * If the value of any provided exp has changed, calls the pure function to return
20867 * an updated value. Or if no values have changed, returns cached value.
20868 *
20869 * @param slotOffset the offset from binding root to the reserved slot
20870 * @param pureFn
20871 * @param exp1
20872 * @param exp2
20873 * @param exp3
20874 * @param exp4
20875 * @param exp5
20876 * @param thisArg Optional calling context of pureFn
20877 * @returns Updated or cached value
20878 *
20879 * @codeGenApi
20880 */
20881function ɵɵpureFunction5(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, thisArg) {
20882 const bindingIndex = getBindingRoot() + slotOffset;
20883 const lView = getLView();
20884 const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
20885 return bindingUpdated(lView, bindingIndex + 4, exp5) || different ?
20886 updateBinding(lView, bindingIndex + 5, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5) :
20887 pureFn(exp1, exp2, exp3, exp4, exp5)) :
20888 getBinding(lView, bindingIndex + 5);
20889}
20890/**
20891 * If the value of any provided exp has changed, calls the pure function to return
20892 * an updated value. Or if no values have changed, returns cached value.
20893 *
20894 * @param slotOffset the offset from binding root to the reserved slot
20895 * @param pureFn
20896 * @param exp1
20897 * @param exp2
20898 * @param exp3
20899 * @param exp4
20900 * @param exp5
20901 * @param exp6
20902 * @param thisArg Optional calling context of pureFn
20903 * @returns Updated or cached value
20904 *
20905 * @codeGenApi
20906 */
20907function ɵɵpureFunction6(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, thisArg) {
20908 const bindingIndex = getBindingRoot() + slotOffset;
20909 const lView = getLView();
20910 const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
20911 return bindingUpdated2(lView, bindingIndex + 4, exp5, exp6) || different ?
20912 updateBinding(lView, bindingIndex + 6, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6) :
20913 pureFn(exp1, exp2, exp3, exp4, exp5, exp6)) :
20914 getBinding(lView, bindingIndex + 6);
20915}
20916/**
20917 * If the value of any provided exp has changed, calls the pure function to return
20918 * an updated value. Or if no values have changed, returns cached value.
20919 *
20920 * @param slotOffset the offset from binding root to the reserved slot
20921 * @param pureFn
20922 * @param exp1
20923 * @param exp2
20924 * @param exp3
20925 * @param exp4
20926 * @param exp5
20927 * @param exp6
20928 * @param exp7
20929 * @param thisArg Optional calling context of pureFn
20930 * @returns Updated or cached value
20931 *
20932 * @codeGenApi
20933 */
20934function ɵɵpureFunction7(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, thisArg) {
20935 const bindingIndex = getBindingRoot() + slotOffset;
20936 const lView = getLView();
20937 let different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
20938 return bindingUpdated3(lView, bindingIndex + 4, exp5, exp6, exp7) || different ?
20939 updateBinding(lView, bindingIndex + 7, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7) :
20940 pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7)) :
20941 getBinding(lView, bindingIndex + 7);
20942}
20943/**
20944 * If the value of any provided exp has changed, calls the pure function to return
20945 * an updated value. Or if no values have changed, returns cached value.
20946 *
20947 * @param slotOffset the offset from binding root to the reserved slot
20948 * @param pureFn
20949 * @param exp1
20950 * @param exp2
20951 * @param exp3
20952 * @param exp4
20953 * @param exp5
20954 * @param exp6
20955 * @param exp7
20956 * @param exp8
20957 * @param thisArg Optional calling context of pureFn
20958 * @returns Updated or cached value
20959 *
20960 * @codeGenApi
20961 */
20962function ɵɵpureFunction8(slotOffset, pureFn, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8, thisArg) {
20963 const bindingIndex = getBindingRoot() + slotOffset;
20964 const lView = getLView();
20965 const different = bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4);
20966 return bindingUpdated4(lView, bindingIndex + 4, exp5, exp6, exp7, exp8) || different ?
20967 updateBinding(lView, bindingIndex + 8, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8) :
20968 pureFn(exp1, exp2, exp3, exp4, exp5, exp6, exp7, exp8)) :
20969 getBinding(lView, bindingIndex + 8);
20970}
20971/**
20972 * pureFunction instruction that can support any number of bindings.
20973 *
20974 * If the value of any provided exp has changed, calls the pure function to return
20975 * an updated value. Or if no values have changed, returns cached value.
20976 *
20977 * @param slotOffset the offset from binding root to the reserved slot
20978 * @param pureFn A pure function that takes binding values and builds an object or array
20979 * containing those values.
20980 * @param exps An array of binding values
20981 * @param thisArg Optional calling context of pureFn
20982 * @returns Updated or cached value
20983 *
20984 * @codeGenApi
20985 */
20986function ɵɵpureFunctionV(slotOffset, pureFn, exps, thisArg) {
20987 return pureFunctionVInternal(getLView(), getBindingRoot(), slotOffset, pureFn, exps, thisArg);
20988}
20989/**
20990 * Results of a pure function invocation are stored in LView in a dedicated slot that is initialized
20991 * to NO_CHANGE. In rare situations a pure pipe might throw an exception on the very first
20992 * invocation and not produce any valid results. In this case LView would keep holding the NO_CHANGE
20993 * value. The NO_CHANGE is not something that we can use in expressions / bindings thus we convert
20994 * it to `undefined`.
20995 */
20996function getPureFunctionReturnValue(lView, returnValueIndex) {
20997 ngDevMode && assertIndexInRange(lView, returnValueIndex);
20998 const lastReturnValue = lView[returnValueIndex];
20999 return lastReturnValue === NO_CHANGE ? undefined : lastReturnValue;
21000}
21001/**
21002 * If the value of the provided exp has changed, calls the pure function to return
21003 * an updated value. Or if the value has not changed, returns cached value.
21004 *
21005 * @param lView LView in which the function is being executed.
21006 * @param bindingRoot Binding root index.
21007 * @param slotOffset the offset from binding root to the reserved slot
21008 * @param pureFn Function that returns an updated value
21009 * @param exp Updated expression value
21010 * @param thisArg Optional calling context of pureFn
21011 * @returns Updated or cached value
21012 */
21013function pureFunction1Internal(lView, bindingRoot, slotOffset, pureFn, exp, thisArg) {
21014 const bindingIndex = bindingRoot + slotOffset;
21015 return bindingUpdated(lView, bindingIndex, exp) ?
21016 updateBinding(lView, bindingIndex + 1, thisArg ? pureFn.call(thisArg, exp) : pureFn(exp)) :
21017 getPureFunctionReturnValue(lView, bindingIndex + 1);
21018}
21019/**
21020 * If the value of any provided exp has changed, calls the pure function to return
21021 * an updated value. Or if no values have changed, returns cached value.
21022 *
21023 * @param lView LView in which the function is being executed.
21024 * @param bindingRoot Binding root index.
21025 * @param slotOffset the offset from binding root to the reserved slot
21026 * @param pureFn
21027 * @param exp1
21028 * @param exp2
21029 * @param thisArg Optional calling context of pureFn
21030 * @returns Updated or cached value
21031 */
21032function pureFunction2Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, thisArg) {
21033 const bindingIndex = bindingRoot + slotOffset;
21034 return bindingUpdated2(lView, bindingIndex, exp1, exp2) ?
21035 updateBinding(lView, bindingIndex + 2, thisArg ? pureFn.call(thisArg, exp1, exp2) : pureFn(exp1, exp2)) :
21036 getPureFunctionReturnValue(lView, bindingIndex + 2);
21037}
21038/**
21039 * If the value of any provided exp has changed, calls the pure function to return
21040 * an updated value. Or if no values have changed, returns cached value.
21041 *
21042 * @param lView LView in which the function is being executed.
21043 * @param bindingRoot Binding root index.
21044 * @param slotOffset the offset from binding root to the reserved slot
21045 * @param pureFn
21046 * @param exp1
21047 * @param exp2
21048 * @param exp3
21049 * @param thisArg Optional calling context of pureFn
21050 * @returns Updated or cached value
21051 */
21052function pureFunction3Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, thisArg) {
21053 const bindingIndex = bindingRoot + slotOffset;
21054 return bindingUpdated3(lView, bindingIndex, exp1, exp2, exp3) ?
21055 updateBinding(lView, bindingIndex + 3, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3) : pureFn(exp1, exp2, exp3)) :
21056 getPureFunctionReturnValue(lView, bindingIndex + 3);
21057}
21058/**
21059 * If the value of any provided exp has changed, calls the pure function to return
21060 * an updated value. Or if no values have changed, returns cached value.
21061 *
21062 * @param lView LView in which the function is being executed.
21063 * @param bindingRoot Binding root index.
21064 * @param slotOffset the offset from binding root to the reserved slot
21065 * @param pureFn
21066 * @param exp1
21067 * @param exp2
21068 * @param exp3
21069 * @param exp4
21070 * @param thisArg Optional calling context of pureFn
21071 * @returns Updated or cached value
21072 *
21073 */
21074function pureFunction4Internal(lView, bindingRoot, slotOffset, pureFn, exp1, exp2, exp3, exp4, thisArg) {
21075 const bindingIndex = bindingRoot + slotOffset;
21076 return bindingUpdated4(lView, bindingIndex, exp1, exp2, exp3, exp4) ?
21077 updateBinding(lView, bindingIndex + 4, thisArg ? pureFn.call(thisArg, exp1, exp2, exp3, exp4) : pureFn(exp1, exp2, exp3, exp4)) :
21078 getPureFunctionReturnValue(lView, bindingIndex + 4);
21079}
21080/**
21081 * pureFunction instruction that can support any number of bindings.
21082 *
21083 * If the value of any provided exp has changed, calls the pure function to return
21084 * an updated value. Or if no values have changed, returns cached value.
21085 *
21086 * @param lView LView in which the function is being executed.
21087 * @param bindingRoot Binding root index.
21088 * @param slotOffset the offset from binding root to the reserved slot
21089 * @param pureFn A pure function that takes binding values and builds an object or array
21090 * containing those values.
21091 * @param exps An array of binding values
21092 * @param thisArg Optional calling context of pureFn
21093 * @returns Updated or cached value
21094 */
21095function pureFunctionVInternal(lView, bindingRoot, slotOffset, pureFn, exps, thisArg) {
21096 let bindingIndex = bindingRoot + slotOffset;
21097 let different = false;
21098 for (let i = 0; i < exps.length; i++) {
21099 bindingUpdated(lView, bindingIndex++, exps[i]) && (different = true);
21100 }
21101 return different ? updateBinding(lView, bindingIndex, pureFn.apply(thisArg, exps)) :
21102 getPureFunctionReturnValue(lView, bindingIndex);
21103}
21104
21105/**
21106 * Create a pipe.
21107 *
21108 * @param index Pipe index where the pipe will be stored.
21109 * @param pipeName The name of the pipe
21110 * @returns T the instance of the pipe.
21111 *
21112 * @codeGenApi
21113 */
21114function ɵɵpipe(index, pipeName) {
21115 const tView = getTView();
21116 let pipeDef;
21117 const adjustedIndex = index + HEADER_OFFSET;
21118 if (tView.firstCreatePass) {
21119 // The `getPipeDef` throws if a pipe with a given name is not found
21120 // (so we use non-null assertion below).
21121 pipeDef = getPipeDef(pipeName, tView.pipeRegistry);
21122 tView.data[adjustedIndex] = pipeDef;
21123 if (pipeDef.onDestroy) {
21124 (tView.destroyHooks || (tView.destroyHooks = [])).push(adjustedIndex, pipeDef.onDestroy);
21125 }
21126 }
21127 else {
21128 pipeDef = tView.data[adjustedIndex];
21129 }
21130 const pipeFactory = pipeDef.factory || (pipeDef.factory = getFactoryDef(pipeDef.type, true));
21131 const previousInjectImplementation = setInjectImplementation(ɵɵdirectiveInject);
21132 try {
21133 // DI for pipes is supposed to behave like directives when placed on a component
21134 // host node, which means that we have to disable access to `viewProviders`.
21135 const previousIncludeViewProviders = setIncludeViewProviders(false);
21136 const pipeInstance = pipeFactory();
21137 setIncludeViewProviders(previousIncludeViewProviders);
21138 store(tView, getLView(), adjustedIndex, pipeInstance);
21139 return pipeInstance;
21140 }
21141 finally {
21142 // we have to restore the injector implementation in finally, just in case the creation of the
21143 // pipe throws an error.
21144 setInjectImplementation(previousInjectImplementation);
21145 }
21146}
21147/**
21148 * Searches the pipe registry for a pipe with the given name. If one is found,
21149 * returns the pipe. Otherwise, an error is thrown because the pipe cannot be resolved.
21150 *
21151 * @param name Name of pipe to resolve
21152 * @param registry Full list of available pipes
21153 * @returns Matching PipeDef
21154 */
21155function getPipeDef(name, registry) {
21156 if (registry) {
21157 for (let i = registry.length - 1; i >= 0; i--) {
21158 const pipeDef = registry[i];
21159 if (name === pipeDef.name) {
21160 return pipeDef;
21161 }
21162 }
21163 }
21164 if (ngDevMode) {
21165 throw new RuntimeError(-302 /* RuntimeErrorCode.PIPE_NOT_FOUND */, getPipeNotFoundErrorMessage(name));
21166 }
21167}
21168/**
21169 * Generates a helpful error message for the user when a pipe is not found.
21170 *
21171 * @param name Name of the missing pipe
21172 * @returns The error message
21173 */
21174function getPipeNotFoundErrorMessage(name) {
21175 const lView = getLView();
21176 const declarationLView = lView[DECLARATION_COMPONENT_VIEW];
21177 const context = declarationLView[CONTEXT];
21178 const hostIsStandalone = isHostComponentStandalone(lView);
21179 const componentInfoMessage = context ? ` in the '${context.constructor.name}' component` : '';
21180 const verifyMessage = `Verify that it is ${hostIsStandalone ? 'included in the \'@Component.imports\' of this component' :
21181 'declared or imported in this module'}`;
21182 const errorMessage = `The pipe '${name}' could not be found${componentInfoMessage}. ${verifyMessage}`;
21183 return errorMessage;
21184}
21185/**
21186 * Invokes a pipe with 1 arguments.
21187 *
21188 * This instruction acts as a guard to {@link PipeTransform#transform} invoking
21189 * the pipe only when an input to the pipe changes.
21190 *
21191 * @param index Pipe index where the pipe was stored on creation.
21192 * @param slotOffset the offset in the reserved slot space
21193 * @param v1 1st argument to {@link PipeTransform#transform}.
21194 *
21195 * @codeGenApi
21196 */
21197function ɵɵpipeBind1(index, slotOffset, v1) {
21198 const adjustedIndex = index + HEADER_OFFSET;
21199 const lView = getLView();
21200 const pipeInstance = load(lView, adjustedIndex);
21201 return isPure(lView, adjustedIndex) ?
21202 pureFunction1Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, pipeInstance) :
21203 pipeInstance.transform(v1);
21204}
21205/**
21206 * Invokes a pipe with 2 arguments.
21207 *
21208 * This instruction acts as a guard to {@link PipeTransform#transform} invoking
21209 * the pipe only when an input to the pipe changes.
21210 *
21211 * @param index Pipe index where the pipe was stored on creation.
21212 * @param slotOffset the offset in the reserved slot space
21213 * @param v1 1st argument to {@link PipeTransform#transform}.
21214 * @param v2 2nd argument to {@link PipeTransform#transform}.
21215 *
21216 * @codeGenApi
21217 */
21218function ɵɵpipeBind2(index, slotOffset, v1, v2) {
21219 const adjustedIndex = index + HEADER_OFFSET;
21220 const lView = getLView();
21221 const pipeInstance = load(lView, adjustedIndex);
21222 return isPure(lView, adjustedIndex) ?
21223 pureFunction2Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
21224 pipeInstance.transform(v1, v2);
21225}
21226/**
21227 * Invokes a pipe with 3 arguments.
21228 *
21229 * This instruction acts as a guard to {@link PipeTransform#transform} invoking
21230 * the pipe only when an input to the pipe changes.
21231 *
21232 * @param index Pipe index where the pipe was stored on creation.
21233 * @param slotOffset the offset in the reserved slot space
21234 * @param v1 1st argument to {@link PipeTransform#transform}.
21235 * @param v2 2nd argument to {@link PipeTransform#transform}.
21236 * @param v3 4rd argument to {@link PipeTransform#transform}.
21237 *
21238 * @codeGenApi
21239 */
21240function ɵɵpipeBind3(index, slotOffset, v1, v2, v3) {
21241 const adjustedIndex = index + HEADER_OFFSET;
21242 const lView = getLView();
21243 const pipeInstance = load(lView, adjustedIndex);
21244 return isPure(lView, adjustedIndex) ?
21245 pureFunction3Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) :
21246 pipeInstance.transform(v1, v2, v3);
21247}
21248/**
21249 * Invokes a pipe with 4 arguments.
21250 *
21251 * This instruction acts as a guard to {@link PipeTransform#transform} invoking
21252 * the pipe only when an input to the pipe changes.
21253 *
21254 * @param index Pipe index where the pipe was stored on creation.
21255 * @param slotOffset the offset in the reserved slot space
21256 * @param v1 1st argument to {@link PipeTransform#transform}.
21257 * @param v2 2nd argument to {@link PipeTransform#transform}.
21258 * @param v3 3rd argument to {@link PipeTransform#transform}.
21259 * @param v4 4th argument to {@link PipeTransform#transform}.
21260 *
21261 * @codeGenApi
21262 */
21263function ɵɵpipeBind4(index, slotOffset, v1, v2, v3, v4) {
21264 const adjustedIndex = index + HEADER_OFFSET;
21265 const lView = getLView();
21266 const pipeInstance = load(lView, adjustedIndex);
21267 return isPure(lView, adjustedIndex) ? pureFunction4Internal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
21268 pipeInstance.transform(v1, v2, v3, v4);
21269}
21270/**
21271 * Invokes a pipe with variable number of arguments.
21272 *
21273 * This instruction acts as a guard to {@link PipeTransform#transform} invoking
21274 * the pipe only when an input to the pipe changes.
21275 *
21276 * @param index Pipe index where the pipe was stored on creation.
21277 * @param slotOffset the offset in the reserved slot space
21278 * @param values Array of arguments to pass to {@link PipeTransform#transform} method.
21279 *
21280 * @codeGenApi
21281 */
21282function ɵɵpipeBindV(index, slotOffset, values) {
21283 const adjustedIndex = index + HEADER_OFFSET;
21284 const lView = getLView();
21285 const pipeInstance = load(lView, adjustedIndex);
21286 return isPure(lView, adjustedIndex) ?
21287 pureFunctionVInternal(lView, getBindingRoot(), slotOffset, pipeInstance.transform, values, pipeInstance) :
21288 pipeInstance.transform.apply(pipeInstance, values);
21289}
21290function isPure(lView, index) {
21291 return lView[TVIEW].data[index].pure;
21292}
21293
21294/// <reference types="rxjs" />
21295class EventEmitter_ extends Subject {
21296 constructor(isAsync = false) {
21297 super();
21298 this.__isAsync = isAsync;
21299 }
21300 emit(value) {
21301 super.next(value);
21302 }
21303 subscribe(observerOrNext, error, complete) {
21304 let nextFn = observerOrNext;
21305 let errorFn = error || (() => null);
21306 let completeFn = complete;
21307 if (observerOrNext && typeof observerOrNext === 'object') {
21308 const observer = observerOrNext;
21309 nextFn = observer.next?.bind(observer);
21310 errorFn = observer.error?.bind(observer);
21311 completeFn = observer.complete?.bind(observer);
21312 }
21313 if (this.__isAsync) {
21314 errorFn = _wrapInTimeout(errorFn);
21315 if (nextFn) {
21316 nextFn = _wrapInTimeout(nextFn);
21317 }
21318 if (completeFn) {
21319 completeFn = _wrapInTimeout(completeFn);
21320 }
21321 }
21322 const sink = super.subscribe({ next: nextFn, error: errorFn, complete: completeFn });
21323 if (observerOrNext instanceof Subscription) {
21324 observerOrNext.add(sink);
21325 }
21326 return sink;
21327 }
21328}
21329function _wrapInTimeout(fn) {
21330 return (value) => {
21331 setTimeout(fn, undefined, value);
21332 };
21333}
21334/**
21335 * @publicApi
21336 */
21337const EventEmitter = EventEmitter_;
21338
21339function symbolIterator() {
21340 return this._results[getSymbolIterator()]();
21341}
21342/**
21343 * An unmodifiable list of items that Angular keeps up to date when the state
21344 * of the application changes.
21345 *
21346 * The type of object that {@link ViewChildren}, {@link ContentChildren}, and {@link QueryList}
21347 * provide.
21348 *
21349 * Implements an iterable interface, therefore it can be used in both ES6
21350 * javascript `for (var i of items)` loops as well as in Angular templates with
21351 * `*ngFor="let i of myList"`.
21352 *
21353 * Changes can be observed by subscribing to the changes `Observable`.
21354 *
21355 * NOTE: In the future this class will implement an `Observable` interface.
21356 *
21357 * @usageNotes
21358 * ### Example
21359 * ```typescript
21360 * @Component({...})
21361 * class Container {
21362 * @ViewChildren(Item) items:QueryList<Item>;
21363 * }
21364 * ```
21365 *
21366 * @publicApi
21367 */
21368class QueryList {
21369 /**
21370 * Returns `Observable` of `QueryList` notifying the subscriber of changes.
21371 */
21372 get changes() {
21373 return this._changes || (this._changes = new EventEmitter());
21374 }
21375 /**
21376 * @param emitDistinctChangesOnly Whether `QueryList.changes` should fire only when actual change
21377 * has occurred. Or if it should fire when query is recomputed. (recomputing could resolve in
21378 * the same result)
21379 */
21380 constructor(_emitDistinctChangesOnly = false) {
21381 this._emitDistinctChangesOnly = _emitDistinctChangesOnly;
21382 this.dirty = true;
21383 this._results = [];
21384 this._changesDetected = false;
21385 this._changes = null;
21386 this.length = 0;
21387 this.first = undefined;
21388 this.last = undefined;
21389 // This function should be declared on the prototype, but doing so there will cause the class
21390 // declaration to have side-effects and become not tree-shakable. For this reason we do it in
21391 // the constructor.
21392 // [getSymbolIterator()](): Iterator<T> { ... }
21393 const symbol = getSymbolIterator();
21394 const proto = QueryList.prototype;
21395 if (!proto[symbol])
21396 proto[symbol] = symbolIterator;
21397 }
21398 /**
21399 * Returns the QueryList entry at `index`.
21400 */
21401 get(index) {
21402 return this._results[index];
21403 }
21404 /**
21405 * See
21406 * [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
21407 */
21408 map(fn) {
21409 return this._results.map(fn);
21410 }
21411 /**
21412 * See
21413 * [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
21414 */
21415 filter(fn) {
21416 return this._results.filter(fn);
21417 }
21418 /**
21419 * See
21420 * [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
21421 */
21422 find(fn) {
21423 return this._results.find(fn);
21424 }
21425 /**
21426 * See
21427 * [Array.reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
21428 */
21429 reduce(fn, init) {
21430 return this._results.reduce(fn, init);
21431 }
21432 /**
21433 * See
21434 * [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
21435 */
21436 forEach(fn) {
21437 this._results.forEach(fn);
21438 }
21439 /**
21440 * See
21441 * [Array.some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
21442 */
21443 some(fn) {
21444 return this._results.some(fn);
21445 }
21446 /**
21447 * Returns a copy of the internal results list as an Array.
21448 */
21449 toArray() {
21450 return this._results.slice();
21451 }
21452 toString() {
21453 return this._results.toString();
21454 }
21455 /**
21456 * Updates the stored data of the query list, and resets the `dirty` flag to `false`, so that
21457 * on change detection, it will not notify of changes to the queries, unless a new change
21458 * occurs.
21459 *
21460 * @param resultsTree The query results to store
21461 * @param identityAccessor Optional function for extracting stable object identity from a value
21462 * in the array. This function is executed for each element of the query result list while
21463 * comparing current query list with the new one (provided as a first argument of the `reset`
21464 * function) to detect if the lists are different. If the function is not provided, elements
21465 * are compared as is (without any pre-processing).
21466 */
21467 reset(resultsTree, identityAccessor) {
21468 // Cast to `QueryListInternal` so that we can mutate fields which are readonly for the usage of
21469 // QueryList (but not for QueryList itself.)
21470 const self = this;
21471 self.dirty = false;
21472 const newResultFlat = flatten$1(resultsTree);
21473 if (this._changesDetected = !arrayEquals(self._results, newResultFlat, identityAccessor)) {
21474 self._results = newResultFlat;
21475 self.length = newResultFlat.length;
21476 self.last = newResultFlat[this.length - 1];
21477 self.first = newResultFlat[0];
21478 }
21479 }
21480 /**
21481 * Triggers a change event by emitting on the `changes` {@link EventEmitter}.
21482 */
21483 notifyOnChanges() {
21484 if (this._changes && (this._changesDetected || !this._emitDistinctChangesOnly))
21485 this._changes.emit(this);
21486 }
21487 /** internal */
21488 setDirty() {
21489 this.dirty = true;
21490 }
21491 /** internal */
21492 destroy() {
21493 this.changes.complete();
21494 this.changes.unsubscribe();
21495 }
21496}
21497Symbol.iterator;
21498
21499/**
21500 * Represents an embedded template that can be used to instantiate embedded views.
21501 * To instantiate embedded views based on a template, use the `ViewContainerRef`
21502 * method `createEmbeddedView()`.
21503 *
21504 * Access a `TemplateRef` instance by placing a directive on an `<ng-template>`
21505 * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view
21506 * is injected into the constructor of the directive,
21507 * using the `TemplateRef` token.
21508 *
21509 * You can also use a `Query` to find a `TemplateRef` associated with
21510 * a component or a directive.
21511 *
21512 * @see `ViewContainerRef`
21513 * @see [Navigate the Component Tree with DI](guide/dependency-injection-navtree)
21514 *
21515 * @publicApi
21516 */
21517class TemplateRef {
21518}
21519/**
21520 * @internal
21521 * @nocollapse
21522 */
21523TemplateRef.__NG_ELEMENT_ID__ = injectTemplateRef;
21524const ViewEngineTemplateRef = TemplateRef;
21525// TODO(alxhub): combine interface and implementation. Currently this is challenging since something
21526// in g3 depends on them being separate.
21527const R3TemplateRef = class TemplateRef extends ViewEngineTemplateRef {
21528 constructor(_declarationLView, _declarationTContainer, elementRef) {
21529 super();
21530 this._declarationLView = _declarationLView;
21531 this._declarationTContainer = _declarationTContainer;
21532 this.elementRef = elementRef;
21533 }
21534 createEmbeddedView(context, injector) {
21535 const embeddedTView = this._declarationTContainer.tViews;
21536 const embeddedLView = createLView(this._declarationLView, embeddedTView, context, 16 /* LViewFlags.CheckAlways */, null, embeddedTView.declTNode, null, null, null, null, injector || null);
21537 const declarationLContainer = this._declarationLView[this._declarationTContainer.index];
21538 ngDevMode && assertLContainer(declarationLContainer);
21539 embeddedLView[DECLARATION_LCONTAINER] = declarationLContainer;
21540 const declarationViewLQueries = this._declarationLView[QUERIES];
21541 if (declarationViewLQueries !== null) {
21542 embeddedLView[QUERIES] = declarationViewLQueries.createEmbeddedView(embeddedTView);
21543 }
21544 renderView(embeddedTView, embeddedLView, context);
21545 return new ViewRef(embeddedLView);
21546 }
21547};
21548/**
21549 * Creates a TemplateRef given a node.
21550 *
21551 * @returns The TemplateRef instance to use
21552 */
21553function injectTemplateRef() {
21554 return createTemplateRef(getCurrentTNode(), getLView());
21555}
21556/**
21557 * Creates a TemplateRef and stores it on the injector.
21558 *
21559 * @param hostTNode The node on which a TemplateRef is requested
21560 * @param hostLView The `LView` to which the node belongs
21561 * @returns The TemplateRef instance or null if we can't create a TemplateRef on a given node type
21562 */
21563function createTemplateRef(hostTNode, hostLView) {
21564 if (hostTNode.type & 4 /* TNodeType.Container */) {
21565 ngDevMode && assertDefined(hostTNode.tViews, 'TView must be allocated');
21566 return new R3TemplateRef(hostLView, hostTNode, createElementRef(hostTNode, hostLView));
21567 }
21568 return null;
21569}
21570
21571/**
21572 * Represents a container where one or more views can be attached to a component.
21573 *
21574 * Can contain *host views* (created by instantiating a
21575 * component with the `createComponent()` method), and *embedded views*
21576 * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
21577 *
21578 * A view container instance can contain other view containers,
21579 * creating a [view hierarchy](guide/glossary#view-tree).
21580 *
21581 * @see `ComponentRef`
21582 * @see `EmbeddedViewRef`
21583 *
21584 * @publicApi
21585 */
21586class ViewContainerRef {
21587}
21588/**
21589 * @internal
21590 * @nocollapse
21591 */
21592ViewContainerRef.__NG_ELEMENT_ID__ = injectViewContainerRef;
21593/**
21594 * Creates a ViewContainerRef and stores it on the injector. Or, if the ViewContainerRef
21595 * already exists, retrieves the existing ViewContainerRef.
21596 *
21597 * @returns The ViewContainerRef instance to use
21598 */
21599function injectViewContainerRef() {
21600 const previousTNode = getCurrentTNode();
21601 return createContainerRef(previousTNode, getLView());
21602}
21603const VE_ViewContainerRef = ViewContainerRef;
21604// TODO(alxhub): cleaning up this indirection triggers a subtle bug in Closure in g3. Once the fix
21605// for that lands, this can be cleaned up.
21606const R3ViewContainerRef = class ViewContainerRef extends VE_ViewContainerRef {
21607 constructor(_lContainer, _hostTNode, _hostLView) {
21608 super();
21609 this._lContainer = _lContainer;
21610 this._hostTNode = _hostTNode;
21611 this._hostLView = _hostLView;
21612 }
21613 get element() {
21614 return createElementRef(this._hostTNode, this._hostLView);
21615 }
21616 get injector() {
21617 return new NodeInjector(this._hostTNode, this._hostLView);
21618 }
21619 /** @deprecated No replacement */
21620 get parentInjector() {
21621 const parentLocation = getParentInjectorLocation(this._hostTNode, this._hostLView);
21622 if (hasParentInjector(parentLocation)) {
21623 const parentView = getParentInjectorView(parentLocation, this._hostLView);
21624 const injectorIndex = getParentInjectorIndex(parentLocation);
21625 ngDevMode && assertNodeInjector(parentView, injectorIndex);
21626 const parentTNode = parentView[TVIEW].data[injectorIndex + 8 /* NodeInjectorOffset.TNODE */];
21627 return new NodeInjector(parentTNode, parentView);
21628 }
21629 else {
21630 return new NodeInjector(null, this._hostLView);
21631 }
21632 }
21633 clear() {
21634 while (this.length > 0) {
21635 this.remove(this.length - 1);
21636 }
21637 }
21638 get(index) {
21639 const viewRefs = getViewRefs(this._lContainer);
21640 return viewRefs !== null && viewRefs[index] || null;
21641 }
21642 get length() {
21643 return this._lContainer.length - CONTAINER_HEADER_OFFSET;
21644 }
21645 createEmbeddedView(templateRef, context, indexOrOptions) {
21646 let index;
21647 let injector;
21648 if (typeof indexOrOptions === 'number') {
21649 index = indexOrOptions;
21650 }
21651 else if (indexOrOptions != null) {
21652 index = indexOrOptions.index;
21653 injector = indexOrOptions.injector;
21654 }
21655 const viewRef = templateRef.createEmbeddedView(context || {}, injector);
21656 this.insert(viewRef, index);
21657 return viewRef;
21658 }
21659 createComponent(componentFactoryOrType, indexOrOptions, injector, projectableNodes, environmentInjector) {
21660 const isComponentFactory = componentFactoryOrType && !isType(componentFactoryOrType);
21661 let index;
21662 // This function supports 2 signatures and we need to handle options correctly for both:
21663 // 1. When first argument is a Component type. This signature also requires extra
21664 // options to be provided as as object (more ergonomic option).
21665 // 2. First argument is a Component factory. In this case extra options are represented as
21666 // positional arguments. This signature is less ergonomic and will be deprecated.
21667 if (isComponentFactory) {
21668 if (ngDevMode) {
21669 assertEqual(typeof indexOrOptions !== 'object', true, 'It looks like Component factory was provided as the first argument ' +
21670 'and an options object as the second argument. This combination of arguments ' +
21671 'is incompatible. You can either change the first argument to provide Component ' +
21672 'type or change the second argument to be a number (representing an index at ' +
21673 'which to insert the new component\'s host view into this container)');
21674 }
21675 index = indexOrOptions;
21676 }
21677 else {
21678 if (ngDevMode) {
21679 assertDefined(getComponentDef$1(componentFactoryOrType), `Provided Component class doesn't contain Component definition. ` +
21680 `Please check whether provided class has @Component decorator.`);
21681 assertEqual(typeof indexOrOptions !== 'number', true, 'It looks like Component type was provided as the first argument ' +
21682 'and a number (representing an index at which to insert the new component\'s ' +
21683 'host view into this container as the second argument. This combination of arguments ' +
21684 'is incompatible. Please use an object as the second argument instead.');
21685 }
21686 const options = (indexOrOptions || {});
21687 if (ngDevMode && options.environmentInjector && options.ngModuleRef) {
21688 throwError(`Cannot pass both environmentInjector and ngModuleRef options to createComponent().`);
21689 }
21690 index = options.index;
21691 injector = options.injector;
21692 projectableNodes = options.projectableNodes;
21693 environmentInjector = options.environmentInjector || options.ngModuleRef;
21694 }
21695 const componentFactory = isComponentFactory ?
21696 componentFactoryOrType :
21697 new ComponentFactory(getComponentDef$1(componentFactoryOrType));
21698 const contextInjector = injector || this.parentInjector;
21699 // If an `NgModuleRef` is not provided explicitly, try retrieving it from the DI tree.
21700 if (!environmentInjector && componentFactory.ngModule == null) {
21701 // For the `ComponentFactory` case, entering this logic is very unlikely, since we expect that
21702 // an instance of a `ComponentFactory`, resolved via `ComponentFactoryResolver` would have an
21703 // `ngModule` field. This is possible in some test scenarios and potentially in some JIT-based
21704 // use-cases. For the `ComponentFactory` case we preserve backwards-compatibility and try
21705 // using a provided injector first, then fall back to the parent injector of this
21706 // `ViewContainerRef` instance.
21707 //
21708 // For the factory-less case, it's critical to establish a connection with the module
21709 // injector tree (by retrieving an instance of an `NgModuleRef` and accessing its injector),
21710 // so that a component can use DI tokens provided in MgModules. For this reason, we can not
21711 // rely on the provided injector, since it might be detached from the DI tree (for example, if
21712 // it was created via `Injector.create` without specifying a parent injector, or if an
21713 // injector is retrieved from an `NgModuleRef` created via `createNgModule` using an
21714 // NgModule outside of a module tree). Instead, we always use `ViewContainerRef`'s parent
21715 // injector, which is normally connected to the DI tree, which includes module injector
21716 // subtree.
21717 const _injector = isComponentFactory ? contextInjector : this.parentInjector;
21718 // DO NOT REFACTOR. The code here used to have a `injector.get(NgModuleRef, null) ||
21719 // undefined` expression which seems to cause internal google apps to fail. This is documented
21720 // in the following internal bug issue: go/b/142967802
21721 const result = _injector.get(EnvironmentInjector, null);
21722 if (result) {
21723 environmentInjector = result;
21724 }
21725 }
21726 const componentRef = componentFactory.create(contextInjector, projectableNodes, undefined, environmentInjector);
21727 this.insert(componentRef.hostView, index);
21728 return componentRef;
21729 }
21730 insert(viewRef, index) {
21731 const lView = viewRef._lView;
21732 const tView = lView[TVIEW];
21733 if (ngDevMode && viewRef.destroyed) {
21734 throw new Error('Cannot insert a destroyed View in a ViewContainer!');
21735 }
21736 if (viewAttachedToContainer(lView)) {
21737 // If view is already attached, detach it first so we clean up references appropriately.
21738 const prevIdx = this.indexOf(viewRef);
21739 // A view might be attached either to this or a different container. The `prevIdx` for
21740 // those cases will be:
21741 // equal to -1 for views attached to this ViewContainerRef
21742 // >= 0 for views attached to a different ViewContainerRef
21743 if (prevIdx !== -1) {
21744 this.detach(prevIdx);
21745 }
21746 else {
21747 const prevLContainer = lView[PARENT];
21748 ngDevMode &&
21749 assertEqual(isLContainer(prevLContainer), true, 'An attached view should have its PARENT point to a container.');
21750 // We need to re-create a R3ViewContainerRef instance since those are not stored on
21751 // LView (nor anywhere else).
21752 const prevVCRef = new R3ViewContainerRef(prevLContainer, prevLContainer[T_HOST], prevLContainer[PARENT]);
21753 prevVCRef.detach(prevVCRef.indexOf(viewRef));
21754 }
21755 }
21756 // Logical operation of adding `LView` to `LContainer`
21757 const adjustedIdx = this._adjustIndex(index);
21758 const lContainer = this._lContainer;
21759 insertView(tView, lView, lContainer, adjustedIdx);
21760 // Physical operation of adding the DOM nodes.
21761 const beforeNode = getBeforeNodeForView(adjustedIdx, lContainer);
21762 const renderer = lView[RENDERER];
21763 const parentRNode = nativeParentNode(renderer, lContainer[NATIVE]);
21764 if (parentRNode !== null) {
21765 addViewToContainer(tView, lContainer[T_HOST], renderer, lView, parentRNode, beforeNode);
21766 }
21767 viewRef.attachToViewContainerRef();
21768 addToArray(getOrCreateViewRefs(lContainer), adjustedIdx, viewRef);
21769 return viewRef;
21770 }
21771 move(viewRef, newIndex) {
21772 if (ngDevMode && viewRef.destroyed) {
21773 throw new Error('Cannot move a destroyed View in a ViewContainer!');
21774 }
21775 return this.insert(viewRef, newIndex);
21776 }
21777 indexOf(viewRef) {
21778 const viewRefsArr = getViewRefs(this._lContainer);
21779 return viewRefsArr !== null ? viewRefsArr.indexOf(viewRef) : -1;
21780 }
21781 remove(index) {
21782 const adjustedIdx = this._adjustIndex(index, -1);
21783 const detachedView = detachView(this._lContainer, adjustedIdx);
21784 if (detachedView) {
21785 // Before destroying the view, remove it from the container's array of `ViewRef`s.
21786 // This ensures the view container length is updated before calling
21787 // `destroyLView`, which could recursively call view container methods that
21788 // rely on an accurate container length.
21789 // (e.g. a method on this view container being called by a child directive's OnDestroy
21790 // lifecycle hook)
21791 removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx);
21792 destroyLView(detachedView[TVIEW], detachedView);
21793 }
21794 }
21795 detach(index) {
21796 const adjustedIdx = this._adjustIndex(index, -1);
21797 const view = detachView(this._lContainer, adjustedIdx);
21798 const wasDetached = view && removeFromArray(getOrCreateViewRefs(this._lContainer), adjustedIdx) != null;
21799 return wasDetached ? new ViewRef(view) : null;
21800 }
21801 _adjustIndex(index, shift = 0) {
21802 if (index == null) {
21803 return this.length + shift;
21804 }
21805 if (ngDevMode) {
21806 assertGreaterThan(index, -1, `ViewRef index must be positive, got ${index}`);
21807 // +1 because it's legal to insert at the end.
21808 assertLessThan(index, this.length + 1 + shift, 'index');
21809 }
21810 return index;
21811 }
21812};
21813function getViewRefs(lContainer) {
21814 return lContainer[VIEW_REFS];
21815}
21816function getOrCreateViewRefs(lContainer) {
21817 return (lContainer[VIEW_REFS] || (lContainer[VIEW_REFS] = []));
21818}
21819/**
21820 * Creates a ViewContainerRef and stores it on the injector.
21821 *
21822 * @param ViewContainerRefToken The ViewContainerRef type
21823 * @param ElementRefToken The ElementRef type
21824 * @param hostTNode The node that is requesting a ViewContainerRef
21825 * @param hostLView The view to which the node belongs
21826 * @returns The ViewContainerRef instance to use
21827 */
21828function createContainerRef(hostTNode, hostLView) {
21829 ngDevMode && assertTNodeType(hostTNode, 12 /* TNodeType.AnyContainer */ | 3 /* TNodeType.AnyRNode */);
21830 let lContainer;
21831 const slotValue = hostLView[hostTNode.index];
21832 if (isLContainer(slotValue)) {
21833 // If the host is a container, we don't need to create a new LContainer
21834 lContainer = slotValue;
21835 }
21836 else {
21837 let commentNode;
21838 // If the host is an element container, the native host element is guaranteed to be a
21839 // comment and we can reuse that comment as anchor element for the new LContainer.
21840 // The comment node in question is already part of the DOM structure so we don't need to append
21841 // it again.
21842 if (hostTNode.type & 8 /* TNodeType.ElementContainer */) {
21843 commentNode = unwrapRNode(slotValue);
21844 }
21845 else {
21846 // If the host is a regular element, we have to insert a comment node manually which will
21847 // be used as an anchor when inserting elements. In this specific case we use low-level DOM
21848 // manipulation to insert it.
21849 const renderer = hostLView[RENDERER];
21850 ngDevMode && ngDevMode.rendererCreateComment++;
21851 commentNode = renderer.createComment(ngDevMode ? 'container' : '');
21852 const hostNative = getNativeByTNode(hostTNode, hostLView);
21853 const parentOfHostNative = nativeParentNode(renderer, hostNative);
21854 nativeInsertBefore(renderer, parentOfHostNative, commentNode, nativeNextSibling(renderer, hostNative), false);
21855 }
21856 hostLView[hostTNode.index] = lContainer =
21857 createLContainer(slotValue, hostLView, commentNode, hostTNode);
21858 addToViewTree(hostLView, lContainer);
21859 }
21860 return new R3ViewContainerRef(lContainer, hostTNode, hostLView);
21861}
21862
21863class LQuery_ {
21864 constructor(queryList) {
21865 this.queryList = queryList;
21866 this.matches = null;
21867 }
21868 clone() {
21869 return new LQuery_(this.queryList);
21870 }
21871 setDirty() {
21872 this.queryList.setDirty();
21873 }
21874}
21875class LQueries_ {
21876 constructor(queries = []) {
21877 this.queries = queries;
21878 }
21879 createEmbeddedView(tView) {
21880 const tQueries = tView.queries;
21881 if (tQueries !== null) {
21882 const noOfInheritedQueries = tView.contentQueries !== null ? tView.contentQueries[0] : tQueries.length;
21883 const viewLQueries = [];
21884 // An embedded view has queries propagated from a declaration view at the beginning of the
21885 // TQueries collection and up until a first content query declared in the embedded view. Only
21886 // propagated LQueries are created at this point (LQuery corresponding to declared content
21887 // queries will be instantiated from the content query instructions for each directive).
21888 for (let i = 0; i < noOfInheritedQueries; i++) {
21889 const tQuery = tQueries.getByIndex(i);
21890 const parentLQuery = this.queries[tQuery.indexInDeclarationView];
21891 viewLQueries.push(parentLQuery.clone());
21892 }
21893 return new LQueries_(viewLQueries);
21894 }
21895 return null;
21896 }
21897 insertView(tView) {
21898 this.dirtyQueriesWithMatches(tView);
21899 }
21900 detachView(tView) {
21901 this.dirtyQueriesWithMatches(tView);
21902 }
21903 dirtyQueriesWithMatches(tView) {
21904 for (let i = 0; i < this.queries.length; i++) {
21905 if (getTQuery(tView, i).matches !== null) {
21906 this.queries[i].setDirty();
21907 }
21908 }
21909 }
21910}
21911class TQueryMetadata_ {
21912 constructor(predicate, flags, read = null) {
21913 this.predicate = predicate;
21914 this.flags = flags;
21915 this.read = read;
21916 }
21917}
21918class TQueries_ {
21919 constructor(queries = []) {
21920 this.queries = queries;
21921 }
21922 elementStart(tView, tNode) {
21923 ngDevMode &&
21924 assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only');
21925 for (let i = 0; i < this.queries.length; i++) {
21926 this.queries[i].elementStart(tView, tNode);
21927 }
21928 }
21929 elementEnd(tNode) {
21930 for (let i = 0; i < this.queries.length; i++) {
21931 this.queries[i].elementEnd(tNode);
21932 }
21933 }
21934 embeddedTView(tNode) {
21935 let queriesForTemplateRef = null;
21936 for (let i = 0; i < this.length; i++) {
21937 const childQueryIndex = queriesForTemplateRef !== null ? queriesForTemplateRef.length : 0;
21938 const tqueryClone = this.getByIndex(i).embeddedTView(tNode, childQueryIndex);
21939 if (tqueryClone) {
21940 tqueryClone.indexInDeclarationView = i;
21941 if (queriesForTemplateRef !== null) {
21942 queriesForTemplateRef.push(tqueryClone);
21943 }
21944 else {
21945 queriesForTemplateRef = [tqueryClone];
21946 }
21947 }
21948 }
21949 return queriesForTemplateRef !== null ? new TQueries_(queriesForTemplateRef) : null;
21950 }
21951 template(tView, tNode) {
21952 ngDevMode &&
21953 assertFirstCreatePass(tView, 'Queries should collect results on the first template pass only');
21954 for (let i = 0; i < this.queries.length; i++) {
21955 this.queries[i].template(tView, tNode);
21956 }
21957 }
21958 getByIndex(index) {
21959 ngDevMode && assertIndexInRange(this.queries, index);
21960 return this.queries[index];
21961 }
21962 get length() {
21963 return this.queries.length;
21964 }
21965 track(tquery) {
21966 this.queries.push(tquery);
21967 }
21968}
21969class TQuery_ {
21970 constructor(metadata, nodeIndex = -1) {
21971 this.metadata = metadata;
21972 this.matches = null;
21973 this.indexInDeclarationView = -1;
21974 this.crossesNgTemplate = false;
21975 /**
21976 * A flag indicating if a given query still applies to nodes it is crossing. We use this flag
21977 * (alongside with _declarationNodeIndex) to know when to stop applying content queries to
21978 * elements in a template.
21979 */
21980 this._appliesToNextNode = true;
21981 this._declarationNodeIndex = nodeIndex;
21982 }
21983 elementStart(tView, tNode) {
21984 if (this.isApplyingToNode(tNode)) {
21985 this.matchTNode(tView, tNode);
21986 }
21987 }
21988 elementEnd(tNode) {
21989 if (this._declarationNodeIndex === tNode.index) {
21990 this._appliesToNextNode = false;
21991 }
21992 }
21993 template(tView, tNode) {
21994 this.elementStart(tView, tNode);
21995 }
21996 embeddedTView(tNode, childQueryIndex) {
21997 if (this.isApplyingToNode(tNode)) {
21998 this.crossesNgTemplate = true;
21999 // A marker indicating a `<ng-template>` element (a placeholder for query results from
22000 // embedded views created based on this `<ng-template>`).
22001 this.addMatch(-tNode.index, childQueryIndex);
22002 return new TQuery_(this.metadata);
22003 }
22004 return null;
22005 }
22006 isApplyingToNode(tNode) {
22007 if (this._appliesToNextNode &&
22008 (this.metadata.flags & 1 /* QueryFlags.descendants */) !== 1 /* QueryFlags.descendants */) {
22009 const declarationNodeIdx = this._declarationNodeIndex;
22010 let parent = tNode.parent;
22011 // Determine if a given TNode is a "direct" child of a node on which a content query was
22012 // declared (only direct children of query's host node can match with the descendants: false
22013 // option). There are 3 main use-case / conditions to consider here:
22014 // - <needs-target><i #target></i></needs-target>: here <i #target> parent node is a query
22015 // host node;
22016 // - <needs-target><ng-template [ngIf]="true"><i #target></i></ng-template></needs-target>:
22017 // here <i #target> parent node is null;
22018 // - <needs-target><ng-container><i #target></i></ng-container></needs-target>: here we need
22019 // to go past `<ng-container>` to determine <i #target> parent node (but we shouldn't traverse
22020 // up past the query's host node!).
22021 while (parent !== null && (parent.type & 8 /* TNodeType.ElementContainer */) &&
22022 parent.index !== declarationNodeIdx) {
22023 parent = parent.parent;
22024 }
22025 return declarationNodeIdx === (parent !== null ? parent.index : -1);
22026 }
22027 return this._appliesToNextNode;
22028 }
22029 matchTNode(tView, tNode) {
22030 const predicate = this.metadata.predicate;
22031 if (Array.isArray(predicate)) {
22032 for (let i = 0; i < predicate.length; i++) {
22033 const name = predicate[i];
22034 this.matchTNodeWithReadOption(tView, tNode, getIdxOfMatchingSelector(tNode, name));
22035 // Also try matching the name to a provider since strings can be used as DI tokens too.
22036 this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, name, false, false));
22037 }
22038 }
22039 else {
22040 if (predicate === TemplateRef) {
22041 if (tNode.type & 4 /* TNodeType.Container */) {
22042 this.matchTNodeWithReadOption(tView, tNode, -1);
22043 }
22044 }
22045 else {
22046 this.matchTNodeWithReadOption(tView, tNode, locateDirectiveOrProvider(tNode, tView, predicate, false, false));
22047 }
22048 }
22049 }
22050 matchTNodeWithReadOption(tView, tNode, nodeMatchIdx) {
22051 if (nodeMatchIdx !== null) {
22052 const read = this.metadata.read;
22053 if (read !== null) {
22054 if (read === ElementRef || read === ViewContainerRef ||
22055 read === TemplateRef && (tNode.type & 4 /* TNodeType.Container */)) {
22056 this.addMatch(tNode.index, -2);
22057 }
22058 else {
22059 const directiveOrProviderIdx = locateDirectiveOrProvider(tNode, tView, read, false, false);
22060 if (directiveOrProviderIdx !== null) {
22061 this.addMatch(tNode.index, directiveOrProviderIdx);
22062 }
22063 }
22064 }
22065 else {
22066 this.addMatch(tNode.index, nodeMatchIdx);
22067 }
22068 }
22069 }
22070 addMatch(tNodeIdx, matchIdx) {
22071 if (this.matches === null) {
22072 this.matches = [tNodeIdx, matchIdx];
22073 }
22074 else {
22075 this.matches.push(tNodeIdx, matchIdx);
22076 }
22077 }
22078}
22079/**
22080 * Iterates over local names for a given node and returns directive index
22081 * (or -1 if a local name points to an element).
22082 *
22083 * @param tNode static data of a node to check
22084 * @param selector selector to match
22085 * @returns directive index, -1 or null if a selector didn't match any of the local names
22086 */
22087function getIdxOfMatchingSelector(tNode, selector) {
22088 const localNames = tNode.localNames;
22089 if (localNames !== null) {
22090 for (let i = 0; i < localNames.length; i += 2) {
22091 if (localNames[i] === selector) {
22092 return localNames[i + 1];
22093 }
22094 }
22095 }
22096 return null;
22097}
22098function createResultByTNodeType(tNode, currentView) {
22099 if (tNode.type & (3 /* TNodeType.AnyRNode */ | 8 /* TNodeType.ElementContainer */)) {
22100 return createElementRef(tNode, currentView);
22101 }
22102 else if (tNode.type & 4 /* TNodeType.Container */) {
22103 return createTemplateRef(tNode, currentView);
22104 }
22105 return null;
22106}
22107function createResultForNode(lView, tNode, matchingIdx, read) {
22108 if (matchingIdx === -1) {
22109 // if read token and / or strategy is not specified, detect it using appropriate tNode type
22110 return createResultByTNodeType(tNode, lView);
22111 }
22112 else if (matchingIdx === -2) {
22113 // read a special token from a node injector
22114 return createSpecialToken(lView, tNode, read);
22115 }
22116 else {
22117 // read a token
22118 return getNodeInjectable(lView, lView[TVIEW], matchingIdx, tNode);
22119 }
22120}
22121function createSpecialToken(lView, tNode, read) {
22122 if (read === ElementRef) {
22123 return createElementRef(tNode, lView);
22124 }
22125 else if (read === TemplateRef) {
22126 return createTemplateRef(tNode, lView);
22127 }
22128 else if (read === ViewContainerRef) {
22129 ngDevMode && assertTNodeType(tNode, 3 /* TNodeType.AnyRNode */ | 12 /* TNodeType.AnyContainer */);
22130 return createContainerRef(tNode, lView);
22131 }
22132 else {
22133 ngDevMode &&
22134 throwError(`Special token to read should be one of ElementRef, TemplateRef or ViewContainerRef but got ${stringify(read)}.`);
22135 }
22136}
22137/**
22138 * A helper function that creates query results for a given view. This function is meant to do the
22139 * processing once and only once for a given view instance (a set of results for a given view
22140 * doesn't change).
22141 */
22142function materializeViewResults(tView, lView, tQuery, queryIndex) {
22143 const lQuery = lView[QUERIES].queries[queryIndex];
22144 if (lQuery.matches === null) {
22145 const tViewData = tView.data;
22146 const tQueryMatches = tQuery.matches;
22147 const result = [];
22148 for (let i = 0; i < tQueryMatches.length; i += 2) {
22149 const matchedNodeIdx = tQueryMatches[i];
22150 if (matchedNodeIdx < 0) {
22151 // we at the <ng-template> marker which might have results in views created based on this
22152 // <ng-template> - those results will be in separate views though, so here we just leave
22153 // null as a placeholder
22154 result.push(null);
22155 }
22156 else {
22157 ngDevMode && assertIndexInRange(tViewData, matchedNodeIdx);
22158 const tNode = tViewData[matchedNodeIdx];
22159 result.push(createResultForNode(lView, tNode, tQueryMatches[i + 1], tQuery.metadata.read));
22160 }
22161 }
22162 lQuery.matches = result;
22163 }
22164 return lQuery.matches;
22165}
22166/**
22167 * A helper function that collects (already materialized) query results from a tree of views,
22168 * starting with a provided LView.
22169 */
22170function collectQueryResults(tView, lView, queryIndex, result) {
22171 const tQuery = tView.queries.getByIndex(queryIndex);
22172 const tQueryMatches = tQuery.matches;
22173 if (tQueryMatches !== null) {
22174 const lViewResults = materializeViewResults(tView, lView, tQuery, queryIndex);
22175 for (let i = 0; i < tQueryMatches.length; i += 2) {
22176 const tNodeIdx = tQueryMatches[i];
22177 if (tNodeIdx > 0) {
22178 result.push(lViewResults[i / 2]);
22179 }
22180 else {
22181 const childQueryIndex = tQueryMatches[i + 1];
22182 const declarationLContainer = lView[-tNodeIdx];
22183 ngDevMode && assertLContainer(declarationLContainer);
22184 // collect matches for views inserted in this container
22185 for (let i = CONTAINER_HEADER_OFFSET; i < declarationLContainer.length; i++) {
22186 const embeddedLView = declarationLContainer[i];
22187 if (embeddedLView[DECLARATION_LCONTAINER] === embeddedLView[PARENT]) {
22188 collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result);
22189 }
22190 }
22191 // collect matches for views created from this declaration container and inserted into
22192 // different containers
22193 if (declarationLContainer[MOVED_VIEWS] !== null) {
22194 const embeddedLViews = declarationLContainer[MOVED_VIEWS];
22195 for (let i = 0; i < embeddedLViews.length; i++) {
22196 const embeddedLView = embeddedLViews[i];
22197 collectQueryResults(embeddedLView[TVIEW], embeddedLView, childQueryIndex, result);
22198 }
22199 }
22200 }
22201 }
22202 }
22203 return result;
22204}
22205/**
22206 * Refreshes a query by combining matches from all active views and removing matches from deleted
22207 * views.
22208 *
22209 * @returns `true` if a query got dirty during change detection or if this is a static query
22210 * resolving in creation mode, `false` otherwise.
22211 *
22212 * @codeGenApi
22213 */
22214function ɵɵqueryRefresh(queryList) {
22215 const lView = getLView();
22216 const tView = getTView();
22217 const queryIndex = getCurrentQueryIndex();
22218 setCurrentQueryIndex(queryIndex + 1);
22219 const tQuery = getTQuery(tView, queryIndex);
22220 if (queryList.dirty &&
22221 (isCreationMode(lView) ===
22222 ((tQuery.metadata.flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */))) {
22223 if (tQuery.matches === null) {
22224 queryList.reset([]);
22225 }
22226 else {
22227 const result = tQuery.crossesNgTemplate ?
22228 collectQueryResults(tView, lView, queryIndex, []) :
22229 materializeViewResults(tView, lView, tQuery, queryIndex);
22230 queryList.reset(result, unwrapElementRef);
22231 queryList.notifyOnChanges();
22232 }
22233 return true;
22234 }
22235 return false;
22236}
22237/**
22238 * Creates new QueryList, stores the reference in LView and returns QueryList.
22239 *
22240 * @param predicate The type for which the query will search
22241 * @param flags Flags associated with the query
22242 * @param read What to save in the query
22243 *
22244 * @codeGenApi
22245 */
22246function ɵɵviewQuery(predicate, flags, read) {
22247 ngDevMode && assertNumber(flags, 'Expecting flags');
22248 const tView = getTView();
22249 if (tView.firstCreatePass) {
22250 createTQuery(tView, new TQueryMetadata_(predicate, flags, read), -1);
22251 if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) {
22252 tView.staticViewQueries = true;
22253 }
22254 }
22255 createLQuery(tView, getLView(), flags);
22256}
22257/**
22258 * Registers a QueryList, associated with a content query, for later refresh (part of a view
22259 * refresh).
22260 *
22261 * @param directiveIndex Current directive index
22262 * @param predicate The type for which the query will search
22263 * @param flags Flags associated with the query
22264 * @param read What to save in the query
22265 * @returns QueryList<T>
22266 *
22267 * @codeGenApi
22268 */
22269function ɵɵcontentQuery(directiveIndex, predicate, flags, read) {
22270 ngDevMode && assertNumber(flags, 'Expecting flags');
22271 const tView = getTView();
22272 if (tView.firstCreatePass) {
22273 const tNode = getCurrentTNode();
22274 createTQuery(tView, new TQueryMetadata_(predicate, flags, read), tNode.index);
22275 saveContentQueryAndDirectiveIndex(tView, directiveIndex);
22276 if ((flags & 2 /* QueryFlags.isStatic */) === 2 /* QueryFlags.isStatic */) {
22277 tView.staticContentQueries = true;
22278 }
22279 }
22280 createLQuery(tView, getLView(), flags);
22281}
22282/**
22283 * Loads a QueryList corresponding to the current view or content query.
22284 *
22285 * @codeGenApi
22286 */
22287function ɵɵloadQuery() {
22288 return loadQueryInternal(getLView(), getCurrentQueryIndex());
22289}
22290function loadQueryInternal(lView, queryIndex) {
22291 ngDevMode &&
22292 assertDefined(lView[QUERIES], 'LQueries should be defined when trying to load a query');
22293 ngDevMode && assertIndexInRange(lView[QUERIES].queries, queryIndex);
22294 return lView[QUERIES].queries[queryIndex].queryList;
22295}
22296function createLQuery(tView, lView, flags) {
22297 const queryList = new QueryList((flags & 4 /* QueryFlags.emitDistinctChangesOnly */) === 4 /* QueryFlags.emitDistinctChangesOnly */);
22298 storeCleanupWithContext(tView, lView, queryList, queryList.destroy);
22299 if (lView[QUERIES] === null)
22300 lView[QUERIES] = new LQueries_();
22301 lView[QUERIES].queries.push(new LQuery_(queryList));
22302}
22303function createTQuery(tView, metadata, nodeIndex) {
22304 if (tView.queries === null)
22305 tView.queries = new TQueries_();
22306 tView.queries.track(new TQuery_(metadata, nodeIndex));
22307}
22308function saveContentQueryAndDirectiveIndex(tView, directiveIndex) {
22309 const tViewContentQueries = tView.contentQueries || (tView.contentQueries = []);
22310 const lastSavedDirectiveIndex = tViewContentQueries.length ? tViewContentQueries[tViewContentQueries.length - 1] : -1;
22311 if (directiveIndex !== lastSavedDirectiveIndex) {
22312 tViewContentQueries.push(tView.queries.length - 1, directiveIndex);
22313 }
22314}
22315function getTQuery(tView, index) {
22316 ngDevMode && assertDefined(tView.queries, 'TQueries must be defined to retrieve a TQuery');
22317 return tView.queries.getByIndex(index);
22318}
22319
22320/**
22321 * Retrieves `TemplateRef` instance from `Injector` when a local reference is placed on the
22322 * `<ng-template>` element.
22323 *
22324 * @codeGenApi
22325 */
22326function ɵɵtemplateRefExtractor(tNode, lView) {
22327 return createTemplateRef(tNode, lView);
22328}
22329
22330/**
22331 * A mapping of the @angular/core API surface used in generated expressions to the actual symbols.
22332 *
22333 * This should be kept up to date with the public exports of @angular/core.
22334 */
22335const angularCoreEnv = (() => ({
22336 'ɵɵattribute': ɵɵattribute,
22337 'ɵɵattributeInterpolate1': ɵɵattributeInterpolate1,
22338 'ɵɵattributeInterpolate2': ɵɵattributeInterpolate2,
22339 'ɵɵattributeInterpolate3': ɵɵattributeInterpolate3,
22340 'ɵɵattributeInterpolate4': ɵɵattributeInterpolate4,
22341 'ɵɵattributeInterpolate5': ɵɵattributeInterpolate5,
22342 'ɵɵattributeInterpolate6': ɵɵattributeInterpolate6,
22343 'ɵɵattributeInterpolate7': ɵɵattributeInterpolate7,
22344 'ɵɵattributeInterpolate8': ɵɵattributeInterpolate8,
22345 'ɵɵattributeInterpolateV': ɵɵattributeInterpolateV,
22346 'ɵɵdefineComponent': ɵɵdefineComponent,
22347 'ɵɵdefineDirective': ɵɵdefineDirective,
22348 'ɵɵdefineInjectable': ɵɵdefineInjectable,
22349 'ɵɵdefineInjector': ɵɵdefineInjector,
22350 'ɵɵdefineNgModule': ɵɵdefineNgModule,
22351 'ɵɵdefinePipe': ɵɵdefinePipe,
22352 'ɵɵdirectiveInject': ɵɵdirectiveInject,
22353 'ɵɵgetInheritedFactory': ɵɵgetInheritedFactory,
22354 'ɵɵinject': ɵɵinject,
22355 'ɵɵinjectAttribute': ɵɵinjectAttribute,
22356 'ɵɵinvalidFactory': ɵɵinvalidFactory,
22357 'ɵɵinvalidFactoryDep': ɵɵinvalidFactoryDep,
22358 'ɵɵtemplateRefExtractor': ɵɵtemplateRefExtractor,
22359 'ɵɵresetView': ɵɵresetView,
22360 'ɵɵHostDirectivesFeature': ɵɵHostDirectivesFeature,
22361 'ɵɵNgOnChangesFeature': ɵɵNgOnChangesFeature,
22362 'ɵɵProvidersFeature': ɵɵProvidersFeature,
22363 'ɵɵCopyDefinitionFeature': ɵɵCopyDefinitionFeature,
22364 'ɵɵInheritDefinitionFeature': ɵɵInheritDefinitionFeature,
22365 'ɵɵStandaloneFeature': ɵɵStandaloneFeature,
22366 'ɵɵnextContext': ɵɵnextContext,
22367 'ɵɵnamespaceHTML': ɵɵnamespaceHTML,
22368 'ɵɵnamespaceMathML': ɵɵnamespaceMathML,
22369 'ɵɵnamespaceSVG': ɵɵnamespaceSVG,
22370 'ɵɵenableBindings': ɵɵenableBindings,
22371 'ɵɵdisableBindings': ɵɵdisableBindings,
22372 'ɵɵelementStart': ɵɵelementStart,
22373 'ɵɵelementEnd': ɵɵelementEnd,
22374 'ɵɵelement': ɵɵelement,
22375 'ɵɵelementContainerStart': ɵɵelementContainerStart,
22376 'ɵɵelementContainerEnd': ɵɵelementContainerEnd,
22377 'ɵɵelementContainer': ɵɵelementContainer,
22378 'ɵɵpureFunction0': ɵɵpureFunction0,
22379 'ɵɵpureFunction1': ɵɵpureFunction1,
22380 'ɵɵpureFunction2': ɵɵpureFunction2,
22381 'ɵɵpureFunction3': ɵɵpureFunction3,
22382 'ɵɵpureFunction4': ɵɵpureFunction4,
22383 'ɵɵpureFunction5': ɵɵpureFunction5,
22384 'ɵɵpureFunction6': ɵɵpureFunction6,
22385 'ɵɵpureFunction7': ɵɵpureFunction7,
22386 'ɵɵpureFunction8': ɵɵpureFunction8,
22387 'ɵɵpureFunctionV': ɵɵpureFunctionV,
22388 'ɵɵgetCurrentView': ɵɵgetCurrentView,
22389 'ɵɵrestoreView': ɵɵrestoreView,
22390 'ɵɵlistener': ɵɵlistener,
22391 'ɵɵprojection': ɵɵprojection,
22392 'ɵɵsyntheticHostProperty': ɵɵsyntheticHostProperty,
22393 'ɵɵsyntheticHostListener': ɵɵsyntheticHostListener,
22394 'ɵɵpipeBind1': ɵɵpipeBind1,
22395 'ɵɵpipeBind2': ɵɵpipeBind2,
22396 'ɵɵpipeBind3': ɵɵpipeBind3,
22397 'ɵɵpipeBind4': ɵɵpipeBind4,
22398 'ɵɵpipeBindV': ɵɵpipeBindV,
22399 'ɵɵprojectionDef': ɵɵprojectionDef,
22400 'ɵɵhostProperty': ɵɵhostProperty,
22401 'ɵɵproperty': ɵɵproperty,
22402 'ɵɵpropertyInterpolate': ɵɵpropertyInterpolate,
22403 'ɵɵpropertyInterpolate1': ɵɵpropertyInterpolate1,
22404 'ɵɵpropertyInterpolate2': ɵɵpropertyInterpolate2,
22405 'ɵɵpropertyInterpolate3': ɵɵpropertyInterpolate3,
22406 'ɵɵpropertyInterpolate4': ɵɵpropertyInterpolate4,
22407 'ɵɵpropertyInterpolate5': ɵɵpropertyInterpolate5,
22408 'ɵɵpropertyInterpolate6': ɵɵpropertyInterpolate6,
22409 'ɵɵpropertyInterpolate7': ɵɵpropertyInterpolate7,
22410 'ɵɵpropertyInterpolate8': ɵɵpropertyInterpolate8,
22411 'ɵɵpropertyInterpolateV': ɵɵpropertyInterpolateV,
22412 'ɵɵpipe': ɵɵpipe,
22413 'ɵɵqueryRefresh': ɵɵqueryRefresh,
22414 'ɵɵviewQuery': ɵɵviewQuery,
22415 'ɵɵloadQuery': ɵɵloadQuery,
22416 'ɵɵcontentQuery': ɵɵcontentQuery,
22417 'ɵɵreference': ɵɵreference,
22418 'ɵɵclassMap': ɵɵclassMap,
22419 'ɵɵclassMapInterpolate1': ɵɵclassMapInterpolate1,
22420 'ɵɵclassMapInterpolate2': ɵɵclassMapInterpolate2,
22421 'ɵɵclassMapInterpolate3': ɵɵclassMapInterpolate3,
22422 'ɵɵclassMapInterpolate4': ɵɵclassMapInterpolate4,
22423 'ɵɵclassMapInterpolate5': ɵɵclassMapInterpolate5,
22424 'ɵɵclassMapInterpolate6': ɵɵclassMapInterpolate6,
22425 'ɵɵclassMapInterpolate7': ɵɵclassMapInterpolate7,
22426 'ɵɵclassMapInterpolate8': ɵɵclassMapInterpolate8,
22427 'ɵɵclassMapInterpolateV': ɵɵclassMapInterpolateV,
22428 'ɵɵstyleMap': ɵɵstyleMap,
22429 'ɵɵstyleMapInterpolate1': ɵɵstyleMapInterpolate1,
22430 'ɵɵstyleMapInterpolate2': ɵɵstyleMapInterpolate2,
22431 'ɵɵstyleMapInterpolate3': ɵɵstyleMapInterpolate3,
22432 'ɵɵstyleMapInterpolate4': ɵɵstyleMapInterpolate4,
22433 'ɵɵstyleMapInterpolate5': ɵɵstyleMapInterpolate5,
22434 'ɵɵstyleMapInterpolate6': ɵɵstyleMapInterpolate6,
22435 'ɵɵstyleMapInterpolate7': ɵɵstyleMapInterpolate7,
22436 'ɵɵstyleMapInterpolate8': ɵɵstyleMapInterpolate8,
22437 'ɵɵstyleMapInterpolateV': ɵɵstyleMapInterpolateV,
22438 'ɵɵstyleProp': ɵɵstyleProp,
22439 'ɵɵstylePropInterpolate1': ɵɵstylePropInterpolate1,
22440 'ɵɵstylePropInterpolate2': ɵɵstylePropInterpolate2,
22441 'ɵɵstylePropInterpolate3': ɵɵstylePropInterpolate3,
22442 'ɵɵstylePropInterpolate4': ɵɵstylePropInterpolate4,
22443 'ɵɵstylePropInterpolate5': ɵɵstylePropInterpolate5,
22444 'ɵɵstylePropInterpolate6': ɵɵstylePropInterpolate6,
22445 'ɵɵstylePropInterpolate7': ɵɵstylePropInterpolate7,
22446 'ɵɵstylePropInterpolate8': ɵɵstylePropInterpolate8,
22447 'ɵɵstylePropInterpolateV': ɵɵstylePropInterpolateV,
22448 'ɵɵclassProp': ɵɵclassProp,
22449 'ɵɵadvance': ɵɵadvance,
22450 'ɵɵtemplate': ɵɵtemplate,
22451 'ɵɵtext': ɵɵtext,
22452 'ɵɵtextInterpolate': ɵɵtextInterpolate,
22453 'ɵɵtextInterpolate1': ɵɵtextInterpolate1,
22454 'ɵɵtextInterpolate2': ɵɵtextInterpolate2,
22455 'ɵɵtextInterpolate3': ɵɵtextInterpolate3,
22456 'ɵɵtextInterpolate4': ɵɵtextInterpolate4,
22457 'ɵɵtextInterpolate5': ɵɵtextInterpolate5,
22458 'ɵɵtextInterpolate6': ɵɵtextInterpolate6,
22459 'ɵɵtextInterpolate7': ɵɵtextInterpolate7,
22460 'ɵɵtextInterpolate8': ɵɵtextInterpolate8,
22461 'ɵɵtextInterpolateV': ɵɵtextInterpolateV,
22462 'ɵɵi18n': ɵɵi18n,
22463 'ɵɵi18nAttributes': ɵɵi18nAttributes,
22464 'ɵɵi18nExp': ɵɵi18nExp,
22465 'ɵɵi18nStart': ɵɵi18nStart,
22466 'ɵɵi18nEnd': ɵɵi18nEnd,
22467 'ɵɵi18nApply': ɵɵi18nApply,
22468 'ɵɵi18nPostprocess': ɵɵi18nPostprocess,
22469 'ɵɵresolveWindow': ɵɵresolveWindow,
22470 'ɵɵresolveDocument': ɵɵresolveDocument,
22471 'ɵɵresolveBody': ɵɵresolveBody,
22472 'ɵɵsetComponentScope': ɵɵsetComponentScope,
22473 'ɵɵsetNgModuleScope': ɵɵsetNgModuleScope,
22474 'ɵɵregisterNgModuleType': registerNgModuleType,
22475 'ɵɵsanitizeHtml': ɵɵsanitizeHtml,
22476 'ɵɵsanitizeStyle': ɵɵsanitizeStyle,
22477 'ɵɵsanitizeResourceUrl': ɵɵsanitizeResourceUrl,
22478 'ɵɵsanitizeScript': ɵɵsanitizeScript,
22479 'ɵɵsanitizeUrl': ɵɵsanitizeUrl,
22480 'ɵɵsanitizeUrlOrResourceUrl': ɵɵsanitizeUrlOrResourceUrl,
22481 'ɵɵtrustConstantHtml': ɵɵtrustConstantHtml,
22482 'ɵɵtrustConstantResourceUrl': ɵɵtrustConstantResourceUrl,
22483 'ɵɵvalidateIframeAttribute': ɵɵvalidateIframeAttribute,
22484 'forwardRef': forwardRef,
22485 'resolveForwardRef': resolveForwardRef,
22486}))();
22487
22488function patchModuleCompilation() {
22489 // Does nothing, but exists as a target for patching.
22490}
22491
22492function isModuleWithProviders$1(value) {
22493 return value.ngModule !== undefined;
22494}
22495function isNgModule$1(value) {
22496 return !!getNgModuleDef(value);
22497}
22498
22499const moduleQueue = [];
22500/**
22501 * Enqueues moduleDef to be checked later to see if scope can be set on its
22502 * component declarations.
22503 */
22504function enqueueModuleForDelayedScoping(moduleType, ngModule) {
22505 moduleQueue.push({ moduleType, ngModule });
22506}
22507let flushingModuleQueue = false;
22508/**
22509 * Loops over queued module definitions, if a given module definition has all of its
22510 * declarations resolved, it dequeues that module definition and sets the scope on
22511 * its declarations.
22512 */
22513function flushModuleScopingQueueAsMuchAsPossible() {
22514 if (!flushingModuleQueue) {
22515 flushingModuleQueue = true;
22516 try {
22517 for (let i = moduleQueue.length - 1; i >= 0; i--) {
22518 const { moduleType, ngModule } = moduleQueue[i];
22519 if (ngModule.declarations && ngModule.declarations.every(isResolvedDeclaration)) {
22520 // dequeue
22521 moduleQueue.splice(i, 1);
22522 setScopeOnDeclaredComponents(moduleType, ngModule);
22523 }
22524 }
22525 }
22526 finally {
22527 flushingModuleQueue = false;
22528 }
22529 }
22530}
22531/**
22532 * Returns truthy if a declaration has resolved. If the declaration happens to be
22533 * an array of declarations, it will recurse to check each declaration in that array
22534 * (which may also be arrays).
22535 */
22536function isResolvedDeclaration(declaration) {
22537 if (Array.isArray(declaration)) {
22538 return declaration.every(isResolvedDeclaration);
22539 }
22540 return !!resolveForwardRef(declaration);
22541}
22542/**
22543 * Compiles a module in JIT mode.
22544 *
22545 * This function automatically gets called when a class has a `@NgModule` decorator.
22546 */
22547function compileNgModule(moduleType, ngModule = {}) {
22548 patchModuleCompilation();
22549 compileNgModuleDefs(moduleType, ngModule);
22550 if (ngModule.id !== undefined) {
22551 registerNgModuleType(moduleType, ngModule.id);
22552 }
22553 // Because we don't know if all declarations have resolved yet at the moment the
22554 // NgModule decorator is executing, we're enqueueing the setting of module scope
22555 // on its declarations to be run at a later time when all declarations for the module,
22556 // including forward refs, have resolved.
22557 enqueueModuleForDelayedScoping(moduleType, ngModule);
22558}
22559/**
22560 * Compiles and adds the `ɵmod`, `ɵfac` and `ɵinj` properties to the module class.
22561 *
22562 * It's possible to compile a module via this API which will allow duplicate declarations in its
22563 * root.
22564 */
22565function compileNgModuleDefs(moduleType, ngModule, allowDuplicateDeclarationsInRoot = false) {
22566 ngDevMode && assertDefined(moduleType, 'Required value moduleType');
22567 ngDevMode && assertDefined(ngModule, 'Required value ngModule');
22568 const declarations = flatten$1(ngModule.declarations || EMPTY_ARRAY);
22569 let ngModuleDef = null;
22570 Object.defineProperty(moduleType, NG_MOD_DEF, {
22571 configurable: true,
22572 get: () => {
22573 if (ngModuleDef === null) {
22574 if (ngDevMode && ngModule.imports && ngModule.imports.indexOf(moduleType) > -1) {
22575 // We need to assert this immediately, because allowing it to continue will cause it to
22576 // go into an infinite loop before we've reached the point where we throw all the errors.
22577 throw new Error(`'${stringifyForError(moduleType)}' module can't import itself`);
22578 }
22579 const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
22580 ngModuleDef = compiler.compileNgModule(angularCoreEnv, `ng:///${moduleType.name}/ɵmod.js`, {
22581 type: moduleType,
22582 bootstrap: flatten$1(ngModule.bootstrap || EMPTY_ARRAY).map(resolveForwardRef),
22583 declarations: declarations.map(resolveForwardRef),
22584 imports: flatten$1(ngModule.imports || EMPTY_ARRAY)
22585 .map(resolveForwardRef)
22586 .map(expandModuleWithProviders),
22587 exports: flatten$1(ngModule.exports || EMPTY_ARRAY)
22588 .map(resolveForwardRef)
22589 .map(expandModuleWithProviders),
22590 schemas: ngModule.schemas ? flatten$1(ngModule.schemas) : null,
22591 id: ngModule.id || null,
22592 });
22593 // Set `schemas` on ngModuleDef to an empty array in JIT mode to indicate that runtime
22594 // should verify that there are no unknown elements in a template. In AOT mode, that check
22595 // happens at compile time and `schemas` information is not present on Component and Module
22596 // defs after compilation (so the check doesn't happen the second time at runtime).
22597 if (!ngModuleDef.schemas) {
22598 ngModuleDef.schemas = [];
22599 }
22600 }
22601 return ngModuleDef;
22602 }
22603 });
22604 let ngFactoryDef = null;
22605 Object.defineProperty(moduleType, NG_FACTORY_DEF, {
22606 get: () => {
22607 if (ngFactoryDef === null) {
22608 const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
22609 ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${moduleType.name}/ɵfac.js`, {
22610 name: moduleType.name,
22611 type: moduleType,
22612 deps: reflectDependencies(moduleType),
22613 target: compiler.FactoryTarget.NgModule,
22614 typeArgumentCount: 0,
22615 });
22616 }
22617 return ngFactoryDef;
22618 },
22619 // Make the property configurable in dev mode to allow overriding in tests
22620 configurable: !!ngDevMode,
22621 });
22622 let ngInjectorDef = null;
22623 Object.defineProperty(moduleType, NG_INJ_DEF, {
22624 get: () => {
22625 if (ngInjectorDef === null) {
22626 ngDevMode && verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot);
22627 const meta = {
22628 name: moduleType.name,
22629 type: moduleType,
22630 providers: ngModule.providers || EMPTY_ARRAY,
22631 imports: [
22632 (ngModule.imports || EMPTY_ARRAY).map(resolveForwardRef),
22633 (ngModule.exports || EMPTY_ARRAY).map(resolveForwardRef),
22634 ],
22635 };
22636 const compiler = getCompilerFacade({ usage: 0 /* JitCompilerUsage.Decorator */, kind: 'NgModule', type: moduleType });
22637 ngInjectorDef =
22638 compiler.compileInjector(angularCoreEnv, `ng:///${moduleType.name}/ɵinj.js`, meta);
22639 }
22640 return ngInjectorDef;
22641 },
22642 // Make the property configurable in dev mode to allow overriding in tests
22643 configurable: !!ngDevMode,
22644 });
22645}
22646function generateStandaloneInDeclarationsError(type, location) {
22647 const prefix = `Unexpected "${stringifyForError(type)}" found in the "declarations" array of the`;
22648 const suffix = `"${stringifyForError(type)}" is marked as standalone and can't be declared ` +
22649 'in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?';
22650 return `${prefix} ${location}, ${suffix}`;
22651}
22652function verifySemanticsOfNgModuleDef(moduleType, allowDuplicateDeclarationsInRoot, importingModule) {
22653 if (verifiedNgModule.get(moduleType))
22654 return;
22655 // skip verifications of standalone components, directives, and pipes
22656 if (isStandalone(moduleType))
22657 return;
22658 verifiedNgModule.set(moduleType, true);
22659 moduleType = resolveForwardRef(moduleType);
22660 let ngModuleDef;
22661 if (importingModule) {
22662 ngModuleDef = getNgModuleDef(moduleType);
22663 if (!ngModuleDef) {
22664 throw new Error(`Unexpected value '${moduleType.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
22665 }
22666 }
22667 else {
22668 ngModuleDef = getNgModuleDef(moduleType, true);
22669 }
22670 const errors = [];
22671 const declarations = maybeUnwrapFn$1(ngModuleDef.declarations);
22672 const imports = maybeUnwrapFn$1(ngModuleDef.imports);
22673 flatten$1(imports).map(unwrapModuleWithProvidersImports).forEach(modOrStandaloneCmpt => {
22674 verifySemanticsOfNgModuleImport(modOrStandaloneCmpt, moduleType);
22675 verifySemanticsOfNgModuleDef(modOrStandaloneCmpt, false, moduleType);
22676 });
22677 const exports = maybeUnwrapFn$1(ngModuleDef.exports);
22678 declarations.forEach(verifyDeclarationsHaveDefinitions);
22679 declarations.forEach(verifyDirectivesHaveSelector);
22680 declarations.forEach((declarationType) => verifyNotStandalone(declarationType, moduleType));
22681 const combinedDeclarations = [
22682 ...declarations.map(resolveForwardRef),
22683 ...flatten$1(imports.map(computeCombinedExports)).map(resolveForwardRef),
22684 ];
22685 exports.forEach(verifyExportsAreDeclaredOrReExported);
22686 declarations.forEach(decl => verifyDeclarationIsUnique(decl, allowDuplicateDeclarationsInRoot));
22687 declarations.forEach(verifyComponentEntryComponentsIsPartOfNgModule);
22688 const ngModule = getAnnotation(moduleType, 'NgModule');
22689 if (ngModule) {
22690 ngModule.imports &&
22691 flatten$1(ngModule.imports).map(unwrapModuleWithProvidersImports).forEach(mod => {
22692 verifySemanticsOfNgModuleImport(mod, moduleType);
22693 verifySemanticsOfNgModuleDef(mod, false, moduleType);
22694 });
22695 ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyCorrectBootstrapType);
22696 ngModule.bootstrap && deepForEach(ngModule.bootstrap, verifyComponentIsPartOfNgModule);
22697 ngModule.entryComponents &&
22698 deepForEach(ngModule.entryComponents, verifyComponentIsPartOfNgModule);
22699 }
22700 // Throw Error if any errors were detected.
22701 if (errors.length) {
22702 throw new Error(errors.join('\n'));
22703 }
22704 ////////////////////////////////////////////////////////////////////////////////////////////////
22705 function verifyDeclarationsHaveDefinitions(type) {
22706 type = resolveForwardRef(type);
22707 const def = getComponentDef$1(type) || getDirectiveDef(type) || getPipeDef$1(type);
22708 if (!def) {
22709 errors.push(`Unexpected value '${stringifyForError(type)}' declared by the module '${stringifyForError(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`);
22710 }
22711 }
22712 function verifyDirectivesHaveSelector(type) {
22713 type = resolveForwardRef(type);
22714 const def = getDirectiveDef(type);
22715 if (!getComponentDef$1(type) && def && def.selectors.length == 0) {
22716 errors.push(`Directive ${stringifyForError(type)} has no selector, please add it!`);
22717 }
22718 }
22719 function verifyNotStandalone(type, moduleType) {
22720 type = resolveForwardRef(type);
22721 const def = getComponentDef$1(type) || getDirectiveDef(type) || getPipeDef$1(type);
22722 if (def?.standalone) {
22723 const location = `"${stringifyForError(moduleType)}" NgModule`;
22724 errors.push(generateStandaloneInDeclarationsError(type, location));
22725 }
22726 }
22727 function verifyExportsAreDeclaredOrReExported(type) {
22728 type = resolveForwardRef(type);
22729 const kind = getComponentDef$1(type) && 'component' || getDirectiveDef(type) && 'directive' ||
22730 getPipeDef$1(type) && 'pipe';
22731 if (kind) {
22732 // only checked if we are declared as Component, Directive, or Pipe
22733 // Modules don't need to be declared or imported.
22734 if (combinedDeclarations.lastIndexOf(type) === -1) {
22735 // We are exporting something which we don't explicitly declare or import.
22736 errors.push(`Can't export ${kind} ${stringifyForError(type)} from ${stringifyForError(moduleType)} as it was neither declared nor imported!`);
22737 }
22738 }
22739 }
22740 function verifyDeclarationIsUnique(type, suppressErrors) {
22741 type = resolveForwardRef(type);
22742 const existingModule = ownerNgModule.get(type);
22743 if (existingModule && existingModule !== moduleType) {
22744 if (!suppressErrors) {
22745 const modules = [existingModule, moduleType].map(stringifyForError).sort();
22746 errors.push(`Type ${stringifyForError(type)} is part of the declarations of 2 modules: ${modules[0]} and ${modules[1]}! ` +
22747 `Please consider moving ${stringifyForError(type)} to a higher module that imports ${modules[0]} and ${modules[1]}. ` +
22748 `You can also create a new NgModule that exports and includes ${stringifyForError(type)} then import that NgModule in ${modules[0]} and ${modules[1]}.`);
22749 }
22750 }
22751 else {
22752 // Mark type as having owner.
22753 ownerNgModule.set(type, moduleType);
22754 }
22755 }
22756 function verifyComponentIsPartOfNgModule(type) {
22757 type = resolveForwardRef(type);
22758 const existingModule = ownerNgModule.get(type);
22759 if (!existingModule && !isStandalone(type)) {
22760 errors.push(`Component ${stringifyForError(type)} is not part of any NgModule or the module has not been imported into your module.`);
22761 }
22762 }
22763 function verifyCorrectBootstrapType(type) {
22764 type = resolveForwardRef(type);
22765 if (!getComponentDef$1(type)) {
22766 errors.push(`${stringifyForError(type)} cannot be used as an entry component.`);
22767 }
22768 if (isStandalone(type)) {
22769 // Note: this error should be the same as the
22770 // `NGMODULE_BOOTSTRAP_IS_STANDALONE` one in AOT compiler.
22771 errors.push(`The \`${stringifyForError(type)}\` class is a standalone component, which can ` +
22772 `not be used in the \`@NgModule.bootstrap\` array. Use the \`bootstrapApplication\` ` +
22773 `function for bootstrap instead.`);
22774 }
22775 }
22776 function verifyComponentEntryComponentsIsPartOfNgModule(type) {
22777 type = resolveForwardRef(type);
22778 if (getComponentDef$1(type)) {
22779 // We know we are component
22780 const component = getAnnotation(type, 'Component');
22781 if (component && component.entryComponents) {
22782 deepForEach(component.entryComponents, verifyComponentIsPartOfNgModule);
22783 }
22784 }
22785 }
22786 function verifySemanticsOfNgModuleImport(type, importingModule) {
22787 type = resolveForwardRef(type);
22788 const directiveDef = getComponentDef$1(type) || getDirectiveDef(type);
22789 if (directiveDef !== null && !directiveDef.standalone) {
22790 throw new Error(`Unexpected directive '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
22791 }
22792 const pipeDef = getPipeDef$1(type);
22793 if (pipeDef !== null && !pipeDef.standalone) {
22794 throw new Error(`Unexpected pipe '${type.name}' imported by the module '${importingModule.name}'. Please add an @NgModule annotation.`);
22795 }
22796 }
22797}
22798function unwrapModuleWithProvidersImports(typeOrWithProviders) {
22799 typeOrWithProviders = resolveForwardRef(typeOrWithProviders);
22800 return typeOrWithProviders.ngModule || typeOrWithProviders;
22801}
22802function getAnnotation(type, name) {
22803 let annotation = null;
22804 collect(type.__annotations__);
22805 collect(type.decorators);
22806 return annotation;
22807 function collect(annotations) {
22808 if (annotations) {
22809 annotations.forEach(readAnnotation);
22810 }
22811 }
22812 function readAnnotation(decorator) {
22813 if (!annotation) {
22814 const proto = Object.getPrototypeOf(decorator);
22815 if (proto.ngMetadataName == name) {
22816 annotation = decorator;
22817 }
22818 else if (decorator.type) {
22819 const proto = Object.getPrototypeOf(decorator.type);
22820 if (proto.ngMetadataName == name) {
22821 annotation = decorator.args[0];
22822 }
22823 }
22824 }
22825 }
22826}
22827/**
22828 * Keep track of compiled components. This is needed because in tests we often want to compile the
22829 * same component with more than one NgModule. This would cause an error unless we reset which
22830 * NgModule the component belongs to. We keep the list of compiled components here so that the
22831 * TestBed can reset it later.
22832 */
22833let ownerNgModule = new WeakMap();
22834let verifiedNgModule = new WeakMap();
22835function resetCompiledComponents() {
22836 ownerNgModule = new WeakMap();
22837 verifiedNgModule = new WeakMap();
22838 moduleQueue.length = 0;
22839}
22840/**
22841 * Computes the combined declarations of explicit declarations, as well as declarations inherited by
22842 * traversing the exports of imported modules.
22843 * @param type
22844 */
22845function computeCombinedExports(type) {
22846 type = resolveForwardRef(type);
22847 const ngModuleDef = getNgModuleDef(type);
22848 // a standalone component, directive or pipe
22849 if (ngModuleDef === null) {
22850 return [type];
22851 }
22852 return [...flatten$1(maybeUnwrapFn$1(ngModuleDef.exports).map((type) => {
22853 const ngModuleDef = getNgModuleDef(type);
22854 if (ngModuleDef) {
22855 verifySemanticsOfNgModuleDef(type, false);
22856 return computeCombinedExports(type);
22857 }
22858 else {
22859 return type;
22860 }
22861 }))];
22862}
22863/**
22864 * Some declared components may be compiled asynchronously, and thus may not have their
22865 * ɵcmp set yet. If this is the case, then a reference to the module is written into
22866 * the `ngSelectorScope` property of the declared type.
22867 */
22868function setScopeOnDeclaredComponents(moduleType, ngModule) {
22869 const declarations = flatten$1(ngModule.declarations || EMPTY_ARRAY);
22870 const transitiveScopes = transitiveScopesFor(moduleType);
22871 declarations.forEach(declaration => {
22872 declaration = resolveForwardRef(declaration);
22873 if (declaration.hasOwnProperty(NG_COMP_DEF)) {
22874 // A `ɵcmp` field exists - go ahead and patch the component directly.
22875 const component = declaration;
22876 const componentDef = getComponentDef$1(component);
22877 patchComponentDefWithScope(componentDef, transitiveScopes);
22878 }
22879 else if (!declaration.hasOwnProperty(NG_DIR_DEF) && !declaration.hasOwnProperty(NG_PIPE_DEF)) {
22880 // Set `ngSelectorScope` for future reference when the component compilation finishes.
22881 declaration.ngSelectorScope = moduleType;
22882 }
22883 });
22884}
22885/**
22886 * Patch the definition of a component with directives and pipes from the compilation scope of
22887 * a given module.
22888 */
22889function patchComponentDefWithScope(componentDef, transitiveScopes) {
22890 componentDef.directiveDefs = () => Array.from(transitiveScopes.compilation.directives)
22891 .map(dir => dir.hasOwnProperty(NG_COMP_DEF) ? getComponentDef$1(dir) : getDirectiveDef(dir))
22892 .filter(def => !!def);
22893 componentDef.pipeDefs = () => Array.from(transitiveScopes.compilation.pipes).map(pipe => getPipeDef$1(pipe));
22894 componentDef.schemas = transitiveScopes.schemas;
22895 // Since we avoid Components/Directives/Pipes recompiling in case there are no overrides, we
22896 // may face a problem where previously compiled defs available to a given Component/Directive
22897 // are cached in TView and may become stale (in case any of these defs gets recompiled). In
22898 // order to avoid this problem, we force fresh TView to be created.
22899 componentDef.tView = null;
22900}
22901/**
22902 * Compute the pair of transitive scopes (compilation scope and exported scope) for a given type
22903 * (either a NgModule or a standalone component / directive / pipe).
22904 */
22905function transitiveScopesFor(type) {
22906 if (isNgModule$1(type)) {
22907 return transitiveScopesForNgModule(type);
22908 }
22909 else if (isStandalone(type)) {
22910 const directiveDef = getComponentDef$1(type) || getDirectiveDef(type);
22911 if (directiveDef !== null) {
22912 return {
22913 schemas: null,
22914 compilation: {
22915 directives: new Set(),
22916 pipes: new Set(),
22917 },
22918 exported: {
22919 directives: new Set([type]),
22920 pipes: new Set(),
22921 },
22922 };
22923 }
22924 const pipeDef = getPipeDef$1(type);
22925 if (pipeDef !== null) {
22926 return {
22927 schemas: null,
22928 compilation: {
22929 directives: new Set(),
22930 pipes: new Set(),
22931 },
22932 exported: {
22933 directives: new Set(),
22934 pipes: new Set([type]),
22935 },
22936 };
22937 }
22938 }
22939 // TODO: change the error message to be more user-facing and take standalone into account
22940 throw new Error(`${type.name} does not have a module def (ɵmod property)`);
22941}
22942/**
22943 * Compute the pair of transitive scopes (compilation scope and exported scope) for a given module.
22944 *
22945 * This operation is memoized and the result is cached on the module's definition. This function can
22946 * be called on modules with components that have not fully compiled yet, but the result should not
22947 * be used until they have.
22948 *
22949 * @param moduleType module that transitive scope should be calculated for.
22950 */
22951function transitiveScopesForNgModule(moduleType) {
22952 const def = getNgModuleDef(moduleType, true);
22953 if (def.transitiveCompileScopes !== null) {
22954 return def.transitiveCompileScopes;
22955 }
22956 const scopes = {
22957 schemas: def.schemas || null,
22958 compilation: {
22959 directives: new Set(),
22960 pipes: new Set(),
22961 },
22962 exported: {
22963 directives: new Set(),
22964 pipes: new Set(),
22965 },
22966 };
22967 maybeUnwrapFn$1(def.imports).forEach((imported) => {
22968 // When this module imports another, the imported module's exported directives and pipes are
22969 // added to the compilation scope of this module.
22970 const importedScope = transitiveScopesFor(imported);
22971 importedScope.exported.directives.forEach(entry => scopes.compilation.directives.add(entry));
22972 importedScope.exported.pipes.forEach(entry => scopes.compilation.pipes.add(entry));
22973 });
22974 maybeUnwrapFn$1(def.declarations).forEach(declared => {
22975 const declaredWithDefs = declared;
22976 if (getPipeDef$1(declaredWithDefs)) {
22977 scopes.compilation.pipes.add(declared);
22978 }
22979 else {
22980 // Either declared has a ɵcmp or ɵdir, or it's a component which hasn't
22981 // had its template compiled yet. In either case, it gets added to the compilation's
22982 // directives.
22983 scopes.compilation.directives.add(declared);
22984 }
22985 });
22986 maybeUnwrapFn$1(def.exports).forEach((exported) => {
22987 const exportedType = exported;
22988 // Either the type is a module, a pipe, or a component/directive (which may not have a
22989 // ɵcmp as it might be compiled asynchronously).
22990 if (isNgModule$1(exportedType)) {
22991 // When this module exports another, the exported module's exported directives and pipes are
22992 // added to both the compilation and exported scopes of this module.
22993 const exportedScope = transitiveScopesFor(exportedType);
22994 exportedScope.exported.directives.forEach(entry => {
22995 scopes.compilation.directives.add(entry);
22996 scopes.exported.directives.add(entry);
22997 });
22998 exportedScope.exported.pipes.forEach(entry => {
22999 scopes.compilation.pipes.add(entry);
23000 scopes.exported.pipes.add(entry);
23001 });
23002 }
23003 else if (getPipeDef$1(exportedType)) {
23004 scopes.exported.pipes.add(exportedType);
23005 }
23006 else {
23007 scopes.exported.directives.add(exportedType);
23008 }
23009 });
23010 def.transitiveCompileScopes = scopes;
23011 return scopes;
23012}
23013function expandModuleWithProviders(value) {
23014 if (isModuleWithProviders$1(value)) {
23015 return value.ngModule;
23016 }
23017 return value;
23018}
23019
23020let _nextReferenceId = 0;
23021class MetadataOverrider {
23022 constructor() {
23023 this._references = new Map();
23024 }
23025 /**
23026 * Creates a new instance for the given metadata class
23027 * based on an old instance and overrides.
23028 */
23029 overrideMetadata(metadataClass, oldMetadata, override) {
23030 const props = {};
23031 if (oldMetadata) {
23032 _valueProps(oldMetadata).forEach((prop) => props[prop] = oldMetadata[prop]);
23033 }
23034 if (override.set) {
23035 if (override.remove || override.add) {
23036 throw new Error(`Cannot set and add/remove ${ɵstringify(metadataClass)} at the same time!`);
23037 }
23038 setMetadata(props, override.set);
23039 }
23040 if (override.remove) {
23041 removeMetadata(props, override.remove, this._references);
23042 }
23043 if (override.add) {
23044 addMetadata(props, override.add);
23045 }
23046 return new metadataClass(props);
23047 }
23048}
23049function removeMetadata(metadata, remove, references) {
23050 const removeObjects = new Set();
23051 for (const prop in remove) {
23052 const removeValue = remove[prop];
23053 if (Array.isArray(removeValue)) {
23054 removeValue.forEach((value) => {
23055 removeObjects.add(_propHashKey(prop, value, references));
23056 });
23057 }
23058 else {
23059 removeObjects.add(_propHashKey(prop, removeValue, references));
23060 }
23061 }
23062 for (const prop in metadata) {
23063 const propValue = metadata[prop];
23064 if (Array.isArray(propValue)) {
23065 metadata[prop] = propValue.filter((value) => !removeObjects.has(_propHashKey(prop, value, references)));
23066 }
23067 else {
23068 if (removeObjects.has(_propHashKey(prop, propValue, references))) {
23069 metadata[prop] = undefined;
23070 }
23071 }
23072 }
23073}
23074function addMetadata(metadata, add) {
23075 for (const prop in add) {
23076 const addValue = add[prop];
23077 const propValue = metadata[prop];
23078 if (propValue != null && Array.isArray(propValue)) {
23079 metadata[prop] = propValue.concat(addValue);
23080 }
23081 else {
23082 metadata[prop] = addValue;
23083 }
23084 }
23085}
23086function setMetadata(metadata, set) {
23087 for (const prop in set) {
23088 metadata[prop] = set[prop];
23089 }
23090}
23091function _propHashKey(propName, propValue, references) {
23092 let nextObjectId = 0;
23093 const objectIds = new Map();
23094 const replacer = (key, value) => {
23095 if (value !== null && typeof value === 'object') {
23096 if (objectIds.has(value)) {
23097 return objectIds.get(value);
23098 }
23099 // Record an id for this object such that any later references use the object's id instead
23100 // of the object itself, in order to break cyclic pointers in objects.
23101 objectIds.set(value, `ɵobj#${nextObjectId++}`);
23102 // The first time an object is seen the object itself is serialized.
23103 return value;
23104 }
23105 else if (typeof value === 'function') {
23106 value = _serializeReference(value, references);
23107 }
23108 return value;
23109 };
23110 return `${propName}:${JSON.stringify(propValue, replacer)}`;
23111}
23112function _serializeReference(ref, references) {
23113 let id = references.get(ref);
23114 if (!id) {
23115 id = `${ɵstringify(ref)}${_nextReferenceId++}`;
23116 references.set(ref, id);
23117 }
23118 return id;
23119}
23120function _valueProps(obj) {
23121 const props = [];
23122 // regular public props
23123 Object.keys(obj).forEach((prop) => {
23124 if (!prop.startsWith('_')) {
23125 props.push(prop);
23126 }
23127 });
23128 // getters
23129 let proto = obj;
23130 while (proto = Object.getPrototypeOf(proto)) {
23131 Object.keys(proto).forEach((protoProp) => {
23132 const desc = Object.getOwnPropertyDescriptor(proto, protoProp);
23133 if (!protoProp.startsWith('_') && desc && 'get' in desc) {
23134 props.push(protoProp);
23135 }
23136 });
23137 }
23138 return props;
23139}
23140
23141const reflection = new ɵReflectionCapabilities();
23142/**
23143 * Allows to override ivy metadata for tests (via the `TestBed`).
23144 */
23145class OverrideResolver {
23146 constructor() {
23147 this.overrides = new Map();
23148 this.resolved = new Map();
23149 }
23150 addOverride(type, override) {
23151 const overrides = this.overrides.get(type) || [];
23152 overrides.push(override);
23153 this.overrides.set(type, overrides);
23154 this.resolved.delete(type);
23155 }
23156 setOverrides(overrides) {
23157 this.overrides.clear();
23158 overrides.forEach(([type, override]) => {
23159 this.addOverride(type, override);
23160 });
23161 }
23162 getAnnotation(type) {
23163 const annotations = reflection.annotations(type);
23164 // Try to find the nearest known Type annotation and make sure that this annotation is an
23165 // instance of the type we are looking for, so we can use it for resolution. Note: there might
23166 // be multiple known annotations found due to the fact that Components can extend Directives (so
23167 // both Directive and Component annotations would be present), so we always check if the known
23168 // annotation has the right type.
23169 for (let i = annotations.length - 1; i >= 0; i--) {
23170 const annotation = annotations[i];
23171 const isKnownType = annotation instanceof Directive || annotation instanceof Component ||
23172 annotation instanceof Pipe || annotation instanceof NgModule;
23173 if (isKnownType) {
23174 return annotation instanceof this.type ? annotation : null;
23175 }
23176 }
23177 return null;
23178 }
23179 resolve(type) {
23180 let resolved = this.resolved.get(type) || null;
23181 if (!resolved) {
23182 resolved = this.getAnnotation(type);
23183 if (resolved) {
23184 const overrides = this.overrides.get(type);
23185 if (overrides) {
23186 const overrider = new MetadataOverrider();
23187 overrides.forEach(override => {
23188 resolved = overrider.overrideMetadata(this.type, resolved, override);
23189 });
23190 }
23191 }
23192 this.resolved.set(type, resolved);
23193 }
23194 return resolved;
23195 }
23196}
23197class DirectiveResolver extends OverrideResolver {
23198 get type() {
23199 return Directive;
23200 }
23201}
23202class ComponentResolver extends OverrideResolver {
23203 get type() {
23204 return Component;
23205 }
23206}
23207class PipeResolver extends OverrideResolver {
23208 get type() {
23209 return Pipe;
23210 }
23211}
23212class NgModuleResolver extends OverrideResolver {
23213 get type() {
23214 return NgModule;
23215 }
23216}
23217
23218var TestingModuleOverride;
23219(function (TestingModuleOverride) {
23220 TestingModuleOverride[TestingModuleOverride["DECLARATION"] = 0] = "DECLARATION";
23221 TestingModuleOverride[TestingModuleOverride["OVERRIDE_TEMPLATE"] = 1] = "OVERRIDE_TEMPLATE";
23222})(TestingModuleOverride || (TestingModuleOverride = {}));
23223function isTestingModuleOverride(value) {
23224 return value === TestingModuleOverride.DECLARATION ||
23225 value === TestingModuleOverride.OVERRIDE_TEMPLATE;
23226}
23227function assertNoStandaloneComponents(types, resolver, location) {
23228 types.forEach(type => {
23229 const component = resolver.resolve(type);
23230 if (component && component.standalone) {
23231 throw new Error(generateStandaloneInDeclarationsError(type, location));
23232 }
23233 });
23234}
23235class TestBedCompiler {
23236 constructor(platform, additionalModuleTypes) {
23237 this.platform = platform;
23238 this.additionalModuleTypes = additionalModuleTypes;
23239 this.originalComponentResolutionQueue = null;
23240 // Testing module configuration
23241 this.declarations = [];
23242 this.imports = [];
23243 this.providers = [];
23244 this.schemas = [];
23245 // Queues of components/directives/pipes that should be recompiled.
23246 this.pendingComponents = new Set();
23247 this.pendingDirectives = new Set();
23248 this.pendingPipes = new Set();
23249 // Keep track of all components and directives, so we can patch Providers onto defs later.
23250 this.seenComponents = new Set();
23251 this.seenDirectives = new Set();
23252 // Keep track of overridden modules, so that we can collect all affected ones in the module tree.
23253 this.overriddenModules = new Set();
23254 // Store resolved styles for Components that have template overrides present and `styleUrls`
23255 // defined at the same time.
23256 this.existingComponentStyles = new Map();
23257 this.resolvers = initResolvers();
23258 this.componentToModuleScope = new Map();
23259 // Map that keeps initial version of component/directive/pipe defs in case
23260 // we compile a Type again, thus overriding respective static fields. This is
23261 // required to make sure we restore defs to their initial states between test runs.
23262 // Note: one class may have multiple defs (for example: ɵmod and ɵinj in case of an
23263 // NgModule), store all of them in a map.
23264 this.initialNgDefs = new Map();
23265 // Array that keeps cleanup operations for initial versions of component/directive/pipe/module
23266 // defs in case TestBed makes changes to the originals.
23267 this.defCleanupOps = [];
23268 this._injector = null;
23269 this.compilerProviders = null;
23270 this.providerOverrides = [];
23271 this.rootProviderOverrides = [];
23272 // Overrides for injectables with `{providedIn: SomeModule}` need to be tracked and added to that
23273 // module's provider list.
23274 this.providerOverridesByModule = new Map();
23275 this.providerOverridesByToken = new Map();
23276 this.scopesWithOverriddenProviders = new Set();
23277 this.testModuleRef = null;
23278 class DynamicTestModule {
23279 }
23280 this.testModuleType = DynamicTestModule;
23281 }
23282 setCompilerProviders(providers) {
23283 this.compilerProviders = providers;
23284 this._injector = null;
23285 }
23286 configureTestingModule(moduleDef) {
23287 // Enqueue any compilation tasks for the directly declared component.
23288 if (moduleDef.declarations !== undefined) {
23289 // Verify that there are no standalone components
23290 assertNoStandaloneComponents(moduleDef.declarations, this.resolvers.component, '"TestBed.configureTestingModule" call');
23291 this.queueTypeArray(moduleDef.declarations, TestingModuleOverride.DECLARATION);
23292 this.declarations.push(...moduleDef.declarations);
23293 }
23294 // Enqueue any compilation tasks for imported modules.
23295 if (moduleDef.imports !== undefined) {
23296 this.queueTypesFromModulesArray(moduleDef.imports);
23297 this.imports.push(...moduleDef.imports);
23298 }
23299 if (moduleDef.providers !== undefined) {
23300 this.providers.push(...moduleDef.providers);
23301 }
23302 if (moduleDef.schemas !== undefined) {
23303 this.schemas.push(...moduleDef.schemas);
23304 }
23305 }
23306 overrideModule(ngModule, override) {
23307 this.overriddenModules.add(ngModule);
23308 // Compile the module right away.
23309 this.resolvers.module.addOverride(ngModule, override);
23310 const metadata = this.resolvers.module.resolve(ngModule);
23311 if (metadata === null) {
23312 throw invalidTypeError(ngModule.name, 'NgModule');
23313 }
23314 this.recompileNgModule(ngModule, metadata);
23315 // At this point, the module has a valid module def (ɵmod), but the override may have introduced
23316 // new declarations or imported modules. Ingest any possible new types and add them to the
23317 // current queue.
23318 this.queueTypesFromModulesArray([ngModule]);
23319 }
23320 overrideComponent(component, override) {
23321 this.verifyNoStandaloneFlagOverrides(component, override);
23322 this.resolvers.component.addOverride(component, override);
23323 this.pendingComponents.add(component);
23324 }
23325 overrideDirective(directive, override) {
23326 this.verifyNoStandaloneFlagOverrides(directive, override);
23327 this.resolvers.directive.addOverride(directive, override);
23328 this.pendingDirectives.add(directive);
23329 }
23330 overridePipe(pipe, override) {
23331 this.verifyNoStandaloneFlagOverrides(pipe, override);
23332 this.resolvers.pipe.addOverride(pipe, override);
23333 this.pendingPipes.add(pipe);
23334 }
23335 verifyNoStandaloneFlagOverrides(type, override) {
23336 if (override.add?.hasOwnProperty('standalone') || override.set?.hasOwnProperty('standalone') ||
23337 override.remove?.hasOwnProperty('standalone')) {
23338 throw new Error(`An override for the ${type.name} class has the \`standalone\` flag. ` +
23339 `Changing the \`standalone\` flag via TestBed overrides is not supported.`);
23340 }
23341 }
23342 overrideProvider(token, provider) {
23343 let providerDef;
23344 if (provider.useFactory !== undefined) {
23345 providerDef = {
23346 provide: token,
23347 useFactory: provider.useFactory,
23348 deps: provider.deps || [],
23349 multi: provider.multi
23350 };
23351 }
23352 else if (provider.useValue !== undefined) {
23353 providerDef = { provide: token, useValue: provider.useValue, multi: provider.multi };
23354 }
23355 else {
23356 providerDef = { provide: token };
23357 }
23358 const injectableDef = typeof token !== 'string' ? ɵgetInjectableDef(token) : null;
23359 const providedIn = injectableDef === null ? null : resolveForwardRef$1(injectableDef.providedIn);
23360 const overridesBucket = providedIn === 'root' ? this.rootProviderOverrides : this.providerOverrides;
23361 overridesBucket.push(providerDef);
23362 // Keep overrides grouped by token as well for fast lookups using token
23363 this.providerOverridesByToken.set(token, providerDef);
23364 if (injectableDef !== null && providedIn !== null && typeof providedIn !== 'string') {
23365 const existingOverrides = this.providerOverridesByModule.get(providedIn);
23366 if (existingOverrides !== undefined) {
23367 existingOverrides.push(providerDef);
23368 }
23369 else {
23370 this.providerOverridesByModule.set(providedIn, [providerDef]);
23371 }
23372 }
23373 }
23374 overrideTemplateUsingTestingModule(type, template) {
23375 const def = type[ɵNG_COMP_DEF];
23376 const hasStyleUrls = () => {
23377 const metadata = this.resolvers.component.resolve(type);
23378 return !!metadata.styleUrls && metadata.styleUrls.length > 0;
23379 };
23380 const overrideStyleUrls = !!def && !isComponentDefPendingResolution(type) && hasStyleUrls();
23381 // In Ivy, compiling a component does not require knowing the module providing the
23382 // component's scope, so overrideTemplateUsingTestingModule can be implemented purely via
23383 // overrideComponent. Important: overriding template requires full Component re-compilation,
23384 // which may fail in case styleUrls are also present (thus Component is considered as required
23385 // resolution). In order to avoid this, we preemptively set styleUrls to an empty array,
23386 // preserve current styles available on Component def and restore styles back once compilation
23387 // is complete.
23388 const override = overrideStyleUrls ? { template, styles: [], styleUrls: [] } : { template };
23389 this.overrideComponent(type, { set: override });
23390 if (overrideStyleUrls && def.styles && def.styles.length > 0) {
23391 this.existingComponentStyles.set(type, def.styles);
23392 }
23393 // Set the component's scope to be the testing module.
23394 this.componentToModuleScope.set(type, TestingModuleOverride.OVERRIDE_TEMPLATE);
23395 }
23396 async compileComponents() {
23397 this.clearComponentResolutionQueue();
23398 // Run compilers for all queued types.
23399 let needsAsyncResources = this.compileTypesSync();
23400 // compileComponents() should not be async unless it needs to be.
23401 if (needsAsyncResources) {
23402 let resourceLoader;
23403 let resolver = (url) => {
23404 if (!resourceLoader) {
23405 resourceLoader = this.injector.get(ResourceLoader);
23406 }
23407 return Promise.resolve(resourceLoader.get(url));
23408 };
23409 await resolveComponentResources(resolver);
23410 }
23411 }
23412 finalize() {
23413 // One last compile
23414 this.compileTypesSync();
23415 // Create the testing module itself.
23416 this.compileTestModule();
23417 this.applyTransitiveScopes();
23418 this.applyProviderOverrides();
23419 // Patch previously stored `styles` Component values (taken from ɵcmp), in case these
23420 // Components have `styleUrls` fields defined and template override was requested.
23421 this.patchComponentsWithExistingStyles();
23422 // Clear the componentToModuleScope map, so that future compilations don't reset the scope of
23423 // every component.
23424 this.componentToModuleScope.clear();
23425 const parentInjector = this.platform.injector;
23426 this.testModuleRef = new ɵRender3NgModuleRef(this.testModuleType, parentInjector);
23427 // ApplicationInitStatus.runInitializers() is marked @internal to core.
23428 // Cast it to any before accessing it.
23429 this.testModuleRef.injector.get(ApplicationInitStatus).runInitializers();
23430 // Set locale ID after running app initializers, since locale information might be updated while
23431 // running initializers. This is also consistent with the execution order while bootstrapping an
23432 // app (see `packages/core/src/application_ref.ts` file).
23433 const localeId = this.testModuleRef.injector.get(LOCALE_ID$1, ɵDEFAULT_LOCALE_ID);
23434 ɵsetLocaleId(localeId);
23435 return this.testModuleRef;
23436 }
23437 /**
23438 * @internal
23439 */
23440 _compileNgModuleSync(moduleType) {
23441 this.queueTypesFromModulesArray([moduleType]);
23442 this.compileTypesSync();
23443 this.applyProviderOverrides();
23444 this.applyProviderOverridesInScope(moduleType);
23445 this.applyTransitiveScopes();
23446 }
23447 /**
23448 * @internal
23449 */
23450 async _compileNgModuleAsync(moduleType) {
23451 this.queueTypesFromModulesArray([moduleType]);
23452 await this.compileComponents();
23453 this.applyProviderOverrides();
23454 this.applyProviderOverridesInScope(moduleType);
23455 this.applyTransitiveScopes();
23456 }
23457 /**
23458 * @internal
23459 */
23460 _getModuleResolver() {
23461 return this.resolvers.module;
23462 }
23463 /**
23464 * @internal
23465 */
23466 _getComponentFactories(moduleType) {
23467 return maybeUnwrapFn(moduleType.ɵmod.declarations).reduce((factories, declaration) => {
23468 const componentDef = declaration.ɵcmp;
23469 componentDef && factories.push(new ɵRender3ComponentFactory(componentDef, this.testModuleRef));
23470 return factories;
23471 }, []);
23472 }
23473 compileTypesSync() {
23474 // Compile all queued components, directives, pipes.
23475 let needsAsyncResources = false;
23476 this.pendingComponents.forEach(declaration => {
23477 needsAsyncResources = needsAsyncResources || isComponentDefPendingResolution(declaration);
23478 const metadata = this.resolvers.component.resolve(declaration);
23479 if (metadata === null) {
23480 throw invalidTypeError(declaration.name, 'Component');
23481 }
23482 this.maybeStoreNgDef(ɵNG_COMP_DEF, declaration);
23483 ɵcompileComponent(declaration, metadata);
23484 });
23485 this.pendingComponents.clear();
23486 this.pendingDirectives.forEach(declaration => {
23487 const metadata = this.resolvers.directive.resolve(declaration);
23488 if (metadata === null) {
23489 throw invalidTypeError(declaration.name, 'Directive');
23490 }
23491 this.maybeStoreNgDef(ɵNG_DIR_DEF, declaration);
23492 ɵcompileDirective(declaration, metadata);
23493 });
23494 this.pendingDirectives.clear();
23495 this.pendingPipes.forEach(declaration => {
23496 const metadata = this.resolvers.pipe.resolve(declaration);
23497 if (metadata === null) {
23498 throw invalidTypeError(declaration.name, 'Pipe');
23499 }
23500 this.maybeStoreNgDef(ɵNG_PIPE_DEF, declaration);
23501 ɵcompilePipe(declaration, metadata);
23502 });
23503 this.pendingPipes.clear();
23504 return needsAsyncResources;
23505 }
23506 applyTransitiveScopes() {
23507 if (this.overriddenModules.size > 0) {
23508 // Module overrides (via `TestBed.overrideModule`) might affect scopes that were previously
23509 // calculated and stored in `transitiveCompileScopes`. If module overrides are present,
23510 // collect all affected modules and reset scopes to force their re-calculation.
23511 const testingModuleDef = this.testModuleType[ɵNG_MOD_DEF];
23512 const affectedModules = this.collectModulesAffectedByOverrides(testingModuleDef.imports);
23513 if (affectedModules.size > 0) {
23514 affectedModules.forEach(moduleType => {
23515 this.storeFieldOfDefOnType(moduleType, ɵNG_MOD_DEF, 'transitiveCompileScopes');
23516 moduleType[ɵNG_MOD_DEF].transitiveCompileScopes = null;
23517 });
23518 }
23519 }
23520 const moduleToScope = new Map();
23521 const getScopeOfModule = (moduleType) => {
23522 if (!moduleToScope.has(moduleType)) {
23523 const isTestingModule = isTestingModuleOverride(moduleType);
23524 const realType = isTestingModule ? this.testModuleType : moduleType;
23525 moduleToScope.set(moduleType, ɵtransitiveScopesFor(realType));
23526 }
23527 return moduleToScope.get(moduleType);
23528 };
23529 this.componentToModuleScope.forEach((moduleType, componentType) => {
23530 const moduleScope = getScopeOfModule(moduleType);
23531 this.storeFieldOfDefOnType(componentType, ɵNG_COMP_DEF, 'directiveDefs');
23532 this.storeFieldOfDefOnType(componentType, ɵNG_COMP_DEF, 'pipeDefs');
23533 // `tView` that is stored on component def contains information about directives and pipes
23534 // that are in the scope of this component. Patching component scope will cause `tView` to be
23535 // changed. Store original `tView` before patching scope, so the `tView` (including scope
23536 // information) is restored back to its previous/original state before running next test.
23537 this.storeFieldOfDefOnType(componentType, ɵNG_COMP_DEF, 'tView');
23538 ɵpatchComponentDefWithScope(componentType.ɵcmp, moduleScope);
23539 });
23540 this.componentToModuleScope.clear();
23541 }
23542 applyProviderOverrides() {
23543 const maybeApplyOverrides = (field) => (type) => {
23544 const resolver = field === ɵNG_COMP_DEF ? this.resolvers.component : this.resolvers.directive;
23545 const metadata = resolver.resolve(type);
23546 if (this.hasProviderOverrides(metadata.providers)) {
23547 this.patchDefWithProviderOverrides(type, field);
23548 }
23549 };
23550 this.seenComponents.forEach(maybeApplyOverrides(ɵNG_COMP_DEF));
23551 this.seenDirectives.forEach(maybeApplyOverrides(ɵNG_DIR_DEF));
23552 this.seenComponents.clear();
23553 this.seenDirectives.clear();
23554 }
23555 /**
23556 * Applies provider overrides to a given type (either an NgModule or a standalone component)
23557 * and all imported NgModules and standalone components recursively.
23558 */
23559 applyProviderOverridesInScope(type) {
23560 const hasScope = isStandaloneComponent(type) || isNgModule(type);
23561 // The function can be re-entered recursively while inspecting dependencies
23562 // of an NgModule or a standalone component. Exit early if we come across a
23563 // type that can not have a scope (directive or pipe) or the type is already
23564 // processed earlier.
23565 if (!hasScope || this.scopesWithOverriddenProviders.has(type)) {
23566 return;
23567 }
23568 this.scopesWithOverriddenProviders.add(type);
23569 // NOTE: the line below triggers JIT compilation of the module injector,
23570 // which also invokes verification of the NgModule semantics, which produces
23571 // detailed error messages. The fact that the code relies on this line being
23572 // present here is suspicious and should be refactored in a way that the line
23573 // below can be moved (for ex. after an early exit check below).
23574 const injectorDef = type[ɵNG_INJ_DEF];
23575 // No provider overrides, exit early.
23576 if (this.providerOverridesByToken.size === 0)
23577 return;
23578 if (isStandaloneComponent(type)) {
23579 // Visit all component dependencies and override providers there.
23580 const def = getComponentDef(type);
23581 const dependencies = maybeUnwrapFn(def.dependencies ?? []);
23582 for (const dependency of dependencies) {
23583 this.applyProviderOverridesInScope(dependency);
23584 }
23585 }
23586 else {
23587 const providers = [
23588 ...injectorDef.providers,
23589 ...(this.providerOverridesByModule.get(type) || [])
23590 ];
23591 if (this.hasProviderOverrides(providers)) {
23592 this.maybeStoreNgDef(ɵNG_INJ_DEF, type);
23593 this.storeFieldOfDefOnType(type, ɵNG_INJ_DEF, 'providers');
23594 injectorDef.providers = this.getOverriddenProviders(providers);
23595 }
23596 // Apply provider overrides to imported modules recursively
23597 const moduleDef = type[ɵNG_MOD_DEF];
23598 const imports = maybeUnwrapFn(moduleDef.imports);
23599 for (const importedModule of imports) {
23600 this.applyProviderOverridesInScope(importedModule);
23601 }
23602 // Also override the providers on any ModuleWithProviders imports since those don't appear in
23603 // the moduleDef.
23604 for (const importedModule of flatten(injectorDef.imports)) {
23605 if (isModuleWithProviders(importedModule)) {
23606 this.defCleanupOps.push({
23607 object: importedModule,
23608 fieldName: 'providers',
23609 originalValue: importedModule.providers
23610 });
23611 importedModule.providers = this.getOverriddenProviders(importedModule.providers);
23612 }
23613 }
23614 }
23615 }
23616 patchComponentsWithExistingStyles() {
23617 this.existingComponentStyles.forEach((styles, type) => type[ɵNG_COMP_DEF].styles = styles);
23618 this.existingComponentStyles.clear();
23619 }
23620 queueTypeArray(arr, moduleType) {
23621 for (const value of arr) {
23622 if (Array.isArray(value)) {
23623 this.queueTypeArray(value, moduleType);
23624 }
23625 else {
23626 this.queueType(value, moduleType);
23627 }
23628 }
23629 }
23630 recompileNgModule(ngModule, metadata) {
23631 // Cache the initial ngModuleDef as it will be overwritten.
23632 this.maybeStoreNgDef(ɵNG_MOD_DEF, ngModule);
23633 this.maybeStoreNgDef(ɵNG_INJ_DEF, ngModule);
23634 ɵcompileNgModuleDefs(ngModule, metadata);
23635 }
23636 queueType(type, moduleType) {
23637 const component = this.resolvers.component.resolve(type);
23638 if (component) {
23639 // Check whether a give Type has respective NG def (ɵcmp) and compile if def is
23640 // missing. That might happen in case a class without any Angular decorators extends another
23641 // class where Component/Directive/Pipe decorator is defined.
23642 if (isComponentDefPendingResolution(type) || !type.hasOwnProperty(ɵNG_COMP_DEF)) {
23643 this.pendingComponents.add(type);
23644 }
23645 this.seenComponents.add(type);
23646 // Keep track of the module which declares this component, so later the component's scope
23647 // can be set correctly. If the component has already been recorded here, then one of several
23648 // cases is true:
23649 // * the module containing the component was imported multiple times (common).
23650 // * the component is declared in multiple modules (which is an error).
23651 // * the component was in 'declarations' of the testing module, and also in an imported module
23652 // in which case the module scope will be TestingModuleOverride.DECLARATION.
23653 // * overrideTemplateUsingTestingModule was called for the component in which case the module
23654 // scope will be TestingModuleOverride.OVERRIDE_TEMPLATE.
23655 //
23656 // If the component was previously in the testing module's 'declarations' (meaning the
23657 // current value is TestingModuleOverride.DECLARATION), then `moduleType` is the component's
23658 // real module, which was imported. This pattern is understood to mean that the component
23659 // should use its original scope, but that the testing module should also contain the
23660 // component in its scope.
23661 //
23662 // Note: standalone components have no associated NgModule, so the `moduleType` can be `null`.
23663 if (moduleType !== null &&
23664 (!this.componentToModuleScope.has(type) ||
23665 this.componentToModuleScope.get(type) === TestingModuleOverride.DECLARATION)) {
23666 this.componentToModuleScope.set(type, moduleType);
23667 }
23668 return;
23669 }
23670 const directive = this.resolvers.directive.resolve(type);
23671 if (directive) {
23672 if (!type.hasOwnProperty(ɵNG_DIR_DEF)) {
23673 this.pendingDirectives.add(type);
23674 }
23675 this.seenDirectives.add(type);
23676 return;
23677 }
23678 const pipe = this.resolvers.pipe.resolve(type);
23679 if (pipe && !type.hasOwnProperty(ɵNG_PIPE_DEF)) {
23680 this.pendingPipes.add(type);
23681 return;
23682 }
23683 }
23684 queueTypesFromModulesArray(arr) {
23685 // Because we may encounter the same NgModule while processing the imports and exports of an
23686 // NgModule tree, we cache them in this set so we can skip ones that have already been seen
23687 // encountered. In some test setups, this caching resulted in 10X runtime improvement.
23688 const processedNgModuleDefs = new Set();
23689 const queueTypesFromModulesArrayRecur = (arr) => {
23690 for (const value of arr) {
23691 if (Array.isArray(value)) {
23692 queueTypesFromModulesArrayRecur(value);
23693 }
23694 else if (hasNgModuleDef(value)) {
23695 const def = value.ɵmod;
23696 if (processedNgModuleDefs.has(def)) {
23697 continue;
23698 }
23699 processedNgModuleDefs.add(def);
23700 // Look through declarations, imports, and exports, and queue
23701 // everything found there.
23702 this.queueTypeArray(maybeUnwrapFn(def.declarations), value);
23703 queueTypesFromModulesArrayRecur(maybeUnwrapFn(def.imports));
23704 queueTypesFromModulesArrayRecur(maybeUnwrapFn(def.exports));
23705 }
23706 else if (isModuleWithProviders(value)) {
23707 queueTypesFromModulesArrayRecur([value.ngModule]);
23708 }
23709 else if (isStandaloneComponent(value)) {
23710 this.queueType(value, null);
23711 const def = getComponentDef(value);
23712 const dependencies = maybeUnwrapFn(def.dependencies ?? []);
23713 dependencies.forEach((dependency) => {
23714 // Note: in AOT, the `dependencies` might also contain regular
23715 // (NgModule-based) Component, Directive and Pipes, so we handle
23716 // them separately and proceed with recursive process for standalone
23717 // Components and NgModules only.
23718 if (isStandaloneComponent(dependency) || hasNgModuleDef(dependency)) {
23719 queueTypesFromModulesArrayRecur([dependency]);
23720 }
23721 else {
23722 this.queueType(dependency, null);
23723 }
23724 });
23725 }
23726 }
23727 };
23728 queueTypesFromModulesArrayRecur(arr);
23729 }
23730 // When module overrides (via `TestBed.overrideModule`) are present, it might affect all modules
23731 // that import (even transitively) an overridden one. For all affected modules we need to
23732 // recalculate their scopes for a given test run and restore original scopes at the end. The goal
23733 // of this function is to collect all affected modules in a set for further processing. Example:
23734 // if we have the following module hierarchy: A -> B -> C (where `->` means `imports`) and module
23735 // `C` is overridden, we consider `A` and `B` as affected, since their scopes might become
23736 // invalidated with the override.
23737 collectModulesAffectedByOverrides(arr) {
23738 const seenModules = new Set();
23739 const affectedModules = new Set();
23740 const calcAffectedModulesRecur = (arr, path) => {
23741 for (const value of arr) {
23742 if (Array.isArray(value)) {
23743 // If the value is an array, just flatten it (by invoking this function recursively),
23744 // keeping "path" the same.
23745 calcAffectedModulesRecur(value, path);
23746 }
23747 else if (hasNgModuleDef(value)) {
23748 if (seenModules.has(value)) {
23749 // If we've seen this module before and it's included into "affected modules" list, mark
23750 // the whole path that leads to that module as affected, but do not descend into its
23751 // imports, since we already examined them before.
23752 if (affectedModules.has(value)) {
23753 path.forEach(item => affectedModules.add(item));
23754 }
23755 continue;
23756 }
23757 seenModules.add(value);
23758 if (this.overriddenModules.has(value)) {
23759 path.forEach(item => affectedModules.add(item));
23760 }
23761 // Examine module imports recursively to look for overridden modules.
23762 const moduleDef = value[ɵNG_MOD_DEF];
23763 calcAffectedModulesRecur(maybeUnwrapFn(moduleDef.imports), path.concat(value));
23764 }
23765 }
23766 };
23767 calcAffectedModulesRecur(arr, []);
23768 return affectedModules;
23769 }
23770 /**
23771 * Preserve an original def (such as ɵmod, ɵinj, etc) before applying an override.
23772 * Note: one class may have multiple defs (for example: ɵmod and ɵinj in case of
23773 * an NgModule). If there is a def in a set already, don't override it, since
23774 * an original one should be restored at the end of a test.
23775 */
23776 maybeStoreNgDef(prop, type) {
23777 if (!this.initialNgDefs.has(type)) {
23778 this.initialNgDefs.set(type, new Map());
23779 }
23780 const currentDefs = this.initialNgDefs.get(type);
23781 if (!currentDefs.has(prop)) {
23782 const currentDef = Object.getOwnPropertyDescriptor(type, prop);
23783 currentDefs.set(prop, currentDef);
23784 }
23785 }
23786 storeFieldOfDefOnType(type, defField, fieldName) {
23787 const def = type[defField];
23788 const originalValue = def[fieldName];
23789 this.defCleanupOps.push({ object: def, fieldName, originalValue });
23790 }
23791 /**
23792 * Clears current components resolution queue, but stores the state of the queue, so we can
23793 * restore it later. Clearing the queue is required before we try to compile components (via
23794 * `TestBed.compileComponents`), so that component defs are in sync with the resolution queue.
23795 */
23796 clearComponentResolutionQueue() {
23797 if (this.originalComponentResolutionQueue === null) {
23798 this.originalComponentResolutionQueue = new Map();
23799 }
23800 clearResolutionOfComponentResourcesQueue().forEach((value, key) => this.originalComponentResolutionQueue.set(key, value));
23801 }
23802 /*
23803 * Restores component resolution queue to the previously saved state. This operation is performed
23804 * as a part of restoring the state after completion of the current set of tests (that might
23805 * potentially mutate the state).
23806 */
23807 restoreComponentResolutionQueue() {
23808 if (this.originalComponentResolutionQueue !== null) {
23809 restoreComponentResolutionQueue(this.originalComponentResolutionQueue);
23810 this.originalComponentResolutionQueue = null;
23811 }
23812 }
23813 restoreOriginalState() {
23814 // Process cleanup ops in reverse order so the field's original value is restored correctly (in
23815 // case there were multiple overrides for the same field).
23816 forEachRight(this.defCleanupOps, (op) => {
23817 op.object[op.fieldName] = op.originalValue;
23818 });
23819 // Restore initial component/directive/pipe defs
23820 this.initialNgDefs.forEach((defs, type) => {
23821 defs.forEach((descriptor, prop) => {
23822 if (!descriptor) {
23823 // Delete operations are generally undesirable since they have performance
23824 // implications on objects they were applied to. In this particular case, situations
23825 // where this code is invoked should be quite rare to cause any noticeable impact,
23826 // since it's applied only to some test cases (for example when class with no
23827 // annotations extends some @Component) when we need to clear 'ɵcmp' field on a given
23828 // class to restore its original state (before applying overrides and running tests).
23829 delete type[prop];
23830 }
23831 else {
23832 Object.defineProperty(type, prop, descriptor);
23833 }
23834 });
23835 });
23836 this.initialNgDefs.clear();
23837 this.scopesWithOverriddenProviders.clear();
23838 this.restoreComponentResolutionQueue();
23839 // Restore the locale ID to the default value, this shouldn't be necessary but we never know
23840 ɵsetLocaleId(ɵDEFAULT_LOCALE_ID);
23841 }
23842 compileTestModule() {
23843 class RootScopeModule {
23844 }
23845 ɵcompileNgModuleDefs(RootScopeModule, {
23846 providers: [...this.rootProviderOverrides],
23847 });
23848 const ngZone = new NgZone({ enableLongStackTrace: true });
23849 const providers = [
23850 { provide: NgZone, useValue: ngZone },
23851 { provide: Compiler, useFactory: () => new R3TestCompiler(this) },
23852 ...this.providers,
23853 ...this.providerOverrides,
23854 ];
23855 const imports = [RootScopeModule, this.additionalModuleTypes, this.imports || []];
23856 // clang-format off
23857 ɵcompileNgModuleDefs(this.testModuleType, {
23858 declarations: this.declarations,
23859 imports,
23860 schemas: this.schemas,
23861 providers,
23862 }, /* allowDuplicateDeclarationsInRoot */ true);
23863 // clang-format on
23864 this.applyProviderOverridesInScope(this.testModuleType);
23865 }
23866 get injector() {
23867 if (this._injector !== null) {
23868 return this._injector;
23869 }
23870 const providers = [];
23871 const compilerOptions = this.platform.injector.get(COMPILER_OPTIONS);
23872 compilerOptions.forEach(opts => {
23873 if (opts.providers) {
23874 providers.push(opts.providers);
23875 }
23876 });
23877 if (this.compilerProviders !== null) {
23878 providers.push(...this.compilerProviders);
23879 }
23880 // TODO(ocombe): make this work with an Injector directly instead of creating a module for it
23881 class CompilerModule {
23882 }
23883 ɵcompileNgModuleDefs(CompilerModule, { providers });
23884 const CompilerModuleFactory = new ɵNgModuleFactory(CompilerModule);
23885 this._injector = CompilerModuleFactory.create(this.platform.injector).injector;
23886 return this._injector;
23887 }
23888 // get overrides for a specific provider (if any)
23889 getSingleProviderOverrides(provider) {
23890 const token = getProviderToken(provider);
23891 return this.providerOverridesByToken.get(token) || null;
23892 }
23893 getProviderOverrides(providers) {
23894 if (!providers || !providers.length || this.providerOverridesByToken.size === 0)
23895 return [];
23896 // There are two flattening operations here. The inner flattenProviders() operates on the
23897 // metadata's providers and applies a mapping function which retrieves overrides for each
23898 // incoming provider. The outer flatten() then flattens the produced overrides array. If this is
23899 // not done, the array can contain other empty arrays (e.g. `[[], []]`) which leak into the
23900 // providers array and contaminate any error messages that might be generated.
23901 return flatten(flattenProviders(providers, (provider) => this.getSingleProviderOverrides(provider) || []));
23902 }
23903 getOverriddenProviders(providers) {
23904 if (!providers || !providers.length || this.providerOverridesByToken.size === 0)
23905 return [];
23906 const flattenedProviders = flattenProviders(providers);
23907 const overrides = this.getProviderOverrides(flattenedProviders);
23908 const overriddenProviders = [...flattenedProviders, ...overrides];
23909 const final = [];
23910 const seenOverriddenProviders = new Set();
23911 // We iterate through the list of providers in reverse order to make sure provider overrides
23912 // take precedence over the values defined in provider list. We also filter out all providers
23913 // that have overrides, keeping overridden values only. This is needed, since presence of a
23914 // provider with `ngOnDestroy` hook will cause this hook to be registered and invoked later.
23915 forEachRight(overriddenProviders, (provider) => {
23916 const token = getProviderToken(provider);
23917 if (this.providerOverridesByToken.has(token)) {
23918 if (!seenOverriddenProviders.has(token)) {
23919 seenOverriddenProviders.add(token);
23920 // Treat all overridden providers as `{multi: false}` (even if it's a multi-provider) to
23921 // make sure that provided override takes highest precedence and is not combined with
23922 // other instances of the same multi provider.
23923 final.unshift({ ...provider, multi: false });
23924 }
23925 }
23926 else {
23927 final.unshift(provider);
23928 }
23929 });
23930 return final;
23931 }
23932 hasProviderOverrides(providers) {
23933 return this.getProviderOverrides(providers).length > 0;
23934 }
23935 patchDefWithProviderOverrides(declaration, field) {
23936 const def = declaration[field];
23937 if (def && def.providersResolver) {
23938 this.maybeStoreNgDef(field, declaration);
23939 const resolver = def.providersResolver;
23940 const processProvidersFn = (providers) => this.getOverriddenProviders(providers);
23941 this.storeFieldOfDefOnType(declaration, field, 'providersResolver');
23942 def.providersResolver = (ngDef) => resolver(ngDef, processProvidersFn);
23943 }
23944 }
23945}
23946function initResolvers() {
23947 return {
23948 module: new NgModuleResolver(),
23949 component: new ComponentResolver(),
23950 directive: new DirectiveResolver(),
23951 pipe: new PipeResolver()
23952 };
23953}
23954function isStandaloneComponent(value) {
23955 const def = getComponentDef(value);
23956 return !!def?.standalone;
23957}
23958function getComponentDef(value) {
23959 return value.ɵcmp ?? null;
23960}
23961function hasNgModuleDef(value) {
23962 return value.hasOwnProperty('ɵmod');
23963}
23964function isNgModule(value) {
23965 return hasNgModuleDef(value);
23966}
23967function maybeUnwrapFn(maybeFn) {
23968 return maybeFn instanceof Function ? maybeFn() : maybeFn;
23969}
23970function flatten(values) {
23971 const out = [];
23972 values.forEach(value => {
23973 if (Array.isArray(value)) {
23974 out.push(...flatten(value));
23975 }
23976 else {
23977 out.push(value);
23978 }
23979 });
23980 return out;
23981}
23982function identityFn(value) {
23983 return value;
23984}
23985function flattenProviders(providers, mapFn = identityFn) {
23986 const out = [];
23987 for (let provider of providers) {
23988 if (ɵisEnvironmentProviders(provider)) {
23989 provider = provider.ɵproviders;
23990 }
23991 if (Array.isArray(provider)) {
23992 out.push(...flattenProviders(provider, mapFn));
23993 }
23994 else {
23995 out.push(mapFn(provider));
23996 }
23997 }
23998 return out;
23999}
24000function getProviderField(provider, field) {
24001 return provider && typeof provider === 'object' && provider[field];
24002}
24003function getProviderToken(provider) {
24004 return getProviderField(provider, 'provide') || provider;
24005}
24006function isModuleWithProviders(value) {
24007 return value.hasOwnProperty('ngModule');
24008}
24009function forEachRight(values, fn) {
24010 for (let idx = values.length - 1; idx >= 0; idx--) {
24011 fn(values[idx], idx);
24012 }
24013}
24014function invalidTypeError(name, expectedType) {
24015 return new Error(`${name} class doesn't have @${expectedType} decorator or is missing metadata.`);
24016}
24017class R3TestCompiler {
24018 constructor(testBed) {
24019 this.testBed = testBed;
24020 }
24021 compileModuleSync(moduleType) {
24022 this.testBed._compileNgModuleSync(moduleType);
24023 return new ɵNgModuleFactory(moduleType);
24024 }
24025 async compileModuleAsync(moduleType) {
24026 await this.testBed._compileNgModuleAsync(moduleType);
24027 return new ɵNgModuleFactory(moduleType);
24028 }
24029 compileModuleAndAllComponentsSync(moduleType) {
24030 const ngModuleFactory = this.compileModuleSync(moduleType);
24031 const componentFactories = this.testBed._getComponentFactories(moduleType);
24032 return new ModuleWithComponentFactories(ngModuleFactory, componentFactories);
24033 }
24034 async compileModuleAndAllComponentsAsync(moduleType) {
24035 const ngModuleFactory = await this.compileModuleAsync(moduleType);
24036 const componentFactories = this.testBed._getComponentFactories(moduleType);
24037 return new ModuleWithComponentFactories(ngModuleFactory, componentFactories);
24038 }
24039 clearCache() { }
24040 clearCacheFor(type) { }
24041 getModuleId(moduleType) {
24042 const meta = this.testBed._getModuleResolver().resolve(moduleType);
24043 return meta && meta.id || undefined;
24044 }
24045}
24046
24047// The formatter and CI disagree on how this import statement should be formatted. Both try to keep
24048let _nextRootElementId = 0;
24049/**
24050 * Returns a singleton of the `TestBed` class.
24051 *
24052 * @publicApi
24053 */
24054function getTestBed() {
24055 return TestBedImpl.INSTANCE;
24056}
24057/**
24058 * @description
24059 * Configures and initializes environment for unit testing and provides methods for
24060 * creating components and services in unit tests.
24061 *
24062 * TestBed is the primary api for writing unit tests for Angular applications and libraries.
24063 */
24064class TestBedImpl {
24065 constructor() {
24066 // Properties
24067 this.platform = null;
24068 this.ngModule = null;
24069 this._compiler = null;
24070 this._testModuleRef = null;
24071 this._activeFixtures = [];
24072 /**
24073 * Internal-only flag to indicate whether a module
24074 * scoping queue has been checked and flushed already.
24075 * @nodoc
24076 */
24077 this.globalCompilationChecked = false;
24078 }
24079 static get INSTANCE() {
24080 return TestBedImpl._INSTANCE = TestBedImpl._INSTANCE || new TestBedImpl();
24081 }
24082 /**
24083 * Initialize the environment for testing with a compiler factory, a PlatformRef, and an
24084 * angular module. These are common to every test in the suite.
24085 *
24086 * This may only be called once, to set up the common providers for the current test
24087 * suite on the current platform. If you absolutely need to change the providers,
24088 * first use `resetTestEnvironment`.
24089 *
24090 * Test modules and platforms for individual platforms are available from
24091 * '@angular/<platform_name>/testing'.
24092 *
24093 * @publicApi
24094 */
24095 static initTestEnvironment(ngModule, platform, options) {
24096 const testBed = TestBedImpl.INSTANCE;
24097 testBed.initTestEnvironment(ngModule, platform, options);
24098 return testBed;
24099 }
24100 /**
24101 * Reset the providers for the test injector.
24102 *
24103 * @publicApi
24104 */
24105 static resetTestEnvironment() {
24106 TestBedImpl.INSTANCE.resetTestEnvironment();
24107 }
24108 static configureCompiler(config) {
24109 return TestBedImpl.INSTANCE.configureCompiler(config);
24110 }
24111 /**
24112 * Allows overriding default providers, directives, pipes, modules of the test injector,
24113 * which are defined in test_injector.js
24114 */
24115 static configureTestingModule(moduleDef) {
24116 return TestBedImpl.INSTANCE.configureTestingModule(moduleDef);
24117 }
24118 /**
24119 * Compile components with a `templateUrl` for the test's NgModule.
24120 * It is necessary to call this function
24121 * as fetching urls is asynchronous.
24122 */
24123 static compileComponents() {
24124 return TestBedImpl.INSTANCE.compileComponents();
24125 }
24126 static overrideModule(ngModule, override) {
24127 return TestBedImpl.INSTANCE.overrideModule(ngModule, override);
24128 }
24129 static overrideComponent(component, override) {
24130 return TestBedImpl.INSTANCE.overrideComponent(component, override);
24131 }
24132 static overrideDirective(directive, override) {
24133 return TestBedImpl.INSTANCE.overrideDirective(directive, override);
24134 }
24135 static overridePipe(pipe, override) {
24136 return TestBedImpl.INSTANCE.overridePipe(pipe, override);
24137 }
24138 static overrideTemplate(component, template) {
24139 return TestBedImpl.INSTANCE.overrideTemplate(component, template);
24140 }
24141 /**
24142 * Overrides the template of the given component, compiling the template
24143 * in the context of the TestingModule.
24144 *
24145 * Note: This works for JIT and AOTed components as well.
24146 */
24147 static overrideTemplateUsingTestingModule(component, template) {
24148 return TestBedImpl.INSTANCE.overrideTemplateUsingTestingModule(component, template);
24149 }
24150 static overrideProvider(token, provider) {
24151 return TestBedImpl.INSTANCE.overrideProvider(token, provider);
24152 }
24153 static inject(token, notFoundValue, flags) {
24154 return TestBedImpl.INSTANCE.inject(token, notFoundValue, ɵconvertToBitFlags(flags));
24155 }
24156 /** @deprecated from v9.0.0 use TestBed.inject */
24157 static get(token, notFoundValue = Injector$1.THROW_IF_NOT_FOUND, flags = InjectFlags$1.Default) {
24158 return TestBedImpl.INSTANCE.inject(token, notFoundValue, flags);
24159 }
24160 /**
24161 * Runs the given function in the `EnvironmentInjector` context of `TestBed`.
24162 *
24163 * @see EnvironmentInjector#runInContext
24164 */
24165 static runInInjectionContext(fn) {
24166 return TestBedImpl.INSTANCE.runInInjectionContext(fn);
24167 }
24168 static createComponent(component) {
24169 return TestBedImpl.INSTANCE.createComponent(component);
24170 }
24171 static resetTestingModule() {
24172 return TestBedImpl.INSTANCE.resetTestingModule();
24173 }
24174 static execute(tokens, fn, context) {
24175 return TestBedImpl.INSTANCE.execute(tokens, fn, context);
24176 }
24177 static get platform() {
24178 return TestBedImpl.INSTANCE.platform;
24179 }
24180 static get ngModule() {
24181 return TestBedImpl.INSTANCE.ngModule;
24182 }
24183 /**
24184 * Initialize the environment for testing with a compiler factory, a PlatformRef, and an
24185 * angular module. These are common to every test in the suite.
24186 *
24187 * This may only be called once, to set up the common providers for the current test
24188 * suite on the current platform. If you absolutely need to change the providers,
24189 * first use `resetTestEnvironment`.
24190 *
24191 * Test modules and platforms for individual platforms are available from
24192 * '@angular/<platform_name>/testing'.
24193 *
24194 * @publicApi
24195 */
24196 initTestEnvironment(ngModule, platform, options) {
24197 if (this.platform || this.ngModule) {
24198 throw new Error('Cannot set base providers because it has already been called');
24199 }
24200 TestBedImpl._environmentTeardownOptions = options?.teardown;
24201 TestBedImpl._environmentErrorOnUnknownElementsOption = options?.errorOnUnknownElements;
24202 TestBedImpl._environmentErrorOnUnknownPropertiesOption = options?.errorOnUnknownProperties;
24203 this.platform = platform;
24204 this.ngModule = ngModule;
24205 this._compiler = new TestBedCompiler(this.platform, this.ngModule);
24206 // TestBed does not have an API which can reliably detect the start of a test, and thus could be
24207 // used to track the state of the NgModule registry and reset it correctly. Instead, when we
24208 // know we're in a testing scenario, we disable the check for duplicate NgModule registration
24209 // completely.
24210 ɵsetAllowDuplicateNgModuleIdsForTest(true);
24211 }
24212 /**
24213 * Reset the providers for the test injector.
24214 *
24215 * @publicApi
24216 */
24217 resetTestEnvironment() {
24218 this.resetTestingModule();
24219 this._compiler = null;
24220 this.platform = null;
24221 this.ngModule = null;
24222 TestBedImpl._environmentTeardownOptions = undefined;
24223 ɵsetAllowDuplicateNgModuleIdsForTest(false);
24224 }
24225 resetTestingModule() {
24226 this.checkGlobalCompilationFinished();
24227 ɵresetCompiledComponents();
24228 if (this._compiler !== null) {
24229 this.compiler.restoreOriginalState();
24230 }
24231 this._compiler = new TestBedCompiler(this.platform, this.ngModule);
24232 // Restore the previous value of the "error on unknown elements" option
24233 ɵsetUnknownElementStrictMode$1(this._previousErrorOnUnknownElementsOption ?? THROW_ON_UNKNOWN_ELEMENTS_DEFAULT);
24234 // Restore the previous value of the "error on unknown properties" option
24235 ɵsetUnknownPropertyStrictMode$1(this._previousErrorOnUnknownPropertiesOption ?? THROW_ON_UNKNOWN_PROPERTIES_DEFAULT);
24236 // We have to chain a couple of try/finally blocks, because each step can
24237 // throw errors and we don't want it to interrupt the next step and we also
24238 // want an error to be thrown at the end.
24239 try {
24240 this.destroyActiveFixtures();
24241 }
24242 finally {
24243 try {
24244 if (this.shouldTearDownTestingModule()) {
24245 this.tearDownTestingModule();
24246 }
24247 }
24248 finally {
24249 this._testModuleRef = null;
24250 this._instanceTeardownOptions = undefined;
24251 this._instanceErrorOnUnknownElementsOption = undefined;
24252 this._instanceErrorOnUnknownPropertiesOption = undefined;
24253 }
24254 }
24255 return this;
24256 }
24257 configureCompiler(config) {
24258 if (config.useJit != null) {
24259 throw new Error('the Render3 compiler JiT mode is not configurable !');
24260 }
24261 if (config.providers !== undefined) {
24262 this.compiler.setCompilerProviders(config.providers);
24263 }
24264 return this;
24265 }
24266 configureTestingModule(moduleDef) {
24267 this.assertNotInstantiated('R3TestBed.configureTestingModule', 'configure the test module');
24268 // Trigger module scoping queue flush before executing other TestBed operations in a test.
24269 // This is needed for the first test invocation to ensure that globally declared modules have
24270 // their components scoped properly. See the `checkGlobalCompilationFinished` function
24271 // description for additional info.
24272 this.checkGlobalCompilationFinished();
24273 // Always re-assign the options, even if they're undefined.
24274 // This ensures that we don't carry them between tests.
24275 this._instanceTeardownOptions = moduleDef.teardown;
24276 this._instanceErrorOnUnknownElementsOption = moduleDef.errorOnUnknownElements;
24277 this._instanceErrorOnUnknownPropertiesOption = moduleDef.errorOnUnknownProperties;
24278 // Store the current value of the strict mode option,
24279 // so we can restore it later
24280 this._previousErrorOnUnknownElementsOption = ɵgetUnknownElementStrictMode$1();
24281 ɵsetUnknownElementStrictMode$1(this.shouldThrowErrorOnUnknownElements());
24282 this._previousErrorOnUnknownPropertiesOption = ɵgetUnknownPropertyStrictMode$1();
24283 ɵsetUnknownPropertyStrictMode$1(this.shouldThrowErrorOnUnknownProperties());
24284 this.compiler.configureTestingModule(moduleDef);
24285 return this;
24286 }
24287 compileComponents() {
24288 return this.compiler.compileComponents();
24289 }
24290 inject(token, notFoundValue, flags) {
24291 if (token === TestBed) {
24292 return this;
24293 }
24294 const UNDEFINED = {};
24295 const result = this.testModuleRef.injector.get(token, UNDEFINED, ɵconvertToBitFlags(flags));
24296 return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) :
24297 result;
24298 }
24299 /** @deprecated from v9.0.0 use TestBed.inject */
24300 get(token, notFoundValue = Injector$1.THROW_IF_NOT_FOUND, flags = InjectFlags$1.Default) {
24301 return this.inject(token, notFoundValue, flags);
24302 }
24303 runInInjectionContext(fn) {
24304 return this.inject(EnvironmentInjector$1).runInContext(fn);
24305 }
24306 execute(tokens, fn, context) {
24307 const params = tokens.map(t => this.inject(t));
24308 return fn.apply(context, params);
24309 }
24310 overrideModule(ngModule, override) {
24311 this.assertNotInstantiated('overrideModule', 'override module metadata');
24312 this.compiler.overrideModule(ngModule, override);
24313 return this;
24314 }
24315 overrideComponent(component, override) {
24316 this.assertNotInstantiated('overrideComponent', 'override component metadata');
24317 this.compiler.overrideComponent(component, override);
24318 return this;
24319 }
24320 overrideTemplateUsingTestingModule(component, template) {
24321 this.assertNotInstantiated('R3TestBed.overrideTemplateUsingTestingModule', 'Cannot override template when the test module has already been instantiated');
24322 this.compiler.overrideTemplateUsingTestingModule(component, template);
24323 return this;
24324 }
24325 overrideDirective(directive, override) {
24326 this.assertNotInstantiated('overrideDirective', 'override directive metadata');
24327 this.compiler.overrideDirective(directive, override);
24328 return this;
24329 }
24330 overridePipe(pipe, override) {
24331 this.assertNotInstantiated('overridePipe', 'override pipe metadata');
24332 this.compiler.overridePipe(pipe, override);
24333 return this;
24334 }
24335 /**
24336 * Overwrites all providers for the given token with the given provider definition.
24337 */
24338 overrideProvider(token, provider) {
24339 this.assertNotInstantiated('overrideProvider', 'override provider');
24340 this.compiler.overrideProvider(token, provider);
24341 return this;
24342 }
24343 overrideTemplate(component, template) {
24344 return this.overrideComponent(component, { set: { template, templateUrl: null } });
24345 }
24346 createComponent(type) {
24347 const testComponentRenderer = this.inject(TestComponentRenderer);
24348 const rootElId = `root${_nextRootElementId++}`;
24349 testComponentRenderer.insertRootElement(rootElId);
24350 const componentDef = type.ɵcmp;
24351 if (!componentDef) {
24352 throw new Error(`It looks like '${ɵstringify(type)}' has not been compiled.`);
24353 }
24354 // TODO: Don't cast as `InjectionToken<boolean>`, proper type is boolean[]
24355 const noNgZone = this.inject(ComponentFixtureNoNgZone, false);
24356 // TODO: Don't cast as `InjectionToken<boolean>`, proper type is boolean[]
24357 const autoDetect = this.inject(ComponentFixtureAutoDetect, false);
24358 const ngZone = noNgZone ? null : this.inject(NgZone, null);
24359 const componentFactory = new ɵRender3ComponentFactory(componentDef);
24360 const initComponent = () => {
24361 const componentRef = componentFactory.create(Injector$1.NULL, [], `#${rootElId}`, this.testModuleRef);
24362 return new ComponentFixture(componentRef, ngZone, autoDetect);
24363 };
24364 const fixture = ngZone ? ngZone.run(initComponent) : initComponent();
24365 this._activeFixtures.push(fixture);
24366 return fixture;
24367 }
24368 /**
24369 * @internal strip this from published d.ts files due to
24370 * https://github.com/microsoft/TypeScript/issues/36216
24371 */
24372 get compiler() {
24373 if (this._compiler === null) {
24374 throw new Error(`Need to call TestBed.initTestEnvironment() first`);
24375 }
24376 return this._compiler;
24377 }
24378 /**
24379 * @internal strip this from published d.ts files due to
24380 * https://github.com/microsoft/TypeScript/issues/36216
24381 */
24382 get testModuleRef() {
24383 if (this._testModuleRef === null) {
24384 this._testModuleRef = this.compiler.finalize();
24385 }
24386 return this._testModuleRef;
24387 }
24388 assertNotInstantiated(methodName, methodDescription) {
24389 if (this._testModuleRef !== null) {
24390 throw new Error(`Cannot ${methodDescription} when the test module has already been instantiated. ` +
24391 `Make sure you are not using \`inject\` before \`${methodName}\`.`);
24392 }
24393 }
24394 /**
24395 * Check whether the module scoping queue should be flushed, and flush it if needed.
24396 *
24397 * When the TestBed is reset, it clears the JIT module compilation queue, cancelling any
24398 * in-progress module compilation. This creates a potential hazard - the very first time the
24399 * TestBed is initialized (or if it's reset without being initialized), there may be pending
24400 * compilations of modules declared in global scope. These compilations should be finished.
24401 *
24402 * To ensure that globally declared modules have their components scoped properly, this function
24403 * is called whenever TestBed is initialized or reset. The _first_ time that this happens, prior
24404 * to any other operations, the scoping queue is flushed.
24405 */
24406 checkGlobalCompilationFinished() {
24407 // Checking _testNgModuleRef is null should not be necessary, but is left in as an additional
24408 // guard that compilations queued in tests (after instantiation) are never flushed accidentally.
24409 if (!this.globalCompilationChecked && this._testModuleRef === null) {
24410 ɵflushModuleScopingQueueAsMuchAsPossible();
24411 }
24412 this.globalCompilationChecked = true;
24413 }
24414 destroyActiveFixtures() {
24415 let errorCount = 0;
24416 this._activeFixtures.forEach((fixture) => {
24417 try {
24418 fixture.destroy();
24419 }
24420 catch (e) {
24421 errorCount++;
24422 console.error('Error during cleanup of component', {
24423 component: fixture.componentInstance,
24424 stacktrace: e,
24425 });
24426 }
24427 });
24428 this._activeFixtures = [];
24429 if (errorCount > 0 && this.shouldRethrowTeardownErrors()) {
24430 throw Error(`${errorCount} ${(errorCount === 1 ? 'component' : 'components')} ` +
24431 `threw errors during cleanup`);
24432 }
24433 }
24434 shouldRethrowTeardownErrors() {
24435 const instanceOptions = this._instanceTeardownOptions;
24436 const environmentOptions = TestBedImpl._environmentTeardownOptions;
24437 // If the new teardown behavior hasn't been configured, preserve the old behavior.
24438 if (!instanceOptions && !environmentOptions) {
24439 return TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT;
24440 }
24441 // Otherwise use the configured behavior or default to rethrowing.
24442 return instanceOptions?.rethrowErrors ?? environmentOptions?.rethrowErrors ??
24443 this.shouldTearDownTestingModule();
24444 }
24445 shouldThrowErrorOnUnknownElements() {
24446 // Check if a configuration has been provided to throw when an unknown element is found
24447 return this._instanceErrorOnUnknownElementsOption ??
24448 TestBedImpl._environmentErrorOnUnknownElementsOption ?? THROW_ON_UNKNOWN_ELEMENTS_DEFAULT;
24449 }
24450 shouldThrowErrorOnUnknownProperties() {
24451 // Check if a configuration has been provided to throw when an unknown property is found
24452 return this._instanceErrorOnUnknownPropertiesOption ??
24453 TestBedImpl._environmentErrorOnUnknownPropertiesOption ??
24454 THROW_ON_UNKNOWN_PROPERTIES_DEFAULT;
24455 }
24456 shouldTearDownTestingModule() {
24457 return this._instanceTeardownOptions?.destroyAfterEach ??
24458 TestBedImpl._environmentTeardownOptions?.destroyAfterEach ??
24459 TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT;
24460 }
24461 tearDownTestingModule() {
24462 // If the module ref has already been destroyed, we won't be able to get a test renderer.
24463 if (this._testModuleRef === null) {
24464 return;
24465 }
24466 // Resolve the renderer ahead of time, because we want to remove the root elements as the very
24467 // last step, but the injector will be destroyed as a part of the module ref destruction.
24468 const testRenderer = this.inject(TestComponentRenderer);
24469 try {
24470 this._testModuleRef.destroy();
24471 }
24472 catch (e) {
24473 if (this.shouldRethrowTeardownErrors()) {
24474 throw e;
24475 }
24476 else {
24477 console.error('Error during cleanup of a testing module', {
24478 component: this._testModuleRef.instance,
24479 stacktrace: e,
24480 });
24481 }
24482 }
24483 finally {
24484 testRenderer.removeAllRootElements?.();
24485 }
24486 }
24487}
24488TestBedImpl._INSTANCE = null;
24489/**
24490 * @description
24491 * Configures and initializes environment for unit testing and provides methods for
24492 * creating components and services in unit tests.
24493 *
24494 * `TestBed` is the primary api for writing unit tests for Angular applications and libraries.
24495 *
24496 * @publicApi
24497 */
24498const TestBed = TestBedImpl;
24499/**
24500 * Allows injecting dependencies in `beforeEach()` and `it()`. Note: this function
24501 * (imported from the `@angular/core/testing` package) can **only** be used to inject dependencies
24502 * in tests. To inject dependencies in your application code, use the [`inject`](api/core/inject)
24503 * function from the `@angular/core` package instead.
24504 *
24505 * Example:
24506 *
24507 * ```
24508 * beforeEach(inject([Dependency, AClass], (dep, object) => {
24509 * // some code that uses `dep` and `object`
24510 * // ...
24511 * }));
24512 *
24513 * it('...', inject([AClass], (object) => {
24514 * object.doSomething();
24515 * expect(...);
24516 * })
24517 * ```
24518 *
24519 * @publicApi
24520 */
24521function inject(tokens, fn) {
24522 const testBed = TestBedImpl.INSTANCE;
24523 // Not using an arrow function to preserve context passed from call site
24524 return function () {
24525 return testBed.execute(tokens, fn, this);
24526 };
24527}
24528/**
24529 * @publicApi
24530 */
24531class InjectSetupWrapper {
24532 constructor(_moduleDef) {
24533 this._moduleDef = _moduleDef;
24534 }
24535 _addModule() {
24536 const moduleDef = this._moduleDef();
24537 if (moduleDef) {
24538 TestBedImpl.configureTestingModule(moduleDef);
24539 }
24540 }
24541 inject(tokens, fn) {
24542 const self = this;
24543 // Not using an arrow function to preserve context passed from call site
24544 return function () {
24545 self._addModule();
24546 return inject(tokens, fn).call(this);
24547 };
24548 }
24549}
24550function withModule(moduleDef, fn) {
24551 if (fn) {
24552 // Not using an arrow function to preserve context passed from call site
24553 return function () {
24554 const testBed = TestBedImpl.INSTANCE;
24555 if (moduleDef) {
24556 testBed.configureTestingModule(moduleDef);
24557 }
24558 return fn.apply(this);
24559 };
24560 }
24561 return new InjectSetupWrapper(() => moduleDef);
24562}
24563
24564/**
24565 * Public Test Library for unit testing Angular applications. Assumes that you are running
24566 * with Jasmine, Mocha, or a similar framework which exports a beforeEach function and
24567 * allows tests to be asynchronous by either returning a promise or using a 'done' parameter.
24568 */
24569const _global = (typeof window === 'undefined' ? global : window);
24570// Reset the test providers and the fake async zone before each test.
24571if (_global.beforeEach) {
24572 _global.beforeEach(getCleanupHook(false));
24573}
24574// We provide both a `beforeEach` and `afterEach`, because the updated behavior for
24575// tearing down the module is supposed to run after the test so that we can associate
24576// teardown errors with the correct test.
24577if (_global.afterEach) {
24578 _global.afterEach(getCleanupHook(true));
24579}
24580function getCleanupHook(expectedTeardownValue) {
24581 return () => {
24582 const testBed = TestBedImpl.INSTANCE;
24583 if (testBed.shouldTearDownTestingModule() === expectedTeardownValue) {
24584 testBed.resetTestingModule();
24585 resetFakeAsyncZone();
24586 }
24587 };
24588}
24589/**
24590 * This API should be removed. But doing so seems to break `google3` and so it requires a bit of
24591 * investigation.
24592 *
24593 * A work around is to mark it as `@codeGenApi` for now and investigate later.
24594 *
24595 * @codeGenApi
24596 */
24597// TODO(iminar): Remove this code in a safe way.
24598const __core_private_testing_placeholder__ = '';
24599
24600/**
24601 * @module
24602 * @description
24603 * Entry point for all public APIs of the core/testing package.
24604 */
24605
24606/// <reference types="jasmine" />
24607// This file only reexports content of the `src` folder. Keep it that way.
24608
24609// This file is not used to build this module. It is only used during editing
24610
24611/**
24612 * Generated bundle index. Do not edit.
24613 */
24614
24615export { ComponentFixture, ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, InjectSetupWrapper, TestBed, TestComponentRenderer, __core_private_testing_placeholder__, async, discardPeriodicTasks, fakeAsync, flush, flushMicrotasks, getTestBed, inject, resetFakeAsyncZone, tick, waitForAsync, withModule, MetadataOverrider as ɵMetadataOverrider };
24616//# sourceMappingURL=testing.mjs.map