@ethlete/core 5.0.0-next.60 → 5.0.0-next.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { assertInInjectionContext, inject, DestroyRef, ElementRef, TemplateRef, InjectionToken, QueryList, RendererFactory2, RendererStyleFlags2, isSignal, DOCUMENT, signal, linkedSignal, isDevMode, computed, afterNextRender, effect, untracked, ApplicationRef, runInInjectionContext, NgZone, Injector, booleanAttribute, numberAttribute, PLATFORM_ID, EnvironmentInjector, createComponent, ViewEncapsulation, Component, Directive, model, ViewContainerRef, input, CSP_NONCE, provideEnvironmentInitializer, output, createEnvironmentInjector, Pipe, afterEveryRender } from '@angular/core';
3
3
  import { takeUntilDestroyed, toSignal, toObservable, outputFromObservable } from '@angular/core/rxjs-interop';
4
- import { Subject, fromEvent, switchMap, startWith, of, defer, from, map, catchError, timer, takeUntil, pairwise, debounceTime, merge, distinctUntilChanged, EMPTY, expand, skip, tap, take, filter, BehaviorSubject, combineLatest, Observable, isObservable, firstValueFrom, defaultIfEmpty, concatMap, concat, finalize, exhaustMap, share, shareReplay } from 'rxjs';
4
+ import { Subject, fromEvent, switchMap, startWith, of, defer, from, map, catchError, timer, takeUntil, tap, pairwise, debounceTime, merge, distinctUntilChanged, EMPTY, expand, skip, take, filter, BehaviorSubject, combineLatest, Observable, isObservable, firstValueFrom, defaultIfEmpty, concatMap, concat, finalize, exhaustMap, share, shareReplay } from 'rxjs';
5
5
  import { FormGroup, FormArray, FormControl } from '@angular/forms';
6
6
  import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals';
7
7
  import { isPlatformBrowser, DOCUMENT as DOCUMENT$1 } from '@angular/common';
8
8
  import { Router, NavigationEnd, NavigationSkipped, NavigationStart, NavigationError, RouterLink } from '@angular/router';
9
9
  import { Title, Meta, DomSanitizer } from '@angular/platform-browser';
10
- import { offset, flip, shift, limitShift, computePosition, autoUpdate, hide, arrow, size } from '@floating-ui/dom';
10
+ import { platform, offset, flip, shift, limitShift, computePosition, autoUpdate, hide, arrow, size } from '@floating-ui/dom';
11
11
 
12
12
  const ANIMATION_DEBUG_STORAGE_KEY = 'et-overlay-debug';
13
13
  let cachedEnabled = null;
@@ -660,8 +660,8 @@ class KeyPressManager {
660
660
  this.keyPressCount++;
661
661
  this._stopTimeout$.next();
662
662
  timer(100)
663
- .pipe(takeUntil(this._stopTimeout$))
664
- .subscribe(() => this.clear());
663
+ .pipe(takeUntil(this._stopTimeout$), tap(() => this.clear()))
664
+ .subscribe();
665
665
  }
666
666
  else {
667
667
  this.clear();
@@ -1693,6 +1693,7 @@ const resolveFromMap = (map, bp, defaultValue) => {
1693
1693
  }
1694
1694
  return defaultValue;
1695
1695
  };
1696
+ /** Builds an input transform that coerces a value or a per-breakpoint map to `T` for the current breakpoint. */
1696
1697
  const breakpointTransformBase = (coerce, defaultValue) => {
1697
1698
  const currentBp = injectCurrentBreakpoint();
1698
1699
  const injector = inject(Injector);
@@ -1852,8 +1853,8 @@ const signalCountdown = (deadline) => {
1852
1853
  const remainingMs = endsAt - Date.now();
1853
1854
  return remainingMs <= 0 ? EMPTY : timer(remainingMs % SECOND_MS || SECOND_MS);
1854
1855
  }), skip(1));
1855
- }), takeUntilDestroyed())
1856
- .subscribe(() => tick.update((count) => count + 1));
1856
+ }), tap(() => tick.update((count) => count + 1)), takeUntilDestroyed())
1857
+ .subscribe();
1857
1858
  }
1858
1859
  return computed(() => {
1859
1860
  tick();
@@ -1897,13 +1898,13 @@ const signalDeferredLoading = (source, options) => {
1897
1898
  }
1898
1899
  const remaining = minDuration - (performance.now() - shownAt);
1899
1900
  return remaining <= 0 ? of(false) : timer(remaining).pipe(map(() => false));
1900
- }), takeUntilDestroyed())
1901
- .subscribe((isVisible) => {
1901
+ }), tap((isVisible) => {
1902
1902
  if (isVisible) {
1903
1903
  shownAt = performance.now();
1904
1904
  }
1905
1905
  visible.set(isVisible);
1906
- });
1906
+ }), takeUntilDestroyed())
1907
+ .subscribe();
1907
1908
  return visible.asReadonly();
1908
1909
  };
1909
1910
 
