UNPKG

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