{"mappings":"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","sources":["scripts/src/react/hotspots/index.tsx","scripts/src/api/events.ts","scripts/src/react/hotspots/App.tsx","scripts/src/api/hotspotAPI.ts","scripts/src/react/utils/pinterest.ts","scripts/src/react/utils/usePrevious.ts","scripts/src/react/hotspots/components/Hotspot.tsx"],"sourcesContent":["import React from \"react\";\r\nimport ReactDOM from \"react-dom\";\r\nimport { events, subscribe, unsubscribe } from \"../../api/events\";\r\n\r\nimport App from \"./App\";\r\n\r\nexport const hotspots = () => {\r\n const elements = document.querySelectorAll(\".js-hotspot-board\");\r\n elements.forEach(el => {\r\n renderHotspots(el);\r\n });\r\n\r\n const applyHotspotsToAppendedSlides = () => {\r\n const elements = document.querySelectorAll('.js-hotspot-board:not(.js-hotspots-active)');\r\n elements.forEach(el => {\r\n renderHotspots(el);\r\n });\r\n };\r\n\r\n subscribe(events.APPLY_HOTSPOTS_TO_APPENDED_SLIDES, applyHotspotsToAppendedSlides);\r\n return () => {\r\n unsubscribe(events.APPLY_HOTSPOTS_TO_APPENDED_SLIDES, applyHotspotsToAppendedSlides);\r\n }; \r\n\r\n function renderHotspots(el: HTMLElement) {\r\n const config = JSON.parse(el.dataset.hotspots || \"\");\r\n if (!config) {\r\n return;\r\n }\r\n const imageId = config.imageId;\r\n const hotspotIds = config.hotspotIds || \"\";\r\n const versionId = config.versionId || \"\";\r\n const html = document.querySelector(\"html\");\r\n const culture = document.querySelector('html')?.dataset.lang || \"en-GB\";\r\n const location = html?.dataset.location || \"GB\";\r\n const imageEl = el.parentElement?.querySelector(\"img\");\r\n\r\n if (!imageEl || !imageId || !hotspotIds) {\r\n return;\r\n }\r\n\r\n ReactDOM.render(\r\n ,\r\n el\r\n );\r\n\r\n el.classList.add(\"js-hotspots-active\");\r\n }\r\n};\r\n","const events = {\r\n ITEM_ADDED_TO_BASKET: \"ITEM_ADDED_TO_BASKET\",\r\n ITEM_REMOVED_FROM_BASKET: \"ITEM_REMOVED_FROM_BASKET\",\r\n ITEM_UPDATED_IN_BASKET: \"ITEM_UPDATED_IN_BASKET\",\r\n PROMO_CODE_CHANGE: \"PROMO_CODE_CHANGE\",\r\n APPLY_GIFT_CARD_TO_BASKET: \"APPLY_GIFT_CARD_TO_BASKET\",\r\n UNAPPLY_GIFT_CARD_TO_BASKET: \"UNAPPLY_GIFT_CARD_TO_BASKET\",\r\n CAROUSEL_SLIDE_CHANGE: \"CAROUSEL_SLIDE_CHANGE\",\r\n CAROUSEL_INIT: \"CAROUSEL_INIT\",\r\n CLOSE_MODAL: \"CLOSE_MODAL\",\r\n REFRESH_BASKET: \"REFRESH_BASKET\", \r\n OPEN_SEARCH: \"OPEN_SEARCH\",\r\n APPLY_HOTSPOTS_TO_APPENDED_SLIDES: \"APPLY_HOTSPOTS_TO_APPENDED_SLIDES\",\r\n CUSTOMIZATION_DROPDOWN_OPENED: \"CUSTOMIZATION_DROPDOWN_OPENED\",\r\n ITEM_ADDED_TO_WISHLIST: \"ITEM_ADDED_TO_WISHLIST\",\r\n AVAILABILITY_VIEW: \"AVAILABILITY_VIEW\",\r\n PRODUCT_DETAIL_VIEW: \"PRODUCT_DETAIL_VIEW\",\r\n SITE_SEARCH: \"SITE_SEARCH\",\r\n PRODUCTPAGE_ERROR:\"PRODUCTPAGE_ERROR\",\r\n};\r\n\r\nconst dispatch = (type: string, data?: any) => {\r\n if (data) {\r\n window.dispatchEvent(new CustomEvent(type, { detail: data }));\r\n } else {\r\n window.dispatchEvent(new Event(type));\r\n }\r\n};\r\n\r\nconst subscribe = (\r\n type: string,\r\n callback: EventListenerOrEventListenerObject\r\n) => {\r\n window.addEventListener(type, callback);\r\n};\r\n\r\nconst unsubscribe = (\r\n type: string,\r\n callback: EventListenerOrEventListenerObject\r\n) => {\r\n window.removeEventListener(type, callback);\r\n};\r\n\r\nexport { dispatch, subscribe, unsubscribe, events };\r\n","// React\r\nimport React, {\r\n FC,\r\n MouseEvent,\r\n useCallback,\r\n useEffect,\r\n useLayoutEffect,\r\n useRef,\r\n useState\r\n} from \"react\";\r\n\r\n// Vendor\r\nimport cx from \"classnames\";\r\n\r\n// App\r\nimport { events, subscribe, unsubscribe } from \"../../api/events\";\r\nimport hotspotAPI from \"../../api/hotspotAPI\";\r\nimport SVGIcon from \"../components/SVGIcon\";\r\nimport { loadPinterest } from \"../utils/pinterest\";\r\nimport usePrevious from \"../utils/usePrevious\";\r\nimport Hotspot from \"./components/Hotspot\";\r\n// import \"./types/globals\";\r\n\r\ninterface IHotspotsProps {\r\n imageId: string;\r\n hotspotIds: string;\r\n versionId: string;\r\n culture: string;\r\n location: string;\r\n image: HTMLImageElement;\r\n showPinterest?: boolean;\r\n}\r\n\r\ninterface IBox {\r\n top: number;\r\n right: number;\r\n bottom: number;\r\n left: number;\r\n}\r\n\r\nconst imagePadding: IBox = {\r\n top: 32,\r\n right: 32,\r\n bottom: 32,\r\n left: 32\r\n};\r\n\r\nconst controlsPaddingRight = 96;\r\nconst controlsPaddingBottom = 60;\r\n\r\nconst App: FC = ({\r\n imageId,\r\n hotspotIds,\r\n versionId,\r\n culture,\r\n location,\r\n image\r\n}) => {\r\n const [isWithinCarousel, setIsWithinCarousel] = useState(false);\r\n const [isActiveSlide, setIsActiveSlide] = useState(false);\r\n const [open, setOpen] = useState(false);\r\n const [hotspots, setHotspots] = useState();\r\n const [cropTranslation, setCropTranslation] = useState();\r\n const [currentSrc, setCurrentSrc] = useState(image.currentSrc);\r\n const previousSrc = usePrevious(currentSrc);\r\n const boardEl = useRef();\r\n const [boardWidth, setBoardWidth] = useState();\r\n const [boardHeight, setBoardHeight] = useState();\r\n const [pinterestLoaded, setPinterestLoaded] = useState(false);\r\n\r\n const checkForSrcChange = () => {\r\n if (previousSrc !== image.currentSrc) {\r\n setCurrentSrc(image.currentSrc);\r\n }\r\n };\r\n\r\n const lazyLoaded = (e: Event) => {\r\n if (e.target === image) {\r\n checkForSrcChange();\r\n setBoardSize();\r\n }\r\n };\r\n\r\n const setBoardSize = () => {\r\n if (!boardEl.current) {\r\n return;\r\n }\r\n\r\n if (boardEl && boardEl.current && boardEl.current.offsetWidth) {\r\n setBoardWidth(boardEl.current.offsetWidth);\r\n setBoardHeight(boardEl.current.offsetHeight);\r\n }\r\n };\r\n\r\n useLayoutEffect(() => {\r\n const delayedCheckForSrcChange = () => setTimeout(checkForSrcChange, 50);\r\n checkForSrcChange();\r\n\r\n window.addEventListener(\"resize\", delayedCheckForSrcChange);\r\n window.removeEventListener(\"lazyloaded\", lazyLoaded);\r\n window.addEventListener(\"lazyloaded\", lazyLoaded);\r\n\r\n return () => {\r\n window.removeEventListener(\"resize\", delayedCheckForSrcChange);\r\n window.removeEventListener(\"lazyloaded\", lazyLoaded);\r\n };\r\n }, [image.currentSrc]);\r\n\r\n const setBoardRef = useCallback((node: HTMLDivElement | null) => {\r\n if (boardEl.current) {\r\n window.removeEventListener(\"resize\", setBoardSize);\r\n window.removeEventListener(\"lazyloaded\", lazyLoaded);\r\n image.removeEventListener(\"load\", setBoardSize);\r\n }\r\n\r\n if (node) {\r\n boardEl.current = node;\r\n\r\n setBoardSize();\r\n window.addEventListener(\"resize\", setBoardSize);\r\n window.removeEventListener(\"lazyloaded\", lazyLoaded);\r\n window.addEventListener(\"lazyloaded\", lazyLoaded);\r\n image.addEventListener(\"load\", setBoardSize);\r\n }\r\n }, []);\r\n\r\n const getHotspots = () => {\r\n if (!hotspotIds) {\r\n return;\r\n }\r\n\r\n hotspotAPI\r\n .getHotspots(imageId, hotspotIds, culture, location, versionId)\r\n .then(res => {\r\n setHotspots(res.data);\r\n })\r\n .catch(() => {\r\n setHotspots(undefined);\r\n });\r\n };\r\n\r\n const getTranslatedCoordinates = () => {\r\n if (!hotspotIds) {\r\n return;\r\n }\r\n\r\n hotspotAPI\r\n .getImageTranslatedCoordinates(image.currentSrc)\r\n .then(crop => {\r\n setCropTranslation(crop);\r\n })\r\n .catch(e => {\r\n console.log(e);\r\n setCropTranslation(undefined);\r\n });\r\n };\r\n\r\n useEffect(() => {\r\n if (hotspots) {\r\n return;\r\n }\r\n\r\n getHotspots();\r\n }, []);\r\n\r\n useEffect(() => {\r\n (async () => {\r\n if (open) {\r\n try {\r\n await loadPinterest();\r\n setPinterestLoaded(true);\r\n } catch {\r\n setPinterestLoaded(false);\r\n }\r\n }\r\n })();\r\n }, [open]);\r\n\r\n useEffect(() => {\r\n getTranslatedCoordinates();\r\n }, [currentSrc]);\r\n\r\n const handleSavePin = () => {\r\n window.PinUtils.pinAny();\r\n };\r\n\r\n // only the active slide should show hotspots\r\n const handleCarouselInit = (e: CustomEvent) => {\r\n const {\r\n activeSlide,\r\n slideContainer\r\n }: { activeSlide: HTMLElement; slideContainer: HTMLElement } = e.detail;\r\n if (!slideContainer || !slideContainer.contains(image)) {\r\n return;\r\n }\r\n\r\n setIsWithinCarousel(true);\r\n setIsActiveSlide(activeSlide.contains(image));\r\n };\r\n\r\n const handleSlideChange = (e: CustomEvent) => {\r\n const { activeSlide }: { activeSlide: HTMLElement } = e.detail;\r\n if (\r\n !activeSlide ||\r\n !activeSlide.parentElement ||\r\n !activeSlide.parentElement.contains(image)\r\n ) {\r\n return;\r\n }\r\n\r\n setIsActiveSlide(activeSlide.contains(image));\r\n };\r\n\r\n const initWithinCarousel = () => {\r\n if (image.closest(\".slick-slide\")) {\r\n setIsWithinCarousel(!!image.closest(\".slick-current\"));\r\n }\r\n };\r\n\r\n const handleToggleClick = (e: MouseEvent) => {\r\n e.stopPropagation();\r\n setOpen(!open);\r\n };\r\n\r\n const handleBoardClick = () => {\r\n if (isWithinCarousel && !isActiveSlide) {\r\n return;\r\n }\r\n\r\n setOpen(true);\r\n };\r\n\r\n useEffect(() => {\r\n initWithinCarousel();\r\n subscribe(events.CAROUSEL_INIT, handleCarouselInit);\r\n subscribe(events.CAROUSEL_SLIDE_CHANGE, handleSlideChange);\r\n return () => {\r\n unsubscribe(events.CAROUSEL_INIT, handleCarouselInit);\r\n unsubscribe(events.CAROUSEL_SLIDE_CHANGE, handleSlideChange);\r\n };\r\n }, []);\r\n\r\n const pxToPercentOfImage = (px: number, dimension: \"width\" | \"height\") => {\r\n if (!boardWidth || !boardHeight) {\r\n return 0;\r\n }\r\n\r\n const total = dimension === \"height\" ? boardHeight : boardWidth;\r\n\r\n return (px / total) * 100;\r\n };\r\n\r\n const paddingBoxToPercent = (box: IBox): IBox => {\r\n return {\r\n top: pxToPercentOfImage(box.top, \"height\"),\r\n right: pxToPercentOfImage(box.right, \"width\"),\r\n bottom: pxToPercentOfImage(box.bottom, \"height\"),\r\n left: pxToPercentOfImage(box.left, \"width\")\r\n };\r\n };\r\n\r\n let translatedHotspots: IHotspot[] | undefined;\r\n\r\n if (cropTranslation) {\r\n const imagePaddingPercentages = paddingBoxToPercent(imagePadding);\r\n const controlsPaddingRightPercentage = pxToPercentOfImage(\r\n controlsPaddingRight,\r\n \"width\"\r\n );\r\n const controlsPaddingBottomPercentage = pxToPercentOfImage(\r\n controlsPaddingBottom,\r\n \"height\"\r\n );\r\n const scaledXRange = cropTranslation.X2 - cropTranslation.X1;\r\n const scaledYRange = cropTranslation.Y2 - cropTranslation.Y1;\r\n translatedHotspots =\r\n hotspots &&\r\n hotspots\r\n .filter(\r\n h =>\r\n h.X >= cropTranslation.X1 &&\r\n h.X <= cropTranslation.X2 &&\r\n h.Y >= cropTranslation.Y1 &&\r\n h.Y <= cropTranslation.Y2\r\n )\r\n .map(h => ({\r\n ...h,\r\n X: ((h.X - cropTranslation.X1) / scaledXRange) * 100,\r\n Y: ((h.Y - cropTranslation.Y1) / scaledYRange) * 100\r\n }))\r\n .filter(\r\n h =>\r\n h.X >= imagePaddingPercentages.left &&\r\n h.X <= 100 - imagePaddingPercentages.right &&\r\n h.Y >= imagePaddingPercentages.top &&\r\n h.Y <= 100 - imagePaddingPercentages.bottom\r\n )\r\n .filter(\r\n h =>\r\n h.X <= 100 - controlsPaddingRightPercentage ||\r\n h.Y <= 100 - controlsPaddingBottomPercentage\r\n );\r\n }\r\n\r\n if (!translatedHotspots || !translatedHotspots.length) {\r\n return <>;\r\n }\r\n\r\n const enabled = !isWithinCarousel || isActiveSlide;\r\n\r\n return (\r\n enabled && (\r\n \r\n {open &&\r\n translatedHotspots.map(h => (\r\n \r\n ))}\r\n
\r\n {pinterestLoaded && window.PinUtils && (\r\n \r\n \r\n \r\n )}\r\n \r\n \r\n \r\n
\r\n \r\n )\r\n );\r\n};\r\n\r\nexport default App;\r\n","import { parseUrl } from \"../react/utils/queryString\";\r\nimport { routes } from \"./config\";\r\nimport { get } from \"./request\";\r\n\r\nimport { TGetHotspots } from \"./types\";\r\n\r\n/**\r\n * Get the collection of hotspots for an image, filtered by a CSV list of hotspot IDs\r\n * @returns {promose} Array of hotspots for the image\r\n */\r\nconst getHotspots: TGetHotspots = (\r\n imageId,\r\n hotspotIds,\r\n culture,\r\n location,\r\n versionId\r\n) => {\r\n return get(routes.Hotspots.GetHotspots, {\r\n options: { noToast: true },\r\n params: {\r\n imageId,\r\n hotspotIds,\r\n culture,\r\n location,\r\n versionId\r\n }\r\n }).then(data => {\r\n return { success: true, data };\r\n });\r\n};\r\n\r\nconst getImageTranslatedCoordinates: (\r\n imageUrl: string\r\n) => Promise = imageUrl => {\r\n if (!imageUrl || !imageUrl.startsWith(\"http\")) {\r\n return Promise.reject(\"Image not loaded from remote source\");\r\n }\r\n const parsed = parseUrl(imageUrl);\r\n const baseUrl = parsed.protocol + \"//\" + parsed.host;\r\n const pathAndQuery = encodeURIComponent(parsed.pathname + parsed.search);\r\n const calculateUrl = routes.Hotspots.CalculateTranslatedCoordinates.replace(\r\n \"IMAGE_BASE_URL\",\r\n baseUrl\r\n ).replace(\"IMAGE_PATH_AND_QUERY\", pathAndQuery);\r\n\r\n return fetch(calculateUrl).then(response => {\r\n if (!response.ok) {\r\n return Promise.reject(`${response.status} - ${response.statusText}`);\r\n }\r\n return response.json();\r\n });\r\n};\r\n\r\nexport default {\r\n getHotspots,\r\n getImageTranslatedCoordinates\r\n};\r\n","export const loadPinterest = () => {\r\n return new Promise((resolve, reject) => {\r\n if (document.querySelector(\"#pinterest-script\")) {\r\n resolve();\r\n return; // already appended script tag\r\n }\r\n const script = document.createElement(\"script\");\r\n script.src = \"https://assets.pinterest.com/js/pinit.js\";\r\n script.async = true;\r\n script.id = \"pinterest-script\";\r\n const timeout = setTimeout(reject, 5000);\r\n script.onload = () => {\r\n clearTimeout(timeout);\r\n setTimeout(resolve, 50); // execute using setTimeout so that pinterest script has a chance to run\r\n };\r\n document.head.appendChild(script);\r\n });\r\n};\r\n","import { useEffect, useRef } from \"react\";\r\n\r\nexport default function usePrevious(value: T) {\r\n // The ref object is a generic container whose current property is mutable ...\r\n // ... and can hold any value, similar to an instance property on a class\r\n const ref = useRef();\r\n\r\n // Store current value in ref\r\n useEffect(() => {\r\n ref.current = value;\r\n }, [value]); // Only re-run if value changes\r\n\r\n // Return previous value (happens before update in useEffect above)\r\n return ref.current;\r\n}\r\n","// React\r\nimport React, { FC, useLayoutEffect, useRef, useState } from \"react\";\r\nimport SVGIcon from \"../../components/SVGIcon\";\r\n\r\n// Vendor\r\nimport cx from \"classnames\";\r\n\r\n// App\r\n\r\nconst renderHotspotContent = (name: string, price: string) => {\r\n return (\r\n <>\r\n {name}\r\n {price && {price}}\r\n \r\n );\r\n};\r\n\r\ninterface IHotspotProps extends IHotspot {\r\n boardWidth?: number;\r\n boardHeight?: number;\r\n}\r\n\r\ntype TPopupPosition =\r\n | \"top-center\"\r\n | \"bottom-center\"\r\n | \"top-right\"\r\n | \"top-left\"\r\n | \"bottom-left\"\r\n | \"bottom-right\";\r\n\r\nconst Hotspot: FC = ({\r\n Name,\r\n Url,\r\n X,\r\n Y,\r\n Price,\r\n boardWidth,\r\n boardHeight\r\n}) => {\r\n const [open, setOpen] = useState(false);\r\n const [opening, setOpening] = useState(false);\r\n const [position, setPosition] = useState(\"bottom-center\");\r\n const linkEl = useRef(null);\r\n\r\n const handleOpen = () => {\r\n setOpen(true);\r\n setOpening(true);\r\n setTimeout(() => setOpening(false), 300);\r\n };\r\n\r\n const handleToggleClick = () => {\r\n if (open && !opening) {\r\n return setOpen(false);\r\n }\r\n setOpen(true);\r\n };\r\n\r\n const handleClose = () => {\r\n setOpen(false);\r\n };\r\n\r\n useLayoutEffect(() => {\r\n if (!linkEl.current || !boardWidth || !boardHeight) {\r\n return;\r\n }\r\n if (linkEl && linkEl.current && linkEl.current.offsetWidth) {\r\n return;\r\n }\r\n\r\n const padding = 4;\r\n const width = linkEl.current.offsetWidth;\r\n const height = linkEl.current.offsetHeight;\r\n const left = (X / 100) * boardWidth;\r\n const top = (Y / 100) * boardHeight;\r\n const overlapLeft = left - width / 2 - padding < 0;\r\n const overlapRight = left + width / 2 + padding > boardWidth;\r\n const overlapBottom = top + height + padding > boardHeight;\r\n let pos: TPopupPosition = \"bottom-center\";\r\n\r\n if (overlapRight) {\r\n pos = \"bottom-left\";\r\n if (overlapBottom) {\r\n pos = \"top-left\";\r\n }\r\n } else if (overlapLeft) {\r\n pos = \"bottom-right\";\r\n if (overlapBottom) {\r\n pos = \"top-right\";\r\n }\r\n } else if (overlapBottom) {\r\n pos = \"top-center\";\r\n }\r\n\r\n setPosition(pos);\r\n }, [linkEl.current, boardWidth, boardHeight]);\r\n\r\n return (\r\n \r\n \r\n \r\n \r\n \r\n {renderHotspotContent(Name, Price)}\r\n \r\n \r\n );\r\n};\r\n\r\nexport default Hotspot;\r\n"],"names":["$eA4hF","parcelRequire","$a247830226929e68$export$d8ad3743e55c2cfd","document","querySelectorAll","forEach","el","renderHotspots","applyHotspotsToAppendedSlides","$aOilU","subscribe","events","APPLY_HOTSPOTS_TO_APPENDED_SLIDES","unsubscribe","ref","ref1","config","JSON","parse","dataset","hotspots","imageId","hotspotIds","versionId","html","querySelector","culture","lang","location","imageEl","parentElement","$parcel$interopDefault","$b9394","render","jsx","$f3CYX","default","image","showPinterest","classList","add","$82b9fe20f672efc6$export$4bf9923669ad6c63","ITEM_ADDED_TO_BASKET","ITEM_REMOVED_FROM_BASKET","ITEM_UPDATED_IN_BASKET","PROMO_CODE_CHANGE","APPLY_GIFT_CARD_TO_BASKET","UNAPPLY_GIFT_CARD_TO_BASKET","CAROUSEL_SLIDE_CHANGE","CAROUSEL_INIT","CLOSE_MODAL","REFRESH_BASKET","OPEN_SEARCH","CUSTOMIZATION_DROPDOWN_OPENED","ITEM_ADDED_TO_WISHLIST","AVAILABILITY_VIEW","PRODUCT_DETAIL_VIEW","SITE_SEARCH","PRODUCTPAGE_ERROR","$82b9fe20f672efc6$export$635e15bbd66f01ea","type","data","window","dispatchEvent","CustomEvent","detail","Event","$82b9fe20f672efc6$export$ec068583843480e7","callback","addEventListener","$82b9fe20f672efc6$export$f500693cef883ffd","removeEventListener","$7oZCY","$0d2ef6ecb1225c50$var$imagePadding","top","right","bottom","left","$0d2ef6ecb1225c50$export$2e2bcd8739ae039","isWithinCarousel","setIsWithinCarousel","$kgL75","useState","isActiveSlide","setIsActiveSlide","open","setOpen","setHotspots","cropTranslation","setCropTranslation","currentSrc","setCurrentSrc","previousSrc","$aV9lC","boardEl","useRef","boardWidth","setBoardWidth","boardHeight","setBoardHeight","pinterestLoaded","setPinterestLoaded","checkForSrcChange","lazyLoaded","e","target","setBoardSize","current","offsetWidth","offsetHeight","useLayoutEffect","delayedCheckForSrcChange","setTimeout","setBoardRef","useCallback","node","useEffect","$je1y6","getHotspots","then","res","catch","undefined","$8Qeok","loadPinterest","getImageTranslatedCoordinates","crop","console","log","handleCarouselInit","activeSlide","slideContainer","contains","handleSlideChange","closest","pxToPercentOfImage","px","dimension","translatedHotspots","imagePaddingPercentages","box","controlsPaddingRightPercentage","controlsPaddingBottomPercentage","scaledXRange","X2","X1","scaledYRange","Y2","Y1","filter","h","X","Y","map","length","Fragment","enabled","jsxs","className","$aGMAK","onClick","$cmJqn","Id","PinUtils","pinAny","$bEN3t","name","stopPropagation","$dFhos","$c1ff365a789105e3$export$2e2bcd8739ae039","$e4FXt","get","$hhkG7","routes","Hotspots","GetHotspots","options","success","imageUrl","startsWith","Promise","reject","parsed","parseUrl","baseUrl","protocol","host","pathAndQuery","encodeURIComponent","pathname","search","calculateUrl","CalculateTranslatedCoordinates","replace","fetch","response","ok","json","status","statusText","$086b4413bee1e01b$export$c59fe32d6bc3e5db","resolve","script","createElement","src","async","id","timeout","onload","clearTimeout","head","appendChild","$d8e893156ac177a6$export$2e2bcd8739ae039","value","$500b39f2c07e30e3$export$2e2bcd8739ae039","Name","Url","Price","opening","setOpening","position","setPosition","linkEl","width","height","overlapBottom","pos","style","animation","onMouseEnter","onMouseLeave","href","price"],"version":3,"file":"hotspots.5a27372c.js.map"}