@@ -1948,8 +1949,6 @@ const signalElementChildren = (el, options) => {
1948
1949
  if (!isRendered())
1949
1950
  return [];
1950
1951
  const els = firstEl();
1951
- // We are not interested what the mutation is, just that there is one.
1952
- // Changes to the DOM may affect the children of the element.
1953
1952
  elementMutations();
1954
1953
  if (!els.currentElement)
1955
1954
  return [];
@@ -2099,8 +2098,6 @@ const signalStyles = (el, styleMap) => {
2099
2098
  const signalHostStyles = (styleMap) => signalStyles(inject(ElementRef), styleMap);
2100
2099
 
2101
2100
  const elementCanScroll = (element, direction) => {
2102
- // Omitting the element means "the document scroller". Resolved lazily and guarded so a call on the
2103
- // server answers "cannot scroll" instead of throwing on a missing `document`.
2104
2101
  const el = element || (typeof document === 'undefined' ? null : document.documentElement);
2105
2102
  if (!el) {
2106
2103
  return false;
@@ -2115,9 +2112,6 @@ const elementCanScroll = (element, direction) => {
2115
2112
  return scrollHeight > clientHeight || scrollWidth > clientWidth;
2116
2113
  };
2117
2114
  const createViewportRect = () => {
2118
- // Collapses to a zero rect where there is no window (server). `isElementVisible` never reaches the
2119
- // intersection math there anyway - `elementCanScroll` already reports the document as unscrollable
2120
- // - so the rect only has to be a valid shape.
2121
2115
  const hasWindow = typeof window !== 'undefined';
2122
2116
  const width = hasWindow ? window.innerWidth : 0;
2123
2117
  const height = hasWindow ? window.innerHeight : 0;
@@ -2212,13 +2206,13 @@ const getElementScrollCoordinates = (options) => {
2212
2206
  scrollTopTo = scrollTop + relativeTop - conHeight / 2 + elHeight / 2;
2213
2207
  };
2214
2208
  const calculateScrollToNearest = () => {
2215
- const isAbove = elBottom < conTop;
2209
+ const isAbove = elBottom <= conTop;
2216
2210
  const isPartialAbove = elTop < conTop && elBottom > conTop;
2217
- const isBelow = elTop > conBottom;
2211
+ const isBelow = elTop >= conBottom;
2218
2212
  const isPartialBelow = elTop < conBottom && elBottom > conBottom;
2219
- const isLeft = elRight < conLeft;
2213
+ const isLeft = elRight <= conLeft;
2220
2214
  const isPartialLeft = elLeft < conLeft && elRight > conLeft;
2221
- const isRight = elLeft > conRight;
2215
+ const isRight = elLeft >= conRight;
2222
2216
  const isPartialRight = elLeft < conRight && elRight > conRight;
2223
2217
  if (isAbove || isPartialAbove || isLeft || isPartialLeft) {
2224
2218
  calculateScrollToStart();
@@ -2252,6 +2246,7 @@ const scrollToElement = (options) => {
2252
2246
  };
2253
2247
 
2254
2248
  const MIN_SNAP_DELTA_PX = 1;
2249
+ const FULLY_VISIBLE_RATIO = 0.99;
2255
2250
  const getScrollSnapTarget = (items, container, direction, origin, margin = 0) => {
2256
2251
  if (!items.length)
2257
2252
  return null;
@@ -2371,7 +2366,7 @@ const getScrollItemTarget = (entries, container, direction, scrollOrigin, axisDi
2371
2366
  }
2372
2367
  const entry = direction === 'start' ? firstVisible : lastVisible;
2373
2368
  const entryIndex = direction === 'start' ? firstIndex : lastIndex;
2374
- if (Math.round(entry.intersectionRatio) === 1) {
2369
+ if (entry.intersectionRatio >= FULLY_VISIBLE_RATIO) {
2375
2370
  if (direction === 'start' && entryIndex === 0)
2376
2371
  return null;
2377
2372
  if (direction === 'end' && entryIndex === entries.length - 1)
@@ -2623,8 +2618,6 @@ const signalElementScrollState = (el, options) => {
2623
2618
  return computed(() => {
2624
2619
  const element = observedEl().currentElement;
2625
2620
  const dimensions = elementDimensions();
2626
- // We are not interested what the mutation is, just that there is one.
2627
- // Changes to the DOM can affect the scroll state of the element.
2628
2621
  elementMutations();
2629
2622
  if (!element || !isRendered())
2630
2623
  return notScrollable(dimensions);
@@ -2745,14 +2738,12 @@ const useCursorDragScroll = (el, options) => {
2745
2738
  // A host destroyed mid-drag never re-runs the styling effect, so the grabbing cursor it wrote on
2746
2739
  // `<html>` would outlive the page it was dragged on.
2747
2740
  destroyRef.onDestroy(() => renderer.removeStyle(document.documentElement, 'cursor'));
2748
- // Cleanup if the element the cursor drag scroll is bound to gets changed
2749
2741
  effect(() => {
2750
2742
  const { previousElement } = element();
2751
2743
  if (previousElement) {
2752
2744
  renderer.removeStyle(previousElement, 'cursor');
2753
2745
  }
2754
2746
  });
2755
- // Conditionally apply styles/classes to the element and the document
2756
2747
  effect(() => {
2757
2748
  const currCanScroll = canScroll();
2758
2749
  const isEnabled = enabled();
@@ -2792,7 +2783,6 @@ const useCursorDragScroll = (el, options) => {
2792
2783
  }
2793
2784
  });
2794
2785
  });
2795
- // Update the element's scroll position when the user drags
2796
2786
  effect(() => {
2797
2787
  const currDragAmount = dragAmount();
2798
2788
  untracked(() => {
@@ -3425,14 +3415,14 @@ const setupScrollRestoration = (config = {}) => {
3425
3415
  }
3426
3416
  };
3427
3417
  router.events
3428
- .pipe(filter((e) => e instanceof NavigationStart || e instanceof NavigationEnd || e instanceof NavigationSkipped), takeUntilDestroyed())
3429
- .subscribe((event) => {
3418
+ .pipe(filter((e) => e instanceof NavigationStart || e instanceof NavigationEnd || e instanceof NavigationSkipped), tap((event) => {
3430
3419
  if (event instanceof NavigationStart) {
3431
3420
  onNavigationStart(event);
3432
3421
  return;
3433
3422
  }
3434
3423
  onNavigationEnd(event);
3435
- });
3424
+ }), takeUntilDestroyed())
3425
+ .subscribe();
3436
3426
  destroyRef.onDestroy(() => {
3437
3427
  cancelPendingRestore();
3438
3428
  if (restoreEnabled && previousScrollRestoration !== null) {
@@ -3470,15 +3460,30 @@ const escapeHtmlPreservingEntities = (str) => str
3470
3460
  .replace(/</g, '&lt;')
3471
3461
  .replace(/>/g, '&gt;')
3472
3462
  .replace(/"/g, '&quot;');
3463
+ /** Attribute that makes `htmlToMarkdown` write an element's text verbatim, without Markdown escaping:
3464
+ * `<span data-markdown-verbatim>{{user:_x}}</span>` becomes `{{user:_x}}`. The element must hold
3465
+ * text only. */
3466
+ const MARKDOWN_VERBATIM_ATTR = 'data-markdown-verbatim';
3467
+ const VERBATIM_RE = /* @__PURE__ */ new RegExp(`<span\\b[^>]*\\b${MARKDOWN_VERBATIM_ATTR}\\b[^>]*>([^<]*)<\\/span>`, 'gi');
3473
3468
  const decodeUrlCharacterReferences = (url) => url
3474
3469
  .replace(/&#(\d+);?/g, (_, value) => String.fromCodePoint(Number(value)))
3475
3470
  .replace(/&#x([0-9a-f]+);?/gi, (_, value) => String.fromCodePoint(Number.parseInt(value, 16)))
3476
3471
  .replace(/&(colon|tab|newline);/gi, (_, entity) => entity.toLowerCase() === 'colon' ? ':' : entity.toLowerCase() === 'tab' ? '\t' : '\n');
3472
+ /** Whether `url` may be embedded as an image `src`: everything except `javascript:` and `vbscript:`
3473
+ * URLs and non-image `data:` URLs, however they are cased, spaced or entity-encoded. */
3477
3474
  const isSafeUrl = (url) => {
3478
3475
  // eslint-disable-next-line no-control-regex -- URL parsers ignore these characters inside schemes.
3479
3476
  const normalized = decodeUrlCharacterReferences(url).replace(/[\s\u0000-\u001f]/g, '');
3480
3477
  return (!/^(javascript|vbscript):/i.test(normalized) && !/^data:(?!image\/(?:avif|gif|jpeg|png|webp);)/i.test(normalized));
3481
3478
  };
3479
+ /** Whether `url` may become a link: `http:`, `https:`, `mailto:` and `tel:` URLs, relative paths and
3480
+ * fragments pass; any other scheme (`javascript:`, `data:`, `vbscript:`, ...) is refused, however
3481
+ * it is cased, spaced or entity-encoded. */
3482
+ const isSafeLinkUrl = (url) => {
3483
+ // eslint-disable-next-line no-control-regex -- URL parsers ignore these characters inside schemes.
3484
+ const normalized = decodeUrlCharacterReferences(url).replace(/[\s\u0000-\u001f]/g, '');
3485
+ return !/^[a-z][a-z0-9+.-]*:/i.test(normalized) || /^(?:https?|mailto|tel):/i.test(normalized);
3486
+ };
3482
3487
  const unescapeHtml = (str) => str
3483
3488
  .replace(/&amp;/g, '&')
3484
3489
  .replace(/&lt;/g, '<')
@@ -3527,7 +3532,7 @@ const sanitizeInlineHtml = (html) => {
3527
3532
  const name = tag.toLowerCase();
3528
3533
  if (name === 'a') {
3529
3534
  const href = closing ? null : /\bhref\s*=\s*"([^"]*)"/i.exec(full)?.[1];
3530
- const anchor = closing ? '</a>' : href && isSafeUrl(href) ? `<a href="${href}">` : '<a>';
3535
+ const anchor = closing ? '</a>' : href && isSafeLinkUrl(href) ? `<a href="${href}">` : '<a>';
3531
3536
  return makePlaceholder('TAG', kept.push(anchor) - 1);
3532
3537
  }
3533
3538
  if (!SAFE_INLINE_TAGS.has(name))
@@ -3538,10 +3543,10 @@ const sanitizeInlineHtml = (html) => {
3538
3543
  };
3539
3544
  const processInline = (text) => {
3540
3545
  const inlineCodes = [];
3541
- text = text.replace(/`([^`]+)`/g, (_, code) => {
3542
- const idx = inlineCodes.push(`<code>${escapeHtml(code)}</code>`) - 1;
3543
- return makePlaceholder('IC', idx);
3544
- });
3546
+ const escapes = [];
3547
+ text = text.replace(/\\([!-/:-@[-`{-~])|`([^`]+)`/g, (_, escaped, code) => escaped !== undefined
3548
+ ? makePlaceholder('ESC', escapes.push(escapeHtml(escaped)) - 1)
3549
+ : makePlaceholder('IC', inlineCodes.push(`<code>${escapeHtml(code ?? '')}</code>`) - 1));
3545
3550
  // "Open in new tab" links round-trip as raw HTML because Markdown has no `target` syntax. Extract
3546
3551
  // them before the raw-HTML escape below, keeping only a safe href, `target="_blank"` and a forced
3547
3552
  // `rel="noopener noreferrer"` (any other attribute - including event handlers - is dropped); their
@@ -3550,9 +3555,11 @@ const processInline = (text) => {
3550
3555
  text = text.replace(/<a\b([^>]*)>([\s\S]*?)<\/a>/gi, (full, attrs, inner) => {
3551
3556
  const href = /\bhref\s*=\s*"([^"]*)"/i.exec(attrs)?.[1];
3552
3557
  const newTab = /\btarget\s*=\s*"_blank"/i.test(attrs);
3553
- if (!newTab || !href || !isSafeUrl(href))
3558
+ if (!newTab || !href)
3554
3559
  return full;
3555
- const anchor = `<a href="${href}" target="_blank" rel="noopener noreferrer">${processInline(inner)}</a>`;
3560
+ const anchor = isSafeLinkUrl(href)
3561
+ ? `<a href="${href}" target="_blank" rel="noopener noreferrer">${processInline(inner)}</a>`
3562
+ : processInline(inner);
3556
3563
  return makePlaceholder('NTLINK', newTabLinks.push(anchor) - 1);
3557
3564
  });
3558
3565
  // Raw HTML in Markdown text is escaped, not rendered - the editor writes this HTML straight
@@ -3570,10 +3577,12 @@ const processInline = (text) => {
3570
3577
  text = text.replace(/~~([^\s~](?:.*?[^\s~])?)~~/g, '<del>$1</del>');
3571
3578
  // Images before links (order matters); URLs with script-running schemes stay literal text
3572
3579
  text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (match, alt, src) => isSafeUrl(src) ? `<img src="${src}" alt="${alt}">` : match);
3573
- text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, label, href) => isSafeUrl(href) ? `<a href="${href}">${label}</a>` : match);
3580
+ text = text.replace(/(?<!!)\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, href) => isSafeLinkUrl(href) ? `<a href="${href}">${label}</a>` : label);
3581
+ // New-tab links first: their inner markup still holds this call's code and escape placeholders.
3574
3582
  return text
3575
- .replace(placeholderRe('IC'), (_, i) => inlineCodes[+i] ?? '')
3576
- .replace(placeholderRe('NTLINK'), (_, i) => newTabLinks[+i] ?? '');
3583
+ .replace(placeholderRe('NTLINK'), (_, i) => newTabLinks[+i] ?? '')
3584
+ .replace(placeholderRe('IC'), (match, i) => inlineCodes[+i] ?? match)
3585
+ .replace(placeholderRe('ESC'), (match, i) => escapes[+i] ?? match);
3577
3586
  };
3578
3587
  /** The first top-level `<ul>`/`<ol>` in `html`, matched with balanced nesting. */
3579
3588
  const findList = (html) => {
@@ -3737,15 +3746,49 @@ const buildListHtml = (lines, start, baseIndent) => {
3737
3746
  const tag = ordered ? 'ol' : 'ul';
3738
3747
  return { html: `<${tag}>${items}</${tag}>`, next: i };
3739
3748
  };
3749
+ const escapeMarkdownText = (text) => text
3750
+ .replace(/[\\`[\]]/g, '\\$&')
3751
+ .replace(/(?<!\s)\*|\*(?!\s)/g, '\\*')
3752
+ .replace(/_+/g, (run, offset, whole) => run.length === 1 && /[a-z0-9]/i.test(whole[offset - 1] ?? '') && /[a-z0-9]/i.test(whole[offset + 1] ?? '')
3753
+ ? run
3754
+ : run.replace(/_/g, '\\_'))
3755
+ .replace(/~{2,}/g, (run) => run.replace(/~/g, '\\~'))
3756
+ .replace(/&lt;(?=\/?(?:u|a|p|h[1-6]|div)\b)/gi, '\\&lt;')
3757
+ .replace(/(^|\n)([ \t]*)(?=[-+*#|:]|&gt;|\d+\.)/g, '$1$2\u{E002}');
3758
+ const escapeMarkdownAlt = (alt) => alt.replace(/[\\[\]]/g, '\\$&');
3759
+ const escapeTextNodes = (html) => {
3760
+ let codeDepth = 0;
3761
+ return html.replace(/(<[^>]*>)|([^<]+)/g, (_, tag, text) => {
3762
+ if (tag) {
3763
+ const code = /^<(\/?)(?:pre|code)\b/i.exec(tag);
3764
+ if (code)
3765
+ codeDepth = Math.max(0, codeDepth + (code[1] ? -1 : 1));
3766
+ return tag;
3767
+ }
3768
+ return codeDepth > 0 ? (text ?? '') : escapeMarkdownText(text ?? '');
3769
+ });
3770
+ };
3771
+ const escapeLineStarts = (md) => md
3772
+ .replace(/^([ \t]*(?:>[ \t]?)*)\u{E002}(?:([-+*])(?=[ \t]|$|--[ \t]*$)|(#{1,6})(?=[ \t]|$)|([>|])|(\d+)\.(?=[ \t]|$)|(?=[-:| \t]*\|[-:| \t]*$)(?=.*-))/gmu, (_, prefix, bullet, hashes, marker, digits) => digits !== undefined ? `${prefix}${digits}\\.` : `${prefix}\\${bullet ?? hashes ?? marker ?? ''}`)
3773
+ .replace(/\u{E002}/gu, '');
3740
3774
  /**
3741
3775
  * Converts a markdown string to HTML.
3742
3776
  * Covers headings, bold, italic, strikethrough, inline code, fenced code blocks,
3743
3777
  * links, images, block quotes, unordered/ordered (and nested) lists, tables, horizontal rules, and paragraphs.
3778
+ * A backslash before a punctuation character keeps that character literal.
3779
+ * Text matching `options.verbatim` is emitted as plain text, untouched by any Markdown syntax:
3780
+ * `markdownToHtml('{{field:__x__}}', { verbatim: /\{\{[^}]+\}\}/ })` returns `<p>{{field:__x__}}</p>`.
3744
3781
  */
3745
- const markdownToHtml = (markdown) => {
3782
+ const markdownToHtml = (markdown, options = {}) => {
3746
3783
  if (!markdown)
3747
3784
  return '';
3748
3785
  let text = markdown.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
3786
+ const verbatims = [];
3787
+ const { verbatim } = options;
3788
+ if (verbatim) {
3789
+ const pattern = new RegExp(verbatim.source, verbatim.flags.includes('g') ? verbatim.flags : `${verbatim.flags}g`);
3790
+ text = text.replace(pattern, (match) => match ? makePlaceholder('VERB', verbatims.push(escapeHtmlPreservingEntities(match)) - 1) : match);
3791
+ }
3749
3792
  // Extract fenced code blocks before any other processing
3750
3793
  const codeBlocks = [];
3751
3794
  text = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => {
@@ -3822,17 +3865,23 @@ const markdownToHtml = (markdown) => {
3822
3865
  })
3823
3866
  .filter(Boolean)
3824
3867
  .join('\n');
3825
- return html.replace(placeholderRe('CODE'), (_, i) => codeBlocks[+i] ?? '');
3868
+ return html
3869
+ .replace(placeholderRe('CODE'), (_, i) => codeBlocks[+i] ?? '')
3870
+ .replace(placeholderRe('VERB'), (match, i) => verbatims[+i] ?? match);
3826
3871
  };
3827
3872
  /**
3828
3873
  * Converts an HTML string to markdown.
3829
3874
  * Covers headings, bold, italic, strikethrough, inline code, fenced code blocks,
3830
3875
  * links, images, block quotes, unordered/ordered lists, tables, horizontal rules, and paragraphs.
3876
+ * Text that would read as Markdown is backslash-escaped (code and {@link MARKDOWN_VERBATIM_ATTR}
3877
+ * elements are left as is), and a link whose URL
3878
+ * fails {@link isSafeLinkUrl} keeps only its text.
3831
3879
  */
3832
3880
  const htmlToMarkdown = (html) => {
3833
3881
  if (!html)
3834
3882
  return '';
3835
- let md = html;
3883
+ const verbatims = [];
3884
+ let md = html.replace(VERBATIM_RE, (_, text) => makePlaceholder('VERB', verbatims.push(unescapeHtml(text)) - 1));
3836
3885
  // Aligned blocks: text-align has no Markdown form, so preserve them as HTML (their inner markup
3837
3886
  // stays HTML) and round-trip via a placeholder - extracted before the block passes below rewrite
3838
3887
  // them, restored after the final tag-strip.
@@ -3841,6 +3890,7 @@ const htmlToMarkdown = (html) => {
3841
3890
  const name = tag.toLowerCase();
3842
3891
  return makePlaceholder('ALIGN', alignedBlocks.push(`<${name}${alignClass(alignOf(attrs))}>${inner}</${name}>`) - 1);
3843
3892
  });
3893
+ md = escapeTextNodes(md);
3844
3894
  // Code blocks - process before inline code
3845
3895
  md = md.replace(/<pre[^>]*><code[^>]*class="language-([^"]+)"[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (_, lang, code) => `\n\`\`\`${lang}\n${unescapeHtml(code.trim())}\n\`\`\`\n`);
3846
3896
  md = md.replace(/<pre[^>]*><code[^>]*>([\s\S]*?)<\/code><\/pre>/gi, (_, code) => `\n\`\`\`\n${unescapeHtml(code.trim())}\n\`\`\`\n`);
@@ -3865,14 +3915,16 @@ const htmlToMarkdown = (html) => {
3865
3915
  const newTabLinks = [];
3866
3916
  md = md.replace(/<a\b([^>]*)>([\s\S]*?)<\/a>/gi, (_, attrs, inner) => {
3867
3917
  const href = /\bhref\s*=\s*"([^"]*)"/i.exec(attrs)?.[1] ?? '';
3918
+ if (!isSafeLinkUrl(href))
3919
+ return inner;
3868
3920
  if (/\btarget\s*=\s*"_blank"/i.test(attrs) && href) {
3869
3921
  const anchor = `<a href="${href}" target="_blank" rel="noopener noreferrer">${inner}</a>`;
3870
3922
  return makePlaceholder('NTLINK', newTabLinks.push(anchor) - 1);
3871
3923
  }
3872
3924
  return href ? `[${inner}](${href})` : inner;
3873
3925
  });
3874
- md = md.replace(/<img[^>]+src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi, '![$2]($1)');
3875
- md = md.replace(/<img[^>]+alt="([^"]*)"[^>]*src="([^"]*)"[^>]*\/?>/gi, '![$1]($2)');
3926
+ md = md.replace(/<img[^>]+src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi, (_, src, alt) => `![${escapeMarkdownAlt(alt)}](${src})`);
3927
+ md = md.replace(/<img[^>]+alt="([^"]*)"[^>]*src="([^"]*)"[^>]*\/?>/gi, (_, alt, src) => `![${escapeMarkdownAlt(alt)}](${src})`);
3876
3928
  md = md.replace(/<img[^>]+src="([^"]*)"[^>]*\/?>/gi, '![]($1)');
3877
3929
  // Underline has no Markdown form - preserve it as raw <u> so it round-trips. Extract it now (its
3878
3930
  // inner markup is already converted above) so the block passes below don't strip the tag; the
@@ -3926,7 +3978,7 @@ const htmlToMarkdown = (html) => {
3926
3978
  // aligned blocks are block-level, so pad with blank lines before collapsing so they survive as
3927
3979
  // their own Markdown block
3928
3980
  md = md.replace(placeholderRe('ALIGN'), (_, i) => `\n\n${alignedBlocks[+i] ?? ''}\n\n`);
3929
- return md.replace(/\n{3,}/g, '\n\n').trim();
3981
+ return escapeLineStarts(md.replace(/\n{3,}/g, '\n\n').trim()).replace(placeholderRe('VERB'), (_, i) => verbatims[+i] ?? '');
3930
3982
  };
3931
3983
 
3932
3984
  const clamp = (value, min = 0, max = 100) => {
@@ -4197,8 +4249,15 @@ const suppressTextSelection = (doc) => {
4197
4249
  existing.count++;
4198
4250
  }
4199
4251
  else {
4200
- suppressions.set(doc, { count: 1, previousInlineValue: doc.documentElement.style.userSelect });
4201
- doc.documentElement.style.userSelect = 'none';
4252
+ const style = doc.documentElement.style;
4253
+ suppressions.set(doc, {
4254
+ count: 1,
4255
+ previousUserSelect: style.userSelect,
4256
+ previousWebkitUserSelect: style.webkitUserSelect,
4257
+ });
4258
+ style.userSelect = 'none';
4259
+ // Safari and WebKitGTK ignore the unprefixed `user-select`.
4260
+ style.webkitUserSelect = 'none';
4202
4261
  }
4203
4262
  let released = false;
4204
4263
  return () => {
@@ -4210,7 +4269,8 @@ const suppressTextSelection = (doc) => {
4210
4269
  if (state.count > 0)
4211
4270
  return;
4212
4271
  suppressions.delete(doc);
4213
- doc.documentElement.style.userSelect = state.previousInlineValue;
4272
+ doc.documentElement.style.userSelect = state.previousUserSelect;
4273
+ doc.documentElement.style.webkitUserSelect = state.previousWebkitUserSelect;
4214
4274
  };
4215
4275
  };
4216
4276
 
@@ -4610,25 +4670,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
4610
4670
  }]
4611
4671
  }], ctorParameters: () => [] });
4612
4672
 
4673
+ /** Runs `cb` two frames from now. Returns a function that cancels it if it has not run yet. */
4613
4674
  const nextFrame = (cb) => {
4614
4675
  if (typeof requestAnimationFrame === 'undefined') {
4615
- return;
4676
+ return () => undefined;
4616
4677
  }
4617
- requestAnimationFrame(() => {
4618
- requestAnimationFrame(cb);
4678
+ let id = requestAnimationFrame(() => {
4679
+ id = requestAnimationFrame(cb);
4619
4680
  });
4681
+ return () => cancelAnimationFrame(id);
4620
4682
  };
4621
4683
  const fromNextFrame = () => {
4622
- return new Observable((observer) => {
4623
- nextFrame(() => {
4624
- observer.next();
4625
- observer.complete();
4626
- });
4627
- });
4684
+ return new Observable((observer) => nextFrame(() => {
4685
+ observer.next();
4686
+ observer.complete();
4687
+ }));
4628
4688
  };
4629
4689
  const forceReflow = (element) => {
4630
- // The `document.body` fallback is resolved lazily and guarded: there is nothing to reflow on the
4631
- // server, so this becomes a no-op instead of throwing on a missing `document`.
4632
4690
  const el = element ?? (typeof document === 'undefined' ? null : document.body);
4633
4691
  return el?.offsetHeight ?? 0;
4634
4692
  };
@@ -5030,14 +5088,16 @@ const createFlipAnimationGroup = (config) => {
5030
5088
  }
5031
5089
  };
5032
5090
  flips.forEach((animation) => {
5033
- animation.onStart$.subscribe(() => {
5091
+ animation.onStart$
5092
+ .pipe(tap(() => {
5034
5093
  startedCount++;
5035
5094
  if (startedCount === flips.length) {
5036
5095
  onStart$.next();
5037
5096
  }
5038
- });
5039
- animation.onFinish$.subscribe(() => settle(false));
5040
- animation.onCancel$.subscribe(() => settle(true));
5097
+ }))
5098
+ .subscribe();
5099
+ animation.onFinish$.pipe(tap(() => settle(false))).subscribe();
5100
+ animation.onCancel$.pipe(tap(() => settle(true))).subscribe();
5041
5101
  });
5042
5102
  const updateInit = () => {
5043
5103
  flips.forEach((animation) => animation.updateInit());
@@ -5155,7 +5215,7 @@ const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() =>
5155
5215
  const hasUnsavedChanges = computed(() => _registrations().some((registration) => registration.hasChanges()), /* @ts-ignore */
5156
5216
  ...(ngDevMode ? [{ debugName: "hasUnsavedChanges" }] : /* istanbul ignore next */ []));
5157
5217
  let pending = null;
5158
- /** @internal Runs a tracker's confirm under the single-flight + abandon regime. */
5218
+ /** Runs a tracker's confirm under the single-flight + abandon regime. */
5159
5219
  const runCheck = (runner) => {
5160
5220
  // A confirm is already on screen - adopt its answer rather than opening a second dialog.
5161
5221
  if (pending) {
@@ -5183,7 +5243,6 @@ const UNSAVED_CHANGES_COORDINATOR_DEF = /* @__PURE__ */ defineRootProvider(() =>
5183
5243
  _isCheckPending.set(true);
5184
5244
  return promise;
5185
5245
  };
5186
- /** @internal */
5187
5246
  const register = (registration) => {
5188
5247
  _registrations.update((current) => [...current, registration]);
5189
5248
  return () => _registrations.update((current) => current.filter((entry) => entry !== registration));
@@ -5670,6 +5729,9 @@ const LINK_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
5670
5729
  }, { name: 'Link Store' });
5671
5730
  const provideLinkStore = /* @__PURE__ */ toProvideFn(LINK_STORE_DEF);
5672
5731
  const injectLinkStore = /* @__PURE__ */ toInjectFn(LINK_STORE_DEF);
5732
+ /**
5733
+ * Binds a link tag. Only removes the elements it created; pre-existing links are left in place, unlike meta bindings.
5734
+ */
5673
5735
  const applyLinkBinding = (binding) => {
5674
5736
  const linkStore = injectLinkStore();
5675
5737
  const linkId = Symbol('link-tag');
@@ -5814,7 +5876,6 @@ const applyHeadTitleMarker = (binding) => {
5814
5876
  };
5815
5877
 
5816
5878
  const DEFAULT_MULTI_INSTANCE_TAGS = /* @__PURE__ */ new Set([
5817
- // Open Graph - Media
5818
5879
  'property="og:image"',
5819
5880
  'property="og:image:url"',
5820
5881
  'property="og:image:secure_url"',
@@ -5824,32 +5885,23 @@ const DEFAULT_MULTI_INSTANCE_TAGS = /* @__PURE__ */ new Set([
5824
5885
  'property="og:audio"',
5825
5886
  'property="og:audio:url"',
5826
5887
  'property="og:audio:secure_url"',
5827
- // Open Graph - Locale alternatives
5828
5888
  'property="og:locale:alternate"',
5829
- // Twitter Card
5830
5889
  'name="twitter:image"',
5831
5890
  'name="twitter:player"',
5832
- // Article tags (arrays)
5833
5891
  'property="article:author"',
5834
5892
  'property="article:tag"',
5835
- // Music tags (arrays)
5836
5893
  'property="music:song"',
5837
5894
  'property="music:musician"',
5838
5895
  'property="music:album"',
5839
- // Video tags (arrays)
5840
5896
  'property="video:actor"',
5841
5897
  'property="video:director"',
5842
5898
  'property="video:writer"',
5843
5899
  'property="video:tag"',
5844
- // Book tags (arrays)
5845
5900
  'property="book:author"',
5846
5901
  'property="book:tag"',
5847
- // Profile (arrays)
5848
5902
  'property="profile:username"',
5849
- // Product tags (arrays)
5850
5903
  'property="product:category"',
5851
5904
  'property="product:retailer_item_id"',
5852
- // App Links (multiple platforms)
5853
5905
  'property="al:ios:url"',
5854
5906
  'property="al:ios:app_store_id"',
5855
5907
  'property="al:ios:app_name"',
@@ -5866,16 +5918,12 @@ const DEFAULT_MULTI_INSTANCE_TAGS = /* @__PURE__ */ new Set([
5866
5918
  'property="al:windows_universal:url"',
5867
5919
  'property="al:windows_universal:app_id"',
5868
5920
  'property="al:windows_universal:app_name"',
5869
- // Dublin Core (can have multiple)
5870
5921
  'name="DC.creator"',
5871
5922
  'name="DC.contributor"',
5872
5923
  'name="DC.subject"',
5873
- // Schema.org (via meta tags - though JSON-LD is preferred)
5874
5924
  'itemprop="image"',
5875
5925
  'itemprop="author"',
5876
- // Apple iOS
5877
5926
  'name="apple-itunes-app"', // Can have multiple app arguments
5878
- // Verification tags (multiple services)
5879
5927
  'name="google-site-verification"',
5880
5928
  'name="msvalidate.01"',
5881
5929
  'name="yandex-verification"',
@@ -6004,6 +6052,9 @@ const META_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
6004
6052
  }, { name: 'Meta Store' });
6005
6053
  const provideMetaStore = /* @__PURE__ */ toProvideFn(META_STORE_DEF);
6006
6054
  const injectMetaStore = /* @__PURE__ */ toInjectFn(META_STORE_DEF);
6055
+ /**
6056
+ * Binds a meta tag. Removes any pre-existing tags matching the same selector, unlike link bindings, which leave pre-existing links alone.
6057
+ */
6007
6058
  const applyMetaBinding = (binding) => {
6008
6059
  const metaStore = injectMetaStore();
6009
6060
  const tagId = Symbol('meta-tag');
@@ -6199,7 +6250,7 @@ const STRUCTURED_DATA_STORE_DEF = /* @__PURE__ */ defineRootProvider(() => {
6199
6250
  renderer.setAttributes(script, { type: 'application/ld+json' });
6200
6251
  if (nonce)
6201
6252
  renderer.setAttribute(script, 'nonce', nonce);
6202
- renderer.setTextContent(script, JSON.stringify(data));
6253
+ renderer.setTextContent(script, JSON.stringify(data).replace(/</g, '\\u003C'));
6203
6254
  const target = getTargetElement();
6204
6255
  renderer.appendChild(target, script);
6205
6256
  newScripts.set(id, script);
@@ -6550,6 +6601,26 @@ const createUnsavedChangesGuard = (config) => {
6550
6601
  };
6551
6602
 
6552
6603
  const isCrossOrigin = (source) => /^(https?:)?\/\//.test(source);
6604
+ const HTML_COMMENT = /<!--[\s\S]*?-->/g;
6605
+ const SCRIPT_SRC = /<script\b[^>]*?\ssrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/gi;
6606
+ const CHARACTER_REFERENCE = /&(?:#(\d+)|#[xX]([\da-fA-F]+)|(amp|quot|apos|lt|gt));/g;
6607
+ const NAMED_REFERENCES = { amp: '&', quot: '"', apos: "'", lt: '<', gt: '>' };
6608
+ const decodeCharacterReferences = (value) => value.replace(CHARACTER_REFERENCE, (_match, decimal, hex, name) => decimal
6609
+ ? String.fromCodePoint(Number(decimal))
6610
+ : hex
6611
+ ? String.fromCodePoint(parseInt(hex, 16))
6612
+ : (NAMED_REFERENCES[name ?? ''] ?? ''));
6613
+ const toFingerprint = (sources) => sources
6614
+ .filter((source) => !!source && !isCrossOrigin(source))
6615
+ .sort()
6616
+ .join('|');
6617
+ /**
6618
+ * Must decode what `getAttribute('src')` decodes, or every check reports an update.
6619
+ *
6620
+ * Not `DOMParser`: its document inherits the page's CSP, and under a nonce `style-src` each `<style>`
6621
+ * in the fetched document, with its fresh nonce, logs a violation.
6622
+ */
6623
+ const readBuildFingerprintFromHtml = (html) => toFingerprint(Array.from(html.replace(HTML_COMMENT, '').matchAll(SCRIPT_SRC), ([, doubleQuoted, singleQuoted, unquoted]) => decodeCharacterReferences(doubleQuoted ?? singleQuoted ?? unquoted ?? '')));
6553
6624
  /**
6554
6625
  * Identifies the build a document boots from, by its entry script filenames. Those carry a content
6555
6626
  * hash in any production build, so two documents share a fingerprint exactly when reloading one would
@@ -6559,32 +6630,17 @@ const isCrossOrigin = (source) => /^(https?:)?\/\//.test(source);
6559
6630
  * Cross-origin scripts are left out: an analytics tag with a cache-busting query would otherwise read
6560
6631
  * as a new deploy on every check.
6561
6632
  */
6562
- const readBuildFingerprint = (document) => Array.from(document.querySelectorAll('script[src]'))
6563
- .map((script) => script.getAttribute('src') ?? '')
6564
- .filter((source) => !!source && !isCrossOrigin(source))
6565
- .sort()
6566
- .join('|');
6633
+ const readBuildFingerprint = (document) => toFingerprint(Array.from(document.querySelectorAll('script[src]'), (script) => script.getAttribute('src') ?? ''));
6567
6634
  /**
6568
- * Reads the currently deployed build's fingerprint from the app's entry document.
6635
+ * Reads the currently deployed build's fingerprint from the app's entry document. Cold: nothing is fetched
6636
+ * until it is subscribed.
6569
6637
  *
6570
6638
  * `null` for anything that is not a usable answer - a network failure, a non-2xx response, or a body
6571
6639
  * with no entry scripts in it. Callers must treat that as "no information" rather than as a change:
6572
6640
  * an error page parses perfectly well as HTML, and acting on its empty fingerprint would reload the
6573
6641
  * app in a loop.
6574
6642
  */
6575
- const fetchDeployedBuildFingerprint = async (url) => {
6576
- try {
6577
- const response = await fetch(url, { cache: 'no-store', headers: { accept: 'text/html' } });
6578
- if (!response.ok) {
6579
- return null;
6580
- }
6581
- const parsed = new DOMParser().parseFromString(await response.text(), 'text/html');
6582
- return readBuildFingerprint(parsed) || null;
6583
- }
6584
- catch {
6585
- return null;
6586
- }
6587
- };
6643
+ const fetchDeployedBuildFingerprint$ = (url) => defer(() => fetch(url, { cache: 'no-store', headers: { accept: 'text/html' } })).pipe(switchMap((response) => (response.ok ? response.text() : of(null))), map((html) => (html === null ? null : readBuildFingerprintFromHtml(html) || null)), catchError(() => of(null)));
6588
6644
 
6589
6645
  /**
6590
6646
  * How each engine words a lazy chunk that could not be loaded. There is no error code and no shared
@@ -6668,19 +6724,15 @@ const APP_UPDATES_DEF = /* @__PURE__ */ defineRootProvider(() => {
6668
6724
  }, /* @ts-ignore */
6669
6725
  ...(ngDevMode ? [{ debugName: "isAvailable" }] : /* istanbul ignore next */ []));
6670
6726
  let lastCheckAt = 0;
6671
- const check = async () => {
6727
+ const check$ = () => defer(() => {
6672
6728
  lastCheckAt = Date.now();
6673
- const deployed = await fetchDeployedBuildFingerprint(config.entryUrl);
6729
+ return fetchDeployedBuildFingerprint$(config.entryUrl);
6730
+ }).pipe(tap((deployed) => {
6674
6731
  if (deployed) {
6675
6732
  _deployedBuild.set(deployed);
6676
6733
  }
6677
- };
6678
- const checkIfDue = async () => {
6679
- if (Date.now() - lastCheckAt < config.minCheckInterval) {
6680
- return;
6681
- }
6682
- await check();
6683
- };
6734
+ }), map(() => undefined));
6735
+ const checkIfDue$ = () => defer(() => (Date.now() - lastCheckAt < config.minCheckInterval ? EMPTY : check$()));
6684
6736
  const reload = () => {
6685
6737
  // The reload is deliberate and the user has already answered for it, so release every
6686
6738
  // beforeunload lock rather than have the browser ask the same question again.
@@ -6728,7 +6780,7 @@ const APP_UPDATES_DEF = /* @__PURE__ */ defineRootProvider(() => {
6728
6780
  if (config.pollInterval > 0) {
6729
6781
  const isDocumentVisible = injectIsDocumentVisible();
6730
6782
  toObservable(isDocumentVisible)
6731
- .pipe(switchMap((isVisible) => (isVisible ? timer(0, config.pollInterval) : EMPTY)), switchMap(() => from(checkIfDue())), takeUntilDestroyed(destroyRef))
6783
+ .pipe(switchMap((isVisible) => (isVisible ? timer(0, config.pollInterval) : EMPTY)), switchMap(() => checkIfDue$()), takeUntilDestroyed(destroyRef))
6732
6784
  .subscribe();
6733
6785
  }
6734
6786
  }
@@ -6736,7 +6788,7 @@ const APP_UPDATES_DEF = /* @__PURE__ */ defineRootProvider(() => {
6736
6788
  isAvailable,
6737
6789
  isRequired: _isRequired.asReadonly(),
6738
6790
  wouldDiscardChanges: coordinator.hasUnsavedChanges,
6739
- check,
6791
+ check$,
6740
6792
  reload,
6741
6793
  };
6742
6794
  }, { name: 'App Updates' });
@@ -6931,12 +6983,10 @@ class ScrollObserverDirective {
6931
6983
  this.isAtEnd = computed(() => this._endIntersection()[0]?.isIntersecting ?? false, /* @ts-ignore */
6932
6984
  ...(ngDevMode ? [{ debugName: "isAtEnd" }] : /* istanbul ignore next */ []));
6933
6985
  }
6934
- /** @internal */
6935
6986
  registerStart(el) {
6936
6987
  this._startEl.set(el);
6937
6988
  return () => this._startEl.update((current) => (current === el ? null : current));
6938
6989
  }
6939
- /** @internal */
6940
6990
  registerEnd(el) {
6941
6991
  this._endEl.set(el);
6942
6992
  return () => this._endEl.update((current) => (current === el ? null : current));
@@ -7229,6 +7279,7 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
7229
7279
  }).pipe(tap((state) => permission.set(state)));
7230
7280
  let nextTagId = 0;
7231
7281
  const clickHandlers = new Map();
7282
+ const confirmedTags = new Set();
7232
7283
  const openCloseFns = new Set();
7233
7284
  let isClickRelayAttached = false;
7234
7285
  const attachClickRelay = () => {
@@ -7250,6 +7301,18 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
7250
7301
  ? // Firefox rejects getNotifications() when the worker is not the one that showed them.
7251
7302
  from(registration.getNotifications(tag ? { tag } : undefined)).pipe(catchError(() => of([])))
7252
7303
  : of([])), tap((notifications) => notifications.forEach((notification) => notification.close())));
7304
+ const pruneDismissedHandlers = (registration) => from(registration.getNotifications()).pipe(catchError(() => of(null)), map((shown) => {
7305
+ if (!shown) {
7306
+ return;
7307
+ }
7308
+ const shownTags = new Set(shown.map((notification) => notification.tag));
7309
+ for (const tag of confirmedTags) {
7310
+ if (!shownTags.has(tag)) {
7311
+ confirmedTags.delete(tag);
7312
+ clickHandlers.delete(tag);
7313
+ }
7314
+ }
7315
+ }));
7253
7316
  const show = (config) => {
7254
7317
  const tag = config.tag ?? `et-notification-${++nextTagId}`;
7255
7318
  const options = toNotificationOptions(config, tag);
@@ -7268,6 +7331,7 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
7268
7331
  closed$.next();
7269
7332
  closed$.complete();
7270
7333
  clickHandlers.delete(tag);
7334
+ confirmedTags.delete(tag);
7271
7335
  openCloseFns.delete(close);
7272
7336
  pageNotification?.close();
7273
7337
  // A no-op unless this notification took the persistent path.
@@ -7301,16 +7365,27 @@ const NOTIFICATIONS_DEF = /* @__PURE__ */ defineRootProvider(() => {
7301
7365
  if (!registration || isClosed) {
7302
7366
  return of('unavailable');
7303
7367
  }
7304
- if (config.onClick) {
7305
- clickHandlers.set(tag, config.onClick);
7306
- attachClickRelay();
7307
- }
7308
- return from(registration.showNotification(config.title, options)).pipe(switchMap(() => isClosed
7309
- ? // Closed while the worker was still putting it up - take back what just appeared.
7310
- closePersistentNotifications(tag).pipe(map(() => 'unavailable'))
7311
- : of('service-worker')), catchError(() => {
7312
- clickHandlers.delete(tag);
7313
- return of('unavailable');
7368
+ return pruneDismissedHandlers(registration).pipe(switchMap(() => {
7369
+ if (isClosed) {
7370
+ return of('unavailable');
7371
+ }
7372
+ if (config.onClick) {
7373
+ clickHandlers.set(tag, config.onClick);
7374
+ attachClickRelay();
7375
+ }
7376
+ return from(registration.showNotification(config.title, options)).pipe(switchMap(() => {
7377
+ if (isClosed) {
7378
+ // Closed while the worker was still putting it up - take back what just appeared.
7379
+ return closePersistentNotifications(tag).pipe(map(() => 'unavailable'));
7380
+ }
7381
+ if (config.onClick) {
7382
+ confirmedTags.add(tag);
7383
+ }
7384
+ return of('service-worker');
7385
+ }), catchError(() => {
7386
+ clickHandlers.delete(tag);
7387
+ return of('unavailable');
7388
+ }));
7314
7389
  }));
7315
7390
  }));
7316
7391
  const shown$ = defer(() => {
@@ -7556,8 +7631,6 @@ const overlayViewportInsetsFor = (element) => overlayViewportInsets(resolveOverl
7556
7631
  /**
7557
7632
  * Runs `listener` whenever a reservation is made or released, so an already positioned overlay can be
7558
7633
  * laid out again. Returns the unsubscribe callback.
7559
- *
7560
- * @internal
7561
7634
  */
7562
7635
  const onOverlayViewportInsetsChange = (listener) => {
7563
7636
  listeners.add(listener);
@@ -7566,10 +7639,6 @@ const onOverlayViewportInsetsChange = (listener) => {
7566
7639
  };
7567
7640
  };
7568
7641
 
7569
- /**
7570
- * Shrinks the host box to the part of the viewport nothing above this overlay has reserved, so a
7571
- * centered or globally placed pane is laid out inside it instead of under a docked panel.
7572
- */
7573
7642
  const applyViewportInsets = (hostElement, renderer) => {
7574
7643
  const insets = overlayViewportInsetsFor(hostElement);
7575
7644
  renderer.setStyle(hostElement, {
@@ -7667,8 +7736,6 @@ let anchoredPositionSetup = null;
7667
7736
  * Installs the anchored positioning implementation. Called by `anchoredOverlayPosition()`, which is
7668
7737
  * the only thing that pulls `@floating-ui/dom` into the bundle - apps that never anchor an overlay
7669
7738
  * do not ship it.
7670
- *
7671
- * @internal
7672
7739
  */
7673
7740
  const registerAnchoredPositionSetup = (setup) => {
7674
7741
  anchoredPositionSetup = setup;
@@ -7700,8 +7767,6 @@ let middlewareExtras = null;
7700
7767
  /**
7701
7768
  * Installs the `size` / `arrow` / `hide` middleware. Called by
7702
7769
  * `enableAnchoredOverlayPositionExtras()`.
7703
- *
7704
- * @internal
7705
7770
  */
7706
7771
  const registerAnchoredPositionMiddlewareExtras = (extras) => {
7707
7772
  middlewareExtras = extras;
@@ -7773,10 +7838,6 @@ const toSideObject = (padding) => typeof padding === 'number'
7773
7838
  bottom: padding.bottom ?? 0,
7774
7839
  left: padding.left ?? 0,
7775
7840
  };
7776
- /**
7777
- * The strategy's own viewport padding plus the space reserved on each edge. Every middleware that
7778
- * measures overflow gets it, so an edge a docked panel covers is as unavailable as the viewport edge.
7779
- */
7780
7841
  const paddingWithInsets = (padding, insets) => {
7781
7842
  const own = toSideObject(padding);
7782
7843
  return {
@@ -7786,6 +7847,43 @@ const paddingWithInsets = (padding, insets) => {
7786
7847
  left: own.left + insets.left,
7787
7848
  };
7788
7849
  };
7850
+ /**
7851
+ * A platform whose first measurement of the pane, in each `computePosition` run, ignores the cap
7852
+ * `size` set in the previous run. `flip` reads that first measurement: against a capped pane, the side
7853
+ * the pane was squeezed onto always fits, so a pane whose content grows after it opened never flips
7854
+ * to the roomier side. Later measurements in the same run see the cap, and `size` resets with them,
7855
+ * so the final position is computed from the pane as it renders.
7856
+ */
7857
+ const platformMeasuringUncappedPaneFirst = (paneElement, renderer) => {
7858
+ let measuredUncapped = false;
7859
+ return {
7860
+ ...platform,
7861
+ getDimensions: (element) => {
7862
+ if (element !== paneElement || measuredUncapped)
7863
+ return platform.getDimensions(element);
7864
+ measuredUncapped = true;
7865
+ const maxWidth = paneElement.style.getPropertyValue('--et-overlay-max-width');
7866
+ const maxHeight = paneElement.style.getPropertyValue('--et-overlay-max-height');
7867
+ if (!maxWidth && !maxHeight)
7868
+ return platform.getDimensions(element);
7869
+ // The uncapped layout clamps the scroll offset of every scroller the cap made scrollable.
7870
+ const scrolled = [...paneElement.querySelectorAll('*')]
7871
+ .filter((el) => el.scrollTop || el.scrollLeft)
7872
+ .map((el) => ({ el, top: el.scrollTop, left: el.scrollLeft }));
7873
+ renderer.setCssProperties(paneElement, { '--et-overlay-max-width': null, '--et-overlay-max-height': null });
7874
+ const dimensions = platform.getDimensions(element);
7875
+ renderer.setCssProperties(paneElement, {
7876
+ '--et-overlay-max-width': maxWidth || null,
7877
+ '--et-overlay-max-height': maxHeight || null,
7878
+ });
7879
+ for (const { el, top, left } of scrolled) {
7880
+ el.scrollTop = top;
7881
+ el.scrollLeft = left;
7882
+ }
7883
+ return dimensions;
7884
+ },
7885
+ };
7886
+ };
7789
7887
  const createAnchoredPositionCleanup = (strategy, paneElement, overlayRef, renderer) => {
7790
7888
  // mirrorWidth needs a real element to measure; virtual references fall back to max-content
7791
7889
  const mirrorWidthElement = strategy.mirrorWidth && isHTMLElement(strategy.referenceElement) ? strategy.referenceElement : null;
@@ -7898,6 +7996,9 @@ const createAnchoredPositionCleanup = (strategy, paneElement, overlayRef, render
7898
7996
  placement: strategy.placement ?? 'bottom',
7899
7997
  strategy: 'absolute',
7900
7998
  middleware,
7999
+ platform: strategy.autoResize && strategy.minAvailableSpace === undefined
8000
+ ? platformMeasuringUncappedPaneFirst(paneElement, renderer)
8001
+ : platform,
7901
8002
  }).then(({ x, y, placement, middlewareData }) => {
7902
8003
  if (destroyed || currentUpdateId !== updateId)
7903
8004
  return;
@@ -7975,7 +8076,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
7975
8076
  elements,
7976
8077
  state: _state.asReadonly(),
7977
8078
  componentInstance: _componentInstance.asReadonly(),
7978
- /** @internal */
7979
8079
  beforeOpenedSubject,
7980
8080
  close(result, source = 'api') {
7981
8081
  if (_state() === 'closing' || _state() === 'closed') {
@@ -8016,11 +8116,9 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8016
8116
  afterClosed() {
8017
8117
  return afterClosedSubject.asObservable();
8018
8118
  },
8019
- /** @internal */
8020
8119
  attachComponentRef(componentRef) {
8021
8120
  _componentInstance.set(componentRef.instance);
8022
8121
  },
8023
- /** @internal */
8024
8122
  attachPositionUpdater(updater) {
8025
8123
  positionUpdater = updater;
8026
8124
  },
@@ -8030,7 +8128,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8030
8128
  }
8031
8129
  positionUpdater?.(strategy);
8032
8130
  },
8033
- /** @internal */
8034
8131
  attachBackdropUpdater(updater) {
8035
8132
  backdropUpdater = updater;
8036
8133
  },
@@ -8044,7 +8141,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8044
8141
  }
8045
8142
  backdropUpdater?.(hasBackdrop);
8046
8143
  },
8047
- /** @internal */
8048
8144
  markOpened() {
8049
8145
  if (_state() !== 'mounting') {
8050
8146
  return;
@@ -8053,7 +8149,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8053
8149
  afterOpenedSubject.next();
8054
8150
  afterOpenedSubject.complete();
8055
8151
  },
8056
- /** @internal */
8057
8152
  beginClose(closeEvent) {
8058
8153
  if (_state() === 'closing' || _state() === 'closed') {
8059
8154
  return false;
@@ -8063,7 +8158,6 @@ const createOverlayRuntimeRef = (id, config, elements, requestClose) => {
8063
8158
  beforeClosedSubject.complete();
8064
8159
  return true;
8065
8160
  },
8066
- /** @internal */
8067
8161
  finishClose(closeEvent) {
8068
8162
  if (_state() === 'closed') {
8069
8163
  return;
@@ -8210,8 +8304,6 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8210
8304
  paneElement,
8211
8305
  }, (result, source) => beginClose({ result, source }));
8212
8306
  animationDebugLog(`runtime ${config.id}`, 'mount');
8213
- // matches the previous overlay behavior: escape/outside-pointer closes are
8214
- // ignored until the enter transition has started plus one frame
8215
8307
  let interactiveCloseReady = false;
8216
8308
  const cleanupFns = [];
8217
8309
  const parentInjector = config.injector ?? config.viewContainerRef?.injector ?? environmentInjector;
@@ -8225,12 +8317,22 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8225
8317
  // `config.providers`.
8226
8318
  providers: [{ provide: EnvironmentInjector, useValue: overlayEnvironmentInjector }],
8227
8319
  });
8228
- const componentRef = createComponent(config.component, {
8229
- environmentInjector: overlayEnvironmentInjector,
8230
- elementInjector,
8231
- hostElement: paneElement,
8232
- bindings: config.bindings ?? [],
8233
- });
8320
+ let componentRef;
8321
+ try {
8322
+ componentRef = createComponent(config.component, {
8323
+ environmentInjector: overlayEnvironmentInjector,
8324
+ elementInjector,
8325
+ hostElement: paneElement,
8326
+ bindings: config.bindings ?? [],
8327
+ });
8328
+ }
8329
+ catch (error) {
8330
+ elementInjector.destroy();
8331
+ overlayEnvironmentInjector.destroy();
8332
+ renderer.removeChild(root, hostElement);
8333
+ maybeDestroyRootElements(targetDocument);
8334
+ throw error;
8335
+ }
8234
8336
  // pane classes are applied after component creation - Angular replaces the host
8235
8337
  // element's class attribute with the component's static host class on creation
8236
8338
  renderer.addClass(paneElement, 'et-overlay-runtime-pane', ...(config.paneClass ?? []));
@@ -8325,10 +8427,8 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8325
8427
  lifecycle.leave();
8326
8428
  }
8327
8429
  lifecycle.state$
8328
- .pipe(filter((state) => state === 'left'), take(1))
8329
- .subscribe(() => {
8330
- destroyMountedOverlay(closeEvent);
8331
- });
8430
+ .pipe(filter((state) => state === 'left'), take(1), tap(() => destroyMountedOverlay(closeEvent)))
8431
+ .subscribe();
8332
8432
  };
8333
8433
  const closeOnEscape = config.closeOnEscape ?? true;
8334
8434
  if (closeOnEscape) {
@@ -8396,12 +8496,12 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8396
8496
  }
8397
8497
  animationDebugLog(`runtime ${config.id}`, `enter (delegate ${config.animationDelegate?.enter ? 'yes' : 'no'}, lifecycle state "${lifecycle.state$.value}")`);
8398
8498
  const readySubscription = lifecycle.state$
8399
- .pipe(filter((state) => state === 'entering' || state === 'entered'), take(1))
8400
- .subscribe(() => {
8499
+ .pipe(filter((state) => state === 'entering' || state === 'entered'), take(1), tap(() => {
8401
8500
  nextFrame(() => {
8402
8501
  interactiveCloseReady = true;
8403
8502
  });
8404
- });
8503
+ }))
8504
+ .subscribe();
8405
8505
  cleanupFns.push(() => readySubscription.unsubscribe());
8406
8506
  if (config.animationDelegate?.enter) {
8407
8507
  config.animationDelegate.enter({ lifecycle, elements: overlayRef.elements });
@@ -8410,11 +8510,11 @@ const OVERLAY_RUNTIME_DEF = /* @__PURE__ */ defineRootProvider(() => {
8410
8510
  lifecycle.enter();
8411
8511
  }
8412
8512
  lifecycle.state$
8413
- .pipe(filter((state) => state === 'entered'), take(1))
8414
- .subscribe(() => {
8513
+ .pipe(filter((state) => state === 'entered'), take(1), tap(() => {
8415
8514
  applyInitialFocus(paneElement, autoFocus, targetDocument);
8416
8515
  overlayRef.markOpened();
8417
- });
8516
+ }))
8517
+ .subscribe();
8418
8518
  });
8419
8519
  return overlayRef;
8420
8520
  };
@@ -9485,11 +9585,9 @@ class ProvideSurfaceDirective {
9485
9585
  this.clearForcedSurface();
9486
9586
  }
9487
9587
  }
9488
- /** @internal */
9489
9588
  forceSurface(surface) {
9490
9589
  this.forcedSurface.set(surface);
9491
9590
  }
9492
- /** @internal */
9493
9591
  clearForcedSurface() {
9494
9592
  if (this.forcedSurface() === FORCED_SURFACE_UNSET) {
9495
9593
  return;
@@ -9586,15 +9684,9 @@ class AutoSurfaceDirective {
9586
9684
  */
9587
9685
  this.surfaceProvider = input(null, /* @ts-ignore */
9588
9686
  ...(ngDevMode ? [{ debugName: "surfaceProvider" }] : /* istanbul ignore next */ []));
9589
- // An overlay *panel* that IS its overlay's own painted surface opts in (see
9590
- // matchOverlaySurface) so it adopts the overlay's registered elevation exactly,
9591
- // rather than layering one level above a parent surface.
9592
9687
  this.isOverlaySurface = signal(false, /* @ts-ignore */
9593
9688
  ...(ngDevMode ? [{ debugName: "isOverlaySurface" }] : /* istanbul ignore next */ []));
9594
- // Bumped by the afterEveryRender watcher in the constructor whenever this element's overlay
9595
- // containment changes. surfaceForElement() below does a non-reactive element.contains() read,
9596
- // so the computed must be re-triggered when this element is (re)grafted into an overlay pane -
9597
- // otherwise a value cached from before the graft (one elevation too low) sticks forever.
9689
+ // surfaceForElement() reads the DOM non-reactively; bumping this re-runs resolvedSurface after a re-graft.
9598
9690
  this.domSettleTick = signal(0, /* @ts-ignore */
9599
9691
  ...(ngDevMode ? [{ debugName: "domSettleTick" }] : /* istanbul ignore next */ []));
9600
9692
  this.resolvedSurface = computed(() => {
@@ -9602,40 +9694,25 @@ class AutoSurfaceDirective {
9602
9694
  if (!themes) {
9603
9695
  return null;
9604
9696
  }
9605
- // Establish a dependency on the DOM-settle tick so the non-reactive containment read below
9606
- // is re-evaluated after the element is grafted into its final overlay pane.
9607
9697
  this.domSettleTick();
9608
9698
  const explicitProvider = this.surfaceProvider();
9609
9699
  const contextTheme = explicitProvider ? explicitProvider.activeTheme() : this.parentSurface();
9610
9700
  const contextElevation = contextTheme?.elevation ?? null;
9611
9701
  const contextType = contextTheme?.type ?? null;
9612
- // An overlay's projected/portaled content keeps the injector of where it was
9613
- // *declared* (the trigger location), not the pane it renders into - so the
9614
- // injector-derived parent surface is one elevation too low. The surface-context
9615
- // tracker records each open overlay's surface *and* its pane element, so we consult
9616
- // only the overlay whose pane actually contains this element in the DOM (portaling
9617
- // moves the DOM into the pane even though the injector stays at the trigger). An
9618
- // auto-surface on the base page therefore stays put when an overlay opens elsewhere.
9702
+ // Portaled content keeps the trigger's injector, so only the pane that contains this element in the DOM counts.
9619
9703
  const overlaySurface = this.surfaceContextTracker.surfaceForElement(this.elementRef.nativeElement);
9620
9704
  const overlayElevation = overlaySurface?.elevation ?? null;
9621
9705
  const overlayType = overlaySurface?.type ?? null;
9622
- // A panel that IS its overlay's own surface (see matchOverlaySurface) must paint exactly the
9623
- // overlay's elevation: the overlay container already resolved it (one above the trigger) and
9624
- // registered it in the tracker. It must NOT re-derive from its declaration injector - that
9625
- // points back at the trigger location and disagrees with the pane whenever the trigger itself
9626
- // sits on a surface, which would double-elevate the panel relative to its own pane.
9706
+ // Must not re-derive from the declaration injector: it points at the trigger and would double-elevate the panel.
9627
9707
  if (this.isOverlaySurface()) {
9628
9708
  if (overlayElevation !== null) {
9629
9709
  return resolveSurfaceByElevation(themes, overlayType ?? contextType ?? 'dark', overlayElevation)?.name ?? null;
9630
9710
  }
9631
- // Rendered outside any tracked overlay: fall back to the injector context unchanged.
9632
9711
  if (contextElevation !== null) {
9633
9712
  return resolveSurfaceByElevation(themes, contextType ?? 'dark', contextElevation)?.name ?? null;
9634
9713
  }
9635
9714
  return null;
9636
9715
  }
9637
- // A regular auto-surface sits one elevation *above* the deeper of its injector context and the
9638
- // overlay pane it renders into (content painted onto the surface it lives on).
9639
9716
  let parentElevation;
9640
9717
  let parentType;
9641
9718
  if (overlayElevation !== null && (contextElevation === null || overlayElevation > contextElevation)) {
@@ -9652,14 +9729,8 @@ class AutoSurfaceDirective {
9652
9729
  return resolveSurfaceByElevation(themes, parentType, parentElevation + 1)?.name ?? null;
9653
9730
  }, /* @ts-ignore */
9654
9731
  ...(ngDevMode ? [{ debugName: "resolvedSurface" }] : /* istanbul ignore next */ []));
9655
- // Projected/portaled auto-surface content (e.g. a select option's avatar) can have its
9656
- // resolvedSurface computed run *before* it is grafted into the overlay pane it visually lives
9657
- // in - or while it is briefly mounted in an off-pane measuring container, as windowed lists do.
9658
- // surfaceForElement()'s element.contains() check then returns the wrong (or no) overlay, so it
9659
- // resolves off the declaration injector (the trigger location) one elevation too low. That check
9660
- // is a plain DOM read, not a signal, so nothing re-runs the computed once the element reaches its
9661
- // final pane. Watch the tracker result across renders and re-trigger the computed whenever the
9662
- // element's overlay containment changes, settling (and unsubscribing) once it stops moving.
9732
+ // The containment check is a plain DOM read, so nothing re-runs resolvedSurface once the element
9733
+ // reaches its final pane (windowed lists mount it in an off-pane container first). Watch it across renders.
9663
9734
  let lastElevation = this.surfaceContextTracker.surfaceForElement(this.elementRef.nativeElement)?.elevation ?? null;
9664
9735
  let stableRenders = 0;
9665
9736
  const settleWatcher = afterEveryRender(() => {
@@ -9671,7 +9742,6 @@ class AutoSurfaceDirective {
9671
9742
  this.domSettleTick.update((v) => v + 1);
9672
9743
  }
9673
9744
  else if (el.isConnected && ++stableRenders >= 2) {
9674
- // containment has held for a couple of renders and the element is in the DOM - done moving
9675
9745
  settleWatcher.destroy();
9676
9746
  }
9677
9747
  });
@@ -9986,8 +10056,6 @@ const COLOR_PROVIDER = new InjectionToken('ColorProvider');
9986
10056
  * it through element DI, so overlay containers fall back to this when resolving the provider to
9987
10057
  * `syncWithProvider()` - resolve at sync time, not at injection time, since root components only
9988
10058
  * register with `ApplicationRef` once bootstrap completes.
9989
- *
9990
- * @internal
9991
10059
  */
9992
10060
  const resolveAppRootColorProvider = (appRef) => {
9993
10061
  for (const componentRef of appRef.components) {
@@ -10040,7 +10108,7 @@ class ProvideColorDirective {
10040
10108
  return SURFACE_COLOR;
10041
10109
  if (!this.themes || !value)
10042
10110
  return;
10043
- if (isDevMode() && !this.themes.some((theme) => theme.name === value) && value !== null) {
10111
+ if (isDevMode() && !this.themes.some((theme) => theme.name === value)) {
10044
10112
  console.error(`Theme ${value} does not exist. Please make sure to add it to provideColorThemesWithTailwind4()`);
10045
10113
  }
10046
10114
  return createCssColorThemeName(value);
@@ -10079,11 +10147,9 @@ class ProvideColorDirective {
10079
10147
  this.clearForcedColor();
10080
10148
  }
10081
10149
  }
10082
- /** @internal */
10083
10150
  forceColor(color) {
10084
10151
  this.forcedColor.set(color);
10085
10152
  }
10086
- /** @internal */
10087
10153
  clearForcedColor() {
10088
10154
  if (this.forcedColor() === FORCED_COLOR_UNSET) {
10089
10155
  return;
@@ -10144,11 +10210,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImpor
10144
10210
 
10145
10211
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
10146
10212
  /** The version of `@ethlete/core` this build was cut from. */
10147
- const CORE_VERSION = '5.0.0-next.60';
10213
+ const CORE_VERSION = '5.0.0-next.61';
10148
10214
 
10149
10215
  /**
10150
10216
  * Generated bundle index. Do not edit.
10151
10217
  */
10152
10218
 
10153
- export { ANIMATABLE_TOKEN, ANIMATED_IF_TOKEN, ANIMATED_LIFECYCLE_TOKEN, ANIMATION_DEBUG_STORAGE_KEY, AT_LEAST_ONE_REQUIRED, AnimatableDirective, AnimatedIfDirective, AnimatedLifecycleDirective, AutoSurfaceDirective, BOUNDARY_ELEMENT_TOKEN, BREAKPOINT_INSTANCE_TOKEN, BREAKPOINT_ORDER, COLOR_PALETTE, COLOR_PROVIDER, CORE_VERSION, ClickOutsideDirective, ColorInteractiveContainerDirective, ColorInteractiveDirective, ColorInteractiveExcludeDirective, ColorInteractiveHasFocusDirective, DEFAULT_MULTI_INSTANCE_RELS, DEFAULT_MULTI_INSTANCE_TAGS, DEFAULT_OVERLAY_LAYER, DEFAULT_VIEWPORT_CONFIG, DISABLE_LOGGER_PARAM, DragHandleDirective, ET_DISABLE_SCROLL_TOP, ET_DISABLE_SCROLL_TOP_AS_RETURN_ROUTE, ET_DISABLE_SCROLL_TOP_ON_PATH_PARAM_CHANGE, ET_PROPERTY_REMOVED, ET_RESTORE_SCROLL, EasingTokensStylesComponent, EthleteValidators, FOCUSABLE_SELECTOR, FOCUS_VISIBLE_TRACKER_TOKEN, HtmlToMarkdownPipe, IS_ARRAY_NOT_EMPTY, IS_EMAIL, InferMimeTypePipe, InitialsPipe, IsArrayNotEmpty, IsEmail, jsonLd as JsonLD, KeyPressManager, MUST_MATCH, MarkdownToHtmlPipe, MatchStateType, MustMatch, NOTIFICATION_CLICK_MESSAGE_TYPE, NormalizeGameResultTypePipe, NormalizeMatchParticipantsPipe, NormalizeMatchScorePipe, NormalizeMatchStatePipe, NormalizeMatchTypePipe, OVERLAY_LAYER_ATTRIBUTE, ProvideColorDirective, ProvideSurfaceDirective, RUNTIME_ERROR_NO_DATA, RepeatDirective, ResizeHandlesComponent, RestoreScrollDirective, RuntimeError, SURFACE_COLOR, SURFACE_PROVIDER, SURFACE_TYPE, SWIPE_VELOCITY_WINDOW_MS, ScrollObserverDirective, ScrollObserverEndDirective, ScrollObserverStartDirective, SlugifyPipe, StructuredDataComponent, SurfaceInteractiveDirective, ToArrayPipe, TypedQueryList, UNSAVED_CHANGES_TITLE_MARKER, ValidateAtLeastOneRequired, anchoredOverlayPosition, animationDebugLog, applyAlternateBinding, applyAlternateLanguagesBindings, applyArticleBindings, applyAuthorBinding, applyCanonicalBinding, applyCenteredPosition, applyDescriptionBinding, applyFaviconOverlay, applyGlobalPosition, applyHeadBinding, applyHeadTitleBinding, applyHeadTitleMarker, applyHostListener, applyHostListeners, applyInitialFocus, applyKeywordsBinding, applyLinkBinding, applyMetaBinding, applyNextBinding, applyOgBinding, applyOpenGraphBindings, applyPrevBinding, applyResourceHintsBindings, applyRobotsBinding, applySocialMediaBindings, applyStructuredDataBinding, applyTwitterCardBindings, boolBreakpointTransform, boundingClientRectToElementRect, breakpointTransformBase, buildElementSignal, buildSignalEffects, canUseSessionMemory, clamp, clone, cloneFormGroup, computedTillFalsy, computedTillTruthy, controlValueSignal, controlValueSignalWithPrevious, copyToClipboard, createAnchoredPositionCleanup, createArrayPropertyBinding, createAutoSessionMemoryKey, createCanAnimateSignal, createComponentId, createCssColorThemeName, createCssSurfaceName, createDestroy, createDocumentElementSignal, createElementDimensions, createEmptyElementSignal, createFlipAnimation, createFlipAnimationGroup, createIsRenderedSignal, createLogger, createObjectUrlHandle, createOverlayRuntimeRef, createPropertyBinding, createRootThemeCss, createRoute, createRouterState, createRxHostListener, createSessionMemory, createSwatchCss, createSwipeTracker, createTailwindColorThemes, createTailwindCssVar, createTailwindRgbVar, createThemeStyle, createUnsavedChangesGuard, createUnsavedChangesTabLock, createUnsavedChangesTracker, createUserConsentProvider, deferredSignal, defineLabels, defineProvider, defineRootProvider, defineStaticProvider, defineStaticRootProvider, deleteCookie, dragGestureFrom, easeElastic, easeIn, easeInOut, easeLinear, easeOut, easeOutBack, easeOutBackStrong, elementCanScroll, enableAnchoredOverlayPositionExtras, equal, fetchDeployedBuildFingerprint, firstElementSignal, focusElement, forceReflow, formatRuntimeError, fromNextFrame, getCookie, getDomain, getElementScrollCoordinates, getFocusableElements, getFormGroupValue, getGroupMatchPoints, getGroupMatchScore, getHeadingElement, getKnockoutMatchScore, getMatchScoreSubLine, getObjectProperty, getScrollContainerTarget, getScrollItemTarget, getScrollSnapTarget, hasCookie, holdScrollRestoration, htmlToMarkdown, inferMimeType, initials, injectAngularRootElement, injectAnimatedBlockSize, injectAppUpdates, injectAppUpdatesConfig, injectBoundaryElement, injectBreakpointInput, injectBreakpointIsMatched, injectBreakpointObserver, injectCanHover, injectColorPalette, injectColorThemes, injectColorThemesPrefix, injectCurrentBreakpoint, injectDefaultColorTheme, injectDefaultSurfaceTheme, injectDeviceInputType, injectDisplayOrientation, injectErrorTheme, injectFaviconStore, injectFileDownload, injectFocusVisibleTracker, injectFragment, injectHasPrecisionInput, injectHasTouchInput, injectHostElement, injectIs2Xl, injectIsDocumentVisible, injectIsLandscape, injectIsLg, injectIsMd, injectIsPortrait, injectIsRouterInitialized, injectIsSm, injectIsXl, injectIsXs, injectLinkStore, injectLinkStoreConfig, injectLocale, injectMediaQueryIsMatched, injectMetaConfig, injectMetaStore, injectNotifications, injectObserveBreakpoint, injectObserveMediaQuery, injectOverlayRuntime, injectParentSurface, injectPathParam, injectPathParamChanges, injectPathParams, injectPrefersReducedMotion, injectQueryParam, injectQueryParamChanges, injectQueryParams, injectRenderer, injectRoute, injectRouteData, injectRouteDataItem, injectRouteTitle, injectRouterEvent, injectRouterNavigationState, injectRouterState, injectScrollbarDimensions, injectStructuredDataConfig, injectStructuredDataStore, injectStyleManager, injectSuccessTheme, injectSurfaceContextTracker, injectSurfaceThemes, injectSurfaceThemesPrefix, injectSurfaceType, injectTemplateRef, injectTitleConfig, injectTitleStore, injectUnsavedChangesCoordinator, injectUrl, injectViewportConfig, injectViewportDimensions, injectViewportSize, injectWarningTheme, isAnimationDebugEnabled, isArray, isElement, isElementSignal, isElementVisible, isFocusable, isGroupMatch, isHTMLElement, isHTMLOrSVGElement, isKnockoutMatch, isObject, isOnHigherOverlayLayer, isStaleBuildError, markdownToHtml, matchesReducedMotion, maybeSignalValue, memoizeSignal, mountEasingTokens, nextFrame, normalizeGameResultType, normalizeMatchParticipant, normalizeMatchParticipants, normalizeMatchScore, normalizeMatchState, normalizeMatchType, normalizeUnsavedChangesSource, numberBreakpointTransform, onOverlayViewportInsetsChange, overlayViewportInsets, overlayViewportInsetsFor, ownsActiveElement, previousSignalValue, provideAppUpdates, provideAppUpdatesConfig, provideAppUpdatesInstance, provideBoundaryElement, provideBreakpointInstance, provideBreakpointObserver, provideColorPalette, provideColorThemes, provideColorThemesWithTailwind4, provideFaviconStore, provideFocusVisibleTracker, provideLinkStore, provideLinkStoreConfig, provideLocale, provideMetaConfig, provideMetaStore, provideNotifications, provideOverlayRuntime, provideRenderer, provideStructuredDataConfig, provideStructuredDataStore, provideStyleManager, provideSurfaceContextTracker, provideSurfaceThemesWithTailwind4, provideTitleConfig, provideTitleStore, provideUnsavedChangesCoordinator, provideViewportConfig, randomId, readBuildFingerprint, readFromClipboard, registerAnchoredPositionMiddlewareExtras, registerAnchoredPositionSetup, reserveOverlayViewportSpace, resetPositioningStyles, resolveAppRootColorProvider, resolveOverlayLayer, resolveSurfaceByElevation, round, routerDisableScrollTop, routerRestoreScroll, scrollToElement, setBackdropStyles, setBaseElementStyles, setCookie, setInputSignal, setupFocusTrap, setupPositioning, setupScrollRestoration, signalAnimatedNumber, signalAttributes, signalClasses, signalCountdown, signalDeferredLoading, signalElementChildren, signalElementDimensions, signalElementIntersection, signalElementLastScrollDirection, signalElementMutations, signalElementScrollState, signalHostAttributes, signalHostClasses, signalHostElementDimensions, signalHostElementIntersection, signalHostElementLastScrollDirection, signalHostElementMutations, signalHostElementScrollState, signalHostStyles, signalIsRendered, signalStyles, slugify, suppressTextSelection, switchQueryListChanges, syncSignal, toArray, toArrayTrackByFn, toInjectFn, toProvideFn, toStringBinding, toToken, transformOrReturn, typedBreakpointTransform, useCursorDragScroll, writeScrollbarSizeToCssVariables, writeViewportSizeToCssVariables, ɵProvideColorThemes, ɵProvideColorThemesPrefix, ɵProvideSurfaceThemes, ɵProvideSurfaceThemesPrefix };
10219
+ export { ANIMATABLE_TOKEN, ANIMATED_IF_TOKEN, ANIMATED_LIFECYCLE_TOKEN, ANIMATION_DEBUG_STORAGE_KEY, AT_LEAST_ONE_REQUIRED, AnimatableDirective, AnimatedIfDirective, AnimatedLifecycleDirective, AutoSurfaceDirective, BOUNDARY_ELEMENT_TOKEN, BREAKPOINT_INSTANCE_TOKEN, BREAKPOINT_ORDER, COLOR_PALETTE, COLOR_PROVIDER, CORE_VERSION, ClickOutsideDirective, ColorInteractiveContainerDirective, ColorInteractiveDirective, ColorInteractiveExcludeDirective, ColorInteractiveHasFocusDirective, DEFAULT_MULTI_INSTANCE_RELS, DEFAULT_MULTI_INSTANCE_TAGS, DEFAULT_OVERLAY_LAYER, DEFAULT_VIEWPORT_CONFIG, DISABLE_LOGGER_PARAM, DragHandleDirective, ET_DISABLE_SCROLL_TOP, ET_DISABLE_SCROLL_TOP_AS_RETURN_ROUTE, ET_DISABLE_SCROLL_TOP_ON_PATH_PARAM_CHANGE, ET_PROPERTY_REMOVED, ET_RESTORE_SCROLL, EasingTokensStylesComponent, EthleteValidators, FOCUSABLE_SELECTOR, FOCUS_VISIBLE_TRACKER_TOKEN, HtmlToMarkdownPipe, IS_ARRAY_NOT_EMPTY, IS_EMAIL, InferMimeTypePipe, InitialsPipe, IsArrayNotEmpty, IsEmail, jsonLd as JsonLD, KeyPressManager, MARKDOWN_VERBATIM_ATTR, MUST_MATCH, MarkdownToHtmlPipe, MatchStateType, MustMatch, NOTIFICATION_CLICK_MESSAGE_TYPE, NormalizeGameResultTypePipe, NormalizeMatchParticipantsPipe, NormalizeMatchScorePipe, NormalizeMatchStatePipe, NormalizeMatchTypePipe, OVERLAY_LAYER_ATTRIBUTE, ProvideColorDirective, ProvideSurfaceDirective, RUNTIME_ERROR_NO_DATA, RepeatDirective, ResizeHandlesComponent, RestoreScrollDirective, RuntimeError, SURFACE_COLOR, SURFACE_PROVIDER, SURFACE_TYPE, SWIPE_VELOCITY_WINDOW_MS, ScrollObserverDirective, ScrollObserverEndDirective, ScrollObserverStartDirective, SlugifyPipe, StructuredDataComponent, SurfaceInteractiveDirective, ToArrayPipe, TypedQueryList, UNSAVED_CHANGES_TITLE_MARKER, ValidateAtLeastOneRequired, anchoredOverlayPosition, animationDebugLog, applyAlternateBinding, applyAlternateLanguagesBindings, applyArticleBindings, applyAuthorBinding, applyCanonicalBinding, applyCenteredPosition, applyDescriptionBinding, applyFaviconOverlay, applyGlobalPosition, applyHeadBinding, applyHeadTitleBinding, applyHeadTitleMarker, applyHostListener, applyHostListeners, applyInitialFocus, applyKeywordsBinding, applyLinkBinding, applyMetaBinding, applyNextBinding, applyOgBinding, applyOpenGraphBindings, applyPrevBinding, applyResourceHintsBindings, applyRobotsBinding, applySocialMediaBindings, applyStructuredDataBinding, applyTwitterCardBindings, boolBreakpointTransform, boundingClientRectToElementRect, breakpointTransformBase, buildElementSignal, buildSignalEffects, canUseSessionMemory, clamp, clone, cloneFormGroup, computedTillFalsy, computedTillTruthy, controlValueSignal, controlValueSignalWithPrevious, copyToClipboard, createAnchoredPositionCleanup, createArrayPropertyBinding, createAutoSessionMemoryKey, createCanAnimateSignal, createComponentId, createCssColorThemeName, createCssSurfaceName, createDestroy, createDocumentElementSignal, createElementDimensions, createEmptyElementSignal, createFlipAnimation, createFlipAnimationGroup, createIsRenderedSignal, createLogger, createObjectUrlHandle, createOverlayRuntimeRef, createPropertyBinding, createRootThemeCss, createRoute, createRouterState, createRxHostListener, createSessionMemory, createSwatchCss, createSwipeTracker, createTailwindColorThemes, createTailwindCssVar, createTailwindRgbVar, createThemeStyle, createUnsavedChangesGuard, createUnsavedChangesTabLock, createUnsavedChangesTracker, createUserConsentProvider, deferredSignal, defineLabels, defineProvider, defineRootProvider, defineStaticProvider, defineStaticRootProvider, deleteCookie, dragGestureFrom, easeElastic, easeIn, easeInOut, easeLinear, easeOut, easeOutBack, easeOutBackStrong, elementCanScroll, enableAnchoredOverlayPositionExtras, equal, fetchDeployedBuildFingerprint$, firstElementSignal, focusElement, forceReflow, formatRuntimeError, fromNextFrame, getCookie, getDomain, getElementScrollCoordinates, getFocusableElements, getFormGroupValue, getGroupMatchPoints, getGroupMatchScore, getHeadingElement, getKnockoutMatchScore, getMatchScoreSubLine, getObjectProperty, getScrollContainerTarget, getScrollItemTarget, getScrollSnapTarget, hasCookie, holdScrollRestoration, htmlToMarkdown, inferMimeType, initials, injectAngularRootElement, injectAnimatedBlockSize, injectAppUpdates, injectAppUpdatesConfig, injectBoundaryElement, injectBreakpointInput, injectBreakpointIsMatched, injectBreakpointObserver, injectCanHover, injectColorPalette, injectColorThemes, injectColorThemesPrefix, injectCurrentBreakpoint, injectDefaultColorTheme, injectDefaultSurfaceTheme, injectDeviceInputType, injectDisplayOrientation, injectErrorTheme, injectFaviconStore, injectFileDownload, injectFocusVisibleTracker, injectFragment, injectHasPrecisionInput, injectHasTouchInput, injectHostElement, injectIs2Xl, injectIsDocumentVisible, injectIsLandscape, injectIsLg, injectIsMd, injectIsPortrait, injectIsRouterInitialized, injectIsSm, injectIsXl, injectIsXs, injectLinkStore, injectLinkStoreConfig, injectLocale, injectMediaQueryIsMatched, injectMetaConfig, injectMetaStore, injectNotifications, injectObserveBreakpoint, injectObserveMediaQuery, injectOverlayRuntime, injectParentSurface, injectPathParam, injectPathParamChanges, injectPathParams, injectPrefersReducedMotion, injectQueryParam, injectQueryParamChanges, injectQueryParams, injectRenderer, injectRoute, injectRouteData, injectRouteDataItem, injectRouteTitle, injectRouterEvent, injectRouterNavigationState, injectRouterState, injectScrollbarDimensions, injectStructuredDataConfig, injectStructuredDataStore, injectStyleManager, injectSuccessTheme, injectSurfaceContextTracker, injectSurfaceThemes, injectSurfaceThemesPrefix, injectSurfaceType, injectTemplateRef, injectTitleConfig, injectTitleStore, injectUnsavedChangesCoordinator, injectUrl, injectViewportConfig, injectViewportDimensions, injectViewportSize, injectWarningTheme, isAnimationDebugEnabled, isArray, isElement, isElementSignal, isElementVisible, isFocusable, isGroupMatch, isHTMLElement, isHTMLOrSVGElement, isKnockoutMatch, isObject, isOnHigherOverlayLayer, isSafeLinkUrl, isSafeUrl, isStaleBuildError, markdownToHtml, matchesReducedMotion, maybeSignalValue, memoizeSignal, mountEasingTokens, nextFrame, normalizeGameResultType, normalizeMatchParticipant, normalizeMatchParticipants, normalizeMatchScore, normalizeMatchState, normalizeMatchType, normalizeUnsavedChangesSource, numberBreakpointTransform, onOverlayViewportInsetsChange, overlayViewportInsets, overlayViewportInsetsFor, ownsActiveElement, previousSignalValue, provideAppUpdates, provideAppUpdatesConfig, provideAppUpdatesInstance, provideBoundaryElement, provideBreakpointInstance, provideBreakpointObserver, provideColorPalette, provideColorThemes, provideColorThemesWithTailwind4, provideFaviconStore, provideFocusVisibleTracker, provideLinkStore, provideLinkStoreConfig, provideLocale, provideMetaConfig, provideMetaStore, provideNotifications, provideOverlayRuntime, provideRenderer, provideStructuredDataConfig, provideStructuredDataStore, provideStyleManager, provideSurfaceContextTracker, provideSurfaceThemesWithTailwind4, provideTitleConfig, provideTitleStore, provideUnsavedChangesCoordinator, provideViewportConfig, randomId, readBuildFingerprint, readFromClipboard, registerAnchoredPositionMiddlewareExtras, registerAnchoredPositionSetup, reserveOverlayViewportSpace, resetPositioningStyles, resolveAppRootColorProvider, resolveOverlayLayer, resolveSurfaceByElevation, round, routerDisableScrollTop, routerRestoreScroll, scrollToElement, setBackdropStyles, setBaseElementStyles, setCookie, setInputSignal, setupFocusTrap, setupPositioning, setupScrollRestoration, signalAnimatedNumber, signalAttributes, signalClasses, signalCountdown, signalDeferredLoading, signalElementChildren, signalElementDimensions, signalElementIntersection, signalElementLastScrollDirection, signalElementMutations, signalElementScrollState, signalHostAttributes, signalHostClasses, signalHostElementDimensions, signalHostElementIntersection, signalHostElementLastScrollDirection, signalHostElementMutations, signalHostElementScrollState, signalHostStyles, signalIsRendered, signalStyles, slugify, suppressTextSelection, switchQueryListChanges, syncSignal, toArray, toArrayTrackByFn, toInjectFn, toProvideFn, toStringBinding, toToken, transformOrReturn, typedBreakpointTransform, useCursorDragScroll, writeScrollbarSizeToCssVariables, writeViewportSizeToCssVariables, ɵProvideColorThemes, ɵProvideColorThemesPrefix, ɵProvideSurfaceThemes, ɵProvideSurfaceThemesPrefix };
10154
10220
  //# sourceMappingURL=ethlete-core.mjs.map