@tilde-nlp/ngx-common 8.1.32 → 8.1.34

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, EventEmitter, Component, Input, ViewChild, Output, NgModule, Optional, Inject, Pipe, inject, Directive, HostListener, HostBinding, ViewChildren, input, effect, ElementRef, ContentChild, ContentChildren, InjectionToken, signal, output, computed } from '@angular/core';
2
+ import { Injectable, EventEmitter, Component, Input, ViewChild, Output, NgModule, Optional, Inject, Pipe, inject, Directive, HostListener, HostBinding, ViewChildren, input, effect, ElementRef, ContentChild, ContentChildren, InjectionToken, signal, output, computed, makeEnvironmentProviders } from '@angular/core';
3
3
  import * as i2 from '@angular/material/icon';
4
4
  import { MatIconModule, MatIcon } from '@angular/material/icon';
5
5
  import * as i2$1 from '@angular/platform-browser';
@@ -3132,7 +3132,7 @@ class FileUploadComponent {
3132
3132
  } if (rf & 2) {
3133
3133
  let _t;
3134
3134
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.fileInput = _t.first);
3135
- } }, inputs: { accept: "accept", useCompactUpload: "useCompactUpload", maxSize: "maxSize", multiple: "multiple", filePreviewProgress: "filePreviewProgress", allowEmpty: "allowEmpty", disabled: "disabled", dragZoneLabel: "dragZoneLabel", uploadIconName: "uploadIconName", uploadButtonLabel: "uploadButtonLabel", openExtensionPopupLabel: "openExtensionPopupLabel", minSupportedFormatCount: "minSupportedFormatCount", hideDefaultLimitsLabel: "hideDefaultLimitsLabel", extendedAcceptList: "extendedAcceptList" }, outputs: { fileChange: "fileChange", errorEvent: "errorEvent" }, standalone: false, ngContentSelectors: _c2$3, decls: 7, vars: 9, consts: [["regularUpload", ""], ["fileInput", ""], ["inputButton", ""], [1, "file-upload", 3, "ngClass.lt-sm"], ["color", "primary", "class", "tld-file-upload-progress", 3, "value", 4, "ngIf"], [4, "ngIf", "ngIfElse"], ["type", "file", "name", "files", 1, "native-file-input", 3, "change", "accept", "multiple", "disabled"], ["color", "primary", 1, "tld-file-upload-progress", 3, "value"], ["tldDragAndDrop", "", "mat-button", "", "type", "button", 1, "upload-button", "text-l", 3, "fileDropped", "click", "disabled"], [4, "ngIf"], ["tldDragAndDrop", "", 1, "upload-zone", 3, "fileDropped"], ["svgIcon", "file_upload_icon"], [1, "upload-zone-labels"], ["color", "accent", 1, "file-upload--btn", 3, "click"], ["fxHide.lt-sm", "", 1, "upload-label", 3, "innerHTML"], [1, "upload-zone-formats"], [4, "ngFor", "ngForOf"], [3, "innerHTML", 4, "ngIf"], [1, "supported-formats-btn"], [3, "innerHTML"], [1, "supported-formats-btn", 3, "click"]], template: function FileUploadComponent_Template(rf, ctx) { if (rf & 1) {
3135
+ } }, inputs: { accept: "accept", useCompactUpload: "useCompactUpload", maxSize: "maxSize", multiple: "multiple", filePreviewProgress: "filePreviewProgress", allowEmpty: "allowEmpty", disabled: "disabled", dragZoneLabel: "dragZoneLabel", uploadIconName: "uploadIconName", uploadButtonLabel: "uploadButtonLabel", openExtensionPopupLabel: "openExtensionPopupLabel", minSupportedFormatCount: "minSupportedFormatCount", hideDefaultLimitsLabel: "hideDefaultLimitsLabel", extendedAcceptList: "extendedAcceptList" }, outputs: { fileChange: "fileChange", errorEvent: "errorEvent" }, standalone: false, ngContentSelectors: _c2$3, decls: 7, vars: 9, consts: [["regularUpload", ""], ["fileInput", ""], ["inputButton", ""], [1, "file-upload", 3, "ngClass.lt-sm"], ["color", "primary", "class", "tld-file-upload-progress", 3, "value", 4, "ngIf"], [4, "ngIf", "ngIfElse"], ["testId", "input-file-upload", "type", "file", "name", "files", 1, "native-file-input", 3, "change", "accept", "multiple", "disabled"], ["color", "primary", 1, "tld-file-upload-progress", 3, "value"], ["tldDragAndDrop", "", "mat-button", "", "type", "button", 1, "upload-button", "text-l", 3, "fileDropped", "click", "disabled"], [4, "ngIf"], ["tldDragAndDrop", "", 1, "upload-zone", 3, "fileDropped"], ["svgIcon", "file_upload_icon"], [1, "upload-zone-labels"], ["color", "accent", 1, "file-upload--btn", 3, "click"], ["fxHide.lt-sm", "", 1, "upload-label", 3, "innerHTML"], [1, "upload-zone-formats"], [4, "ngFor", "ngForOf"], [3, "innerHTML", 4, "ngIf"], [1, "supported-formats-btn"], [3, "innerHTML"], [1, "supported-formats-btn", 3, "click"]], template: function FileUploadComponent_Template(rf, ctx) { if (rf & 1) {
3136
3136
  const _r1 = i0.ɵɵgetCurrentView();
3137
3137
  i0.ɵɵprojectionDef(_c1$5);
3138
3138
  i0.ɵɵelementStart(0, "div", 3);
@@ -3154,7 +3154,7 @@ class FileUploadComponent {
3154
3154
  }
3155
3155
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FileUploadComponent, [{
3156
3156
  type: Component,
3157
- args: [{ selector: 'lib-file-upload', standalone: false, template: "<div class=\"file-upload\" [ngClass.lt-sm]=\"'file-upload-mobile'\" [class.disabled]=\"disabled\">\r\n <mat-progress-bar \r\n *ngIf=\"filePreviewProgress\"\r\n color=\"primary\" \r\n class=\"tld-file-upload-progress\" \r\n [value]=\"filePreviewProgress\"\r\n >\r\n </mat-progress-bar>\r\n\r\n <ng-container *ngIf=\"useCompactUpload; else regularUpload\">\r\n <button tldDragAndDrop #inputButton mat-button (fileDropped)=\"onFileDrop($event)\" [disabled]=\"disabled\" type=\"button\"\r\n (click)=\"fileInput.click()\" class=\"upload-button text-l\"\r\n >\r\n <mat-icon *ngIf=\"uploadIconName\">{{uploadIconName}}</mat-icon>\r\n <ng-content></ng-content>\r\n </button>\r\n </ng-container>\r\n\r\n <ng-template #regularUpload>\r\n <div class=\"upload-zone\" tldDragAndDrop (fileDropped)=\"onFileDrop($event)\" [class.disabled-upload-zone]=\"disabled\">\r\n <mat-icon svgIcon=\"file_upload_icon\"></mat-icon>\r\n\r\n \r\n <div class=\"upload-zone-labels\">\r\n <a color=\"accent\" class=\"file-upload--btn\" (click)=\"fileInput.click()\">{{ uploadButtonLabel }}</a>\r\n \r\n <span fxHide.lt-sm class=\"upload-label\" [innerHTML]=\"dragZoneLabel\"></span>\r\n \r\n <p class=\"upload-zone-formats\">\r\n (<span *ngFor=\"let format of accept; let i = index\"\r\n >{{ format\r\n }}{{ accept.length - 1 === i ? \".\" : \", \" }}</span\r\n >\r\n\r\n <span *ngIf=\"maxSize && !hideDefaultLimitsLabel\" [innerHTML]=\"'FILE_UPLOAD.LIMITS_MB' | translate: { maxSize: maxSizeLabel }\"></span>)\r\n </p>\r\n\r\n <ng-content select=\"[customLimits]\"></ng-content>\r\n \r\n @if (accept.length > minSupportedFormatCount) {\r\n <a class=\"supported-formats-btn\" (click)=\"openSupportedFormatModal()\">{{openExtensionPopupLabel}}</a>\r\n }\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<input \r\n #fileInput \r\n type=\"file\" \r\n name=\"files\" \r\n class=\"native-file-input\"\r\n [accept]=\"allowedExtensions\" \r\n [multiple]=\"multiple\" \r\n [disabled]=\"disabled\"\r\n (change)=\"handleFileInput($event)\" \r\n/>\r\n", styles: [":host{width:100%}.file-upload{display:flex;flex-direction:column;justify-content:center;text-align:center}.file-upload .upload-button,.file-upload .upload-zone{display:flex;border-radius:16px;border:1px dashed var(--base-65)}.file-upload .upload-button{justify-content:start;min-height:52px;overflow:hidden;padding:16px!important}.file-upload .upload-zone{flex-direction:column;justify-content:center;align-items:center;width:100%;padding:42px 0}.file-upload .upload-zone-labels{margin-top:10px;font-size:1rem;font-weight:600;line-height:24px;text-align:center}.file-upload .upload-zone-labels .upload-zone-formats{font-weight:400}.file-upload .supported-formats-btn{cursor:pointer;color:var(--accent);font-weight:400}.file-upload .disabled-upload-zone{cursor:not-allowed}.file-upload .file-upload--btn,.file-upload .upload-label:not(.file-upload-mobile){margin-top:10px}.file-upload .file-upload--btn{cursor:pointer;align-self:center;max-width:fit-content;color:var(--accent)}.file-upload .file-upload--btn:hover{text-decoration:underline}.file-upload .upload-zone-formats{margin:10px 0 4px;color:var(--base-40)}.disabled .file-upload--btn{pointer-events:none!important;text-decoration:none!important;color:var(--base-40)}.native-file-input{display:none}\n"] }]
3157
+ args: [{ selector: 'lib-file-upload', standalone: false, template: "<div class=\"file-upload\" [ngClass.lt-sm]=\"'file-upload-mobile'\" [class.disabled]=\"disabled\">\r\n <mat-progress-bar \r\n *ngIf=\"filePreviewProgress\"\r\n color=\"primary\" \r\n class=\"tld-file-upload-progress\" \r\n [value]=\"filePreviewProgress\"\r\n >\r\n </mat-progress-bar>\r\n\r\n <ng-container *ngIf=\"useCompactUpload; else regularUpload\">\r\n <button tldDragAndDrop #inputButton mat-button (fileDropped)=\"onFileDrop($event)\" [disabled]=\"disabled\" type=\"button\"\r\n (click)=\"fileInput.click()\" class=\"upload-button text-l\"\r\n >\r\n <mat-icon *ngIf=\"uploadIconName\">{{uploadIconName}}</mat-icon>\r\n <ng-content></ng-content>\r\n </button>\r\n </ng-container>\r\n\r\n <ng-template #regularUpload>\r\n <div class=\"upload-zone\" tldDragAndDrop (fileDropped)=\"onFileDrop($event)\" [class.disabled-upload-zone]=\"disabled\">\r\n <mat-icon svgIcon=\"file_upload_icon\"></mat-icon>\r\n\r\n \r\n <div class=\"upload-zone-labels\">\r\n <a color=\"accent\" class=\"file-upload--btn\" (click)=\"fileInput.click()\">{{ uploadButtonLabel }}</a>\r\n \r\n <span fxHide.lt-sm class=\"upload-label\" [innerHTML]=\"dragZoneLabel\"></span>\r\n \r\n <p class=\"upload-zone-formats\">\r\n (<span *ngFor=\"let format of accept; let i = index\"\r\n >{{ format\r\n }}{{ accept.length - 1 === i ? \".\" : \", \" }}</span\r\n >\r\n\r\n <span *ngIf=\"maxSize && !hideDefaultLimitsLabel\" [innerHTML]=\"'FILE_UPLOAD.LIMITS_MB' | translate: { maxSize: maxSizeLabel }\"></span>)\r\n </p>\r\n\r\n <ng-content select=\"[customLimits]\"></ng-content>\r\n \r\n @if (accept.length > minSupportedFormatCount) {\r\n <a class=\"supported-formats-btn\" (click)=\"openSupportedFormatModal()\">{{openExtensionPopupLabel}}</a>\r\n }\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<input \r\n testId=\"input-file-upload\"\r\n #fileInput \r\n type=\"file\" \r\n name=\"files\" \r\n class=\"native-file-input\"\r\n [accept]=\"allowedExtensions\" \r\n [multiple]=\"multiple\" \r\n [disabled]=\"disabled\"\r\n (change)=\"handleFileInput($event)\" \r\n/>\r\n", styles: [":host{width:100%}.file-upload{display:flex;flex-direction:column;justify-content:center;text-align:center}.file-upload .upload-button,.file-upload .upload-zone{display:flex;border-radius:16px;border:1px dashed var(--base-65)}.file-upload .upload-button{justify-content:start;min-height:52px;overflow:hidden;padding:16px!important}.file-upload .upload-zone{flex-direction:column;justify-content:center;align-items:center;width:100%;padding:42px 0}.file-upload .upload-zone-labels{margin-top:10px;font-size:1rem;font-weight:600;line-height:24px;text-align:center}.file-upload .upload-zone-labels .upload-zone-formats{font-weight:400}.file-upload .supported-formats-btn{cursor:pointer;color:var(--accent);font-weight:400}.file-upload .disabled-upload-zone{cursor:not-allowed}.file-upload .file-upload--btn,.file-upload .upload-label:not(.file-upload-mobile){margin-top:10px}.file-upload .file-upload--btn{cursor:pointer;align-self:center;max-width:fit-content;color:var(--accent)}.file-upload .file-upload--btn:hover{text-decoration:underline}.file-upload .upload-zone-formats{margin:10px 0 4px;color:var(--base-40)}.disabled .file-upload--btn{pointer-events:none!important;text-decoration:none!important;color:var(--base-40)}.native-file-input{display:none}\n"] }]
3158
3158
  }], () => [], { fileInput: [{
3159
3159
  type: ViewChild,
3160
3160
  args: ['fileInput']
@@ -9140,6 +9140,37 @@ class AnalyticsService {
9140
9140
  }]
9141
9141
  }], null, null); })();
