/** * @license Angular v14.2.8 * (c) 2010-2022 Google LLC. https://angular.io/ * License: MIT */ import { ComponentFactoryResolver, NgZone, Injector, ChangeDetectorRef, ApplicationRef, SimpleChange, Version } from '@angular/core'; import { ReplaySubject, merge } from 'rxjs'; import { switchMap, map } from 'rxjs/operators'; /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Provide methods for scheduling the execution of a callback. */ const scheduler = { /** * Schedule a callback to be called after some delay. * * Returns a function that when executed will cancel the scheduled function. */ schedule(taskFn, delay) { const id = setTimeout(taskFn, delay); return () => clearTimeout(id); }, /** * Schedule a callback to be called before the next render. * (If `window.requestAnimationFrame()` is not available, use `scheduler.schedule()` instead.) * * Returns a function that when executed will cancel the scheduled function. */ scheduleBeforeRender(taskFn) { // TODO(gkalpak): Implement a better way of accessing `requestAnimationFrame()` // (e.g. accounting for vendor prefix, SSR-compatibility, etc). if (typeof window === 'undefined') { // For SSR just schedule immediately. return scheduler.schedule(taskFn, 0); } if (typeof window.requestAnimationFrame === 'undefined') { const frameMs = 16; return scheduler.schedule(taskFn, frameMs); } const id = window.requestAnimationFrame(taskFn); return () => window.cancelAnimationFrame(id); }, }; /** * Convert a camelCased string to kebab-cased. */ function camelToDashCase(input) { return input.replace(/[A-Z]/g, char => `-${char.toLowerCase()}`); } /** * Check whether the input is an `Element`. */ function isElement(node) { return !!node && node.nodeType === Node.ELEMENT_NODE; } /** * Check whether the input is a function. */ function isFunction(value) { return typeof value === 'function'; } /** * Convert a kebab-cased string to camelCased. */ function kebabToCamelCase(input) { return input.replace(/-([a-z\d])/g, (_, char) => char.toUpperCase()); } let _matches; /** * Check whether an `Element` matches a CSS selector. * NOTE: this is duplicated from @angular/upgrade, and can * be consolidated in the future */ function matchesSelector(el, selector) { if (!_matches) { const elProto = Element.prototype; _matches = elProto.matches || elProto.matchesSelector || elProto.mozMatchesSelector || elProto.msMatchesSelector || elProto.oMatchesSelector || elProto.webkitMatchesSelector; } return el.nodeType === Node.ELEMENT_NODE ? _matches.call(el, selector) : false; } /** * Test two values for strict equality, accounting for the fact that `NaN !== NaN`. */ function strictEquals(value1, value2) { return value1 === value2 || (value1 !== value1 && value2 !== value2); } /** Gets a map of default set of attributes to observe and the properties they affect. */ function getDefaultAttributeToPropertyInputs(inputs) { const attributeToPropertyInputs = {}; inputs.forEach(({ propName, templateName }) => { attributeToPropertyInputs[camelToDashCase(templateName)] = propName; }); return attributeToPropertyInputs; } /** * Gets a component's set of inputs. Uses the injector to get the component factory where the inputs * are defined. */ function getComponentInputs(component, injector) { const componentFactoryResolver = injector.get(ComponentFactoryResolver); const componentFactory = componentFactoryResolver.resolveComponentFactory(component); return componentFactory.inputs; } /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ function extractProjectableNodes(host, ngContentSelectors) { const nodes = host.childNodes; const projectableNodes = ngContentSelectors.map(() => []); let wildcardIndex = -1; ngContentSelectors.some((selector, i) => { if (selector === '*') { wildcardIndex = i; return true; } return false; }); for (let i = 0, ii = nodes.length; i < ii; ++i) { const node = nodes[i]; const ngContentIndex = findMatchingIndex(node, ngContentSelectors, wildcardIndex); if (ngContentIndex !== -1) { projectableNodes[ngContentIndex].push(node); } } return projectableNodes; } function findMatchingIndex(node, selectors, defaultIndex) { let matchingIndex = defaultIndex; if (isElement(node)) { selectors.some((selector, i) => { if ((selector !== '*') && matchesSelector(node, selector)) { matchingIndex = i; return true; } return false; }); } return matchingIndex; } /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** Time in milliseconds to wait before destroying the component ref when disconnected. */ const DESTROY_DELAY = 10; /** * Factory that creates new ComponentNgElementStrategy instance. Gets the component factory with the * constructor's injector's factory resolver and passes that factory to each strategy. * * @publicApi */ class ComponentNgElementStrategyFactory { constructor(component, injector) { this.componentFactory = injector.get(ComponentFactoryResolver).resolveComponentFactory(component); } create(injector) { return new ComponentNgElementStrategy(this.componentFactory, injector); } } /** * Creates and destroys a component ref using a component factory and handles change detection * in response to input changes. * * @publicApi */ class ComponentNgElementStrategy { constructor(componentFactory, injector) { this.componentFactory = componentFactory; this.injector = injector; // Subject of `NgElementStrategyEvent` observables corresponding to the component's outputs. this.eventEmitters = new ReplaySubject(1); /** Merged stream of the component's output events. */ this.events = this.eventEmitters.pipe(switchMap(emitters => merge(...emitters))); /** Reference to the component that was created on connect. */ this.componentRef = null; /** Reference to the component view's `ChangeDetectorRef`. */ this.viewChangeDetectorRef = null; /** * Changes that have been made to component inputs since the last change detection run. * (NOTE: These are only recorded if the component implements the `OnChanges` interface.) */ this.inputChanges = null; /** Whether changes have been made to component inputs since the last change detection run. */ this.hasInputChanges = false; /** Whether the created component implements the `OnChanges` interface. */ this.implementsOnChanges = false; /** Whether a change detection has been scheduled to run on the component. */ this.scheduledChangeDetectionFn = null; /** Callback function that when called will cancel a scheduled destruction on the component. */ this.scheduledDestroyFn = null; /** Initial input values that were set before the component was created. */ this.initialInputValues = new Map(); /** * Set of component inputs that have not yet changed, i.e. for which `recordInputChange()` has not * fired. * (This helps detect the first change of an input, even if it is explicitly set to `undefined`.) */ this.unchangedInputs = new Set(this.componentFactory.inputs.map(({ propName }) => propName)); /** Service for setting zone context. */ this.ngZone = this.injector.get(NgZone); /** The zone the element was created in or `null` if Zone.js is not loaded. */ this.elementZone = (typeof Zone === 'undefined') ? null : this.ngZone.run(() => Zone.current); } /** * Initializes a new component if one has not yet been created and cancels any scheduled * destruction. */ connect(element) { this.runInZone(() => { // If the element is marked to be destroyed, cancel the task since the component was // reconnected if (this.scheduledDestroyFn !== null) { this.scheduledDestroyFn(); this.scheduledDestroyFn = null; return; } if (this.componentRef === null) { this.initializeComponent(element); } }); } /** * Schedules the component to be destroyed after some small delay in case the element is just * being moved across the DOM. */ disconnect() { this.runInZone(() => { // Return if there is no componentRef or the component is already scheduled for destruction if (this.componentRef === null || this.scheduledDestroyFn !== null) { return; } // Schedule the component to be destroyed after a small timeout in case it is being // moved elsewhere in the DOM this.scheduledDestroyFn = scheduler.schedule(() => { if (this.componentRef !== null) { this.componentRef.destroy(); this.componentRef = null; this.viewChangeDetectorRef = null; } }, DESTROY_DELAY); }); } /** * Returns the component property value. If the component has not yet been created, the value is * retrieved from the cached initialization values. */ getInputValue(property) { return this.runInZone(() => { if (this.componentRef === null) { return this.initialInputValues.get(property); } return this.componentRef.instance[property]; }); } /** * Sets the input value for the property. If the component has not yet been created, the value is * cached and set when the component is created. */ setInputValue(property, value) { this.runInZone(() => { if (this.componentRef === null) { this.initialInputValues.set(property, value); return; } // Ignore the value if it is strictly equal to the current value, except if it is `undefined` // and this is the first change to the value (because an explicit `undefined` _is_ strictly // equal to not having a value set at all, but we still need to record this as a change). if (strictEquals(value, this.getInputValue(property)) && !((value === undefined) && this.unchangedInputs.has(property))) { return; } // Record the changed value and update internal state to reflect the fact that this input has // changed. this.recordInputChange(property, value); this.unchangedInputs.delete(property); this.hasInputChanges = true; // Update the component instance and schedule change detection. this.componentRef.instance[property] = value; this.scheduleDetectChanges(); }); } /** * Creates a new component through the component factory with the provided element host and * sets up its initial inputs, listens for outputs changes, and runs an initial change detection. */ initializeComponent(element) { const childInjector = Injector.create({ providers: [], parent: this.injector }); const projectableNodes = extractProjectableNodes(element, this.componentFactory.ngContentSelectors); this.componentRef = this.componentFactory.create(childInjector, projectableNodes, element); this.viewChangeDetectorRef = this.componentRef.injector.get(ChangeDetectorRef); this.implementsOnChanges = isFunction(this.componentRef.instance.ngOnChanges); this.initializeInputs(); this.initializeOutputs(this.componentRef); this.detectChanges(); const applicationRef = this.injector.get(ApplicationRef); applicationRef.attachView(this.componentRef.hostView); } /** Set any stored initial inputs on the component's properties. */ initializeInputs() { this.componentFactory.inputs.forEach(({ propName }) => { if (this.initialInputValues.has(propName)) { // Call `setInputValue()` now that the component has been instantiated to update its // properties and fire `ngOnChanges()`. this.setInputValue(propName, this.initialInputValues.get(propName)); } }); this.initialInputValues.clear(); } /** Sets up listeners for the component's outputs so that the events stream emits the events. */ initializeOutputs(componentRef) { const eventEmitters = this.componentFactory.outputs.map(({ propName, templateName }) => { const emitter = componentRef.instance[propName]; return emitter.pipe(map(value => ({ name: templateName, value }))); }); this.eventEmitters.next(eventEmitters); } /** Calls ngOnChanges with all the inputs that have changed since the last call. */ callNgOnChanges(componentRef) { if (!this.implementsOnChanges || this.inputChanges === null) { return; } // Cache the changes and set inputChanges to null to capture any changes that might occur // during ngOnChanges. const inputChanges = this.inputChanges; this.inputChanges = null; componentRef.instance.ngOnChanges(inputChanges); } /** * Marks the component view for check, if necessary. * (NOTE: This is required when the `ChangeDetectionStrategy` is set to `OnPush`.) */ markViewForCheck(viewChangeDetectorRef) { if (this.hasInputChanges) { this.hasInputChanges = false; viewChangeDetectorRef.markForCheck(); } } /** * Schedules change detection to run on the component. * Ignores subsequent calls if already scheduled. */ scheduleDetectChanges() { if (this.scheduledChangeDetectionFn) { return; } this.scheduledChangeDetectionFn = scheduler.scheduleBeforeRender(() => { this.scheduledChangeDetectionFn = null; this.detectChanges(); }); } /** * Records input changes so that the component receives SimpleChanges in its onChanges function. */ recordInputChange(property, currentValue) { // Do not record the change if the component does not implement `OnChanges`. if (!this.implementsOnChanges) { return; } if (this.inputChanges === null) { this.inputChanges = {}; } // If there already is a change, modify the current value to match but leave the values for // `previousValue` and `isFirstChange`. const pendingChange = this.inputChanges[property]; if (pendingChange) { pendingChange.currentValue = currentValue; return; } const isFirstChange = this.unchangedInputs.has(property); const previousValue = isFirstChange ? undefined : this.getInputValue(property); this.inputChanges[property] = new SimpleChange(previousValue, currentValue, isFirstChange); } /** Runs change detection on the component. */ detectChanges() { if (this.componentRef === null) { return; } this.callNgOnChanges(this.componentRef); this.markViewForCheck(this.viewChangeDetectorRef); this.componentRef.changeDetectorRef.detectChanges(); } /** Runs in the angular zone, if present. */ runInZone(fn) { return (this.elementZone && Zone.current !== this.elementZone) ? this.ngZone.run(fn) : fn(); } } /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Implements the functionality needed for a custom element. * * @publicApi */ class NgElement extends HTMLElement { constructor() { super(...arguments); /** * A subscription to change, connect, and disconnect events in the custom element. */ this.ngElementEventsSubscription = null; } } /** * @description Creates a custom element class based on an Angular component. * * Builds a class that encapsulates the functionality of the provided component and * uses the configuration information to provide more context to the class. * Takes the component factory's inputs and outputs to convert them to the proper * custom element API and add hooks to input changes. * * The configuration's injector is the initial injector set on the class, * and used by default for each created instance.This behavior can be overridden with the * static property to affect all newly created instances, or as a constructor argument for * one-off creations. * * @see [Angular Elements Overview](guide/elements "Turning Angular components into custom elements") * * @param component The component to transform. * @param config A configuration that provides initialization information to the created class. * @returns The custom-element construction class, which can be registered with * a browser's `CustomElementRegistry`. * * @publicApi */ function createCustomElement(component, config) { const inputs = getComponentInputs(component, config.injector); const strategyFactory = config.strategyFactory || new ComponentNgElementStrategyFactory(component, config.injector); const attributeToPropertyInputs = getDefaultAttributeToPropertyInputs(inputs); class NgElementImpl extends NgElement { constructor(injector) { super(); this.injector = injector; } get ngElementStrategy() { // NOTE: // Some polyfills (e.g. `document-register-element`) do not call the constructor, therefore // it is not safe to set `ngElementStrategy` in the constructor and assume it will be // available inside the methods. // // TODO(andrewseguin): Add e2e tests that cover cases where the constructor isn't called. For // now this is tested using a Google internal test suite. if (!this._ngElementStrategy) { const strategy = this._ngElementStrategy = strategyFactory.create(this.injector || config.injector); // Re-apply pre-existing input values (set as properties on the element) through the // strategy. inputs.forEach(({ propName }) => { if (!this.hasOwnProperty(propName)) { // No pre-existing value for `propName`. return; } // Delete the property from the instance and re-apply it through the strategy. const value = this[propName]; delete this[propName]; strategy.setInputValue(propName, value); }); } return this._ngElementStrategy; } attributeChangedCallback(attrName, oldValue, newValue, namespace) { const propName = attributeToPropertyInputs[attrName]; this.ngElementStrategy.setInputValue(propName, newValue); } connectedCallback() { // For historical reasons, some strategies may not have initialized the `events` property // until after `connect()` is run. Subscribe to `events` if it is available before running // `connect()` (in order to capture events emitted during initialization), otherwise subscribe // afterwards. // // TODO: Consider deprecating/removing the post-connect subscription in a future major version // (e.g. v11). let subscribedToEvents = false; if (this.ngElementStrategy.events) { // `events` are already available: Subscribe to it asap. this.subscribeToEvents(); subscribedToEvents = true; } this.ngElementStrategy.connect(this); if (!subscribedToEvents) { // `events` were not initialized before running `connect()`: Subscribe to them now. // The events emitted during the component initialization have been missed, but at least // future events will be captured. this.subscribeToEvents(); } } disconnectedCallback() { // Not using `this.ngElementStrategy` to avoid unnecessarily creating the `NgElementStrategy`. if (this._ngElementStrategy) { this._ngElementStrategy.disconnect(); } if (this.ngElementEventsSubscription) { this.ngElementEventsSubscription.unsubscribe(); this.ngElementEventsSubscription = null; } } subscribeToEvents() { // Listen for events from the strategy and dispatch them as custom events. this.ngElementEventsSubscription = this.ngElementStrategy.events.subscribe(e => { const customEvent = new CustomEvent(e.name, { detail: e.value }); this.dispatchEvent(customEvent); }); } } // Work around a bug in closure typed optimizations(b/79557487) where it is not honoring static // field externs. So using quoted access to explicitly prevent renaming. NgElementImpl['observedAttributes'] = Object.keys(attributeToPropertyInputs); // Add getters and setters to the prototype for each property input. inputs.forEach(({ propName }) => { Object.defineProperty(NgElementImpl.prototype, propName, { get() { return this.ngElementStrategy.getInputValue(propName); }, set(newValue) { this.ngElementStrategy.setInputValue(propName, newValue); }, configurable: true, enumerable: true, }); }); return NgElementImpl; } /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * @publicApi */ const VERSION = new Version('14.2.8'); /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ // This file only reexports content of the `src` folder. Keep it that way. /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * Generated bundle index. Do not edit. */ export { NgElement, VERSION, createCustomElement }; //# sourceMappingURL=elements.mjs.map