import { NgIf, NgClass, AsyncPipe } from '@angular/common';
import * as i0 from '@angular/core';
import { inject, Injectable, Input, Component, input, computed, ChangeDetectionStrategy, viewChild, SecurityContext, EventEmitter, ViewChild, Output, DestroyRef, signal } from '@angular/core';
import * as i2 from '@angular/forms';
import { FormsModule } from '@angular/forms';
import { Router, RouterModule } from '@angular/router';
import { gettext } from '@c8y/ngx-components/gettext';
import * as i2$1 from '@c8y/ngx-components';
import { AppStateService, Permissions, IconDirective, C8yTranslatePipe, ManagedObjectRealtimeService, TabsModule, LoadingComponent, AlertService, OptionsService, ClipboardService, DismissAlertStrategy, DynamicComponentAlert } from '@c8y/ngx-components';
import { WidgetConfigService, quoteAndEscape, WidgetConfigFeedbackComponent, AssetPropertyMappingsService, renamePropertyKeyInCode } from '@c8y/ngx-components/context-dashboard';
import * as i1 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';
import * as i3 from 'ngx-bootstrap/tooltip';
import { TooltipModule, TooltipDirective } from 'ngx-bootstrap/tooltip';
import { isEmpty } from 'lodash';
import { Subject, first, map, filter, withLatestFrom, switchMap, shareReplay, takeUntil, startWith, combineLatest, distinctUntilChanged, debounceTime, of, EMPTY, merge, from, isEmpty as isEmpty$1, catchError, fromEvent, BehaviorSubject } from 'rxjs';
import { DomSanitizer } from '@angular/platform-browser';
import { InventoryService } from '@c8y/client';
import { kebabCase } from 'lodash-es';
import { TranslateService } from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { PRESET_NAME, REFRESH_OPTION, LocalControlsComponent, GLOBAL_CONTEXT_DISPLAY_MODE, GlobalContextConnectorComponent } from '@c8y/ngx-components/global-context';
import * as i4 from 'ngx-bootstrap/dropdown';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import { EditorComponent } from '@c8y/ngx-components/editor';
import { map as map$1, distinctUntilChanged as distinctUntilChanged$1, switchMap as switchMap$1, filter as filter$1, tap, retry, take } from 'rxjs/operators';
import { AssetPropertyListComponent, AssetPropertyActionDirective } from '@c8y/ngx-components/asset-properties';

const INITIAL_HTML_FORMATTED = `<div>
  <h2>Hello from <span class="branded">HTML widget</span></h2>

  <p class="m-t-16">
    \$\{this.c8yTranslate('You can use HTML and JavaScript template literals:')\}
  </p>

  <p class="m-t-16">
    <b>Context properties:</b> Access the assigned asset's properties via <code>c8yContext</code>: <br>
    <tt>\$\{this.c8yContext ? this.c8yTranslate('Selected asset: {{ assetName }}', { assetName: this.c8yContext.name }) : this.c8yTranslate('No asset selected')\}</tt>
  </p>
  
  <p class="m-t-16">
    <b>Asset properties:</b> Use "Asset properties" to select other properties (incl. computed ones) and access their values via <code>c8yProperties</code>: <br>
    <i>Last updated:</i> <tt>\${this.c8yProperties?.lastUpdated || '-'}</tt> <br>
    <i>Last device message:</i> <tt>\${this.c8yProperties?.lastDeviceMessage || '-'}</tt>
  </p>

  <p class="m-t-16">
    <b>Styles:</b> Use the CSS editor to customize styles. You can use <span class="text-bold">any design-token CSS variable</span> in there.
  </p>

  <p class="m-t-16">
    <b>Translations:</b> Use the <code>c8yTranslate('text {{ var }}', { var: value })</code> function to translate strings.
    <i>Note: texts must be written in English and their translations must be available in the loaded standard or custom translation resources.</i>
  </p>

  <p class="m-t-16">
    <b>Buttons:</b> Use other HTML elements like buttons: <br>
    <a class="btn btn-primary m-t-8" href="#/group">\$\{this.c8yTranslate('Go to groups')\}</a>
  </p>
</div>`;
const INITIAL_CSS_FORMATTED = `
:host > div {
  padding: var(--c8y-root-component-padding-default);
}
span.branded { 
  color: var(--brand-primary, var(--c8y-brand-primary)); 
}`;
const defaultWebComponentName = 'DefaultWebComponent';
const defaultWebComponentAttributeNameContext = 'c8yContext';

const webComponentTemplate = (html, css, viewEncapsulation, name = defaultWebComponentName) => `
import { LitElement, html, css} from 'lit';
${!viewEncapsulation ? `import { styleImports } from 'styles';` : ''}

export default class ${name} extends LitElement {
  static styles = css\`
    ${css}
  \`;

  static properties = {
    // The managed object this widget is assigned to. Can be null.
    c8yContext: { type: Object },
    // The object with realtime values of configured properties. Can be null.
    c8yProperties: { type: Object },
    // The instant translation function.
    c8yTranslate: { type: Function },
  };

  constructor() {
    super();
  }

  render() {
    return html\`${viewEncapsulation
    ? html
    : `
      <style>
        \${styleImports}
      </style>
      ${html}
    `}\`;
  }
}
`;