9142
9142
 
9143
+ const DEFAULT_ACCESSIBILITY_PREFERENCES = {
9144
+ fontSizeIndex: 0,
9145
+ contrast: AccessibilityContrasts.BASE,
9146
+ textMagnifierEnabled: false,
9147
+ screenMaskEnabled: false,
9148
+ };
9149
+
9150
+ const DEFAULT_COOKIE_CONSENT_PREFERENCES = {
9151
+ accepted: false,
9152
+ };
9153
+
9154
+ const USER_CONFIG_VERSION = 1;
9155
+ const createMetadata = (updatedAt = new Date().toISOString()) => ({
9156
+ version: USER_CONFIG_VERSION,
9157
+ updatedAt,
9158
+ });
9159
+
9160
+ const DEFAULT_USER_CONFIG = {
9161
+ accessibility: DEFAULT_ACCESSIBILITY_PREFERENCES,
9162
+ cookieConsent: DEFAULT_COOKIE_CONSENT_PREFERENCES,
9163
+ language: {
9164
+ locale: null,
9165
+ },
9166
+ metadata: {
9167
+ version: USER_CONFIG_VERSION,
9168
+ updatedAt: new Date(0).toISOString(),
9169
+ },
9170
+ };
9171
+
9172
+ const USER_CONFIG_STORAGE = new InjectionToken('USER_CONFIG_STORAGE');
9173
+
9143
9174
  function CookieConsentComponent_Conditional_0_Conditional_19_Template(rf, ctx) { if (rf & 1) {
9144
9175
  i0.ɵɵelementStart(0, "mat-icon", 9);
9145
9176
  i0.ɵɵtext(1);
@@ -9421,32 +9452,17 @@ class CookieConsentComponent {
9421
9452
  constructor() {
9422
9453
  this.#analytics = inject(AnalyticsService);
9423
9454
  this.#translate = inject(TranslateService);
9455
+ this.#storage = inject(USER_CONFIG_STORAGE, { optional: true });
9424
9456
  this.isClosed = false;
9425
9457
  this.isDetailsVisible = false;
9426
- this.#COOKIE_CONSENT_ACCEPTED_KEY = 'cookie-consent-accepted';
9427
- this.#COOKIE_CONSENT_DECLINED_KEY = 'cookie-consent-declined';
9428
9458
  this.destroy = new Subject();
9429
9459
  }
9430
9460
  #analytics;
9431
9461
  #translate;
9432
- #COOKIE_CONSENT_ACCEPTED_KEY;
9433
- #COOKIE_CONSENT_DECLINED_KEY;
9462
+ #storage;
9434
9463
  ngOnInit() {
9435
- const script = document.createElement('script');
9436
- script.async = true;
9437
- script.defer = true;
9438
- script.innerHTML = this.script;
9439
- document.body.appendChild(script);
9440
- if (this.isCookieConsentConfigured()) {
9441
- this.isClosed = true;
9442
- return;
9443
- }
9444
- const currentLang = this.#translate.getCurrentLang();
9445
- this.#translate.setTranslation(currentLang, translations[currentLang.split('-')[0]], true);
9446
- this.#translate.onLangChange.pipe(takeUntil(this.destroy)).subscribe((langChangeEvent) => {
9447
- const newLang = langChangeEvent.lang;
9448
- this.#translate.setTranslation(newLang, translations[newLang.split('-')[0]], true);
9449
- });
9464
+ this.injectConsentScript();
9465
+ this.resolveInitialConsentState();
9450
9466
  }
9451
9467
  ngOnDestroy() {
9452
9468
  this.removeListeners();
@@ -9456,25 +9472,108 @@ class CookieConsentComponent {
9456
9472
  }
9457
9473
  accept() {
9458
9474
  this.#analytics.cookieConsentGiven(true);
9459
- localStorage.setItem(this.#COOKIE_CONSENT_ACCEPTED_KEY, 'true');
9475
+ this.persistConsent(true);
9460
9476
  this.removeListeners();
9461
9477
  }
9462
9478
  decline() {
9463
- localStorage.setItem(this.#COOKIE_CONSENT_DECLINED_KEY, 'true');
9479
+ this.persistConsent(false);
9464
9480
  this.removeListeners();
9465
9481
  }
9466
9482
  removeListeners() {
9483
+ if (this.isClosed) {
9484
+ return;
9485
+ }
9467
9486
  this.isClosed = true;
9468
9487
  this.destroy.next();
9469
9488
  this.destroy.complete();
9470
9489
  }
9490
+ resolveInitialConsentState() {
9491
+ const configured = this.isCookieConsentConfigured();
9492
+ if (this.isPromise(configured)) {
9493
+ configured.then((isConfigured) => {
9494
+ if (isConfigured) {
9495
+ this.isClosed = true;
9496
+ return;
9497
+ }
9498
+ this.setupTranslations();
9499
+ });
9500
+ return;
9501
+ }
9502
+ if (configured) {
9503
+ this.isClosed = true;
9504
+ return;
9505
+ }
9506
+ this.setupTranslations();
9507
+ }
9508
+ injectConsentScript() {
9509
+ if (!this.script || typeof document === 'undefined') {
9510
+ return;
9511
+ }
9512
+ const script = document.createElement('script');
9513
+ script.async = true;
9514
+ script.defer = true;
9515
+ script.innerHTML = this.script;
9516
+ document.body.appendChild(script);
9517
+ }
9518
+ setupTranslations() {
9519
+ const currentLang = this.#translate.getCurrentLang();
9520
+ this.#translate.setTranslation(currentLang, translations[this.translationKey(currentLang)], true);
9521
+ this.#translate.onLangChange
9522
+ .pipe(takeUntil(this.destroy))
9523
+ .subscribe((langChangeEvent) => {
9524
+ const newLang = langChangeEvent.lang;
9525
+ this.#translate.setTranslation(newLang, translations[this.translationKey(newLang)], true);
9526
+ });
9527
+ }
9528
+ translationKey(locale) {
9529
+ const key = (locale?.split('-')[0] ?? 'en');
9530
+ return translations[key] ? key : 'en';
9531
+ }
9471
9532
  isCookieConsentConfigured() {
9472
- const isAccepted = localStorage.getItem(this.#COOKIE_CONSENT_ACCEPTED_KEY) === 'true';
9473
- const isDeclined = localStorage.getItem(this.#COOKIE_CONSENT_DECLINED_KEY) === 'true';
9474
- if (isAccepted) {
9475
- this.accept();
9533
+ const snapshot = this.#storage?.snapshot();
9534
+ if (snapshot) {
9535
+ return this.applyStoredConsent(snapshot);
9536
+ }
9537
+ const stored = this.#storage?.read();
9538
+ if (this.isPromise(stored)) {
9539
+ return stored.then((config) => this.applyStoredConsent(config));
9540
+ }
9541
+ return this.applyStoredConsent(stored ?? null);
9542
+ }
9543
+ applyStoredConsent(config) {
9544
+ const consent = config?.cookieConsent;
9545
+ if (!consent?.updatedAt) {
9546
+ return false;
9547
+ }
9548
+ if (consent.accepted) {
9549
+ this.#analytics.cookieConsentGiven(true);
9476
9550
  }
9477
- return isAccepted || isDeclined;
9551
+ return true;
9552
+ }
9553
+ persistConsent(accepted) {
9554
+ const latest = this.#storage?.snapshot();
9555
+ if (latest) {
9556
+ this.writeConsent(latest, accepted);
9557
+ return;
9558
+ }
9559
+ const stored = this.#storage?.read();
9560
+ if (this.isPromise(stored)) {
9561
+ stored.then((config) => this.writeConsent(config ?? DEFAULT_USER_CONFIG, accepted));
9562
+ return;
9563
+ }
9564
+ this.writeConsent(stored ?? DEFAULT_USER_CONFIG, accepted);
9565
+ }
9566
+ writeConsent(source, accepted) {
9567
+ const now = new Date().toISOString();
9568
+ this.#storage?.write({
9569
+ cookieConsent: {
9570
+ accepted,
9571
+ updatedAt: now,
9572
+ },
9573
+ });
9574
+ }
9575
+ isPromise(value) {
9576
+ return !!value && typeof value.then === 'function';
9478
9577
  }
9479
9578
  static { this.ɵfac = function CookieConsentComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || CookieConsentComponent)(); }; }
9480
9579
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: CookieConsentComponent, selectors: [["lib-cookie-consent"]], inputs: { script: "script", privacyPolicyUrl: "privacyPolicyUrl" }, decls: 1, vars: 1, consts: [[1, "cookie-consent-container"], [1, "cookie-consent-content"], [1, "cookie-consent-description", 3, "innerHTML"], [1, "cookie-consent-actions"], ["mat-flat-button", "", 3, "click"], ["mat-stroked-button", "", 3, "click"], [1, "cookie-consent-info"], [1, "cookie-consent-info--privacy", 3, "href"], [1, "cookie-consent-info--details", 3, "click"], [1, "spin"], [1, "cookie-consent--details"]], template: function CookieConsentComponent_Template(rf, ctx) { if (rf & 1) {
@@ -9491,7 +9590,288 @@ class CookieConsentComponent {
9491
9590
  }], privacyPolicyUrl: [{
9492
9591
  type: Input
9493
9592
  }] }); })();
