dom_renderer-DGKzginR.mjs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  1. /**
  2. * @license Angular v19.2.13
  3. * (c) 2010-2025 Google LLC. https://angular.io/
  4. * License: MIT
  5. */
  6. import { isPlatformServer, DOCUMENT, ɵgetDOM as _getDOM } from '@angular/common';
  7. import * as i0 from '@angular/core';
  8. import { InjectionToken, ɵRuntimeError as _RuntimeError, Inject, Injectable, APP_ID, CSP_NONCE, PLATFORM_ID, Optional, ViewEncapsulation, ɵTracingService as _TracingService, RendererStyleFlags2 } from '@angular/core';
  9. /**
  10. * The injection token for plugins of the `EventManager` service.
  11. *
  12. * @publicApi
  13. */
  14. const EVENT_MANAGER_PLUGINS = new InjectionToken(ngDevMode ? 'EventManagerPlugins' : '');
  15. /**
  16. * An injectable service that provides event management for Angular
  17. * through a browser plug-in.
  18. *
  19. * @publicApi
  20. */
  21. class EventManager {
  22. _zone;
  23. _plugins;
  24. _eventNameToPlugin = new Map();
  25. /**
  26. * Initializes an instance of the event-manager service.
  27. */
  28. constructor(plugins, _zone) {
  29. this._zone = _zone;
  30. plugins.forEach((plugin) => {
  31. plugin.manager = this;
  32. });
  33. this._plugins = plugins.slice().reverse();
  34. }
  35. /**
  36. * Registers a handler for a specific element and event.
  37. *
  38. * @param element The HTML element to receive event notifications.
  39. * @param eventName The name of the event to listen for.
  40. * @param handler A function to call when the notification occurs. Receives the
  41. * event object as an argument.
  42. * @param options Options that configure how the event listener is bound.
  43. * @returns A callback function that can be used to remove the handler.
  44. */
  45. addEventListener(element, eventName, handler, options) {
  46. const plugin = this._findPluginFor(eventName);
  47. return plugin.addEventListener(element, eventName, handler, options);
  48. }
  49. /**
  50. * Retrieves the compilation zone in which event listeners are registered.
  51. */
  52. getZone() {
  53. return this._zone;
  54. }
  55. /** @internal */
  56. _findPluginFor(eventName) {
  57. let plugin = this._eventNameToPlugin.get(eventName);
  58. if (plugin) {
  59. return plugin;
  60. }
  61. const plugins = this._plugins;
  62. plugin = plugins.find((plugin) => plugin.supports(eventName));
  63. if (!plugin) {
  64. throw new _RuntimeError(5101 /* RuntimeErrorCode.NO_PLUGIN_FOR_EVENT */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
  65. `No event manager plugin found for event ${eventName}`);
  66. }
  67. this._eventNameToPlugin.set(eventName, plugin);
  68. return plugin;
  69. }
  70. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: EventManager, deps: [{ token: EVENT_MANAGER_PLUGINS }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
  71. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: EventManager });
  72. }
  73. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: EventManager, decorators: [{
  74. type: Injectable
  75. }], ctorParameters: () => [{ type: undefined, decorators: [{
  76. type: Inject,
  77. args: [EVENT_MANAGER_PLUGINS]
  78. }] }, { type: i0.NgZone }] });
  79. /**
  80. * The plugin definition for the `EventManager` class
  81. *
  82. * It can be used as a base class to create custom manager plugins, i.e. you can create your own
  83. * class that extends the `EventManagerPlugin` one.
  84. *
  85. * @publicApi
  86. */
  87. class EventManagerPlugin {
  88. _doc;
  89. // TODO: remove (has some usage in G3)
  90. constructor(_doc) {
  91. this._doc = _doc;
  92. }
  93. // Using non-null assertion because it's set by EventManager's constructor
  94. manager;
  95. }
  96. /** The style elements attribute name used to set value of `APP_ID` token. */
  97. const APP_ID_ATTRIBUTE_NAME = 'ng-app-id';
  98. /**
  99. * Removes all provided elements from the document.
  100. * @param elements An array of HTML Elements.
  101. */
  102. function removeElements(elements) {
  103. for (const element of elements) {
  104. element.remove();
  105. }
  106. }
  107. /**
  108. * Creates a `style` element with the provided inline style content.
  109. * @param style A string of the inline style content.
  110. * @param doc A DOM Document to use to create the element.
  111. * @returns An HTMLStyleElement instance.
  112. */
  113. function createStyleElement(style, doc) {
  114. const styleElement = doc.createElement('style');
  115. styleElement.textContent = style;
  116. return styleElement;
  117. }
  118. /**
  119. * Searches a DOM document's head element for style elements with a matching application
  120. * identifier attribute (`ng-app-id`) to the provide identifier and adds usage records for each.
  121. * @param doc An HTML DOM document instance.
  122. * @param appId A string containing an Angular application identifer.
  123. * @param inline A Map object for tracking inline (defined via `styles` in component decorator) style usage.
  124. * @param external A Map object for tracking external (defined via `styleUrls` in component decorator) style usage.
  125. */
  126. function addServerStyles(doc, appId, inline, external) {
  127. const elements = doc.head?.querySelectorAll(`style[${APP_ID_ATTRIBUTE_NAME}="${appId}"],link[${APP_ID_ATTRIBUTE_NAME}="${appId}"]`);
  128. if (elements) {
  129. for (const styleElement of elements) {
  130. styleElement.removeAttribute(APP_ID_ATTRIBUTE_NAME);
  131. if (styleElement instanceof HTMLLinkElement) {
  132. // Only use filename from href
  133. // The href is build time generated with a unique value to prevent duplicates.
  134. external.set(styleElement.href.slice(styleElement.href.lastIndexOf('/') + 1), {
  135. usage: 0,
  136. elements: [styleElement],
  137. });
  138. }
  139. else if (styleElement.textContent) {
  140. inline.set(styleElement.textContent, { usage: 0, elements: [styleElement] });
  141. }
  142. }
  143. }
  144. }
  145. /**
  146. * Creates a `link` element for the provided external style URL.
  147. * @param url A string of the URL for the stylesheet.
  148. * @param doc A DOM Document to use to create the element.
  149. * @returns An HTMLLinkElement instance.
  150. */
  151. function createLinkElement(url, doc) {
  152. const linkElement = doc.createElement('link');
  153. linkElement.setAttribute('rel', 'stylesheet');
  154. linkElement.setAttribute('href', url);
  155. return linkElement;
  156. }
  157. class SharedStylesHost {
  158. doc;
  159. appId;
  160. nonce;
  161. /**
  162. * Provides usage information for active inline style content and associated HTML <style> elements.
  163. * Embedded styles typically originate from the `styles` metadata of a rendered component.
  164. */
  165. inline = new Map();
  166. /**
  167. * Provides usage information for active external style URLs and the associated HTML <link> elements.
  168. * External styles typically originate from the `ɵɵExternalStylesFeature` of a rendered component.
  169. */
  170. external = new Map();
  171. /**
  172. * Set of host DOM nodes that will have styles attached.
  173. */
  174. hosts = new Set();
  175. /**
  176. * Whether the application code is currently executing on a server.
  177. */
  178. isServer;
  179. constructor(doc, appId, nonce, platformId = {}) {
  180. this.doc = doc;
  181. this.appId = appId;
  182. this.nonce = nonce;
  183. this.isServer = isPlatformServer(platformId);
  184. addServerStyles(doc, appId, this.inline, this.external);
  185. this.hosts.add(doc.head);
  186. }
  187. /**
  188. * Adds embedded styles to the DOM via HTML `style` elements.
  189. * @param styles An array of style content strings.
  190. */
  191. addStyles(styles, urls) {
  192. for (const value of styles) {
  193. this.addUsage(value, this.inline, createStyleElement);
  194. }
  195. urls?.forEach((value) => this.addUsage(value, this.external, createLinkElement));
  196. }
  197. /**
  198. * Removes embedded styles from the DOM that were added as HTML `style` elements.
  199. * @param styles An array of style content strings.
  200. */
  201. removeStyles(styles, urls) {
  202. for (const value of styles) {
  203. this.removeUsage(value, this.inline);
  204. }
  205. urls?.forEach((value) => this.removeUsage(value, this.external));
  206. }
  207. addUsage(value, usages, creator) {
  208. // Attempt to get any current usage of the value
  209. const record = usages.get(value);
  210. // If existing, just increment the usage count
  211. if (record) {
  212. if ((typeof ngDevMode === 'undefined' || ngDevMode) && record.usage === 0) {
  213. // A usage count of zero indicates a preexisting server generated style.
  214. // This attribute is solely used for debugging purposes of SSR style reuse.
  215. record.elements.forEach((element) => element.setAttribute('ng-style-reused', ''));
  216. }
  217. record.usage++;
  218. }
  219. else {
  220. // Otherwise, create an entry to track the elements and add element for each host
  221. usages.set(value, {
  222. usage: 1,
  223. elements: [...this.hosts].map((host) => this.addElement(host, creator(value, this.doc))),
  224. });
  225. }
  226. }
  227. removeUsage(value, usages) {
  228. // Attempt to get any current usage of the value
  229. const record = usages.get(value);
  230. // If there is a record, reduce the usage count and if no longer used,
  231. // remove from DOM and delete usage record.
  232. if (record) {
  233. record.usage--;
  234. if (record.usage <= 0) {
  235. removeElements(record.elements);
  236. usages.delete(value);
  237. }
  238. }
  239. }
  240. ngOnDestroy() {
  241. for (const [, { elements }] of [...this.inline, ...this.external]) {
  242. removeElements(elements);
  243. }
  244. this.hosts.clear();
  245. }
  246. /**
  247. * Adds a host node to the set of style hosts and adds all existing style usage to
  248. * the newly added host node.
  249. *
  250. * This is currently only used for Shadow DOM encapsulation mode.
  251. */
  252. addHost(hostNode) {
  253. this.hosts.add(hostNode);
  254. // Add existing styles to new host
  255. for (const [style, { elements }] of this.inline) {
  256. elements.push(this.addElement(hostNode, createStyleElement(style, this.doc)));
  257. }
  258. for (const [url, { elements }] of this.external) {
  259. elements.push(this.addElement(hostNode, createLinkElement(url, this.doc)));
  260. }
  261. }
  262. removeHost(hostNode) {
  263. this.hosts.delete(hostNode);
  264. }
  265. addElement(host, element) {
  266. // Add a nonce if present
  267. if (this.nonce) {
  268. element.setAttribute('nonce', this.nonce);
  269. }
  270. // Add application identifier when on the server to support client-side reuse
  271. if (this.isServer) {
  272. element.setAttribute(APP_ID_ATTRIBUTE_NAME, this.appId);
  273. }
  274. // Insert the element into the DOM with the host node as parent
  275. return host.appendChild(element);
  276. }
  277. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SharedStylesHost, deps: [{ token: DOCUMENT }, { token: APP_ID }, { token: CSP_NONCE, optional: true }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable });
  278. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SharedStylesHost });
  279. }
  280. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SharedStylesHost, decorators: [{
  281. type: Injectable
  282. }], ctorParameters: () => [{ type: Document, decorators: [{
  283. type: Inject,
  284. args: [DOCUMENT]
  285. }] }, { type: undefined, decorators: [{
  286. type: Inject,
  287. args: [APP_ID]
  288. }] }, { type: undefined, decorators: [{
  289. type: Inject,
  290. args: [CSP_NONCE]
  291. }, {
  292. type: Optional
  293. }] }, { type: undefined, decorators: [{
  294. type: Inject,
  295. args: [PLATFORM_ID]
  296. }] }] });
  297. const NAMESPACE_URIS = {
  298. 'svg': 'http://www.w3.org/2000/svg',
  299. 'xhtml': 'http://www.w3.org/1999/xhtml',
  300. 'xlink': 'http://www.w3.org/1999/xlink',
  301. 'xml': 'http://www.w3.org/XML/1998/namespace',
  302. 'xmlns': 'http://www.w3.org/2000/xmlns/',
  303. 'math': 'http://www.w3.org/1998/Math/MathML',
  304. };
  305. const COMPONENT_REGEX = /%COMP%/g;
  306. const SOURCEMAP_URL_REGEXP = /\/\*#\s*sourceMappingURL=(.+?)\s*\*\//;
  307. const PROTOCOL_REGEXP = /^https?:/;
  308. const COMPONENT_VARIABLE = '%COMP%';
  309. const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
  310. const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
  311. /**
  312. * The default value for the `REMOVE_STYLES_ON_COMPONENT_DESTROY` DI token.
  313. */
  314. const REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT = true;
  315. /**
  316. * A DI token that indicates whether styles
  317. * of destroyed components should be removed from DOM.
  318. *
  319. * By default, the value is set to `true`.
  320. * @publicApi
  321. */
  322. const REMOVE_STYLES_ON_COMPONENT_DESTROY = new InjectionToken(ngDevMode ? 'RemoveStylesOnCompDestroy' : '', {
  323. providedIn: 'root',
  324. factory: () => REMOVE_STYLES_ON_COMPONENT_DESTROY_DEFAULT,
  325. });
  326. function shimContentAttribute(componentShortId) {
  327. return CONTENT_ATTR.replace(COMPONENT_REGEX, componentShortId);
  328. }
  329. function shimHostAttribute(componentShortId) {
  330. return HOST_ATTR.replace(COMPONENT_REGEX, componentShortId);
  331. }
  332. function shimStylesContent(compId, styles) {
  333. return styles.map((s) => s.replace(COMPONENT_REGEX, compId));
  334. }
  335. /**
  336. * Prepends a baseHref to the `sourceMappingURL` within the provided CSS content.
  337. * If the `sourceMappingURL` contains an inline (encoded) map, the function skips processing.
  338. *
  339. * @note For inline stylesheets, the `sourceMappingURL` is relative to the page's origin
  340. * and not the provided baseHref. This function is needed as when accessing the page with a URL
  341. * containing two or more segments.
  342. * For example, if the baseHref is set to `/`, and you visit a URL like `http://localhost/foo/bar`,
  343. * the map would be requested from `http://localhost/foo/bar/comp.css.map` instead of what you'd expect,
  344. * which is `http://localhost/comp.css.map`. This behavior is corrected by modifying the `sourceMappingURL`
  345. * to ensure external source maps are loaded relative to the baseHref.
  346. *
  347. * @param baseHref - The base URL to prepend to the `sourceMappingURL`.
  348. * @param styles - An array of CSS content strings, each potentially containing a `sourceMappingURL`.
  349. * @returns The updated array of CSS content strings with modified `sourceMappingURL` values,
  350. * or the original content if no modification is needed.
  351. */
  352. function addBaseHrefToCssSourceMap(baseHref, styles) {
  353. if (!baseHref) {
  354. return styles;
  355. }
  356. const absoluteBaseHrefUrl = new URL(baseHref, 'http://localhost');
  357. return styles.map((cssContent) => {
  358. if (!cssContent.includes('sourceMappingURL=')) {
  359. return cssContent;
  360. }
  361. return cssContent.replace(SOURCEMAP_URL_REGEXP, (_, sourceMapUrl) => {
  362. if (sourceMapUrl[0] === '/' ||
  363. sourceMapUrl.startsWith('data:') ||
  364. PROTOCOL_REGEXP.test(sourceMapUrl)) {
  365. return `/*# sourceMappingURL=${sourceMapUrl} */`;
  366. }
  367. const { pathname: resolvedSourceMapUrl } = new URL(sourceMapUrl, absoluteBaseHrefUrl);
  368. return `/*# sourceMappingURL=${resolvedSourceMapUrl} */`;
  369. });
  370. });
  371. }
  372. class DomRendererFactory2 {
  373. eventManager;
  374. sharedStylesHost;
  375. appId;
  376. removeStylesOnCompDestroy;
  377. doc;
  378. platformId;
  379. ngZone;
  380. nonce;
  381. tracingService;
  382. rendererByCompId = new Map();
  383. defaultRenderer;
  384. platformIsServer;
  385. constructor(eventManager, sharedStylesHost, appId, removeStylesOnCompDestroy, doc, platformId, ngZone, nonce = null, tracingService = null) {
  386. this.eventManager = eventManager;
  387. this.sharedStylesHost = sharedStylesHost;
  388. this.appId = appId;
  389. this.removeStylesOnCompDestroy = removeStylesOnCompDestroy;
  390. this.doc = doc;
  391. this.platformId = platformId;
  392. this.ngZone = ngZone;
  393. this.nonce = nonce;
  394. this.tracingService = tracingService;
  395. this.platformIsServer = isPlatformServer(platformId);
  396. this.defaultRenderer = new DefaultDomRenderer2(eventManager, doc, ngZone, this.platformIsServer, this.tracingService);
  397. }
  398. createRenderer(element, type) {
  399. if (!element || !type) {
  400. return this.defaultRenderer;
  401. }
  402. if (this.platformIsServer && type.encapsulation === ViewEncapsulation.ShadowDom) {
  403. // Domino does not support shadow DOM.
  404. type = { ...type, encapsulation: ViewEncapsulation.Emulated };
  405. }
  406. const renderer = this.getOrCreateRenderer(element, type);
  407. // Renderers have different logic due to different encapsulation behaviours.
  408. // Ex: for emulated, an attribute is added to the element.
  409. if (renderer instanceof EmulatedEncapsulationDomRenderer2) {
  410. renderer.applyToHost(element);
  411. }
  412. else if (renderer instanceof NoneEncapsulationDomRenderer) {
  413. renderer.applyStyles();
  414. }
  415. return renderer;
  416. }
  417. getOrCreateRenderer(element, type) {
  418. const rendererByCompId = this.rendererByCompId;
  419. let renderer = rendererByCompId.get(type.id);
  420. if (!renderer) {
  421. const doc = this.doc;
  422. const ngZone = this.ngZone;
  423. const eventManager = this.eventManager;
  424. const sharedStylesHost = this.sharedStylesHost;
  425. const removeStylesOnCompDestroy = this.removeStylesOnCompDestroy;
  426. const platformIsServer = this.platformIsServer;
  427. const tracingService = this.tracingService;
  428. switch (type.encapsulation) {
  429. case ViewEncapsulation.Emulated:
  430. renderer = new EmulatedEncapsulationDomRenderer2(eventManager, sharedStylesHost, type, this.appId, removeStylesOnCompDestroy, doc, ngZone, platformIsServer, tracingService);
  431. break;
  432. case ViewEncapsulation.ShadowDom:
  433. return new ShadowDomRenderer(eventManager, sharedStylesHost, element, type, doc, ngZone, this.nonce, platformIsServer, tracingService);
  434. default:
  435. renderer = new NoneEncapsulationDomRenderer(eventManager, sharedStylesHost, type, removeStylesOnCompDestroy, doc, ngZone, platformIsServer, tracingService);
  436. break;
  437. }
  438. rendererByCompId.set(type.id, renderer);
  439. }
  440. return renderer;
  441. }
  442. ngOnDestroy() {
  443. this.rendererByCompId.clear();
  444. }
  445. /**
  446. * Used during HMR to clear any cached data about a component.
  447. * @param componentId ID of the component that is being replaced.
  448. */
  449. componentReplaced(componentId) {
  450. this.rendererByCompId.delete(componentId);
  451. }
  452. static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: DomRendererFactory2, deps: [{ token: EventManager }, { token: SharedStylesHost }, { token: APP_ID }, { token: REMOVE_STYLES_ON_COMPONENT_DESTROY }, { token: DOCUMENT }, { token: PLATFORM_ID }, { token: i0.NgZone }, { token: CSP_NONCE }, { token: _TracingService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
  453. static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: DomRendererFactory2 });
  454. }
  455. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: DomRendererFactory2, decorators: [{
  456. type: Injectable
  457. }], ctorParameters: () => [{ type: EventManager }, { type: SharedStylesHost }, { type: undefined, decorators: [{
  458. type: Inject,
  459. args: [APP_ID]
  460. }] }, { type: undefined, decorators: [{
  461. type: Inject,
  462. args: [REMOVE_STYLES_ON_COMPONENT_DESTROY]
  463. }] }, { type: Document, decorators: [{
  464. type: Inject,
  465. args: [DOCUMENT]
  466. }] }, { type: Object, decorators: [{
  467. type: Inject,
  468. args: [PLATFORM_ID]
  469. }] }, { type: i0.NgZone }, { type: undefined, decorators: [{
  470. type: Inject,
  471. args: [CSP_NONCE]
  472. }] }, { type: i0.ɵTracingService, decorators: [{
  473. type: Inject,
  474. args: [_TracingService]
  475. }, {
  476. type: Optional
  477. }] }] });
  478. class DefaultDomRenderer2 {
  479. eventManager;
  480. doc;
  481. ngZone;
  482. platformIsServer;
  483. tracingService;
  484. data = Object.create(null);
  485. /**
  486. * By default this renderer throws when encountering synthetic properties
  487. * This can be disabled for example by the AsyncAnimationRendererFactory
  488. */
  489. throwOnSyntheticProps = true;
  490. constructor(eventManager, doc, ngZone, platformIsServer, tracingService) {
  491. this.eventManager = eventManager;
  492. this.doc = doc;
  493. this.ngZone = ngZone;
  494. this.platformIsServer = platformIsServer;
  495. this.tracingService = tracingService;
  496. }
  497. destroy() { }
  498. destroyNode = null;
  499. createElement(name, namespace) {
  500. if (namespace) {
  501. // TODO: `|| namespace` was added in
  502. // https://github.com/angular/angular/commit/2b9cc8503d48173492c29f5a271b61126104fbdb to
  503. // support how Ivy passed around the namespace URI rather than short name at the time. It did
  504. // not, however extend the support to other parts of the system (setAttribute, setAttribute,
  505. // and the ServerRenderer). We should decide what exactly the semantics for dealing with
  506. // namespaces should be and make it consistent.
  507. // Related issues:
  508. // https://github.com/angular/angular/issues/44028
  509. // https://github.com/angular/angular/issues/44883
  510. return this.doc.createElementNS(NAMESPACE_URIS[namespace] || namespace, name);
  511. }
  512. return this.doc.createElement(name);
  513. }
  514. createComment(value) {
  515. return this.doc.createComment(value);
  516. }
  517. createText(value) {
  518. return this.doc.createTextNode(value);
  519. }
  520. appendChild(parent, newChild) {
  521. const targetParent = isTemplateNode(parent) ? parent.content : parent;
  522. targetParent.appendChild(newChild);
  523. }
  524. insertBefore(parent, newChild, refChild) {
  525. if (parent) {
  526. const targetParent = isTemplateNode(parent) ? parent.content : parent;
  527. targetParent.insertBefore(newChild, refChild);
  528. }
  529. }
  530. removeChild(_parent, oldChild) {
  531. oldChild.remove();
  532. }
  533. selectRootElement(selectorOrNode, preserveContent) {
  534. let el = typeof selectorOrNode === 'string' ? this.doc.querySelector(selectorOrNode) : selectorOrNode;
  535. if (!el) {
  536. throw new _RuntimeError(-5104 /* RuntimeErrorCode.ROOT_NODE_NOT_FOUND */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
  537. `The selector "${selectorOrNode}" did not match any elements`);
  538. }
  539. if (!preserveContent) {
  540. el.textContent = '';
  541. }
  542. return el;
  543. }
  544. parentNode(node) {
  545. return node.parentNode;
  546. }
  547. nextSibling(node) {
  548. return node.nextSibling;
  549. }
  550. setAttribute(el, name, value, namespace) {
  551. if (namespace) {
  552. name = namespace + ':' + name;
  553. const namespaceUri = NAMESPACE_URIS[namespace];
  554. if (namespaceUri) {
  555. el.setAttributeNS(namespaceUri, name, value);
  556. }
  557. else {
  558. el.setAttribute(name, value);
  559. }
  560. }
  561. else {
  562. el.setAttribute(name, value);
  563. }
  564. }
  565. removeAttribute(el, name, namespace) {
  566. if (namespace) {
  567. const namespaceUri = NAMESPACE_URIS[namespace];
  568. if (namespaceUri) {
  569. el.removeAttributeNS(namespaceUri, name);
  570. }
  571. else {
  572. el.removeAttribute(`${namespace}:${name}`);
  573. }
  574. }
  575. else {
  576. el.removeAttribute(name);
  577. }
  578. }
  579. addClass(el, name) {
  580. el.classList.add(name);
  581. }
  582. removeClass(el, name) {
  583. el.classList.remove(name);
  584. }
  585. setStyle(el, style, value, flags) {
  586. if (flags & (RendererStyleFlags2.DashCase | RendererStyleFlags2.Important)) {
  587. el.style.setProperty(style, value, flags & RendererStyleFlags2.Important ? 'important' : '');
  588. }
  589. else {
  590. el.style[style] = value;
  591. }
  592. }
  593. removeStyle(el, style, flags) {
  594. if (flags & RendererStyleFlags2.DashCase) {
  595. // removeProperty has no effect when used on camelCased properties.
  596. el.style.removeProperty(style);
  597. }
  598. else {
  599. el.style[style] = '';
  600. }
  601. }
  602. setProperty(el, name, value) {
  603. if (el == null) {
  604. return;
  605. }
  606. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  607. this.throwOnSyntheticProps &&
  608. checkNoSyntheticProp(name, 'property');
  609. el[name] = value;
  610. }
  611. setValue(node, value) {
  612. node.nodeValue = value;
  613. }
  614. listen(target, event, callback, options) {
  615. (typeof ngDevMode === 'undefined' || ngDevMode) &&
  616. this.throwOnSyntheticProps &&
  617. checkNoSyntheticProp(event, 'listener');
  618. if (typeof target === 'string') {
  619. target = _getDOM().getGlobalEventTarget(this.doc, target);
  620. if (!target) {
  621. throw new _RuntimeError(5102 /* RuntimeErrorCode.UNSUPPORTED_EVENT_TARGET */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
  622. `Unsupported event target ${target} for event ${event}`);
  623. }
  624. }
  625. let wrappedCallback = this.decoratePreventDefault(callback);
  626. if (this.tracingService?.wrapEventListener) {
  627. wrappedCallback = this.tracingService.wrapEventListener(target, event, wrappedCallback);
  628. }
  629. return this.eventManager.addEventListener(target, event, wrappedCallback, options);
  630. }
  631. decoratePreventDefault(eventHandler) {
  632. // `DebugNode.triggerEventHandler` needs to know if the listener was created with
  633. // decoratePreventDefault or is a listener added outside the Angular context so it can handle
  634. // the two differently. In the first case, the special '__ngUnwrap__' token is passed to the
  635. // unwrap the listener (see below).
  636. return (event) => {
  637. // Ivy uses '__ngUnwrap__' as a special token that allows us to unwrap the function
  638. // so that it can be invoked programmatically by `DebugNode.triggerEventHandler`. The
  639. // debug_node can inspect the listener toString contents for the existence of this special
  640. // token. Because the token is a string literal, it is ensured to not be modified by compiled
  641. // code.
  642. if (event === '__ngUnwrap__') {
  643. return eventHandler;
  644. }
  645. // Run the event handler inside the ngZone because event handlers are not patched
  646. // by Zone on the server. This is required only for tests.
  647. const allowDefaultBehavior = this.platformIsServer
  648. ? this.ngZone.runGuarded(() => eventHandler(event))
  649. : eventHandler(event);
  650. if (allowDefaultBehavior === false) {
  651. event.preventDefault();
  652. }
  653. return undefined;
  654. };
  655. }
  656. }
  657. const AT_CHARCODE = (() => '@'.charCodeAt(0))();
  658. function checkNoSyntheticProp(name, nameKind) {
  659. if (name.charCodeAt(0) === AT_CHARCODE) {
  660. throw new _RuntimeError(5105 /* RuntimeErrorCode.UNEXPECTED_SYNTHETIC_PROPERTY */, `Unexpected synthetic ${nameKind} ${name} found. Please make sure that:
  661. - Make sure \`provideAnimationsAsync()\`, \`provideAnimations()\` or \`provideNoopAnimations()\` call was added to a list of providers used to bootstrap an application.
  662. - There is a corresponding animation configuration named \`${name}\` defined in the \`animations\` field of the \`@Component\` decorator (see https://angular.dev/api/core/Component#animations).`);
  663. }
  664. }
  665. function isTemplateNode(node) {
  666. return node.tagName === 'TEMPLATE' && node.content !== undefined;
  667. }
  668. class ShadowDomRenderer extends DefaultDomRenderer2 {
  669. sharedStylesHost;
  670. hostEl;
  671. shadowRoot;
  672. constructor(eventManager, sharedStylesHost, hostEl, component, doc, ngZone, nonce, platformIsServer, tracingService) {
  673. super(eventManager, doc, ngZone, platformIsServer, tracingService);
  674. this.sharedStylesHost = sharedStylesHost;
  675. this.hostEl = hostEl;
  676. this.shadowRoot = hostEl.attachShadow({ mode: 'open' });
  677. this.sharedStylesHost.addHost(this.shadowRoot);
  678. let styles = component.styles;
  679. if (ngDevMode) {
  680. // We only do this in development, as for production users should not add CSS sourcemaps to components.
  681. const baseHref = _getDOM().getBaseHref(doc) ?? '';
  682. styles = addBaseHrefToCssSourceMap(baseHref, styles);
  683. }
  684. styles = shimStylesContent(component.id, styles);
  685. for (const style of styles) {
  686. const styleEl = document.createElement('style');
  687. if (nonce) {
  688. styleEl.setAttribute('nonce', nonce);
  689. }
  690. styleEl.textContent = style;
  691. this.shadowRoot.appendChild(styleEl);
  692. }
  693. // Apply any external component styles to the shadow root for the component's element.
  694. // The ShadowDOM renderer uses an alternative execution path for component styles that
  695. // does not use the SharedStylesHost that other encapsulation modes leverage. Much like
  696. // the manual addition of embedded styles directly above, any external stylesheets
  697. // must be manually added here to ensure ShadowDOM components are correctly styled.
  698. // TODO: Consider reworking the DOM Renderers to consolidate style handling.
  699. const styleUrls = component.getExternalStyles?.();
  700. if (styleUrls) {
  701. for (const styleUrl of styleUrls) {
  702. const linkEl = createLinkElement(styleUrl, doc);
  703. if (nonce) {
  704. linkEl.setAttribute('nonce', nonce);
  705. }
  706. this.shadowRoot.appendChild(linkEl);
  707. }
  708. }
  709. }
  710. nodeOrShadowRoot(node) {
  711. return node === this.hostEl ? this.shadowRoot : node;
  712. }
  713. appendChild(parent, newChild) {
  714. return super.appendChild(this.nodeOrShadowRoot(parent), newChild);
  715. }
  716. insertBefore(parent, newChild, refChild) {
  717. return super.insertBefore(this.nodeOrShadowRoot(parent), newChild, refChild);
  718. }
  719. removeChild(_parent, oldChild) {
  720. return super.removeChild(null, oldChild);
  721. }
  722. parentNode(node) {
  723. return this.nodeOrShadowRoot(super.parentNode(this.nodeOrShadowRoot(node)));
  724. }
  725. destroy() {
  726. this.sharedStylesHost.removeHost(this.shadowRoot);
  727. }
  728. }
  729. class NoneEncapsulationDomRenderer extends DefaultDomRenderer2 {
  730. sharedStylesHost;
  731. removeStylesOnCompDestroy;
  732. styles;
  733. styleUrls;
  734. constructor(eventManager, sharedStylesHost, component, removeStylesOnCompDestroy, doc, ngZone, platformIsServer, tracingService, compId) {
  735. super(eventManager, doc, ngZone, platformIsServer, tracingService);
  736. this.sharedStylesHost = sharedStylesHost;
  737. this.removeStylesOnCompDestroy = removeStylesOnCompDestroy;
  738. let styles = component.styles;
  739. if (ngDevMode) {
  740. // We only do this in development, as for production users should not add CSS sourcemaps to components.
  741. const baseHref = _getDOM().getBaseHref(doc) ?? '';
  742. styles = addBaseHrefToCssSourceMap(baseHref, styles);
  743. }
  744. this.styles = compId ? shimStylesContent(compId, styles) : styles;
  745. this.styleUrls = component.getExternalStyles?.(compId);
  746. }
  747. applyStyles() {
  748. this.sharedStylesHost.addStyles(this.styles, this.styleUrls);
  749. }
  750. destroy() {
  751. if (!this.removeStylesOnCompDestroy) {
  752. return;
  753. }
  754. this.sharedStylesHost.removeStyles(this.styles, this.styleUrls);
  755. }
  756. }
  757. class EmulatedEncapsulationDomRenderer2 extends NoneEncapsulationDomRenderer {
  758. contentAttr;
  759. hostAttr;
  760. constructor(eventManager, sharedStylesHost, component, appId, removeStylesOnCompDestroy, doc, ngZone, platformIsServer, tracingService) {
  761. const compId = appId + '-' + component.id;
  762. super(eventManager, sharedStylesHost, component, removeStylesOnCompDestroy, doc, ngZone, platformIsServer, tracingService, compId);
  763. this.contentAttr = shimContentAttribute(compId);
  764. this.hostAttr = shimHostAttribute(compId);
  765. }
  766. applyToHost(element) {
  767. this.applyStyles();
  768. this.setAttribute(element, this.hostAttr, '');
  769. }
  770. createElement(parent, name) {
  771. const el = super.createElement(parent, name);
  772. super.setAttribute(el, this.contentAttr, '');
  773. return el;
  774. }
  775. }
  776. export { DomRendererFactory2, EVENT_MANAGER_PLUGINS, EventManager, EventManagerPlugin, REMOVE_STYLES_ON_COMPONENT_DESTROY, SharedStylesHost };
  777. //# sourceMappingURL=dom_renderer-DGKzginR.mjs.map