> = forwardRef<\r\n HTMLButtonElement,\r\n VideoButton\r\n>((props, ref) => {\r\n const {\r\n videoUrl,\r\n videoTitle,\r\n videoDescription,\r\n videoImage,\r\n videoUploadDate,\r\n videoTitleYouTube,\r\n videoTitleLinkedIn,\r\n youTubeUrl,\r\n hasEmbeddedMeta,\r\n embeddedCodeWithoutIframe,\r\n buttonText,\r\n id,\r\n customClassName,\r\n useButtonWithIcon = false,\r\n } = props;\r\n\r\n const seoData = () => {\r\n const data = {\r\n '@@context': 'https://schema.org',\r\n '@@type': 'VideoObject',\r\n name: videoTitle,\r\n description: videoDescription,\r\n contentUrl: videoUrl,\r\n thumbnailUrl: [videoImage],\r\n uploadDate: videoUploadDate,\r\n };\r\n return JSON.stringify(data);\r\n };\r\n\r\n return (\r\n <>\r\n \r\n {hasEmbeddedMeta ? (\r\n \r\n ) : (\r\n videoUrl?.length > 0 && (\r\n \r\n )\r\n )}\r\n >\r\n );\r\n});\r\n\r\nVideoButton.displayName = 'VideoButton';\r\n\r\nexport { VideoButton };\r\n","import React, { useMemo } from 'react';\r\nimport { AnyFile } from 'app/types';\r\nimport { Link } from '@molecules/Link';\r\nimport { FileDownloadLink } from '@molecules/FileDownloadLink';\r\n\r\nexport type BaseCardLink = {\r\n type: 'heading' | 'body';\r\n url: string;\r\n label: string;\r\n file: AnyFile;\r\n target: string;\r\n classNames: { gated: string; nonGated: string; nonFile: string };\r\n rel?: string;\r\n};\r\n\r\nexport const BaseCardLink = ({ file, url, classNames, label, target, type = 'body' }: BaseCardLink) => {\r\n const _label = type === 'heading' ? label : '';\r\n\r\n const relValue = useMemo(() => {\r\n let value = file?.relAttribute.split('=')[1];\r\n if (value) {\r\n value = value.replaceAll('\"', '');\r\n }\r\n\r\n return value;\r\n }, [file]);\r\n\r\n // dataAttrName -> data-attr-name\r\n const dataAttributes = useMemo(() => {\r\n const source = file || {};\r\n const obj: { [key: string]: string } = {};\r\n\r\n Object.keys(source)\r\n .filter((k) => k.startsWith('data'))\r\n .forEach((k) => {\r\n const key = k\r\n .split(/(?=[A-Z])/)\r\n .map((chunk) => chunk.toLocaleLowerCase())\r\n .join('-');\r\n obj[key] = source[k];\r\n });\r\n\r\n return obj;\r\n }, [file]);\r\n\r\n if (!file) {\r\n return (\r\n \r\n );\r\n }\r\n\r\n const { gatedContent } = file;\r\n\r\n return (\r\n \r\n );\r\n};\r\n","import React from 'react';\r\n\r\nexport const BaseCardContentWrapper = ({\r\n children,\r\n wrap,\r\n className,\r\n}: {\r\n wrap: boolean;\r\n children: JSX.Element | JSX.Element[];\r\n className: string;\r\n}) => (wrap ? {children}
: children);\r\n","// extracted by mini-css-extract-plugin\nexport default {\"text-area\":\"text-area__OjZd2\",\"textArea\":\"text-area__OjZd2\",\"btn\":\"btn__I7g9x\",\"image-area\":\"image-area__Qwt2c\",\"imageArea\":\"image-area__Qwt2c\",\"image-area__image\":\"image-area__image__ZZxzZ\",\"imageAreaImage\":\"image-area__image__ZZxzZ\",\"label-list\":\"label-list__bIGVu\",\"labelList\":\"label-list__bIGVu\",\"card-heading\":\"card-heading__CTf_P\",\"cardHeading\":\"card-heading__CTf_P\",\"btn-wrap\":\"btn-wrap__EjpaK\",\"btnWrap\":\"btn-wrap__EjpaK\",\"divider\":\"divider__X6zS4\",\"label\":\"label__akTOu\",\"body-text-2\":\"body-text-2__pbV0w\",\"bodyText2\":\"body-text-2__pbV0w\",\"as-link\":\"as-link__cSeyB\",\"asLink\":\"as-link__cSeyB\",\"card-link\":\"card-link__CbhHM\",\"cardLink\":\"card-link__CbhHM\",\"card-stretched-link\":\"card-stretched-link__QvTxV\",\"cardStretchedLink\":\"card-stretched-link__QvTxV\",\"body\":\"body__kbsrK\"};","import React, { ReactElement } from 'react';\r\nimport Button from '@atoms/Button';\r\nimport { Image } from '@atoms/Image';\r\nimport { AnyFile } from 'app/types';\r\nimport { tidyStr } from '@utils/tidy-str';\r\nimport { screenSizes } from 'app/scripts/core/consts';\r\nimport { BaseCardLink } from './BaseCardLink';\r\nimport { BaseCardContentWrapper } from './BaseCardContentWrapper';\r\n\r\nimport styles from './BaseCard.module.scss';\r\n\r\nexport interface BaseCard {\r\n imageMaxWidth: string;\r\n url: string;\r\n target?: string;\r\n imageSize?: string;\r\n extensions: string;\r\n title: string;\r\n hasTitle: boolean;\r\n description: string;\r\n hasDescription: boolean;\r\n image: Image;\r\n hasImage: boolean;\r\n singleStacked: boolean;\r\n labels: string[];\r\n hasLabels: boolean;\r\n hasOverLay: boolean;\r\n overlay: 'overlay' | '';\r\n primaryBtn: Button;\r\n secondaryBtn: Button;\r\n brandLabel?: string;\r\n brandClass?: string;\r\n hasBrand: boolean;\r\n altText?: string;\r\n hideLabels: boolean;\r\n centerAligned: boolean;\r\n file?: AnyFile;\r\n isFullLink: boolean;\r\n isHiddenFromSearch: boolean;\r\n unwrap?: boolean;\r\n events?: string;\r\n labelDateString?: string;\r\n innerTextLabel?: string;\r\n rel?: string;\r\n figureChildren?: ReactElement;\r\n displayDate?: boolean;\r\n customBtnWrapChildren?: ReactElement;\r\n useBodyLink?: boolean;\r\n}\r\n\r\nexport const BaseCard = ({\r\n extensions,\r\n hasImage,\r\n overlay,\r\n imageMaxWidth,\r\n image,\r\n primaryBtn,\r\n secondaryBtn,\r\n centerAligned,\r\n singleStacked,\r\n hasLabels,\r\n hideLabels,\r\n labels,\r\n hasDescription,\r\n description,\r\n hasTitle,\r\n title,\r\n file,\r\n url,\r\n target,\r\n brandClass,\r\n hasBrand,\r\n brandLabel,\r\n unwrap,\r\n events,\r\n labelDateString,\r\n innerTextLabel,\r\n figureChildren,\r\n displayDate,\r\n customBtnWrapChildren,\r\n useBodyLink = true,\r\n}: BaseCard) => {\r\n const mobileWidth = 344; // px\r\n const splittedLabels = labels\r\n .flatMap((label) => (label ? label.split('|') : []))\r\n .map((splittedLabel) => splittedLabel.trim());\r\n\r\n const filteredSplittedLabels = splittedLabels.filter((label) => label !== '');\r\n\r\n return (\r\n \r\n {hasImage && (\r\n
\r\n \r\n \r\n {figureChildren}\r\n \r\n
\r\n )}\r\n
\r\n
\r\n \r\n {hasLabels && !hideLabels && (\r\n \r\n {filteredSplittedLabels.map((label) => (\r\n
\r\n {label}\r\n
\r\n ))}\r\n {displayDate &&
{labelDateString}
}\r\n
\r\n )}\r\n\r\n \r\n {hasTitle && !!title && (\r\n
\r\n \r\n
\r\n )}\r\n {hasDescription && !!description && (\r\n
{description}
\r\n )}\r\n {innerTextLabel &&
{innerTextLabel}
}\r\n {events && typeof events === 'string' && (\r\n
\r\n )}\r\n
\r\n \r\n \r\n\r\n {(primaryBtn?.hasContent || secondaryBtn?.hasContent || customBtnWrapChildren) && (\r\n
\r\n {primaryBtn?.hasContent &&
}\r\n {secondaryBtn?.hasContent && (\r\n <>\r\n
\r\n
\r\n >\r\n )}\r\n {customBtnWrapChildren}\r\n {hasBrand &&
{brandLabel}
}\r\n
\r\n )}\r\n
\r\n\r\n {useBodyLink && (\r\n
\r\n )}\r\n
\r\n );\r\n};\r\n","// extracted by mini-css-extract-plugin\nexport default {\"O95-product-card\":\"O95-product-card__rJ6gY\",\"o95ProductCard\":\"O95-product-card__rJ6gY\",\"card\":\"card__pQ8Jo\",\"card-with-download\":\"card-with-download__iQDL8\",\"cardWithDownload\":\"card-with-download__iQDL8\",\"text-area\":\"text-area__Z6Kdt\",\"textArea\":\"text-area__Z6Kdt\",\"inner-card-content\":\"inner-card-content__e76uZ\",\"innerCardContent\":\"inner-card-content__e76uZ\",\"is-pointer\":\"is-pointer__QBzur\",\"isPointer\":\"is-pointer__QBzur\",\"label-list\":\"label-list__Y8gYH\",\"labelList\":\"label-list__Y8gYH\",\"image-area\":\"image-area__XZBqi\",\"imageArea\":\"image-area__XZBqi\",\"label\":\"label__dZR5m\",\"card-heading\":\"card-heading__jnK5s\",\"cardHeading\":\"card-heading__jnK5s\",\"product-card-details__item\":\"product-card-details__item__oGvSy\",\"productCardDetailsItem\":\"product-card-details__item__oGvSy\",\"product-card-details__item__title\":\"product-card-details__item__title__WBGow\",\"productCardDetailsItemTitle\":\"product-card-details__item__title__WBGow\",\"product-card-details__item__value\":\"product-card-details__item__value__cm0yt\",\"productCardDetailsItemValue\":\"product-card-details__item__value__cm0yt\",\"product-card-details__description\":\"product-card-details__description__iFHCb\",\"productCardDetailsDescription\":\"product-card-details__description__iFHCb\",\"btn-wrap\":\"btn-wrap__a4CNZ\",\"btnWrap\":\"btn-wrap__a4CNZ\",\"js-card-carousel\":\"js-card-carousel__ZelgA\",\"jsCardCarousel\":\"js-card-carousel__ZelgA\",\"inner-link\":\"inner-link__PKKrW\",\"innerLink\":\"inner-link__PKKrW\",\"file-download-link\":\"file-download-link__VLpem\",\"fileDownloadLink\":\"file-download-link__VLpem\",\"divider\":\"divider__MmiFp\",\"product-card-btn-wrap\":\"product-card-btn-wrap__OtxeV\",\"productCardBtnWrap\":\"product-card-btn-wrap__OtxeV\",\"btn\":\"btn__I6MqA\",\"checkbox-wrap\":\"checkbox-wrap__go7_y\",\"checkboxWrap\":\"checkbox-wrap__go7_y\"};","import React, { ReactElement, ReactNode } from 'react';\r\nimport { MediaFile } from '@molecules/MediaFile';\r\nimport { tidyStr } from '@utils/tidy-str';\r\nimport { SecondaryButton } from '@molecules/SecondaryButton';\r\n\r\nimport styles from './ProductCard.module.scss';\r\n\r\nexport interface ProductCard {\r\n deepLinkId: string;\r\n hasAnchorId: string;\r\n image: MediaFile;\r\n headline: string;\r\n labels: string[];\r\n linkText: string;\r\n link: string;\r\n openLinkInNewTab: boolean;\r\n bundleDetails: { [key: string]: string };\r\n secondaryButton: SecondaryButton;\r\n hideImages?: boolean;\r\n orderSampleButton?: ReactNode;\r\n useMediaFileImage?: boolean;\r\n cardExtensionCss?: string;\r\n useJsOrderSampleEdgeOptions?: boolean;\r\n onClickCard?: (e) => void;\r\n labelCssExtensions?: string;\r\n productCardBtnWrapChildren?: ReactElement;\r\n excerpt?: string;\r\n}\r\n\r\nexport const ProductCard = ({\r\n deepLinkId,\r\n hasAnchorId,\r\n bundleDetails,\r\n headline,\r\n image,\r\n labels,\r\n link,\r\n linkText,\r\n openLinkInNewTab,\r\n secondaryButton,\r\n hideImages,\r\n orderSampleButton,\r\n cardExtensionCss,\r\n useMediaFileImage = true,\r\n useJsOrderSampleEdgeOptions = false,\r\n onClickCard,\r\n labelCssExtensions,\r\n productCardBtnWrapChildren,\r\n excerpt,\r\n}: ProductCard) => (\r\n \r\n {image && !hideImages && useMediaFileImage && (\r\n
\r\n \r\n \r\n \r\n
\r\n )}\r\n {!useMediaFileImage && image && !hideImages && (\r\n
\r\n
\r\n
\r\n \r\n
\r\n )}\r\n
\r\n
\r\n {labels?.length > 0 && (\r\n
\r\n {labels.map((label) => (\r\n
\r\n {label}\r\n
\r\n ))}\r\n
\r\n )}\r\n\r\n
\r\n {headline &&
{headline}
}\r\n {bundleDetails && (\r\n
\r\n {Object.entries(bundleDetails).map(([key, value]) => (\r\n
\r\n
{key}
\r\n
{value}
\r\n
\r\n ))}\r\n
\r\n )}\r\n {excerpt &&
{excerpt}
}\r\n
\r\n
\r\n
\r\n {((linkText && link) || orderSampleButton) && (\r\n <>\r\n
\r\n {orderSampleButton}\r\n
\r\n \r\n {productCardBtnWrapChildren}\r\n
\r\n >\r\n )}\r\n
\r\n
\r\n
\r\n);\r\n","var Handlebars = require(\"handlebars\"); var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};\ntemplates['compare-products-collector-item'] = template({\"1\":function(container,depth0,helpers,partials,data) {\n var helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=\"function\", alias4=container.escapeExpression, lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n };\n\n return \"
\\r\\n\";\n},\"compiler\":[8,\">= 4.3.0\"],\"main\":function(container,depth0,helpers,partials,data) {\n var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3=\"function\", lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n };\n\n return \"\\r\\n
\\r\\n \\r\\n\\r\\n \\r\\n\"\n + ((stack1 = lookupProperty(helpers,\"if\").call(alias1,(depth0 != null ? lookupProperty(depth0,\"productImage\") : depth0),{\"name\":\"if\",\"hash\":{},\"fn\":container.program(1, data, 0),\"inverse\":container.noop,\"data\":data,\"loc\":{\"start\":{\"line\":10,\"column\":6},\"end\":{\"line\":12,\"column\":13}}})) != null ? stack1 : \"\")\n + \" \\r\\n
\\r\\n
\\r\\n\"\n + ((stack1 = ((helper = (helper = lookupProperty(helpers,\"productName\") || (depth0 != null ? lookupProperty(depth0,\"productName\") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{\"name\":\"productName\",\"hash\":{},\"data\":data,\"loc\":{\"start\":{\"line\":16,\"column\":71},\"end\":{\"line\":16,\"column\":88}}}) : helper))) != null ? stack1 : \"\")\n + \"
\";\n},\"useData\":true});\n","import { PubSub, PubSubKeys } from '@utils/pubSub';\r\nimport React, { FC, useEffect, useState, useMemo, useRef, ReactElement } from 'react';\r\nimport objectFitImages from 'object-fit-images';\r\nimport { createRoot } from 'react-dom/client';\r\nimport {\r\n EventDetailRow,\r\n FilteredAdviceFeed,\r\n FilteredBlogPost,\r\n FilteredFeed,\r\n FilteredJobList,\r\n FilteredLapinusCaseStudyFeed,\r\n FilteredMediaFeed,\r\n FilteredProducts,\r\n FilteredReportFeed,\r\n ParafonSearchPageResult,\r\n ProductFilter,\r\n} from './template';\r\nimport {\r\n AjaxListModel,\r\n FetchOptionsType,\r\n HistoryStateObject,\r\n ItemContainers,\r\n ItemModel,\r\n MainCategories,\r\n RequestOptions,\r\n RequestOptionsType,\r\n ResponseFiltersType,\r\n ResponseType,\r\n URLParams,\r\n} from './models';\r\n\r\nconst classNames = {\r\n isHidden: 'is-hidden',\r\n isDisabled: 'is-disabled',\r\n isShow: 'is-shown',\r\n hasNoResults: 'has-no-results',\r\n isLoading: 'is-loading',\r\n};\r\nconst requestMethod = {\r\n post: 'POST',\r\n get: 'GET',\r\n};\r\nconst historyStateKey = 'ajaxListView';\r\nconst availableTemplates = {\r\n 'event-detail-row': EventDetailRow,\r\n 'filtered-advice-feed': FilteredAdviceFeed,\r\n 'filtered-blog-post': FilteredBlogPost,\r\n 'filtered-feed': FilteredFeed,\r\n 'filtered-job-list': FilteredJobList,\r\n 'filtered-lapinus-case-study-feed': FilteredLapinusCaseStudyFeed,\r\n 'filtered-media-feed': FilteredMediaFeed,\r\n 'filtered-products': FilteredProducts,\r\n 'filtered-report-feed': FilteredReportFeed,\r\n 'product-filter': ProductFilter,\r\n 'parafon-search-page-result': ParafonSearchPageResult,\r\n};\r\n\r\nconst AjaxList: FC = ({ el }) => {\r\n const [isLoading, setIsLoading] = useState(false);\r\n const [templatesToAppend, setTemplatesToAppend] = useState([]);\r\n const [replacementViewTemplates, setReplacementViewTemplates] = useState([]);\r\n const [isReplacingComponents, setIsReplacingComponents] = useState(false);\r\n\r\n const itemTemplate = useMemo(() => {\r\n const templateEl = el.querySelector('.js-template');\r\n return templateEl.getAttribute('id');\r\n }, [el]);\r\n const itemContainer = useRef(null);\r\n const itemContainersRef = useRef([]);\r\n const fetchOptions = useRef({ pagenumber: 0 });\r\n const fetchBody = useRef<{ [key: string]: unknown }>({});\r\n const pageType = useMemo(() => el.getAttribute('data-pagetype'), [el]);\r\n const isJobList = useMemo(() => el.classList.contains('O9-job-list'), [el]);\r\n const listId = useMemo(() => el.getAttribute('data-list-id'), [el]);\r\n const isReportList = useMemo(() => el.classList.contains('is-report-list'), [el]);\r\n const isEventDetailRow = useMemo(() => el.classList.contains('O76-event-detail-row'), [el]);\r\n const isBlogPosts = useMemo(() => el.classList.contains('O19-8-filtered-blog-posts'), [el]);\r\n const isPageType = useMemo(() => el.classList.contains('data-endpoint-pagetype'), [el]);\r\n const isAdditionalFilters = useMemo(() => el.classList.contains('is-additional-filters'), [el]);\r\n const dataSelectedType = useMemo(() => el.getAttribute('data-selected-type'), [el]);\r\n const noResults = useMemo(() => el.querySelector('.no-results-box'), [el]);\r\n const fetchMethod = useMemo(() => {\r\n if (isPageType) {\r\n return requestMethod.post;\r\n }\r\n return (isJobList || listId) && !isEventDetailRow && !isBlogPosts ? requestMethod.get : requestMethod.post;\r\n }, [isPageType, isJobList, listId, isEventDetailRow, isBlogPosts]);\r\n\r\n const moreEndpoint = useMemo(() => {\r\n if (isPageType) {\r\n return 'api/pagetype/filtered';\r\n }\r\n if (isJobList) {\r\n return 'api/vacancies/filtered';\r\n }\r\n if (isBlogPosts) {\r\n return 'api/blog/filteredposts';\r\n }\r\n if (el.classList.contains('is-article')) {\r\n return 'api/article/filtered';\r\n }\r\n if (isAdditionalFilters) {\r\n return 'api/rockwool/product/filter';\r\n }\r\n if (pageType?.toLowerCase() === 'products') {\r\n return 'api/filter';\r\n }\r\n if (listId && !isEventDetailRow) {\r\n return isReportList ? 'api/report/filtered' : 'api/media/filtered';\r\n }\r\n\r\n return 'api/pagetype/filtered';\r\n }, [isPageType, isJobList, listId, isEventDetailRow, isBlogPosts, el, isAdditionalFilters, pageType]);\r\n\r\n const apiUrl = useMemo(\r\n () => (pageType?.toLowerCase() === 'products' ? window?.searchApi?.apiUrl || '/' : '/'),\r\n [pageType]\r\n );\r\n\r\n const initializeReactRootWithTemplates = (templates: ReactElement[]) => {\r\n const wrapperSpan = document.createElement('span');\r\n const root = createRoot(wrapperSpan);\r\n root.render(templates);\r\n return wrapperSpan;\r\n };\r\n\r\n useEffect(() => {\r\n if (itemContainer?.current && isReplacingComponents) {\r\n const wrapperSpan = initializeReactRootWithTemplates(replacementViewTemplates);\r\n requestAnimationFrame(() => {\r\n itemContainer.current.replaceChildren(...wrapperSpan.children);\r\n setIsReplacingComponents(false);\r\n setReplacementViewTemplates([]);\r\n handleMasonryUpdate(itemContainer.current);\r\n });\r\n }\r\n }, [replacementViewTemplates, itemContainer, isReplacingComponents]);\r\n\r\n useEffect(() => {\r\n if (templatesToAppend.length && itemContainer) {\r\n const wrapperSpan = initializeReactRootWithTemplates(templatesToAppend);\r\n requestAnimationFrame(() => {\r\n Array.from(wrapperSpan.children).forEach((child) => {\r\n itemContainer.current.appendChild(child);\r\n });\r\n setTemplatesToAppend([]);\r\n handleMasonryUpdate(itemContainer.current);\r\n });\r\n }\r\n }, [templatesToAppend, itemContainer]);\r\n\r\n const onClickMore = (e: Event) => {\r\n e.preventDefault();\r\n\r\n if (isLoading) {\r\n return;\r\n }\r\n\r\n getItems(false);\r\n };\r\n\r\n useEffect(() => {\r\n const loadMoreBtn = el.querySelector('.js-ajax-list__more');\r\n loadMoreBtn.addEventListener('click', onClickMore);\r\n\r\n return () => {\r\n loadMoreBtn.removeEventListener('click', onClickMore);\r\n };\r\n }, [el, onClickMore]);\r\n\r\n const getListIdKey = () => {\r\n // Return the list ID as a key, if it is not null or empty.\r\n if (listId) {\r\n return listId + dataSelectedType;\r\n }\r\n // Return the index of the list element as a key, if no valid list ID was given.\r\n const listElements = [...document.getElementsByClassName('js-ajax-list')];\r\n const listIndex = listElements.indexOf(el);\r\n return `list-index-${listIndex}`;\r\n };\r\n\r\n const getHistoryState = () => {\r\n // Get the ajax list view state from history.\r\n const state = window.history.state || {};\r\n const ajaxListViewState = state[historyStateKey] || {};\r\n\r\n // Get the history for this specific list.\r\n const listIdKey = getListIdKey(); // Get a valid list ID key.\r\n const specificListState = ajaxListViewState[listIdKey] || {}; // Get state for this particular list.\r\n\r\n return {\r\n ...specificListState,\r\n };\r\n };\r\n\r\n const checkEmptyLabels = () => {\r\n const labels = itemContainer?.current?.parentNode?.querySelectorAll('.label');\r\n labels?.forEach((label) => {\r\n if (label.innerText === '') {\r\n label.style.display = 'none';\r\n }\r\n });\r\n };\r\n\r\n useEffect(() => {\r\n checkEmptyLabels();\r\n }, [itemContainer]);\r\n\r\n const onChangeSelect = (e: Event) => {\r\n const target = e.currentTarget;\r\n\r\n if (target instanceof HTMLSelectElement) {\r\n handleSelectDimming(target);\r\n getItems(true);\r\n }\r\n };\r\n\r\n const dispatchEvent = (eventName: string, element: HTMLElement, data?: object) => {\r\n let event = null;\r\n\r\n if (window.CustomEvent) {\r\n event = new CustomEvent(eventName, {\r\n detail: data || {},\r\n });\r\n } else {\r\n event = document.createEvent('CustomEvent');\r\n event.initCustomEvent(eventName, true, true, data || {});\r\n }\r\n\r\n element.dispatchEvent(event);\r\n };\r\n\r\n const checkIfMore = (response: ResponseType) => {\r\n const loadMoreBtn = el.querySelector('.js-ajax-list__more');\r\n const allItems = itemContainer.current.parentNode.querySelectorAll('.card');\r\n\r\n if (!response || response.hasMore === false || allItems.length >= response.totalCount) {\r\n (loadMoreBtn.parentNode as HTMLElement).style.display = 'none';\r\n } else {\r\n (loadMoreBtn.parentNode as HTMLElement).style.display = 'block';\r\n }\r\n };\r\n\r\n const renderEmptyResult = () => {\r\n setReplacementViewTemplates([]);\r\n setIsReplacingComponents(true);\r\n itemContainer.current.style.display = 'none';\r\n (itemContainer.current.parentNode as HTMLElement).classList.add(classNames.hasNoResults);\r\n\r\n itemContainersRef?.current.forEach((item) => {\r\n item.el.innerHTML = '';\r\n item.el.style.display = 'none';\r\n });\r\n\r\n if (noResults) {\r\n setTimeout(() => {\r\n noResults.classList.add(classNames.isShow);\r\n }, 100);\r\n }\r\n };\r\n\r\n const fillTemplateData = (model: ItemModel) => {\r\n const templateData = { ...model };\r\n\r\n if (window.templateData[itemTemplate]) {\r\n const template = window.templateData[itemTemplate];\r\n templateData.dimensionTitle = template?.dimensionTitle || '';\r\n templateData.colorsTitle = template?.colorsTitle || '';\r\n templateData.edgesTitle = template?.edgesTitle || '';\r\n templateData.reactionToFireTitle = template?.reactionToFireTitle || '';\r\n templateData.viewProductTitle = template?.viewProductTitle || '';\r\n templateData.hideCompare = template?.hideCompare || '';\r\n templateData.compareText = template?.compareText || '';\r\n templateData.viewProduct = template?.viewProduct || templateData?.viewProductTitle;\r\n templateData.filterProperties = template?.filterProperties || [];\r\n templateData.viewPostText = template?.viewPostText || '';\r\n templateData.readMoreLabel = template?.readMoreLabel || '';\r\n templateData?.filterProperties?.forEach((item) => {\r\n item.value = templateData.properties[item.name];\r\n });\r\n }\r\n\r\n return templateData;\r\n };\r\n\r\n const renderItem = (model: ItemModel, index: number) => {\r\n if (!itemTemplate) return null;\r\n const templateProps = fillTemplateData(model);\r\n const Component = availableTemplates[itemTemplate];\r\n return ;\r\n };\r\n\r\n const replaceAndRenderItems = (itemsMarkup: ReactElement[]) => {\r\n if (itemContainersRef?.current?.length === 0) {\r\n setReplacementViewTemplates(itemsMarkup);\r\n setIsReplacingComponents(true);\r\n return;\r\n }\r\n\r\n let totalIndex = 0;\r\n let allContainersFilled = true;\r\n\r\n itemContainersRef?.current?.forEach((item) => {\r\n totalIndex += item.indexes.length;\r\n\r\n item.indexes.forEach((index) => {\r\n if (!itemsMarkup[index]) {\r\n allContainersFilled = false;\r\n }\r\n });\r\n\r\n handleMasonryUpdate(item.el);\r\n });\r\n\r\n if (allContainersFilled) {\r\n const remainingItems = itemsMarkup.slice(totalIndex);\r\n setReplacementViewTemplates(remainingItems);\r\n setIsReplacingComponents(true);\r\n } else {\r\n setReplacementViewTemplates([]);\r\n setIsReplacingComponents(true);\r\n }\r\n };\r\n\r\n const renderItems = (\r\n items: ItemModel[] | { list?: ItemModel[] } | { contents?: ItemModel[] },\r\n response: ResponseType,\r\n replaceAll: boolean\r\n ) => {\r\n if (!itemContainer) {\r\n return;\r\n }\r\n\r\n let list: ItemModel[] = [];\r\n\r\n if (Array.isArray(items)) {\r\n list = items;\r\n } else if ('list' in items) {\r\n list = items.list;\r\n } else if ('contents' in items) {\r\n list = items.contents;\r\n }\r\n\r\n if (list.length === 0) {\r\n renderEmptyResult();\r\n checkIfMore(response);\r\n return;\r\n }\r\n\r\n (itemContainer.current.parentNode as HTMLElement).classList.remove(classNames.hasNoResults);\r\n itemContainer.current.style.display = '';\r\n\r\n itemContainersRef?.current.forEach((item) => {\r\n item.el.style.display = '';\r\n });\r\n\r\n if (noResults) {\r\n noResults.classList.remove(classNames.isShow);\r\n }\r\n\r\n const itemsMarkup = list.map((item, index) => renderItem(item, index));\r\n\r\n if (!fetchOptions?.current || fetchOptions?.current?.pagenumber !== 0) {\r\n setTemplatesToAppend(itemsMarkup);\r\n }\r\n\r\n if (replaceAll) {\r\n replaceAndRenderItems(itemsMarkup);\r\n }\r\n\r\n PubSub.trigger(PubSubKeys.scrollRevealNewElements);\r\n\r\n checkEmptyLabels();\r\n checkIfMore(response);\r\n handleMasonryUpdate(itemContainer.current);\r\n };\r\n\r\n const updateJobFilterCounts = (filters: ResponseFiltersType) => {\r\n filters?.forEach((filter) => {\r\n const filterEl = document.querySelector(`[data-filter-key=${filter.key}]`);\r\n\r\n if (!filterEl) {\r\n return;\r\n }\r\n\r\n filter.filterBlockItems.forEach((value) => {\r\n const valueEl = filterEl.querySelector(`option[value='${value.value}']`);\r\n\r\n if (!valueEl) {\r\n return;\r\n }\r\n\r\n const items = filterEl.querySelector(`.select__option[data-value='${value.value}'] .items-count`);\r\n\r\n if (items) {\r\n items.innerText = value.count;\r\n }\r\n });\r\n });\r\n };\r\n\r\n const createHistoryStateObj = (data: HistoryStateObject) => {\r\n // Get the ajax list view state from history.\r\n const state = window.history.state || {};\r\n const ajaxListViewState = state[historyStateKey] || {};\r\n\r\n // Update the ajax list view state with the new state data.\r\n const listIdKey = getListIdKey(); // Get a valid list ID key.\r\n const updatedAjaxListViewState = {\r\n ...ajaxListViewState,\r\n [listIdKey]: {\r\n // Add new state data for this list.\r\n ...data,\r\n listIdKey, // Also save the list ID key, so it's easier to retrieve, when fetching.\r\n },\r\n };\r\n\r\n const stateObj = {\r\n ...(window.history.state || {}), // Existing history state.\r\n [historyStateKey]: {\r\n ...updatedAjaxListViewState, // Updated ajax list view state.\r\n },\r\n };\r\n return stateObj;\r\n };\r\n\r\n const addParameters = (params: string, value: string) => {\r\n let parameters = params;\r\n\r\n if (parameters.length > 0) {\r\n parameters += ',';\r\n }\r\n\r\n parameters += value;\r\n return parameters;\r\n };\r\n\r\n const getOptionKey = (selectName: string) =>\r\n fetchMethod === requestMethod.post ? selectName : selectName.charAt(0).toLowerCase() + selectName.slice(1);\r\n\r\n const createCombinedValuesObject = (select: HTMLSelectElement) => {\r\n let combinedValue = '';\r\n let combinedSubValue = '';\r\n\r\n [...select.options].forEach((option) => {\r\n if (option.selected) {\r\n if (option.classList.contains('subitem')) {\r\n combinedSubValue = addParameters(combinedSubValue, option.value);\r\n const optionParentElement = option.parentElement;\r\n\r\n if (\r\n optionParentElement instanceof HTMLOptionElement &&\r\n !combinedValue.includes(optionParentElement.label.toLowerCase())\r\n ) {\r\n combinedValue = addParameters(combinedValue, optionParentElement.label.toLowerCase());\r\n }\r\n } else {\r\n combinedValue = addParameters(combinedValue, option.value);\r\n }\r\n }\r\n });\r\n\r\n return { combinedValue, combinedSubValue };\r\n };\r\n\r\n const collectSelectedOptionsFromMultiselectFilters = (\r\n newFetchBodyState: { [key: string]: unknown },\r\n newFetchOpptions: FetchOptionsType\r\n ) => {\r\n const multiSelects = el.querySelectorAll('.js-multi-select');\r\n\r\n multiSelects.forEach((select: HTMLSelectElement) => {\r\n const selectName = select.getAttribute('name');\r\n\r\n if (!selectName) {\r\n return;\r\n }\r\n\r\n const optionKey = getOptionKey(selectName);\r\n const selectNameSubItem = select.getAttribute('data-sub-items');\r\n const optionSubKey = getOptionKey(selectNameSubItem);\r\n const { combinedValue, combinedSubValue } = createCombinedValuesObject(select);\r\n\r\n if (fetchMethod === requestMethod.post) {\r\n newFetchBodyState[optionKey] = combinedValue;\r\n newFetchBodyState[optionSubKey] = combinedSubValue;\r\n } else {\r\n newFetchOpptions[optionKey] = combinedValue;\r\n newFetchOpptions[optionSubKey] = combinedSubValue;\r\n }\r\n });\r\n };\r\n\r\n const collectSelectedOptionsFromSelectFilters = (\r\n newFetchBodyState: { [key: string]: unknown },\r\n newFetchOpptions: FetchOptionsType\r\n ) => {\r\n const selects = el.querySelectorAll('.js-select');\r\n\r\n selects.forEach((select) => {\r\n const selectName = select.getAttribute('name');\r\n\r\n if (selectName === '' || selectName === undefined) {\r\n return;\r\n }\r\n\r\n const optionKey = getOptionKey(selectName);\r\n const value = select.value && select.value !== 'null' && select.value !== '' ? select.value : 'all';\r\n\r\n if (fetchMethod === requestMethod.post) {\r\n newFetchBodyState[optionKey] = value;\r\n } else {\r\n newFetchOpptions[optionKey] = value;\r\n }\r\n });\r\n };\r\n\r\n const prepareFetchOptions = (allNew: boolean) => {\r\n const newFetchOpptions: FetchOptionsType = fetchOptions.current;\r\n const newFetchBodyState = {};\r\n\r\n if (allNew && fetchOptions?.current) {\r\n newFetchOpptions.pagenumber = 0;\r\n } else if (fetchOptions?.current) {\r\n newFetchOpptions.pagenumber += 1;\r\n }\r\n\r\n collectSelectedOptionsFromSelectFilters(newFetchBodyState, newFetchOpptions);\r\n collectSelectedOptionsFromMultiselectFilters(newFetchBodyState, newFetchOpptions);\r\n fetchOptions.current = newFetchOpptions;\r\n fetchBody.current = { ...fetchBody.current, ...newFetchBodyState };\r\n };\r\n\r\n const prepareRequestData = (options: RequestOptions, urlParams: URLParams) => {\r\n let data = options;\r\n urlParams.listIdSourcesContent = el?.dataset?.listIdSourcesContent?.toLowerCase() === 'true';\r\n let params = Object.keys(urlParams)\r\n .map((key) => `${key}=${urlParams[key]}`)\r\n .join('&');\r\n\r\n if (pageType?.toLowerCase() === 'products') {\r\n const categories = el.getAttribute('data-filter-category');\r\n\r\n const filterData = [];\r\n const additionalFiltersData = [];\r\n let mainCategories: MainCategories = [];\r\n\r\n if (categories) {\r\n filterData.push({\r\n field: 'productCategories',\r\n values: categories.split(','),\r\n logicOperator: 'OR',\r\n });\r\n mainCategories = {\r\n field: 'productCategories',\r\n values: categories.split(','),\r\n logicOperator: 'OR',\r\n };\r\n }\r\n\r\n Object.keys(data).forEach((key) => {\r\n if (data[key] === 'all') {\r\n return;\r\n }\r\n\r\n if (key === 'thickness' || key === 'rValue') {\r\n filterData.push({\r\n field: key,\r\n from: parseFloat(data[key] as string),\r\n to: parseFloat(data[key] as string),\r\n });\r\n } else if (isAdditionalFilters && key.startsWith('additional-filter')) {\r\n additionalFiltersData.push({\r\n field: key.replace('additional-filter-', ''),\r\n values: [data[key]],\r\n });\r\n } else {\r\n filterData.push({\r\n field: key,\r\n values: [data[key]],\r\n });\r\n }\r\n });\r\n\r\n const requireFacetFields = el.getAttribute('data-requireFacetFieldsOnProducts');\r\n let returnFields = el.getAttribute('data-returnFields');\r\n let facetFields = el.getAttribute('data-facetFields');\r\n let filterCategory = el.getAttribute('data-filterCategory');\r\n\r\n returnFields = returnFields == null ? window.returnFields : JSON.parse(returnFields);\r\n facetFields = facetFields == null ? window.facetFields : JSON.parse(facetFields);\r\n filterCategory = filterCategory || window.filterCategory;\r\n\r\n const showOnlyDeepestLevelSubcategories =\r\n el.dataset.showOnlyDeepestLevelSubcategories &&\r\n el.dataset.showOnlyDeepestLevelSubcategories.toLowerCase() === 'true';\r\n const showOnlyFirstLevelSubcategories =\r\n el.dataset.showOnlyFirstLevelSubcategories &&\r\n el.dataset.showOnlyFirstLevelSubcategories.toLowerCase() === 'true';\r\n\r\n data = {\r\n filters: filterData,\r\n additionalFilters: additionalFiltersData,\r\n count: urlParams.resultsPerPage,\r\n page: urlParams.pagenumber,\r\n languageCode: window.searchApi.languageCode || 'en',\r\n culture: window.searchApi.culture || 'en',\r\n site: window.searchApi.site,\r\n facetFields: facetFields || [],\r\n returnFields: returnFields || [],\r\n filterCategory: filterCategory || '',\r\n requireFacetFieldsOnDocument: requireFacetFields === 'True',\r\n mainCategories,\r\n showOnlyDeepestLevelSubcategories,\r\n showOnlyFirstLevelSubcategories,\r\n };\r\n\r\n params = '';\r\n }\r\n\r\n return { data, params };\r\n };\r\n\r\n const _fetch = (urlParams: URLParams, options: RequestOptions) => {\r\n const requestOptions: RequestOptionsType = {\r\n method: fetchMethod,\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n },\r\n };\r\n let url = apiUrl + moreEndpoint;\r\n const { data, params } = prepareRequestData(options, urlParams);\r\n\r\n if (fetchMethod === requestMethod.post) {\r\n requestOptions.body = JSON.stringify(data);\r\n }\r\n\r\n if (params) {\r\n url += `?${params}`;\r\n }\r\n\r\n return fetch(url, requestOptions)\r\n .then((response) => {\r\n if (!response.ok) {\r\n throw new Error(`${response.status} - ${response.statusText} (url: ${response.url})`);\r\n }\r\n\r\n return response.json();\r\n })\r\n .catch((ex) => {\r\n console.warn('somethings wrong', ex);\r\n });\r\n };\r\n\r\n const setStyleForAllNew = (loaderOverlay: HTMLElement, loader: HTMLElement) => {\r\n if (loaderOverlay) {\r\n loaderOverlay.classList.remove(classNames.isHidden);\r\n } else {\r\n loader.classList.remove(classNames.isHidden);\r\n }\r\n\r\n (itemContainer?.current?.parentNode as HTMLElement)?.classList?.add('is-loader-list-loading');\r\n };\r\n\r\n const updateStylesBeforeFetch = (\r\n loader: HTMLElement,\r\n loadMoreBtn: HTMLElement,\r\n loaderOverlay: HTMLElement,\r\n allNew?: boolean\r\n ) => {\r\n if (loader && loadMoreBtn) {\r\n loadMoreBtn.classList.add(classNames.isLoading);\r\n\r\n if (allNew) {\r\n setStyleForAllNew(loaderOverlay, loader);\r\n } else {\r\n loader.classList.remove(classNames.isHidden);\r\n }\r\n } else if (loadMoreBtn) {\r\n loadMoreBtn.classList.add(classNames.isDisabled);\r\n }\r\n };\r\n\r\n // Replace data in history state.\r\n const createItemList = (isAllNewItems: boolean, fetchResponse: ResponseType) => {\r\n const newItems = fetchResponse ? fetchResponse.list || fetchResponse.contents || [] : [];\r\n\r\n // If all new, replace old list with new one.\r\n if (isAllNewItems) {\r\n return newItems;\r\n }\r\n // Else merge with previous list.\r\n const { itemListFull } = getHistoryState();\r\n\r\n if (itemListFull) {\r\n return [...itemListFull, ...newItems];\r\n }\r\n // If no prior list found, return null.\r\n return [...newItems];\r\n };\r\n\r\n const getItems = (allNew: boolean) => {\r\n if (isLoading) {\r\n return;\r\n }\r\n\r\n setIsLoading(true);\r\n\r\n const loader: HTMLElement = el.querySelector('.loader:not(.is-absolute-centered)');\r\n const loaderOverlay: HTMLElement = el.querySelector('.loader.is-absolute-centered');\r\n const loadMoreBtn: HTMLElement = el.querySelector('.js-ajax-list__more');\r\n\r\n updateStylesBeforeFetch(loader, loadMoreBtn, loaderOverlay, allNew);\r\n prepareFetchOptions(allNew);\r\n\r\n const itemFilters = fetchBody?.current;\r\n const itemFiltersMultiple = fetchOptions?.current;\r\n\r\n const promise = _fetch(fetchOptions?.current, fetchBody?.current);\r\n\r\n promise.then((response) => {\r\n setTimeout(() => {\r\n if (loader && loadMoreBtn) {\r\n loader.classList.add(classNames.isHidden);\r\n loadMoreBtn.classList.remove(classNames.isLoading);\r\n (itemContainer?.current?.parentNode as HTMLElement)?.classList?.remove('is-loader-list-loading');\r\n\r\n if (loaderOverlay) {\r\n loaderOverlay.classList.add(classNames.isHidden);\r\n }\r\n } else if (loadMoreBtn) {\r\n loadMoreBtn.classList.remove(classNames.isDisabled);\r\n }\r\n\r\n setIsLoading(false);\r\n\r\n // Create the item list to save.\r\n const itemList = createItemList(allNew, response);\r\n\r\n // Should replace all (when going back), if at any time allNew was true.\r\n // ReplaceAll from history stores if allNew has been true previously.\r\n const { replaceAll } = getHistoryState();\r\n const shouldReplaceAllOnHistory = !!replaceAll || !!allNew;\r\n // Create the history object to save.\r\n const stateData = createHistoryStateObj({\r\n itemListFull: itemList,\r\n itemFetchResponse: response,\r\n itemFilters: { ...itemFilters },\r\n itemFiltersMultiple: { ...itemFiltersMultiple },\r\n pagenumber: fetchOptions?.current?.pagenumber,\r\n replaceAll: shouldReplaceAllOnHistory,\r\n });\r\n window.history.replaceState(stateData, 'ItemFetchResponse');\r\n\r\n // Render items.\r\n renderItems(response ? response?.list || response?.contents || [] : [], response, allNew);\r\n\r\n if (isJobList) {\r\n updateJobFilterCounts(response.filters);\r\n }\r\n\r\n // Run IE / Edge objectFitImage to fix polyfills on newly loaded images\r\n objectFitImages('img', {\r\n watchMQ: true,\r\n });\r\n }, 500);\r\n });\r\n };\r\n\r\n const handleSelectDimming = (select: HTMLSelectElement) => {\r\n const selectEl = select.closest('.select');\r\n const { value } = select;\r\n const dimClass = 'is-dimmed';\r\n\r\n if (selectEl?.classList?.contains('js-dim-on-all-option')) {\r\n const container = selectEl.querySelector('.select__container');\r\n if (container) {\r\n if (value === 'all') {\r\n container.classList.add(dimClass);\r\n } else {\r\n container.classList.remove(dimClass);\r\n }\r\n }\r\n }\r\n };\r\n\r\n const generateUpdatedView = (historyState: HistoryStateObject) => {\r\n // Setting change event listeners on filter selects.\r\n // This has to be setup after the new Select(), as that framework script\r\n // changes the markup and thereby the event listeners.\r\n const selects = el.querySelectorAll('.js-select, .js-multi-select');\r\n // Reading history state and reinstating last loaded items, if any.\r\n const { itemListFull, itemFetchResponse, itemFilters, itemFiltersMultiple, replaceAll, listIdKey } = historyState;\r\n if ((itemListFull && itemFetchResponse) || itemFilters || itemFiltersMultiple) {\r\n // Update selected filter(s).\r\n const filterElems = el.querySelectorAll('.filter');\r\n setTimeout(() => {\r\n // Dispatch event to button filters.\r\n filterElems.forEach((filterElem) => {\r\n dispatchEvent('update:selected-filters', filterElem, {\r\n itemFilters,\r\n itemFiltersMultiple,\r\n listIdKey,\r\n });\r\n });\r\n // Dispatch event to select filters.\r\n selects.forEach((select) => {\r\n dispatchEvent('update:selected-filters', select, { itemFilters, itemFiltersMultiple, listIdKey });\r\n });\r\n }, 0);\r\n\r\n // Render items.\r\n renderItems(itemListFull || [], itemFetchResponse, replaceAll);\r\n\r\n if (isJobList) {\r\n const responseFilters = itemFetchResponse ? itemFetchResponse.filters : undefined;\r\n updateJobFilterCounts(responseFilters);\r\n }\r\n\r\n // Run IE / Edge objectFitImage to fix polyfills on newly loaded images\r\n objectFitImages('img', {\r\n watchMQ: true,\r\n });\r\n } else if (dataSelectedType) {\r\n getItems(true);\r\n }\r\n\r\n setTimeout(() => {\r\n selects.forEach((select) => {\r\n handleSelectDimming(select);\r\n });\r\n }, 0);\r\n };\r\n\r\n const handleMasonryUpdate = (element: HTMLElement) => {\r\n if (element?.classList?.contains('js-card-masonry')) {\r\n dispatchEvent('update:masonry', element);\r\n }\r\n };\r\n\r\n const templateLocationAssignment = () => {\r\n // Locating item template\r\n const itemContainersElements = el.querySelectorAll('.js-ajax-list__container');\r\n itemContainer.current = itemContainersElements?.[0];\r\n\r\n if (itemContainersElements.length > 1) {\r\n itemContainersElements.forEach((container) => {\r\n const containerType = container.getAttribute('data-feed-container');\r\n\r\n if (containerType === 'default') {\r\n itemContainer.current = container;\r\n } else {\r\n const indexes = containerType.split(',').map((type) => parseInt(type, 10));\r\n itemContainersRef.current.push({\r\n el: container,\r\n indexes,\r\n });\r\n }\r\n });\r\n }\r\n };\r\n\r\n useEffect(() => {\r\n const historyState = getHistoryState();\r\n\r\n if (\r\n window?.performance?.getEntriesByType('navigation')?.[0]?.type === 'reload' &&\r\n historyState.itemFilters !== undefined\r\n ) {\r\n Object.entries(historyState.itemFilters).forEach(([key]) => {\r\n historyState.itemFilters[key] = 'all';\r\n });\r\n historyState.itemListFull = historyState.itemFetchResponse;\r\n }\r\n\r\n const fetchOptionsPagenumber = historyState.pagenumber || 0;\r\n\r\n const fetchOptionsObj: FetchOptionsType = {\r\n pagenumber: fetchOptionsPagenumber,\r\n resultsPerPage: isJobList ? 10 : 6,\r\n languageCode: window.languageCode || 'en',\r\n };\r\n\r\n if (pageType?.toLowerCase() === 'products') {\r\n fetchOptionsObj.resultsPerPage = 8;\r\n }\r\n\r\n if (pageType) {\r\n fetchOptionsObj.type = pageType;\r\n }\r\n\r\n if (listId) {\r\n fetchOptionsObj.listid = listId;\r\n }\r\n\r\n if (dataSelectedType) {\r\n fetchOptionsObj.type = dataSelectedType;\r\n }\r\n\r\n fetchOptions.current = fetchOptionsObj;\r\n\r\n templateLocationAssignment();\r\n generateUpdatedView(historyState);\r\n }, []);\r\n\r\n useEffect(() => {\r\n // Setting change event listeners on filter selects.\r\n // This has to be setup after the new Select(), as that framework script\r\n // changes the markup and thereby the event listeners.\r\n const selects = el.querySelectorAll('.js-select, .js-multi-select');\r\n selects.forEach((select) => {\r\n select.addEventListener('change', onChangeSelect);\r\n });\r\n\r\n return () => {\r\n selects.forEach((select) => {\r\n select.removeEventListener('change', onChangeSelect);\r\n });\r\n };\r\n }, [onChangeSelect]);\r\n\r\n return null;\r\n};\r\n\r\nexport const renderAjaxListComponent = (el: HTMLElement) => {\r\n const ajaxList = document.createElement('span');\r\n ajaxList.id = 'ajaxList';\r\n el.appendChild(ajaxList);\r\n const root = createRoot(ajaxList);\r\n root.render();\r\n};\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { Brand } from '@utils/brand';\r\nimport { htmlAttributes } from 'app/consts';\r\nimport React, { FC } from 'react';\r\n\r\ninterface EventDetailRow {\r\n internalUrl: string;\r\n inboundLinkTarget: string;\r\n imageUrl: string;\r\n altText: string;\r\n brand: Brand;\r\n metaLabel: string;\r\n metaTitle: string;\r\n metaDescription: string;\r\n dateString: string;\r\n metaCTA: string;\r\n imageClass: string;\r\n}\r\n\r\nconst EventDetailRow: FC = ({\r\n internalUrl,\r\n inboundLinkTarget,\r\n imageUrl,\r\n altText,\r\n brand,\r\n metaLabel,\r\n metaTitle,\r\n metaDescription,\r\n dateString,\r\n metaCTA,\r\n imageClass,\r\n}) => (\r\n ,\r\n iconName: 'arrow-right',\r\n extensions: 'btn btn--secondary is-small',\r\n relationship:\r\n inboundLinkTarget === htmlAttributes.targetValueBlank\r\n ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule\r\n : '',\r\n link: internalUrl,\r\n }}\r\n hasBrand={false}\r\n url={internalUrl}\r\n imageMaxWidth=\"\"\r\n target={inboundLinkTarget}\r\n hasImage={Boolean(imageUrl)}\r\n singleStacked={false}\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n innerTextLabel={dateString}\r\n displayDate\r\n />\r\n);\r\n\r\nexport { EventDetailRow };\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport DownloadIcon from '@atoms/icons/DownloadIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { htmlAttributes } from 'app/consts';\r\nimport React, { FC, useMemo } from 'react';\r\nimport { FigureChildren } from '../FigureChildren';\r\nimport { WrappedVideoButton } from '../WrappedVideoButton';\r\n\r\ninterface FilteredAdviceFeed {\r\n hasImage: boolean;\r\n hasVideo: boolean;\r\n thumbnailUrl: string;\r\n altText: string;\r\n isArticle: boolean;\r\n metaLabel: string;\r\n fullscreenUrl: string;\r\n videoUrl: string;\r\n mediaSize: string;\r\n mediaType: string;\r\n headline: string;\r\n bodyText: string;\r\n hasHeadline: boolean;\r\n hasText: boolean;\r\n videoTitle: string;\r\n mediaUrl: string;\r\n target: string;\r\n fileName: string;\r\n linkTarget: string;\r\n trackingLabel: string;\r\n linkText: string;\r\n linkUrl: string;\r\n extensions: string;\r\n videoTitleLinkedIn: string;\r\n videoTitleYouTube: string;\r\n youTubeUrl: string;\r\n}\r\n\r\nconst FilteredAdviceFeed: FC = ({\r\n hasImage,\r\n hasVideo,\r\n thumbnailUrl,\r\n altText,\r\n isArticle,\r\n metaLabel,\r\n fullscreenUrl,\r\n videoUrl,\r\n mediaSize,\r\n mediaType,\r\n headline,\r\n bodyText,\r\n hasHeadline,\r\n hasText,\r\n videoTitle,\r\n mediaUrl,\r\n target,\r\n fileName,\r\n linkTarget,\r\n trackingLabel,\r\n linkText,\r\n linkUrl,\r\n extensions,\r\n videoTitleLinkedIn,\r\n videoTitleYouTube,\r\n youTubeUrl,\r\n}) => {\r\n const secondaryBtnObj = useMemo(() => {\r\n if (hasVideo && !isArticle) {\r\n return null;\r\n }\r\n\r\n if (!hasVideo && !isArticle) {\r\n return {\r\n hasContent: true,\r\n title: linkText,\r\n svg: ,\r\n iconName: 'icon--download',\r\n extensions: 'download-link',\r\n fileTitle: fileName,\r\n trackingLabel,\r\n fileType: mediaType,\r\n fileCategory: 'download',\r\n file: true,\r\n isWhiteIcon: false,\r\n target,\r\n link: mediaUrl,\r\n relationship:\r\n target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : '',\r\n };\r\n }\r\n\r\n return {\r\n hasContent: true,\r\n title: linkText,\r\n svg: ,\r\n iconName: 'icon--arrow-right',\r\n extensions: 'download-link',\r\n isWhiteIcon: false,\r\n target: linkTarget,\r\n link: linkUrl,\r\n relationship:\r\n target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : '',\r\n };\r\n }, [hasVideo, isArticle]);\r\n\r\n return (\r\n \r\n )\r\n }\r\n useBodyLink={!hasVideo}\r\n brandClass=\"\"\r\n hasBrand={false}\r\n url={!isArticle && !hasVideo ? mediaUrl : linkUrl}\r\n imageMaxWidth=\"\"\r\n target={!isArticle && !hasVideo ? target : linkTarget}\r\n hasImage={hasImage || hasVideo}\r\n singleStacked={false}\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n rel={linkTarget === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : ''}\r\n figureChildren={\r\n !isArticle && (\r\n \r\n )\r\n }\r\n file={\r\n !isArticle && !hasVideo\r\n ? {\r\n name: fileName,\r\n dataTrackingAction: 'click',\r\n trackingLabel,\r\n hrefAttribute: mediaUrl,\r\n gatedContent: false,\r\n fileType: mediaType,\r\n dataTrackingCategory: 'download',\r\n targetAttribute: target,\r\n dataTrackingLabel: trackingLabel,\r\n dataTrackingFileType: mediaType,\r\n relAttribute: `rel=${\r\n target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : ''\r\n }`,\r\n }\r\n : null\r\n }\r\n secondaryBtn={secondaryBtnObj}\r\n />\r\n );\r\n};\r\n\r\nexport { FilteredAdviceFeed };\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { Brand } from '@utils/brand';\r\nimport React, { FC } from 'react';\r\n\r\ninterface FilteredBlogPost {\r\n internalUrl: string;\r\n imageUrl: string;\r\n brandClass: string;\r\n brand: Brand;\r\n readMoreLabel: string;\r\n metaTitle: string;\r\n metaDescription: string;\r\n dateString: string;\r\n topicLabels: string[];\r\n imageClass: string;\r\n}\r\n\r\nconst FilteredBlogPost: FC = ({\r\n internalUrl,\r\n imageUrl,\r\n brandClass,\r\n brand,\r\n readMoreLabel,\r\n metaTitle,\r\n metaDescription,\r\n dateString,\r\n topicLabels,\r\n imageClass,\r\n}) => (\r\n ,\r\n iconName: 'arrow-right',\r\n extensions: 'btn btn--secondary is-small',\r\n link: internalUrl,\r\n }}\r\n hasBrand\r\n brandClass={brandClass}\r\n brandLabel={brand}\r\n url={internalUrl}\r\n imageMaxWidth=\"\"\r\n hasImage={Boolean(imageUrl)}\r\n singleStacked={false}\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n displayDate\r\n />\r\n);\r\n\r\nexport { FilteredBlogPost };\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { Brand } from '@utils/brand';\r\nimport { htmlAttributes } from 'app/consts';\r\nimport React, { FC } from 'react';\r\n\r\ninterface FilteredFeed {\r\n internalUrl: string;\r\n inboundLinkTarget: string;\r\n imageUrl: string;\r\n metaAltTextWithFallback: string;\r\n brand: Brand;\r\n metaLabel: string;\r\n metaTitle: string;\r\n metaDescription: string;\r\n dateString: string;\r\n metaCTA: string;\r\n displayDate: boolean;\r\n linkTarget: string;\r\n events: string;\r\n brandClass: string;\r\n imageClass: string;\r\n}\r\n\r\nconst FilteredFeed: FC = ({\r\n internalUrl,\r\n inboundLinkTarget,\r\n imageUrl,\r\n metaAltTextWithFallback,\r\n brand,\r\n metaLabel,\r\n metaTitle,\r\n metaDescription,\r\n dateString,\r\n metaCTA,\r\n displayDate,\r\n linkTarget,\r\n events,\r\n brandClass,\r\n imageClass,\r\n}) => (\r\n ,\r\n iconName: 'arrow-right',\r\n extensions: 'btn btn--secondary is-small',\r\n relationship:\r\n inboundLinkTarget === htmlAttributes.targetValueBlank\r\n ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule\r\n : '',\r\n link: internalUrl,\r\n target: linkTarget,\r\n }}\r\n hasBrand\r\n brandClass={brandClass}\r\n brandLabel={brand}\r\n url={internalUrl}\r\n imageMaxWidth=\"\"\r\n target={linkTarget}\r\n hasImage\r\n singleStacked={false}\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n events={events}\r\n displayDate={displayDate}\r\n />\r\n);\r\n\r\nexport { FilteredFeed };\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { Brand } from '@utils/brand';\r\nimport { htmlAttributes } from 'app/consts';\r\nimport React, { FC } from 'react';\r\n\r\ninterface FilteredJobList {\r\n link: string;\r\n areaOfInterest: string;\r\n country: string;\r\n brand: Brand;\r\n title: string;\r\n viewPostText: string;\r\n linkTarget: string;\r\n}\r\n\r\nconst FilteredJobList: FC = ({\r\n link,\r\n areaOfInterest,\r\n country,\r\n brand,\r\n title,\r\n viewPostText,\r\n linkTarget,\r\n}) => (\r\n <>\r\n ,\r\n iconName: 'arrow-right',\r\n extensions: 'btn btn--secondary is-small',\r\n relationship:\r\n linkTarget === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : '',\r\n link,\r\n target: linkTarget,\r\n }}\r\n hasBrand={false}\r\n brandClass=\"\"\r\n brandLabel={null}\r\n url={link}\r\n imageMaxWidth=\"\"\r\n target={linkTarget}\r\n hasImage={false}\r\n singleStacked\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned={false}\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n />\r\n \r\n >\r\n);\r\n\r\nexport { FilteredJobList };\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { Brand } from '@utils/brand';\r\nimport { htmlAttributes } from 'app/consts';\r\nimport React, { FC } from 'react';\r\n\r\ninterface FilteredLapinusCaseStudyFeed {\r\n internalUrl: string;\r\n inboundLinkTarget: string;\r\n imageUrl: string;\r\n brand: Brand;\r\n metaLabel: string;\r\n metaTitle: string;\r\n metaDescription: string;\r\n dateString: string;\r\n metaCTA: string;\r\n linkTarget: string;\r\n metaAltTextWithFallback: string;\r\n imageClass: string;\r\n displayDate: boolean;\r\n brandClass: string;\r\n}\r\n\r\nconst FilteredLapinusCaseStudyFeed: FC = ({\r\n internalUrl,\r\n inboundLinkTarget,\r\n imageUrl,\r\n linkTarget,\r\n brand,\r\n metaLabel,\r\n metaTitle,\r\n metaDescription,\r\n dateString,\r\n metaCTA,\r\n metaAltTextWithFallback,\r\n imageClass,\r\n displayDate,\r\n brandClass,\r\n}) => (\r\n ,\r\n iconName: 'arrow-right',\r\n extensions: 'btn btn--secondary is-small',\r\n link: internalUrl,\r\n target: linkTarget,\r\n relationship:\r\n inboundLinkTarget === htmlAttributes.targetValueBlank\r\n ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule\r\n : '',\r\n }}\r\n hasBrand\r\n brandClass={brandClass}\r\n brandLabel={brand}\r\n url={internalUrl}\r\n imageMaxWidth=\"\"\r\n target={linkTarget}\r\n hasImage\r\n singleStacked={false}\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned={false}\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n displayDate={displayDate}\r\n />\r\n);\r\n\r\nexport { FilteredLapinusCaseStudyFeed };\r\n","import DownloadIcon from '@atoms/icons/DownloadIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { htmlAttributes } from 'app/consts';\r\nimport React, { FC, HTMLAttributeAnchorTarget } from 'react';\r\nimport { FigureChildren } from '../FigureChildren';\r\nimport { WrappedVideoButton } from '../WrappedVideoButton';\r\n\r\ninterface FilteredMediaFeed {\r\n extensions: string;\r\n thumbnailUrl: string;\r\n hasMedia: boolean;\r\n hasVideo: boolean;\r\n fullscreenUrl: string;\r\n videoUrl: string;\r\n mediaType: string;\r\n mediaSize: string;\r\n altText: string;\r\n headline: string;\r\n bodyText: string;\r\n hasHeadline: boolean;\r\n hasText: boolean;\r\n videoTitle: string;\r\n videoText: string;\r\n mediaUrl: string;\r\n linkText: string;\r\n fileName: string;\r\n target: HTMLAttributeAnchorTarget;\r\n trackingLabel: string;\r\n videoTitleLinkedIn: string;\r\n videoTitleYouTube: string;\r\n youTubeUrl: string;\r\n}\r\n\r\nconst FilteredMediaFeed: FC = ({\r\n extensions,\r\n thumbnailUrl,\r\n altText,\r\n hasMedia,\r\n hasVideo,\r\n fullscreenUrl,\r\n videoUrl,\r\n mediaType,\r\n mediaSize,\r\n headline,\r\n bodyText,\r\n hasHeadline,\r\n hasText,\r\n videoTitle,\r\n videoText,\r\n mediaUrl,\r\n linkText,\r\n fileName,\r\n target,\r\n trackingLabel,\r\n videoTitleLinkedIn,\r\n videoTitleYouTube,\r\n youTubeUrl,\r\n}) => (\r\n ,\r\n iconName: 'icon--download',\r\n extensions: 'download-link',\r\n fileTitle: fileName,\r\n trackingLabel,\r\n fileType: mediaType,\r\n fileCategory: 'download',\r\n file: true,\r\n isWhiteIcon: false,\r\n target,\r\n link: mediaUrl,\r\n relationship:\r\n target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : '',\r\n }\r\n }\r\n useBodyLink={!hasVideo}\r\n customBtnWrapChildren={\r\n hasVideo && (\r\n \r\n )\r\n }\r\n hasBrand={false}\r\n url={hasVideo ? null : mediaUrl}\r\n imageMaxWidth=\"\"\r\n target={target}\r\n hasImage={Boolean(thumbnailUrl)}\r\n singleStacked={false}\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n rel={target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : ''}\r\n figureChildren={\r\n \r\n }\r\n file={\r\n hasVideo\r\n ? null\r\n : {\r\n name: fileName,\r\n dataTrackingAction: 'click',\r\n trackingLabel,\r\n hrefAttribute: mediaUrl,\r\n gatedContent: false,\r\n fileType: mediaType,\r\n dataTrackingCategory: 'download',\r\n targetAttribute: target,\r\n dataTrackingLabel: trackingLabel,\r\n dataTrackingFileType: mediaType,\r\n relAttribute: `rel=${\r\n target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : ''\r\n }`,\r\n }\r\n }\r\n />\r\n);\r\n\r\nexport { FilteredMediaFeed };\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport { ProductCard } from '@organisms/O95-ProductCard';\r\nimport React, { FC, useMemo } from 'react';\r\nimport { CompareProductButton } from '../CompareProductButton';\r\n\r\ninterface FilteredProducts {\r\n image: string;\r\n name: string;\r\n productCategories: string;\r\n url: string;\r\n properties: { dimensions?: string; colors?: string; edges?: string; reactionToFire?: string };\r\n dimensionTitle: string;\r\n colorsTitle: string;\r\n edgesTitle: string;\r\n reactionToFireTitle: string;\r\n excerpt: string;\r\n viewProductTitle: string;\r\n pageType: 'RockfonProductSystem' | 'RockfonProductGrid';\r\n hideCompare: boolean;\r\n compareText: string;\r\n id: string;\r\n}\r\n\r\nconst FilteredProducts: FC = ({\r\n image,\r\n name,\r\n productCategories,\r\n url,\r\n properties,\r\n dimensionTitle,\r\n colorsTitle,\r\n edgesTitle,\r\n reactionToFireTitle,\r\n excerpt,\r\n viewProductTitle,\r\n pageType,\r\n hideCompare,\r\n compareText,\r\n id,\r\n}) => {\r\n const bundleDetails = useMemo(() => {\r\n const bundleDetailsObj = {};\r\n\r\n const { dimensions, colors, edges, reactionToFire } = properties;\r\n if (dimensions) {\r\n bundleDetailsObj[dimensionTitle] = dimensions;\r\n }\r\n if (colors) {\r\n bundleDetailsObj[colorsTitle] = colors;\r\n }\r\n if (edges) {\r\n bundleDetailsObj[edgesTitle] = edges;\r\n }\r\n if (reactionToFire) {\r\n bundleDetailsObj[reactionToFireTitle] = reactionToFire;\r\n }\r\n\r\n return bundleDetailsObj;\r\n }, [edgesTitle, dimensionTitle, colorsTitle, reactionToFireTitle, properties]);\r\n\r\n return (\r\n ,\r\n iconName: 'arrow-right',\r\n cssClass: 'btn btn--secondary is-small',\r\n link: url,\r\n }}\r\n cardExtensionCss=\"text-centered image\"\r\n labelCssExtensions=\"box-shadow text-centered image\"\r\n productCardBtnWrapChildren={\r\n pageType !== 'RockfonProductGrid' &&\r\n pageType !== 'RockfonProductSystem' &&\r\n hideCompare && \r\n }\r\n excerpt={excerpt}\r\n />\r\n );\r\n};\r\n\r\nexport { FilteredProducts };\r\n","import DownloadIcon from '@atoms/icons/DownloadIcon';\r\nimport { BaseCard } from '@organisms/BaseCard';\r\nimport { htmlAttributes } from 'app/consts';\r\nimport React, { FC } from 'react';\r\n\r\ninterface FilteredReportFeed {\r\n extensions: string;\r\n thumbnailUrl: string;\r\n altText: string;\r\n mediaSize: string;\r\n mediaType: string;\r\n headline: string;\r\n bodyText: string;\r\n hasHeadline: boolean;\r\n hasText: boolean;\r\n mediaUrl: string;\r\n target: string;\r\n fileName: string;\r\n trackingLabel: string;\r\n linkText: string;\r\n}\r\n\r\nconst FilteredReportFeed: FC = ({\r\n extensions,\r\n thumbnailUrl,\r\n altText,\r\n mediaSize,\r\n mediaType,\r\n headline,\r\n bodyText,\r\n hasHeadline,\r\n hasText,\r\n mediaUrl,\r\n target,\r\n fileName,\r\n trackingLabel,\r\n linkText,\r\n}) => (\r\n ,\r\n iconName: 'download',\r\n extensions: 'download-link',\r\n fileTitle: fileName,\r\n trackingLabel,\r\n fileType: mediaType,\r\n fileCategory: 'download',\r\n file: true,\r\n isWhiteIcon: false,\r\n target,\r\n link: mediaUrl,\r\n relationship:\r\n target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : '',\r\n }}\r\n hasBrand\r\n brandClass=\"\"\r\n brandLabel=\"\"\r\n url={mediaUrl}\r\n imageMaxWidth=\"\"\r\n target={target}\r\n hasImage\r\n singleStacked={false}\r\n hasLabels\r\n hasOverLay={false}\r\n overlay=\"\"\r\n primaryBtn={undefined}\r\n hideLabels={false}\r\n centerAligned\r\n isFullLink={false}\r\n isHiddenFromSearch={false}\r\n useBodyLink={false}\r\n file={{\r\n name: fileName,\r\n dataTrackingAction: 'click',\r\n trackingLabel,\r\n hrefAttribute: mediaUrl,\r\n gatedContent: false,\r\n fileType: mediaType,\r\n dataTrackingCategory: 'download',\r\n targetAttribute: target,\r\n dataTrackingLabel: trackingLabel,\r\n dataTrackingFileType: mediaType,\r\n relAttribute: `rel=${\r\n target === htmlAttributes.targetValueBlank ? htmlAttributes.relAttributeWithNoopenerNoReferrerVaule : ''\r\n }`,\r\n }}\r\n />\r\n);\r\n\r\nexport { FilteredReportFeed };\r\n","import ArrowIcon from '@atoms/icons/ArrowIcon';\r\nimport { ProductCard } from '@organisms/O95-ProductCard';\r\nimport React, { FC } from 'react';\r\nimport { CompareProductButton } from '../CompareProductButton';\r\n\r\ninterface ProductFilter {\r\n url: string;\r\n pageType: string;\r\n filterProperties: { value?: string; title?: string }[];\r\n name: string;\r\n image: string;\r\n productCategories: string;\r\n viewProduct: string;\r\n hideCompare: boolean;\r\n compareText: string;\r\n id: string;\r\n}\r\n\r\nconst ProductFilter: FC = ({\r\n url,\r\n image,\r\n name,\r\n productCategories,\r\n filterProperties,\r\n pageType,\r\n viewProduct,\r\n hideCompare,\r\n compareText,\r\n id,\r\n}) => {\r\n const bundleDetailsObj = {};\r\n filterProperties?.forEach(({ title, value }) => {\r\n if (value) {\r\n bundleDetailsObj[title] = value;\r\n }\r\n });\r\n\r\n return (\r\n ,\r\n iconName: 'arrow-right',\r\n cssClass: 'btn btn--secondary is-small',\r\n link: url,\r\n }}\r\n cardExtensionCss=\"text-centered image\"\r\n labelCssExtensions=\"box-shadow text-centered image\"\r\n productCardBtnWrapChildren={\r\n pageType !== 'RockfonProductGrid' &&\r\n pageType !== 'RockfonProductSystem' &&\r\n hideCompare && \r\n }\r\n />\r\n );\r\n};\r\n\r\nexport { ProductFilter };\r\n","import { htmlAttributes } from 'app/consts';\r\nimport React, { FC } from 'react';\r\n\r\ninterface ParafonSearchPageResult {\r\n url: string;\r\n pageType: string;\r\n goToPageText: string;\r\n name: string;\r\n location: string;\r\n excerpt: string;\r\n}\r\n\r\nconst ParafonSearchPageResult: FC = ({\r\n url,\r\n location,\r\n name,\r\n excerpt,\r\n goToPageText,\r\n pageType,\r\n}) => (\r\n \r\n);\r\n\r\nexport { ParafonSearchPageResult };\r\n","import { renderAjaxListComponent } from '@molecules/AjaxList';\r\nimport { elementsFactory } from './vanilla/elementsFactory';\r\n\r\nconst render = (el: HTMLElement): void => {\r\n renderAjaxListComponent(el);\r\n};\r\n\r\nexport default elementsFactory('js-ajax-list', render).create;\r\n","/**\r\n * Compare Products Tool\r\n *\r\n * @author Johannes Greve \r\n */\r\n\r\nimport Backbone from 'backbone';\r\nimport Handlebars from 'handlebars';\r\nimport { PubSubKeys ,PubSub} from '@utils/pubSub';\r\nimport { Marionette } from '../../framework/vendor';\r\n\r\nimport storage from '../../framework/storage';\r\nimport { ServerView } from '../../framework/server-side-views';\r\n\r\nimport iconClose from '../../../assets/images/svg/close.svg';\r\nimport { delegateSelector } from '../../framework/custom-functions';\r\n\r\nimport '../compiled-templates/rockworld/compare-products-collector-item-template';\r\n\r\nconst compareProductsCollectorItemTemplate = 'compare-products-collector-item';\r\n/**\r\n * List Item for CollectionView\r\n */\r\nconst ListItemView = Marionette.View.extend({\r\n className: 'O91-compare-products-collector__product-list__item',\r\n template: Handlebars.templates[compareProductsCollectorItemTemplate],\r\n\r\n triggers: {\r\n 'click .js-compare-collector-remove-product': 'clicked:item',\r\n },\r\n\r\n initialize() {\r\n this.model.set('iconClose', iconClose);\r\n },\r\n});\r\n\r\n/**\r\n * Collection view for View, uses ListItemView\r\n */\r\nconst CollectionView = Marionette.CollectionView.extend({\r\n className: 'O91-compare-products-collector__product-list',\r\n childView: ListItemView,\r\n\r\n initialize(options) {\r\n this.parent = options.parent;\r\n },\r\n\r\n onChildviewClickedItem(item) {\r\n this.parent.storage.removeItemById(item.model.attributes.productId);\r\n },\r\n});\r\n\r\n/**\r\n * Main view, the layout for this organism\r\n */\r\nconst View = ServerView.extend({\r\n el: '.O91-compare-products-collector',\r\n\r\n regions: {\r\n productList: '.js-compare-product-list',\r\n },\r\n\r\n events: {\r\n 'click .js-product-compare-open-toggle': '_toggleCollectorIsOpen',\r\n 'click .js-product-compare-clear-all': '_clearProducts',\r\n 'click .js-compare-cta': '_compareProducts',\r\n },\r\n\r\n onRender() {\r\n this.storage = storage('compareProducts', {\r\n idKey: 'productId',\r\n });\r\n\r\n this.storage.on('change', () => {\r\n this.collection.reset(this.storage.items);\r\n });\r\n\r\n this.collection = new Backbone.Collection();\r\n\r\n this.collection.bind('all', () => {\r\n this.updateAll();\r\n });\r\n\r\n this.collection.reset(this.storage.items);\r\n\r\n this.showChildView(\r\n 'productList',\r\n new CollectionView({\r\n parent: this,\r\n collection: this.collection,\r\n })\r\n );\r\n\r\n this.setupAddToCompare();\r\n },\r\n\r\n setupAddToCompare() {\r\n delegateSelector('body', 'click', '.js-select-compare-product', this.addToCompare.bind(this));\r\n },\r\n\r\n addToCompare(e) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n\r\n const element = e.target.closest('.js-select-compare-product');\r\n const isCheckbox = element.classList.contains('checkbox-wrap');\r\n const isChecked = !element.classList.contains('is-checked');\r\n const currentProductName = element.getAttribute('data-link-name');\r\n const currentProductImage = element.getAttribute('data-link-image');\r\n const currentProductId = element.getAttribute('data-link-id');\r\n\r\n // Get current productinfo\r\n const currentProduct = {\r\n productName: currentProductName,\r\n productImage: currentProductImage,\r\n productId: currentProductId,\r\n };\r\n\r\n // Add product to compare selector if is checked and maxAmount is not yet maxed\r\n if ((isCheckbox && isChecked && this.storage.count < 3) || (!isCheckbox && this.storage.count < 3)) {\r\n this.storage.addItem(currentProduct);\r\n\r\n if (isCheckbox) {\r\n element.classList.add('is-checked');\r\n }\r\n } else if (isCheckbox && !isChecked) {\r\n // If product is unchecked, remove product from product array\r\n this.storage.removeItemById(currentProductId);\r\n\r\n element.classList.remove('is-checked');\r\n } else if (this.storage.count === 3) {\r\n const warningMsg = document.querySelector('.js-compare-warning-message');\r\n const warningMsgText = warningMsg.getAttribute('data-message-to-many');\r\n const warningMsgBox = warningMsg.querySelector('.js-warning-text');\r\n\r\n warningMsgBox.innerHTML = warningMsgText;\r\n\r\n // If products array has the maximum amount of products show warning msg\r\n if (isCheckbox) {\r\n element.classList.remove('is-checked');\r\n }\r\n\r\n warningMsg.classList.remove('is-hidden');\r\n\r\n setTimeout(() => {\r\n warningMsg.classList.add('is-hidden');\r\n }, 3000);\r\n }\r\n },\r\n\r\n toggleShowTool() {\r\n if (this.collection.isEmpty()) {\r\n this.el.classList.add('is-hidden');\r\n this.el.classList.remove('is-open');\r\n } else {\r\n this.el.classList.remove('is-hidden');\r\n }\r\n },\r\n\r\n updateCompareCount() {\r\n const amountMarkup = this.el.querySelector('.js-compare-amount');\r\n amountMarkup.innerHTML = `${this.collection.length}/3`;\r\n },\r\n\r\n _toggleCollectorIsOpen() {\r\n const isOpen = this.el.classList.contains('is-open');\r\n if (isOpen) {\r\n this.el.classList.remove('is-open');\r\n } else {\r\n this.el.classList.add('is-open');\r\n }\r\n },\r\n\r\n _clearProducts() {\r\n PubSub.trigger(PubSubKeys.compareRemoveall);\r\n this.storage.removeAll();\r\n },\r\n\r\n updateAll() {\r\n this.toggleShowTool();\r\n this.updateCompareCount();\r\n this.updateCheckboxes();\r\n },\r\n\r\n updateCheckboxes() {\r\n const compareCheckboxes = document.querySelectorAll('.js-select-compare-product');\r\n\r\n [].forEach.call(compareCheckboxes, (checkbox) => {\r\n const isCheckbox = checkbox.classList.contains('checkbox-wrap');\r\n const currentProductId = checkbox.getAttribute('data-link-id');\r\n\r\n if (isCheckbox) {\r\n checkbox.classList.remove('is-checked');\r\n }\r\n\r\n [].forEach.call(this.storage.items, (product) => {\r\n if (currentProductId === product.productId && isCheckbox) {\r\n checkbox.classList.add('is-checked');\r\n }\r\n });\r\n });\r\n },\r\n\r\n _compareProducts(e) {\r\n const element = e.target;\r\n let compareUrl = element.getAttribute('href');\r\n const warningMsg = document.querySelector('.js-compare-warning-message');\r\n const warningMsgText = warningMsg.getAttribute('data-message-not-enough');\r\n const warningMsgBox = warningMsg.querySelector('.js-warning-text');\r\n\r\n if (this.storage.count === 1) {\r\n e.preventDefault();\r\n warningMsgBox.innerHTML = warningMsgText;\r\n // If products array has the maximum amount of products show warning msg\r\n warningMsg.classList.remove('is-hidden');\r\n setTimeout(() => {\r\n warningMsg.classList.add('is-hidden');\r\n }, 3000);\r\n } else {\r\n const customTrackingData = {};\r\n compareUrl += '?products=';\r\n [].forEach.call(this.storage.items, (item, index) => {\r\n // Check if it is last item\r\n if (index === this.storage.count - 1) {\r\n compareUrl += `${item.productId}`;\r\n } else {\r\n compareUrl += `${item.productId},`;\r\n }\r\n customTrackingData[`comparedProduct${index + 1}`] = item.productName;\r\n });\r\n App.tracking.trackEvent('product pages', 'comparison', 'product', customTrackingData);\r\n }\r\n\r\n element.setAttribute('href', compareUrl);\r\n },\r\n});\r\n\r\nexport default View;\r\n","import { EventEnum, publish, subscribe, unsubscribe } from '@utils/custom-events';\r\nimport { elementsFactory } from './vanilla/elementsFactory';\r\n\r\nexport interface GatedContentFile {\r\n fileTypes: FileType[];\r\n image: string;\r\n text: string;\r\n title: string | null;\r\n selectLabel: string;\r\n downloadBtnText: string | null;\r\n hasDownload: boolean;\r\n isGated: boolean;\r\n isNotDownloadedAfterFormSent: boolean;\r\n isSingleSignupDownload: boolean;\r\n fileMarketoFormId: string;\r\n fileMarketoFormName: string;\r\n fileMarketoFilename: string;\r\n hasPrint: boolean;\r\n}\r\ninterface FileType {\r\n label: string;\r\n value: string;\r\n fileId: number;\r\n url: string | null;\r\n isGated: boolean;\r\n isPrintOnly: boolean;\r\n printId: number | null;\r\n marketoFormId: string;\r\n marketoFormName: string;\r\n marketoFilename: string;\r\n}\r\n\r\nlet anhorLinkElements = null;\r\nlet downloadMaterialsHandler = null;\r\nlet checkIfGated = null;\r\n\r\nexport const render = (el: Element): void => {\r\n const formUrl = '/custom-routes/personalization/submitform';\r\n let fileIdToDownload = null;\r\n anhorLinkElements = el.querySelectorAll('.js-download');\r\n downloadMaterialsHandler = (props: CustomEvent) => {\r\n if (props.detail && fileIdToDownload) {\r\n publish(EventEnum.closeReactModal, true);\r\n downloadMaterials();\r\n }\r\n };\r\n\r\n subscribe(EventEnum.downloadMaterialsGatedContent, downloadMaterialsHandler);\r\n\r\n checkIfGated = (e: Event) => {\r\n const currentElement = e.currentTarget as HTMLElement;\r\n const dataObjectStr = currentElement.getAttribute('data-object');\r\n if (!dataObjectStr) {\r\n return;\r\n }\r\n\r\n const dataObject = JSON.parse(dataObjectStr);\r\n const { fileTypes, isGated, isSingleSignupDownload } = dataObject;\r\n const firstFileType = fileTypes[0];\r\n fileIdToDownload = firstFileType.fileId;\r\n if (firstFileType.isGated) {\r\n e.preventDefault();\r\n let formName = currentElement.getAttribute('data-form-name');\r\n let formId = currentElement.getAttribute('data-form-id');\r\n const noDownload = currentElement.getAttribute('data-no-download') === 'true';\r\n const dataTarget = currentElement.getAttribute('data-target');\r\n\r\n let gatedDownload = [\r\n {\r\n count: 1,\r\n data: dataObject,\r\n format: firstFileType.value,\r\n id: firstFileType.fileId,\r\n },\r\n ];\r\n\r\n if (dataObject.fileMarketoFormId) {\r\n formId = dataObject.fileMarketoFormId;\r\n formName = dataObject.fileMarketoFormName;\r\n }\r\n\r\n if (noDownload) {\r\n gatedDownload = [];\r\n }\r\n\r\n if (dataObject.isNotDownloadedAfterFormSent) {\r\n fileIdToDownload = null;\r\n }\r\n\r\n const visitorFilledForm = App.visitor.isFormFilled;\r\n\r\n if (\r\n !isGated ||\r\n (visitorFilledForm &&\r\n (document.querySelector('body').dataset.enableSingleSignupDocumentsDownload === 'true' ||\r\n isSingleSignupDownload))\r\n ) {\r\n downloadMaterials();\r\n return;\r\n }\r\n\r\n openGateModal('multiple-download', formName, formId, formUrl, gatedDownload, dataTarget, dataObject);\r\n }\r\n };\r\n\r\n const downloadMaterials = () => {\r\n if (fileIdToDownload) {\r\n window.open(`/api/MultipleDownloads/zip?files=${fileIdToDownload}`);\r\n fileIdToDownload = null;\r\n }\r\n };\r\n\r\n const openGateModal = (\r\n gateType,\r\n dataFormName,\r\n dataFormId,\r\n dataFormUrl,\r\n gatedDownload,\r\n dataTarget,\r\n gatedDownloads\r\n ) => {\r\n publish(EventEnum.openGatedContentModal, {\r\n modalType: 'marketo',\r\n name: dataFormName ? dataFormName.trim() : '',\r\n formUrl: dataFormUrl,\r\n id: dataFormId ? dataFormId.trim() : '',\r\n gateType,\r\n 'data-target': dataTarget,\r\n fileIdToDownload,\r\n gatedDownloads,\r\n });\r\n };\r\n\r\n if (anhorLinkElements?.length) {\r\n anhorLinkElements.forEach((anhorLinkElement) => anhorLinkElement.addEventListener('click', checkIfGated));\r\n }\r\n};\r\n\r\nexport const destroy = (): void => {\r\n if (anhorLinkElements?.length) {\r\n anhorLinkElements.forEach((anhorLinkElement) => anhorLinkElement.removeEventListener('click', checkIfGated));\r\n }\r\n\r\n unsubscribe(EventEnum.downloadMaterialsGatedContent, downloadMaterialsHandler);\r\n};\r\n\r\nexport default elementsFactory('gated-link-container', render, destroy).create;\r\n","/**\r\n * Go To Top\r\n *\r\n * @author Johannes Holger Greve \r\n */\r\n\r\nimport { elementsFactory } from './vanilla/elementsFactory';\r\n\r\nconst render = (): void => {\r\n window.onload = () => {\r\n if (!window.location.hash) {\r\n return;\r\n }\r\n\r\n const uniqueAnchor = document.querySelectorAll(window.location.hash);\r\n const fixedElementHeight = 64;\r\n\r\n if (uniqueAnchor.length > 0) {\r\n const rect = uniqueAnchor[0].getBoundingClientRect();\r\n const scrollPosition = rect.top + window.scrollY - fixedElementHeight;\r\n setTimeout(() => {\r\n window.scrollTo(0, scrollPosition);\r\n }, 200);\r\n }\r\n };\r\n};\r\n\r\nexport default elementsFactory('hasAnchorId', render).create;\r\n","export default {\r\n Esc: 27,\r\n Enter: 13,\r\n arrowLeft: 37,\r\n arrowUp: 38,\r\n arrowRight: 39,\r\n arrowDown:40\r\n};\r\n","/**\r\n * Modal Multi Selector\r\n *\r\n * @author Daniel Kvistgaard \r\n */\r\n\r\nimport _ from 'underscore';\r\n\r\nimport { PubSubKeys ,PubSub} from '@utils/pubSub';\r\nimport { ServerView } from '../../framework/server-side-views';\r\nimport { delegateSelector } from '../../framework/custom-functions';\r\n\r\nconst triggerBrandSelector = function triggerBrandSelector(e) {\r\n const paramString = e.target.search;\r\n\r\n // Stop if there aren't a link on the clicked element, no parameters\r\n // in the link or if the user is right clicking.\r\n if (!e.target.href || !paramString || e.which === 3) {\r\n return;\r\n }\r\n\r\n // Determine which brand selector it is by looking at the linked url\r\n const hostname = e.target.hostname.replace(/^www.|^.com/g, '');\r\n const isRockwool = hostname.indexOf('rockwool') >= 0 && hostname.indexOf('rockwoolgroup') === -1;\r\n const isRockfon = hostname.indexOf('rockfon') >= 0;\r\n const isGrodan = hostname.indexOf('grodan') >= 0;\r\n const isLapinus = hostname.indexOf('lapinus') >= 0;\r\n const isRockpanel = hostname.indexOf('rockpanel') >= 0;\r\n\r\n // Parse the url parameters of the clicked element into an object\r\n const params = _.object(\r\n _.compact(_.map(paramString.replace(/\\?/g, '').split('&'), (item) => (item ? item.split('=') : '')))\r\n );\r\n\r\n // If brandselector is defined in the url parameters, then we'll open\r\n // the brand selector matching the url.\r\n if (Object.keys(params).indexOf('brandselector') >= 0) {\r\n if (isRockwool) {\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n PubSub.trigger(PubSubKeys.openMultiSelector, e, 'rockwool');\r\n } else if (isRockfon) {\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n PubSub.trigger(PubSubKeys.openMultiSelector, e, 'rockfon');\r\n } else if (isGrodan) {\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n PubSub.trigger(PubSubKeys.openMultiSelector, e, 'grodan');\r\n } else if (isLapinus) {\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n PubSub.trigger(PubSubKeys.openMultiSelector, e, 'lapinus');\r\n } else if (isRockpanel) {\r\n e.preventDefault();\r\n e.stopImmediatePropagation();\r\n PubSub.trigger(PubSubKeys.openMultiSelector, e, 'rockpanel');\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * Registrering global click event listeners.\r\n * Instead of simply using click events, we're reimplementing the\r\n * click event using mousedown and touchstart/touchend. That way we\r\n * can trigger the JS functionality before any other functions bound\r\n * to the normal click event.\r\n */\r\nlet hasGlobalEventListener = false;\r\nconst globalClickEventListener = function globalClickEventListener() {\r\n if (hasGlobalEventListener) {\r\n return;\r\n }\r\n\r\n hasGlobalEventListener = true;\r\n\r\n // Using mousedown for desktop devices which triggers before click.\r\n delegateSelector('body', 'mousedown', 'a', (e) => {\r\n triggerBrandSelector(e);\r\n });\r\n\r\n // Using touchstart and touchend to register the tab event on\r\n // touch devices. Using 300ms as the max delay between touchstart\r\n // and touchend to determine if the user is tapping instead of\r\n // e.g. scrolling.\r\n let tabStart = null;\r\n let touchStartX = null;\r\n let touchStartY = null;\r\n\r\n delegateSelector('body', 'touchstart', 'a', (e) => {\r\n tabStart = new Date().getTime();\r\n\r\n const changedTouch = e.changedTouches[0];\r\n\r\n touchStartX = changedTouch.clientX;\r\n touchStartY = changedTouch.clientY;\r\n });\r\n\r\n delegateSelector('body', 'touchend', 'a', (e) => {\r\n const changedTouch = e.changedTouches[0];\r\n const elem = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY);\r\n\r\n if (\r\n e.target !== elem ||\r\n Math.abs(touchStartX - changedTouch.clientX) > 10 ||\r\n Math.abs(touchStartY - changedTouch.clientY) > 10\r\n ) {\r\n return;\r\n }\r\n\r\n if (Math.abs(new Date().getTime() - tabStart) <= 300) {\r\n triggerBrandSelector(e);\r\n }\r\n });\r\n};\r\n\r\nconst View = ServerView.extend({\r\n el: '.js-select-modal',\r\n\r\n events: {\r\n 'click .js-lang__close': 'onClickLangClose',\r\n },\r\n\r\n onRender() {\r\n globalClickEventListener();\r\n\r\n // Listener on global Radio\r\n PubSub.on('open:multi-selector', this.onClickLangOpen.bind(this));\r\n\r\n this.initializeFilters();\r\n\r\n this.brand = false;\r\n\r\n if (this.el.classList.contains('is-rockwool-selector')) {\r\n this.brand = 'ROCKWOOL';\r\n } else if (this.el.classList.contains('is-rockfon-selector')) {\r\n this.brand = 'Rockfon';\r\n } else if (this.el.classList.contains('is-rockpanel-selector')) {\r\n this.brand = 'Rockpanel';\r\n } else if (this.el.classList.contains('is-grodan-selector')) {\r\n this.brand = 'Grodan';\r\n } else if (this.el.classList.contains('is-lapinus-selector')) {\r\n this.brand = 'Lapinus';\r\n } else if (this.el.classList.contains('is-group-selector')) {\r\n this.brand = 'Group';\r\n }\r\n },\r\n\r\n initializeFilters() {\r\n const filters = this.el.querySelectorAll('.js-filter');\r\n [].forEach.call(filters, (filter) => {\r\n filter.addEventListener('new-active', this.onClickFilter.bind(this), false);\r\n });\r\n },\r\n\r\n onClickLangOpen(e, brand) {\r\n if (e) {\r\n e.preventDefault();\r\n }\r\n\r\n document.documentElement.classList.add('modal-multi-selector--open');\r\n\r\n const target = e?.currentTarget;\r\n const isLanguageSelector =\r\n this.el.classList.contains('is-language-selector') && target?.classList.contains('open-language-selector');\r\n const isBrandRockWoolSelector =\r\n this.el.classList.contains('is-rockwool-selector') &&\r\n ((target?.classList.contains('open-rockwool-selector') && !brand) || brand === 'rockwool');\r\n const isBrandRockfonSelector =\r\n this.el.classList.contains('is-rockfon-selector') &&\r\n ((target?.classList.contains('open-rockfon-selector') && !brand) || brand === 'rockfon');\r\n const isBrandRockpanelSelector =\r\n this.el.classList.contains('is-rockpanel-selector') &&\r\n ((target?.classList.contains('open-rockpanel-selector') && !brand) || brand === 'rockpanel');\r\n const isBrandGrodanSelector =\r\n this.el.classList.contains('is-grodan-selector') &&\r\n ((target?.classList.contains('open-grodan-selector') && !brand) || brand === 'grodan');\r\n const isBrandLapinusSelector =\r\n this.el.classList.contains('is-lapinus-selector') &&\r\n ((target?.classList.contains('open-lapinus-selector') && !brand) || brand === 'lapinus');\r\n const isGroupSelector =\r\n this.el.classList.contains('is-group-selector') &&\r\n ((target?.classList.contains('open-group-selector') && !brand) || brand === 'group');\r\n\r\n if (isLanguageSelector) {\r\n this.getLanguageSelectorAsync('language', 'is-language-selector', true);\r\n this.el.classList.add('is-open');\r\n } else if (isBrandRockWoolSelector) {\r\n this.getLanguageSelectorAsync('rockwool', 'is-rockwool-selector', false);\r\n this.el.classList.add('is-open');\r\n } else if (isBrandRockfonSelector) {\r\n this.getLanguageSelectorAsync('rockfon', 'is-rockfon-selector', false);\r\n this.el.classList.add('is-open');\r\n } else if (isBrandRockpanelSelector) {\r\n this.getLanguageSelectorAsync('rockpanel', 'is-rockpanel-selector', false);\r\n this.el.classList.add('is-open');\r\n } else if (isBrandGrodanSelector) {\r\n this.getLanguageSelectorAsync('grodan', 'is-grodan-selector', false);\r\n this.el.classList.add('is-open');\r\n } else if (isBrandLapinusSelector) {\r\n this.getLanguageSelectorAsync('lapinus', 'is-lapinus-selector', false);\r\n this.el.classList.add('is-open');\r\n } else if (isGroupSelector) {\r\n this.getLanguageSelectorAsync('group', 'is-group-selector', false);\r\n this.el.classList.add('is-open');\r\n }\r\n },\r\n\r\n setInitLanguageView(isLanguageSelector, allFilters) {\r\n // If is brand-selector start in Europe\r\n if (!isLanguageSelector && allFilters) {\r\n let activeItem = this.el.querySelector('.O23-tabs__item.is-active .js-filter');\r\n\r\n if (!activeItem) {\r\n activeItem = this.el.querySelector('.js-filter[data-filter=\"europe\"]');\r\n }\r\n\r\n if (!activeItem) {\r\n activeItem = this.el.querySelector('.js-filter');\r\n }\r\n\r\n const predictionEl = this.el.querySelector('.js-prediction');\r\n\r\n if (predictionEl) {\r\n predictionEl.parentNode.classList.add('is-hidden');\r\n }\r\n\r\n if (activeItem) {\r\n activeItem.click();\r\n }\r\n }\r\n },\r\n\r\n onClickLangClose(e) {\r\n e.preventDefault();\r\n\r\n document.documentElement.classList.remove('modal-multi-selector--open');\r\n this.el.classList.remove('is-open');\r\n },\r\n\r\n onClickFilter(e) {\r\n e.preventDefault();\r\n let { target } = e;\r\n if (!target.classList.contains('js-filter')) {\r\n target = e.target.closest('.js-filter.');\r\n }\r\n const parent = target.parentNode;\r\n\r\n // Stopping if the target already is the active filter\r\n if (parent.classList.contains('is-filtered')) {\r\n return;\r\n }\r\n\r\n const { filter } = target.dataset;\r\n this.showItemsByFilter(filter);\r\n\r\n // Set current target as active filter\r\n parent.classList.add('is-filtered');\r\n },\r\n\r\n showItemsByFilter(filter) {\r\n const allFilters = this.el.querySelectorAll('.js-filter');\r\n const allItems = this.el.querySelectorAll('.modal-multi-selector__item');\r\n\r\n // Remove all other previously active filters\r\n [].forEach.call(allFilters, (item) => {\r\n item.parentNode.classList.remove('is-filtered');\r\n });\r\n\r\n // Loop through all items and check if they match the filter\r\n [].forEach.call(allItems, (item) => {\r\n const itemFilter = item.getAttribute('data-filter');\r\n\r\n if (filter === 'all' || itemFilter.indexOf(filter) >= 0) {\r\n item.style.display = '';\r\n } else {\r\n item.style.display = 'none';\r\n }\r\n });\r\n },\r\n\r\n // eslint-disable-next-line consistent-return\r\n getLanguageSelectorAsync(brand, selectorClass, isLanguageSelector) {\r\n const asyncContainer = document.querySelector('.async-language-selector');\r\n const url = asyncContainer ? asyncContainer.dataset.url : '';\r\n const cultureInfo = asyncContainer ? asyncContainer.dataset.cultureInfo : '';\r\n const callback = asyncContainer ? asyncContainer.dataset.callback : '';\r\n const languageSelectorContainer = document.querySelector(`.${selectorClass}`);\r\n\r\n const fetchOptions = {\r\n method: 'GET',\r\n };\r\n if (languageSelectorContainer.children.length === 1) {\r\n return fetch(`${url}?brand=${brand}&cultureInfo=${cultureInfo}`, fetchOptions)\r\n .then((response) => {\r\n if (!response.ok) {\r\n throw new Error(`${response.status} - ${response.statusText} (url: ${response.url})`);\r\n }\r\n return response.text();\r\n })\r\n .then(async (text) => {\r\n languageSelectorContainer.innerHTML = text;\r\n\r\n if (callback && window.App.InitializeView) {\r\n await window.App.InitializeView(callback.split(','));\r\n }\r\n window.App.InitBasicSelects(languageSelectorContainer);\r\n\r\n const allFilters = this.el.querySelectorAll('.O23-tabs__item__inner');\r\n this.initializeFilters();\r\n this.setInitLanguageView(isLanguageSelector, allFilters);\r\n })\r\n .catch((ex) => {\r\n languageSelectorContainer.innerHTML = '';\r\n console.warn('somethings wrong', ex);\r\n });\r\n }\r\n },\r\n});\r\n\r\nexport default View;\r\n","/**\r\n * O7 Brand Selector\r\n */\r\n\r\nimport Backbone from 'backbone';\r\n\r\nimport { ServerView } from '../../framework/server-side-views';\r\n\r\nconst View = ServerView.extend({\r\n el: '.js-lang-selector__open',\r\n\r\n events: {\r\n click: 'onClickLangSelectorOpen',\r\n },\r\n\r\n isVisible: false,\r\n onBodyClickHandler: () => {},\r\n\r\n onRender() {\r\n this.linksMenu = this.el.querySelector('.login-links-menu');\r\n\r\n if (this.linksMenu) {\r\n window.addEventListener('scroll', this.onWindowScroll.bind(this), { passive: true });\r\n }\r\n\r\n this.onBodyClickHandler = this.onBodyClick.bind(this);\r\n },\r\n\r\n onWindowScroll() {\r\n if (this.linksMenu && this.isVisible) {\r\n this.close();\r\n }\r\n },\r\n\r\n onClickLangSelectorOpen(e) {\r\n let classList = e.target.classList;\r\n let parentElement = e.target.parentElement;\r\n let parentNode = e.target.parentNode;\r\n\r\n if (\r\n (classList && classList.contains('js-lang-selector-allow')) ||\r\n (parentElement && parentElement.classList && parentElement.classList.contains('js-lang-selector-allow'))\r\n ) {\r\n return;\r\n }\r\n\r\n if (classList && classList.contains('lang-selector-menu__item')) {\r\n return;\r\n }\r\n\r\n if (this.linksMenu && Modernizr && Modernizr.mq(`(min-width: ${App.mq.desktop}px)`)) {\r\n if (\r\n this.isVisible &&\r\n classList &&\r\n (classList.contains('js-lang-selector__open') || parentNode.classList.contains('js-lang-selector__open'))\r\n ) {\r\n this.close();\r\n } else if (!this.isVisible) {\r\n this.open();\r\n }\r\n } else {\r\n // Setting up certificate modals\r\n App.modal.create({\r\n view: 'OpenLangSelector',\r\n model: new Backbone.Model(JSON.parse(e.currentTarget.getAttribute('data-content'))),\r\n });\r\n }\r\n },\r\n close() {\r\n this.isVisible = false;\r\n this.linksMenu.classList.remove('is-visible');\r\n\r\n document.removeEventListener('click', this.onBodyClickHandler);\r\n },\r\n\r\n open() {\r\n this.isVisible = true;\r\n this.linksMenu.classList.add('is-visible');\r\n\r\n setTimeout(() => {\r\n document.addEventListener('click', this.onBodyClickHandler);\r\n }, 0);\r\n },\r\n onBodyClick(e) {\r\n if (!e.target || !e.target.classList) {\r\n return;\r\n }\r\n\r\n if (\r\n e.target.classList.contains('.js-lang-selector__open') ||\r\n e.target.parentElement.querySelectorAll('.js-lang-selector__open').length\r\n ) {\r\n return;\r\n }\r\n\r\n this.close();\r\n },\r\n\r\n /**\r\n * Finding the closest element in the parent tree.\r\n *\r\n * @param {HTMLElement} el\r\n * @param {String} target\r\n */\r\n closest(el, target) {\r\n let ancestor = el;\r\n\r\n if (!document.documentElement.contains(el)) {\r\n return null;\r\n }\r\n\r\n do {\r\n if (ancestor && ancestor instanceof HTMLElement) {\r\n if (ancestor.matches ? ancestor.matches(target) : ancestor.msMatchesSelector(target)) {\r\n return ancestor;\r\n }\r\n }\r\n\r\n ancestor = ancestor.parentElement || ancestor.parentNode;\r\n } while (ancestor !== null);\r\n\r\n return null;\r\n },\r\n});\r\n\r\nexport default View;\r\n","/**\r\n * O7 Brand Selector\r\n */\r\nimport { PubSubKeys , PubSub} from '@utils/pubSub';\r\nimport { ServerView } from '../../framework/server-side-views';\r\n\r\nconst View = ServerView.extend({\r\n el: '.js-multi-selector__open',\r\n\r\n events: {\r\n click: 'onClickMultiSelectOpen',\r\n },\r\n\r\n onClickMultiSelectOpen(e) {\r\n e.preventDefault();\r\n\r\n PubSub.trigger(PubSubKeys.openMultiSelector, e);\r\n },\r\n});\r\n\r\nexport default View;\r\n","import { elementsFactory } from './vanilla/elementsFactory';\r\nimport BasketOverlay from './O97-basket-overlay';\r\n\r\ndeclare global {\r\n interface CheckoutData {\r\n orderSampleHeading: string;\r\n orderSampleEdgeText: string;\r\n orderSampleColor: string;\r\n orderSampleBtnText: string;\r\n selectEdgePlaceholder: string;\r\n selectColorPlaceholder: string;\r\n quantity: string;\r\n \r\n }\r\n\r\n interface Window {\r\n checkoutData: CheckoutData;\r\n }\r\n}\r\n\r\ntype OrderSampleData = {\r\n orderSampleHeading?: string;\r\n orderSampleEdgeText?: string;\r\n orderSampleColor?: string;\r\n orderSampleBtnText?: string;\r\n selectEdgePlaceholder?: string;\r\n selectColorPlaceholder?: string;\r\n quantity?: string;\r\n tileName?: string;\r\n tileId?: string;\r\n noExcludeColour?: boolean;\r\n noExcludeEdges?: boolean;\r\n edgeOptions?: string;\r\n selectedEdgeValue?: string;\r\n selectedColorValue?: string;\r\n productImage?: string;\r\n};\r\n\r\nconst render = (el: HTMLDivElement): void => {\r\n const orderSampleData: OrderSampleData = {};\r\n\r\n const onWindowScroll = () => {\r\n const scrollTop = window.scrollY || window.pageYOffset;\r\n\r\n if (scrollTop >= 100) {\r\n el.classList.remove('is-hidden');\r\n } else {\r\n el.classList.add('is-hidden');\r\n }\r\n };\r\n\r\n const onClick = (e) => {\r\n e.preventDefault();\r\n\r\n let orderSampleButton = e.currentTarget;\r\n if (\r\n !(\r\n (orderSampleButton.classList.contains('O81-product-hero__buttons__item') ||\r\n orderSampleButton.classList.contains('btn')) &&\r\n orderSampleButton.classList.contains('js-order-sample-edge-options')\r\n )\r\n ) {\r\n orderSampleButton = orderSampleButton.closest('.card').querySelector('.btn.js-order-sample-edge-options');\r\n }\r\n\r\n if (!orderSampleButton) {\r\n return;\r\n }\r\n\r\n const thisTileId = orderSampleButton.getAttribute('data-product-id');\r\n const thisTileName = orderSampleButton.getAttribute('data-product-name');\r\n const thisEdgeOptions = orderSampleButton.getAttribute('data-edge-options');\r\n const selectedEdgeValueFilterSelector = orderSampleButton.getAttribute('data-edge-value-selector');\r\n const selectedColorValueFilterSelector = orderSampleButton.getAttribute('data-color-value-selector');\r\n const thisExcludeColour = orderSampleButton.getAttribute('data-exclude-colour').toLowerCase() === 'true';\r\n const thisExcludeEdges = orderSampleButton.getAttribute('data-exclude-edges').toLowerCase() === 'true';\r\n\r\n // Setting modal translation texts\r\n orderSampleData.orderSampleHeading = window.checkoutData.orderSampleHeading;\r\n orderSampleData.orderSampleEdgeText = window.checkoutData.orderSampleEdgeText;\r\n orderSampleData.orderSampleColor = window.checkoutData.orderSampleColor;\r\n orderSampleData.orderSampleBtnText = window.checkoutData.orderSampleBtnText;\r\n orderSampleData.selectEdgePlaceholder = window.checkoutData.selectEdgePlaceholder;\r\n orderSampleData.selectColorPlaceholder = window.checkoutData.selectColorPlaceholder;\r\n orderSampleData.quantity = window.checkoutData.quantity;\r\n orderSampleData.tileName = thisTileName;\r\n orderSampleData.tileId = thisTileId;\r\n orderSampleData.noExcludeColour = !thisExcludeColour;\r\n orderSampleData.noExcludeEdges = !thisExcludeEdges;\r\n orderSampleData.edgeOptions = JSON.parse(thisEdgeOptions);\r\n orderSampleData.selectedEdgeValue = getSelectedValueInFilter(selectedEdgeValueFilterSelector);\r\n orderSampleData.selectedColorValue = getSelectedValueInFilter(selectedColorValueFilterSelector);\r\n\r\n const image = el.querySelector('.image-wrap img');\r\n const imgSrc = image ? image.getAttribute('src') : '';\r\n orderSampleData.productImage = imgSrc;\r\n\r\n App.modal.create({\r\n view: BasketOverlay,\r\n type: 'order-sample',\r\n model: orderSampleData,\r\n });\r\n };\r\n\r\n const getSelectedValueInFilter = (filterSelector) => {\r\n const element = document.querySelector(filterSelector);\r\n if (!element) {\r\n return null;\r\n }\r\n\r\n return element.parentNode\r\n .querySelector('.is-selected')\r\n .getAttribute('data-value')\r\n .replace(/ /g, String.fromCharCode(160));\r\n };\r\n\r\n onWindowScroll();\r\n window.addEventListener('scroll', onWindowScroll, { passive: true });\r\n el.addEventListener('click', onClick);\r\n};\r\n\r\nexport default elementsFactory('js-order-sample-edge-options', render).create;\r\n","import { ServerView } from '../../../framework/server-side-views';\r\nimport KeyCodes from '../key-codes';\r\nimport Search from '../../../framework/search';\r\nimport { SwipeEventDispatcher } from './helpers';\r\n\r\nconst overlayClass = 'js-menu-overlay';\r\nconst suggestionsVisibleClass = 'suggestions-visible';\r\nconst headerSearchOpenClass = 'header-search-open';\r\nconst menuOpenClass = 'menu-open';\r\nconst languageOpenClass = 'language-open';\r\nconst isOpenClass = 'is-open';\r\nconst isActiveClass = 'is-active';\r\n\r\nconst O201headerview = ServerView.extend({\r\n el: '.O201-header-js',\r\n events: {\r\n 'click .js-menu': 'onClickMenu',\r\n 'click .js-menu-item': 'onTopNavClick',\r\n 'click .js-menu-sub-item': 'onSubItemClick',\r\n 'click .btn-language': 'onLanguageClick',\r\n 'click .js-menu-overlay': 'onOverlayClick',\r\n 'click .js-search__reset': 'clearSearch',\r\n 'submit form': 'trackSubmitSearchForm',\r\n 'focus .O201-header-search-input': 'trackOpenSearch',\r\n 'click .O201-header__search__suggestions__list': 'trackSelectSuggest',\r\n },\r\n\r\n onRender() {\r\n this.isDesktopSelectorName = App.mq.desktopLarge;\r\n this.searchInput = this.el.querySelector('.O201-header-search-input');\r\n this.overlayEl = document.querySelector(`.${overlayClass}`);\r\n this.searchBtn = this.el.querySelector('.O201-header__tools__item__search');\r\n this.topNavArea = this.el.querySelector('.O201-header__top_nav');\r\n this.languageSelector = document.querySelector('.language-selector');\r\n this.languageBtn = this.el.querySelector('.btn-language');\r\n this.search = new Search();\r\n window.addEventListener('resize', this.onResize.bind(this));\r\n this.overlayEl.addEventListener('click', this.onOverlayClick.bind(this));\r\n this.searchBtn.addEventListener('click', this.onSearchClick.bind(this));\r\n this.searchInput.addEventListener('keyup', this.searchKeyUp.bind(this));\r\n const searchForm = this.el.querySelector('.search-form__form');\r\n if (searchForm && searchForm.length) {\r\n this.searchUrl = searchForm.getAttribute('action');\r\n }\r\n\r\n this.lastWindowWidth = window.innerWidth;\r\n this.isMainMenuOpen = false;\r\n this.isLanguageMenuOpen = false;\r\n\r\n const topNavAreaSwipe = new SwipeEventDispatcher(this.topNavArea);\r\n topNavAreaSwipe.on('SWIPE_RIGHT', () => {\r\n if (this.isMainMenuOpen) {\r\n this.removeMenuClasses();\r\n }\r\n });\r\n\r\n const languageSelectorSwipe = new SwipeEventDispatcher(this.languageSelector);\r\n languageSelectorSwipe.on('SWIPE_LEFT', () => {\r\n if (this.isLanguageMenuOpen) {\r\n this.removeLanguageClasses();\r\n }\r\n });\r\n },\r\n\r\n onResize() {\r\n if (this.isResizingFromMobileToDesktop()) {\r\n this.removeMenuClasses();\r\n this.removeLanguageClasses();\r\n this.hideSearch();\r\n }\r\n this.lastWindowWidth = window.innerWidth;\r\n },\r\n\r\n isResizingFromMobileToDesktop() {\r\n const currentWindowWidth = window.innerWidth;\r\n if (this.lastWindowWidth < this.isDesktopSelectorName && currentWindowWidth >= this.isDesktopSelectorName) {\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n\r\n isResizingFromDesktopToMobile() {\r\n const currentWindowWidth = window.innerWidth;\r\n if (this.lastWindowWidth >= this.isDesktopSelectorName && currentWindowWidth < this.isDesktopSelectorName) {\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n\r\n clearSearch() {\r\n this.searchInput.value = '';\r\n\r\n if (this.el.classList.contains(suggestionsVisibleClass)) {\r\n this.el.classList.remove(suggestionsVisibleClass);\r\n }\r\n if (this.el.classList.contains('search-input-dirty')) {\r\n this.el.classList.remove('search-input-dirty');\r\n }\r\n },\r\n\r\n onOverlayClick() {\r\n this.closeSubMenu();\r\n this.overlayEl.classList.remove(isActiveClass);\r\n this.hideSearch();\r\n },\r\n\r\n addMenuClasses() {\r\n document.querySelector('.O201-header__tools__menu-btn').classList.add(menuOpenClass);\r\n this.el.classList.add(menuOpenClass);\r\n document.documentElement.classList.add(menuOpenClass);\r\n this.isMainMenuOpen = true;\r\n },\r\n\r\n removeMenuClasses() {\r\n document.querySelector('.O201-header__tools__menu-btn').classList.remove(menuOpenClass);\r\n this.el.classList.remove(menuOpenClass);\r\n document.documentElement.classList.remove(menuOpenClass);\r\n this.isMainMenuOpen = false;\r\n },\r\n\r\n addLanguageClasses() {\r\n this.languageBtn.classList.add(languageOpenClass);\r\n this.el.classList.add(languageOpenClass);\r\n document.documentElement.classList.add(languageOpenClass);\r\n this.isLanguageMenuOpen = true;\r\n },\r\n\r\n removeLanguageClasses() {\r\n this.languageBtn.classList.remove(languageOpenClass);\r\n this.el.classList.remove(languageOpenClass);\r\n document.documentElement.classList.remove(languageOpenClass);\r\n\r\n if (this.languageSelector) {\r\n this.languageSelector.classList.remove(isOpenClass);\r\n }\r\n\r\n this.isLanguageMenuOpen = false;\r\n },\r\n\r\n onClickMenu(e) {\r\n this.trackOpenLanguageDropdown();\r\n e.preventDefault();\r\n this.removeLanguageClasses();\r\n\r\n if (e.delegateTarget.classList.contains(menuOpenClass)) {\r\n this.removeMenuClasses();\r\n this.el.classList.remove(headerSearchOpenClass);\r\n return;\r\n }\r\n\r\n this.addMenuClasses();\r\n this.el.classList.add('right-transition');\r\n this.el.classList.add(headerSearchOpenClass);\r\n },\r\n\r\n onLanguageClick(e) {\r\n this.trackOpenLanguageDropdown();\r\n this.hideSearch();\r\n e.preventDefault();\r\n\r\n if (e.delegateTarget.classList.contains(languageOpenClass)) {\r\n this.removeLanguageClasses();\r\n return;\r\n }\r\n\r\n if (this.languageSelector) {\r\n this.openSubMenu(this.languageSelector, e);\r\n this.addLanguageClasses();\r\n }\r\n\r\n this.removeMenuClasses();\r\n if (this.el.classList.contains('right-transition')) {\r\n this.el.classList.remove('right-transition');\r\n }\r\n },\r\n\r\n onTopNavClick(e) {\r\n const hasSub = e.delegateTarget.classList.contains('has-sub');\r\n const childElem = e.target.closest('.js-menu-sub-item');\r\n if (!hasSub || childElem) {\r\n // No sub-items or sub-item click, so just go to the link - default behaviour.\r\n return;\r\n }\r\n e.preventDefault();\r\n this.hideSearch();\r\n this.openSubMenu(e.delegateTarget, e);\r\n },\r\n\r\n onSubItemClick(e) {\r\n // Allow default clicking behaviour on sub-item\r\n e.stopPropagation();\r\n\r\n if (e.delegateTarget.dataset.languageName) {\r\n this.trackSelectLanguage(e);\r\n }\r\n },\r\n\r\n onSearchClick() {\r\n this.trackOpenSearch();\r\n this.closeSubMenu();\r\n this.overlayEl.classList.add(isActiveClass);\r\n this.el.classList.add(headerSearchOpenClass);\r\n this.searchInput.value = '';\r\n this.searchInput.focus();\r\n },\r\n\r\n openSubMenu(param, e) {\r\n this.trackOpenDropdown(e);\r\n if (param.classList.contains(isOpenClass)) {\r\n param.classList.remove(isOpenClass);\r\n this.overlayEl.classList.remove(isActiveClass);\r\n return;\r\n }\r\n\r\n this.closeSubMenu();\r\n param.classList.add(isOpenClass);\r\n this.overlayEl.classList.add(isActiveClass);\r\n },\r\n\r\n closeSubMenu() {\r\n [].forEach.call(document.querySelectorAll('.dropdown.is-open'), (elem) => {\r\n elem.classList.remove(isOpenClass);\r\n });\r\n if (this.languageBtn.classList.contains(languageOpenClass)) {\r\n this.languageBtn.classList.remove(languageOpenClass);\r\n }\r\n },\r\n\r\n hideSearch() {\r\n if (!this.isMobile()) {\r\n if (this.el.classList.contains(headerSearchOpenClass)) {\r\n this.el.classList.remove(headerSearchOpenClass);\r\n }\r\n if (this.el.classList.contains(suggestionsVisibleClass)) {\r\n this.el.classList.remove(suggestionsVisibleClass);\r\n }\r\n }\r\n },\r\n\r\n searchKeyUp(e) {\r\n if (this.el.classList.contains(suggestionsVisibleClass)) {\r\n this.el.classList.remove(suggestionsVisibleClass);\r\n }\r\n // Close when pressing ESC\r\n if (e.keyCode === KeyCodes.Esc) {\r\n if (this.el.classList.contains(headerSearchOpenClass)) {\r\n this.hideSearch();\r\n }\r\n } else {\r\n this.el.classList.add('search-input-dirty');\r\n\r\n if (e.target.value === '') {\r\n this.el.classList.remove('search-input-dirty');\r\n }\r\n clearTimeout(this.typingTimeout);\r\n this.typingTimeout = setTimeout(() => {\r\n this.getSuggestions(e.target.value);\r\n }, 50);\r\n }\r\n },\r\n\r\n getSuggestions(value) {\r\n if (this.el.classList.contains(suggestionsVisibleClass)) {\r\n this.el.classList.remove(suggestionsVisibleClass);\r\n }\r\n const promise = this.search.suggest(value);\r\n\r\n promise.then((response) => {\r\n const resultList = this.el.querySelector('.O201-header__search__suggestions__list');\r\n\r\n const template = resultList.children[0].cloneNode(true);\r\n resultList.innerHTML = '';\r\n resultList.appendChild(template);\r\n\r\n if (response && response.items && response.items.length > 0) {\r\n const items = response.items.slice(0, Math.min(6, response.items.length));\r\n\r\n items.forEach((item) => {\r\n const li = document.createElement('li');\r\n resultList.appendChild(li);\r\n\r\n li.appendChild(template.children[0].cloneNode(true));\r\n li.appendChild(template.children[1].cloneNode(true));\r\n li.childNodes[1].innerHTML = item.label;\r\n li.childNodes[1].setAttribute('href', `${this.searchUrl}?q=${encodeURI(item.label)}`);\r\n });\r\n\r\n this.el.classList.add(suggestionsVisibleClass);\r\n }\r\n });\r\n },\r\n\r\n isMobile() {\r\n const currentWindowWidth = window.innerWidth;\r\n if (currentWindowWidth <= this.isDesktopSelectorName) {\r\n return true;\r\n }\r\n\r\n return false;\r\n },\r\n\r\n trackOpenLanguageDropdown() {\r\n App.tracking.trackParafonEvent('main navigation', 'open mobile menu', '(O201) Header');\r\n },\r\n\r\n trackOpenMobileMenu() {\r\n App.tracking.trackParafonEvent('main navigation', 'open locale selector', '(O201) Header');\r\n },\r\n\r\n trackSelectLanguage(e) {\r\n App.tracking.trackParafonEvent(\r\n 'main navigation',\r\n 'select locale',\r\n '(O201) Header',\r\n e.delegateTarget.dataset.languageName\r\n );\r\n },\r\n\r\n trackSubmitSearchForm(e) {\r\n App.tracking.trackParafonEvent('main navigation', 'search by query', '(O201) Header', e.target['q'].value);\r\n },\r\n\r\n trackOpenDropdown(e) {\r\n let target = e.target || e.delegateTarget;\r\n App.tracking.trackParafonEvent('main navigation', 'open dropdown', '(O201) Header', target.innerText);\r\n },\r\n\r\n trackOpenSearch() {\r\n App.tracking.trackParafonEvent('main navigation', 'open search', '(O201) Header');\r\n },\r\n\r\n trackSelectSuggest(e) {\r\n App.tracking.trackParafonEvent('main navigation', 'search by suggestion', '(O201) Header', e.target.innerHTML);\r\n },\r\n});\r\n\r\nexport default O201headerview;\r\n","/**\r\n * O202 Parafon Footer View\r\n *\r\n */\r\n\r\nimport Accordion from '../../../framework/accordion';\r\nimport { ServerView } from '../../../framework/server-side-views';\r\n\r\nconst O202ParafonFooterView = ServerView.extend({\r\n el: '.O202-parafon-footer',\r\n\r\n events: {\r\n 'click .O202-parafon-footer__back-to-top-button': 'backToTop',\r\n },\r\n\r\n onRender() {\r\n this.accordion = new Accordion({\r\n parent: this.el,\r\n mdSelector: '.js-accordion-md',\r\n });\r\n },\r\n\r\n backToTop() {\r\n window.scrollTo({ top: 0, behavior: 'smooth' });\r\n this.trackBackToTop();\r\n },\r\n\r\n trackBackToTop() {\r\n App.tracking.trackParafonEvent('mobile', 'go back to top', '(O202) Footer');\r\n },\r\n});\r\n\r\nexport default O202ParafonFooterView;\r\n","/**\r\n * O204-benefits-accordion-block\r\n *\r\n */\r\n\r\nimport Accordion from '../../../framework/accordion';\r\nimport { ServerView } from '../../../framework/server-side-views';\r\n\r\nconst O204BenefitsAccordionBlockView = ServerView.extend({\r\n el: '.O204-benefits-accordion-block',\r\n\r\n onRender() {\r\n this.accordion = new Accordion({\r\n parent: '.O204-benefits-accordion-block__faq',\r\n selector: '.O204-benefits-accordion-block__item__title',\r\n target: '.O204-benefits-accordion-block__item__text',\r\n slideDuration: 400,\r\n slideEasing: 'easeInOutQuad',\r\n closeOthers: true,\r\n });\r\n },\r\n});\r\n\r\nexport default O204BenefitsAccordionBlockView;\r\n","export class SwipeEventDispatcher {\r\n constructor(element, options = {}) {\r\n this.evtMap = {\r\n SWIPE_LEFT: [],\r\n SWIPE_UP: [],\r\n SWIPE_DOWN: [],\r\n SWIPE_RIGHT: [],\r\n };\r\n\r\n this.xDown = null;\r\n this.yDown = null;\r\n this.element = element;\r\n this.options = Object.assign({ triggerPercent: 0.25 }, options);\r\n\r\n element.addEventListener('touchstart', (evt) => this.handleTouchStart(evt), { passive: true });\r\n element.addEventListener('touchend', (evt) => this.handleTouchEnd(evt), { passive: true });\r\n }\r\n\r\n on(evt, cb) {\r\n this.evtMap[evt].push(cb);\r\n }\r\n\r\n off(evt, lcb) {\r\n this.evtMap[evt] = this.evtMap[evt].filter((cb) => cb !== lcb);\r\n }\r\n\r\n trigger(evt, data) {\r\n this.evtMap[evt].map((handler) => handler(data));\r\n }\r\n\r\n handleTouchStart(evt) {\r\n this.xDown = evt.touches[0].clientX;\r\n this.yDown = evt.touches[0].clientY;\r\n }\r\n\r\n handleTouchEnd(evt) {\r\n const deltaX = evt.changedTouches[0].clientX - this.xDown;\r\n const deltaY = evt.changedTouches[0].clientY - this.yDown;\r\n const distMoved = Math.abs(Math.abs(deltaX) > Math.abs(deltaY) ? deltaX : deltaY);\r\n const activePct = distMoved / this.element.offsetWidth;\r\n\r\n if (activePct > this.options.triggerPercent) {\r\n if (Math.abs(deltaX) > Math.abs(deltaY)) {\r\n deltaX < 0 ? this.trigger('SWIPE_LEFT') : this.trigger('SWIPE_RIGHT');\r\n } else {\r\n deltaY > 0 ? this.trigger('SWIPE_UP') : this.trigger('SWIPE_DOWN');\r\n }\r\n }\r\n }\r\n}\r\n","import { ServerView } from '../../../framework/server-side-views';\r\n\r\nconst ParafonBtnsNavigation = ServerView.extend({\r\n el: '.parafon-btns-navigation',\r\n events: {\r\n 'click .js-btn': 'onClickBtn',\r\n },\r\n onRender() {\r\n window.addEventListener('scroll', this.onWindowScroll.bind(this), { passive: true });\r\n\r\n this.initOffsetTop = this.el.offsetTop;\r\n this.navigation = this.el.querySelectorAll('.parafon-btns-navigation__content a');\r\n },\r\n onClickBtn(e) {\r\n e.preventDefault();\r\n const element = e.delegateTarget;\r\n\r\n [].forEach.call(this.el.querySelectorAll('a.is-active'), (el) => {\r\n el.classList.remove('is-active');\r\n });\r\n element.classList.add('is-active');\r\n\r\n const elementToMove = document.getElementById(element.hash.replace('#', ''));\r\n\r\n elementToMove.scrollIntoView();\r\n },\r\n\r\n onWindowScroll() {\r\n if (window.pageYOffset >= this.initOffsetTop) {\r\n this.el.classList.add('is-sticky');\r\n } else {\r\n this.el.classList.remove('is-sticky');\r\n }\r\n\r\n const scrollPos = window.scrollY || window.pageYOffset;\r\n [].forEach.call(this.navigation, (select) => {\r\n const refElement = document.querySelector(select.getAttribute('href'));\r\n if (refElement && refElement.offsetTop - 100 <= scrollPos) {\r\n [].forEach.call(this.el.querySelectorAll('a.is-active'), (el) => {\r\n el.classList.remove('is-active');\r\n });\r\n select.classList.add('is-active');\r\n } else {\r\n select.classList.remove('is-active');\r\n }\r\n });\r\n },\r\n});\r\n\r\nexport default ParafonBtnsNavigation;\r\n","import type { FuncCreateElements } from './types';\r\n\r\nexport const elementsFactory = (name: string, render: FuncCreateElements, destroy?: () => void, isClass = true) => {\r\n const elements = document.querySelectorAll(isClass ? `.${name}` : name);\r\n\r\n const create = () => {\r\n [].forEach.call(elements, (el: HTMLElement) => {\r\n if (el.dataset.initilaizedElements == null || !el.dataset.initilaizedElements.includes(name)) {\r\n render(el);\r\n el.dataset.initilaizedElements = `${el.dataset.initilaizedElements ?? ''} ${name}`;\r\n }\r\n });\r\n };\r\n\r\n const destroyElements = () => {\r\n if (destroy) {\r\n [].forEach.call(elements, (el: { dataset: { initilaizedElements: string } }) => {\r\n destroy();\r\n if (el.dataset.initilaizedElements) {\r\n el.dataset.initilaizedElements = el.dataset.initilaizedElements.replace(name, '');\r\n }\r\n });\r\n }\r\n };\r\n\r\n return { create, destroy: destroyElements };\r\n};\r\n","/* eslint-disable max-classes-per-file */\r\n/**\r\n * Accordion\r\n *\r\n * @author Daniel Kvistgaard \r\n */\r\n\r\nimport _ from 'underscore';\r\nimport EventEmitter from 'events';\r\nimport { PubSubKeys, PubSub } from '@utils/pubSub';\r\nimport * as customFunctions from './custom-functions';\r\n\r\n// Setting up default options parameters\r\nconst defaults = {\r\n selector: '.js-accordion',\r\n forceOpen: 'js-accordion-force-open',\r\n target: '.js-accordion__target',\r\n closeSelector: false,\r\n openClass: 'has-accordion-open',\r\n\r\n targetIsNext: false,\r\n closeOthers: true,\r\n scrollIntoView: false,\r\n\r\n slideDuration: 300,\r\n slideEasing: 'easeOutQuad',\r\n rowDisplay: 'block',\r\n};\r\n\r\n/**\r\n * Instance\r\n * A single instance of an accordion container.\r\n */\r\nclass Instance extends EventEmitter {\r\n constructor(options) {\r\n super();\r\n\r\n this.eventsNamespace = [];\r\n this.options = options;\r\n this.group = this.options.group;\r\n this.el = this.options.el;\r\n this.isDesktopSelectorName = this.options.isDesktopSelectorName || App.mq.desktop;\r\n this.mdTargetSelector = this.options.mdTargetSelector || `${this.options.mdSelector}--target`;\r\n this.xsTargetSelector = this.options.xsTargetSelector || `${this.options.xsSelector}--target`;\r\n\r\n // Used to check if tablet/mobile events are bound\r\n this.mdEventsBound = false;\r\n this.xsEventsBound = false;\r\n\r\n // Checking if the media query matches the desktop size breakpoint\r\n this.isDesktop = Modernizr.mq(`(min-width: ${this.isDesktopSelectorName}px)`);\r\n this.isMobile = Modernizr.mq(`(max-width: ${App.mq.tablet - 1}px)`);\r\n\r\n // Trigger for closing all accordions in this instance\r\n this.on('close', () => {\r\n // Selecting all normal accordion selectors to close them\r\n const allElems = this.el.querySelectorAll(this.options.selector);\r\n const otherOpen = this.el.querySelectorAll(`${this.options.selector}.${this.options.openClass}`);\r\n\r\n // Looping through the accordions, getting their target element\r\n // and triggering the close function\r\n [].forEach.call(allElems, (elem) => {\r\n const target = this.getElTarget(elem);\r\n this.close(elem, target, !!otherOpen.length);\r\n });\r\n\r\n // If we aren't on a desktop device (mobile or tablet), then also the accordions\r\n // for tablet and mobile devices needs to be closed.\r\n if (!this.isDesktop && this.options.mdSelector) {\r\n const allMdElems = this.el.querySelectorAll(this.options.mdSelector);\r\n\r\n // Looping through the accordions, getting their target element\r\n // and triggering the close function\r\n [].forEach.call(allMdElems, (elem) => {\r\n const target = this.getElTarget(elem, this.mdTargetSelector);\r\n this.close(elem, target, !!otherOpen.length);\r\n });\r\n }\r\n });\r\n\r\n // Binding user action events\r\n customFunctions.delegateSelectorInElement(\r\n [this.el],\r\n 'click',\r\n this.options.selector,\r\n this.onClick.bind(this, this.options.target)\r\n );\r\n customFunctions.delegateSelectorInElement(\r\n [this.el],\r\n 'click',\r\n this.options.mdSelector,\r\n this.onClick.bind(this, this.mdTargetSelector)\r\n );\r\n customFunctions.delegateSelectorInElement(\r\n [this.el],\r\n 'click',\r\n this.options.xsSelector,\r\n this.onClick.bind(this, this.xsTargetSelector)\r\n );\r\n\r\n if (this.options.closeSelector && typeof this.options.closeSelector === 'string') {\r\n customFunctions.delegateSelectorInElement(\r\n [this.el],\r\n 'click',\r\n this.options.closeSelector,\r\n this.onClickClose.bind(this, this.options.target)\r\n );\r\n customFunctions.delegateSelectorInElement(\r\n [this.el],\r\n 'click',\r\n this.options.mdSelector,\r\n this.onClickClose.bind(this, this.mdTargetSelector)\r\n );\r\n customFunctions.delegateSelectorInElement(\r\n [this.el],\r\n 'click',\r\n this.options.xsSelector,\r\n this.onClickClose.bind(this, this.xsTargetSelector)\r\n );\r\n }\r\n }\r\n\r\n // Checking if the clicked target was a link\r\n // that should follow user somewhere instead\r\n // of opening accordion.\r\n isTargetLink(target) {\r\n if (target?.tagName?.toString() === 'A') {\r\n // a link was clicked.\r\n if (!customFunctions.is(target, this.options.targetSelector)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * onClick\r\n *\r\n * @param {String} targetSelector\r\n * @param {Object} e\r\n */\r\n onClick(targetSelector, e) {\r\n if (this.isTargetLink(e.target)) {\r\n if (!e.target.classList.contains(defaults.forceOpen)) {\r\n return;\r\n }\r\n }\r\n\r\n if (e && e.cancelable) {\r\n e.preventDefault();\r\n e.stopPropagation();\r\n }\r\n\r\n const { userAgent } = window.navigator;\r\n this.isIE = userAgent.indexOf('MSIE ') >= 0 || userAgent.indexOf('Trident/') >= 0;\r\n\r\n const el = this.setElement(e);\r\n\r\n // Fix to stop some of the rows jumping in IE\r\n const tableRow = el.parentNode.parentNode;\r\n const tableRowChildren = tableRow.querySelectorAll('th');\r\n\r\n if (this.isIE && tableRow) {\r\n _.each(tableRowChildren, (element) => {\r\n const initialWidth = window.getComputedStyle(element, null).getPropertyValue('width');\r\n element.style.width = initialWidth;\r\n });\r\n }\r\n\r\n // Stop if we don't have a target to open\r\n if (!this.options.target && !targetSelector) {\r\n return;\r\n }\r\n\r\n if (!this._canOpenBasedOnVersion(targetSelector)) {\r\n return;\r\n }\r\n\r\n // Getting the target\r\n const target = this.getElTarget(el, targetSelector);\r\n this.clickOpen(el, target);\r\n }\r\n\r\n setElement(e) {\r\n let el = e.target;\r\n let className = this.options.selector;\r\n\r\n if (className[0]?.toString() === '.') {\r\n className = className.substring(1);\r\n }\r\n if (![].some.call(e.target.classList, (cl) => cl.includes(className))) {\r\n const [firstEl] = e.target.parents(`[class*='${className}']`);\r\n el = firstEl;\r\n }\r\n return el;\r\n }\r\n\r\n _canOpenBasedOnVersion(targetSelector) {\r\n this.isDesktop = Modernizr.mq(`(min-width: ${this.isDesktopSelectorName}px)`);\r\n this.isMobile = Modernizr.mq(`(max-width: ${App.mq.tablet - 1}px)`);\r\n\r\n if (\r\n this.isDesktop &&\r\n (targetSelector === this.options.mdTargetSelector || targetSelector === this.options.xsTargetSelector)\r\n ) {\r\n return false;\r\n }\r\n if (!this.isMobile && targetSelector === this.options.xsTargetSelector) {\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * onClickClose\r\n */\r\n onClickClose(targetSelector, e) {\r\n if (this.isTargetLink(e.target)) {\r\n // if we got there - the link was clicked and it is not\r\n // the trigger to open/close the accordion, so we\r\n // should fallback to the default behaviour, not\r\n // proceed with our code.\r\n return;\r\n }\r\n\r\n if (e && e.cancelable) {\r\n e.preventDefault();\r\n }\r\n\r\n // Stop if we don't have a target to close\r\n if (!this.options.target && !targetSelector) {\r\n return;\r\n }\r\n\r\n // Getting the target\r\n const el = e.currentTarget;\r\n const target = Instance.closest(el, targetSelector);\r\n const btnEl = this.getElTarget(target, this.options.selector);\r\n\r\n this.close(btnEl, target, false);\r\n }\r\n\r\n /**\r\n * Opening an element and checking if others are open.\r\n *\r\n * @param {HTMLElement} el\r\n * @param {HTMLElement} target\r\n */\r\n clickOpen(el, target) {\r\n const opening = !el.classList.contains(this.options.openClass);\r\n\r\n // Figuring out if other accordion items are open\r\n const otherOpen = this.el.querySelectorAll(`${this.options.selector}.${this.options.openClass}`);\r\n\r\n // Close other open accordions (unless closeOthers are set to false)\r\n if (opening && this.options.closeOthers && otherOpen.length > 0) {\r\n this.emit('close');\r\n }\r\n\r\n // Toggling the accordion\r\n if (opening) {\r\n this.open(el, target, !!otherOpen.length);\r\n } else {\r\n this.close(el, target, false);\r\n }\r\n }\r\n\r\n /**\r\n * getElTarget\r\n * Getting the target element of a specified element in its context.\r\n *\r\n * @param {HTMLElement} el - the DOM element of which we want to find its target\r\n * @param {String} targetSelector\r\n */\r\n getElTarget(el, targetSelector) {\r\n // Getting the target\r\n const containsTarget = (targetSelector || this.options.target).replace('.', '');\r\n const elem = this.options.selectorParent ? Instance.closest(el, this.options.selectorParent) : el;\r\n let target = null;\r\n\r\n if (this.options.targetIsNext) {\r\n const nextEl = elem.nextElementSibling;\r\n const nextIsElem = nextEl && nextEl instanceof HTMLElement;\r\n target = nextIsElem && nextEl.classList.contains(containsTarget) ? nextEl : false;\r\n\r\n if (!target && nextIsElem && nextEl.querySelector(targetSelector || this.options.target)) {\r\n target = nextEl.querySelector(targetSelector || this.options.target);\r\n }\r\n\r\n if (target) {\r\n return target;\r\n }\r\n }\r\n\r\n target = elem.parentNode.querySelector(targetSelector || this.options.target);\r\n\r\n // If we can't find the target within the parentNode, then we'll expand the search a bit\r\n if (!target) {\r\n target = elem.parentNode.parentNode.querySelector(targetSelector || this.options.target);\r\n }\r\n\r\n return target;\r\n }\r\n\r\n /**\r\n * Opening an accordion element.\r\n *\r\n * @param {HTMLElement} el\r\n * @param {HTMLElement} target\r\n */\r\n open(el, target, otherOpen) {\r\n const targetEl = !target || typeof target === 'string' ? this.getElTarget(el, target) : target;\r\n const item = this.options.item ? Instance.closest(el, this.options.item) : null;\r\n const selectorParent =\r\n targetEl && this.options.selectorParent ? Instance.closest(targetEl, this.options.selectorParent) : null;\r\n\r\n el.classList.add(this.options.openClass);\r\n\r\n if (this.options.parentOpenClass && !otherOpen) {\r\n this.el.classList.add(this.options.openClass);\r\n }\r\n\r\n if (item) {\r\n item.classList.add(this.options.openClass);\r\n }\r\n\r\n if (targetEl) {\r\n targetEl.classList.add(this.options.openClass);\r\n }\r\n\r\n if (selectorParent) {\r\n selectorParent.classList.add(`${this.options.openClass}--parent`);\r\n }\r\n\r\n if (this.options.indicatorSelector) {\r\n [].forEach.call(el.querySelectorAll(this.options.indicatorSelector), (e) => {\r\n e.classList.add(this.options.openClass);\r\n });\r\n }\r\n\r\n // Calling beforeOpen if it's set.\r\n // beforeOpen makes it possible to do calculations or similar before the accordion item opens\r\n if (this.options.beforeOpen && _.isFunction(this.options.beforeOpen)) {\r\n this.options.beforeOpen(otherOpen, targetEl, item, () => {\r\n if (targetEl) {\r\n this.slide(targetEl, 'down');\r\n }\r\n });\r\n } else if (targetEl) {\r\n this.slide(targetEl, 'down');\r\n }\r\n }\r\n\r\n /**\r\n * Closing an accordion element.\r\n *\r\n * @param {HTMLElement} el\r\n * @param {HTMLElement} target\r\n */\r\n close(el, target, otherOpen) {\r\n const targetEl = !target || typeof target === 'string' ? this.getElTarget(el, target) : target;\r\n const item = this.options.item ? Instance.closest(el, this.options.item) : null;\r\n\r\n // Prevent closing elements that aren't open\r\n if (!el.classList.contains(this.options.openClass)) {\r\n return;\r\n }\r\n\r\n el.classList.remove(this.options.openClass);\r\n\r\n if (this.options.parentOpenClass && !otherOpen) {\r\n this.el.classList.remove(this.options.openClass);\r\n }\r\n\r\n if (item) {\r\n item.classList.remove(this.options.openClass);\r\n }\r\n\r\n if (targetEl) {\r\n targetEl.classList.remove(this.options.openClass);\r\n }\r\n\r\n if (this.options.indicatorSelector) {\r\n [].forEach.call(el.querySelectorAll(this.options.indicatorSelector), (e) => {\r\n e.classList.remove(this.options.openClass);\r\n });\r\n }\r\n\r\n // Calling beforeClose if it's set.\r\n // beforeClose makes it possible to do calculations or similar before the accordion item closes\r\n if (this.options.beforeClose && _.isFunction(this.options.beforeClose)) {\r\n this.options.beforeClose(otherOpen, targetEl, item, () => {\r\n if (targetEl) {\r\n this.slide(targetEl, 'up');\r\n }\r\n });\r\n } else if (targetEl) {\r\n this.slide(targetEl, 'up');\r\n }\r\n }\r\n\r\n /**\r\n * Clearing all tablet/mobile setup (classes and stylings).\r\n * This is used when we go from tablet/mobile to desktop.\r\n */\r\n clearMdSetup() {\r\n const mdSelectors = this.el.querySelectorAll(this.options.mdSelector);\r\n const mdTargets = this.el.querySelectorAll(this.mdTargetSelector);\r\n\r\n [].forEach.call(mdSelectors, (mdSelector) => {\r\n mdSelector.classList.remove(this.options.openClass);\r\n });\r\n\r\n [].forEach.call(mdTargets, (mdTarget) => {\r\n mdTarget.style.display = '';\r\n mdTarget.classList.add(this.options.openClass);\r\n });\r\n }\r\n\r\n /**\r\n * Clearing all mobile setup (classes and stylings).\r\n * This is used when we go from mobile to tablet/desktop.\r\n */\r\n clearXsSetup() {\r\n const xsSelectors = this.el.querySelectorAll(this.options.xsSelector);\r\n const xsTargets = this.el.querySelectorAll(this.xsTargetSelector);\r\n\r\n [].forEach.call(xsSelectors, (xsSelector) => {\r\n xsSelector.classList.remove(this.options.openClass);\r\n });\r\n\r\n [].forEach.call(xsTargets, (xsTarget) => {\r\n xsTarget.style.display = '';\r\n xsTarget.classList.add(this.options.openClass);\r\n });\r\n }\r\n\r\n /**\r\n * Slide up or down functionality\r\n * Velocity doesn't handle stress sliding very well, so we had to remake\r\n * the functionality.\r\n *\r\n * @param {HTMLElement} target\r\n * @param {String} direction - values: 'up' or 'down'\r\n */\r\n async slide(target, direction) {\r\n const self = this;\r\n const selectorParent = this.options.selectorParent ? Instance.closest(target, this.options.selectorParent) : null;\r\n const targetStyles = window.getComputedStyle(target);\r\n let height = 0;\r\n let paddingTop = 0;\r\n let paddingBottom = 0;\r\n\r\n // Stop any previous velocity animations\r\n const Velocity = (await import('velocity-animate')).default;\r\n Velocity.animate(target, 'stop');\r\n\r\n if (direction === 'down') {\r\n const prevHeight = target.offsetHeight;\r\n const prevPaddingTop = prevHeight === 0 ? 0 : parseInt(targetStyles.getPropertyValue('padding-top') || 0, 10);\r\n const prevPaddingBottom =\r\n prevHeight === 0 ? 0 : parseInt(targetStyles.getPropertyValue('padding-bottom') || 0, 10);\r\n\r\n target.style.display = this.options.rowDisplay;\r\n target.style.height = '';\r\n target.style.paddingTop = '';\r\n target.style.paddingBottom = '';\r\n\r\n height = target.offsetHeight;\r\n paddingTop = parseInt(targetStyles.getPropertyValue('padding-top'), 10);\r\n paddingBottom = parseInt(targetStyles.getPropertyValue('padding-bottom'), 10);\r\n\r\n target.style.height = `${prevHeight}px`;\r\n target.style.paddingTop = `${prevPaddingTop}px`;\r\n target.style.paddingBottom = `${prevPaddingBottom}px`;\r\n }\r\n\r\n target.style.overflow = 'hidden';\r\n\r\n Velocity.animate(\r\n target,\r\n {\r\n height,\r\n paddingTop,\r\n paddingBottom,\r\n },\r\n {\r\n duration: this.options.slideDuration,\r\n easing: this.options.slideEasing,\r\n\r\n complete() {\r\n if (direction === 'up') {\r\n target.style.display = '';\r\n\r\n if (selectorParent) {\r\n selectorParent.classList.remove(`${self.options.openClass}--parent`);\r\n }\r\n }\r\n\r\n target.style.overflow = '';\r\n target.style.height = '';\r\n target.style.paddingTop = '';\r\n target.style.paddingBottom = '';\r\n\r\n if (\r\n direction === 'down' &&\r\n (self.options.scrollIntoView === true ||\r\n (App.mq[self.options.scrollIntoView] &&\r\n Modernizr.mq(`(max-width: ${App.mq[self.options.scrollIntoView] - 1}px)`)))\r\n ) {\r\n self.scrollIntoView(target);\r\n }\r\n },\r\n }\r\n );\r\n }\r\n\r\n /**\r\n * Scrolling the target into view.\r\n *\r\n * @param {HTMLElement} target\r\n */\r\n async scrollIntoView(target) {\r\n const targetEl = this.options.item ? Instance.closest(target, this.options.item) : target;\r\n const headerHeight = PubSub.request(PubSubKeys.headerGetHeight);\r\n const scrollTop = window.scrollY || window.pageYOffset;\r\n const top = targetEl.getBoundingClientRect().top + scrollTop;\r\n const bottom = top + targetEl.offsetHeight;\r\n const windowTop = scrollTop + headerHeight;\r\n const windowBottom = scrollTop + window.innerHeight;\r\n if (top < windowTop || bottom > windowBottom) {\r\n const Velocity = (await import('velocity-animate')).default;\r\n Velocity.animate(targetEl, 'scroll', {\r\n offset: (headerHeight + 50) * -1,\r\n duration: this.options.slideDuration,\r\n easing: 'easeOutQuad',\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Finding the closest element in the parent tree.\r\n *\r\n * @param {HTMLElement} el\r\n * @param {String} target\r\n */\r\n static closest(el, target) {\r\n let ancestor = el;\r\n\r\n if (!document.documentElement.contains(el)) {\r\n return null;\r\n }\r\n\r\n do {\r\n if (ancestor.matches ? ancestor.matches(target) : ancestor.msMatchesSelector(target)) {\r\n return ancestor;\r\n }\r\n\r\n ancestor = ancestor.parentElement;\r\n } while (ancestor !== null);\r\n\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Setting up all accordion instances.\r\n * If the parent options is set as a string selector, then we'll loop\r\n * through all elements matching the selector and initialize a new instance\r\n * for that element. All items within that parent matching the selector\r\n * parameter will then get the accordion functionality attached.\r\n */\r\nclass Accordion {\r\n constructor(options) {\r\n this.options = _.extend({}, defaults, options);\r\n this.instances = [];\r\n\r\n if (this.options.parent instanceof NodeList) {\r\n [].forEach.call(this.options.parent, (parent) => {\r\n this.createInstance(parent);\r\n });\r\n } else if (this.options.parent && (_.isString(this.options.parent) || _.isArray(this.options.parent))) {\r\n const parents = _.isString(this.options.parent)\r\n ? document.querySelectorAll(this.options.parent)\r\n : this.options.parent;\r\n\r\n [].forEach.call(parents, (parent) => {\r\n this.createInstance(parent);\r\n });\r\n } else if (this.options.parent) {\r\n this.createInstance(this.options.parent);\r\n }\r\n return this;\r\n }\r\n\r\n createInstance(parent) {\r\n const initializedClass = 'accordion--initialized';\r\n if (parent.classList.contains(initializedClass)) {\r\n return;\r\n }\r\n\r\n this.instances.push(\r\n new Instance(\r\n _.extend(\r\n {\r\n el: parent,\r\n group: this,\r\n },\r\n this.options\r\n )\r\n )\r\n );\r\n parent.classList.add(initializedClass);\r\n }\r\n\r\n open(el, target) {\r\n [].forEach.call(this.instances, (instance) => {\r\n instance.clickOpen(el, target);\r\n });\r\n }\r\n\r\n /**\r\n * Closing all accordions of all instances.\r\n */\r\n closeAll() {\r\n [].forEach.call(this.instances, (instance) => {\r\n instance.emit('close');\r\n });\r\n }\r\n}\r\n\r\nexport default Accordion;\r\n","/**\r\n * Search\r\n *\r\n * @author Daniel Kvistgaard \r\n */\r\nconst defaults = window.searchApi || {\r\n culture: 'en-GB',\r\n languageCode: 'en',\r\n apiUrl: '/',\r\n site: 'ROCKWOOL',\r\n};\r\n\r\nclass Search {\r\n constructor(el, options) {\r\n this.options = Object.assign({}, defaults, options);\r\n this.apiUrl = this.options.apiUrl;\r\n this.data = Object.assign({}, this.options);\r\n delete this.data.apiUrl;\r\n }\r\n\r\n /**\r\n * Getting search suggestings based an a query string.\r\n *\r\n * @param {String} query - the search query\r\n * @return {Promise}\r\n */\r\n suggest(query) {\r\n return this._fetch(\r\n 'api/suggest',\r\n Object.assign(\r\n {},\r\n {\r\n query,\r\n },\r\n this.data\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * Getting search results based an a query string.\r\n *\r\n * @param {String} query - the search query\r\n * @param {Number} page - the current pagenumber\r\n * @return {Promise}\r\n */\r\n loadMore(query, page) {\r\n return this._fetch(\r\n 'api/search',\r\n Object.assign(\r\n {},\r\n {\r\n query,\r\n page,\r\n count: 10,\r\n },\r\n this.data\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * The base fetch functionality.\r\n *\r\n * @param {String} url - the endpoint url\r\n * @param {Object} body - the post body data\r\n * @return {Promise}\r\n */\r\n _fetch(url, body) {\r\n return fetch(`${this.apiUrl}${url}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(body),\r\n })\r\n .then((response) => {\r\n if (!response.ok) {\r\n throw new Error(`${response.status} - ${response.statusText} (url: ${response.url})`);\r\n }\r\n\r\n return response.json();\r\n })\r\n .catch((ex) => {\r\n console.warn('somethings wrong', ex);\r\n });\r\n }\r\n}\r\n\r\nexport default Search;\r\n","/**\r\n * Storage\r\n *\r\n * @author Daniel Kvistgaard \r\n */\r\n\r\nimport EventEmitter from 'events';\r\n\r\nconst defaults = {\r\n expiration: 2, // in days\r\n idKey: 'id',\r\n upCountOnAdd: false,\r\n};\r\n\r\nclass Storage extends EventEmitter {\r\n constructor(name, options) {\r\n super();\r\n\r\n this.name = name;\r\n this.options = Object.assign({}, defaults, options);\r\n this.data = window.localStorage.getItem(name) ? JSON.parse(window.localStorage.getItem(name)) : {};\r\n\r\n this.events();\r\n this.checkData();\r\n this.checkDate();\r\n }\r\n\r\n events() {\r\n window.addEventListener('storage', this.change.bind(this), false);\r\n }\r\n\r\n change(e) {\r\n if (e.key !== this.name) {\r\n return;\r\n }\r\n\r\n if (e.newValue !== undefined) {\r\n this.data = JSON.parse(e.newValue);\r\n }\r\n\r\n this.emit('change', this.data);\r\n }\r\n\r\n checkData() {\r\n let changed = false;\r\n\r\n if (this.data instanceof Array) {\r\n this.data = {\r\n date: new Date(),\r\n items: this.data,\r\n };\r\n changed = true;\r\n }\r\n\r\n if (!this.data.date) {\r\n this.data.date = new Date();\r\n changed = true;\r\n }\r\n\r\n if (!this.data.items || !(this.data.items instanceof Array)) {\r\n this.data.items = [];\r\n changed = true;\r\n }\r\n\r\n if (changed && this.data.items.length > 0) {\r\n this.saveData();\r\n }\r\n }\r\n\r\n checkDate() {\r\n const date = new Date(this.data.date);\r\n\r\n if (new Date() - date > 1000 * 60 * 60 * 24 * this.options.expiration) {\r\n this.data = {\r\n date: new Date(),\r\n };\r\n\r\n this.data.items = [];\r\n this.saveData();\r\n }\r\n }\r\n\r\n itemExists(item, isId = false) {\r\n return this.data.items.find((singleItem) => {\r\n return `${singleItem[this.options.idKey]}` === `${isId ? item : item[this.options.idKey]}`;\r\n });\r\n }\r\n\r\n itemIndex(item, isId = false) {\r\n return this.data.items.findIndex((singleItem) => {\r\n return `${singleItem[this.options.idKey]}` === `${isId ? item : item[this.options.idKey]}`;\r\n });\r\n }\r\n\r\n addItem(item, addingMultiple = false) {\r\n const existingItem = this.itemExists(item);\r\n\r\n if (existingItem && !this.options.upCountOnAdd) {\r\n return;\r\n }\r\n\r\n if (existingItem && this.options.upCountOnAdd) {\r\n existingItem.count += item.count;\r\n } else {\r\n this.data.items.push(item);\r\n }\r\n\r\n if (!addingMultiple) {\r\n this.saveData();\r\n }\r\n }\r\n\r\n addItems(items) {\r\n items.forEach((item) => {\r\n this.addItem(item, true);\r\n });\r\n\r\n this.saveData();\r\n }\r\n\r\n removeItem(item, removingMultiple = false) {\r\n const existingItem = this.itemExists(item);\r\n\r\n if (!existingItem) {\r\n return;\r\n }\r\n\r\n this.data.items.splice(this.data.items.indexOf(existingItem), 1);\r\n\r\n if (!removingMultiple) {\r\n this.saveData();\r\n }\r\n }\r\n\r\n replaceOrAddItemById(itemId, newItem) {\r\n const existingItemIndex = this.itemIndex(itemId, true);\r\n\r\n if (existingItemIndex > -1) {\r\n this.data.items[existingItemIndex] = newItem;\r\n } else {\r\n this.addItem(newItem);\r\n }\r\n\r\n this.saveData();\r\n }\r\n\r\n removeItemById(itemId) {\r\n const existingItem = this.itemExists(itemId, true);\r\n\r\n if (!existingItem) {\r\n return;\r\n }\r\n\r\n this.data.items.splice(this.data.items.indexOf(existingItem), 1);\r\n this.saveData();\r\n }\r\n\r\n removeItems(items) {\r\n items.forEach((item) => {\r\n this.removeItem(item, true);\r\n });\r\n\r\n this.saveData();\r\n }\r\n\r\n removeAll() {\r\n this.data.items = [];\r\n this.saveData();\r\n }\r\n\r\n saveData() {\r\n this.data.date = new Date();\r\n\r\n window.localStorage.setItem(this.name, JSON.stringify(this.data));\r\n\r\n this.change({ key: this.name });\r\n }\r\n\r\n get items() {\r\n return this.data.items;\r\n }\r\n\r\n get count() {\r\n return this.data.items.length;\r\n }\r\n}\r\n\r\nconst instances = [];\r\nconst handler = (name, options) => {\r\n let instance = instances.find((single) => {\r\n return single.name === name;\r\n });\r\n\r\n if (instance) {\r\n return instance;\r\n }\r\n\r\n instance = new Storage(name, options);\r\n instances.push(instance);\r\n\r\n return instance;\r\n};\r\n\r\nexport default handler;\r\n","/*!\n\tCopyright (c) 2018 Jed Watson.\n\tLicensed under the MIT License (MIT), see\n\thttp://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames () {\n\t\tvar classes = '';\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (arg) {\n\t\t\t\tclasses = appendClass(classes, parseValue(arg));\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction parseValue (arg) {\n\t\tif (typeof arg === 'string' || typeof arg === 'number') {\n\t\t\treturn arg;\n\t\t}\n\n\t\tif (typeof arg !== 'object') {\n\t\t\treturn '';\n\t\t}\n\n\t\tif (Array.isArray(arg)) {\n\t\t\treturn classNames.apply(null, arg);\n\t\t}\n\n\t\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\t\treturn arg.toString();\n\t\t}\n\n\t\tvar classes = '';\n\n\t\tfor (var key in arg) {\n\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\tclasses = appendClass(classes, key);\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction appendClass (value, newClass) {\n\t\tif (!newClass) {\n\t\t\treturn value;\n\t\t}\n\t\n\t\tif (value) {\n\t\t\treturn value + ' ' + newClass;\n\t\t}\n\t\n\t\treturn value + newClass;\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","import objectWithoutPropertiesLoose from \"./objectWithoutPropertiesLoose.js\";\nfunction _objectWithoutProperties(e, t) {\n if (null == e) return {};\n var o,\n r,\n i = objectWithoutPropertiesLoose(e, t);\n if (Object.getOwnPropertySymbols) {\n var s = Object.getOwnPropertySymbols(e);\n for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);\n }\n return i;\n}\nexport { _objectWithoutProperties as default };","function _objectWithoutPropertiesLoose(r, e) {\n if (null == r) return {};\n var t = {};\n for (var n in r) if ({}.hasOwnProperty.call(r, n)) {\n if (e.includes(n)) continue;\n t[n] = r[n];\n }\n return t;\n}\nexport { _objectWithoutPropertiesLoose as default };"],"names":["props","React","_extends","xmlns","width","height","viewBox","d","className","CompareProductButton","_ref","id","name","image","compareText","FigureChildren","showScaleIcon","hasVideo","fullscreenUrl","videoUrl","videoTitleLinkedIn","videoTitleYouTube","youTubeUrl","ScaleImageToFullScreenButton","VideoButton","hasEmbeddedMeta","customClassName","WrappedVideoButton","videoTitle","videoText","buttonText","useButtonWithIcon","MediaFile","mobileSrcset","imageDescription","imageCssClass","imageDataSrc","isImage","isEps","contentDataSrc","contentName","isPdf","media","srcSet","alt","tidyStr","loading","src","displayName","getDefaultButton","openLinkInNewTab","openThankYouPageInTheSamePage","cssClass","rest","_objectWithoutProperties","_excluded","Button","relationship","htmlAttributes","relAttributeWithNoopenerNoReferrerVaule","undefined","target","targetValueBlank","targetValueSelf","fileDataTarget","hasContent","PrimaryButton","_ref2","_excluded2","title","trim","link","_objectSpread","linksToVideo","style","cssStyle","onClick","points","type","ScaleIcon","SecondaryButton","getButtonContent","forwardRef","ref","videoDescription","videoImage","videoUploadDate","embeddedCodeWithoutIframe","VideoIcon","suppressHydrationWarning","dangerouslySetInnerHTML","__html","length","seoData","data","description","contentUrl","thumbnailUrl","uploadDate","JSON","stringify","BaseCardLink","file","url","classNames","label","_label","relValue","useMemo","value","relAttribute","split","replaceAll","dataAttributes","source","obj","Object","keys","filter","k","startsWith","forEach","key","map","chunk","toLocaleLowerCase","join","Link","testId","nonFile","showIcon","rel","gatedContent","FileDownloadLink","isGated","overrideClassNames","clasName","gated","nonGated","dataObject","BaseCardContentWrapper","children","wrap","BaseCard","extensions","hasImage","overlay","imageMaxWidth","primaryBtn","secondaryBtn","centerAligned","singleStacked","hasLabels","hideLabels","labels","hasDescription","hasTitle","brandClass","hasBrand","brandLabel","unwrap","events","labelDateString","innerTextLabel","figureChildren","displayDate","customBtnWrapChildren","useBodyLink","filteredSplittedLabels","flatMap","splittedLabel","styles","Image","sizes","screenSizes","tablet","draggable","ProductCard","deepLinkId","hasAnchorId","bundleDetails","headline","linkText","secondaryButton","hideImages","orderSampleButton","cardExtensionCss","useMediaFileImage","useJsOrderSampleEdgeOptions","onClickCard","labelCssExtensions","productCardBtnWrapChildren","excerpt","card","entries","divider","Handlebars","require","template","templates","container","depth0","helpers","partials","helper","alias1","nullContext","alias2","hooks","helperMissing","alias3","alias4","escapeExpression","lookupProperty","parent","propertyName","prototype","hasOwnProperty","call","stack1","program","noop","requestMethod","historyStateKey","availableTemplates","internalUrl","inboundLinkTarget","imageUrl","altText","brand","metaLabel","metaTitle","metaDescription","dateString","metaCTA","imageClass","svg","ArrowIcon","iconName","Boolean","hasOverLay","isFullLink","isHiddenFromSearch","isArticle","mediaSize","mediaType","bodyText","hasHeadline","hasText","mediaUrl","fileName","linkTarget","trackingLabel","linkUrl","secondaryBtnObj","isWhiteIcon","DownloadIcon","fileTitle","fileType","fileCategory","dataTrackingAction","hrefAttribute","dataTrackingCategory","targetAttribute","dataTrackingLabel","dataTrackingFileType","readMoreLabel","topicLabels","metaAltTextWithFallback","areaOfInterest","country","viewPostText","hasMedia","productCategories","properties","dimensionTitle","colorsTitle","edgesTitle","reactionToFireTitle","viewProductTitle","pageType","hideCompare","bundleDetailsObj","dimensions","colors","edges","reactionToFire","filterProperties","viewProduct","location","goToPageText","href","AjaxList","el","isLoading","setIsLoading","useState","templatesToAppend","setTemplatesToAppend","replacementViewTemplates","setReplacementViewTemplates","isReplacingComponents","setIsReplacingComponents","itemTemplate","querySelector","getAttribute","itemContainer","useRef","itemContainersRef","fetchOptions","pagenumber","fetchBody","isJobList","classList","contains","listId","isReportList","isEventDetailRow","isBlogPosts","isPageType","isAdditionalFilters","dataSelectedType","noResults","fetchMethod","moreEndpoint","toLowerCase","apiUrl","window","searchApi","initializeReactRootWithTemplates","wrapperSpan","document","createElement","createRoot","render","useEffect","current","requestAnimationFrame","replaceChildren","handleMasonryUpdate","Array","from","child","appendChild","onClickMore","e","preventDefault","getItems","loadMoreBtn","addEventListener","removeEventListener","getListIdKey","getElementsByClassName","indexOf","getHistoryState","history","state","checkEmptyLabels","parentNode","querySelectorAll","innerText","display","onChangeSelect","currentTarget","HTMLSelectElement","handleSelectDimming","dispatchEvent","eventName","element","event","CustomEvent","detail","createEvent","initCustomEvent","checkIfMore","response","allItems","hasMore","totalCount","renderItem","model","index","templateProps","templateData","item","fillTemplateData","Component","renderItems","items","list","isArray","contents","add","innerHTML","setTimeout","remove","itemsMarkup","totalIndex","allContainersFilled","indexes","remainingItems","slice","replaceAndRenderItems","PubSub","trigger","PubSubKeys","scrollRevealNewElements","updateJobFilterCounts","filters","filterEl","filterBlockItems","count","addParameters","params","parameters","getOptionKey","selectName","charAt","collectSelectedOptionsFromMultiselectFilters","newFetchBodyState","newFetchOpptions","select","optionKey","selectNameSubItem","optionSubKey","combinedValue","combinedSubValue","options","option","selected","optionParentElement","parentElement","HTMLOptionElement","includes","createCombinedValuesObject","prepareFetchOptions","allNew","collectSelectedOptionsFromSelectFilters","_fetch","urlParams","requestOptions","method","headers","Accept","prepareRequestData","listIdSourcesContent","dataset","categories","filterData","additionalFiltersData","mainCategories","push","field","values","logicOperator","parseFloat","to","replace","requireFacetFields","returnFields","facetFields","filterCategory","parse","showOnlyDeepestLevelSubcategories","showOnlyFirstLevelSubcategories","additionalFilters","resultsPerPage","page","languageCode","culture","site","requireFacetFieldsOnDocument","body","fetch","then","ok","Error","status","statusText","json","catch","ex","console","warn","updateStylesBeforeFetch","loader","loaderOverlay","setStyleForAllNew","itemFilters","itemFiltersMultiple","itemList","createItemList","isAllNewItems","fetchResponse","newItems","itemListFull","shouldReplaceAllOnHistory","stateData","ajaxListViewState","listIdKey","updatedAjaxListViewState","createHistoryStateObj","itemFetchResponse","replaceState","objectFitImages","watchMQ","selectEl","closest","dimClass","historyState","performance","getEntriesByType","fetchOptionsObj","listid","templateLocationAssignment","itemContainersElements","containerType","parseInt","selects","filterElems","filterElem","responseFilters","generateUpdatedView","elementsFactory","ajaxList","renderAjaxListComponent","ListItemView","Marionette","View","extend","triggers","initialize","this","set","iconClose","CollectionView","childView","onChildviewClickedItem","storage","removeItemById","attributes","productId","ServerView","regions","productList","onRender","idKey","on","collection","reset","Backbone","bind","updateAll","showChildView","setupAddToCompare","delegateSelector","addToCompare","stopPropagation","isCheckbox","isChecked","currentProductName","currentProductImage","currentProductId","currentProduct","productName","productImage","addItem","warningMsg","warningMsgText","toggleShowTool","isEmpty","updateCompareCount","_toggleCollectorIsOpen","_clearProducts","compareRemoveall","removeAll","updateCheckboxes","compareCheckboxes","checkbox","product","_compareProducts","compareUrl","warningMsgBox","customTrackingData","App","tracking","trackEvent","setAttribute","anhorLinkElements","downloadMaterialsHandler","checkIfGated","fileIdToDownload","publish","EventEnum","closeReactModal","downloadMaterials","subscribe","downloadMaterialsGatedContent","currentElement","dataObjectStr","fileTypes","isSingleSignupDownload","firstFileType","fileId","formName","formId","noDownload","dataTarget","gatedDownload","format","fileMarketoFormId","fileMarketoFormName","isNotDownloadedAfterFormSent","visitorFilledForm","visitor","isFormFilled","enableSingleSignupDocumentsDownload","openGateModal","open","gateType","dataFormName","dataFormId","dataFormUrl","gatedDownloads","openGatedContentModal","modalType","formUrl","anhorLinkElement","destroy","unsubscribe","onload","hash","uniqueAnchor","scrollPosition","getBoundingClientRect","top","scrollY","scrollTo","Esc","Enter","arrowLeft","arrowUp","arrowRight","arrowDown","triggerBrandSelector","paramString","search","which","hostname","isRockwool","isRockfon","isGrodan","isLapinus","isRockpanel","_","stopImmediatePropagation","openMultiSelector","hasGlobalEventListener","tabStart","touchStartX","touchStartY","Date","getTime","changedTouch","changedTouches","clientX","clientY","elem","elementFromPoint","Math","abs","globalClickEventListener","onClickLangOpen","initializeFilters","onClickFilter","documentElement","isLanguageSelector","isBrandRockWoolSelector","isBrandRockfonSelector","isBrandRockpanelSelector","isBrandGrodanSelector","isBrandLapinusSelector","isGroupSelector","getLanguageSelectorAsync","setInitLanguageView","allFilters","activeItem","predictionEl","click","onClickLangClose","showItemsByFilter","itemFilter","selectorClass","asyncContainer","cultureInfo","callback","languageSelectorContainer","text","async","InitializeView","InitBasicSelects","isVisible","onBodyClickHandler","linksMenu","onWindowScroll","passive","onBodyClick","close","onClickLangSelectorOpen","Modernizr","mq","desktop","modal","create","view","ancestor","HTMLElement","matches","msMatchesSelector","onClickMultiSelectOpen","orderSampleData","pageYOffset","getSelectedValueInFilter","filterSelector","String","fromCharCode","thisTileId","thisTileName","thisEdgeOptions","selectedEdgeValueFilterSelector","selectedColorValueFilterSelector","thisExcludeColour","thisExcludeEdges","orderSampleHeading","checkoutData","orderSampleEdgeText","orderSampleColor","orderSampleBtnText","selectEdgePlaceholder","selectColorPlaceholder","quantity","tileName","tileId","noExcludeColour","noExcludeEdges","edgeOptions","selectedEdgeValue","selectedColorValue","imgSrc","BasketOverlay","suggestionsVisibleClass","headerSearchOpenClass","menuOpenClass","languageOpenClass","isOpenClass","isActiveClass","O201headerview","isDesktopSelectorName","desktopLarge","searchInput","overlayEl","searchBtn","topNavArea","languageSelector","languageBtn","Search","onResize","onOverlayClick","onSearchClick","searchKeyUp","searchForm","searchUrl","lastWindowWidth","innerWidth","isMainMenuOpen","isLanguageMenuOpen","SwipeEventDispatcher","removeMenuClasses","removeLanguageClasses","isResizingFromMobileToDesktop","hideSearch","currentWindowWidth","isResizingFromDesktopToMobile","clearSearch","closeSubMenu","addMenuClasses","addLanguageClasses","onClickMenu","trackOpenLanguageDropdown","delegateTarget","onLanguageClick","openSubMenu","onTopNavClick","hasSub","childElem","onSubItemClick","languageName","trackSelectLanguage","trackOpenSearch","focus","param","trackOpenDropdown","isMobile","keyCode","KeyCodes","clearTimeout","typingTimeout","getSuggestions","suggest","resultList","cloneNode","min","li","childNodes","encodeURI","trackParafonEvent","trackOpenMobileMenu","trackSubmitSearchForm","trackSelectSuggest","O202ParafonFooterView","accordion","Accordion","mdSelector","backToTop","behavior","trackBackToTop","O204BenefitsAccordionBlockView","selector","slideDuration","slideEasing","closeOthers","constructor","arguments","evtMap","SWIPE_LEFT","SWIPE_UP","SWIPE_DOWN","SWIPE_RIGHT","xDown","yDown","triggerPercent","evt","handleTouchStart","handleTouchEnd","cb","off","lcb","handler","touches","deltaX","deltaY","offsetWidth","ParafonBtnsNavigation","initOffsetTop","offsetTop","navigation","onClickBtn","getElementById","scrollIntoView","scrollPos","refElement","isClass","elements","initilaizedElements","destroyElements","defaults","forceOpen","closeSelector","openClass","targetIsNext","rowDisplay","Instance","EventEmitter","super","eventsNamespace","group","mdTargetSelector","xsTargetSelector","xsSelector","mdEventsBound","xsEventsBound","isDesktop","allElems","otherOpen","getElTarget","allMdElems","customFunctions","onClickClose","isTargetLink","tagName","toString","targetSelector","cancelable","userAgent","navigator","isIE","setElement","tableRow","tableRowChildren","initialWidth","getComputedStyle","getPropertyValue","_canOpenBasedOnVersion","clickOpen","substring","some","cl","firstEl","parents","btnEl","opening","emit","containsTarget","selectorParent","nextEl","nextElementSibling","nextIsElem","targetEl","parentOpenClass","indicatorSelector","beforeOpen","slide","beforeClose","clearMdSetup","mdSelectors","mdTargets","mdTarget","clearXsSetup","xsSelectors","xsTargets","xsTarget","direction","self","targetStyles","paddingTop","paddingBottom","Velocity","default","animate","prevHeight","offsetHeight","prevPaddingTop","prevPaddingBottom","overflow","duration","easing","complete","headerHeight","request","headerGetHeight","scrollTop","bottom","windowTop","windowBottom","innerHeight","offset","instances","NodeList","createInstance","initializedClass","instance","closeAll","query","loadMore","expiration","upCountOnAdd","Storage","localStorage","getItem","checkData","checkDate","change","newValue","changed","date","saveData","itemExists","isId","find","singleItem","itemIndex","findIndex","addingMultiple","existingItem","addItems","removeItem","removingMultiple","splice","replaceOrAddItemById","itemId","newItem","existingItemIndex","removeItems","setItem","single","hasOwn","classes","i","arg","appendClass","parseValue","apply","newClass","module","exports","t","o","r","getOwnPropertySymbols","s","propertyIsEnumerable","_objectWithoutPropertiesLoose","n"],"sourceRoot":""}