9494
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CookieConsentComponent, { className: "CookieConsentComponent", filePath: "lib/analytics/cookie-consent/cookie-consent.component.ts", lineNumber: 16 }); })();
9593
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(CookieConsentComponent, { className: "CookieConsentComponent", filePath: "lib/analytics/cookie-consent/cookie-consent.component.ts", lineNumber: 18 }); })();
9594
+
9595
+ var UserConfigStrategy;
9596
+ (function (UserConfigStrategy) {
9597
+ UserConfigStrategy["Local"] = "local";
9598
+ UserConfigStrategy["Iframe"] = "iframe";
9599
+ })(UserConfigStrategy || (UserConfigStrategy = {}));
9600
+
9601
+ const USER_CONFIG_IFRAME_NAME = 'ngx-user-config-bridge';
9602
+ const DEFAULT_USER_CONFIG_IFRAME_URL = '/assets/static-files/user-config-bridge.html';
9603
+ const USER_CONFIG_IFRAME_TARGET_ORIGIN = '*';
9604
+ const USER_CONFIG_MESSAGE_SNAPSHOT = 'ngx-user-config:snapshot';
9605
+ const USER_CONFIG_MESSAGE_REQUEST = 'ngx-user-config:request';
9606
+ const USER_CONFIG_MESSAGE_UPDATE = 'ngx-user-config:update';
9607
+
9608
+ const DEFAULT_USER_CONFIG_OPTIONS = {
9609
+ strategy: UserConfigStrategy.Local,
9610
+ iframeUrl: DEFAULT_USER_CONFIG_IFRAME_URL,
9611
+ };
9612
+
9613
+ function userConfigStorageFactory(options, localStorageStrategy, iframeStrategy) {
9614
+ return options.strategy === UserConfigStrategy.Iframe ? iframeStrategy : localStorageStrategy;
9615
+ }
9616
+
9617
+ const USER_CONFIG_STORAGE_KEY = 'ngx_common_user_config';
9618
+
9619
+ const USER_CONFIG_OPTIONS = new InjectionToken('USER_CONFIG_OPTIONS');
9620
+
9621
+ class IframeStorageStrategy {
9622
+ #dom;
9623
+ #options;
9624
+ #iframe;
9625
+ #messageHandler;
9626
+ constructor() {
9627
+ this.#dom = inject(DOMService);
9628
+ this.#options = inject(USER_CONFIG_OPTIONS);
9629
+ this.snapshot = signal(null, ...(ngDevMode ? [{ debugName: "snapshot" }] : []));
9630
+ this.#ensureIframe();
9631
+ const win = this.#dom.window;
9632
+ if (!win) {
9633
+ return;
9634
+ }
9635
+ this.#messageHandler = (event) => this.#handleMessage(event);
9636
+ win.addEventListener('message', this.#messageHandler);
9637
+ const cached = this.#readCache();
9638
+ if (cached) {
9639
+ this.snapshot.set(cached);
9640
+ }
9641
+ }
9642
+ read() {
9643
+ return this.#readCache();
9644
+ }
9645
+ write(config) {
9646
+ const merged = this.#mergeWithBase(config);
9647
+ this.#writeCache(merged);
9648
+ this.#postToBridge({ type: USER_CONFIG_MESSAGE_UPDATE, payload: merged });
9649
+ this.snapshot.set(merged);
9650
+ }
9651
+ ngOnDestroy() {
9652
+ if (this.#messageHandler && this.#dom.window) {
9653
+ this.#dom.window.removeEventListener('message', this.#messageHandler);
9654
+ }
9655
+ }
9656
+ #ensureIframe() {
9657
+ if (this.#iframe) {
9658
+ return;
9659
+ }
9660
+ const win = this.#dom.window;
9661
+ const doc = win?.document;
9662
+ if (!doc) {
9663
+ return;
9664
+ }
9665
+ const iframe = doc.createElement('iframe');
9666
+ iframe.style.display = 'none';
9667
+ iframe.name = USER_CONFIG_IFRAME_NAME;
9668
+ iframe.src = this.#options?.iframeUrl ?? DEFAULT_USER_CONFIG_IFRAME_URL;
9669
+ iframe.setAttribute('aria-hidden', 'true');
9670
+ iframe.addEventListener('load', () => this.#postToBridge({ type: USER_CONFIG_MESSAGE_REQUEST }));
9671
+ doc.body?.appendChild(iframe);
9672
+ this.#iframe = iframe;
9673
+ }
9674
+ #handleMessage(event) {
9675
+ if (!this.#iframe || event.source !== this.#iframe.contentWindow) {
9676
+ return;
9677
+ }
9678
+ const data = event.data;
9679
+ if (data?.type === USER_CONFIG_MESSAGE_SNAPSHOT && data.payload) {
9680
+ const snapshot = data.payload;
9681
+ this.#writeCache(snapshot);
9682
+ this.snapshot.set(snapshot);
9683
+ }
9684
+ }
9685
+ #postToBridge(message) {
9686
+ if (!this.#iframe?.contentWindow) {
9687
+ return;
9688
+ }
9689
+ this.#iframe.contentWindow.postMessage(message, USER_CONFIG_IFRAME_TARGET_ORIGIN);
9690
+ }
9691
+ #readCache() {
9692
+ const store = this.#dom.localStorage;
9693
+ if (!store) {
9694
+ return null;
9695
+ }
9696
+ return this.#parse(store.getItem(USER_CONFIG_STORAGE_KEY));
9697
+ }
9698
+ #writeCache(config) {
9699
+ const store = this.#dom.localStorage;
9700
+ if (!store) {
9701
+ return;
9702
+ }
9703
+ try {
9704
+ store.setItem(USER_CONFIG_STORAGE_KEY, JSON.stringify(config));
9705
+ }
9706
+ catch {
9707
+ /* noop */
9708
+ }
9709
+ }
9710
+ #parse(raw) {
9711
+ if (!raw) {
9712
+ return null;
9713
+ }
9714
+ try {
9715
+ return JSON.parse(raw);
9716
+ }
9717
+ catch {
9718
+ return DEFAULT_USER_CONFIG;
9719
+ }
9720
+ }
9721
+ #mergeWithBase(update) {
9722
+ const base = this.snapshot() ?? this.read() ?? DEFAULT_USER_CONFIG;
9723
+ const mergedMetadata = {
9724
+ ...base.metadata,
9725
+ ...update.metadata,
9726
+ updatedAt: new Date().toISOString(),
9727
+ };
9728
+ return {
9729
+ accessibility: {
9730
+ ...base.accessibility,
9731
+ ...update.accessibility,
9732
+ },
9733
+ cookieConsent: {
9734
+ ...base.cookieConsent,
9735
+ ...update.cookieConsent,
9736
+ },
9737
+ language: {
9738
+ ...base.language,
9739
+ ...update.language,
9740
+ },
9741
+ metadata: mergedMetadata,
9742
+ };
9743
+ }
9744
+ static { this.ɵfac = function IframeStorageStrategy_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || IframeStorageStrategy)(); }; }
9745
+ static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: IframeStorageStrategy, factory: IframeStorageStrategy.ɵfac, providedIn: 'root' }); }
9746
+ }
9747
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(IframeStorageStrategy, [{
9748
+ type: Injectable,
9749
+ args: [{ providedIn: 'root' }]
9750
+ }], () => [], null); })();
9751
+
9752
+ class LocalStorageStrategy {
9753
+ #dom;
9754
+ #storageListener;
9755
+ constructor() {
9756
+ this.#dom = inject(DOMService);
9757
+ this.snapshot = signal(null, ...(ngDevMode ? [{ debugName: "snapshot" }] : []));
9758
+ const win = this.#dom.window;
9759
+ if (!win) {
9760
+ return;
9761
+ }
9762
+ this.#storageListener = (event) => {
9763
+ if (event.key !== USER_CONFIG_STORAGE_KEY || !event.newValue) {
9764
+ return;
9765
+ }
9766
+ const parsed = this.#parse(event.newValue);
9767
+ if (parsed) {
9768
+ this.snapshot.set(parsed);
9769
+ }
9770
+ };
9771
+ win.addEventListener('storage', this.#storageListener);
9772
+ const current = this.read();
9773
+ if (current) {
9774
+ this.snapshot.set(current);
9775
+ }
9776
+ }
9777
+ read() {
9778
+ const store = this.#dom.localStorage;
9779
+ if (!store) {
9780
+ return null;
9781
+ }
9782
+ return this.#parse(store.getItem(USER_CONFIG_STORAGE_KEY));
9783
+ }
9784
+ write(config) {
9785
+ const store = this.#dom.localStorage;
9786
+ if (!store) {
9787
+ return;
9788
+ }
9789
+ const merged = this.#mergeWithBase(config);
9790
+ try {
9791
+ store.setItem(USER_CONFIG_STORAGE_KEY, JSON.stringify(merged));
9792
+ this.snapshot.set(merged);
9793
+ }
9794
+ catch {
9795
+ /* noop */
9796
+ }
9797
+ }
9798
+ ngOnDestroy() {
9799
+ if (this.#storageListener && this.#dom.window) {
9800
+ this.#dom.window.removeEventListener('storage', this.#storageListener);
9801
+ }
9802
+ }
9803
+ #parse(raw) {
9804
+ if (!raw) {
9805
+ return null;
9806
+ }
9807
+ try {
9808
+ return JSON.parse(raw);
9809
+ }
9810
+ catch {
9811
+ return DEFAULT_USER_CONFIG;
9812
+ }
9813
+ }
9814
+ #mergeWithBase(update) {
9815
+ const base = this.snapshot() ?? this.read() ?? DEFAULT_USER_CONFIG;
9816
+ const mergedMetadata = {
9817
+ ...base.metadata,
9818
+ ...update.metadata,
9819
+ updatedAt: new Date().toISOString(),
9820
+ };
9821
+ return {
9822
+ accessibility: {
9823
+ ...base.accessibility,
9824
+ ...update.accessibility,
9825
+ },
9826
+ cookieConsent: {
9827
+ ...base.cookieConsent,
9828
+ ...update.cookieConsent,
9829
+ },
9830
+ language: {
9831
+ ...base.language,
9832
+ ...update.language,
9833
+ },
9834
+ metadata: mergedMetadata,
9835
+ };
9836
+ }
9837
+ static { this.ɵfac = function LocalStorageStrategy_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || LocalStorageStrategy)(); }; }
9838
+ static { this.ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: LocalStorageStrategy, factory: LocalStorageStrategy.ɵfac, providedIn: 'root' }); }
9839
+ }
9840
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(LocalStorageStrategy, [{
9841
+ type: Injectable,
9842
+ args: [{ providedIn: 'root' }]
9843
+ }], () => [], null); })();
9844
+
9845
+ function isUserConfigOptionsFactoryProvider(input) {
9846
+ return typeof input === 'object' && input !== null && 'useFactory' in input;
9847
+ }
9848
+
9849
+ function provideUserConfig(options = {}) {
9850
+ const optionsProvider = isUserConfigOptionsFactoryProvider(options)
9851
+ ? {
9852
+ provide: USER_CONFIG_OPTIONS,
9853
+ useFactory: (...deps) => ({
9854
+ ...DEFAULT_USER_CONFIG_OPTIONS,
9855
+ ...options.useFactory(...deps),
9856
+ }),
9857
+ deps: options.deps
9858
+ }
9859
+ : {
9860
+ provide: USER_CONFIG_OPTIONS,
9861
+ useValue: {
9862
+ ...DEFAULT_USER_CONFIG_OPTIONS,
9863
+ ...options,
9864
+ },
9865
+ };
9866
+ return makeEnvironmentProviders([
9867
+ optionsProvider,
9868
+ {
9869
+ provide: USER_CONFIG_STORAGE,
9870
+ useFactory: userConfigStorageFactory,
9871
+ deps: [USER_CONFIG_OPTIONS, LocalStorageStrategy, IframeStorageStrategy],
9872
+ },
9873
+ ]);
9874
+ }
9495
9875
 
