UNPKG

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