import * as i2 from '@angular/common';
import { CommonModule } from '@angular/common';
import * as i0 from '@angular/core';
import { Input, Component, signal, computed, ViewChild, Inject, Optional } from '@angular/core';
import * as i1 from '@c8y/client';
import * as i2$1 from '@c8y/ngx-components';
import { C8yTranslateDirective, LoadingComponent, DatePipe, CommonModule as CommonModule$1, FormsModule as FormsModule$1, C8yTranslatePipe, DismissAlertStrategy, DynamicComponentAlert, AssetLinkPipe } from '@c8y/ngx-components';
import * as i6 from '@angular/forms';
import { NgForm, ControlContainer, FormsModule, ReactiveFormsModule } from '@angular/forms';
import * as i4$1 from '@c8y/ngx-components/context-dashboard';
import { gettext } from '@c8y/ngx-components/gettext';
import * as i5 from '@c8y/ngx-components/global-context';
import { GLOBAL_CONTEXT_DISPLAY_MODE, GlobalContextWidgetWrapperComponent } from '@c8y/ngx-components/global-context';
import * as i7 from '@c8y/ngx-components/icon-selector';
import { IconSelectorModule } from '@c8y/ngx-components/icon-selector';
import * as i4 from '@c8y/ngx-components/map';
import { defaultFitBoundsOptions, MAP_DEFAULT_CONFIG, MapModule, MapComponent, ClusterMapComponent } from '@c8y/ngx-components/map';
import * as i3 from '@ngx-translate/core';
import { isUndefined } from 'lodash';
import * as i9 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';
import * as i8 from 'ngx-bootstrap/tooltip';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import * as i10 from 'rxjs';
import { Subject, fromEvent } from 'rxjs';
import { takeUntil, combineLatestWith, take, filter } from 'rxjs/operators';
import { RouterLink } from '@angular/router';
import { defaultWidgetIds } from '@c8y/ngx-components/widgets/definitions';
import { merge, debounce } from 'lodash-es';