const legacyTemplate = (html, deviceId, deviceName) => {
    const escapedHtml = html
        // Finds: \  Replaces with: \\
        .replace(/\\/g, '\\\\')
        // Finds: ` Replaces with: \`
        .replace(/`/g, '\\`')
        // Finds: $ Replaces with: \$
        .replace(/\$/g, '\\$');
    return `
import { angular } from 'angular';

// NOTE: This is a legacy template for the HTML widget.
// It is used to compile the HTML content in the context of the AngularJS application.
// The template is injected into the AngularJS application and compiled using the AngularJS compiler.
// The template should only be used for backward compatibility purposes.
// It is recommended to use a web component instead.

if(!angular) {
  throw new Error('AngularJS is not available. Please make sure to include AngularJS in your project.');
}

const $injector = angular.element(document.querySelector('c8y-ui-root')).injector();
if (!$injector) {
  throw new Error('AngularJS injector is not available. Maybe not an hybrid application?');
}

// defining a new scope
const $rootScope = $injector.get('$rootScope');
const $scope = $rootScope.$new(true); 

// faking the old angularjs config 
$scope.child = { 
  config: {
    ${deviceId ? `device: { id: "${deviceId}", name: "${deviceName}" },` : ''}
    html: \`<div ng-controller="HtmlWidgetCtrl">${escapedHtml}</div>\`
  }
};

// load the needed services
const $compile = $injector.get('$compile');
const $controller = $injector.get('$controller');

// create the element
const htmlElement = angular.element($scope.child.config.html);

// The default controller providing the context
$controller('HtmlWidgetCtrl', { $scope });

// Compile the element
$compile(htmlElement)($scope);

// Apply the scope changes
$rootScope.$apply();

export default htmlElement[0];`;
};

class HtmlWidgetConfigService {
    constructor() {
        this.DEFAULT_AUTO_SAVE_DEBOUNCE = 1000;
        this.codeChange$ = new Subject();
        this.widgetConfigService = inject(WidgetConfigService);
        this.appState = inject(AppStateService);
        this.destroy$ = new Subject();
        this.notify$ = this.widgetConfigService.notify$;
        this.init$ = this.widgetConfigService.currentConfig$.pipe(first(), map(current => {
            if (current.html) {
                current.config = this.mapLegacyConfig(current);
            }
            return (current.config || {});
        }), filter(config => !!config), withLatestFrom(this.appState.currentApplicationConfig), switchMap(([widgetConfig, appConfig]) => this.initConfig(appConfig, widgetConfig)), shareReplay(), takeUntil(this.destroy$));
        this.config$ = this.init$.pipe(switchMap(initValue => this.configChanged$.pipe(startWith(initValue))));
        this.codeEditorChangeConfig$ = combineLatest([
            this.codeChange$.pipe(startWith(undefined)),
            this.config$
        ]).pipe(distinctUntilChanged(), takeUntil(this.destroy$), debounceTime(this.DEFAULT_AUTO_SAVE_DEBOUNCE), map(([change, config]) => {
            if (!change) {
                return config;
            }
            if (change.type === 'css') {
                config.css = change.value;
            }
            else {
                config.code = change.value;
            }
            return { ...config };
        }));
        this.configChanged$ = new Subject();
    }
    initConfig(appConfig, widgetConfig) {
        const defaultToAdvancedMode = appConfig?.htmlWidgetDefaultToAdvancedMode ?? false;
        const isEmptyConfig = isEmpty(widgetConfig);
        if (isEmptyConfig && !defaultToAdvancedMode) {
            widgetConfig = this.initDefaultMode(!appConfig?.htmlWidgetDisableSanitization);
            this.save(widgetConfig);
            return of(widgetConfig);
        }
        if (isEmptyConfig) {
            widgetConfig = this.enableAdvancedMode(widgetConfig);
        }
        // new config is needed to trigger ngOnChanges
        const newConfig = { ...widgetConfig };
        this.save(newConfig);
        return of(newConfig);
    }
    destroy() {
        this.destroy$.next();
        this.codeChange$.complete();
        this.configChanged$.complete();
    }
    save(config) {
        this.widgetConfigService.updateConfig({
            config
        });
    }
    changeCode(value) {
        this.codeChange$.next({ value, type: 'code' });
    }
    changeCss(value) {
        this.codeChange$.next({ value, type: 'css' });
    }
    enableAdvancedMode(currentConfig) {
        const currentHTML = currentConfig?.code || INITIAL_HTML_FORMATTED;
        const currentCSS = currentConfig?.css || INITIAL_CSS_FORMATTED;
        const code = currentConfig?.legacy
            ? legacyTemplate(currentHTML, this.widgetConfigService.currentConfig?.device?.id, this.widgetConfigService.currentConfig?.device?.name)
            : webComponentTemplate(currentHTML, currentCSS, false);
        currentConfig = {
            css: '',
            code,
            legacy: false,
            devMode: true,
            options: {
                cssEncapsulation: false,
                advancedSecurity: false
            }
        };
        return currentConfig;
    }
    initDefaultMode(advancedSecurity = true) {
        return {
            css: INITIAL_CSS_FORMATTED,
            code: INITIAL_HTML_FORMATTED,
            legacy: false,
            devMode: false,
            options: {
                cssEncapsulation: false,
                advancedSecurity
            }
        };
    }
    mapLegacyConfig(current) {
        const isAlreadyInAdvancedMode = current?.config?.devMode === true;
        if (isAlreadyInAdvancedMode) {
            return current.config;
        }
        const isAlreadyMapped = current?.config?.legacy === true;
        if (isAlreadyMapped) {
            return current.config;
        }
        return {
            code: current.html,
            css: '',
            legacy: true,
            devMode: false,
            options: {
                cssEncapsulation: false,
                advancedSecurity: current.sanitization === 'strict'
            }
        };
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetConfigService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetConfigService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetConfigService, decorators: [{
            type: Injectable
        }] });

class AdvancedSettingsComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
        this.htmlWidgetConfigService = inject(HtmlWidgetConfigService);
        this.router = inject(Router);
        this.permissionService = inject(Permissions);
        this.canChangeSettings = false;
        this.CSS_ENCAPSULATION_HELP_CONTEXT = gettext('If enabled, the CSS will be encapsulated and no platform styling will be applied.');
    }
    ngOnInit() {
        this.canChangeSettings = this.permissionService.hasAnyRole([
            Permissions.ROLE_APPLICATION_MANAGEMENT_ADMIN,
            Permissions.ROLE_TENANT_ADMIN
        ]);
    }
    disableAdvancedMode() {
        const config = this.htmlWidgetConfigService.initDefaultMode();
        this.htmlWidgetConfigService.save(config);
        this.htmlWidgetConfigService.configChanged$.next(config);
    }
    enableAdvancedMode() {
        let { config } = this.widgetConfigService.currentConfig;
        config = this.htmlWidgetConfigService.enableAdvancedMode(config);
        this.htmlWidgetConfigService.save(config);
        this.htmlWidgetConfigService.configChanged$.next(config);
    }
    toggleAdvancedMode() {
        this.devMode ? this.disableAdvancedMode() : this.enableAdvancedMode();
        this.devMode = !this.devMode;
    }
    async changeOption(option) {
        const { config } = this.widgetConfigService.currentConfig;
        config.options[option] = !config.options[option];
        this.htmlWidgetConfigService.save(config);
        this.htmlWidgetConfigService.configChanged$.next(config);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AdvancedSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: AdvancedSettingsComponent, isStandalone: true, selector: "c8y-html-widget-advanced-settings", inputs: { devMode: "devMode", cssEncapsulation: "cssEncapsulation" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset m-t-0\">\n  <legend>{{ 'Developer mode' | translate }}</legend>\n\n  <div class=\"d-flex a-i-center p-b-16\">\n    <label class=\"c8y-switch\">\n      <input\n        type=\"checkbox\"\n        [ngModel]=\"devMode\"\n        (change)=\"toggleAdvancedMode()\"\n        [disabled]=\"!canChangeSettings\"\n      />\n      <span></span>\n      <span>{{ 'Advanced developer mode' | translate }}</span>\n    </label>\n\n    <button class=\"btn-help\"\n      [attr.aria-label]=\"'Help' | translate\"\n      [popover]=\"devMode ? disableAdvanced : enableAdvanced\"\n      container=\"body\"\n      placement=\"right\"\n      triggers=\"focus\"\n      type=\"button\"\n    ></button>\n\n    <ng-template #enableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        <i [c8yIcon]=\"'imac-settings'\"></i>\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        Create custom widgets by modifying a basic WebComponent with HTML and JavaScript. This\n        <strong>unsupported</strong>\n        feature is ideal for rapid prototyping and simple customizations.\n      </p>\n      <p class=\"p-b-8\" translate>\n        For production environments, we recommend our fully-supported Angular-based\n        <a href=\"https://styleguide.cumulocity.com\" target=\"_blank\">Web SDK</a>.\n        <br />\n        Enable advanced developer mode to start coding!\n      </p>\n    </ng-template>\n\n    <ng-template #disableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        The advanced developer mode is enabled for this widget allowing to build extensive Web\n        Components.\n      </p>\n      <p class=\"p-b-8\" translate>\n        You can disable this mode again, but it will reset the current code.\n      </p>\n    </ng-template>\n\n    <ng-container *ngIf=\"!devMode\">\n      <label\n        class=\"c8y-switch m-l-auto\"\n      >\n        <input\n          type=\"checkbox\"\n          (change)=\"changeOption('cssEncapsulation')\"\n          [disabled]=\"!canChangeSettings\"\n          [ngModel]=\"cssEncapsulation\"\n        />\n        <span></span>\n        <span>{{ 'CSS encapsulation' | translate }}</span>\n      </label>\n      <button\n        class=\"btn-help m-0\"\n        [attr.aria-label]=\"'Help' | translate\"\n        popover=\"{{ CSS_ENCAPSULATION_HELP_CONTEXT | translate }}\"\n        triggers=\"focus\"\n        placement=\"left\"\n        container=\"body\"\n        type=\"button\"\n      ></button>\n    </ng-container>\n\n  </div>\n</fieldset>\n", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AdvancedSettingsComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [IconDirective, NgIf, TooltipModule, PopoverModule, C8yTranslatePipe, FormsModule], selector: 'c8y-html-widget-advanced-settings', template: "<fieldset class=\"c8y-fieldset m-t-0\">\n  <legend>{{ 'Developer mode' | translate }}</legend>\n\n  <div class=\"d-flex a-i-center p-b-16\">\n    <label class=\"c8y-switch\">\n      <input\n        type=\"checkbox\"\n        [ngModel]=\"devMode\"\n        (change)=\"toggleAdvancedMode()\"\n        [disabled]=\"!canChangeSettings\"\n      />\n      <span></span>\n      <span>{{ 'Advanced developer mode' | translate }}</span>\n    </label>\n\n    <button class=\"btn-help\"\n      [attr.aria-label]=\"'Help' | translate\"\n      [popover]=\"devMode ? disableAdvanced : enableAdvanced\"\n      container=\"body\"\n      placement=\"right\"\n      triggers=\"focus\"\n      type=\"button\"\n    ></button>\n\n    <ng-template #enableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        <i [c8yIcon]=\"'imac-settings'\"></i>\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        Create custom widgets by modifying a basic WebComponent with HTML and JavaScript. This\n        <strong>unsupported</strong>\n        feature is ideal for rapid prototyping and simple customizations.\n      </p>\n      <p class=\"p-b-8\" translate>\n        For production environments, we recommend our fully-supported Angular-based\n        <a href=\"https://styleguide.cumulocity.com\" target=\"_blank\">Web SDK</a>.\n        <br />\n        Enable advanced developer mode to start coding!\n      </p>\n    </ng-template>\n\n    <ng-template #disableAdvanced>\n      <p class=\"text-16 text-bold p-b-8\">\n        {{ 'Advanced developer mode' | translate }}\n      </p>\n      <p class=\"p-b-8\" translate>\n        The advanced developer mode is enabled for this widget allowing to build extensive Web\n        Components.\n      </p>\n      <p class=\"p-b-8\" translate>\n        You can disable this mode again, but it will reset the current code.\n      </p>\n    </ng-template>\n\n    <ng-container *ngIf=\"!devMode\">\n      <label\n        class=\"c8y-switch m-l-auto\"\n      >\n        <input\n          type=\"checkbox\"\n          (change)=\"changeOption('cssEncapsulation')\"\n          [disabled]=\"!canChangeSettings\"\n          [ngModel]=\"cssEncapsulation\"\n        />\n        <span></span>\n        <span>{{ 'CSS encapsulation' | translate }}</span>\n      </label>\n      <button\n        class=\"btn-help m-0\"\n        [attr.aria-label]=\"'Help' | translate\"\n        popover=\"{{ CSS_ENCAPSULATION_HELP_CONTEXT | translate }}\"\n        triggers=\"focus\"\n        placement=\"left\"\n        container=\"body\"\n        type=\"button\"\n      ></button>\n    </ng-container>\n\n  </div>\n</fieldset>\n" }]
        }], propDecorators: { devMode: [{
                type: Input
            }], cssEncapsulation: [{
                type: Input
            }] } });

