{"version":3,"file":"helpers-ce8cfe32.js","sources":["../../../src/js/modules/helpers.js"],"sourcesContent":["import FetchWrapper from './fetch-wrapper.js';\nconst thisWebsiteAPI = new FetchWrapper(`${window.location.protocol}//${window.location.host}` );\n\n// Add a class \"js-scrolled\" both on scroll and if the page is scrolled on load (e.g, on a refresh)\nfunction windowHasScrolled() {\n\tconst html = document.querySelector('html');\n\n\tif (window.scrollY > 0) {\n\t\thtml.classList.add('js-scrolled');\n\t} else {\n\t\thtml.classList.remove('js-scrolled');\n\t}\n}\n\n// Indicate to users that Sprig forms are doing something when submitted\ndocument.querySelectorAll('form[sprig]').forEach(sprigForm => {\n\tsprigForm.insertAdjacentHTML('beforeend', `\n\t\t\n\t\t\t

Please wait

\n\t\t\t\n\t\t
\n\t`);\n\n\tlet dialog = sprigForm.querySelector('.sprigActionFeedback');\n\n\tdialog.querySelector('button').addEventListener('click', () => {\n\t\tdialog.close();\n\t});\n\n\tsprigForm.addEventListener('submit', () => {\n\t\tdialog.showModal();\n\t});\n});\n\n/**\n * Returns what type of interaction mechanism the current device uses\n * @returns {string}\n */\nexport function interactionType() {\n\tlet interactionType = 'mouseover';\n\n\tif( window.matchMedia('(hover: hover)') ) { // desktop\n\t\tinteractionType = 'mouseover'; }\n\tif( window.matchMedia('(hover: none) and (pointer: coarse)') ) { // touchscreen\n\t\tinteractionType = 'click'; }\n\tif( window.matchMedia('(hover: none) and (pointer: fine)') ) { // stylus\n\t\tinteractionType = 'click'; }\n\tif( window.matchMedia('(hover: hover) and (pointer: coarse)') ) { // Wii/Kinect/etc\n\t\tinteractionType = 'mouseover'; }\n\tif( window.matchMedia('(hover: hover) and (pointer: fine)') ) { // mouse\n\t\tinteractionType = 'mouseover'; }\n\n\treturn interactionType;\n}\n\n/**\n * Adds an event listener which will indicate if the page has scrolled by manipulating a class on the HTML element.\n */\nexport function initWindowHasScrolled() {\n\twindow.addEventListener('scroll', function() {\n\t\twindowHasScrolled();\n\t});\n}\n\nexport function timelineChange() {\n\tdocument.querySelectorAll('.period').forEach(period => {\n\t\tperiod.addEventListener('click', (e) => {\n\t\t\te.preventDefault();\n\t\t\tlet clickedLink = e.currentTarget;\n\t\t\tlet parser = new DOMParser();\n\t\t\tconsole.log(clickedLink);\n\t\t\tclickedLink.classList.toggle('open');\n\t\t});\n\t});\n}\n\n/**\n * Enable animated \"intro\" on blocks controlled by CSS when elements scroll into view\n */\nexport function initAnimatedIntroBlocks() {\n\tif (!!window.IntersectionObserver) {\n\t\tdocument.querySelector('html').classList.add('js-supportsIntersectionObserver');\n\n\t\tlet observer = new IntersectionObserver((watchList, observer) => {\n\t\t\twatchList.forEach(watchedElement => {\n\t\t\t\tif (watchedElement.isIntersecting) {\n\t\t\t\t\t// console.log(watchedElement);\n\n\t\t\t\t\twatchedElement.target.classList.add('js-inViewport');\n\t\t\t\t\tobserver.unobserve(watchedElement.target);\n\t\t\t\t}\n\t\t\t});\n\t\t}, {rootMargin: \"0px 0px -20% 0px\"});\n\n\t\tdocument.querySelectorAll('[data-scroll-reveal]').forEach(watchTarget => {\n\t\t\tobserver.observe(watchTarget);\n\t\t});\n\t}\n}\n\n/**\n * Handle anchors with a class of popup\n */\nexport function initPopups() {\n\tdocument.querySelectorAll('a.popup').forEach(popupLink => {\n\t\tpopupLink.addEventListener('click', (e) => {\n\t\t\te.preventDefault();\n\t\t\tlet clickedLink = e.currentTarget;\n\t\t\tlet parser = new DOMParser();\n\t\t\tclickedLink.classList.add('js-loading');\n\n\t\t\tthisWebsiteAPI.getHtml(clickedLink.getAttribute('href')).then(response => {\n\t\t\t\tlet responseAsDom = parser.parseFromString(response, \"text/html\");\n\t\t\t\tlet imageWeWant = responseAsDom.querySelector('#ajaxcontent').outerHTML;\n\t\t\t\tlet lightbox = document.querySelector('#lightbox') ?? null;\n\n\t\t\t\tif (lightbox) {\n\t\t\t\t\tdocument.querySelector('#lightbox .content').innerHTML = imageWeWant;\n\t\t\t\t\tlightbox.showModal();\n\t\t\t\t\tclickedLink.classList.remove('js-loading');\n\t\t\t\t} else {\n\t\t\t\t\tdocument.querySelector('body').insertAdjacentHTML('afterbegin', `\n\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t${imageWeWant}\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t`);\n\n\t\t\t\t\tlet lightbox = document.querySelector('#lightbox');\n\t\t\t\t\tlightbox.showModal();\n\t\t\t\t\tclickedLink.classList.remove('js-loading');\n\t\t\t\t}\n\t\t\t}).catch(error => {\n\t\t\t\tconsole.error(error);\n\t\t\t});\n\n\t\t\tconsole.log(clickedLink);\n\t\t});\n\t});\n\n\tdocument.querySelectorAll('a.popup-video').forEach(popupLink => {\n\t\tpopupLink.addEventListener('click', (e) => {\n\t\t\te.preventDefault();\n\t\t\tlet clickedLink = e.currentTarget;\n\t\t\tlet clickedUrl = new URL(clickedLink.href);\n\t\t\tlet videoId = clickedUrl.searchParams.get('v');\n\t\t\tlet videoWeWant = ``;\n\n\t\t\tclickedLink.classList.add('js-loading');\n\n\t\t\tdocument.querySelector('body').insertAdjacentHTML('afterbegin', `\n\t\t\t\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t${videoWeWant}\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t`);\n\n\t\t\tlet lightbox = document.querySelector('#lightbox') ?? null;\n\t\t\tlet theVideo = document.querySelector('#lightbox iframe');\n\n\t\t\tlightbox.showModal();\n\t\t\tclickedLink.classList.remove('js-loading');\n\n\t\t\t// remove the entire thing from the DOM when closed, to stop the video from continuing to play\n\t\t\tdocument.querySelector('#lightbox').addEventListener(\"close\", e => {\n\t\t\t\te.target.remove();\n\t\t\t});\n\t\t});\n\t});\n}\n\n/**\n * Adds a Dismiss button and behaviour to CMS generated Flash messages\n */\nexport function dismissNotice() {\n\tlet theNotice = document.querySelector(\"#notice:not(.cart)\");\n\n\tif (theNotice) {\n\t\ttheNotice.insertAdjacentHTML('beforeend', ``);\n\t\ttheNotice.querySelector('button').addEventListener('click', e => {\n\t\t\te.preventDefault();\n\t\t\ttheNotice.remove();\n\t\t});\n\t}\n}\n\n/**\n * Initiate the VAT toggle behaviour.\n * Inserts a toggle button, and handles all applicable prices.\n */\nexport function initVAT() {\n\tlet currentPreference = getPreferredVAT();\n\n\t/* Get the nav item we'll punt the dynamic toggle button before */\n\tconst changeState = document.querySelector('.nav_secondary');\n\n\tlet buttonToggle = `\n\t\t
  • \n\t\t\t\n\t\t\t
    ${ currentPreference == 'exVat' ? 'Excluding' : 'Including' } VAT in prices shown for products on the site.
    \n\t\t
  • \n\t`;\n\tchangeState.insertAdjacentHTML('beforeend', buttonToggle);\n\n\t/* Add the toggle button and assign the toggle function to it */\n\tlet button = document.querySelector('#vatToggle');\n\tbutton.addEventListener('click', toggleVAT);\n\n\t/* update all the prices on the page */\n\tupdateAllPrices( currentPreference );\n}\n\n/**\n * Updates all HTML nodes with a [data-raw-price] attribute to reflect the price preference of the visitor.\n * .i.e., including or excluding VAT\n * @param mode\n */\nexport function updateAllPrices( mode ) {\n\tconst toChange = document.querySelectorAll(\"[data-raw-price]\");\n\tconst vatRate = parseFloat( document.querySelector('html').dataset.vatRate ) + 1;\n\n\ttoChange.forEach( item => {\n\t\tlet newPrice = item.dataset.rawPrice;\n\t\tlet message = `(ex VAT)`;\n\n\t\tif ( mode == 'incVat' ) {\n\t\t\tnewPrice = item.dataset.rawPrice * vatRate;\n\t\t\tmessage = `(inc VAT)`;\n\t\t}\n\n\t\tlet formattedPrice = new Intl.NumberFormat(`en-GB`, {\n\t\t\tcurrency: `GBP`,\n\t\t\tstyle: 'currency',\n\t\t}).format(newPrice);\n\n\t\titem.textContent = `${formattedPrice} ${message}`;\n\t});\n}\n\n/**\n * Respond to clicking the toggle VAT button.\n * Updates the button state, sets the localStorage value, updates all prices on the page, updates the toggle tooltip.\n * @param event\n */\nexport function toggleVAT( event ) {\n\tif ( event.currentTarget.dataset.vat == 'exVat' ) {\n\t\tevent.currentTarget.dataset.vat = 'incVat'; // updates the button state\n\t\tsetPreferredVAT('incVat');\n\t\tupdateAllPrices( 'incVat' );\n\t\tdocument.querySelector('#vatToggle + .tooltip span').textContent = 'Including';\n\t}\n\telse {\n\t\tevent.currentTarget.dataset.vat = 'exVat';\n\t\tsetPreferredVAT('exVat');\n\t\tupdateAllPrices( 'exVat' );\n\t\tdocument.querySelector('#vatToggle + .tooltip span').textContent = 'Excluding';\n\t}\n}\n\n/**\n * Returns value of `vatPreference` from localStorage, and sets it as `exVat` if not set.\n * @returns {string}\n */\nexport function getPreferredVAT() {\n\tlet storedValue = localStorage.getItem(\"vatPreference\");\n\n\tif (storedValue) {\n\t\treturn storedValue;\n\t}\n\telse {\n\t\tsetPreferredVAT(\"exVat\");\n\t\treturn \"exVat\";\n\t}\n}\n\n/**\n * Sets the value of `vatPreference` in localStorage\n * @param {string} value - incVat or exVat\n */\nexport function setPreferredVAT(value) {\n\tlocalStorage.setItem(\"vatPreference\", value);\n}\n\n/**\n * Adds a \"Just in\" label to products that were added within the last ten days.\n * This is client-side calculated to allow efficient caching server side\n */\nexport function justIn() {\n\tconst utcDate = new Date();\n\tconst isoDate = utcDate.toISOString();\n\tdocument\n\t\t.querySelectorAll('.Product[data-date-added]')\n\t\t.forEach(product => {\n\t\t\tconst prodDate = new Date(product.dataset.dateAdded);\n\t\t\tconst dateInUTC = new Date(isoDate);\n\n\t\t\tlet differenceInDays = Math.abs(dateInUTC.getDate() - prodDate.getDate());\n\n\t\t\tif (differenceInDays < 10) {\n\t\t\t\tproduct\n\t\t\t\t\t.querySelector(':scope .flashMessages')\n\t\t\t\t\t.insertAdjacentHTML(\n\t\t\t\t\t\t'beforeend',\n\t\t\t\t\t\t`
  • Just In
  • `\n\t\t\t\t\t);\n\t\t\t}\n\t\t});\n}\n"],"names":["thisWebsiteAPI","FetchWrapper","windowHasScrolled","html","sprigForm","dialog","initWindowHasScrolled","timelineChange","period","e","clickedLink","initAnimatedIntroBlocks","observer","watchList","watchedElement","watchTarget","initPopups","popupLink","parser","response","imageWeWant","lightbox","error","videoWeWant","dismissNotice","theNotice","initVAT","currentPreference","getPreferredVAT","changeState","buttonToggle","toggleVAT","updateAllPrices","mode","toChange","vatRate","item","newPrice","message","formattedPrice","event","setPreferredVAT","storedValue","value"],"mappings":"+CACA,MAAMA,EAAiB,IAAIC,EAAa,GAAG,OAAO,SAAS,QAAQ,KAAK,OAAO,SAAS,IAAI,EAAE,EAG9F,SAASC,GAAoB,CAC5B,MAAMC,EAAO,SAAS,cAAc,MAAM,EAEtC,OAAO,QAAU,EACpBA,EAAK,UAAU,IAAI,aAAa,EAEhCA,EAAK,UAAU,OAAO,aAAa,CAErC,CAGA,SAAS,iBAAiB,aAAa,EAAE,QAAQC,GAAa,CAC7DA,EAAU,mBAAmB,YAAa;AAAA;AAAA;AAAA;AAAA;AAAA,EAKzC,EAED,IAAIC,EAASD,EAAU,cAAc,sBAAsB,EAE3DC,EAAO,cAAc,QAAQ,EAAE,iBAAiB,QAAS,IAAM,CAC9DA,EAAO,MAAK,CACd,CAAE,EAEDD,EAAU,iBAAiB,SAAU,IAAM,CAC1CC,EAAO,UAAS,CAClB,CAAE,CACF,CAAC,EA0BM,SAASC,GAAwB,CACvC,OAAO,iBAAiB,SAAU,UAAW,CAC5CJ,GACF,CAAE,CACF,CAEO,SAASK,GAAiB,CAChC,SAAS,iBAAiB,SAAS,EAAE,QAAQC,GAAU,CACtDA,EAAO,iBAAiB,QAAUC,GAAM,CACvCA,EAAE,eAAc,EAChB,IAAIC,EAAcD,EAAE,cACP,IAAI,UACjB,QAAQ,IAAIC,CAAW,EACvBA,EAAY,UAAU,OAAO,MAAM,CACtC,CAAG,CACH,CAAE,CACF,CAKO,SAASC,GAA0B,CACzC,GAAM,OAAO,qBAAsB,CAClC,SAAS,cAAc,MAAM,EAAE,UAAU,IAAI,iCAAiC,EAE9E,IAAIC,EAAW,IAAI,qBAAqB,CAACC,EAAWD,IAAa,CAChEC,EAAU,QAAQC,GAAkB,CAC/BA,EAAe,iBAGlBA,EAAe,OAAO,UAAU,IAAI,eAAe,EACnDF,EAAS,UAAUE,EAAe,MAAM,EAE7C,CAAI,CACJ,EAAK,CAAC,WAAY,kBAAkB,CAAC,EAEnC,SAAS,iBAAiB,sBAAsB,EAAE,QAAQC,GAAe,CACxEH,EAAS,QAAQG,CAAW,CAC/B,CAAG,CACD,CACF,CAKO,SAASC,GAAa,CAC5B,SAAS,iBAAiB,SAAS,EAAE,QAAQC,GAAa,CACzDA,EAAU,iBAAiB,QAAUR,GAAM,CAC1CA,EAAE,eAAc,EAChB,IAAIC,EAAcD,EAAE,cAChBS,EAAS,IAAI,UACjBR,EAAY,UAAU,IAAI,YAAY,EAEtCV,EAAe,QAAQU,EAAY,aAAa,MAAM,CAAC,EAAE,KAAKS,GAAY,CAEzE,IAAIC,EADgBF,EAAO,gBAAgBC,EAAU,WAAW,EAChC,cAAc,cAAc,EAAE,UAC1DE,EAAW,SAAS,cAAc,WAAW,GAAK,KAElDA,GACH,SAAS,cAAc,oBAAoB,EAAE,UAAYD,EACzDC,EAAS,UAAS,EAClBX,EAAY,UAAU,OAAO,YAAY,IAEzC,SAAS,cAAc,MAAM,EAAE,mBAAmB,aAAc;AAAA;AAAA;AAAA;AAAA,SAI5DU,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAOf,EAEe,SAAS,cAAc,WAAW,EACxC,UAAS,EAClBV,EAAY,UAAU,OAAO,YAAY,EAE9C,CAAI,EAAE,MAAMY,GAAS,CACjB,QAAQ,MAAMA,CAAK,CACvB,CAAI,EAED,QAAQ,IAAIZ,CAAW,CAC1B,CAAG,CACH,CAAE,EAED,SAAS,iBAAiB,eAAe,EAAE,QAAQO,GAAa,CAC/DA,EAAU,iBAAiB,QAAUR,GAAM,CAC1CA,EAAE,eAAc,EAChB,IAAIC,EAAcD,EAAE,cAGhBc,EAAc,gFAFD,IAAI,IAAIb,EAAY,IAAI,EAChB,aAAa,IAAI,GAAG,CAC4D,uKAEzGA,EAAY,UAAU,IAAI,YAAY,EAEtC,SAAS,cAAc,MAAM,EAAE,mBAAmB,aAAc;AAAA;AAAA;AAAA;AAAA,QAI3Da,CAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAOhB,EAEA,IAAIF,EAAW,SAAS,cAAc,WAAW,GAAK,KACvC,SAAS,cAAc,kBAAkB,EAExDA,EAAS,UAAS,EAClBX,EAAY,UAAU,OAAO,YAAY,EAGzC,SAAS,cAAc,WAAW,EAAE,iBAAiB,QAASD,GAAK,CAClEA,EAAE,OAAO,QACb,CAAI,CACJ,CAAG,CACH,CAAE,CACF,CAKO,SAASe,GAAgB,CAC/B,IAAIC,EAAY,SAAS,cAAc,oBAAoB,EAEvDA,IACHA,EAAU,mBAAmB,YAAa,wDAAwD,EAClGA,EAAU,cAAc,QAAQ,EAAE,iBAAiB,QAAShB,GAAK,CAChEA,EAAE,eAAc,EAChBgB,EAAU,OAAM,CACnB,CAAG,EAEH,CAMO,SAASC,GAAU,CACzB,IAAIC,EAAoBC,IAGxB,MAAMC,EAAc,SAAS,cAAc,gBAAgB,EAE3D,IAAIC,EAAe;AAAA;AAAA,qCAEkBH,CAAmB;AAAA,gCACxBA,GAAqB,QAAU,YAAc,WAAa;AAAA;AAAA,GAG1FE,EAAY,mBAAmB,YAAaC,CAAY,EAG3C,SAAS,cAAc,YAAY,EACzC,iBAAiB,QAASC,CAAS,EAG1CC,EAAiBL,CAAiB,CACnC,CAOO,SAASK,EAAiBC,EAAO,CACvC,MAAMC,EAAW,SAAS,iBAAiB,kBAAkB,EACvDC,EAAU,WAAY,SAAS,cAAc,MAAM,EAAE,QAAQ,OAAS,EAAG,EAE/ED,EAAS,QAASE,GAAQ,CACzB,IAAIC,EAAWD,EAAK,QAAQ,SACxBE,EAAU,WAETL,GAAQ,WACZI,EAAWD,EAAK,QAAQ,SAAWD,EACnCG,EAAU,aAGX,IAAIC,EAAiB,IAAI,KAAK,aAAa,QAAS,CACnD,SAAU,MACV,MAAO,UACV,CAAG,EAAE,OAAOF,CAAQ,EAElBD,EAAK,YAAc,GAAGG,CAAc,IAAID,CAAO,EACjD,CAAE,CACF,CAOO,SAASP,EAAWS,EAAQ,CAC7BA,EAAM,cAAc,QAAQ,KAAO,SACvCA,EAAM,cAAc,QAAQ,IAAM,SAClCC,EAAgB,QAAQ,EACxBT,EAAiB,QAAQ,EACzB,SAAS,cAAc,4BAA4B,EAAE,YAAc,cAGnEQ,EAAM,cAAc,QAAQ,IAAM,QAClCC,EAAgB,OAAO,EACvBT,EAAiB,OAAO,EACxB,SAAS,cAAc,4BAA4B,EAAE,YAAc,YAErE,CAMO,SAASJ,GAAkB,CACjC,IAAIc,EAAc,aAAa,QAAQ,eAAe,EAEtD,OAAIA,IAIHD,EAAgB,OAAO,EAChB,QAET,CAMO,SAASA,EAAgBE,EAAO,CACtC,aAAa,QAAQ,gBAAiBA,CAAK,CAC5C"}