UNPKG

87.6 kBJavaScriptView Raw
1/**
2 * @license Angular v13.0.2
3 * (c) 2010-2021 Google LLC. https://angular.io/
4 * License: MIT
5 */
6
7import * as i0 from '@angular/core';
8import { Injector, ChangeDetectorRef, Testability, TestabilityRegistry, ApplicationRef, SimpleChange, NgZone, ComponentFactoryResolver, Version, ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, PlatformRef, EventEmitter, Directive, isDevMode, NgModule } from '@angular/core';
9import { platformBrowser } from '@angular/platform-browser';
10
11/**
12 * @license
13 * Copyright Google LLC All Rights Reserved.
14 *
15 * Use of this source code is governed by an MIT-style license that can be
16 * found in the LICENSE file at https://angular.io/license
17 */
18function noNg() {
19 throw new Error('AngularJS v1.x is not loaded!');
20}
21const noNgElement = (() => noNg());
22noNgElement.cleanData = noNg;
23let angular = {
24 bootstrap: noNg,
25 module: noNg,
26 element: noNgElement,
27 injector: noNg,
28 version: undefined,
29 resumeBootstrap: noNg,
30 getTestability: noNg
31};
32try {
33 if (window.hasOwnProperty('angular')) {
34 angular = window.angular;
35 }
36}
37catch (_a) {
38 // ignore in CJS mode.
39}
40/**
41 * @deprecated Use `setAngularJSGlobal` instead.
42 *
43 * @publicApi
44 */
45function setAngularLib(ng) {
46 setAngularJSGlobal(ng);
47}
48/**
49 * @deprecated Use `getAngularJSGlobal` instead.
50 *
51 * @publicApi
52 */
53function getAngularLib() {
54 return getAngularJSGlobal();
55}
56/**
57 * Resets the AngularJS global.
58 *
59 * Used when AngularJS is loaded lazily, and not available on `window`.
60 *
61 * @publicApi
62 */
63function setAngularJSGlobal(ng) {
64 angular = ng;
65}
66/**
67 * Returns the current AngularJS global.
68 *
69 * @publicApi
70 */
71function getAngularJSGlobal() {
72 return angular;
73}
74const bootstrap = (e, modules, config) => angular.bootstrap(e, modules, config);
75// Do not declare as `module` to avoid webpack bug
76// (see https://github.com/angular/angular/issues/30050).
77const module_ = (prefix, dependencies) => angular.module(prefix, dependencies);
78const element = (e => angular.element(e));
79element.cleanData = nodes => angular.element.cleanData(nodes);
80const injector = (modules, strictDi) => angular.injector(modules, strictDi);
81const resumeBootstrap = () => angular.resumeBootstrap();
82const getTestability = e => angular.getTestability(e);
83
84/**
85 * @license
86 * Copyright Google LLC All Rights Reserved.
87 *
88 * Use of this source code is governed by an MIT-style license that can be
89 * found in the LICENSE file at https://angular.io/license
90 */
91const $COMPILE = '$compile';
92const $CONTROLLER = '$controller';
93const $DELEGATE = '$delegate';
94const $EXCEPTION_HANDLER = '$exceptionHandler';
95const $HTTP_BACKEND = '$httpBackend';
96const $INJECTOR = '$injector';
97const $INTERVAL = '$interval';
98const $PARSE = '$parse';
99const $PROVIDE = '$provide';
100const $ROOT_ELEMENT = '$rootElement';
101const $ROOT_SCOPE = '$rootScope';
102const $SCOPE = '$scope';
103const $TEMPLATE_CACHE = '$templateCache';
104const $TEMPLATE_REQUEST = '$templateRequest';
105const $$TESTABILITY = '$$testability';
106const COMPILER_KEY = '$$angularCompiler';
107const DOWNGRADED_MODULE_COUNT_KEY = '$$angularDowngradedModuleCount';
108const GROUP_PROJECTABLE_NODES_KEY = '$$angularGroupProjectableNodes';
109const INJECTOR_KEY = '$$angularInjector';
110const LAZY_MODULE_REF = '$$angularLazyModuleRef';
111const NG_ZONE_KEY = '$$angularNgZone';
112const UPGRADE_APP_TYPE_KEY = '$$angularUpgradeAppType';
113const REQUIRE_INJECTOR = '?^^' + INJECTOR_KEY;
114const REQUIRE_NG_MODEL = '?ngModel';
115const UPGRADE_MODULE_NAME = '$$UpgradeModule';
116
117/**
118 * @license
119 * Copyright Google LLC All Rights Reserved.
120 *
121 * Use of this source code is governed by an MIT-style license that can be
122 * found in the LICENSE file at https://angular.io/license
123 */
124/**
125 * A `PropertyBinding` represents a mapping between a property name
126 * and an attribute name. It is parsed from a string of the form
127 * `"prop: attr"`; or simply `"propAndAttr" where the property
128 * and attribute have the same identifier.
129 */
130class PropertyBinding {
131 constructor(prop, attr) {
132 this.prop = prop;
133 this.attr = attr;
134 this.parseBinding();
135 }
136 parseBinding() {
137 this.bracketAttr = `[${this.attr}]`;
138 this.parenAttr = `(${this.attr})`;
139 this.bracketParenAttr = `[(${this.attr})]`;
140 const capitalAttr = this.attr.charAt(0).toUpperCase() + this.attr.substr(1);
141 this.onAttr = `on${capitalAttr}`;
142 this.bindAttr = `bind${capitalAttr}`;
143 this.bindonAttr = `bindon${capitalAttr}`;
144 }
145}
146
147/**
148 * @license
149 * Copyright Google LLC All Rights Reserved.
150 *
151 * Use of this source code is governed by an MIT-style license that can be
152 * found in the LICENSE file at https://angular.io/license
153 */
154const DIRECTIVE_PREFIX_REGEXP = /^(?:x|data)[:\-_]/i;
155const DIRECTIVE_SPECIAL_CHARS_REGEXP = /[:\-_]+(.)/g;
156function onError(e) {
157 // TODO: (misko): We seem to not have a stack trace here!
158 if (console.error) {
159 console.error(e, e.stack);
160 }
161 else {
162 // tslint:disable-next-line:no-console
163 console.log(e, e.stack);
164 }
165 throw e;
166}
167/**
168 * Clean the jqLite/jQuery data on the element and all its descendants.
169 * Equivalent to how jqLite/jQuery invoke `cleanData()` on an Element when removed:
170 * https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/jqLite.js#L349-L355
171 * https://github.com/jquery/jquery/blob/6984d1747623dbc5e87fd6c261a5b6b1628c107c/src/manipulation.js#L182
172 *
173 * NOTE:
174 * `cleanData()` will also invoke the AngularJS `$destroy` DOM event on the element:
175 * https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/Angular.js#L1932-L1945
176 *
177 * @param node The DOM node whose data needs to be cleaned.
178 */
179function cleanData(node) {
180 element.cleanData([node]);
181 if (isParentNode(node)) {
182 element.cleanData(node.querySelectorAll('*'));
183 }
184}
185function controllerKey(name) {
186 return '$' + name + 'Controller';
187}
188/**
189 * Destroy an AngularJS app given the app `$injector`.
190 *
191 * NOTE: Destroying an app is not officially supported by AngularJS, but try to do our best by
192 * destroying `$rootScope` and clean the jqLite/jQuery data on `$rootElement` and all
193 * descendants.
194 *
195 * @param $injector The `$injector` of the AngularJS app to destroy.
196 */
197function destroyApp($injector) {
198 const $rootElement = $injector.get($ROOT_ELEMENT);
199 const $rootScope = $injector.get($ROOT_SCOPE);
200 $rootScope.$destroy();
201 cleanData($rootElement[0]);
202}
203function directiveNormalize(name) {
204 return name.replace(DIRECTIVE_PREFIX_REGEXP, '')
205 .replace(DIRECTIVE_SPECIAL_CHARS_REGEXP, (_, letter) => letter.toUpperCase());
206}
207function getTypeName(type) {
208 // Return the name of the type or the first line of its stringified version.
209 return type.overriddenName || type.name || type.toString().split('\n')[0];
210}
211function getDowngradedModuleCount($injector) {
212 return $injector.has(DOWNGRADED_MODULE_COUNT_KEY) ? $injector.get(DOWNGRADED_MODULE_COUNT_KEY) :
213 0;
214}
215function getUpgradeAppType($injector) {
216 return $injector.has(UPGRADE_APP_TYPE_KEY) ? $injector.get(UPGRADE_APP_TYPE_KEY) :
217 0 /* None */;
218}
219function isFunction(value) {
220 return typeof value === 'function';
221}
222function isParentNode(node) {
223 return isFunction(node.querySelectorAll);
224}
225function validateInjectionKey($injector, downgradedModule, injectionKey, attemptedAction) {
226 const upgradeAppType = getUpgradeAppType($injector);
227 const downgradedModuleCount = getDowngradedModuleCount($injector);
228 // Check for common errors.
229 switch (upgradeAppType) {
230 case 1 /* Dynamic */:
231 case 2 /* Static */:
232 if (downgradedModule) {
233 throw new Error(`Error while ${attemptedAction}: 'downgradedModule' unexpectedly specified.\n` +
234 'You should not specify a value for \'downgradedModule\', unless you are downgrading ' +
235 'more than one Angular module (via \'downgradeModule()\').');
236 }
237 break;
238 case 3 /* Lite */:
239 if (!downgradedModule && (downgradedModuleCount >= 2)) {
240 throw new Error(`Error while ${attemptedAction}: 'downgradedModule' not specified.\n` +
241 'This application contains more than one downgraded Angular module, thus you need to ' +
242 'always specify \'downgradedModule\' when downgrading components and injectables.');
243 }
244 if (!$injector.has(injectionKey)) {
245 throw new Error(`Error while ${attemptedAction}: Unable to find the specified downgraded module.\n` +
246 'Did you forget to downgrade an Angular module or include it in the AngularJS ' +
247 'application?');
248 }
249 break;
250 default:
251 throw new Error(`Error while ${attemptedAction}: Not a valid '@angular/upgrade' application.\n` +
252 'Did you forget to downgrade an Angular module or include it in the AngularJS ' +
253 'application?');
254 }
255}
256class Deferred {
257 constructor() {
258 this.promise = new Promise((res, rej) => {
259 this.resolve = res;
260 this.reject = rej;
261 });
262 }
263}
264/**
265 * @return Whether the passed-in component implements the subset of the
266 * `ControlValueAccessor` interface needed for AngularJS `ng-model`
267 * compatibility.
268 */
269function supportsNgModel(component) {
270 return typeof component.writeValue === 'function' &&
271 typeof component.registerOnChange === 'function';
272}
273/**
274 * Glue the AngularJS `NgModelController` (if it exists) to the component
275 * (if it implements the needed subset of the `ControlValueAccessor` interface).
276 */
277function hookupNgModel(ngModel, component) {
278 if (ngModel && supportsNgModel(component)) {
279 ngModel.$render = () => {
280 component.writeValue(ngModel.$viewValue);
281 };
282 component.registerOnChange(ngModel.$setViewValue.bind(ngModel));
283 if (typeof component.registerOnTouched === 'function') {
284 component.registerOnTouched(ngModel.$setTouched.bind(ngModel));
285 }
286 }
287}
288/**
289 * Test two values for strict equality, accounting for the fact that `NaN !== NaN`.
290 */
291function strictEquals(val1, val2) {
292 return val1 === val2 || (val1 !== val1 && val2 !== val2);
293}
294
295/**
296 * @license
297 * Copyright Google LLC All Rights Reserved.
298 *
299 * Use of this source code is governed by an MIT-style license that can be
300 * found in the LICENSE file at https://angular.io/license
301 */
302const INITIAL_VALUE$1 = {
303 __UNINITIALIZED__: true
304};
305class DowngradeComponentAdapter {
306 constructor(element, attrs, scope, ngModel, parentInjector, $compile, $parse, componentFactory, wrapCallback) {
307 this.element = element;
308 this.attrs = attrs;
309 this.scope = scope;
310 this.ngModel = ngModel;
311 this.parentInjector = parentInjector;
312 this.$compile = $compile;
313 this.$parse = $parse;
314 this.componentFactory = componentFactory;
315 this.wrapCallback = wrapCallback;
316 this.implementsOnChanges = false;
317 this.inputChangeCount = 0;
318 this.inputChanges = {};
319 this.componentScope = scope.$new();
320 }
321 compileContents() {
322 const compiledProjectableNodes = [];
323 const projectableNodes = this.groupProjectableNodes();
324 const linkFns = projectableNodes.map(nodes => this.$compile(nodes));
325 this.element.empty();
326 linkFns.forEach(linkFn => {
327 linkFn(this.scope, (clone) => {
328 compiledProjectableNodes.push(clone);
329 this.element.append(clone);
330 });
331 });
332 return compiledProjectableNodes;
333 }
334 createComponent(projectableNodes) {
335 const providers = [{ provide: $SCOPE, useValue: this.componentScope }];
336 const childInjector = Injector.create({ providers: providers, parent: this.parentInjector, name: 'DowngradeComponentAdapter' });
337 this.componentRef =
338 this.componentFactory.create(childInjector, projectableNodes, this.element[0]);
339 this.viewChangeDetector = this.componentRef.injector.get(ChangeDetectorRef);
340 this.changeDetector = this.componentRef.changeDetectorRef;
341 this.component = this.componentRef.instance;
342 // testability hook is commonly added during component bootstrap in
343 // packages/core/src/application_ref.bootstrap()
344 // in downgraded application, component creation will take place here as well as adding the
345 // testability hook.
346 const testability = this.componentRef.injector.get(Testability, null);
347 if (testability) {
348 this.componentRef.injector.get(TestabilityRegistry)
349 .registerApplication(this.componentRef.location.nativeElement, testability);
350 }
351 hookupNgModel(this.ngModel, this.component);
352 }
353 setupInputs(manuallyAttachView, propagateDigest = true) {
354 const attrs = this.attrs;
355 const inputs = this.componentFactory.inputs || [];
356 for (let i = 0; i < inputs.length; i++) {
357 const input = new PropertyBinding(inputs[i].propName, inputs[i].templateName);
358 let expr = null;
359 if (attrs.hasOwnProperty(input.attr)) {
360 const observeFn = (prop => {
361 let prevValue = INITIAL_VALUE$1;
362 return (currValue) => {
363 // Initially, both `$observe()` and `$watch()` will call this function.
364 if (!strictEquals(prevValue, currValue)) {
365 if (prevValue === INITIAL_VALUE$1) {
366 prevValue = currValue;
367 }
368 this.updateInput(prop, prevValue, currValue);
369 prevValue = currValue;
370 }
371 };
372 })(input.prop);
373 attrs.$observe(input.attr, observeFn);
374 // Use `$watch()` (in addition to `$observe()`) in order to initialize the input in time
375 // for `ngOnChanges()`. This is necessary if we are already in a `$digest`, which means that
376 // `ngOnChanges()` (which is called by a watcher) will run before the `$observe()` callback.
377 let unwatch = this.componentScope.$watch(() => {
378 unwatch();
379 unwatch = null;
380 observeFn(attrs[input.attr]);
381 });
382 }
383 else if (attrs.hasOwnProperty(input.bindAttr)) {
384 expr = attrs[input.bindAttr];
385 }
386 else if (attrs.hasOwnProperty(input.bracketAttr)) {
387 expr = attrs[input.bracketAttr];
388 }
389 else if (attrs.hasOwnProperty(input.bindonAttr)) {
390 expr = attrs[input.bindonAttr];
391 }
392 else if (attrs.hasOwnProperty(input.bracketParenAttr)) {
393 expr = attrs[input.bracketParenAttr];
394 }
395 if (expr != null) {
396 const watchFn = (prop => (currValue, prevValue) => this.updateInput(prop, prevValue, currValue))(input.prop);
397 this.componentScope.$watch(expr, watchFn);
398 }
399 }
400 // Invoke `ngOnChanges()` and Change Detection (when necessary)
401 const detectChanges = () => this.changeDetector.detectChanges();
402 const prototype = this.componentFactory.componentType.prototype;
403 this.implementsOnChanges = !!(prototype && prototype.ngOnChanges);
404 this.componentScope.$watch(() => this.inputChangeCount, this.wrapCallback(() => {
405 // Invoke `ngOnChanges()`
406 if (this.implementsOnChanges) {
407 const inputChanges = this.inputChanges;
408 this.inputChanges = {};
409 this.component.ngOnChanges(inputChanges);
410 }
411 this.viewChangeDetector.markForCheck();
412 // If opted out of propagating digests, invoke change detection when inputs change.
413 if (!propagateDigest) {
414 detectChanges();
415 }
416 }));
417 // If not opted out of propagating digests, invoke change detection on every digest
418 if (propagateDigest) {
419 this.componentScope.$watch(this.wrapCallback(detectChanges));
420 }
421 // If necessary, attach the view so that it will be dirty-checked.
422 // (Allow time for the initial input values to be set and `ngOnChanges()` to be called.)
423 if (manuallyAttachView || !propagateDigest) {
424 let unwatch = this.componentScope.$watch(() => {
425 unwatch();
426 unwatch = null;
427 const appRef = this.parentInjector.get(ApplicationRef);
428 appRef.attachView(this.componentRef.hostView);
429 });
430 }
431 }
432 setupOutputs() {
433 const attrs = this.attrs;
434 const outputs = this.componentFactory.outputs || [];
435 for (let j = 0; j < outputs.length; j++) {
436 const output = new PropertyBinding(outputs[j].propName, outputs[j].templateName);
437 const bindonAttr = output.bindonAttr.substring(0, output.bindonAttr.length - 6);
438 const bracketParenAttr = `[(${output.bracketParenAttr.substring(2, output.bracketParenAttr.length - 8)})]`;
439 // order below is important - first update bindings then evaluate expressions
440 if (attrs.hasOwnProperty(bindonAttr)) {
441 this.subscribeToOutput(output, attrs[bindonAttr], true);
442 }
443 if (attrs.hasOwnProperty(bracketParenAttr)) {
444 this.subscribeToOutput(output, attrs[bracketParenAttr], true);
445 }
446 if (attrs.hasOwnProperty(output.onAttr)) {
447 this.subscribeToOutput(output, attrs[output.onAttr]);
448 }
449 if (attrs.hasOwnProperty(output.parenAttr)) {
450 this.subscribeToOutput(output, attrs[output.parenAttr]);
451 }
452 }
453 }
454 subscribeToOutput(output, expr, isAssignment = false) {
455 const getter = this.$parse(expr);
456 const setter = getter.assign;
457 if (isAssignment && !setter) {
458 throw new Error(`Expression '${expr}' is not assignable!`);
459 }
460 const emitter = this.component[output.prop];
461 if (emitter) {
462 emitter.subscribe({
463 next: isAssignment ? (v) => setter(this.scope, v) :
464 (v) => getter(this.scope, { '$event': v })
465 });
466 }
467 else {
468 throw new Error(`Missing emitter '${output.prop}' on component '${getTypeName(this.componentFactory.componentType)}'!`);
469 }
470 }
471 registerCleanup() {
472 const testabilityRegistry = this.componentRef.injector.get(TestabilityRegistry);
473 const destroyComponentRef = this.wrapCallback(() => this.componentRef.destroy());
474 let destroyed = false;
475 this.element.on('$destroy', () => {
476 // The `$destroy` event may have been triggered by the `cleanData()` call in the
477 // `componentScope` `$destroy` handler below. In that case, we don't want to call
478 // `componentScope.$destroy()` again.
479 if (!destroyed)
480 this.componentScope.$destroy();
481 });
482 this.componentScope.$on('$destroy', () => {
483 if (!destroyed) {
484 destroyed = true;
485 testabilityRegistry.unregisterApplication(this.componentRef.location.nativeElement);
486 // The `componentScope` might be getting destroyed, because an ancestor element is being
487 // removed/destroyed. If that is the case, jqLite/jQuery would normally invoke `cleanData()`
488 // on the removed element and all descendants.
489 // https://github.com/angular/angular.js/blob/2e72ea13fa98bebf6ed4b5e3c45eaf5f990ed16f/src/jqLite.js#L349-L355
490 // https://github.com/jquery/jquery/blob/6984d1747623dbc5e87fd6c261a5b6b1628c107c/src/manipulation.js#L182
491 //
492 // Here, however, `destroyComponentRef()` may under some circumstances remove the element
493 // from the DOM and therefore it will no longer be a descendant of the removed element when
494 // `cleanData()` is called. This would result in a memory leak, because the element's data
495 // and event handlers (and all objects directly or indirectly referenced by them) would be
496 // retained.
497 //
498 // To ensure the element is always properly cleaned up, we manually call `cleanData()` on
499 // this element and its descendants before destroying the `ComponentRef`.
500 cleanData(this.element[0]);
501 destroyComponentRef();
502 }
503 });
504 }
505 getInjector() {
506 return this.componentRef.injector;
507 }
508 updateInput(prop, prevValue, currValue) {
509 if (this.implementsOnChanges) {
510 this.inputChanges[prop] = new SimpleChange(prevValue, currValue, prevValue === currValue);
511 }
512 this.inputChangeCount++;
513 this.component[prop] = currValue;
514 }
515 groupProjectableNodes() {
516 let ngContentSelectors = this.componentFactory.ngContentSelectors;
517 return groupNodesBySelector(ngContentSelectors, this.element.contents());
518 }
519}
520/**
521 * Group a set of DOM nodes into `ngContent` groups, based on the given content selectors.
522 */
523function groupNodesBySelector(ngContentSelectors, nodes) {
524 const projectableNodes = [];
525 for (let i = 0, ii = ngContentSelectors.length; i < ii; ++i) {
526 projectableNodes[i] = [];
527 }
528 for (let j = 0, jj = nodes.length; j < jj; ++j) {
529 const node = nodes[j];
530 const ngContentIndex = findMatchingNgContentIndex(node, ngContentSelectors);
531 if (ngContentIndex != null) {
532 projectableNodes[ngContentIndex].push(node);
533 }
534 }
535 return projectableNodes;
536}
537function findMatchingNgContentIndex(element, ngContentSelectors) {
538 const ngContentIndices = [];
539 let wildcardNgContentIndex = -1;
540 for (let i = 0; i < ngContentSelectors.length; i++) {
541 const selector = ngContentSelectors[i];
542 if (selector === '*') {
543 wildcardNgContentIndex = i;
544 }
545 else {
546 if (matchesSelector(element, selector)) {
547 ngContentIndices.push(i);
548 }
549 }
550 }
551 ngContentIndices.sort();
552 if (wildcardNgContentIndex !== -1) {
553 ngContentIndices.push(wildcardNgContentIndex);
554 }
555 return ngContentIndices.length ? ngContentIndices[0] : null;
556}
557let _matches;
558function matchesSelector(el, selector) {
559 if (!_matches) {
560 const elProto = Element.prototype;
561 _matches = elProto.matches || elProto.matchesSelector || elProto.mozMatchesSelector ||
562 elProto.msMatchesSelector || elProto.oMatchesSelector || elProto.webkitMatchesSelector;
563 }
564 return el.nodeType === Node.ELEMENT_NODE ? _matches.call(el, selector) : false;
565}
566
567/**
568 * @license
569 * Copyright Google LLC All Rights Reserved.
570 *
571 * Use of this source code is governed by an MIT-style license that can be
572 * found in the LICENSE file at https://angular.io/license
573 */
574function isThenable(obj) {
575 return !!obj && isFunction(obj.then);
576}
577/**
578 * Synchronous, promise-like object.
579 */
580class SyncPromise {
581 constructor() {
582 this.resolved = false;
583 this.callbacks = [];
584 }
585 static all(valuesOrPromises) {
586 const aggrPromise = new SyncPromise();
587 let resolvedCount = 0;
588 const results = [];
589 const resolve = (idx, value) => {
590 results[idx] = value;
591 if (++resolvedCount === valuesOrPromises.length)
592 aggrPromise.resolve(results);
593 };
594 valuesOrPromises.forEach((p, idx) => {
595 if (isThenable(p)) {
596 p.then(v => resolve(idx, v));
597 }
598 else {
599 resolve(idx, p);
600 }
601 });
602 return aggrPromise;
603 }
604 resolve(value) {
605 // Do nothing, if already resolved.
606 if (this.resolved)
607 return;
608 this.value = value;
609 this.resolved = true;
610 // Run the queued callbacks.
611 this.callbacks.forEach(callback => callback(value));
612 this.callbacks.length = 0;
613 }
614 then(callback) {
615 if (this.resolved) {
616 callback(this.value);
617 }
618 else {
619 this.callbacks.push(callback);
620 }
621 }
622}
623
624/**
625 * @license
626 * Copyright Google LLC All Rights Reserved.
627 *
628 * Use of this source code is governed by an MIT-style license that can be
629 * found in the LICENSE file at https://angular.io/license
630 */
631/**
632 * @description
633 *
634 * A helper function that allows an Angular component to be used from AngularJS.
635 *
636 * *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
637 * library for hybrid upgrade apps that support AOT compilation*
638 *
639 * This helper function returns a factory function to be used for registering
640 * an AngularJS wrapper directive for "downgrading" an Angular component.
641 *
642 * @usageNotes
643 * ### Examples
644 *
645 * Let's assume that you have an Angular component called `ng2Heroes` that needs
646 * to be made available in AngularJS templates.
647 *
648 * {@example upgrade/static/ts/full/module.ts region="ng2-heroes"}
649 *
650 * We must create an AngularJS [directive](https://docs.angularjs.org/guide/directive)
651 * that will make this Angular component available inside AngularJS templates.
652 * The `downgradeComponent()` function returns a factory function that we
653 * can use to define the AngularJS directive that wraps the "downgraded" component.
654 *
655 * {@example upgrade/static/ts/full/module.ts region="ng2-heroes-wrapper"}
656 *
657 * For more details and examples on downgrading Angular components to AngularJS components please
658 * visit the [Upgrade guide](guide/upgrade#using-angular-components-from-angularjs-code).
659 *
660 * @param info contains information about the Component that is being downgraded:
661 *
662 * - `component: Type<any>`: The type of the Component that will be downgraded
663 * - `downgradedModule?: string`: The name of the downgraded module (if any) that the component
664 * "belongs to", as returned by a call to `downgradeModule()`. It is the module, whose
665 * corresponding Angular module will be bootstrapped, when the component needs to be instantiated.
666 * <br />
667 * (This option is only necessary when using `downgradeModule()` to downgrade more than one
668 * Angular module.)
669 * - `propagateDigest?: boolean`: Whether to perform {@link ChangeDetectorRef#detectChanges
670 * change detection} on the component on every
671 * [$digest](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest). If set to `false`,
672 * change detection will still be performed when any of the component's inputs changes.
673 * (Default: true)
674 *
675 * @returns a factory function that can be used to register the component in an
676 * AngularJS module.
677 *
678 * @publicApi
679 */
680function downgradeComponent(info) {
681 const directiveFactory = function ($compile, $injector, $parse) {
682 // When using `downgradeModule()`, we need to handle certain things specially. For example:
683 // - We always need to attach the component view to the `ApplicationRef` for it to be
684 // dirty-checked.
685 // - We need to ensure callbacks to Angular APIs (e.g. change detection) are run inside the
686 // Angular zone.
687 // NOTE: This is not needed, when using `UpgradeModule`, because `$digest()` will be run
688 // inside the Angular zone (except if explicitly escaped, in which case we shouldn't
689 // force it back in).
690 const isNgUpgradeLite = getUpgradeAppType($injector) === 3 /* Lite */;
691 const wrapCallback = !isNgUpgradeLite ? cb => cb : cb => () => NgZone.isInAngularZone() ? cb() : ngZone.run(cb);
692 let ngZone;
693 // When downgrading multiple modules, special handling is needed wrt injectors.
694 const hasMultipleDowngradedModules = isNgUpgradeLite && (getDowngradedModuleCount($injector) > 1);
695 return {
696 restrict: 'E',
697 terminal: true,
698 require: [REQUIRE_INJECTOR, REQUIRE_NG_MODEL],
699 link: (scope, element, attrs, required) => {
700 // We might have to compile the contents asynchronously, because this might have been
701 // triggered by `UpgradeNg1ComponentAdapterBuilder`, before the Angular templates have
702 // been compiled.
703 const ngModel = required[1];
704 const parentInjector = required[0];
705 let moduleInjector = undefined;
706 let ranAsync = false;
707 if (!parentInjector || hasMultipleDowngradedModules) {
708 const downgradedModule = info.downgradedModule || '';
709 const lazyModuleRefKey = `${LAZY_MODULE_REF}${downgradedModule}`;
710 const attemptedAction = `instantiating component '${getTypeName(info.component)}'`;
711 validateInjectionKey($injector, downgradedModule, lazyModuleRefKey, attemptedAction);
712 const lazyModuleRef = $injector.get(lazyModuleRefKey);
713 moduleInjector = lazyModuleRef.injector || lazyModuleRef.promise;
714 }
715 // Notes:
716 //
717 // There are two injectors: `finalModuleInjector` and `finalParentInjector` (they might be
718 // the same instance, but that is irrelevant):
719 // - `finalModuleInjector` is used to retrieve `ComponentFactoryResolver`, thus it must be
720 // on the same tree as the `NgModule` that declares this downgraded component.
721 // - `finalParentInjector` is used for all other injection purposes.
722 // (Note that Angular knows to only traverse the component-tree part of that injector,
723 // when looking for an injectable and then switch to the module injector.)
724 //
725 // There are basically three cases:
726 // - If there is no parent component (thus no `parentInjector`), we bootstrap the downgraded
727 // `NgModule` and use its injector as both `finalModuleInjector` and
728 // `finalParentInjector`.
729 // - If there is a parent component (and thus a `parentInjector`) and we are sure that it
730 // belongs to the same `NgModule` as this downgraded component (e.g. because there is only
731 // one downgraded module, we use that `parentInjector` as both `finalModuleInjector` and
732 // `finalParentInjector`.
733 // - If there is a parent component, but it may belong to a different `NgModule`, then we
734 // use the `parentInjector` as `finalParentInjector` and this downgraded component's
735 // declaring `NgModule`'s injector as `finalModuleInjector`.
736 // Note 1: If the `NgModule` is already bootstrapped, we just get its injector (we don't
737 // bootstrap again).
738 // Note 2: It is possible that (while there are multiple downgraded modules) this
739 // downgraded component and its parent component both belong to the same NgModule.
740 // In that case, we could have used the `parentInjector` as both
741 // `finalModuleInjector` and `finalParentInjector`, but (for simplicity) we are
742 // treating this case as if they belong to different `NgModule`s. That doesn't
743 // really affect anything, since `parentInjector` has `moduleInjector` as ancestor
744 // and trying to resolve `ComponentFactoryResolver` from either one will return
745 // the same instance.
746 // If there is a parent component, use its injector as parent injector.
747 // If this is a "top-level" Angular component, use the module injector.
748 const finalParentInjector = parentInjector || moduleInjector;
749 // If this is a "top-level" Angular component or the parent component may belong to a
750 // different `NgModule`, use the module injector for module-specific dependencies.
751 // If there is a parent component that belongs to the same `NgModule`, use its injector.
752 const finalModuleInjector = moduleInjector || parentInjector;
753 const doDowngrade = (injector, moduleInjector) => {
754 // Retrieve `ComponentFactoryResolver` from the injector tied to the `NgModule` this
755 // component belongs to.
756 const componentFactoryResolver = moduleInjector.get(ComponentFactoryResolver);
757 const componentFactory = componentFactoryResolver.resolveComponentFactory(info.component);
758 if (!componentFactory) {
759 throw new Error(`Expecting ComponentFactory for: ${getTypeName(info.component)}`);
760 }
761 const injectorPromise = new ParentInjectorPromise(element);
762 const facade = new DowngradeComponentAdapter(element, attrs, scope, ngModel, injector, $compile, $parse, componentFactory, wrapCallback);
763 const projectableNodes = facade.compileContents();
764 facade.createComponent(projectableNodes);
765 facade.setupInputs(isNgUpgradeLite, info.propagateDigest);
766 facade.setupOutputs();
767 facade.registerCleanup();
768 injectorPromise.resolve(facade.getInjector());
769 if (ranAsync) {
770 // If this is run async, it is possible that it is not run inside a
771 // digest and initial input values will not be detected.
772 scope.$evalAsync(() => { });
773 }
774 };
775 const downgradeFn = !isNgUpgradeLite ? doDowngrade : (pInjector, mInjector) => {
776 if (!ngZone) {
777 ngZone = pInjector.get(NgZone);
778 }
779 wrapCallback(() => doDowngrade(pInjector, mInjector))();
780 };
781 // NOTE:
782 // Not using `ParentInjectorPromise.all()` (which is inherited from `SyncPromise`), because
783 // Closure Compiler (or some related tool) complains:
784 // `TypeError: ...$src$downgrade_component_ParentInjectorPromise.all is not a function`
785 SyncPromise.all([finalParentInjector, finalModuleInjector])
786 .then(([pInjector, mInjector]) => downgradeFn(pInjector, mInjector));
787 ranAsync = true;
788 }
789 };
790 };
791 // bracket-notation because of closure - see #14441
792 directiveFactory['$inject'] = [$COMPILE, $INJECTOR, $PARSE];
793 return directiveFactory;
794}
795/**
796 * Synchronous promise-like object to wrap parent injectors,
797 * to preserve the synchronous nature of AngularJS's `$compile`.
798 */
799class ParentInjectorPromise extends SyncPromise {
800 constructor(element) {
801 super();
802 this.element = element;
803 this.injectorKey = controllerKey(INJECTOR_KEY);
804 // Store the promise on the element.
805 element.data(this.injectorKey, this);
806 }
807 resolve(injector) {
808 // Store the real injector on the element.
809 this.element.data(this.injectorKey, injector);
810 // Release the element to prevent memory leaks.
811 this.element = null;
812 // Resolve the promise.
813 super.resolve(injector);
814 }
815}
816
817/**
818 * @license
819 * Copyright Google LLC All Rights Reserved.
820 *
821 * Use of this source code is governed by an MIT-style license that can be
822 * found in the LICENSE file at https://angular.io/license
823 */
824/**
825 * @description
826 *
827 * A helper function to allow an Angular service to be accessible from AngularJS.
828 *
829 * *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
830 * library for hybrid upgrade apps that support AOT compilation*
831 *
832 * This helper function returns a factory function that provides access to the Angular
833 * service identified by the `token` parameter.
834 *
835 * @usageNotes
836 * ### Examples
837 *
838 * First ensure that the service to be downgraded is provided in an `NgModule`
839 * that will be part of the upgrade application. For example, let's assume we have
840 * defined `HeroesService`
841 *
842 * {@example upgrade/static/ts/full/module.ts region="ng2-heroes-service"}
843 *
844 * and that we have included this in our upgrade app `NgModule`
845 *
846 * {@example upgrade/static/ts/full/module.ts region="ng2-module"}
847 *
848 * Now we can register the `downgradeInjectable` factory function for the service
849 * on an AngularJS module.
850 *
851 * {@example upgrade/static/ts/full/module.ts region="downgrade-ng2-heroes-service"}
852 *
853 * Inside an AngularJS component's controller we can get hold of the
854 * downgraded service via the name we gave when downgrading.
855 *
856 * {@example upgrade/static/ts/full/module.ts region="example-app"}
857 *
858 * <div class="alert is-important">
859 *
860 * When using `downgradeModule()`, downgraded injectables will not be available until the Angular
861 * module that provides them is instantiated. In order to be safe, you need to ensure that the
862 * downgraded injectables are not used anywhere _outside_ the part of the app where it is
863 * guaranteed that their module has been instantiated.
864 *
865 * For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
866 * from a downgraded Angular component provided by the same Angular module as the injectable, but
867 * it is _not OK_ to use it in an AngularJS component that may be used independently of Angular or
868 * use it in a downgraded Angular component from a different module.
869 *
870 * </div>
871 *
872 * @param token an `InjectionToken` that identifies a service provided from Angular.
873 * @param downgradedModule the name of the downgraded module (if any) that the injectable
874 * "belongs to", as returned by a call to `downgradeModule()`. It is the module, whose injector will
875 * be used for instantiating the injectable.<br />
876 * (This option is only necessary when using `downgradeModule()` to downgrade more than one Angular
877 * module.)
878 *
879 * @returns a [factory function](https://docs.angularjs.org/guide/di) that can be
880 * used to register the service on an AngularJS module.
881 *
882 * @publicApi
883 */
884function downgradeInjectable(token, downgradedModule = '') {
885 const factory = function ($injector) {
886 const injectorKey = `${INJECTOR_KEY}${downgradedModule}`;
887 const injectableName = isFunction(token) ? getTypeName(token) : String(token);
888 const attemptedAction = `instantiating injectable '${injectableName}'`;
889 validateInjectionKey($injector, downgradedModule, injectorKey, attemptedAction);
890 try {
891 const injector = $injector.get(injectorKey);
892 return injector.get(token);
893 }
894 catch (err) {
895 throw new Error(`Error while ${attemptedAction}: ${err.message || err}`);
896 }
897 };
898 factory['$inject'] = [$INJECTOR];
899 return factory;
900}
901
902/**
903 * @license
904 * Copyright Google LLC All Rights Reserved.
905 *
906 * Use of this source code is governed by an MIT-style license that can be
907 * found in the LICENSE file at https://angular.io/license
908 */
909/**
910 * @publicApi
911 */
912const VERSION = new Version('13.0.2');
913
914/**
915 * @license
916 * Copyright Google LLC All Rights Reserved.
917 *
918 * Use of this source code is governed by an MIT-style license that can be
919 * found in the LICENSE file at https://angular.io/license
920 */
921// We have to do a little dance to get the ng1 injector into the module injector.
922// We store the ng1 injector so that the provider in the module injector can access it
923// Then we "get" the ng1 injector from the module injector, which triggers the provider to read
924// the stored injector and release the reference to it.
925let tempInjectorRef = null;
926function setTempInjectorRef(injector) {
927 tempInjectorRef = injector;
928}
929function injectorFactory() {
930 if (!tempInjectorRef) {
931 throw new Error('Trying to get the AngularJS injector before it being set.');
932 }
933 const injector = tempInjectorRef;
934 tempInjectorRef = null; // clear the value to prevent memory leaks
935 return injector;
936}
937function rootScopeFactory(i) {
938 return i.get('$rootScope');
939}
940function compileFactory(i) {
941 return i.get('$compile');
942}
943function parseFactory(i) {
944 return i.get('$parse');
945}
946const angular1Providers = [
947 // We must use exported named functions for the ng2 factories to keep the compiler happy:
948 // > Metadata collected contains an error that will be reported at runtime:
949 // > Function calls are not supported.
950 // > Consider replacing the function or lambda with a reference to an exported function
951 { provide: '$injector', useFactory: injectorFactory, deps: [] },
952 { provide: '$rootScope', useFactory: rootScopeFactory, deps: ['$injector'] },
953 { provide: '$compile', useFactory: compileFactory, deps: ['$injector'] },
954 { provide: '$parse', useFactory: parseFactory, deps: ['$injector'] }
955];
956
957/**
958 * @license
959 * Copyright Google LLC All Rights Reserved.
960 *
961 * Use of this source code is governed by an MIT-style license that can be
962 * found in the LICENSE file at https://angular.io/license
963 */
964class NgAdapterInjector {
965 constructor(modInjector) {
966 this.modInjector = modInjector;
967 }
968 // When Angular locate a service in the component injector tree, the not found value is set to
969 // `NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR`. In such a case we should not walk up to the module
970 // injector.
971 // AngularJS only supports a single tree and should always check the module injector.
972 get(token, notFoundValue) {
973 if (notFoundValue === ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
974 return notFoundValue;
975 }
976 return this.modInjector.get(token, notFoundValue);
977 }
978}
979
980/**
981 * @license
982 * Copyright Google LLC All Rights Reserved.
983 *
984 * Use of this source code is governed by an MIT-style license that can be
985 * found in the LICENSE file at https://angular.io/license
986 */
987let moduleUid = 0;
988/**
989 * @description
990 *
991 * A helper function for creating an AngularJS module that can bootstrap an Angular module
992 * "on-demand" (possibly lazily) when a {@link downgradeComponent downgraded component} needs to be
993 * instantiated.
994 *
995 * *Part of the [upgrade/static](api?query=upgrade/static) library for hybrid upgrade apps that
996 * support AOT compilation.*
997 *
998 * It allows loading/bootstrapping the Angular part of a hybrid application lazily and not having to
999 * pay the cost up-front. For example, you can have an AngularJS application that uses Angular for
1000 * specific routes and only instantiate the Angular modules if/when the user visits one of these
1001 * routes.
1002 *
1003 * The Angular module will be bootstrapped once (when requested for the first time) and the same
1004 * reference will be used from that point onwards.
1005 *
1006 * `downgradeModule()` requires either an `NgModuleFactory` or a function:
1007 * - `NgModuleFactory`: If you pass an `NgModuleFactory`, it will be used to instantiate a module
1008 * using `platformBrowser`'s {@link PlatformRef#bootstrapModuleFactory bootstrapModuleFactory()}.
1009 * - `Function`: If you pass a function, it is expected to return a promise resolving to an
1010 * `NgModuleRef`. The function is called with an array of extra {@link StaticProvider Providers}
1011 * that are expected to be available from the returned `NgModuleRef`'s `Injector`.
1012 *
1013 * `downgradeModule()` returns the name of the created AngularJS wrapper module. You can use it to
1014 * declare a dependency in your main AngularJS module.
1015 *
1016 * {@example upgrade/static/ts/lite/module.ts region="basic-how-to"}
1017 *
1018 * For more details on how to use `downgradeModule()` see
1019 * [Upgrading for Performance](guide/upgrade-performance).
1020 *
1021 * @usageNotes
1022 *
1023 * Apart from `UpgradeModule`, you can use the rest of the `upgrade/static` helpers as usual to
1024 * build a hybrid application. Note that the Angular pieces (e.g. downgraded services) will not be
1025 * available until the downgraded module has been bootstrapped, i.e. by instantiating a downgraded
1026 * component.
1027 *
1028 * <div class="alert is-important">
1029 *
1030 * You cannot use `downgradeModule()` and `UpgradeModule` in the same hybrid application.<br />
1031 * Use one or the other.
1032 *
1033 * </div>
1034 *
1035 * ### Differences with `UpgradeModule`
1036 *
1037 * Besides their different API, there are two important internal differences between
1038 * `downgradeModule()` and `UpgradeModule` that affect the behavior of hybrid applications:
1039 *
1040 * 1. Unlike `UpgradeModule`, `downgradeModule()` does not bootstrap the main AngularJS module
1041 * inside the {@link NgZone Angular zone}.
1042 * 2. Unlike `UpgradeModule`, `downgradeModule()` does not automatically run a
1043 * [$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest) when changes are
1044 * detected in the Angular part of the application.
1045 *
1046 * What this means is that applications using `UpgradeModule` will run change detection more
1047 * frequently in order to ensure that both frameworks are properly notified about possible changes.
1048 * This will inevitably result in more change detection runs than necessary.
1049 *
1050 * `downgradeModule()`, on the other side, does not try to tie the two change detection systems as
1051 * tightly, restricting the explicit change detection runs only to cases where it knows it is
1052 * necessary (e.g. when the inputs of a downgraded component change). This improves performance,
1053 * especially in change-detection-heavy applications, but leaves it up to the developer to manually
1054 * notify each framework as needed.
1055 *
1056 * For a more detailed discussion of the differences and their implications, see
1057 * [Upgrading for Performance](guide/upgrade-performance).
1058 *
1059 * <div class="alert is-helpful">
1060 *
1061 * You can manually trigger a change detection run in AngularJS using
1062 * [scope.$apply(...)](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply) or
1063 * [$rootScope.$digest()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$digest).
1064 *
1065 * You can manually trigger a change detection run in Angular using {@link NgZone#run
1066 * ngZone.run(...)}.
1067 *
1068 * </div>
1069 *
1070 * ### Downgrading multiple modules
1071 *
1072 * It is possible to downgrade multiple modules and include them in an AngularJS application. In
1073 * that case, each downgraded module will be bootstrapped when an associated downgraded component or
1074 * injectable needs to be instantiated.
1075 *
1076 * Things to keep in mind, when downgrading multiple modules:
1077 *
1078 * - Each downgraded component/injectable needs to be explicitly associated with a downgraded
1079 * module. See `downgradeComponent()` and `downgradeInjectable()` for more details.
1080 *
1081 * - If you want some injectables to be shared among all downgraded modules, you can provide them as
1082 * `StaticProvider`s, when creating the `PlatformRef` (e.g. via `platformBrowser` or
1083 * `platformBrowserDynamic`).
1084 *
1085 * - When using {@link PlatformRef#bootstrapmodule `bootstrapModule()`} or
1086 * {@link PlatformRef#bootstrapmodulefactory `bootstrapModuleFactory()`} to bootstrap the
1087 * downgraded modules, each one is considered a "root" module. As a consequence, a new instance
1088 * will be created for every injectable provided in `"root"` (via
1089 * {@link Injectable#providedIn `providedIn`}).
1090 * If this is not your intention, you can have a shared module (that will act as act as the "root"
1091 * module) and create all downgraded modules using that module's injector:
1092 *
1093 * {@example upgrade/static/ts/lite-multi-shared/module.ts region="shared-root-module"}
1094 *
1095 * @publicApi
1096 */
1097function downgradeModule(moduleFactoryOrBootstrapFn) {
1098 const lazyModuleName = `${UPGRADE_MODULE_NAME}.lazy${++moduleUid}`;
1099 const lazyModuleRefKey = `${LAZY_MODULE_REF}${lazyModuleName}`;
1100 const lazyInjectorKey = `${INJECTOR_KEY}${lazyModuleName}`;
1101 const bootstrapFn = isFunction(moduleFactoryOrBootstrapFn) ?
1102 moduleFactoryOrBootstrapFn :
1103 (extraProviders) => platformBrowser(extraProviders).bootstrapModuleFactory(moduleFactoryOrBootstrapFn);
1104 let injector;
1105 // Create an ng1 module to bootstrap.
1106 module_(lazyModuleName, [])
1107 .constant(UPGRADE_APP_TYPE_KEY, 3 /* Lite */)
1108 .factory(INJECTOR_KEY, [lazyInjectorKey, identity])
1109 .factory(lazyInjectorKey, () => {
1110 if (!injector) {
1111 throw new Error('Trying to get the Angular injector before bootstrapping the corresponding ' +
1112 'Angular module.');
1113 }
1114 return injector;
1115 })
1116 .factory(LAZY_MODULE_REF, [lazyModuleRefKey, identity])
1117 .factory(lazyModuleRefKey, [
1118 $INJECTOR,
1119 ($injector) => {
1120 setTempInjectorRef($injector);
1121 const result = {
1122 promise: bootstrapFn(angular1Providers).then(ref => {
1123 injector = result.injector = new NgAdapterInjector(ref.injector);
1124 injector.get($INJECTOR);
1125 // Destroy the AngularJS app once the Angular `PlatformRef` is destroyed.
1126 // This does not happen in a typical SPA scenario, but it might be useful for
1127 // other use-cases where disposing of an Angular/AngularJS app is necessary
1128 // (such as Hot Module Replacement (HMR)).
1129 // See https://github.com/angular/angular/issues/39935.
1130 injector.get(PlatformRef).onDestroy(() => destroyApp($injector));
1131 return injector;
1132 })
1133 };
1134 return result;
1135 }
1136 ])
1137 .config([
1138 $INJECTOR, $PROVIDE,
1139 ($injector, $provide) => {
1140 $provide.constant(DOWNGRADED_MODULE_COUNT_KEY, getDowngradedModuleCount($injector) + 1);
1141 }
1142 ]);
1143 return lazyModuleName;
1144}
1145function identity(x) {
1146 return x;
1147}
1148
1149/**
1150 * @license
1151 * Copyright Google LLC All Rights Reserved.
1152 *
1153 * Use of this source code is governed by an MIT-style license that can be
1154 * found in the LICENSE file at https://angular.io/license
1155 */
1156// Constants
1157const REQUIRE_PREFIX_RE = /^(\^\^?)?(\?)?(\^\^?)?/;
1158// Classes
1159class UpgradeHelper {
1160 constructor(injector, name, elementRef, directive) {
1161 this.name = name;
1162 this.$injector = injector.get($INJECTOR);
1163 this.$compile = this.$injector.get($COMPILE);
1164 this.$controller = this.$injector.get($CONTROLLER);
1165 this.element = elementRef.nativeElement;
1166 this.$element = element(this.element);
1167 this.directive = directive || UpgradeHelper.getDirective(this.$injector, name);
1168 }
1169 static getDirective($injector, name) {
1170 const directives = $injector.get(name + 'Directive');
1171 if (directives.length > 1) {
1172 throw new Error(`Only support single directive definition for: ${name}`);
1173 }
1174 const directive = directives[0];
1175 // AngularJS will transform `link: xyz` to `compile: () => xyz`. So we can only tell there was a
1176 // user-defined `compile` if there is no `link`. In other cases, we will just ignore `compile`.
1177 if (directive.compile && !directive.link)
1178 notSupported(name, 'compile');
1179 if (directive.replace)
1180 notSupported(name, 'replace');
1181 if (directive.terminal)
1182 notSupported(name, 'terminal');
1183 return directive;
1184 }
1185 static getTemplate($injector, directive, fetchRemoteTemplate = false, $element) {
1186 if (directive.template !== undefined) {
1187 return getOrCall(directive.template, $element);
1188 }
1189 else if (directive.templateUrl) {
1190 const $templateCache = $injector.get($TEMPLATE_CACHE);
1191 const url = getOrCall(directive.templateUrl, $element);
1192 const template = $templateCache.get(url);
1193 if (template !== undefined) {
1194 return template;
1195 }
1196 else if (!fetchRemoteTemplate) {
1197 throw new Error('loading directive templates asynchronously is not supported');
1198 }
1199 return new Promise((resolve, reject) => {
1200 const $httpBackend = $injector.get($HTTP_BACKEND);
1201 $httpBackend('GET', url, null, (status, response) => {
1202 if (status === 200) {
1203 resolve($templateCache.put(url, response));
1204 }
1205 else {
1206 reject(`GET component template from '${url}' returned '${status}: ${response}'`);
1207 }
1208 });
1209 });
1210 }
1211 else {
1212 throw new Error(`Directive '${directive.name}' is not a component, it is missing template.`);
1213 }
1214 }
1215 buildController(controllerType, $scope) {
1216 // TODO: Document that we do not pre-assign bindings on the controller instance.
1217 // Quoted properties below so that this code can be optimized with Closure Compiler.
1218 const locals = { '$scope': $scope, '$element': this.$element };
1219 const controller = this.$controller(controllerType, locals, null, this.directive.controllerAs);
1220 this.$element.data(controllerKey(this.directive.name), controller);
1221 return controller;
1222 }
1223 compileTemplate(template) {
1224 if (template === undefined) {
1225 template =
1226 UpgradeHelper.getTemplate(this.$injector, this.directive, false, this.$element);
1227 }
1228 return this.compileHtml(template);
1229 }
1230 onDestroy($scope, controllerInstance) {
1231 if (controllerInstance && isFunction(controllerInstance.$onDestroy)) {
1232 controllerInstance.$onDestroy();
1233 }
1234 $scope.$destroy();
1235 cleanData(this.element);
1236 }
1237 prepareTransclusion() {
1238 const transclude = this.directive.transclude;
1239 const contentChildNodes = this.extractChildNodes();
1240 const attachChildrenFn = (scope, cloneAttachFn) => {
1241 // Since AngularJS v1.5.8, `cloneAttachFn` will try to destroy the transclusion scope if
1242 // `$template` is empty. Since the transcluded content comes from Angular, not AngularJS,
1243 // there will be no transclusion scope here.
1244 // Provide a dummy `scope.$destroy()` method to prevent `cloneAttachFn` from throwing.
1245 scope = scope || { $destroy: () => undefined };
1246 return cloneAttachFn($template, scope);
1247 };
1248 let $template = contentChildNodes;
1249 if (transclude) {
1250 const slots = Object.create(null);
1251 if (typeof transclude === 'object') {
1252 $template = [];
1253 const slotMap = Object.create(null);
1254 const filledSlots = Object.create(null);
1255 // Parse the element selectors.
1256 Object.keys(transclude).forEach(slotName => {
1257 let selector = transclude[slotName];
1258 const optional = selector.charAt(0) === '?';
1259 selector = optional ? selector.substring(1) : selector;
1260 slotMap[selector] = slotName;
1261 slots[slotName] = null; // `null`: Defined but not yet filled.
1262 filledSlots[slotName] = optional; // Consider optional slots as filled.
1263 });
1264 // Add the matching elements into their slot.
1265 contentChildNodes.forEach(node => {
1266 const slotName = slotMap[directiveNormalize(node.nodeName.toLowerCase())];
1267 if (slotName) {
1268 filledSlots[slotName] = true;
1269 slots[slotName] = slots[slotName] || [];
1270 slots[slotName].push(node);
1271 }
1272 else {
1273 $template.push(node);
1274 }
1275 });
1276 // Check for required slots that were not filled.
1277 Object.keys(filledSlots).forEach(slotName => {
1278 if (!filledSlots[slotName]) {
1279 throw new Error(`Required transclusion slot '${slotName}' on directive: ${this.name}`);
1280 }
1281 });
1282 Object.keys(slots).filter(slotName => slots[slotName]).forEach(slotName => {
1283 const nodes = slots[slotName];
1284 slots[slotName] = (scope, cloneAttach) => {
1285 return cloneAttach(nodes, scope);
1286 };
1287 });
1288 }
1289 // Attach `$$slots` to default slot transclude fn.
1290 attachChildrenFn.$$slots = slots;
1291 // AngularJS v1.6+ ignores empty or whitespace-only transcluded text nodes. But Angular
1292 // removes all text content after the first interpolation and updates it later, after
1293 // evaluating the expressions. This would result in AngularJS failing to recognize text
1294 // nodes that start with an interpolation as transcluded content and use the fallback
1295 // content instead.
1296 // To avoid this issue, we add a
1297 // [zero-width non-joiner character](https://en.wikipedia.org/wiki/Zero-width_non-joiner)
1298 // to empty text nodes (which can only be a result of Angular removing their initial content).
1299 // NOTE: Transcluded text content that starts with whitespace followed by an interpolation
1300 // will still fail to be detected by AngularJS v1.6+
1301 $template.forEach(node => {
1302 if (node.nodeType === Node.TEXT_NODE && !node.nodeValue) {
1303 node.nodeValue = '\u200C';
1304 }
1305 });
1306 }
1307 return attachChildrenFn;
1308 }
1309 resolveAndBindRequiredControllers(controllerInstance) {
1310 const directiveRequire = this.getDirectiveRequire();
1311 const requiredControllers = this.resolveRequire(directiveRequire);
1312 if (controllerInstance && this.directive.bindToController && isMap(directiveRequire)) {
1313 const requiredControllersMap = requiredControllers;
1314 Object.keys(requiredControllersMap).forEach(key => {
1315 controllerInstance[key] = requiredControllersMap[key];
1316 });
1317 }
1318 return requiredControllers;
1319 }
1320 compileHtml(html) {
1321 this.element.innerHTML = html;
1322 return this.$compile(this.element.childNodes);
1323 }
1324 extractChildNodes() {
1325 const childNodes = [];
1326 let childNode;
1327 while (childNode = this.element.firstChild) {
1328 this.element.removeChild(childNode);
1329 childNodes.push(childNode);
1330 }
1331 return childNodes;
1332 }
1333 getDirectiveRequire() {
1334 const require = this.directive.require || (this.directive.controller && this.directive.name);
1335 if (isMap(require)) {
1336 Object.keys(require).forEach(key => {
1337 const value = require[key];
1338 const match = value.match(REQUIRE_PREFIX_RE);
1339 const name = value.substring(match[0].length);
1340 if (!name) {
1341 require[key] = match[0] + key;
1342 }
1343 });
1344 }
1345 return require;
1346 }
1347 resolveRequire(require, controllerInstance) {
1348 if (!require) {
1349 return null;
1350 }
1351 else if (Array.isArray(require)) {
1352 return require.map(req => this.resolveRequire(req));
1353 }
1354 else if (typeof require === 'object') {
1355 const value = {};
1356 Object.keys(require).forEach(key => value[key] = this.resolveRequire(require[key]));
1357 return value;
1358 }
1359 else if (typeof require === 'string') {
1360 const match = require.match(REQUIRE_PREFIX_RE);
1361 const inheritType = match[1] || match[3];
1362 const name = require.substring(match[0].length);
1363 const isOptional = !!match[2];
1364 const searchParents = !!inheritType;
1365 const startOnParent = inheritType === '^^';
1366 const ctrlKey = controllerKey(name);
1367 const elem = startOnParent ? this.$element.parent() : this.$element;
1368 const value = searchParents ? elem.inheritedData(ctrlKey) : elem.data(ctrlKey);
1369 if (!value && !isOptional) {
1370 throw new Error(`Unable to find required '${require}' in upgraded directive '${this.name}'.`);
1371 }
1372 return value;
1373 }
1374 else {
1375 throw new Error(`Unrecognized 'require' syntax on upgraded directive '${this.name}': ${require}`);
1376 }
1377 }
1378}
1379function getOrCall(property, ...args) {
1380 return isFunction(property) ? property(...args) : property;
1381}
1382// NOTE: Only works for `typeof T !== 'object'`.
1383function isMap(value) {
1384 return value && !Array.isArray(value) && typeof value === 'object';
1385}
1386function notSupported(name, feature) {
1387 throw new Error(`Upgraded directive '${name}' contains unsupported feature: '${feature}'.`);
1388}
1389
1390/**
1391 * @license
1392 * Copyright Google LLC All Rights Reserved.
1393 *
1394 * Use of this source code is governed by an MIT-style license that can be
1395 * found in the LICENSE file at https://angular.io/license
1396 */
1397const NOT_SUPPORTED = 'NOT_SUPPORTED';
1398const INITIAL_VALUE = {
1399 __UNINITIALIZED__: true
1400};
1401class Bindings {
1402 constructor() {
1403 this.twoWayBoundProperties = [];
1404 this.twoWayBoundLastValues = [];
1405 this.expressionBoundProperties = [];
1406 this.propertyToOutputMap = {};
1407 }
1408}
1409/**
1410 * @description
1411 *
1412 * A helper class that allows an AngularJS component to be used from Angular.
1413 *
1414 * *Part of the [upgrade/static](api?query=upgrade%2Fstatic)
1415 * library for hybrid upgrade apps that support AOT compilation.*
1416 *
1417 * This helper class should be used as a base class for creating Angular directives
1418 * that wrap AngularJS components that need to be "upgraded".
1419 *
1420 * @usageNotes
1421 * ### Examples
1422 *
1423 * Let's assume that you have an AngularJS component called `ng1Hero` that needs
1424 * to be made available in Angular templates.
1425 *
1426 * {@example upgrade/static/ts/full/module.ts region="ng1-hero"}
1427 *
1428 * We must create a `Directive` that will make this AngularJS component
1429 * available inside Angular templates.
1430 *
1431 * {@example upgrade/static/ts/full/module.ts region="ng1-hero-wrapper"}
1432 *
1433 * In this example you can see that we must derive from the `UpgradeComponent`
1434 * base class but also provide an {@link Directive `@Directive`} decorator. This is
1435 * because the AOT compiler requires that this information is statically available at
1436 * compile time.
1437 *
1438 * Note that we must do the following:
1439 * * specify the directive's selector (`ng1-hero`)
1440 * * specify all inputs and outputs that the AngularJS component expects
1441 * * derive from `UpgradeComponent`
1442 * * call the base class from the constructor, passing
1443 * * the AngularJS name of the component (`ng1Hero`)
1444 * * the `ElementRef` and `Injector` for the component wrapper
1445 *
1446 * @publicApi
1447 * @extensible
1448 */
1449class UpgradeComponent {
1450 /**
1451 * Create a new `UpgradeComponent` instance. You should not normally need to do this.
1452 * Instead you should derive a new class from this one and call the super constructor
1453 * from the base class.
1454 *
1455 * {@example upgrade/static/ts/full/module.ts region="ng1-hero-wrapper" }
1456 *
1457 * * The `name` parameter should be the name of the AngularJS directive.
1458 * * The `elementRef` and `injector` parameters should be acquired from Angular by dependency
1459 * injection into the base class constructor.
1460 */
1461 constructor(name, elementRef, injector) {
1462 this.name = name;
1463 this.elementRef = elementRef;
1464 this.injector = injector;
1465 this.helper = new UpgradeHelper(injector, name, elementRef);
1466 this.$injector = this.helper.$injector;
1467 this.element = this.helper.element;
1468 this.$element = this.helper.$element;
1469 this.directive = this.helper.directive;
1470 this.bindings = this.initializeBindings(this.directive);
1471 // We ask for the AngularJS scope from the Angular injector, since
1472 // we will put the new component scope onto the new injector for each component
1473 const $parentScope = injector.get($SCOPE);
1474 // QUESTION 1: Should we create an isolated scope if the scope is only true?
1475 // QUESTION 2: Should we make the scope accessible through `$element.scope()/isolateScope()`?
1476 this.$componentScope = $parentScope.$new(!!this.directive.scope);
1477 this.initializeOutputs();
1478 }
1479 ngOnInit() {
1480 // Collect contents, insert and compile template
1481 const attachChildNodes = this.helper.prepareTransclusion();
1482 const linkFn = this.helper.compileTemplate();
1483 // Instantiate controller
1484 const controllerType = this.directive.controller;
1485 const bindToController = this.directive.bindToController;
1486 if (controllerType) {
1487 this.controllerInstance = this.helper.buildController(controllerType, this.$componentScope);
1488 }
1489 else if (bindToController) {
1490 throw new Error(`Upgraded directive '${this.directive.name}' specifies 'bindToController' but no controller.`);
1491 }
1492 // Set up outputs
1493 this.bindingDestination = bindToController ? this.controllerInstance : this.$componentScope;
1494 this.bindOutputs();
1495 // Require other controllers
1496 const requiredControllers = this.helper.resolveAndBindRequiredControllers(this.controllerInstance);
1497 // Hook: $onChanges
1498 if (this.pendingChanges) {
1499 this.forwardChanges(this.pendingChanges);
1500 this.pendingChanges = null;
1501 }
1502 // Hook: $onInit
1503 if (this.controllerInstance && isFunction(this.controllerInstance.$onInit)) {
1504 this.controllerInstance.$onInit();
1505 }
1506 // Hook: $doCheck
1507 if (this.controllerInstance && isFunction(this.controllerInstance.$doCheck)) {
1508 const callDoCheck = () => this.controllerInstance.$doCheck();
1509 this.unregisterDoCheckWatcher = this.$componentScope.$parent.$watch(callDoCheck);
1510 callDoCheck();
1511 }
1512 // Linking
1513 const link = this.directive.link;
1514 const preLink = typeof link == 'object' && link.pre;
1515 const postLink = typeof link == 'object' ? link.post : link;
1516 const attrs = NOT_SUPPORTED;
1517 const transcludeFn = NOT_SUPPORTED;
1518 if (preLink) {
1519 preLink(this.$componentScope, this.$element, attrs, requiredControllers, transcludeFn);
1520 }
1521 linkFn(this.$componentScope, null, { parentBoundTranscludeFn: attachChildNodes });
1522 if (postLink) {
1523 postLink(this.$componentScope, this.$element, attrs, requiredControllers, transcludeFn);
1524 }
1525 // Hook: $postLink
1526 if (this.controllerInstance && isFunction(this.controllerInstance.$postLink)) {
1527 this.controllerInstance.$postLink();
1528 }
1529 }
1530 ngOnChanges(changes) {
1531 if (!this.bindingDestination) {
1532 this.pendingChanges = changes;
1533 }
1534 else {
1535 this.forwardChanges(changes);
1536 }
1537 }
1538 ngDoCheck() {
1539 const twoWayBoundProperties = this.bindings.twoWayBoundProperties;
1540 const twoWayBoundLastValues = this.bindings.twoWayBoundLastValues;
1541 const propertyToOutputMap = this.bindings.propertyToOutputMap;
1542 twoWayBoundProperties.forEach((propName, idx) => {
1543 const newValue = this.bindingDestination[propName];
1544 const oldValue = twoWayBoundLastValues[idx];
1545 if (!Object.is(newValue, oldValue)) {
1546 const outputName = propertyToOutputMap[propName];
1547 const eventEmitter = this[outputName];
1548 eventEmitter.emit(newValue);
1549 twoWayBoundLastValues[idx] = newValue;
1550 }
1551 });
1552 }
1553 ngOnDestroy() {
1554 if (isFunction(this.unregisterDoCheckWatcher)) {
1555 this.unregisterDoCheckWatcher();
1556 }
1557 this.helper.onDestroy(this.$componentScope, this.controllerInstance);
1558 }
1559 initializeBindings(directive) {
1560 const btcIsObject = typeof directive.bindToController === 'object';
1561 if (btcIsObject && Object.keys(directive.scope).length) {
1562 throw new Error(`Binding definitions on scope and controller at the same time is not supported.`);
1563 }
1564 const context = btcIsObject ? directive.bindToController : directive.scope;
1565 const bindings = new Bindings();
1566 if (typeof context == 'object') {
1567 Object.keys(context).forEach(propName => {
1568 const definition = context[propName];
1569 const bindingType = definition.charAt(0);
1570 // QUESTION: What about `=*`? Ignore? Throw? Support?
1571 switch (bindingType) {
1572 case '@':
1573 case '<':
1574 // We don't need to do anything special. They will be defined as inputs on the
1575 // upgraded component facade and the change propagation will be handled by
1576 // `ngOnChanges()`.
1577 break;
1578 case '=':
1579 bindings.twoWayBoundProperties.push(propName);
1580 bindings.twoWayBoundLastValues.push(INITIAL_VALUE);
1581 bindings.propertyToOutputMap[propName] = propName + 'Change';
1582 break;
1583 case '&':
1584 bindings.expressionBoundProperties.push(propName);
1585 bindings.propertyToOutputMap[propName] = propName;
1586 break;
1587 default:
1588 let json = JSON.stringify(context);
1589 throw new Error(`Unexpected mapping '${bindingType}' in '${json}' in '${this.name}' directive.`);
1590 }
1591 });
1592 }
1593 return bindings;
1594 }
1595 initializeOutputs() {
1596 // Initialize the outputs for `=` and `&` bindings
1597 this.bindings.twoWayBoundProperties.concat(this.bindings.expressionBoundProperties)
1598 .forEach(propName => {
1599 const outputName = this.bindings.propertyToOutputMap[propName];
1600 this[outputName] = new EventEmitter();
1601 });
1602 }
1603 bindOutputs() {
1604 // Bind `&` bindings to the corresponding outputs
1605 this.bindings.expressionBoundProperties.forEach(propName => {
1606 const outputName = this.bindings.propertyToOutputMap[propName];
1607 const emitter = this[outputName];
1608 this.bindingDestination[propName] = (value) => emitter.emit(value);
1609 });
1610 }
1611 forwardChanges(changes) {
1612 // Forward input changes to `bindingDestination`
1613 Object.keys(changes).forEach(propName => this.bindingDestination[propName] = changes[propName].currentValue);
1614 if (isFunction(this.bindingDestination.$onChanges)) {
1615 this.bindingDestination.$onChanges(changes);
1616 }
1617 }
1618}
1619UpgradeComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeComponent, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
1620UpgradeComponent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "13.0.2", type: UpgradeComponent, usesOnChanges: true, ngImport: i0 });
1621i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeComponent, decorators: [{
1622 type: Directive
1623 }], ctorParameters: function () { return [{ type: undefined }, { type: i0.ElementRef }, { type: i0.Injector }]; } });
1624
1625/**
1626 * @license
1627 * Copyright Google LLC All Rights Reserved.
1628 *
1629 * Use of this source code is governed by an MIT-style license that can be
1630 * found in the LICENSE file at https://angular.io/license
1631 */
1632/**
1633 * @description
1634 *
1635 * An `NgModule`, which you import to provide AngularJS core services,
1636 * and has an instance method used to bootstrap the hybrid upgrade application.
1637 *
1638 * *Part of the [upgrade/static](api?query=upgrade/static)
1639 * library for hybrid upgrade apps that support AOT compilation*
1640 *
1641 * The `upgrade/static` package contains helpers that allow AngularJS and Angular components
1642 * to be used together inside a hybrid upgrade application, which supports AOT compilation.
1643 *
1644 * Specifically, the classes and functions in the `upgrade/static` module allow the following:
1645 *
1646 * 1. Creation of an Angular directive that wraps and exposes an AngularJS component so
1647 * that it can be used in an Angular template. See `UpgradeComponent`.
1648 * 2. Creation of an AngularJS directive that wraps and exposes an Angular component so
1649 * that it can be used in an AngularJS template. See `downgradeComponent`.
1650 * 3. Creation of an Angular root injector provider that wraps and exposes an AngularJS
1651 * service so that it can be injected into an Angular context. See
1652 * {@link UpgradeModule#upgrading-an-angular-1-service Upgrading an AngularJS service} below.
1653 * 4. Creation of an AngularJS service that wraps and exposes an Angular injectable
1654 * so that it can be injected into an AngularJS context. See `downgradeInjectable`.
1655 * 3. Bootstrapping of a hybrid Angular application which contains both of the frameworks
1656 * coexisting in a single application.
1657 *
1658 * @usageNotes
1659 *
1660 * ```ts
1661 * import {UpgradeModule} from '@angular/upgrade/static';
1662 * ```
1663 *
1664 * See also the {@link UpgradeModule#examples examples} below.
1665 *
1666 * ### Mental Model
1667 *
1668 * When reasoning about how a hybrid application works it is useful to have a mental model which
1669 * describes what is happening and explains what is happening at the lowest level.
1670 *
1671 * 1. There are two independent frameworks running in a single application, each framework treats
1672 * the other as a black box.
1673 * 2. Each DOM element on the page is owned exactly by one framework. Whichever framework
1674 * instantiated the element is the owner. Each framework only updates/interacts with its own
1675 * DOM elements and ignores others.
1676 * 3. AngularJS directives always execute inside the AngularJS framework codebase regardless of
1677 * where they are instantiated.
1678 * 4. Angular components always execute inside the Angular framework codebase regardless of
1679 * where they are instantiated.
1680 * 5. An AngularJS component can be "upgraded"" to an Angular component. This is achieved by
1681 * defining an Angular directive, which bootstraps the AngularJS component at its location
1682 * in the DOM. See `UpgradeComponent`.
1683 * 6. An Angular component can be "downgraded" to an AngularJS component. This is achieved by
1684 * defining an AngularJS directive, which bootstraps the Angular component at its location
1685 * in the DOM. See `downgradeComponent`.
1686 * 7. Whenever an "upgraded"/"downgraded" component is instantiated the host element is owned by
1687 * the framework doing the instantiation. The other framework then instantiates and owns the
1688 * view for that component.
1689 * 1. This implies that the component bindings will always follow the semantics of the
1690 * instantiation framework.
1691 * 2. The DOM attributes are parsed by the framework that owns the current template. So
1692 * attributes in AngularJS templates must use kebab-case, while AngularJS templates must use
1693 * camelCase.
1694 * 3. However the template binding syntax will always use the Angular style, e.g. square
1695 * brackets (`[...]`) for property binding.
1696 * 8. Angular is bootstrapped first; AngularJS is bootstrapped second. AngularJS always owns the
1697 * root component of the application.
1698 * 9. The new application is running in an Angular zone, and therefore it no longer needs calls to
1699 * `$apply()`.
1700 *
1701 * ### The `UpgradeModule` class
1702 *
1703 * This class is an `NgModule`, which you import to provide AngularJS core services,
1704 * and has an instance method used to bootstrap the hybrid upgrade application.
1705 *
1706 * * Core AngularJS services
1707 * Importing this `NgModule` will add providers for the core
1708 * [AngularJS services](https://docs.angularjs.org/api/ng/service) to the root injector.
1709 *
1710 * * Bootstrap
1711 * The runtime instance of this class contains a {@link UpgradeModule#bootstrap `bootstrap()`}
1712 * method, which you use to bootstrap the top level AngularJS module onto an element in the
1713 * DOM for the hybrid upgrade app.
1714 *
1715 * It also contains properties to access the {@link UpgradeModule#injector root injector}, the
1716 * bootstrap `NgZone` and the
1717 * [AngularJS $injector](https://docs.angularjs.org/api/auto/service/$injector).
1718 *
1719 * ### Examples
1720 *
1721 * Import the `UpgradeModule` into your top level {@link NgModule Angular `NgModule`}.
1722 *
1723 * {@example upgrade/static/ts/full/module.ts region='ng2-module'}
1724 *
1725 * Then inject `UpgradeModule` into your Angular `NgModule` and use it to bootstrap the top level
1726 * [AngularJS module](https://docs.angularjs.org/api/ng/type/angular.Module) in the
1727 * `ngDoBootstrap()` method.
1728 *
1729 * {@example upgrade/static/ts/full/module.ts region='bootstrap-ng1'}
1730 *
1731 * Finally, kick off the whole process, by bootstrapping your top level Angular `NgModule`.
1732 *
1733 * {@example upgrade/static/ts/full/module.ts region='bootstrap-ng2'}
1734 *
1735 * {@a upgrading-an-angular-1-service}
1736 * ### Upgrading an AngularJS service
1737 *
1738 * There is no specific API for upgrading an AngularJS service. Instead you should just follow the
1739 * following recipe:
1740 *
1741 * Let's say you have an AngularJS service:
1742 *
1743 * {@example upgrade/static/ts/full/module.ts region="ng1-text-formatter-service"}
1744 *
1745 * Then you should define an Angular provider to be included in your `NgModule` `providers`
1746 * property.
1747 *
1748 * {@example upgrade/static/ts/full/module.ts region="upgrade-ng1-service"}
1749 *
1750 * Then you can use the "upgraded" AngularJS service by injecting it into an Angular component
1751 * or service.
1752 *
1753 * {@example upgrade/static/ts/full/module.ts region="use-ng1-upgraded-service"}
1754 *
1755 * @publicApi
1756 */
1757class UpgradeModule {
1758 constructor(
1759 /** The root `Injector` for the upgrade application. */
1760 injector,
1761 /** The bootstrap zone for the upgrade application */
1762 ngZone,
1763 /**
1764 * The owning `NgModuleRef`s `PlatformRef` instance.
1765 * This is used to tie the lifecycle of the bootstrapped AngularJS apps to that of the Angular
1766 * `PlatformRef`.
1767 */
1768 platformRef) {
1769 this.ngZone = ngZone;
1770 this.platformRef = platformRef;
1771 this.injector = new NgAdapterInjector(injector);
1772 }
1773 /**
1774 * Bootstrap an AngularJS application from this NgModule
1775 * @param element the element on which to bootstrap the AngularJS application
1776 * @param [modules] the AngularJS modules to bootstrap for this application
1777 * @param [config] optional extra AngularJS bootstrap configuration
1778 */
1779 bootstrap(element$1, modules = [], config /*angular.IAngularBootstrapConfig*/) {
1780 const INIT_MODULE_NAME = UPGRADE_MODULE_NAME + '.init';
1781 // Create an ng1 module to bootstrap
1782 module_(INIT_MODULE_NAME, [])
1783 .constant(UPGRADE_APP_TYPE_KEY, 2 /* Static */)
1784 .value(INJECTOR_KEY, this.injector)
1785 .factory(LAZY_MODULE_REF, [INJECTOR_KEY, (injector) => ({ injector })])
1786 .config([
1787 $PROVIDE, $INJECTOR,
1788 ($provide, $injector) => {
1789 if ($injector.has($$TESTABILITY)) {
1790 $provide.decorator($$TESTABILITY, [
1791 $DELEGATE,
1792 (testabilityDelegate) => {
1793 const originalWhenStable = testabilityDelegate.whenStable;
1794 const injector = this.injector;
1795 // Cannot use arrow function below because we need the context
1796 const newWhenStable = function (callback) {
1797 originalWhenStable.call(testabilityDelegate, function () {
1798 const ng2Testability = injector.get(Testability);
1799 if (ng2Testability.isStable()) {
1800 callback();
1801 }
1802 else {
1803 ng2Testability.whenStable(newWhenStable.bind(testabilityDelegate, callback));
1804 }
1805 });
1806 };
1807 testabilityDelegate.whenStable = newWhenStable;
1808 return testabilityDelegate;
1809 }
1810 ]);
1811 }
1812 if ($injector.has($INTERVAL)) {
1813 $provide.decorator($INTERVAL, [
1814 $DELEGATE,
1815 (intervalDelegate) => {
1816 // Wrap the $interval service so that setInterval is called outside NgZone,
1817 // but the callback is still invoked within it. This is so that $interval
1818 // won't block stability, which preserves the behavior from AngularJS.
1819 let wrappedInterval = (fn, delay, count, invokeApply, ...pass) => {
1820 return this.ngZone.runOutsideAngular(() => {
1821 return intervalDelegate((...args) => {
1822 // Run callback in the next VM turn - $interval calls
1823 // $rootScope.$apply, and running the callback in NgZone will
1824 // cause a '$digest already in progress' error if it's in the
1825 // same vm turn.
1826 setTimeout(() => {
1827 this.ngZone.run(() => fn(...args));
1828 });
1829 }, delay, count, invokeApply, ...pass);
1830 });
1831 };
1832 Object.keys(intervalDelegate)
1833 .forEach(prop => wrappedInterval[prop] = intervalDelegate[prop]);
1834 // the `flush` method will be present when ngMocks is used
1835 if (intervalDelegate.hasOwnProperty('flush')) {
1836 wrappedInterval['flush'] = () => {
1837 intervalDelegate['flush']();
1838 return wrappedInterval;
1839 };
1840 }
1841 return wrappedInterval;
1842 }
1843 ]);
1844 }
1845 }
1846 ])
1847 .run([
1848 $INJECTOR,
1849 ($injector) => {
1850 this.$injector = $injector;
1851 const $rootScope = $injector.get('$rootScope');
1852 // Initialize the ng1 $injector provider
1853 setTempInjectorRef($injector);
1854 this.injector.get($INJECTOR);
1855 // Put the injector on the DOM, so that it can be "required"
1856 element(element$1).data(controllerKey(INJECTOR_KEY), this.injector);
1857 // Destroy the AngularJS app once the Angular `PlatformRef` is destroyed.
1858 // This does not happen in a typical SPA scenario, but it might be useful for
1859 // other use-cases where disposing of an Angular/AngularJS app is necessary
1860 // (such as Hot Module Replacement (HMR)).
1861 // See https://github.com/angular/angular/issues/39935.
1862 this.platformRef.onDestroy(() => destroyApp($injector));
1863 // Wire up the ng1 rootScope to run a digest cycle whenever the zone settles
1864 // We need to do this in the next tick so that we don't prevent the bootup stabilizing
1865 setTimeout(() => {
1866 const subscription = this.ngZone.onMicrotaskEmpty.subscribe(() => {
1867 if ($rootScope.$$phase) {
1868 if (isDevMode()) {
1869 console.warn('A digest was triggered while one was already in progress. This may mean that something is triggering digests outside the Angular zone.');
1870 }
1871 return $rootScope.$evalAsync();
1872 }
1873 return $rootScope.$digest();
1874 });
1875 $rootScope.$on('$destroy', () => {
1876 subscription.unsubscribe();
1877 });
1878 }, 0);
1879 }
1880 ]);
1881 const upgradeModule = module_(UPGRADE_MODULE_NAME, [INIT_MODULE_NAME].concat(modules));
1882 // Make sure resumeBootstrap() only exists if the current bootstrap is deferred
1883 const windowAngular = window['angular'];
1884 windowAngular.resumeBootstrap = undefined;
1885 // Bootstrap the AngularJS application inside our zone
1886 this.ngZone.run(() => {
1887 bootstrap(element$1, [upgradeModule.name], config);
1888 });
1889 // Patch resumeBootstrap() to run inside the ngZone
1890 if (windowAngular.resumeBootstrap) {
1891 const originalResumeBootstrap = windowAngular.resumeBootstrap;
1892 const ngZone = this.ngZone;
1893 windowAngular.resumeBootstrap = function () {
1894 let args = arguments;
1895 windowAngular.resumeBootstrap = originalResumeBootstrap;
1896 return ngZone.run(() => windowAngular.resumeBootstrap.apply(this, args));
1897 };
1898 }
1899 }
1900}
1901UpgradeModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeModule, deps: [{ token: i0.Injector }, { token: i0.NgZone }, { token: i0.PlatformRef }], target: i0.ɵɵFactoryTarget.NgModule });
1902UpgradeModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeModule });
1903UpgradeModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeModule, providers: [angular1Providers] });
1904i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.2", ngImport: i0, type: UpgradeModule, decorators: [{
1905 type: NgModule,
1906 args: [{ providers: [angular1Providers] }]
1907 }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.NgZone }, { type: i0.PlatformRef }]; } });
1908
1909/**
1910 * @license
1911 * Copyright Google LLC All Rights Reserved.
1912 *
1913 * Use of this source code is governed by an MIT-style license that can be
1914 * found in the LICENSE file at https://angular.io/license
1915 */
1916// This file only re-exports items to appear in the public api. Keep it that way.
1917
1918/**
1919 * @license
1920 * Copyright Google LLC All Rights Reserved.
1921 *
1922 * Use of this source code is governed by an MIT-style license that can be
1923 * found in the LICENSE file at https://angular.io/license
1924 */
1925
1926/**
1927 * Generated bundle index. Do not edit.
1928 */
1929
1930export { UpgradeComponent, UpgradeModule, VERSION, downgradeComponent, downgradeInjectable, downgradeModule, getAngularJSGlobal, getAngularLib, setAngularJSGlobal, setAngularLib };
1931//# sourceMappingURL=static.mjs.map