class HtmlAiChatToolDetailsComponent {
    constructor() {
        this.tool = input.required(...(ngDevMode ? [{ debugName: "tool" }] : []));
        this.code = computed(() => this.tool().input?.code || '', ...(ngDevMode ? [{ debugName: "code" }] : []));
        this.isExecuting = computed(() => this.tool().type !== 'tool-result', ...(ngDevMode ? [{ debugName: "isExecuting" }] : []));
        this.htmlWidgetConfigService = inject(HtmlWidgetConfigService);
    }
    /**
     * Revert to the last applied code.
     */
    revert() {
        this.applyCurrentCode();
    }
    applyCurrentCode() {
        const newConfig = {
            code: this.code(),
            css: '',
            devMode: true,
            legacy: false,
            options: { advancedSecurity: false, cssEncapsulation: false }
        };
        this.htmlWidgetConfigService.configChanged$.next(newConfig);
        this.htmlWidgetConfigService.widgetConfigService.updateConfig({ config: newConfig });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlAiChatToolDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: HtmlAiChatToolDetailsComponent, isStandalone: true, selector: "c8y-html-ai-chat-tool-details", inputs: { tool: { classPropertyName: "tool", publicName: "tool", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<pre\n  class=\"fit-w\"\n  style=\"max-height: 320px\"\n  >{{ code() }}</pre\n>\n@if (!isExecuting()) {\n  <button\n    class=\"btn btn-default btn-sm\"\n    [attr.aria-label]=\"'Revert to this version' | translate\"\n    [tooltip]=\"'Revert to this version' | translate\"\n    container=\"body\"\n    (click)=\"revert()\"\n  >\n    <i c8yIcon=\"undo\"></i>\n  </button>\n}\n", dependencies: [{ kind: "directive", type: TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlAiChatToolDetailsComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-ai-chat-tool-details', imports: [TooltipDirective, C8yTranslatePipe, IconDirective], standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<pre\n  class=\"fit-w\"\n  style=\"max-height: 320px\"\n  >{{ code() }}</pre\n>\n@if (!isExecuting()) {\n  <button\n    class=\"btn btn-default btn-sm\"\n    [attr.aria-label]=\"'Revert to this version' | translate\"\n    [tooltip]=\"'Revert to this version' | translate\"\n    container=\"body\"\n    (click)=\"revert()\"\n  >\n    <i c8yIcon=\"undo\"></i>\n  </button>\n}\n" }]
        }], propDecorators: { tool: [{ type: i0.Input, args: [{ isSignal: true, alias: "tool", required: true }] }] } });

class HtmlFrameComponent {
    constructor() {
        this.propertyValues$ = EMPTY;
        /**
         * Gates the managed-object realtime subscription. Defaults to always-on
         * for backwards compatibility; callers that own a global time-context
         * control (e.g. SCADA) can pass a live-mode observable to pause the MO
         * updates when the widget is paused.
         */
        this.realtimeControl$ = of(true);
        /**
         * If set to true, it will be ensured that a unique hash is generated
         * for every webcomponent. This is useful if configured as otherwise it might
         * happen that the same code is already used in another webcomponent and the
         * error messages can not be assigned correctly.
         */
        this.useSalt = false;
        this.alerts = [];
        this.sanitizer = inject(DomSanitizer);
        this.translateService = inject(TranslateService);
        this.moRealtime = inject(ManagedObjectRealtimeService);
        this.destroy$ = new Subject();
        this.hostElement = viewChild('hostElement', ...(ngDevMode ? [{ debugName: "hostElement" }] : []));
        this.reload$ = new Subject();
        this.htmlContentInitialization$ = this.reload$.pipe(filter(() => !!this.hostElement()), map(() => this.hostElement().nativeElement), switchMap((div) => merge(this.listenToErrors(), from(this.initDiv(div)).pipe(isEmpty$1(), filter(isEmpty => !!isEmpty), catchError(error => from([{ text: error, type: 'danger' }]))))), filter(alert => !!alert), takeUntil(this.destroy$));
        this.inventoryService = inject(InventoryService);
        this.htmlContentInitialization$.pipe(takeUntil(this.destroy$)).subscribe((alert) => {
            this.alerts.push(alert);
            console.error(alert.text);
        });
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
    }
    ngOnChanges(changes) {
        if (changes.config?.currentValue ||
            (this.config && changes.device?.previousValue !== changes.device?.currentValue)) {
            this.reloadComponent();
        }
    }
    reloadComponent() {
        this.alerts = [];
        const div = this.hostElement();
        div.nativeElement.innerHTML = '';
        this.currentWebComponent = undefined;
        this.currentLegacyElement = undefined;
        this.reload$.next();
    }
    async initDiv(divHostElement) {
        const code = this.getCode();
        const hash = await this.generateHash(code, this.useSalt);
        const webComponentName = kebabCase(defaultWebComponentName) + hash;
        const context = await this.getContext(this.device);
        if (customElements.get(webComponentName)) {
            return this.createWebComponent(webComponentName, divHostElement, context);
        }
        const url = this.generateUrl(code);
        const defaultModule = await this.loadScript(url);
        // if the default module is a string, we will not use a webcomponent
        // instead we will simply parse the string and add it to the div
        // this is the case for legacy HTML widgets
        if (typeof defaultModule.default === 'string') {
            divHostElement.innerHTML = defaultModule.default;
            return EMPTY;
        }
        // same goes for an HTML Element
        if (defaultModule.default instanceof HTMLElement) {
            if (this.currentLegacyElement?.parentNode === divHostElement) {
                divHostElement.removeChild(this.currentLegacyElement);
            }
            divHostElement.appendChild(defaultModule.default);
            this.currentLegacyElement = defaultModule.default;
            // Find and execute scripts
            const scripts = divHostElement.querySelectorAll('script');
            scripts.forEach(script => {
                const newScript = document.createElement('script');
                Array.from(script.attributes).forEach(attr => {
                    newScript.setAttribute(attr.name, attr.value);
                });
                newScript.textContent = script.textContent;
                script.parentNode.replaceChild(newScript, script);
            });
            return EMPTY;
        }
        // as a race condition can happen on loading, we need
        // to check again if the web component is already defined
        if (!customElements.get(webComponentName)) {
            customElements.define(webComponentName, defaultModule.default);
        }
        this.createWebComponent(webComponentName, divHostElement, context);
        return EMPTY;
    }
    async getContext(device) {
        if (!device) {
            return;
        }
        if (!device.self) {
            const { data } = await this.inventoryService.detail(device.id);
            return data;
        }
        return device;
    }
    async loadScript(url) {
        const module = await import(/* webpackIgnore: true */ url);
        if (!module.default) {
            throw 'No default export found. Add an "export default" statement to your code.';
        }
        return module;
    }
    generateUrl(script) {
        const blob = new Blob([script], { type: 'application/javascript' });
        const url = URL.createObjectURL(blob);
        if (this.latestUrl) {
            URL.revokeObjectURL(this.latestUrl);
        }
        this.latestUrl = url;
        return url;
    }
    listenToErrors() {
        const errorEvents$ = fromEvent(window, 'error').pipe(filter(event => event.filename?.includes(this.latestUrl)), map(event => this.mapErrorEventToAlert(event)));
        const rejectionEvents$ = fromEvent(window, 'unhandledrejection').pipe(filter(event => event.reason?.stack?.includes(this.latestUrl)), map(event => this.mapErrorEventToAlert(event)), takeUntil(this.destroy$));
        return merge(errorEvents$, rejectionEvents$);
    }
    createWebComponent(webComponentName, divHostElement, context) {
        // Remove previous web component if it exists
        if (this.currentWebComponent && this.currentWebComponent.parentNode === divHostElement) {
            divHostElement.removeChild(this.currentWebComponent);
        }
        const webComponent = document.createElement(webComponentName);
        const instantTranslate = this.translateService.instant.bind(this.translateService);
        webComponent.c8yTranslate = instantTranslate;
        webComponent.c8yContext = context;
        if (context?.id) {
            this.realtimeControl$
                .pipe(switchMap(enabled => (enabled ? this.moRealtime.onAll$(context.id) : EMPTY)), takeUntil(merge(this.reload$, this.destroy$)))
                .subscribe(res => {
                webComponent.c8yContext = res.data;
            });
        }
        this.propertyValues$.pipe(takeUntil(merge(this.reload$, this.destroy$))).subscribe(values => {
            webComponent.c8yProperties = values;
        });
        divHostElement.appendChild(webComponent);
        // Store reference to current component
        this.currentWebComponent = webComponent;
        return webComponent;
    }
    mapErrorEventToAlert(event) {
        const hasReason = 'reason' in event;
        if (hasReason && event.reason?.name === ReferenceError.name) {
            const undefinedVar = event.reason.message.split(' ')[0];
            return { text: undefinedVar + ' is not defined', type: 'info' };
        }
        return { text: hasReason ? event.reason.message : event.message, type: 'danger' };
    }
    getCode() {
        const isDevMode = this.config.devMode;
        if (isDevMode) {
            return this.config.code;
        }
        return this.createDefaultWebcomponentCode();
    }
    async generateHash(value, useSalt) {
        const encoder = new TextEncoder();
        const data = encoder.encode(value + (useSalt ? Math.random() : ''));
        const hashBuffer = await crypto.subtle.digest('SHA-256', data);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
        return hashHex;
    }
    createDefaultWebcomponentCode() {
        if (this.config.legacy) {
            const legacyWebComponent = legacyTemplate(this.config.options.advancedSecurity
                ? this.sanitizer.sanitize(SecurityContext.HTML, this.config.code)
                : this.config.code, this.device?.id, this.device?.name);
            return legacyWebComponent;
        }
        const webComponentScript = webComponentTemplate(this.config.options.advancedSecurity
            ? this.sanitizer.sanitize(SecurityContext.HTML, this.config.code)
            : this.config.code, this.config.options.advancedSecurity
            ? this.sanitizer.sanitize(SecurityContext.STYLE, this.config.css)
            : this.config.css, this.config.options.cssEncapsulation);
        return webComponentScript;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlFrameComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: HtmlFrameComponent, isStandalone: true, selector: "c8y-html-frame", inputs: { config: "config", device: "device", propertyValues$: "propertyValues$", realtimeControl$: "realtimeControl$", useSalt: "useSalt" }, host: { classAttribute: "d-contents" }, viewQueries: [{ propertyName: "hostElement", first: true, predicate: ["hostElement"], descendants: true, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "@for (alert of alerts; track alert) {\n  <div\n    class=\"alert m-8\"\n    role=\"alert\"\n    [ngClass]=\"{\n      'alert-danger': alert.type === 'danger',\n      'alert-warning': alert.type === 'warning',\n      'alert-info': alert.type === 'info',\n      'alert-success': alert.type === 'success'\n    }\"\n  >\n    <p><strong translate>There was an issue in the HTML widget:</strong></p>\n    <pre>{{ alert.text }}</pre>\n  </div>\n}\n<div\n  class=\"fit-w fit-h\"\n  #hostElement\n></div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlFrameComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [NgClass], selector: 'c8y-html-frame', host: { class: 'd-contents' }, template: "@for (alert of alerts; track alert) {\n  <div\n    class=\"alert m-8\"\n    role=\"alert\"\n    [ngClass]=\"{\n      'alert-danger': alert.type === 'danger',\n      'alert-warning': alert.type === 'warning',\n      'alert-info': alert.type === 'info',\n      'alert-success': alert.type === 'success'\n    }\"\n  >\n    <p><strong translate>There was an issue in the HTML widget:</strong></p>\n    <pre>{{ alert.text }}</pre>\n  </div>\n}\n<div\n  class=\"fit-w fit-h\"\n  #hostElement\n></div>\n" }]
        }], ctorParameters: () => [], propDecorators: { config: [{
                type: Input
            }], device: [{
                type: Input
            }], propertyValues$: [{
                type: Input
            }], realtimeControl$: [{
                type: Input
            }], useSalt: [{
                type: Input
            }], hostElement: [{ type: i0.ViewChild, args: ['hostElement', { isSignal: true }] }] } });

class WidgetCodeEditorComponent {
    constructor() {
        this.mode = 'code';
        this.showAdvancedSettings = true;
        this.codeChange = new EventEmitter();
        this.cssChange = new EventEmitter();
        this.translate = inject(TranslateService);
        this.widgetConfigService = inject(WidgetConfigService);
        this.defaultPropertyKeys$ = this.widgetConfigService.currentConfig$.pipe(map(config => Object.keys(config?.properties || {}).sort((a, b) => a.localeCompare(b))));
        this.propertyKeys$ = this.defaultPropertyKeys$;
        this.propertyPrefix = 'this.c8yProperties';
        this.isAutoSaveEnabled = true;
        this.language = 'html';
        this.isLoading = false;
        this.TAB_WEBCOMPONENT_LABEL = gettext('Web Component`Tab label of HTML Widget`');
        this.TAB_HTML_LABEL = gettext('HTML`Tab label of HTML Widget`');
        this.TAB_CSS_LABEL = gettext('CSS`Tab label of HTML Widget`');
        this.BUTTON_DISABLE_AUTOSAVE_LABEL = gettext('Disable auto save`An action you can do on the html widget editor`');
        this.BUTTON_ENABLE_AUTOSAVE_LABEL = gettext('Enable auto save`An action you can do on the html widget editor`');
        this.TAB_OUTLET_NAME = 'html-widget-tab-outlet';
        this.destroy$ = new Subject();
        this.suggestionProviders = [];
        this.getFormattedPropertyExpression = (key) => {
            const nonAlphanumericRegex = /[^a-zA-Z0-9]/;
            const formattedKey = nonAlphanumericRegex.test(key) ? `[${quoteAndEscape(key)}]` : `${key}`;
            return '${' + this.propertyPrefix + '?.' + formattedKey + '}';
        };
    }
    ngOnDestroy() {
        this.suggestionProviders.forEach(a => a.dispose());
        this.destroy$.next();
        this.destroy$.complete();
    }
    ngOnChanges(changes) {
        if (changes.config) {
            this.changeCode(this.config.code);
        }
        if (changes.config?.currentValue) {
            this.loadCode();
        }
    }
    loadCode() {
        this.isLoading = true;
        const isInDevMode = this.config?.devMode;
        this.language = 'html';
        if (isInDevMode) {
            this.language = 'javascript';
        }
        if (this.mode === 'css') {
            this.language = 'css';
        }
        this.value = this.mode === 'code' ? this.config.code : this.config.css;
        this.isLoading = false;
        if (this.editor) {
            queueMicrotask(() => this.formatCode());
        }
    }
    switchMode(mode) {
        this.mode = mode;
        this.loadCode();
    }
    editorLoaded(editor) {
        this.editor = editor;
        const monaco = this.editorComponent.monaco;
        this.editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
            this.saveCode();
        });
        this.propertyKeys$.pipe(takeUntil(this.destroy$)).subscribe(keys => {
            this.registerPropertySuggestions(keys);
        });
    }
    formatCode() {
        this.editor.getAction('editor.action.formatDocument').run();
    }
    redo() {
        this.editor.trigger('keyboard', 'redo', null);
    }
    undo() {
        this.editor.trigger('keyboard', 'undo', null);
    }
    changeCode($event) {
        if (this.isAutoSaveEnabled) {
            this.saveCode($event);
        }
    }
    saveCode(codeStr) {
        const code = codeStr || this.editor.getValue();
        if (this.mode === 'code') {
            this.codeChange.next(code);
            return;
        }
        this.cssChange.next(code);
    }
    insertPropertyAtCursor(key) {
        if (!this.editor) {
            return;
        }
        const text = this.getFormattedPropertyExpression(key);
        const monaco = this.editorComponent.monaco;
        const position = this.editor.getPosition();
        const range = new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column);
        this.editor.executeEdits('insert-property', [{ range, text, forceMoveMarkers: true }]);
        this.editor.focus();
    }
    registerPropertySuggestions(keys) {
        this.suggestionProviders.forEach(a => a.dispose());
        this.suggestionProviders = [];
        if (!keys.length)
            return;
        const monaco = this.editorComponent.monaco;
        const escapedPrefix = this.propertyPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const providerOptions = {
            triggerCharacters: ['$', '.', '[', "'"],
            provideCompletionItems: (model, position) => {
                const lineText = model.getValueInRange({
                    startLineNumber: position.lineNumber,
                    startColumn: 1,
                    endLineNumber: position.lineNumber,
                    endColumn: position.column
                });
                const makeRange = (startColumn) => ({
                    startLineNumber: position.lineNumber,
                    endLineNumber: position.lineNumber,
                    startColumn,
                    endColumn: position.column
                });
                // Case 1: after dot access – <prefix>?. or <prefix>.
                const dotMatch = new RegExp(escapedPrefix + '\\??\\.(' + '\\w*)$').exec(lineText);
                if (dotMatch) {
                    const typed = dotMatch[1];
                    const startColumn = position.column - typed.length;
                    const suggestions = keys
                        .filter(key => !/[^a-zA-Z0-9_]/.test(key) && key.startsWith(typed))
                        .map(key => ({
                        label: key,
                        kind: monaco.languages.CompletionItemKind.Property,
                        insertText: key,
                        range: makeRange(startColumn),
                        sortText: key
                    }));
                    return { suggestions };
                }
                // Case 2: after bracket access – <prefix>?.[ or <prefix>[
                const bracketMatch = new RegExp(escapedPrefix + '(?:\\?\\.)?' + '\\[([^\\]]*)$').exec(lineText);
                if (bracketMatch) {
                    const typed = bracketMatch[1];
                    const charAfterCursor = model.getValueInRange({
                        startLineNumber: position.lineNumber,
                        startColumn: position.column,
                        endLineNumber: position.lineNumber,
                        endColumn: position.column + 1
                    });
                    const closingBracketAlreadyPresent = charAfterCursor === ']';
                    const startColumn = position.column - typed.length;
                    const endColumn = closingBracketAlreadyPresent ? position.column + 1 : position.column;
                    const suggestions = keys.map(key => ({
                        label: key,
                        kind: monaco.languages.CompletionItemKind.Property,
                        insertText: `${quoteAndEscape(key)}]`,
                        range: {
                            startLineNumber: position.lineNumber,
                            endLineNumber: position.lineNumber,
                            startColumn,
                            endColumn
                        },
                        sortText: key
                    }));
                    return { suggestions };
                }
                // Case 3: fresh new expression – cursor is at a word boundary and the user
                // has started typing a key name.
                const freshMatch = /(?:^|[\s=(<>,;{}\[\]"'`])(\w*)$/.exec(lineText);
                if (freshMatch) {
                    const typed = freshMatch[1];
                    if (!typed)
                        return { suggestions: [] };
                    const startColumn = position.column - typed.length;
                    const suggestions = keys
                        .filter(key => key.startsWith(typed))
                        .map(key => {
                        const insertText = this.getFormattedPropertyExpression(key);
                        return {
                            label: key,
                            kind: monaco.languages.CompletionItemKind.Property,
                            insertText,
                            range: makeRange(startColumn),
                            detail: insertText,
                            filterText: key,
                            sortText: key
                        };
                    });
                    return { suggestions };
                }
                return { suggestions: [] };
            }
        };
        this.suggestionProviders.push(monaco.languages.registerCompletionItemProvider('html', providerOptions), monaco.languages.registerCompletionItemProvider('javascript', providerOptions));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: WidgetCodeEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: WidgetCodeEditorComponent, isStandalone: true, selector: "c8y-widget-code-editor", inputs: { mode: "mode", config: "config", showAdvancedSettings: "showAdvancedSettings", propertyKeys$: "propertyKeys$", propertyPrefix: "propertyPrefix", getFormattedPropertyExpression: "getFormattedPropertyExpression" }, outputs: { codeChange: "codeChange", cssChange: "cssChange" }, viewQueries: [{ propertyName: "editorComponent", first: true, predicate: EditorComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<c8y-widget-config-feedback>\n  <div class=\"d-flex\">\n    @if (config?.devMode && !config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        translate\n      >\n        Advanced developer mode\n      </span>\n    }\n  </div>\n  <div class=\"d-flex\">\n    @if (config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        [title]=\"\n          'This widget is in legacy mode. Consider to upgrade this to a new HTML widget. Read our documentation on details to transform your widget'\n            | translate\n        \"\n        translate\n      >\n        Legacy mode\n      </span>\n    }\n  </div>\n</c8y-widget-config-feedback>\n\n<div class=\"d-flex d-col fit-h fit-w\">\n  @if (showAdvancedSettings) {\n    <c8y-html-widget-advanced-settings\n      [devMode]=\"config?.devMode\"\n      [cssEncapsulation]=\"config?.options?.cssEncapsulation\"\n    ></c8y-html-widget-advanced-settings>\n  }\n\n  <fieldset class=\"c8y-fieldset p-0 overflow-hidden\">\n    <legend class=\"m-l-16 p-l-0\">{{ 'Code' | translate }}</legend>\n\n    <div class=\"btn-group btn-group-sm m-l-0 p-t-8 p-b-8 p-l-16 p-r-16 fit-w d-flex\">\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Undo' | translate\"\n        [tooltip]=\"'Undo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"undo()\"\n      >\n        <i [c8yIcon]=\"'undo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Redo' | translate\"\n        [tooltip]=\"'Redo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"redo()\"\n      >\n        <i [c8yIcon]=\"'redo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Format code' | translate\"\n        [tooltip]=\"'Format code' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"formatCode()\"\n      >\n        <i [c8yIcon]=\"'format-align-left'\"></i>\n      </button>\n\n      @let propertyKeys = propertyKeys$ | async;\n      @if (mode !== 'css' && propertyKeys?.length) {\n        <div\n          class=\"btn-group btn-group-sm m-l-4\"\n          container=\"body\"\n          dropdown\n        >\n          <button\n            class=\"btn btn-default\"\n            [attr.aria-label]=\"'Insert property' | translate\"\n            [tooltip]=\"'Insert property' | translate\"\n            placement=\"top\"\n            container=\"body\"\n            type=\"button\"\n            [delay]=\"500\"\n            dropdownToggle\n          >\n            <i [c8yIcon]=\"'plus-circle'\"></i>\n          </button>\n          <ul\n            class=\"dropdown-menu\"\n            *dropdownMenu\n          >\n            @for (key of propertyKeys; track key) {\n              <li>\n                <button\n                  class=\"dropdown-item\"\n                  type=\"button\"\n                  (click)=\"insertPropertyAtCursor(key)\"\n                >\n                  {{ key }}\n                </button>\n              </li>\n            }\n          </ul>\n        </div>\n      }\n\n      <label class=\"c8y-switch m-l-auto\">\n        <input\n          type=\"checkbox\"\n          [checked]=\"isAutoSaveEnabled\"\n          (change)=\"isAutoSaveEnabled = !isAutoSaveEnabled\"\n        />\n        <span></span>\n        <span translate>Auto save</span>\n      </label>\n    </div>\n\n    <div\n      class=\"btn-toolbar m-0 p-relative\"\n      role=\"toolbar\"\n    >\n      <c8y-tabs-outlet\n        class=\"elevation-none\"\n        [outletName]=\"TAB_OUTLET_NAME\"\n        [orientation]=\"'horizontal'\"\n        [openFirstTab]=\"false\"\n      ></c8y-tabs-outlet>\n      <c8y-tab\n        [icon]=\"'code'\"\n        [label]=\"(config?.devMode ? TAB_WEBCOMPONENT_LABEL : TAB_HTML_LABEL) | translate\"\n        [priority]=\"100\"\n        [showAlways]=\"true\"\n        [tabsOutlet]=\"TAB_OUTLET_NAME\"\n        [isActive]=\"mode === 'code'\"\n        (onSelect)=\"switchMode('code')\"\n      ></c8y-tab>\n      @if (!config?.devMode && !config?.legacy) {\n        <c8y-tab\n          [icon]=\"'c8y-css'\"\n          [label]=\"TAB_CSS_LABEL | translate\"\n          [priority]=\"0\"\n          [tabsOutlet]=\"TAB_OUTLET_NAME\"\n          [isActive]=\"mode === 'css'\"\n          (onSelect)=\"switchMode('css')\"\n        ></c8y-tab>\n      }\n    </div>\n\n    @if (!isLoading) {\n      @if (!(mode === 'css' && config?.devMode)) {\n        <c8y-editor\n          class=\"flex-grow d-block\"\n          style=\"height: 450px\"\n          [ngModel]=\"value\"\n          (ngModelChange)=\"changeCode($event)\"\n          [editorOptions]=\"{\n            language,\n            tabSize: 2,\n            insertSpaces: true,\n            minimap: { enabled: false }\n          }\"\n          (editorInit)=\"editorLoaded($event)\"\n        ></c8y-editor>\n      }\n    } @else {\n      <c8y-loading></c8y-loading>\n    }\n  </fieldset>\n</div>\n", dependencies: [{ kind: "component", type: EditorComponent, selector: "c8y-editor", inputs: ["editorOptions", "theme"], outputs: ["editorInit"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "component", type: WidgetConfigFeedbackComponent, selector: "c8y-widget-config-feedback" }, { kind: "ngmodule", type: TabsModule }, { kind: "component", type: i2$1.TabsOutletComponent, selector: "c8y-tabs-outlet,c8y-ui-tabs", inputs: ["tabs", "orientation", "navigatorOpen", "outletName", "context", "openFirstTab", "hasHeader"] }, { kind: "component", type: i2$1.TabComponent, selector: "c8y-tab", inputs: ["path", "label", "icon", "priority", "orientation", "injector", "tabsOutlet", "isActive", "text", "showAlways"], outputs: ["onSelect"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: BsDropdownModule }, { kind: "directive", type: i4.BsDropdownMenuDirective, selector: "[bsDropdownMenu],[dropdownMenu]", exportAs: ["bs-dropdown-menu"] }, { kind: "directive", type: i4.BsDropdownToggleDirective, selector: "[bsDropdownToggle],[dropdownToggle]", exportAs: ["bs-dropdown-toggle"] }, { kind: "directive", type: i4.BsDropdownDirective, selector: "[bsDropdown], [dropdown]", inputs: ["placement", "triggers", "container", "dropup", "autoClose", "isAnimated", "insideClick", "isDisabled", "isOpen"], outputs: ["isOpenChange", "onShown", "onHidden"], exportAs: ["bs-dropdown"] }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "component", type: AdvancedSettingsComponent, selector: "c8y-html-widget-advanced-settings", inputs: ["devMode", "cssEncapsulation"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: WidgetCodeEditorComponent, decorators: [{
            type: Component,
            args: [{ standalone: true, imports: [
                        AsyncPipe,
                        EditorComponent,
                        FormsModule,
                        IconDirective,
                        C8yTranslatePipe,
                        WidgetConfigFeedbackComponent,
                        TabsModule,
                        TooltipModule,
                        PopoverModule,
                        BsDropdownModule,
                        LoadingComponent,
                        AdvancedSettingsComponent
                    ], selector: 'c8y-widget-code-editor', template: "<c8y-widget-config-feedback>\n  <div class=\"d-flex\">\n    @if (config?.devMode && !config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        translate\n      >\n        Advanced developer mode\n      </span>\n    }\n  </div>\n  <div class=\"d-flex\">\n    @if (config?.legacy) {\n      <span\n        class=\"tag tag--warning text-12\"\n        [title]=\"\n          'This widget is in legacy mode. Consider to upgrade this to a new HTML widget. Read our documentation on details to transform your widget'\n            | translate\n        \"\n        translate\n      >\n        Legacy mode\n      </span>\n    }\n  </div>\n</c8y-widget-config-feedback>\n\n<div class=\"d-flex d-col fit-h fit-w\">\n  @if (showAdvancedSettings) {\n    <c8y-html-widget-advanced-settings\n      [devMode]=\"config?.devMode\"\n      [cssEncapsulation]=\"config?.options?.cssEncapsulation\"\n    ></c8y-html-widget-advanced-settings>\n  }\n\n  <fieldset class=\"c8y-fieldset p-0 overflow-hidden\">\n    <legend class=\"m-l-16 p-l-0\">{{ 'Code' | translate }}</legend>\n\n    <div class=\"btn-group btn-group-sm m-l-0 p-t-8 p-b-8 p-l-16 p-r-16 fit-w d-flex\">\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Undo' | translate\"\n        [tooltip]=\"'Undo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"undo()\"\n      >\n        <i [c8yIcon]=\"'undo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Redo' | translate\"\n        [tooltip]=\"'Redo' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"redo()\"\n      >\n        <i [c8yIcon]=\"'redo'\"></i>\n      </button>\n\n      <button\n        class=\"btn btn-default\"\n        [attr.aria-label]=\"'Format code' | translate\"\n        [tooltip]=\"'Format code' | translate\"\n        placement=\"top\"\n        container=\"body\"\n        type=\"button\"\n        [delay]=\"500\"\n        (click)=\"formatCode()\"\n      >\n        <i [c8yIcon]=\"'format-align-left'\"></i>\n      </button>\n\n      @let propertyKeys = propertyKeys$ | async;\n      @if (mode !== 'css' && propertyKeys?.length) {\n        <div\n          class=\"btn-group btn-group-sm m-l-4\"\n          container=\"body\"\n          dropdown\n        >\n          <button\n            class=\"btn btn-default\"\n            [attr.aria-label]=\"'Insert property' | translate\"\n            [tooltip]=\"'Insert property' | translate\"\n            placement=\"top\"\n            container=\"body\"\n            type=\"button\"\n            [delay]=\"500\"\n            dropdownToggle\n          >\n            <i [c8yIcon]=\"'plus-circle'\"></i>\n          </button>\n          <ul\n            class=\"dropdown-menu\"\n            *dropdownMenu\n          >\n            @for (key of propertyKeys; track key) {\n              <li>\n                <button\n                  class=\"dropdown-item\"\n                  type=\"button\"\n                  (click)=\"insertPropertyAtCursor(key)\"\n                >\n                  {{ key }}\n                </button>\n              </li>\n            }\n          </ul>\n        </div>\n      }\n\n      <label class=\"c8y-switch m-l-auto\">\n        <input\n          type=\"checkbox\"\n          [checked]=\"isAutoSaveEnabled\"\n          (change)=\"isAutoSaveEnabled = !isAutoSaveEnabled\"\n        />\n        <span></span>\n        <span translate>Auto save</span>\n      </label>\n    </div>\n\n    <div\n      class=\"btn-toolbar m-0 p-relative\"\n      role=\"toolbar\"\n    >\n      <c8y-tabs-outlet\n        class=\"elevation-none\"\n        [outletName]=\"TAB_OUTLET_NAME\"\n        [orientation]=\"'horizontal'\"\n        [openFirstTab]=\"false\"\n      ></c8y-tabs-outlet>\n      <c8y-tab\n        [icon]=\"'code'\"\n        [label]=\"(config?.devMode ? TAB_WEBCOMPONENT_LABEL : TAB_HTML_LABEL) | translate\"\n        [priority]=\"100\"\n        [showAlways]=\"true\"\n        [tabsOutlet]=\"TAB_OUTLET_NAME\"\n        [isActive]=\"mode === 'code'\"\n        (onSelect)=\"switchMode('code')\"\n      ></c8y-tab>\n      @if (!config?.devMode && !config?.legacy) {\n        <c8y-tab\n          [icon]=\"'c8y-css'\"\n          [label]=\"TAB_CSS_LABEL | translate\"\n          [priority]=\"0\"\n          [tabsOutlet]=\"TAB_OUTLET_NAME\"\n          [isActive]=\"mode === 'css'\"\n          (onSelect)=\"switchMode('css')\"\n        ></c8y-tab>\n      }\n    </div>\n\n    @if (!isLoading) {\n      @if (!(mode === 'css' && config?.devMode)) {\n        <c8y-editor\n          class=\"flex-grow d-block\"\n          style=\"height: 450px\"\n          [ngModel]=\"value\"\n          (ngModelChange)=\"changeCode($event)\"\n          [editorOptions]=\"{\n            language,\n            tabSize: 2,\n            insertSpaces: true,\n            minimap: { enabled: false }\n          }\"\n          (editorInit)=\"editorLoaded($event)\"\n        ></c8y-editor>\n      }\n    } @else {\n      <c8y-loading></c8y-loading>\n    }\n  </fieldset>\n</div>\n" }]
        }], propDecorators: { mode: [{
                type: Input
            }], config: [{
                type: Input
            }], showAdvancedSettings: [{
                type: Input
            }], codeChange: [{
                type: Output
            }], cssChange: [{
                type: Output
            }], propertyKeys$: [{
                type: Input
            }], propertyPrefix: [{
                type: Input
            }], editorComponent: [{
                type: ViewChild,
                args: [EditorComponent]
            }], getFormattedPropertyExpression: [{
                type: Input
            }] } });

class HtmlWidgetConfigComponent {
    set htmlPreviewTemplate(template) {
        if (template) {
            this.widgetConfigService.setPreview(template);
        }
        else {
            this.widgetConfigService.setPreview(null);
        }
    }
    constructor() {
        this.alert = inject(AlertService);
        this.options = inject(OptionsService);
        this.htmlWidgetConfigService = inject(HtmlWidgetConfigService);
        this.widgetConfigService = inject(WidgetConfigService);
        this.assetPropertyMappings = inject(AssetPropertyMappingsService);
        this.controls = PRESET_NAME.AUTO_REFRESH_ONLY_CONFIG;
        this.destroyRef = inject(DestroyRef);
        this.realtimeControl$ = this.widgetConfigService.currentConfig$.pipe(map$1(config => config?.refreshOption === REFRESH_OPTION.LIVE && config?.isAutoRefreshEnabled === true), distinctUntilChanged$1());
        this.propertyValues$ = this.widgetConfigService.currentConfig$.pipe(switchMap$1(config => this.assetPropertyMappings.getValues$(config?.properties, this.realtimeControl$)));
        this.globalContextState$ = this.widgetConfigService.currentConfig$.pipe(map$1(config => config));
        this.htmlWidgetConfigService.notify$
            .pipe(takeUntilDestroyed(this.destroyRef), filter$1((n) => n.type === 'asset-property-mapping-key-renamed'))
            .subscribe(notification => this.handleRename(notification));
    }
    ngOnDestroy() {
        // sadly the service is component scoped
        // but still not recycled correctly. That is why we do
        // it here.
        this.htmlWidgetConfigService.destroy();
    }
    onCodeChange(value) {
        this.htmlWidgetConfigService.changeCode(value);
    }
    onCssChange(value) {
        this.htmlWidgetConfigService.changeCss(value);
    }
    handleRename(notification) {
        const { oldKey, newKey } = notification;
        const htmlWidgetConfig = this.widgetConfigService.currentConfig;
        if (!htmlWidgetConfig?.config) {
            return;
        }
        const newCode = renamePropertyKeyInCode(htmlWidgetConfig.config.code, oldKey, newKey);
        if (newCode === htmlWidgetConfig.config.code) {
            return;
        }
        const updated = { ...htmlWidgetConfig.config, code: newCode };
        this.htmlWidgetConfigService.save(updated);
        this.htmlWidgetConfigService.configChanged$.next(updated);
        this.alert.success(gettext('Renamed asset property and updated references in HTML code.'));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetConfigComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: HtmlWidgetConfigComponent, isStandalone: true, selector: "c8y-html-widget-config", viewQueries: [{ propertyName: "htmlPreviewTemplate", first: true, predicate: ["htmlPreview"], descendants: true }], ngImport: i0, template: "<c8y-widget-code-editor\n  [config]=\"htmlWidgetConfigService.config$ | async\"\n  [mode]=\"'code'\"\n  (codeChange)=\"onCodeChange($event)\"\n  (cssChange)=\"onCssChange($event)\"\n></c8y-widget-code-editor>\n\n<ng-template #htmlPreview>\n  @if ((widgetConfigService.currentConfig$ | async)?.displayMode !== 'dashboard') {\n    <c8y-local-controls\n      [controls]=\"controls\"\n      [displayMode]=\"(widgetConfigService.currentConfig$ | async)?.displayMode\"\n      [config]=\"globalContextState$ | async\"\n      [disabled]=\"true\"\n    ></c8y-local-controls>\n  }\n  <c8y-html-frame\n    [config]=\"htmlWidgetConfigService.codeEditorChangeConfig$ | async\"\n    [device]=\"(widgetConfigService.currentConfig$ | async).device\"\n    [propertyValues$]=\"propertyValues$\"\n    [useSalt]=\"true\"\n  ></c8y-html-frame>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: HtmlFrameComponent, selector: "c8y-html-frame", inputs: ["config", "device", "propertyValues$", "realtimeControl$", "useSalt"] }, { kind: "component", type: WidgetCodeEditorComponent, selector: "c8y-widget-code-editor", inputs: ["mode", "config", "showAdvancedSettings", "propertyKeys$", "propertyPrefix", "getFormattedPropertyExpression"], outputs: ["codeChange", "cssChange"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "propagateAutoRefresh", "emitRefresh", "dashboardChild"], outputs: ["configChange", "refresh"] }, { kind: "pipe", type: AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-widget-config', standalone: true, imports: [
                        RouterModule,
                        FormsModule,
                        AsyncPipe,
                        HtmlFrameComponent,
                        WidgetCodeEditorComponent,
                        LocalControlsComponent
                    ], template: "<c8y-widget-code-editor\n  [config]=\"htmlWidgetConfigService.config$ | async\"\n  [mode]=\"'code'\"\n  (codeChange)=\"onCodeChange($event)\"\n  (cssChange)=\"onCssChange($event)\"\n></c8y-widget-code-editor>\n\n<ng-template #htmlPreview>\n  @if ((widgetConfigService.currentConfig$ | async)?.displayMode !== 'dashboard') {\n    <c8y-local-controls\n      [controls]=\"controls\"\n      [displayMode]=\"(widgetConfigService.currentConfig$ | async)?.displayMode\"\n      [config]=\"globalContextState$ | async\"\n      [disabled]=\"true\"\n    ></c8y-local-controls>\n  }\n  <c8y-html-frame\n    [config]=\"htmlWidgetConfigService.codeEditorChangeConfig$ | async\"\n    [device]=\"(widgetConfigService.currentConfig$ | async).device\"\n    [propertyValues$]=\"propertyValues$\"\n    [useSalt]=\"true\"\n  ></c8y-html-frame>\n</ng-template>\n" }]
        }], ctorParameters: () => [], propDecorators: { htmlPreviewTemplate: [{
                type: ViewChild,
                args: ['htmlPreview']
            }] } });

class HtmlWidgetPropertiesSelectorComponent {
    constructor() {
        this.widgetConfigService = inject(WidgetConfigService);
        this.clipboardService = inject(ClipboardService);
    }
    /**
     * Copies the property path to the clipboard in a format suitable for use in HTML widget.
     * For nested properties, it uses the keyPath to create a path to nested property.
     * @param context The context containing the property information.
     */
    async copyProperty(context) {
        const nonAlphanumericRegex = /[^a-zA-Z0-9]/;
        let path;
        if (context.keyPath) {
            path = context.keyPath
                .map(key => (nonAlphanumericRegex.test(key) ? `['${key}']` : `.${key}`))
                .join('');
        }
        else {
            path = nonAlphanumericRegex.test(context.name) ? `['${context.name}']` : `.${context.name}`;
        }
        const content = '${this.c8yContext' + path + '}';
        await this.clipboardService.writeText(content);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetPropertiesSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: HtmlWidgetPropertiesSelectorComponent, isStandalone: true, selector: "c8y-html-widget-properties-selector", host: { classAttribute: "bg-level-1" }, ngImport: i0, template: "<div\n  class=\"d-flex m-b-8\"\n  style=\"margin-top: -16px\"\n>\n  <em\n    class=\"m-l-24 text-muted\"\n    translate\n  >\n    How to use properties in the widget\n  </em>\n  <button\n    class=\"btn-help btn-help--sm\"\n    [attr.aria-label]=\"'Help' | translate\"\n    [popover]=\"helpContent\"\n    placement=\"right\"\n    triggers=\"focus\"\n    container=\"body\"\n    type=\"button\"\n  ></button>\n</div>\n<c8y-asset-property-list\n  class=\"inner-scroll bg-inherit d-block\"\n  style=\"max-height: 450px\"\n  [asset]=\"(widgetConfigService.currentConfig$ | async).device\"\n  [config]=\"{\n    selectMode: 'none',\n    expansionMode: 'expandedByDefault',\n    filterable: false,\n    allowDragAndDrop: true\n  }\"\n>\n  <button\n    class=\"btn-dot btn fit-h\"\n    [attr.aria-label]=\"'Copy' | translate\"\n    tooltip=\"{{ 'Copy' | translate }}\"\n    type=\"button\"\n    *c8yAssetPropertyAction=\"let context\"\n    [delay]=\"500\"\n    (click)=\"copyProperty(context)\"\n  >\n    <i class=\"dlt-c8y-icon-copy\"></i>\n  </button>\n</c8y-asset-property-list>\n\n<ng-template #helpContent>\n  <p\n    class=\"p-b-8\"\n    translate\n  >\n    Click the copy icon next to a property, then paste it into the HTML editor below as a template\n    literal.\n  </p>\n</ng-template>\n", dependencies: [{ kind: "component", type: AssetPropertyListComponent, selector: "c8y-asset-property-list", inputs: ["config", "asset", "extraProperties"], outputs: ["selectedProperties"] }, { kind: "directive", type: AssetPropertyActionDirective, selector: "[c8yAssetPropertyAction]" }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i1.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i3.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetPropertiesSelectorComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-widget-properties-selector', host: {
                        class: 'bg-level-1'
                    }, standalone: true, imports: [
                        AssetPropertyListComponent,
                        AssetPropertyActionDirective,
                        AsyncPipe,
                        C8yTranslatePipe,
                        PopoverModule,
                        TooltipModule
                    ], template: "<div\n  class=\"d-flex m-b-8\"\n  style=\"margin-top: -16px\"\n>\n  <em\n    class=\"m-l-24 text-muted\"\n    translate\n  >\n    How to use properties in the widget\n  </em>\n  <button\n    class=\"btn-help btn-help--sm\"\n    [attr.aria-label]=\"'Help' | translate\"\n    [popover]=\"helpContent\"\n    placement=\"right\"\n    triggers=\"focus\"\n    container=\"body\"\n    type=\"button\"\n  ></button>\n</div>\n<c8y-asset-property-list\n  class=\"inner-scroll bg-inherit d-block\"\n  style=\"max-height: 450px\"\n  [asset]=\"(widgetConfigService.currentConfig$ | async).device\"\n  [config]=\"{\n    selectMode: 'none',\n    expansionMode: 'expandedByDefault',\n    filterable: false,\n    allowDragAndDrop: true\n  }\"\n>\n  <button\n    class=\"btn-dot btn fit-h\"\n    [attr.aria-label]=\"'Copy' | translate\"\n    tooltip=\"{{ 'Copy' | translate }}\"\n    type=\"button\"\n    *c8yAssetPropertyAction=\"let context\"\n    [delay]=\"500\"\n    (click)=\"copyProperty(context)\"\n  >\n    <i class=\"dlt-c8y-icon-copy\"></i>\n  </button>\n</c8y-asset-property-list>\n\n<ng-template #helpContent>\n  <p\n    class=\"p-b-8\"\n    translate\n  >\n    Click the copy icon next to a property, then paste it into the HTML editor below as a template\n    literal.\n  </p>\n</ng-template>\n" }]
        }] });