9496
9876
  /*
9497
9877
  * Public API Surface of ngx-common
@@ -9501,5 +9881,5 @@ class CookieConsentComponent {
9501
9881
  * Generated bundle index. Do not edit.
9502
9882
  */
9503
9883
 
9504
- export { ALERT_CONFIGURATION_TOKEN, AccessibilityContrasts, AccessibilityDialogComponent, AccessibilityFontSizes, AccessibilityService, AccessibilityTextMagnifierService, AlertService, AnalyticsService, AuthHeadersHelper, COLLECTIONS_MENU, CUSTOM_TITLE_CONFIGURATION_TOKEN, ClickOutsideDirective, ClickOutsideModule, CloseButtonComponent, CloseButtonModule, CombinedCollection, CombinedCollectionTooltipKey, CompanyProductComponent, CompanyProductModule, Confirmation, ConfirmationModalComponent, ConfirmationModalModule, ConfirmationService, ConversionHelper, CookieConsentComponent, CustomPaginatorInernationalizationHelper, CustomTitleStrategyService, CustomTranslateLoader, DISABLE_EXPORT_ATTRIBUTE_NAME, DOMService, DateAgoModule, DateAgoPipe, DomainTranslatePipe, DragAndDropDirective, DragAndDropModule, ERROR_CODES, EngineTermApiService, EngineTermCollectionScope, EngineTermCollectionSource, EngineTermCollectionStatus, EngineTermCollectionSubStatus, ExportFormat, ExtensionDialogComponent, ExtensionDialogModule, ExtensionDialogService, FILE_SIZE_UNIT, FileCategories, FileExtensionHelper, FileExtensions, FileSizeLabelPipe, FileTypeIcons, FileTypes, FileUploadComponent, FileUploadErrorTypeEnum, FileUploadModule, FilterBarComponent, FilterBarModule, FilterWithHighlightModule, FilterWithHighlightPipe, FooterComponent, FooterModule, GlobalMessageComponent, HashHelper, HtmlElementParseHelper, HtmlHelper, IconService, InlineMessageComponent, InlineMessageIconPosition, InlineMessageModule, InlineMessageType, LAST_USED_SYSTEM_LOCAL_STORAGE_KEY, LLMActions, LLMComponent, LLMModule, LLM_CONFIGURATION_TOKEN, LanguageTranslateModule, LanguageTranslatePipe, LanguageTranslateService, MatButtonLoadingDirective, MatButtonLoadingModule, MatomoService, MissingTranslationHandlerService, MissingTranslationHelper, MtCollectionStatus, MultiFunctionalTableComponent, MultiFunctionalTableModule, NewFeatureDialogWrapperComponent, NotificationMessageComponent, NotificationMessageModule, NotificationMessageType, NotificationService, OPEN_CLOSE_BTN_ICONS_TOKEN, ObjectLengthModule, ObjectLengthPipe, OpenCloseButtonComponent, OpenCloseButtonModule, OpenExtensionDialogComponent, Operations, PlausibleEventDirective, PlausibleHelper, PlausibleModule, ResolutionHelper, SCREEN_SIZE, SaveFileHelper, SelectLanguageDialogComponent, SidebarComponent, SidebarService, SortAlphabeticallyModule, SortAlphabeticallyPipe, SortByNumberPipe, SortDomainsPipe, SortHelper, SortLanguageListPipe, SortTranslationsByPropertyModule, SortTranslationsByPropertyPipe, SortTranslationsModule, SortTranslationsPipe, StatusDisplayComponent, StatusDisplayModule, SubscriptionComponent, SubscriptionPlan, SystemService, TerminologyApiService, TerminologyCollectionService, TerminologyComponent, TerminologyConfigService, TerminologyCreateCollectionComponent, TerminologyModule, TerminologyService, TextToSpeechComponent, TldLoaderComponent, TldLoaderModule, ToastComponent, ToastService, getFileSizeLabel, provideCustomTitleStrategy };
9884
+ export { ALERT_CONFIGURATION_TOKEN, AccessibilityContrasts, AccessibilityDialogComponent, AccessibilityFontSizes, AccessibilityService, AccessibilityTextMagnifierService, AlertService, AnalyticsService, AuthHeadersHelper, COLLECTIONS_MENU, CUSTOM_TITLE_CONFIGURATION_TOKEN, ClickOutsideDirective, ClickOutsideModule, CloseButtonComponent, CloseButtonModule, CombinedCollection, CombinedCollectionTooltipKey, CompanyProductComponent, CompanyProductModule, Confirmation, ConfirmationModalComponent, ConfirmationModalModule, ConfirmationService, ConversionHelper, CookieConsentComponent, CustomPaginatorInernationalizationHelper, CustomTitleStrategyService, CustomTranslateLoader, DEFAULT_ACCESSIBILITY_PREFERENCES, DEFAULT_COOKIE_CONSENT_PREFERENCES, DEFAULT_USER_CONFIG, DEFAULT_USER_CONFIG_IFRAME_URL, DEFAULT_USER_CONFIG_OPTIONS, DISABLE_EXPORT_ATTRIBUTE_NAME, DOMService, DateAgoModule, DateAgoPipe, DomainTranslatePipe, DragAndDropDirective, DragAndDropModule, ERROR_CODES, EngineTermApiService, EngineTermCollectionScope, EngineTermCollectionSource, EngineTermCollectionStatus, EngineTermCollectionSubStatus, ExportFormat, ExtensionDialogComponent, ExtensionDialogModule, ExtensionDialogService, FILE_SIZE_UNIT, FileCategories, FileExtensionHelper, FileExtensions, FileSizeLabelPipe, FileTypeIcons, FileTypes, FileUploadComponent, FileUploadErrorTypeEnum, FileUploadModule, FilterBarComponent, FilterBarModule, FilterWithHighlightModule, FilterWithHighlightPipe, FooterComponent, FooterModule, GlobalMessageComponent, HashHelper, HtmlElementParseHelper, HtmlHelper, IconService, InlineMessageComponent, InlineMessageIconPosition, InlineMessageModule, InlineMessageType, LAST_USED_SYSTEM_LOCAL_STORAGE_KEY, LLMActions, LLMComponent, LLMModule, LLM_CONFIGURATION_TOKEN, LanguageTranslateModule, LanguageTranslatePipe, LanguageTranslateService, MatButtonLoadingDirective, MatButtonLoadingModule, MatomoService, MissingTranslationHandlerService, MissingTranslationHelper, MtCollectionStatus, MultiFunctionalTableComponent, MultiFunctionalTableModule, NewFeatureDialogWrapperComponent, NotificationMessageComponent, NotificationMessageModule, NotificationMessageType, NotificationService, OPEN_CLOSE_BTN_ICONS_TOKEN, ObjectLengthModule, ObjectLengthPipe, OpenCloseButtonComponent, OpenCloseButtonModule, OpenExtensionDialogComponent, Operations, PlausibleEventDirective, PlausibleHelper, PlausibleModule, ResolutionHelper, SCREEN_SIZE, SaveFileHelper, SelectLanguageDialogComponent, SidebarComponent, SidebarService, SortAlphabeticallyModule, SortAlphabeticallyPipe, SortByNumberPipe, SortDomainsPipe, SortHelper, SortLanguageListPipe, SortTranslationsByPropertyModule, SortTranslationsByPropertyPipe, SortTranslationsModule, SortTranslationsPipe, StatusDisplayComponent, StatusDisplayModule, SubscriptionComponent, SubscriptionPlan, SystemService, TerminologyApiService, TerminologyCollectionService, TerminologyComponent, TerminologyConfigService, TerminologyCreateCollectionComponent, TerminologyModule, TerminologyService, TextToSpeechComponent, TldLoaderComponent, TldLoaderModule, ToastComponent, ToastService, USER_CONFIG_IFRAME_NAME, USER_CONFIG_IFRAME_TARGET_ORIGIN, USER_CONFIG_MESSAGE_REQUEST, USER_CONFIG_MESSAGE_SNAPSHOT, USER_CONFIG_MESSAGE_UPDATE, USER_CONFIG_OPTIONS, USER_CONFIG_STORAGE, USER_CONFIG_STORAGE_KEY, USER_CONFIG_VERSION, UserConfigStrategy, createMetadata, getFileSizeLabel, isUserConfigOptionsFactoryProvider, provideCustomTitleStrategy, provideUserConfig, userConfigStorageFactory };
9505
9885
  //# sourceMappingURL=tilde-nlp-ngx-common.mjs.map