,\n private _productSearchService: CatalogContentAndSearchResolver,\n ) {}\n ngAfterContentInit(): void {\n queueMicrotask(() => this.recalcMaxWidth())\n }\n recalcMaxWidth(): void {\n // if (window.matchMedia('(min-width: 1280px)').matches)\n // this._elm.nativeElement\n // .querySelectorAll('details')\n // .forEach(detailsElm => {\n // detailsElm.style.maxWidth = detailsElm.querySelector('summary').clientWidth + 'px';\n // })\n }\n updateUrl() {\n this._productSearchService.updateUrlByForm(new FormData(window['catalogForm']));\n }\n}\n","\n{{ result?.totalResults }} {{ 'CATALOG.FACET.TOTALRESULT' | translate }}
\n","import { CommonModule } from '@angular/common';\nimport { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { TranslateModule } from '@ngx-translate/core';\nimport { CatalogContentAndSearchResolver, SearchResult } from 'src/core-lib/navigation/services/content.resolver';\nimport { MediaModule } from 'src/layout/media/media.module';\nimport { UrlUtil } from 'src/utils/url.util';\nimport { CatalogFacetsComponent } from '../catalog-facets/catalog-facets.component';\nimport { CatalogSortComponent } from '../catalog-sort/catalog-sort.component';\n\n@Component({\n selector: 'app-catalog-form',\n standalone: true,\n imports: [CatalogFacetsComponent, CatalogSortComponent, TranslateModule, CommonModule, MediaModule],\n templateUrl: './catalog-form.component.html',\n styleUrl: './catalog-form.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class CatalogFormComponent {\n @Input() result: SearchResult;\n constructor(\n private _productSearchService: CatalogContentAndSearchResolver,\n ) {}\n\n updateUrl() {\n this._productSearchService.updateUrlByForm(new FormData(document.forms['catalogForm']));\n }\n\n resetFacetsInUrl() {\n var data = new FormData(window['catalogForm']);\n for (const [key, value] of data.entries())\n if (key.startsWith(UrlUtil.FACET_PREFIX) || value.toString().length < 1)\n data.delete(key);\n this._productSearchService.updateUrlByForm(data);\n }\n deselectFacet(id: string) {\n (document.getElementById(id) as HTMLInputElement).checked = false;\n\n this._productSearchService.updateUrlByForm(new FormData(window['catalogForm']));\n }\n}\n","\n 1\">\n \n\n\n\n\n\n\n \n\n","import { Location } from '@angular/common';\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Injector, Input, isDevMode, Output } from '@angular/core';\nimport { DefaultUrlSerializer, Router } from '@angular/router';\nimport { CatalogContentAndSearchResolver } from 'src/core-lib/navigation/services/content.resolver';\nimport { SerializerUtil } from 'src/utils/serializer.util';\nimport { UrlUtil } from 'src/utils/url.util';\n\n@Component({\n selector: 'pagination-page',\n templateUrl: './pagination-page.component.html',\n styleUrls: ['./pagination-page.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n\n})\nexport class PaginationPageComponent {\n\n private se: DefaultUrlSerializer = new DefaultUrlSerializer();\n\n navigateablePages: number[];\n prev?: number;\n next?: number;\n _max: number = -1;\n _from: number = -1;\n _size: number = -1;\n hasMoreNextPages: boolean;\n hasMorePrevPages: boolean;\n pageFromUrl: number;\n maxPage: number;\n current: any;\n\n\n @Input()\n set max(max: number) {\n this._max = max;\n this._update();\n }\n\n @Input()\n set from(from: number) {\n this._from = from;\n this._update();\n }\n\n @Input()\n set size(size: number) {\n this._size = size;\n this._update();\n }\n\n @Input()\n state: 'pending' | 'error' | 'success';\n\n @Input()\n pagesShown: number = 5;\n\n @Output()\n update: EventEmitter;\n\n constructor(\n private _detector: ChangeDetectorRef,\n private _location: Location,\n private _router: Router,\n private _productSearchService: CatalogContentAndSearchResolver,\n ) {\n this.update = new EventEmitter();\n this._size = 48;\n this._from = +(new URL(window.location.href).searchParams.get(UrlUtil.PAGE_PREFIX)) * this._size;\n }\n\n updateUrl(from: number) {\n var search = new URL(window.location.href).searchParams;\n if (from - 1 > 0)\n search.set(UrlUtil.PAGE_PREFIX, (from - 1).toString());\n else\n search.delete(UrlUtil.PAGE_PREFIX);\n var _url = UrlUtil.safeUrl(search);\n var route = SerializerUtil.serialize(_url);\n this._location.replaceState(route);\n this._router.getCurrentNavigation();\n this._productSearchService.search(this._productSearchService.urlModel(_url.href),1).then(() => window.document.querySelector('app-catalog-search-layout')?.scrollIntoView());\n\n }\n\n private _update() {\n [\n this.navigateablePages,\n this.next,\n this.prev,\n this.maxPage,\n this.current,\n ] = this._updateButtons(this._max, this._from, this._size, this.pagesShown);\n this._detector.markForCheck();\n\n }\n\n private _updateButtons(_max: number, _from: number, _size: number, pagesShown: number) {\n if (_max === -1 ||\n _from === -1 ||\n _size === -1)\n return [[], false, false, null, null];\n\n let navigateablePages = [];\n let next = null;\n let prev = null;\n\n if (_max > _from + _size) {\n next = (_from + _size * 2) / _size;\n }\n\n if (_from > 0) {\n prev = (_from) / _size;\n }\n\n var start = _from / _size - 1;\n var maxPage = Math.ceil(_max / _size);\n var current = Math.ceil(_from / _size) + 1;\n if ((_max / _size - 5) < start)\n start = maxPage - 5;\n if (start < 1 || _max / _size < 5)\n start = 1;\n for (let index = start; index <= maxPage && navigateablePages.length < pagesShown; index++)\n navigateablePages.push(index);\n\n return [navigateablePages, next, prev, maxPage, current];\n }\n\n}\n","\n\n\n\n\n @defer (when result?.results?.length > 0) {\n @for (product of result?.results; track product.id) {\n \n }\n } @placeholder {\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n }\n @for (banner of banners; track banner?.id) {\n -2 ? banner?.rowSize : 1\"\n [attr.grid-column-start]=\"banner?.column\"\n [attr.grid-column-size]=\"banner?.columnSize > -2 ? banner?.columnSize : 1\"\n [list]=\"banner?.banner?.layout\">\n }\n\n@if(result == null) {\n \n}\n\n\n","import { CommonModule } from '@angular/common';\nimport { AfterContentInit, ChangeDetectionStrategy, Component, HostBinding, Inject, Input, OnDestroy, TemplateRef, ViewChild } from '@angular/core';\nimport { TranslateModule } from '@ngx-translate/core';\nimport { ProductCardComponent } from 'src/business/product/components/product-card/product-card.component';\nimport { CatalogContentAndSearchResolver, SearchResult } from 'src/core-lib/navigation/services/content.resolver';\nimport { TagManagerModule } from 'src/externals/tag-manager/tag-manager.module';\nimport { DialogModule } from 'src/layout/dialog/dialog.module';\nimport { GridModule } from 'src/layout/grid/grid.module';\nimport { MediaModule } from 'src/layout/media/media.module';\nimport { PaginationModule } from 'src/layout/pagination/pagination.module';\nimport { CatalogBanner } from '../../models/catalog.model';\nimport { CatalogFormComponent } from '../catalog-form/catalog-form.component';\n\n\n\n\n\n@Component({\n selector: 'app-catalog-search-layout',\n templateUrl: './catalog-search-layout.component.html',\n styleUrls: ['./catalog-search-layout.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n standalone: true,\n imports: [\n DialogModule,\n TranslateModule,\n PaginationModule,\n TagManagerModule,\n CommonModule,\n MediaModule,\n CatalogFormComponent,\n GridModule,\n ProductCardComponent\n ]\n})\nexport class CatalogSearchLayoutComponent {\n\n _maxPage: number;\n size: number;\n @HostBinding('class.pointer') pointerIsSharp: boolean;\n @ViewChild(\"overlay\") overlayTemplate: TemplateRef;\n @Input() tags: string[][];\n @Input() banners: CatalogBanner[];\n @Input() listName: string;\n @Input() listId: string;\n @Input() result: SearchResult;\n\n constructor(\n private searchService: CatalogContentAndSearchResolver,\n\n ) {\n this.size = this.searchService.size;\n\n }\n\n\n requestByUrl() {\n // this.searchService.requestByUrl(); // TODO\n }\n\n idTrack(index: number, item: { id: string }) {\n return item.id;\n }\n\n openOverlay() {\n window['catalog-modal'].showModal();\n }\n\n\n}\n","import { CommonModule } from '@angular/common';\nimport { NgModule } from '@angular/core';\nimport { ReactiveFormsModule } from '@angular/forms';\nimport { TranslateModule } from '@ngx-translate/core';\nimport { IntersectionObserverModule } from 'src/core-lib/intersection-observer/intersection-observer.module';\nimport { NavigationModule } from 'src/core-lib/navigation/navigation.module';\nimport { PipeModule } from 'src/core-lib/pipes/pipe.module';\nimport { VarModule } from 'src/core-lib/var/var.module';\nimport { TagManagerModule } from 'src/externals/tag-manager/tag-manager.module';\nimport { BreadcrumbsModule } from 'src/layout/breadcrumbs/breadcrumbs.module';\nimport { GridModule } from 'src/layout/grid/grid.module';\nimport { MediaModule } from 'src/layout/media/media.module';\nimport { PaginationModule } from 'src/layout/pagination/pagination.module';\nimport { CatalogFormComponent } from './components/catalog-form/catalog-form.component';\nimport { CatalogSearchLayoutComponent } from './components/catalog-search-layout/catalog-search-layout.component';\nimport { CatalogSearchComponent } from './components/catalog-search/catalog-search.component';\nimport { ProductInspirationComponent } from './components/product-inspiration/product-inspiration.component';\nimport { FacetGroupKeyPipe } from './pipes/facet-group-key.pipe';\nimport { FacetValuesValuePipe } from './pipes/facet-values-value.pipe';\nimport { FormReadyPipe } from './pipes/form-ready.pipe';\nimport { InspirationPositionPipe } from './pipes/inspiration-position.pipe';\nimport { SkeletonPipe } from './pipes/skeleton.pipe';\nimport { SplashesPipe } from './pipes/splashes.pipe';\n\n\n@NgModule({\n declarations: [\n ProductInspirationComponent,\n InspirationPositionPipe,\n SplashesPipe,\n FacetGroupKeyPipe,\n FacetValuesValuePipe,\n FormReadyPipe,\n CatalogSearchComponent,\n SkeletonPipe\n ],\n imports: [\n VarModule,\n CommonModule,\n BreadcrumbsModule,\n GridModule,\n TranslateModule,\n PipeModule,\n MediaModule,\n NavigationModule,\n IntersectionObserverModule,\n ReactiveFormsModule,\n PaginationModule,\n TagManagerModule,\n CatalogFormComponent,\n CatalogSearchLayoutComponent,\n ],\n exports: [\n ProductInspirationComponent,\n InspirationPositionPipe,\n SplashesPipe,\n CatalogSearchLayoutComponent,\n FacetGroupKeyPipe,\n FacetValuesValuePipe,\n FormReadyPipe,\n CatalogSearchComponent,\n SkeletonPipe,\n VarModule,\n CommonModule,\n BreadcrumbsModule,\n GridModule,\n TranslateModule,\n PipeModule,\n MediaModule,\n NavigationModule,\n IntersectionObserverModule,\n ReactiveFormsModule,\n PaginationModule,\n TagManagerModule,\n CatalogFormComponent,\n ]\n})\nexport class CatalogPageModule {\n}\n","import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';\nimport { FormControl, FormGroup } from '@angular/forms';\nimport { ActivatedRoute } from '@angular/router';\nimport { Observable, Subscription } from 'rxjs';\nimport { map, startWith } from 'rxjs/operators';\nimport { CombinedResult } from 'src/business/chrome/search-bar/search-bar.component';\nimport { CatalogContentAndSearchResolver } from 'src/core-lib/navigation/services/content.resolver';\nimport { UrlUtil } from 'src/utils/url.util';\n\n\n\n@Component({\n selector: 'app-catalog-search',\n templateUrl: './catalog-search.component.html',\n styleUrls: ['./catalog-search.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class CatalogSearchComponent implements OnDestroy, OnInit {\n formGroup: FormGroup<{ search: FormControl; }>;\n results$: Observable;\n state$: Observable<'error' | 'pending' | 'success'>;\n amount$: Observable;\n\n sub: Subscription;\n\n constructor(\n private productSearchService: CatalogContentAndSearchResolver,\n private _route: ActivatedRoute,\n ) {\n this.amount$ = this.productSearchService.result$.pipe(startWith(this._route.snapshot.data.content.result), map(_ => _?.totalResults ?? 0), startWith(0));\n }\n ngOnInit(): void {\n let value = this.productSearchService.urlModel(globalThis.document.location.href).searchTerms;\n if (value) { // initial value\n this._setValue(value, 'search-catalog-input');\n this._setValue(value, 'search-catalog-input-hidden');\n\n } else\n this._setValue(window['search-catalog-input'].value, 'search-catalog-input-hidden');\n }\n ngOnDestroy(): void {\n if (this.sub != null && !this.sub.closed)\n this.sub.unsubscribe()\n }\n\n private _setValue(value: string, id: string, retries = 0) {\n if (retries > 30) // > 30 sec to set value stop trying\n return;\n let elm = window[id];\n if (!elm)\n setTimeout(() => this._setValue(value, id, retries++), 1000)\n else\n elm.value = value;\n\n }\n\n search(event?: SubmitEvent, value?: string) {\n if (value) { // initial value\n this._setValue(value, 'search-catalog-input');\n this._setValue(value, 'search-catalog-input-hidden');\n\n } else\n this._setValue(window['search-catalog-input'].value, 'search-catalog-input-hidden');\n\n if (!event)\n return false;\n if (window['search-catalog-input'].value.length === 0 || window['search-catalog-input'].value.length > 2 || value != null)\n this.productSearchService.updateUrlByForm(new FormData(window['catalogForm']));\n\n return false;\n }\n}\n","{{ 'MENU.SEARCH_HEADER' | translate }}
\n\n","\n\n\n\n\n","import { Location } from '@angular/common';\nimport { CommonModule } from '@angular/common';\nimport { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { debounceTime, Observable, startWith, Subject, Subscription, switchMap, tap } from 'rxjs';\nimport { PageBase } from 'src/core-lib/navigation/base-page';\nimport { ComponentPage } from 'src/core-lib/navigation/models/component-page.model';\nimport { CatalogContentAndSearchResolver, SearchResult } from 'src/core-lib/navigation/services/content.resolver';\nimport { BreadcrumbsComponent } from 'src/layout/breadcrumbs/breadcrumbs.component';\nimport { OverlayService } from 'src/layout/overlay/services/overlay.service';\nimport { UrlUtil } from 'src/utils/url.util';\nimport { Uuid } from 'src/utils/uuid.util';\nimport { CatalogPageModule } from './catalog-page.module';\nimport { CatalogBanner, CatalogPage } from './models/catalog.model';\nimport { URL_UPDATE } from '../tokens';\n\n\nexport namespace FormUtils {\n export function getFormData(formId: string): { [key: string]: any } {\n var _form = window[formId];\n if (_form == null)\n throw new Error(`Form not found: \"${formId}\"`);\n return Object.fromEntries(new FormData(_form).entries());\n }\n}\n\n@Component({\n selector: 'app-catalog-page',\n standalone: true,\n imports: [\n CommonModule,\n CatalogPageModule,\n ],\n templateUrl: './catalog-page.component.html',\n styleUrls: ['./catalog-page.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CatalogPageComponent extends PageBase implements ComponentPage, OnInit, OnDestroy {\n private _subscriptions: Array = [];\n\n result$: Observable\n page: CatalogPage;\n\n @ViewChild('overlay')\n template: TemplateRef;\n\n @ViewChild('form')\n form: ElementRef;\n\n @ViewChild(BreadcrumbsComponent)\n breadcrumbComp: BreadcrumbsComponent;\n skeletons: null[];\n toggleState: { [key: string]: boolean } = {};\n banners: CatalogBanner[];\n\n constructor(\n private _productSearchService: CatalogContentAndSearchResolver,\n private _overlay: OverlayService,\n private _detector: ChangeDetectorRef,\n private _elm: ElementRef,\n route: ActivatedRoute,\n ) {\n super(route);\n this._subscriptions = [];\n }\n\n ngOnInit(): void {\n this.result$ = this._productSearchService.result$.pipe(\n startWith(this._route.snapshot.data.content.result)\n );\n this.banners = (this.page?.banners??[])\n .filter(banner => banner?.banner?.layout?.length > 0)\n .map(banner => {\n var _banner = structuredClone(banner);\n _banner.id = Uuid.create();\n return _banner;\n })\n }\n\n ngOnDestroy(): void {\n super.ngOnDestroy();\n this._subscriptions.forEach(subscription => {\n if (subscription != null && !subscription.closed)\n subscription.unsubscribe();\n });\n this._elm.nativeElement.remove();\n }\n\n setToggleState($event: Event, id: string) {\n this.toggleState[id] = ($event.target as HTMLDetailsElement).open;\n }\n\n openOverlay() {\n this._overlay.open({ template: this.template });\n }\n\n closeOverlay() {\n this._overlay.close();\n }\n\n valueTrack(item: { value: string, selected: boolean }, index) {\n return item.value + (item.selected ? '_true' : '_false');;\n }\n\n idNSelectedTrack(item: { id: string, values: { value: string, selected: boolean }[] }, index) {\n return item.id + (item.values.map(v => `${v.value}_${v.selected ? '_true' : '_false'}`).join(';'));\n }\n\n idTrack(index: number, item: { id: string }) {\n return item.id;\n }\n\n\n}\n","import { CommonModule } from '@angular/common';\nimport { Component, OnDestroy, OnInit } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { skip, Subscription } from 'rxjs';\nimport { Page } from './models/page.interface';\n\n@Component({\n standalone: true,\n imports: [CommonModule],\n selector: 'page-base',\n template: ''\n})\nexport class PageBase implements OnDestroy, OnInit {\n page: Page;\n protected subscriptions:Array\n constructor(\n protected _route: ActivatedRoute,\n ) {\n this.subscriptions = [];\n this.page = this._route?.snapshot?.data?.content?.page;\n this.subscriptions.push(this._route.data.pipe(skip(1)).subscribe(data => {\n this.page = data?.content?.page;\n this.ngOnInit();\n }))\n }\n ngOnInit() {}\n ngOnDestroy(): void {\n this.subscriptions.forEach(s => {\n if(s != null && !s.closed)\n s.unsubscribe();\n })\n }\n}\n","import { Directive, Input } from '@angular/core';\nimport { ViewDirective } from './view.directive';\nimport { GA4 } from '../models/ga4.model';\n\n@Directive({\n selector: '[ga4ViewItemList]'\n})\nexport class ViewItemListDirective extends ViewDirective {\n track: GA4.ViewItemList = { __event_name: 'view_item_list' };\n @Input()\n set items(data: Array>) {\n this.track.items = data;\n }\n @Input()\n set item_list_name(data: string) {\n this.track.item_list_name = data;\n }\n @Input()\n set item_list_id(data: string) {\n this.track.item_list_id = data;\n }\n}\n","import { AfterViewInit, Directive, ElementRef } from '@angular/core';\nimport { Observable } from 'rxjs';\nimport { filter, map, mergeMap, take } from 'rxjs/operators';\nimport { GA4 } from '../models/ga4.model';\n\n\n\n\n@Directive()\nexport abstract class ViewDirective implements AfterViewInit {\n abstract track: T;\n constructor(private element: ElementRef) {}\n ngAfterViewInit(): void {\n new Observable(_observer => {\n const intersectionObserver = new IntersectionObserver(entries => _observer.next(entries));\n intersectionObserver.observe(this.element.nativeElement);\n return () => intersectionObserver.disconnect();\n }).pipe(\n mergeMap((entries: IntersectionObserverEntry[]) => entries),\n map(entry => entry.isIntersecting),\n filter(_ => _),\n take(1)\n ).toPromise()\n .then(() => new Promise((resolve) => {\n const fn = () => {\n if (this?.track?.items?.length > 0)\n resolve(true);\n else\n setTimeout(() => { fn(); }, 200);\n }\n fn();\n }))\n .then(() => queueMicrotask(() => GA4.track(this.track)));\n }\n}\n"],"x_google_ignoreList":[]}