class HtmlWidgetComponent {
    constructor() {
        this.config = input(...(ngDevMode ? [undefined, { debugName: "config" }] : []));
        this.realtimeControl$ = new BehaviorSubject(false);
        this.refresh$ = new Subject();
        this.SERVER_ERROR_TEXT = gettext('Server error occurred.');
        this.propertyValues$ = computed(() => this.assetPropertiesMappings
            .getValues$(this.config()?.properties, this.realtimeControl$.asObservable(), this.refresh$.asObservable())
            .pipe(tap({
            next: () => this.isLoading.set(false),
            error: () => this.isLoading.set(false)
        }), retry({ delay: err => this.handleFetchError(err) })), ...(ngDevMode ? [{ debugName: "propertyValues$" }] : []));
        this.displayMode = signal(GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD, ...(ngDevMode ? [{ debugName: "displayMode" }] : []));
        this.contextConfig = signal({}, ...(ngDevMode ? [{ debugName: "contextConfig" }] : []));
        this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
        /**
         * Controls link state to global context:
         * - true/undefined: Widget follows global time context
         * - false: Widget uses local state, ignores global changes
         * Set to false when user is interacting (scrolling, zooming) to prevent interruption
         */
        this.isLinkedToGlobal = signal(undefined, ...(ngDevMode ? [{ debugName: "isLinkedToGlobal" }] : []));
        this.GLOBAL_CONTEXT_DISPLAY_MODE = GLOBAL_CONTEXT_DISPLAY_MODE;
        this.PRESET_NAME = PRESET_NAME;
        this.assetPropertiesMappings = inject(AssetPropertyMappingsService);
    }
    ngOnInit() {
        this.alerts?.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.TEMPORARY_OR_PERMANENT);
        const config = this.config();
        if (config?.html && !config.config) {
            config.config = this.mapLegacyConfig(config);
        }
        const { displayMode = GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD, dateTimeContext, aggregation, isAutoRefreshEnabled, refreshInterval, refreshOption } = config;
        this.displayMode.set(displayMode);
        this.contextConfig.set({
            dateTimeContext,
            aggregation,
            isAutoRefreshEnabled,
            refreshInterval,
            refreshOption
        });
        // Initialize realtime from persisted config — controls may be hidden in
        // config display mode (AUTO_REFRESH_ONLY.config = []), in which case
        // onContextChange never fires and realtimeControl$ would stay false.
        this.realtimeControl$.next(this.shouldEnableRealtime());
    }
    onContextChange(event) {
        const { context } = event;
        this.contextConfig.set(context);
        this.realtimeControl$.next(this.shouldEnableRealtime());
        this.onRefresh();
    }
    onRefresh() {
        this.isLoading.set(true);
        this.refresh$.next();
    }
    shouldEnableRealtime() {
        const ctx = this.contextConfig();
        return ctx.refreshOption === REFRESH_OPTION.LIVE && ctx.isAutoRefreshEnabled === true;
    }
    mapLegacyConfig(current) {
        const isAlreadyInAdvancedMode = current?.config?.devMode === true;
        if (isAlreadyInAdvancedMode) {
            return current.config;
        }
        return {
            code: current.html,
            css: '',
            legacy: true,
            devMode: false,
            options: {
                cssEncapsulation: false,
                advancedSecurity: current.sanitization === 'strict'
            }
        };
    }
    handleFetchError(err) {
        this.alerts?.addAlerts(new DynamicComponentAlert({
            type: 'warning',
            text: err?.message ?? this.SERVER_ERROR_TEXT
        }));
        return this.refresh$.pipe(take(1));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: HtmlWidgetComponent, isStandalone: true, selector: "c8y-html-widget", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n  <c8y-global-context-connector\n    [controls]=\"PRESET_NAME.AUTO_REFRESH_ONLY_CONFIG\"\n    [config]=\"contextConfig()\"\n    [isLoading]=\"isLoading()\"\n    [linked]=\"isLinkedToGlobal()\"\n    [propagateAutoRefresh]=\"false\"\n    (configChange)=\"onContextChange($event)\"\n    (refresh)=\"onRefresh()\"\n  >\n  </c8y-global-context-connector>\n} @else {\n  <c8y-local-controls\n    [controls]=\"PRESET_NAME.AUTO_REFRESH_ONLY_CONFIG\"\n    [displayMode]=\"displayMode()\"\n    [config]=\"contextConfig()\"\n    [isLoading]=\"isLoading()\"\n    [propagateAutoRefresh]=\"false\"\n    (configChange)=\"onContextChange($event)\"\n    (refresh)=\"onRefresh()\"\n  >\n  </c8y-local-controls>\n}\n\n<div class=\"p-16\">\n  <c8y-html-frame\n    [config]=\"config().config\"\n    [device]=\"config().device\"\n    [propertyValues$]=\"propertyValues$()\"\n  ></c8y-html-frame>\n</div>\n", dependencies: [{ kind: "ngmodule", type: RouterModule }, { kind: "component", type: HtmlFrameComponent, selector: "c8y-html-frame", inputs: ["config", "device", "propertyValues$", "realtimeControl$", "useSalt"] }, { kind: "component", type: GlobalContextConnectorComponent, selector: "c8y-global-context-connector", inputs: ["controls", "config", "isLoading", "dashboardChild", "linked", "propagateAutoRefresh", "emitRefresh"], outputs: ["configChange", "refresh", "linkedChange"] }, { kind: "component", type: LocalControlsComponent, selector: "c8y-local-controls", inputs: ["controls", "displayMode", "config", "isLoading", "disabled", "propagateAutoRefresh", "emitRefresh", "dashboardChild"], outputs: ["configChange", "refresh"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: HtmlWidgetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-html-widget', standalone: true, imports: [
                        RouterModule,
                        HtmlFrameComponent,
                        GlobalContextConnectorComponent,
                        LocalControlsComponent
                    ], template: "@if (displayMode() === GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD) {\n  <c8y-global-context-connector\n    [controls]=\"PRESET_NAME.AUTO_REFRESH_ONLY_CONFIG\"\n    [config]=\"contextConfig()\"\n    [isLoading]=\"isLoading()\"\n    [linked]=\"isLinkedToGlobal()\"\n    [propagateAutoRefresh]=\"false\"\n    (configChange)=\"onContextChange($event)\"\n    (refresh)=\"onRefresh()\"\n  >\n  </c8y-global-context-connector>\n} @else {\n  <c8y-local-controls\n    [controls]=\"PRESET_NAME.AUTO_REFRESH_ONLY_CONFIG\"\n    [displayMode]=\"displayMode()\"\n    [config]=\"contextConfig()\"\n    [isLoading]=\"isLoading()\"\n    [propagateAutoRefresh]=\"false\"\n    (configChange)=\"onContextChange($event)\"\n    (refresh)=\"onRefresh()\"\n  >\n  </c8y-local-controls>\n}\n\n<div class=\"p-16\">\n  <c8y-html-frame\n    [config]=\"config().config\"\n    [device]=\"config().device\"\n    [propertyValues$]=\"propertyValues$()\"\n  ></c8y-html-frame>\n</div>\n" }]
        }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: false }] }] } });

/**
 * Generated bundle index. Do not edit.
 */

export { AdvancedSettingsComponent, HtmlAiChatToolDetailsComponent, HtmlFrameComponent, HtmlWidgetComponent, HtmlWidgetConfigComponent, HtmlWidgetConfigService, HtmlWidgetPropertiesSelectorComponent, INITIAL_CSS_FORMATTED, INITIAL_HTML_FORMATTED, WidgetCodeEditorComponent, defaultWebComponentAttributeNameContext, defaultWebComponentName, legacyTemplate, webComponentTemplate };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-html-widget.mjs.map