class MapEventInfoComponent {
    constructor(eventService) {
        this.eventService = eventService;
        this.loading = true;
    }
    async ngOnChanges() {
        this.loading = true;
        const { data } = await this.eventService.list({
            dateFrom: '1970-01-01',
            dateTo: new Date(Date.now()).toISOString(),
            fragmentType: 'c8y_Position',
            pageSize: 1,
            source: this.asset.id
        });
        this.event = data[0];
        this.loading = false;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapEventInfoComponent, deps: [{ token: i1.EventService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: MapEventInfoComponent, isStandalone: true, selector: "c8y-map-event-info", inputs: { asset: "asset" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"text-muted text-10 p-t-4 p-b-4\">\n  <span translate>Last position update:</span>\n  <br />\n  <c8y-loading *ngIf=\"loading\"></c8y-loading>\n  <ng-container *ngIf=\"!loading\">\n    <i *ngIf=\"!event\" translate>No information found</i>\n    <ng-container *ngIf=\"event\">\n      <i>{{ event.time | c8yDate }}</i>\n      <ng-content></ng-content>\n    </ng-container>\n  </ng-container>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "component", type: LoadingComponent, selector: "c8y-loading", inputs: ["layout", "progress", "message"] }, { kind: "pipe", type: DatePipe, name: "c8yDate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapEventInfoComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-map-event-info', standalone: true, imports: [CommonModule, C8yTranslateDirective, DatePipe, LoadingComponent], template: "<div class=\"text-muted text-10 p-t-4 p-b-4\">\n  <span translate>Last position update:</span>\n  <br />\n  <c8y-loading *ngIf=\"loading\"></c8y-loading>\n  <ng-container *ngIf=\"!loading\">\n    <i *ngIf=\"!event\" translate>No information found</i>\n    <ng-container *ngIf=\"event\">\n      <i>{{ event.time | c8yDate }}</i>\n      <ng-content></ng-content>\n    </ng-container>\n  </ng-container>\n</div>\n" }]
        }], ctorParameters: () => [{ type: i1.EventService }], propDecorators: { asset: [{
                type: Input
            }] } });

class MapWidgetConfigComponent {
    set previewMapSet(template) {
        if (template) {
            this.widgetConfigService.setPreview(template);
            return;
        }
        this.widgetConfigService.setPreview(null);
    }
    constructor(mapService, alertService, translateService, defaultMapConfig, widgetConfigService) {
        this.mapService = mapService;
        this.alertService = alertService;
        this.translateService = translateService;
        this.defaultMapConfig = defaultMapConfig;
        this.widgetConfigService = widgetConfigService;
        this.config = {
            mapConfig: undefined
        };
        this.device = signal(undefined, ...(ngDevMode ? [{ debugName: "device" }] : []));
        this.assets = signal([], ...(ngDevMode ? [{ debugName: "assets" }] : []));
        this.canAutoCenter = computed(() => {
            const assetsList = this.assets();
            const deviceValue = this.device();
            const isSingleAsset = assetsList?.length === 1;
            const idMatchesDevice = deviceValue?.id === assetsList[0]?.id;
            if (isSingleAsset && idMatchesDevice) {
                return this.mapService.hasPosition(assetsList[0]);
            }
            return false;
        }, ...(ngDevMode ? [{ debugName: "canAutoCenter" }] : []));
        this.isPositionedDevice = computed(() => {
            const assetsList = this.assets();
            const deviceValue = this.device();
            const isSingleAsset = assetsList?.length === 1;
            const idMatchesDevice = deviceValue?.id === assetsList[0]?.id;
            if (isSingleAsset && idMatchesDevice) {
                return this.mapService.isPositionedDevice(assetsList[0]);
            }
            return false;
        }, ...(ngDevMode ? [{ debugName: "isPositionedDevice" }] : []));
        this.refreshOption = 'live';
        this.destroyed$ = new Subject();
        // JS precision for floating point number is 15 for 64-bit and because of this provided to leaflet are recalculated
        // to values almost-like provided (e.g. 0 is recalculated to 1.46e-14; for 180 it's 180.00000000000003).
        // To ensure provided value has no floating point error, number needs to be rounded to fixed point 13.
        this.FIXED_POINT_DIGITS = 13;
    }
    ngOnDestroy() {
        this.destroyed$.next();
    }
    ngOnInit() {
        this.device.set(this.config.device);
        this.defaultMapConfig.pipe(takeUntil(this.destroyed$)).subscribe(defaultConfig => {
            this.formConfig = {
                zoomLevel: defaultConfig.zoomLevel,
                center: defaultConfig.center
            };
            this.initForm();
        });
    }
    async fitToBound() {
        if (this.formConfig.autoFitToBounds) {
            const bounds = await this.mapService.getAssetsBounds(this.assets());
            this.previewMap.map.fitBounds(bounds, defaultFitBoundsOptions);
        }
    }
    async initForm() {
        if (!this.config.mapConfig) {
            this.config.mapConfig = this.formConfig;
        }
        this.formConfig = {
            ...this.formConfig,
            ...this.config.mapConfig,
            disablePan: false,
            displayMode: this.config.mapConfig?.displayMode ?? GLOBAL_CONTEXT_DISPLAY_MODE.DASHBOARD,
            // Keep both in sync
            realtime: this.config.mapConfig?.isRealtimeEnabled ?? this.config.mapConfig?.realtime ?? false,
            refreshOption: this.config.mapConfig?.refreshOption,
            refreshInterval: this.config.mapConfig?.refreshInterval ?? 30_000
        };
        await this.updateAsset();
    }
    async ngOnChanges(changes) {
        if (changes.config) {
            this.device.set(this.config.device);
            if (changes.config.currentValue !== changes.config.previousValue &&
                !changes.config.firstChange) {
                await this.updateAsset();
            }
        }
    }
    previewMapInit(leaflet) {
        this.leaflet = leaflet;
        this.addCenterIcon(this.formConfig.center[0], this.formConfig.center[1]);
    }
    onBeforeSave() {
        this.config.mapConfig = this.formConfig;
        return true;
    }
    zoomLevelChanged() {
        this.config.mapConfig = {
            ...this.config.mapConfig,
            zoomLevel: this.formConfig.zoomLevel
        };
    }
    changeCenter() {
        if (this.centerIcon) {
            this.centerIcon.remove();
        }
        this.config.mapConfig = { ...this.formConfig, center: [...this.formConfig.center] };
        this.addCenterIcon(this.formConfig.center[0], this.formConfig.center[1]);
    }
    changeCenterOnEnterKey(event) {
        event.preventDefault();
        this.changeCenter();
    }
    onPreviewZoomStart() {
        this.centerIcon.remove();
    }
    onPreviewZoomEnd(event) {
        this.formConfig.zoomLevel = Math.floor(event.target.getZoom());
    }
    onPreviewMapMove(event) {
        if (this.centerIcon) {
            this.centerIcon.remove();
        }
        const { lat, lng } = event.target.getCenter();
        const fixedLat = +lat.toFixed(this.FIXED_POINT_DIGITS);
        const fixedLng = +lng.toFixed(this.FIXED_POINT_DIGITS);
        this.addCenterIcon(fixedLat, fixedLng);
        this.formConfig.center = [fixedLat, fixedLng];
    }
    useOwnPosition() {
        navigator.geolocation.getCurrentPosition(({ coords }) => {
            this.formConfig.center = [coords.latitude, coords.longitude];
            this.changeCenter();
        });
    }
    centerToAsset() {
        this.formConfig.center = [
            this.config.device.c8y_Position.lat,
            this.config.device.c8y_Position.lng
        ];
        this.changeCenter();
    }
    async fitToBounds() {
        const assetsList = this.assets();
        if (!assetsList?.length || !this.previewMap?.map) {
            return;
        }
        const bounds = await this.mapService.getAssetsBounds(assetsList);
        // map.fitBounds causes zoom and move events, so get new center and zoom level only when these events are finished
        fromEvent(this.previewMap.map, 'moveend')
            .pipe(combineLatestWith(fromEvent(this.previewMap.map, 'zoomend')), take(1))
            .subscribe(() => {
            const center = this.previewMap.map.getCenter();
            const zoom = this.previewMap.map.getZoom();
            const fixedLat = +center.lat.toFixed(this.FIXED_POINT_DIGITS);
            const fixedLng = +center.lng.toFixed(this.FIXED_POINT_DIGITS);
            this.formConfig.center = [fixedLat, fixedLng];
            this.formConfig.zoomLevel = Math.floor(zoom);
            this.changeCenter();
        });
        this.previewMap.map.fitBounds(bounds, defaultFitBoundsOptions);
    }
    selectIcon(icon) {
        this.formConfig.icon = icon;
        this.config.mapConfig.icon = icon;
        this.previewMap.refreshMarkers();
    }
    async updateAsset() {
        const { data, paging } = await this.mapService.getAllPositionMOs(this.device());
        const onlyAssetsWithLatLng = data.filter(asset => !isUndefined(asset.c8y_Position?.lat) && !isUndefined(asset.c8y_Position?.lng));
        this.assets.set(onlyAssetsWithLatLng);
        if (paging.totalPages > 1) {
            this.alertService.danger(gettext('It might be possible that assets are not shown in the preview, as the current selection has more than 500 assets and the preview only supports a maximum of 500 assets.'));
        }
    }
    addCenterIcon(lat, lng) {
        const titleText = this.translateService.instant(gettext('Map center'));
        const icon = this.leaflet.divIcon({
            html: `<i style="pointer-events: none" class="c8y-map-marker-icon text-muted dlt-c8y-icon-target icon-2x" title="${titleText}" />`
        });
        this.centerIcon = this.leaflet.marker([lat, lng], {
            icon
        });
        this.previewMap.addMarkerToMap(this.centerIcon);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapWidgetConfigComponent, deps: [{ token: i4.MapService }, { token: i2$1.AlertService }, { token: i3.TranslateService }, { token: MAP_DEFAULT_CONFIG }, { token: i4$1.WidgetConfigService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: MapWidgetConfigComponent, isStandalone: true, selector: "c8y-map-widget-config", inputs: { config: "config" }, viewQueries: [{ propertyName: "previewMap", first: true, predicate: MapComponent, descendants: true }, { propertyName: "previewMapSet", first: true, predicate: ["previewMap"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (formConfig) {\n  <fieldset class=\"c8y-fieldset\">\n    <legend>{{ 'Map' | translate }}</legend>\n    <div class=\"row tight-grid\">\n      <div class=\"col-xs-3\">\n        <c8y-form-group class=\"m-b-16 text-center form-group-sm\">\n          <label translate>Marker icon</label>\n          <c8y-icon-selector-wrapper\n            [canRemoveIcon]=\"true\"\n            [iconSize]=\"16\"\n            [selectedIcon]=\"formConfig.icon\"\n            (onSelect)=\"selectIcon($event)\"\n          ></c8y-icon-selector-wrapper>\n        </c8y-form-group>\n      </div>\n      <div [ngClass]=\"{ 'col-xs-6': !isPositionedDevice(), 'col-xs-9': isPositionedDevice() }\">\n        <div class=\"form-group form-group-sm\">\n          <label translate>Zoom level</label>\n          <c8y-range\n            class=\"label-bottom\"\n            name=\"zoomLevel\"\n            #range\n            [(ngModel)]=\"formConfig.zoomLevel\"\n            (change)=\"zoomLevelChanged()\"\n          >\n            <input\n              type=\"range\"\n              min=\"0\"\n              max=\"18\"\n              step=\"1\"\n              [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n            />\n          </c8y-range>\n        </div>\n      </div>\n      @if (!isPositionedDevice()) {\n        <div class=\"col-xs-3\">\n          <c8y-form-group class=\"m-b-16 text-center form-group-sm\">\n            <label translate>Fit to bounds</label>\n            <div class=\"m-t-4 d-flex j-c-center\">\n              <label class=\"c8y-switch c8y-switch--inline\">\n                <input\n                  name=\"autoFitToBounds\"\n                  type=\"checkbox\"\n                  [(ngModel)]=\"formConfig.autoFitToBounds\"\n                  (ngModelChange)=\"fitToBound()\"\n                />\n                <span></span>\n              </label>\n              <button\n                class=\"btn-help\"\n                aria-label=\"Help content\"\n                [popover]=\"\n                  'If enabled, the zoom level and center bound is always adjusted to fit all assets within the map view.'\n                    | translate\n                \"\n                placement=\"top\"\n                triggers=\"focus\"\n                container=\"body\"\n                type=\"button\"\n              ></button>\n            </div>\n          </c8y-form-group>\n        </div>\n      }\n    </div>\n    <c8y-form-group class=\"form-group-sm\">\n      <label translate>Center bound</label>\n      <div class=\"input-group input-group-sm\">\n        <input\n          class=\"form-control\"\n          placeholder=\"{{ 'lat.`latitude`' | translate }}\"\n          name=\"centerLat\"\n          type=\"number\"\n          required\n          [(ngModel)]=\"formConfig.center[0]\"\n          (change)=\"changeCenter()\"\n          (keydown.enter)=\"changeCenterOnEnterKey($event)\"\n          min=\"-90\"\n          max=\"90\"\n          step=\"0.1\"\n          [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n        />\n        <input\n          class=\"form-control\"\n          placeholder=\"{{ 'lng.`longitude`' | translate }}\"\n          name=\"centerLng\"\n          type=\"number\"\n          required\n          min=\"-180\"\n          max=\"180\"\n          [(ngModel)]=\"formConfig.center[1]\"\n          (change)=\"changeCenter()\"\n          (keydown.enter)=\"changeCenterOnEnterKey($event)\"\n          min=\"-180\"\n          max=\"180\"\n          step=\"0.1\"\n          [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n        />\n        <div class=\"input-group-btn\">\n          <button\n            class=\"btn btn-default\"\n            [attr.aria-label]=\"'Use your location' | translate\"\n            [tooltip]=\"'Use your location' | translate\"\n            placement=\"top\"\n            container=\"body\"\n            (click)=\"useOwnPosition()\"\n            [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n          >\n            <i\n              class=\"icon-14\"\n              c8yIcon=\"location-arrow\"\n            ></i>\n          </button>\n        </div>\n        <div class=\"input-group-btn\">\n          @if (canAutoCenter()) {\n            <button\n              class=\"btn btn-default\"\n              [attr.aria-label]=\"'Use selected asset location' | translate\"\n              [tooltip]=\"'Use selected asset location' | translate\"\n              placement=\"top\"\n              container=\"body\"\n              (click)=\"centerToAsset()\"\n              [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n            >\n              <i\n                class=\"icon-14\"\n                c8yIcon=\"location\"\n              ></i>\n            </button>\n          } @else {\n            <button\n              class=\"btn btn-default\"\n              [attr.aria-label]=\"'Fit to assets bounds' | translate\"\n              [tooltip]=\"'Fit to assets bounds' | translate\"\n              placement=\"top\"\n              container=\"body\"\n              (click)=\"fitToBounds()\"\n              [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n            >\n              <i\n                class=\"icon-14\"\n                c8yIcon=\"waypoint-map\"\n              ></i>\n            </button>\n          }\n        </div>\n      </div>\n\n      @if (!formConfig.autoFitToBounds) {\n        <c8y-messages\n          [helpMessage]=\"'Drag the map to the desired position' | translate\"\n        ></c8y-messages>\n      }\n    </c8y-form-group>\n  </fieldset>\n\n  @if (isPositionedDevice()) {\n    <fieldset class=\"c8y-fieldset\">\n      <legend>{{ 'Follow' | translate }}</legend>\n      <div class=\"row tight-grid d-flex a-i-center\">\n        <div class=\"col-xs-12\">\n          <div class=\"form-group form-group-sm m-b-16\">\n            <label class=\"c8y-switch c8y-switch--inline\">\n              <input\n                name=\"follow\"\n                type=\"checkbox\"\n                [(ngModel)]=\"formConfig.follow\"\n              />\n              <span></span>\n              <span\n                class=\"text-12\"\n                translate\n              >\n                Follow selected\n              </span>\n            </label>\n          </div>\n        </div>\n      </div>\n    </fieldset>\n  }\n}\n\n<ng-template #previewMap>\n  <div style=\"width: 100%; height: 100%\">\n    @if (config.mapConfig) {\n      <c8y-map\n        [assets]=\"assets()\"\n        [config]=\"config.mapConfig\"\n        (onMove)=\"onPreviewMapMove($event)\"\n        (onZoomStart)=\"onPreviewZoomStart()\"\n        (onZoomEnd)=\"onPreviewZoomEnd($event)\"\n        (onInit)=\"previewMapInit($event)\"\n      ></c8y-map>\n    }\n  </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i6.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i6.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i6.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i6.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i6.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i6.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i6.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i6.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i2$1.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2$1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "directive", type: i2$1.MinValidationDirective, selector: "[min]", inputs: ["min"] }, { kind: "directive", type: i2$1.MaxValidationDirective, selector: "[max]", inputs: ["max"] }, { kind: "component", type: i2$1.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "component", type: i2$1.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage", "additionalMessages"] }, { kind: "directive", type: i2$1.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i2$1.RangeDirective, selector: "input[type=\"range\"]" }, { kind: "component", type: i2$1.RangeComponent, selector: "c8y-range", inputs: ["valueDisplayMode"] }, { kind: "ngmodule", type: IconSelectorModule }, { kind: "component", type: i7.IconSelectorWrapperComponent, selector: "c8y-icon-selector-wrapper", inputs: ["canRemoveIcon", "selectedIcon", "iconSize"], outputs: ["onSelect"] }, { kind: "ngmodule", type: MapModule }, { kind: "component", type: i4.MapComponent, selector: "c8y-map", inputs: ["config", "assets", "polyline$", "polylineOptions"], outputs: ["onRealtimeUpdate", "onMove", "onMoveEnd", "onZoomStart", "onZoomEnd", "onMap", "onInit"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i8.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: ReactiveFormsModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "directive", type: i9.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i2$1.C8yTranslatePipe, name: "translate" }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapWidgetConfigComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-map-widget-config', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], standalone: true, imports: [
                        CommonModule,
                        FormsModule,
                        CommonModule$1,
                        FormsModule$1,
                        IconSelectorModule,
                        MapModule,
                        TooltipModule,
                        ReactiveFormsModule,
                        PopoverModule,
                        C8yTranslatePipe
                    ], template: "@if (formConfig) {\n  <fieldset class=\"c8y-fieldset\">\n    <legend>{{ 'Map' | translate }}</legend>\n    <div class=\"row tight-grid\">\n      <div class=\"col-xs-3\">\n        <c8y-form-group class=\"m-b-16 text-center form-group-sm\">\n          <label translate>Marker icon</label>\n          <c8y-icon-selector-wrapper\n            [canRemoveIcon]=\"true\"\n            [iconSize]=\"16\"\n            [selectedIcon]=\"formConfig.icon\"\n            (onSelect)=\"selectIcon($event)\"\n          ></c8y-icon-selector-wrapper>\n        </c8y-form-group>\n      </div>\n      <div [ngClass]=\"{ 'col-xs-6': !isPositionedDevice(), 'col-xs-9': isPositionedDevice() }\">\n        <div class=\"form-group form-group-sm\">\n          <label translate>Zoom level</label>\n          <c8y-range\n            class=\"label-bottom\"\n            name=\"zoomLevel\"\n            #range\n            [(ngModel)]=\"formConfig.zoomLevel\"\n            (change)=\"zoomLevelChanged()\"\n          >\n            <input\n              type=\"range\"\n              min=\"0\"\n              max=\"18\"\n              step=\"1\"\n              [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n            />\n          </c8y-range>\n        </div>\n      </div>\n      @if (!isPositionedDevice()) {\n        <div class=\"col-xs-3\">\n          <c8y-form-group class=\"m-b-16 text-center form-group-sm\">\n            <label translate>Fit to bounds</label>\n            <div class=\"m-t-4 d-flex j-c-center\">\n              <label class=\"c8y-switch c8y-switch--inline\">\n                <input\n                  name=\"autoFitToBounds\"\n                  type=\"checkbox\"\n                  [(ngModel)]=\"formConfig.autoFitToBounds\"\n                  (ngModelChange)=\"fitToBound()\"\n                />\n                <span></span>\n              </label>\n              <button\n                class=\"btn-help\"\n                aria-label=\"Help content\"\n                [popover]=\"\n                  'If enabled, the zoom level and center bound is always adjusted to fit all assets within the map view.'\n                    | translate\n                \"\n                placement=\"top\"\n                triggers=\"focus\"\n                container=\"body\"\n                type=\"button\"\n              ></button>\n            </div>\n          </c8y-form-group>\n        </div>\n      }\n    </div>\n    <c8y-form-group class=\"form-group-sm\">\n      <label translate>Center bound</label>\n      <div class=\"input-group input-group-sm\">\n        <input\n          class=\"form-control\"\n          placeholder=\"{{ 'lat.`latitude`' | translate }}\"\n          name=\"centerLat\"\n          type=\"number\"\n          required\n          [(ngModel)]=\"formConfig.center[0]\"\n          (change)=\"changeCenter()\"\n          (keydown.enter)=\"changeCenterOnEnterKey($event)\"\n          min=\"-90\"\n          max=\"90\"\n          step=\"0.1\"\n          [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n        />\n        <input\n          class=\"form-control\"\n          placeholder=\"{{ 'lng.`longitude`' | translate }}\"\n          name=\"centerLng\"\n          type=\"number\"\n          required\n          min=\"-180\"\n          max=\"180\"\n          [(ngModel)]=\"formConfig.center[1]\"\n          (change)=\"changeCenter()\"\n          (keydown.enter)=\"changeCenterOnEnterKey($event)\"\n          min=\"-180\"\n          max=\"180\"\n          step=\"0.1\"\n          [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n        />\n        <div class=\"input-group-btn\">\n          <button\n            class=\"btn btn-default\"\n            [attr.aria-label]=\"'Use your location' | translate\"\n            [tooltip]=\"'Use your location' | translate\"\n            placement=\"top\"\n            container=\"body\"\n            (click)=\"useOwnPosition()\"\n            [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n          >\n            <i\n              class=\"icon-14\"\n              c8yIcon=\"location-arrow\"\n            ></i>\n          </button>\n        </div>\n        <div class=\"input-group-btn\">\n          @if (canAutoCenter()) {\n            <button\n              class=\"btn btn-default\"\n              [attr.aria-label]=\"'Use selected asset location' | translate\"\n              [tooltip]=\"'Use selected asset location' | translate\"\n              placement=\"top\"\n              container=\"body\"\n              (click)=\"centerToAsset()\"\n              [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n            >\n              <i\n                class=\"icon-14\"\n                c8yIcon=\"location\"\n              ></i>\n            </button>\n          } @else {\n            <button\n              class=\"btn btn-default\"\n              [attr.aria-label]=\"'Fit to assets bounds' | translate\"\n              [tooltip]=\"'Fit to assets bounds' | translate\"\n              placement=\"top\"\n              container=\"body\"\n              (click)=\"fitToBounds()\"\n              [disabled]=\"!isPositionedDevice() && formConfig.autoFitToBounds\"\n            >\n              <i\n                class=\"icon-14\"\n                c8yIcon=\"waypoint-map\"\n              ></i>\n            </button>\n          }\n        </div>\n      </div>\n\n      @if (!formConfig.autoFitToBounds) {\n        <c8y-messages\n          [helpMessage]=\"'Drag the map to the desired position' | translate\"\n        ></c8y-messages>\n      }\n    </c8y-form-group>\n  </fieldset>\n\n  @if (isPositionedDevice()) {\n    <fieldset class=\"c8y-fieldset\">\n      <legend>{{ 'Follow' | translate }}</legend>\n      <div class=\"row tight-grid d-flex a-i-center\">\n        <div class=\"col-xs-12\">\n          <div class=\"form-group form-group-sm m-b-16\">\n            <label class=\"c8y-switch c8y-switch--inline\">\n              <input\n                name=\"follow\"\n                type=\"checkbox\"\n                [(ngModel)]=\"formConfig.follow\"\n              />\n              <span></span>\n              <span\n                class=\"text-12\"\n                translate\n              >\n                Follow selected\n              </span>\n            </label>\n          </div>\n        </div>\n      </div>\n    </fieldset>\n  }\n}\n\n<ng-template #previewMap>\n  <div style=\"width: 100%; height: 100%\">\n    @if (config.mapConfig) {\n      <c8y-map\n        [assets]=\"assets()\"\n        [config]=\"config.mapConfig\"\n        (onMove)=\"onPreviewMapMove($event)\"\n        (onZoomStart)=\"onPreviewZoomStart()\"\n        (onZoomEnd)=\"onPreviewZoomEnd($event)\"\n        (onInit)=\"previewMapInit($event)\"\n      ></c8y-map>\n    }\n  </div>\n</ng-template>\n" }]
        }], ctorParameters: () => [{ type: i4.MapService }, { type: i2$1.AlertService }, { type: i3.TranslateService }, { type: i10.Observable, decorators: [{
                    type: Inject,
                    args: [MAP_DEFAULT_CONFIG]
                }] }, { type: i4$1.WidgetConfigService }], propDecorators: { config: [{
                type: Input
            }], previewMap: [{
                type: ViewChild,
                args: [MapComponent]
            }], previewMapSet: [{
                type: ViewChild,
                args: ['previewMap']
            }] } });

class MapWidgetComponent {
    constructor(dashboardChild, dashboardContextComponent, inventory, mapService, widgetsDashboardComponent, dynamicComponentService, widgetConfigMigrationService) {
        this.dashboardContextComponent = dashboardContextComponent;
        this.inventory = inventory;
        this.mapService = mapService;
        this.widgetsDashboardComponent = widgetsDashboardComponent;
        this.dynamicComponentService = dynamicComponentService;
        this.widgetConfigMigrationService = widgetConfigMigrationService;
        this.mapConfig = { center: [0, 0] };
        this.TIMEOUT_ERROR_TEXT = gettext('The request is taking longer than usual. We apologize for the inconvenience.');
        this.SERVER_ERROR_TEXT = gettext('Server error occurred.');
        this.destroy$ = new Subject();
        this.listenToWidgetResizeEvent(dashboardChild);
    }
    async ngOnInit() {
        this.alerts.setAlertGroupDismissStrategy('warning', DismissAlertStrategy.TEMPORARY_OR_PERMANENT);
        await this.applyDeviceTypeTarget();
        if (this.config.device) {
            this.rootNode = this.config.device;
        }
        this.mapConfig = { ...this.config.mapConfig };
        // Check for legacy widget BEFORE updateMapConfigRealtime() overwrites it
        const isLegacyWidgetWithRealtime = !this.config.hasOwnProperty('displayMode') && this.mapConfig.realtime;
        this.updateMapConfigRealtime();
        this.savedNode = this.rootNode;
        await this.updateAssets();
        const migratedConfig = this.widgetConfigMigrationService.migrateWidgetConfig(this.config);
        this.config = merge(this.config, migratedConfig);
        // For legacy widgets (with realtime), set displayMode to 'view_and_config' to maintain previous behavior to some degree
        // TODO: adjust widgetConfigMigrationService.migrateWidgetConfig or do nothing.
        // Map widget is a special case since it blocks auto-refresh in the config live mode.
        if (isLegacyWidgetWithRealtime) {
            this.config.displayMode = GLOBAL_CONTEXT_DISPLAY_MODE.VIEW_AND_CONFIG;
        }
        if (this.assets.length > 1) {
            this.mapConfig = {
                ...this.mapConfig,
                follow: false
            };
        }
        this.controls =
            (await this.dynamicComponentService.getById(defaultWidgetIds.MAP)).data?.widgetControls || {};
    }
    ngAfterViewInit() {
        this.subscribeToErrorsOccurred();
        if (this.widgetsDashboardComponent) {
            const resizeCallback = debounce(() => {
                // initial size of map is assigned when left drawer is closed, but if it is opened after initialization
                // the map size is not updated, so we need to call invalidateSize so that map use the correct size
                this.clusterMap.map?.invalidateSize(false);
            }, 100);
            this.resizeObserver = new ResizeObserver(resizeCallback);
            this.resizeObserver.observe(this.widgetsDashboardComponent.nativeElement);
        }
    }
    startFollow(context) {
        if (context.id !== this.rootNode?.id) {
            this.rootNode = context;
        }
        this.mapConfig = {
            ...this.mapConfig,
            follow: true
        };
    }
    stopFollow() {
        this.mapConfig = {
            ...this.mapConfig,
            follow: false
        };
        this.rootNode = this.savedNode;
    }
    ngOnDestroy() {
        if (this.resizeObserver) {
            this.resizeObserver.disconnect();
        }
        this.destroy$.next();
    }
    async fitToBounds() {
        await this.updateAssets();
        if (this.assets.length === 0 || !this.clusterMap?.map) {
            return;
        }
        const bounds = await this.mapService.getAssetsBounds(this.assets);
        this.clusterMap.map.fitBounds(bounds, defaultFitBoundsOptions);
    }
    async onGlobalContext(event) {
        const { context } = event;
        const isRealtime = this.mapConfig.realtime;
        if (!isRealtime && context.isAutoRefreshEnabled) {
            // If follow is enabled, fetch latest position and center map before reload
            if (this.mapConfig.follow && this.config.device?.c8y_Position) {
                await this.updateAssets();
                if (this.assets?.length === 1) {
                    // Update clusterMap's asset so marker can be rendered even if cluster query returns 0 positions
                    this.clusterMap.assets = this.assets[0];
                    this.clusterMap?.moveToPositionOfMo(this.assets[0]);
                    // Prevent cluster subscription from re-centering to same position
                    const pos = this.assets[0].c8y_Position;
                    this.clusterMap.lastFollowedPosition = { lat: pos.lat, lng: pos.lng };
                }
            }
            this.clusterMap?.reload();
        }
    }
    async updateAssets() {
        this.assets = (await this.mapService.getAllPositionMOs(this.config.device)).data;
    }
    listenToWidgetResizeEvent(dashboardChild) {
        dashboardChild.changeEnd
            .pipe(filter(child => child.lastChange === 'resize'), takeUntil(this.destroy$))
            .subscribe(() => {
            this.clusterMap.reset();
        });
    }
    subscribeToErrorsOccurred() {
        this.clusterMap.errorNotifier
            .pipe(filter(Boolean), takeUntil(this.destroy$))
            .subscribe(error => {
            this.alerts.addAlerts(new DynamicComponentAlert({
                type: 'warning',
                text: error?.name === 'TimeoutError'
                    ? this.TIMEOUT_ERROR_TEXT
                    : (error?.message ?? this.SERVER_ERROR_TEXT)
            }));
        });
    }
    updateMapConfigRealtime() {
        this.mapConfig.realtime = this.config.realtime || false;
    }
    async applyDeviceTypeTarget() {
        const hasDeviceTarget = !!this.config.device; // The map widget supports not selecting any target
        const isDeviceTypeDashboard = !!this.dashboardContextComponent?.dashboard?.deviceType;
        if (isDeviceTypeDashboard && hasDeviceTarget) {
            const context = this.dashboardContextComponent.context;
            if (context?.id) {
                const { id } = context;
                this.config.device = (await this.inventory.detail(id)).data;
            }
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapWidgetComponent, deps: [{ token: i2$1.DashboardChildComponent }, { token: i4$1.ContextDashboardComponent, optional: true }, { token: i1.InventoryService }, { token: i4.MapService }, { token: i2$1.WidgetsDashboardComponent, optional: true }, { token: i2$1.DynamicComponentService }, { token: i5.WidgetConfigMigrationService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: MapWidgetComponent, isStandalone: true, selector: "c8y-map-widget", inputs: { config: "config" }, viewQueries: [{ propertyName: "clusterMap", first: true, predicate: ClusterMapComponent, descendants: true }], ngImport: i0, template: "@if (mapConfig) {\n  <c8y-map-status\n    [clusterMap]=\"mapWidget\"\n    [(config)]=\"mapConfig\"\n    (onUnfollow)=\"stopFollow()\"\n    (fitAssetsToBounds)=\"fitToBounds()\"\n    [assets]=\"assets\"\n    [buttonsConfig]=\"{\n      autoRefresh: { show: false },\n      refresh: { show: false },\n      realtime: { show: false },\n      fitToBounds: { show: true }\n    }\"\n  >\n    @if (controls) {\n      <c8y-global-context-widget-wrapper\n        [widgetControls]=\"controls\"\n        [config]=\"config\"\n        [isLoading]=\"mapWidget.isLoading$ | async\"\n        (globalContextChange)=\"onGlobalContext($event)\"\n      ></c8y-global-context-widget-wrapper>\n    }\n  </c8y-map-status>\n\n  <c8y-cluster-map\n    #mapWidget\n    [rootNode]=\"rootNode\"\n    [config]=\"mapConfig\"\n    [asset]=\"assets\"\n  >\n    <div\n      class=\"map-marker\"\n      *c8yMapPopup=\"let context\"\n    >\n      <a\n        class=\"text-truncate deviceLink text-12\"\n        routerLink=\"{{ context | assetLink }}\"\n      >\n        @if (context.name) {\n          <strong>{{ context.name }}</strong>\n        } @else {\n          <strong\n            ngNonBindable\n            translate\n            [translateParams]=\"{ id: context.id }\"\n          >\n            Device {{ id }}\n          </strong>\n        }\n      </a>\n      <c8y-map-event-info [asset]=\"context\">\n        @if (!mapConfig.follow) {\n          <button\n            class=\"btn btn-default btn-xs btn-block m-t-8\"\n            [title]=\"'Activate realtime on this asset and follow it if it moves' | translate\"\n            type=\"button\"\n            (click)=\"startFollow(context)\"\n            translate\n          >\n            Follow\n          </button>\n        } @else {\n          <button\n            class=\"btn btn-default btn-xs btn-block m-t-8\"\n            [title]=\"'Stop following this asset.' | translate\"\n            type=\"button\"\n            (click)=\"stopFollow()\"\n            translate\n          >\n            Unfollow\n          </button>\n        }\n      </c8y-map-event-info>\n    </div>\n  </c8y-cluster-map>\n}\n", dependencies: [{ kind: "component", type: MapEventInfoComponent, selector: "c8y-map-event-info", inputs: ["asset"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CommonModule$1 }, { kind: "directive", type: i2$1.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "ngmodule", type: FormsModule$1 }, { kind: "ngmodule", type: IconSelectorModule }, { kind: "ngmodule", type: MapModule }, { kind: "component", type: i4.MapStatusComponent, selector: "c8y-map-status", inputs: ["config", "assets", "clusterMap", "buttonsConfig"], outputs: ["configChange", "onUnfollow", "fitAssetsToBounds"] }, { kind: "component", type: i4.ClusterMapComponent, selector: "c8y-cluster-map", inputs: ["config", "rootNode", "asset", "showClusterColor"], outputs: ["mapChange"] }, { kind: "directive", type: i4.MapPopupDirective, selector: "[c8yMapPopup]" }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: GlobalContextWidgetWrapperComponent, selector: "c8y-global-context-widget-wrapper", inputs: ["isLoading", "displayMode", "widgetControls", "controlLinks", "dashboardChildForLegacy", "config", "disableRefreshEmits"], outputs: ["globalContextChange"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }, { kind: "pipe", type: i2$1.C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: i2$1.AssetLinkPipe, name: "assetLink" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: MapWidgetComponent, decorators: [{
            type: Component,
            args: [{ selector: 'c8y-map-widget', standalone: true, imports: [
                        MapEventInfoComponent,
                        CommonModule,
                        FormsModule,
                        CommonModule$1,
                        FormsModule$1,
                        IconSelectorModule,
                        MapModule,
                        AssetLinkPipe,
                        TooltipModule,
                        RouterLink,
                        GlobalContextWidgetWrapperComponent
                    ], template: "@if (mapConfig) {\n  <c8y-map-status\n    [clusterMap]=\"mapWidget\"\n    [(config)]=\"mapConfig\"\n    (onUnfollow)=\"stopFollow()\"\n    (fitAssetsToBounds)=\"fitToBounds()\"\n    [assets]=\"assets\"\n    [buttonsConfig]=\"{\n      autoRefresh: { show: false },\n      refresh: { show: false },\n      realtime: { show: false },\n      fitToBounds: { show: true }\n    }\"\n  >\n    @if (controls) {\n      <c8y-global-context-widget-wrapper\n        [widgetControls]=\"controls\"\n        [config]=\"config\"\n        [isLoading]=\"mapWidget.isLoading$ | async\"\n        (globalContextChange)=\"onGlobalContext($event)\"\n      ></c8y-global-context-widget-wrapper>\n    }\n  </c8y-map-status>\n\n  <c8y-cluster-map\n    #mapWidget\n    [rootNode]=\"rootNode\"\n    [config]=\"mapConfig\"\n    [asset]=\"assets\"\n  >\n    <div\n      class=\"map-marker\"\n      *c8yMapPopup=\"let context\"\n    >\n      <a\n        class=\"text-truncate deviceLink text-12\"\n        routerLink=\"{{ context | assetLink }}\"\n      >\n        @if (context.name) {\n          <strong>{{ context.name }}</strong>\n        } @else {\n          <strong\n            ngNonBindable\n            translate\n            [translateParams]=\"{ id: context.id }\"\n          >\n            Device {{ id }}\n          </strong>\n        }\n      </a>\n      <c8y-map-event-info [asset]=\"context\">\n        @if (!mapConfig.follow) {\n          <button\n            class=\"btn btn-default btn-xs btn-block m-t-8\"\n            [title]=\"'Activate realtime on this asset and follow it if it moves' | translate\"\n            type=\"button\"\n            (click)=\"startFollow(context)\"\n            translate\n          >\n            Follow\n          </button>\n        } @else {\n          <button\n            class=\"btn btn-default btn-xs btn-block m-t-8\"\n            [title]=\"'Stop following this asset.' | translate\"\n            type=\"button\"\n            (click)=\"stopFollow()\"\n            translate\n          >\n            Unfollow\n          </button>\n        }\n      </c8y-map-event-info>\n    </div>\n  </c8y-cluster-map>\n}\n" }]
        }], ctorParameters: () => [{ type: i2$1.DashboardChildComponent }, { type: i4$1.ContextDashboardComponent, decorators: [{
                    type: Optional
                }] }, { type: i1.InventoryService }, { type: i4.MapService }, { type: i2$1.WidgetsDashboardComponent, decorators: [{
                    type: Optional
                }] }, { type: i2$1.DynamicComponentService }, { type: i5.WidgetConfigMigrationService }], propDecorators: { config: [{
                type: Input
            }], clusterMap: [{
                type: ViewChild,
                args: [ClusterMapComponent]
            }] } });

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

export { MapEventInfoComponent, MapWidgetComponent, MapWidgetConfigComponent };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-map.mjs.map
