{"version":3,"file":"kg-main.min.js","sources":["../../src/scripts/foundation/moduleInitializer.js","../../src/scripts/utilities/browser-detect.js","../../src/scripts/feature/accounts/zen-login-cta.js","../../src/scripts/feature/accounts/zen-login-selectors.js","../../src/scripts/feature/accounts/login.js","../../src/scripts/feature/accounts/marketing-preferences.js","../../src/scripts/feature/accounts/reveal-password.js","../../src/scripts/feature/accounts/sign-in-link.js","../../src/scripts/feature/cta/cookie-banner.js","../../src/scripts/foundation/eventBus.js","../../src/scripts/utilities/debounce.js","../../src/scripts/utilities/scroll-top.js","../../src/scripts/utilities/scroller.js","../../src/scripts/foundation/form/validation.js","../../src/scripts/feature/form/field.js","../../src/scripts/feature/form/enable-form.js","../../src/scripts/utilities/ajax.js","../../src/scripts/feature/order/bank-account-lookup.js","../../src/scripts/utilities/file-saver.js","../../src/scripts/feature/order/download-agreement.js","../../src/scripts/feature/order/postcode-lookup.js","../../src/scripts/feature/order/request-price.js","../../src/scripts/feature/order/step-indicator.js","../../src/scripts/utilities/offset.js","../../src/scripts/feature/page-content/anchor-list.js","../../src/scripts/feature/page-content/page-accordion.js","../../src/scripts/utilities/toggle-slide.js","../../src/scripts/feature/page-content/why-us-carousel.js","../../src/scripts/feature/navigation/burger-menu.js","../../src/scripts/feature/navigation/global-header.js","../../src/scripts/feature/navigation/primary-nav.js","../../src/scripts/feature/form/prepopulate-email.js","../../src/scripts/feature/page-content/cookie-consent.js","../../src/scripts/pco.js"],"sourcesContent":["import document from 'document';\r\n\r\nexport const loadModules = modules => {\r\n modules.forEach((Module, index) => {\r\n if (!Module || typeof Module !== 'function') {\r\n console.log('Invalid module format', module, index);\r\n }\r\n\r\n const module = Module();\r\n \r\n if (module.selector) {\r\n const components = document.querySelectorAll(module.selector);\r\n if (components) {\r\n [...components].forEach(component => {\r\n //console.log('initializing module', module.selector);\r\n module.init(component);\r\n });\r\n } else {\r\n //console.log('module not found', module.selector);\r\n }\r\n } else {\r\n module.init();\r\n }\r\n });\r\n};","import window from 'window';\r\nimport document from 'document';\r\n\r\nexport const browserDetect = () => {\r\n \r\n function _detectIE() {\r\n var ua = window.navigator.userAgent;\r\n var msie = ua.indexOf('MSIE ');\r\n if (msie > 0) {\r\n // IE 10 or older => return version number\r\n return ['IE', parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10)];\r\n }\r\n var trident = ua.indexOf('Trident/');\r\n if (trident > 0) {\r\n // IE 11 => return version number\r\n var rv = ua.indexOf('rv:');\r\n return ['IE', parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10)];\r\n }\r\n var edge = ua.indexOf('Edge/');\r\n if (edge > 0) {\r\n // Edge (IE 12+) => return version number\r\n return ['IE', parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10)];\r\n }\r\n // other browser\r\n return false;\r\n }\r\n\r\n function _getOperatingSystem() {\r\n var userAgent = window.navigator.userAgent,\r\n platform = window.navigator.platform,\r\n macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'],\r\n windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'],\r\n iosPlatforms = ['iPhone', 'iPad', 'iPod'],\r\n os = null;\r\n if (macosPlatforms.indexOf(platform) !== -1) {\r\n os = 'mac-os';\r\n } else if (iosPlatforms.indexOf(platform) !== -1) {\r\n os = 'ios';\r\n } else if (windowsPlatforms.indexOf(platform) !== -1) {\r\n os = 'windows';\r\n } else if (/Android/.test(userAgent)) {\r\n os = 'android';\r\n } else if (!os && /Linux/.test(platform)) {\r\n os = 'linux';\r\n }\r\n return os;\r\n }\r\n\r\n var init = function() {\r\n\r\n var nAgt = navigator.userAgent;\r\n var browserName = navigator.appName;\r\n\r\n var fullVersion = '' + parseFloat(navigator.appVersion);\r\n var majorVersion = parseInt(navigator.appVersion, 10);\r\n var nameOffset, verOffset, ix;\r\n\r\n // // In Opera 15+, the true version is after \"OPR/\"\r\n if ((verOffset = nAgt.indexOf('OPR/')) != -1) {\r\n browserName = 'Opera';\r\n fullVersion = nAgt.substring(verOffset + 4);\r\n }\r\n // // In older Opera, the true version is after \"Opera\" or after \"Version\"\r\n else if ((verOffset = nAgt.indexOf('Opera')) != -1) {\r\n browserName = 'Opera';\r\n fullVersion = nAgt.substring(verOffset + 6);\r\n if ((verOffset = nAgt.indexOf('Version')) != -1) fullVersion = nAgt.substring(verOffset + 8);\r\n }\r\n // // In MSIE, the true version is after \"MSIE\" in userAgent\r\n else if ((verOffset = nAgt.indexOf('MSIE')) != -1) {\r\n browserName = 'Microsoft Internet Explorer';\r\n fullVersion = nAgt.substring(verOffset + 5);\r\n }\r\n // // In Chrome, the true version is after \"Chrome\"\r\n else if ((verOffset = nAgt.indexOf('Chrome')) != -1) {\r\n browserName = 'Chrome';\r\n fullVersion = nAgt.substring(verOffset + 7);\r\n }\r\n // // In Safari, the true version is after \"Safari\" or after \"Version\"\r\n else if ((verOffset = nAgt.indexOf('Safari')) != -1) {\r\n browserName = 'Safari';\r\n fullVersion = nAgt.substring(verOffset + 7);\r\n if ((verOffset = nAgt.indexOf('Version')) != -1) fullVersion = nAgt.substring(verOffset + 8);\r\n }\r\n // // In Firefox, the true version is after \"Firefox\"\r\n else if ((verOffset = nAgt.indexOf('Firefox')) != -1) {\r\n browserName = 'Firefox';\r\n fullVersion = nAgt.substring(verOffset + 8);\r\n }\r\n // // In most other browsers, \"name/version\" is at the end of userAgent\r\n else if ((nameOffset = nAgt.lastIndexOf(' ') + 1) < (verOffset = nAgt.lastIndexOf('/'))) {\r\n browserName = nAgt.substring(nameOffset, verOffset);\r\n fullVersion = nAgt.substring(verOffset + 1);\r\n if (browserName.toLowerCase() == browserName.toUpperCase()) {\r\n browserName = navigator.appName;\r\n }\r\n }\r\n // // trim the fullVersion string at semicolon/space if present\r\n if ((ix = fullVersion.indexOf(';')) != -1) fullVersion = fullVersion.substring(0, ix);\r\n if ((ix = fullVersion.indexOf(' ')) != -1) fullVersion = fullVersion.substring(0, ix);\r\n\r\n // majorVersion = parseInt('' + fullVersion, 10);\r\n if (isNaN(majorVersion)) {\r\n fullVersion = '' + parseFloat(navigator.appVersion);\r\n majorVersion = parseInt(navigator.appVersion, 10);\r\n }\r\n var IE = _detectIE();\r\n if (IE) {\r\n browserName = IE[0];\r\n majorVersion = IE[1];\r\n }\r\n var Browser = browserName ? browserName.toLowerCase() : browserName;\r\n var Version = majorVersion;\r\n var OS = _getOperatingSystem();\r\n var isTouch = 'ontouchstart' in document.documentElement;\r\n var body = document.querySelector('body');\r\n body.classList.add(Browser);\r\n body.classList.add(Browser + '-' + Version);\r\n body.classList.add(OS);\r\n body.classList.add((isTouch ? 'has' : 'no') + '-touch');\r\n };\r\n\r\n return {\r\n init: init\r\n };\r\n};","import document from 'document';\r\n\r\nconst lastMenuItem = document.querySelector('#zen-account-menu .zen-login-cta__menu-list .zen-login-cta__menu-item:last-child button');\r\nconst firstMenuItem = document.querySelector('#zen-account-menu .zen-login-cta__menu-list .zen-login-cta__menu-item a');\r\n\r\nexport const zenLoginCtaMenu = (selectors) => {\r\n const menu = document.querySelector(selectors.menu);\r\n const button = document.querySelector(selectors.button);\r\n\r\n const _toggleMenuVisibility = () => {\r\n menu.classList.toggle(selectors.menuOpen);\r\n };\r\n\r\n const _toggleOptionsVisibility = function (e) {\r\n e.stopPropagation();\r\n\r\n document.addEventListener('keydown', _handleKeyPress);\r\n document.addEventListener('click', _handleClick);\r\n\r\n const isOpen = button.classList.contains(selectors.buttonOpen);\r\n\r\n if (isOpen) {\r\n this.classList.add(selectors.buttonClosed);\r\n this.classList.remove(selectors.buttonOpen);\r\n this.setAttribute(\"aria-expanded\", \"false\")\r\n } else {\r\n this.classList.remove(selectors.buttonClosed);\r\n this.classList.add(selectors.buttonOpen);\r\n this.setAttribute(\"aria-expanded\", \"true\")\r\n }\r\n _toggleMenuVisibility();\r\n };\r\n\r\n const _closeLoginOptions = () => {\r\n if (menu.classList.contains(selectors.menuOpen.replace(/\\./g,''))) {\r\n menu.classList.remove(selectors.menuOpen);\r\n button.classList.remove(selectors.buttonOpen);\r\n button.classList.add(selectors.buttonClosed);\r\n button.setAttribute(\"aria-expanded\", \"false\");\r\n }\r\n\r\n document.removeEventListener('click', _handleClick);\r\n document.removeEventListener('keydown', _handleKeyPress);\r\n };\r\n\r\n const _toggleMobileOptionsVisibility = function (e) {\r\n e.stopPropagation();\r\n\r\n const isHidden = menu.getAttribute('aria-hidden') === 'true';\r\n\r\n _toggleMenuVisibility();\r\n\r\n if (isHidden) {\r\n button.classList.remove(selectors.mobileButtonClosed);\r\n button.classList.add(selectors.mobileButtonOpen);\r\n menu.setAttribute('aria-expanded', 'true');\r\n menu.setAttribute('aria-hidden', 'false');\r\n } else {\r\n button.classList.add(selectors.mobileButtonClosed);\r\n button.classList.remove(selectors.mobileButtonOpen);\r\n menu.setAttribute('aria-expanded', 'false');\r\n menu.setAttribute('aria-hidden', 'true');\r\n }\r\n };\r\n\r\n const _closeMobileLoginOptions = () => {\r\n if (menu.classList.contains(selectors.menuOpen.replace(/\\./g,''))) {\r\n menu.classList.remove(selectors.menuOpen);\r\n document.querySelector(selectors.mobileButton).classList.remove(selectors.mobileButtonOpen);\r\n document.querySelector(selectors.mobileButton).classList.add(selectors.mobileButtonClosed);\r\n menu.setAttribute('aria-expanded', 'false');\r\n menu.setAttribute('aria-hidden', 'true');\r\n }\r\n };\r\n\r\n const _loopNavigation = (e) => {\r\n if (e.key == 'Tab' && !e.shiftKey) {\r\n e.preventDefault();\r\n firstMenuItem.focus();\r\n }\r\n }\r\n\r\n const _loopNavigationReverse = (e) => {\r\n if (e.key == 'Tab' && e.shiftKey) {\r\n e.preventDefault();\r\n lastMenuItem.focus();\r\n }\r\n }\r\n\r\n // Handle pressing ESC on the login menu\r\n const _handleKeyPress = (e) => {\r\n if (e.key === 'Escape') {\r\n _closeLoginOptions.call(this, e);\r\n button.focus();\r\n }\r\n }\r\n\r\n // Handle clicking off the login menu\r\n const _handleClick = (e) => {\r\n if (!menu.contains(e.target) && !button.contains(e.target)) {\r\n _closeLoginOptions.call(this, e);\r\n }\r\n }\r\n\r\n const _bindEvents = function (el) {\r\n el.addEventListener('click', _toggleOptionsVisibility);\r\n lastMenuItem && lastMenuItem.addEventListener(\"keydown\", _loopNavigation)\r\n firstMenuItem && firstMenuItem.addEventListener(\"keydown\", _loopNavigationReverse)\r\n };\r\n\r\n const _bindMobileEvents = function (el) {\r\n el.onclick = _toggleMobileOptionsVisibility;\r\n document.onclick = _closeMobileLoginOptions;\r\n };\r\n\r\n return {\r\n bindEvents: _bindEvents,\r\n bindMobileEvents: _bindMobileEvents,\r\n };\r\n};\r\n","\r\nexport const zenLoginSelectors = {\r\n button: '.zen-login-cta__options',\r\n mobileButton: '.zen-login-cta__options_mobile',\r\n menu: '.zen-login-cta__menu',\r\n menuOpen: 'zen-login-cta__menu--open',\r\n menuState: 'closed',\r\n buttonOpen: 'zen-login-cta__options--open',\r\n buttonClosed: 'zen-login-cta__options--closed',\r\n mobileButtonOpen: 'zen-login-cta__options_mobile--open',\r\n mobileButtonClosed: 'zen-login-cta__options_mobile--closed'\r\n};\r\n\r\nexport const loginSelectors = {\r\n button: '.login-cta__options',\r\n mobileButton: '.login-cta__options_mobile',\r\n menu: '.login-cta__menu',\r\n menuOpen: 'login-cta__menu--open',\r\n menuState: 'closed',\r\n buttonOpen: 'login-cta__options--open',\r\n buttonClosed: 'login-cta__options--closed',\r\n mobileButtonOpen: 'login-cta__options_mobile--open',\r\n mobileButtonClosed: 'login-cta__options_mobile--closed'\r\n};","import document from 'document';\r\n\r\nimport { zenLoginCtaMenu } from './zen-login-cta';\r\nimport { loginSelectors as selectors } from './zen-login-selectors';\r\n\r\nexport const loginMenu = () => {\r\n const init = () => {\r\n const button = document.querySelector(selectors.button);\r\n const ctaMenu = zenLoginCtaMenu(selectors);\r\n \r\n if (button !== null) {\r\n ctaMenu.bindEvents(button);\r\n }\r\n\r\n const mobileButton = document.querySelector(selectors.mobileButton);\r\n if (mobileButton !== null) {\r\n ctaMenu.bindMobileEvents(mobileButton);\r\n }\r\n };\r\n\r\n return {\r\n init: init\r\n };\r\n};\r\n","export const marketingPreferences = () => {\r\n const selectedClass = 'selected';\r\n\r\n const createNewEvent = eventName => {\r\n let event;\r\n if (typeof (Event) === 'function') {\r\n event = new Event(eventName, { bubbles: true });\r\n } else {\r\n event = document.createEvent('Event');\r\n event.initEvent(eventName, true, true);\r\n }\r\n return event;\r\n };\r\n\r\n const unselectBoth = toggles => {\r\n toggles.forEach(toggle => {\r\n toggle.classList.remove(selectedClass);\r\n });\r\n };\r\n\r\n const handleToggle = (buttons, field) => event => {\r\n event.preventDefault();\r\n\r\n const button = event.currentTarget;\r\n\r\n const switchingOn = !button.classList.contains(selectedClass);\r\n\r\n unselectBoth(buttons);\r\n\r\n if (switchingOn) {\r\n button.classList.add(selectedClass);\r\n }\r\n\r\n field.value = switchingOn ? button.getAttribute('data-val') : null;\r\n field.dispatchEvent(createNewEvent('input'));\r\n };\r\n\r\n const init = component => {\r\n\r\n const toggleSwitches = component.querySelectorAll('.toggle-switch');\r\n\r\n [...toggleSwitches].forEach(toggle => {\r\n\r\n const field = toggle.querySelector('.js-field');\r\n const buttons = [...toggle.querySelectorAll('[data-val]')];\r\n \r\n buttons.forEach(button => {\r\n button.addEventListener('click', handleToggle(buttons, field));\r\n });\r\n });\r\n };\r\n\r\n return {\r\n init,\r\n selector: '[data-component=\"marketing-preferences\"]'\r\n };\r\n};","export const revealPassword = () => {\r\n \r\n const toggleInputType = input => {\r\n const type = input.type === 'password' ? 'text' : 'password';\r\n input.setAttribute('type', type);\r\n };\r\n\r\n const init = component => {\r\n const revelPasswordIcon = component.querySelector('.js-reveal-password');\r\n const passwordField = component.querySelector('.js-password-field');\r\n\r\n if (revelPasswordIcon && passwordField) {\r\n revelPasswordIcon.addEventListener('click', () => {\r\n toggleInputType(passwordField);\r\n });\r\n }\r\n };\r\n\r\n return {\r\n init,\r\n selector: '[data-component=\"password-reveal\"]'\r\n };\r\n};\r\n","import window from 'window';\r\n\r\nexport const signInLink = () => {\r\n\r\n const init = (link) => {\t \r\n const getLoginPageUrl = () => {\r\n const href = link.getAttribute('href');\t\r\n return href.indexOf('redirectUrl') > 0 ? href.split('?')[0] : href;\r\n }\r\n \r\n link.addEventListener('click', () => {\r\n const loginPageUrl = getLoginPageUrl();\r\n \r\n const currentPageUrl = window.location.pathname + window.location.search;\r\n const redirectParameter = `redirectUrl=${encodeURIComponent(currentPageUrl)}`;\r\n \r\n const separator = loginPageUrl.indexOf('?') < 0 ? '?' : '&';\r\n const newUrl = `${loginPageUrl}${separator}${redirectParameter}`;\r\n\r\n link.setAttribute('href', newUrl);\r\n });\r\n };\r\n\r\n return {\r\n init: init,\r\n selector: '[data-component=\"sign-in-link\"]'\r\n };\r\n};\r\n","import document from 'document';\r\n\r\nexport const cookieBanner = () => {\r\n const selectors = {\r\n root: '.js-cookie-banner',\r\n closeBtn: '.js-close-and-store-cookie',\r\n cookieBannerVisible: 'cookie-banner--visible'\r\n };\r\n\r\n const config = {\r\n cookieName: 'cookiePolicyAccepted'\r\n };\r\n\r\n const _createCookie = (name, value, days) => {\r\n var expires;\r\n if (days) {\r\n var date = new Date();\r\n date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));\r\n expires = '; expires=' + date.toGMTString();\r\n }\r\n else {\r\n expires = '';\r\n }\r\n document.cookie = name + '=' + value + expires + '; path=/';\r\n };\r\n\r\n const _readCookie = (name) => {\r\n var cookieName = name + '=';\r\n var storedCookies = document.cookie.split(';');\r\n var filteredCookie = storedCookies.filter(function (cookie) {\r\n if (cookie.indexOf(cookieName) != -1) {\r\n return cookieName;\r\n }\r\n });\r\n return filteredCookie;\r\n };\r\n\r\n const _showCookieBanner = (el) => {\r\n el.classList.add(selectors.cookieBannerVisible);\r\n };\r\n\r\n const _hideCookieBanner = (el) => {\r\n el.closest(selectors.root).classList.remove(selectors.cookieBannerVisible);\r\n };\r\n\r\n const _bindEvents = () => {\r\n const button = document.querySelectorAll(selectors.closeBtn)[0];\r\n button.addEventListener('click', (e) => {\r\n e.preventDefault();\r\n _createCookie(config.cookieName, 'true', 30);\r\n _hideCookieBanner(e.currentTarget);\r\n });\r\n };\r\n\r\n const init = component => {\r\n if (!_readCookie(config.cookieName).length) {\r\n _showCookieBanner(component);\r\n _bindEvents();\r\n }\r\n };\r\n\r\n return {\r\n init: init,\r\n selector: selectors.root\r\n };\r\n};","// example from https://davidwalsh.name/pubsub-javascript\r\nexport const eventBus = (() => {\r\n\r\n var topics = {};\r\n\r\n var subscribe = function (topic, listener) {\r\n if (!topics.hasOwnProperty.call(topics, topic)) {\r\n topics[topic] = [];\r\n }\r\n topics[topic].push(listener);\r\n };\r\n\r\n var unsubscribe = function (topic) {\r\n delete topics[topic];\r\n };\r\n\r\n // fire the subscribed event\r\n var publish = function (topic, options) {\r\n // manages event queue\r\n if (!topics.hasOwnProperty.call(topics, topic)) {\r\n return;\r\n }\r\n\r\n topics[topic].forEach(function (item) {\r\n item(options != undefined ? options : {});\r\n });\r\n };\r\n\r\n return {\r\n subscribe: subscribe,\r\n unsubscribe: unsubscribe,\r\n publish: publish\r\n };\r\n})();","// debounce example function https://remysharp.com/2010/07/21/throttling-function-calls#comment-497362\r\nexport const debounce = (fn, delay) => {\r\n let timer = null;\r\n return function () {\r\n const context = this;\r\n const args = arguments;\r\n clearTimeout(timer);\r\n timer = setTimeout(() => {\r\n fn.apply(context, args);\r\n }, delay);\r\n };\r\n};","import window from 'window';\r\nimport document from 'document';\r\n\r\nexport const scrollTop = (() => {\r\n\r\n let activeAnimation;\r\n\r\n const stop = () => {\r\n activeAnimation = false;\r\n };\r\n \r\n /**\r\n * Simulates the animated scrollTop of jQuery. Used when css3:false or scrollBar:true or autoScrolling:false\r\n */\r\n const scrollTo = (params) => {\r\n const element = typeof params.element !== 'undefined' ? params.element : window;\r\n const to = params.to;\r\n const duration = typeof params.duration !== 'undefined' ? params.duration : 700;\r\n const callback = typeof params.callback !== 'undefined' ? params.callback : null;\r\n const easing = typeof params.easing !== 'undefined' ? params.easing : Math.easeInOutCubic;\r\n\r\n const start = element!==window ? element.scrollTop : (window.pageYOffset || document.documentElement.scrollTop) - (document.documentElement.clientTop || 0);\r\n const change = to - start;\r\n let currentTime = 0;\r\n const increment = 16; //same amount of milliseconds as requestAnimationFrame\r\n activeAnimation = true;\r\n\r\n const animateScroll = () => {\r\n\r\n //in case we want to stop it from other function whenever we want\r\n if (activeAnimation) {\r\n currentTime += increment;\r\n const easingValue = duration ? easing(currentTime, start, change, duration) : to;\r\n element.scrollTo(0, easingValue);\r\n\r\n if (currentTime < duration) {\r\n setTimeout(animateScroll, increment);\r\n } else if (callback){\r\n callback();\r\n }\r\n }else if (currentTime < duration && callback){\r\n callback();\r\n }\r\n };\r\n\r\n animateScroll();\r\n }\r\n\r\n return {\r\n scrollTo,\r\n stop\r\n };\r\n\r\n})();","import window from 'window';\r\nimport document from 'document';\r\nimport { scrollTop } from './scroll-top';\r\n\r\nexport const scroller = (() => {\r\n const isSmoothScrollSupported = 'scrollBehavior' in document.documentElement.style;\r\n const main = document.querySelector('main');\r\n\r\n const getScrollOffset = () => main ? main.offsetTop : 0;\r\n\r\n const scrollToElement = (element, callback, offsetTop = 0) => {\r\n const scrollOffset = getScrollOffset();\r\n const noop = () => { };\r\n\r\n if (isSmoothScrollSupported) {\r\n function scrollTo(offset, callback = noop) {\r\n const onScroll = function () {\r\n const scrollTop = window.scrollTop || window.pageYOffset;\r\n\r\n // run callback when current position has further scroll\r\n if (scrollTop <= offset) {\r\n window.removeEventListener('scroll', onScroll);\r\n callback();\r\n }\r\n }\r\n window.addEventListener('scroll', onScroll);\r\n onScroll();\r\n window.scrollTo({\r\n top: offset,\r\n behavior: 'smooth'\r\n });\r\n }\r\n\r\n const topOfElement = element.getBoundingClientRect().top + (window.scrollY || window.pageYOffset) - scrollOffset;\r\n\r\n scrollTo(topOfElement - offsetTop, callback);\r\n\r\n } else {\r\n const to = document.querySelector(element).offset().top - scrollOffset - offsetTop;\r\n scrollTop.scrollTo({\r\n to: to,\r\n easing: window.easings.easeOutBounce,\r\n duration: 500,\r\n callback: callback\r\n });\r\n }\r\n };\r\n\r\n const isScrolledIntoView = (element, offsetTop = 0) => {\r\n const rect = element.getBoundingClientRect();\r\n const scrollOffset = getScrollOffset() + offsetTop;\r\n const isInView = (rect.top >= scrollOffset) && (rect.bottom <= window.innerHeight);\r\n return isInView;\r\n };\r\n\r\n return {\r\n scrollToElement,\r\n isScrolledIntoView\r\n };\r\n})();","export const hasAttribute = (field, name) => field.getAttribute(name) != null;\r\n\r\nconst hasClass = (field, className) => field.classList.contains(className);\r\n\r\nexport const hasPattern = field => hasAttribute(field, 'pattern');\r\n\r\nexport const isRequired = field => hasAttribute(field, 'required');\r\n\r\nexport const isGroupRequired = field => hasAttribute(field, 'group-required');\r\n\r\nexport const accessibleInvalid = field => hasAttribute(field, 'aria-invalid');\r\n\r\nexport const isReadOnly = field => hasAttribute(field, 'readonly') || field.readOnly;\r\n\r\nexport const isDisabled = field => hasAttribute(field, 'disabled') || field.disabled;\r\n\r\nexport const hasMatching = field => hasAttribute(field, 'data-matching-text');\r\n\r\nexport const hasOptionValidate = field => hasAttribute(field, 'data-runtime-validate');\r\n\r\nexport const hasRuntimeValidate = field => hasClass(field, 'js-runtime-validate');\r\n\r\nexport const hasBlankAndPattern = field => hasPattern(field) && hasClass(field, 'js-validation-blank');\r\n\r\nexport const hasPatternOnlyValidation = field => hasAttribute(field, 'pattern-only-validation');\r\n\r\nexport const shouldBeValidated = field => {\r\n if (!hasPatternOnlyValidation(field) && !isRequired(field) && !field.value && !hasBlankAndPattern(field) && !isGroupRequired(field)) {\r\n return false;\r\n } else {\r\n return (!isReadOnly(field) &&\r\n !isDisabled(field) &&\r\n (hasPattern(field) || hasMatching(field) || isRequired(field)) || isGroupRequired(field));\r\n }\r\n};\r\n\r\nexport const matchesPattern = field => {\r\n try { \r\n if (!hasPattern(field)) {\r\n return false;\r\n } else {\r\n var pattern = field.getAttribute('pattern');\r\n\r\n return (field.value || hasBlankAndPattern(field)) && new RegExp(pattern).test(field.value);\r\n }\r\n }\r\n catch (error) {\r\n console.error(error.message);\r\n }\r\n};\r\n\r\nexport const matchingText = field => {\r\n try {\r\n if (!hasMatching(field)) {\r\n return false;\r\n } else {\r\n const fieldToMatch = field.getAttribute('data-matching-text');\r\n const matchingElement = document.getElementById(fieldToMatch);\r\n\r\n return matchingElement === null || field.value === matchingElement.value;\r\n }\r\n }\r\n catch (error) {\r\n console.error(error.message);\r\n }\r\n};\r\n\r\nexport const hasMinimumOptionsChecked = field => {\r\n var nodeList = document.getElementsByName(field.name);\r\n\r\n var minRequiredOptions = field.getAttribute('min-required');\r\n\r\n if (isNaN(minRequiredOptions)) {\r\n return false;\r\n }\r\n\r\n if (nodeList && nodeList.length > 0) {\r\n var group = Array.from(nodeList);\r\n\r\n var checkedItems = group.filter(function (element) {\r\n return element.checked;\r\n });\r\n\r\n return checkedItems.length >= minRequiredOptions;\r\n }\r\n\r\n return false;\r\n};\r\n\r\nexport const disableSubmitButton = btn => {\r\n\r\n if (btn) {\r\n btn.setAttribute(\"disabled\", \"true\");\r\n }\r\n};","export const Field = field => {\r\n const visible = 'visible';\r\n\r\n const formGroup = field.closest('.js-field-validate , [data-validation-role=\"js-field-validate\"]');\r\n const fieldAsterik = field.parentNode.querySelector('.form-field-group__asterik');\r\n const errorMessage = formGroup && formGroup.querySelector('.js-field-validate--error');\r\n\r\n if (!errorMessage) {\r\n console.warn(\"Error message missing for\", field);\r\n }\r\n\r\n return {\r\n element: field,\r\n formGroup: formGroup,\r\n showError() {\r\n if (field.type === \"checkbox\" || field.type === \"radio\") {\r\n this.toggleForGroup(true);\r\n } else {\r\n field.setAttribute('aria-invalid', 'true');\r\n }\r\n if (errorMessage) {\r\n const adjacentInput = errorMessage.previousElementSibling;\r\n const inputName = adjacentInput.getAttribute('name');\r\n const errorId = `${inputName}-error`;\r\n errorMessage.classList.add(visible);\r\n adjacentInput.setAttribute('aria-describedby', errorId);\r\n errorMessage.setAttribute('id', errorId);\r\n }\r\n },\r\n hideError() {\r\n if (field.type === \"checkbox\" || field.type === \"radio\") {\r\n this.toggleForGroup(false);\r\n } else {\r\n field.removeAttribute('aria-invalid');\r\n }\r\n if (errorMessage) {\r\n const adjacentInput = errorMessage.previousElementSibling;\r\n const inputName = adjacentInput.getAttribute('name');\r\n const errorId = `${inputName}-error`;\r\n errorMessage.classList.remove(visible);\r\n adjacentInput.removeAttribute('aria-describedby', errorId);\r\n errorMessage.removeAttribute('id', errorId);\r\n }\r\n },\r\n setEagerValidation() {\r\n field.setAttribute('data-eager-validate', '');\r\n },\r\n setRequired() {\r\n field.setAttribute('required', 'required');\r\n fieldAsterik && fieldAsterik.classList.remove('hide');\r\n },\r\n removeRequired() {\r\n field.removeAttribute('required');\r\n fieldAsterik && fieldAsterik.classList.add('hide');\r\n },\r\n isReactiveValidationEnabled() {\r\n return field.hasAttribute('data-reactive-validate');\r\n },\r\n isEagerValidationEnabled() {\r\n return field.hasAttribute('data-eager-validate');\r\n },\r\n focus() {\r\n field.focus();\r\n },\r\n toggleForGroup(toggleOn) {\r\n\r\n var nodeList = document.getElementsByName(field.name);\r\n\r\n if (nodeList && nodeList.length > 0) {\r\n if (toggleOn) {\r\n [...nodeList].map(x => x.setAttribute('aria-invalid', 'true'));\r\n } else {\r\n [...nodeList].map(x => x.removeAttribute('aria-invalid'));\r\n\r\n }\r\n }\r\n }\r\n };\r\n}","import { scroller } from '../../utilities/scroller';\r\nimport { eventBus } from '../../foundation/eventBus';\r\nimport { shouldBeValidated, isRequired, hasPattern, hasAttribute, matchesPattern, hasMatching, matchingText, isGroupRequired, hasMinimumOptionsChecked, disableSubmitButton } from '../../foundation/form/validation';\r\nimport { Field } from './field';\r\n\r\nexport const events = {\r\n formSubmitted: 'formSubmitted',\r\n validate: 'validate',\r\n};\r\n\r\nexport const enableFormSubmission = () => {\r\n\r\n const Form = function (form) {\r\n form.setAttribute('novalidate', '');\r\n\r\n let isSubmitted = false;\r\n\r\n // Check if input needs validating or not\r\n const shouldSkipValidation = (field) => !field || !field.element;\r\n\r\n function getFields() {\r\n const allFields = [...form.querySelectorAll('input, textarea, select')];\r\n\r\n return allFields.filter(field => shouldBeValidated(field))\r\n .map(field => Field(field));\r\n }\r\n\r\n const isValidField = field => {\r\n if (shouldSkipValidation(field)) {\r\n return;\r\n }\r\n\r\n let isInvalid;\r\n\r\n const element = field.element;\r\n\r\n if (hasAttribute(element, 'skip-validation')) {\r\n return true;\r\n } else if (element.type === 'checkbox' || element.type === 'radio') {\r\n isInvalid = (isRequired(element) && !element.checked) || (isGroupRequired(element) && !hasMinimumOptionsChecked(element));\r\n } else if(hasAttribute(element, 'pattern-only-validation')) {\r\n if(isRequired(element)) {\r\n isInvalid = !element.value.trim() || (hasPattern(element) && !matchesPattern(element));\r\n } else {\r\n isInvalid = element.value.trim() ? (hasPattern(element) && !matchesPattern(element)) : false;\r\n }\r\n } else {\r\n isInvalid = (isRequired(element) && !element.value.trim()) || (hasPattern(element) && !matchesPattern(element)) || (hasMatching(element) && !matchingText(element));\r\n }\r\n\r\n return !isInvalid;\r\n };\r\n\r\n const isValidForm = () => getFields().every(isValidField);\r\n\r\n const validateField = field => {\r\n if (shouldSkipValidation(field)) {\r\n return;\r\n }\r\n\r\n const isValid = isValidField(field);\r\n\r\n if (isValid) {\r\n field.hideError();\r\n } else {\r\n field.showError();\r\n field.setEagerValidation();\r\n }\r\n\r\n return isValid;\r\n };\r\n\r\n const validateForm = (options = {}) => {\r\n const isValid = isValidForm();\r\n\r\n if (!options.ignoreErrors) {\r\n getFields().forEach(field => {\r\n validateField(field);\r\n });\r\n }\r\n\r\n return isValid;\r\n };\r\n\r\n function submitEvent(event) {\r\n isSubmitted = true;\r\n const isValidForm = validateForm();\r\n\r\n eventBus.publish(events.formSubmitted, { isValidForm: isValidForm, id : form.id, event: event });\r\n\r\n if (!isValidForm) {\r\n event.preventDefault();\r\n const field = getFields().find(field => !isValidField(field));\r\n const element = field.formGroup;\r\n\r\n const paddingTop = 8;\r\n\r\n if (element && !scroller.isScrolledIntoView(element, paddingTop)) {\r\n scroller.scrollToElement(element, field.focus, paddingTop);\r\n } else {\r\n field.focus();\r\n }\r\n } else {\r\n var button = document.querySelector(\"[data-submit-disable]\");\r\n disableSubmitButton(button);\r\n }\r\n }\r\n\r\n const bindEvents = () => {\r\n getFields().forEach(field => {\r\n if (shouldSkipValidation(field)) {\r\n return;\r\n }\r\n\r\n const element = field.element;\r\n const eventType = ['checkbox', 'radio', 'select-one'].includes(element.type) ? 'change' : 'blur';\r\n\r\n const handler = event => {\r\n const shouldRun = ((event.type === 'blur' || event.type === 'change') && !field.isEagerValidationEnabled() && (isSubmitted || Boolean(element.value))) || field.isEagerValidationEnabled();\r\n\r\n if (shouldRun) {\r\n validateField(field);\r\n }\r\n\r\n if (eventType === 'blur' && element && element.type === 'date' && element.value && element.max) {\r\n let date = new Date(element.value).toISOString().slice(0, 10);\r\n let today = new Date().toISOString().slice(0, 10);\r\n if (date > today) {\r\n element.value = today;\r\n }\r\n }\r\n };\r\n\r\n element.addEventListener(eventType, handler);\r\n\r\n if (eventType === 'blur') {\r\n element.addEventListener('input', handler);\r\n }\r\n });\r\n\r\n form.onsubmit = submitEvent;\r\n };\r\n\r\n return {\r\n init() {\r\n bindEvents();\r\n eventBus.subscribe(events.validate, validateForm);\r\n }\r\n };\r\n };\r\n\r\n const init = component => {\r\n const form = new Form(component);\r\n form.init();\r\n };\r\n\r\n return {\r\n init: init,\r\n selector: '[data-component=\"form\"] .js-form, .form .js-form'\r\n };\r\n};","\r\nconst ajaxRequest = (() => {\r\n\r\n const post = (url, data, contentType, success) => {\r\n return _makeRequest('POST', url, data, contentType, success);\r\n }\r\n\r\n const get = (url, data, contentType, success) => {\r\n return _makeRequest('GET', url, data, contentType, success);\r\n }\r\n\r\n const _makeRequest = (httpMethod, url, data, contentType, success) => {\r\n const params = typeof data == 'string' ? data : Object.keys(data).map(\r\n (k) => { return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }\r\n ).join('&');\r\n const xhr = new XMLHttpRequest();\r\n xhr.open(httpMethod, url);\r\n xhr.onreadystatechange = function() {\r\n if (xhr.readyState>3 && xhr.status==200) { success(xhr.responseText); }\r\n };\r\n xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r\n xhr.setRequestHeader('Content-Type', contentType || 'application/x-www-form-urlencoded; charset=UTF-8');\r\n xhr.send(params);\r\n return xhr;\r\n }\r\n\r\n const jsonp = (url, dataObj, timeout) => {\r\n const params = typeof dataObj == 'string' ? dataObj : Object.keys(dataObj).map(\r\n (k) => { return encodeURIComponent(k) + '=' + encodeURIComponent(dataObj[k]) }\r\n ).join('&');\r\n \r\n return new Promise((resolve, reject) => { \r\n timeout = timeout || 10000; // default timeout \r\n const callbackName=\"jsonp\"+(+new Date());\r\n const head = document.getElementsByTagName('head')[0] || document.documentElement;\r\n const script = document.createElement('script');\r\n script.src =`${url}${url.includes('?')?'&':'?'}${params}&callback=${callbackName}&v=1.0`;\r\n script.async = \"true\";\r\n \r\n window[callbackName] = (data) => {\r\n cleanUp();\r\n resolve(data);\r\n }\r\n \r\n script.onerror = () => {\r\n cleanUp();\r\n reject( Error(\"Network error loading \" + script.src) );\r\n }\r\n \r\n head.appendChild(script);\r\n const timeoutFunction = setTimeout(function() {\r\n cleanUp();\r\n reject( Error(\"Request to \" + url + \" failed to execute callback after \" + timeout + \"ms.\") ) \r\n }, timeout);\r\n \r\n const cleanUp = () => {\r\n timeoutFunction && clearTimeout(timeoutFunction);\r\n window[callbackName] && delete window[callbackName];\r\n script && head.removeChild(script);\r\n }\r\n \r\n } );\r\n }\r\n\r\n const fetchRequest = async (url, data, method, success) => {\r\n return await fetch(\r\n url,\r\n {\r\n method: method,\r\n body: data\r\n })\r\n .then((response) => {\r\n success(response); \r\n }) \r\n .catch((err) => {\r\n throw new Error(`Request to ${url} failed!`, err);\r\n });\r\n }\r\n\r\n return {\r\n post,\r\n get,\r\n jsonp,\r\n fetch: fetchRequest,\r\n };\r\n})();\r\n\r\nexport { ajaxRequest };","import document from 'document';\r\nimport { eventBus } from '../../foundation/eventBus';\r\nimport { debounce } from '../../utilities/debounce';\r\nimport { events as formEvents } from '../../feature/form/enable-form';\r\nimport { ajaxRequest } from '../../utilities/ajax';\r\n\r\nexport const bankAccountLookup = () => {\r\n // API documentation https://www.pcapredict.com/support/webservice/bankaccountvalidation/interactive/validate/2/\r\n const selectors = {\r\n root: '.form',\r\n button: '.find-bank-account-btn',\r\n field: '.form-field-group__input',\r\n sortCodeField: '.js-sort-code',\r\n accountNumberField: '.js-account-number',\r\n errorMessage: '.form-field-group__error',\r\n resultError: '.find-bank-account-error',\r\n postcodeError: '.bank-postcode-error',\r\n continueBtn: '.form-button-group__submit-btn',\r\n hiddenFieldValidator: '.js-bank-valid'\r\n };\r\n\r\n let config = {}; // global object that gets mapped to later in the code\r\n\r\n const _makeRequest = (service, dataObj, func) => {\r\n\r\n const url = service.url + '?' + Object.keys(dataObj).map(\r\n (k) => { return encodeURIComponent(k) + '=' + encodeURIComponent(dataObj[k]) }\r\n ).join('&');\r\n ajaxRequest.get(url, dataObj, 'application/json', func);\r\n };\r\n\r\n const _populateAddress = (data) => {\r\n\r\n if (data != null && data != {}) {\r\n\r\n const fields = Array.from(document.querySelector(selectors.root).querySelectorAll(selectors.field));\r\n const addressKeys = Object.keys(data);\r\n\r\n function populateField(key) {\r\n const field = fields.filter((item) => {\r\n const itemAttr = item.getAttribute('data-address-key');\r\n if (itemAttr === key) {\r\n return itemAttr;\r\n }\r\n })[0];\r\n\r\n if (field) {\r\n field.value = data[field.getAttribute('data-address-key')];\r\n field.removeAttribute('aria-invalid');\r\n field.removeAttribute(selectors.errorMessage);\r\n field.classList.remove('visible');\r\n }\r\n }\r\n\r\n addressKeys.forEach(populateField);\r\n\r\n _validatePageBeacon();\r\n }\r\n };\r\n\r\n const _clearBankAddressFields = (data) => {\r\n const fields = Array.from(document.querySelector(selectors.root).querySelectorAll(selectors.field));\r\n const addressKeys = Object.keys(data);\r\n\r\n function clearField(key) {\r\n const field = fields.filter((item) => {\r\n const itemAttr = item.getAttribute('data-address-key');\r\n if (itemAttr === key) {\r\n return itemAttr;\r\n }\r\n })[0];\r\n\r\n if (field) {\r\n field.value = '';\r\n }\r\n }\r\n\r\n addressKeys.forEach(clearField);\r\n }\r\n\r\n const _validatePageBeacon = () => {\r\n eventBus.publish(formEvents.validate);\r\n };\r\n\r\n const _validBankDetails = () => {\r\n document.querySelector(selectors.resultError).classList.remove('visible');\r\n };\r\n\r\n const _invalidBankDetails = () => {\r\n document.querySelector(selectors.resultError).classList.add('visible');\r\n };\r\n\r\n const _updateHiddenCheckbox = (state) => {\r\n document.querySelector(selectors.hiddenFieldValidator).check = state;\r\n };\r\n\r\n const _isHiddenCheckboxChecked = () => {\r\n return document.querySelector(selectors.hiddenFieldValidator).check;\r\n };\r\n\r\n const _hasBankAccountDetailsBeenCorrected = () => {\r\n // returns a true or false statement from the PCA api if the account number requires correction\r\n // due to acceptance criteria change we have hard coded the corrected details to false as of 18/01/2018\r\n return false;\r\n };\r\n\r\n const _correctAccountNumber = (data) => {\r\n // if PCA returns corrected details update the bank account number field \r\n document.querySelector(selectors.accountNumberField).value = data.correctedAccountNumber;\r\n };\r\n\r\n const _isDirectDebitCompatible = (data) => {\r\n // return a true or false if the bank account has direct debit capability\r\n return data.isDirectDebitCapable;\r\n };\r\n\r\n const _isCorrectBankDetails = (data) => {\r\n return data.isCorrect;\r\n };\r\n\r\n const _isBankPostcodeAvailable = (data) => {\r\n if (data.contactPostcode == '' || null) {\r\n return false;\r\n }\r\n else {\r\n _validBankPostcode();\r\n return true;\r\n }\r\n };\r\n\r\n const _validBankPostcode = () => {\r\n document.querySelector(selectors.postcodeError).classList.remove('visible');\r\n _enableContinueBtn();\r\n };\r\n\r\n const _invalidBankPostcode = () => {\r\n document.querySelector(selectors.postcodeError).classList.add('visible');\r\n _disableContinueBtn();\r\n };\r\n\r\n const _enableContinueBtn = () => {\r\n document.querySelector(selectors.continueBtn).classList.remove('disabled');\r\n };\r\n\r\n const _disableContinueBtn = () => {\r\n if (!document.querySelector(selectors.continueBtn).classList.contains('disabled')) {\r\n document.querySelector(selectors.continueBtn).classList.add('disabled');\r\n }\r\n };\r\n\r\n const _responseActions = (data) => {\r\n var dataObj = JSON.parse(data);\r\n if (!_isBankPostcodeAvailable(dataObj)) {\r\n _clearBankAddressFields(dataObj);\r\n _invalidBankPostcode();\r\n }\r\n else if (_isCorrectBankDetails(dataObj) && _isDirectDebitCompatible(dataObj) && !_hasBankAccountDetailsBeenCorrected()) {\r\n _updateHiddenCheckbox(true);\r\n _populateAddress(dataObj);\r\n _validBankDetails();\r\n }\r\n else if (_isCorrectBankDetails(dataObj) && _isDirectDebitCompatible(dataObj) && _hasBankAccountDetailsBeenCorrected()) {\r\n _correctAccountNumber(dataObj);\r\n _updateHiddenCheckbox(true);\r\n _populateAddress(dataObj);\r\n _validBankDetails();\r\n }\r\n else {\r\n _updateHiddenCheckbox(false);\r\n _invalidBankDetails();\r\n }\r\n };\r\n\r\n const _getBranchDetails = (sortCode, accountNumber) => {\r\n if (sortCode != '' && accountNumber != '') {\r\n const branchDetails = [\r\n {\r\n url: config.bankAccountValidatorService\r\n },\r\n {\r\n AccountNumber: accountNumber,\r\n SortCode: sortCode\r\n },\r\n function (data) {\r\n _responseActions(data);\r\n }\r\n ];\r\n _makeRequest.apply(null, branchDetails);\r\n }\r\n };\r\n\r\n const _getSortCode = (el) => {\r\n return el.closest(selectors.root).querySelector(selectors.sortCodeField).value;\r\n };\r\n\r\n const _getAccountNumber = (el) => {\r\n return el.closest(selectors.root).querySelector(selectors.accountNumberField).value;\r\n };\r\n\r\n const _bindEvents = (root) => {\r\n const button = root.querySelector(selectors.button);\r\n button.addEventListener('click', (e) => {\r\n e.preventDefault();\r\n _getBranchDetails.call(\r\n null,\r\n _getSortCode(e.currentTarget),\r\n _getAccountNumber(e.currentTarget)\r\n );\r\n eventBus.publish(formEvents.validate);\r\n });\r\n\r\n root.querySelector(selectors.sortCodeField).addEventListener('input', _debounce);\r\n root.querySelector(selectors.accountNumberField).addEventListener('input', _debounce);\r\n };\r\n\r\n const _debounce = debounce(function () {\r\n if (_isHiddenCheckboxChecked()) {\r\n _updateHiddenCheckbox(false);\r\n }\r\n }, 200);\r\n\r\n const _getSettings = (root, resolve) => {\r\n const pcaBtn = root.querySelectorAll(selectors.button);\r\n\r\n if (pcaBtn.length > 0) {\r\n const element = pcaBtn[0];\r\n const data = {\r\n bankAccountValidatorService: element.dataset.bankAccountValidator\r\n };\r\n config = { ...config, ...data };\r\n resolve(true);\r\n }\r\n };\r\n\r\n const init = () => {\r\n const root = document.querySelectorAll(selectors.root)[0];\r\n if (root) {\r\n const readSettings = new Promise((resolve) => {\r\n _getSettings(root, resolve);\r\n });\r\n readSettings\r\n .then(() => {\r\n _bindEvents(root);\r\n });\r\n }\r\n };\r\n\r\n return {\r\n init: init\r\n };\r\n};\r\n","import window from 'window';\r\n\r\n/* FileSaver.js\r\n * A saveAs() FileSaver implementation.\r\n * 1.3.2\r\n * 2016-06-16 18:25:19\r\n *\r\n * By Eli Grey, http://eligrey.com\r\n * License: MIT\r\n * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md\r\n */\r\n\r\n/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */\r\n\r\n/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */\r\n\r\nvar saveAs = saveAs || (function (view) {\r\n \"use strict\";\r\n \r\n // IE <10 is explicitly unsupported\r\n if (typeof view === \"undefined\" || typeof navigator !== \"undefined\" && /MSIE [1-9]\\./.test(navigator.userAgent)) {\r\n return;\r\n }\r\n var\r\n doc = view.document\r\n // only get URL when necessary in case Blob.js hasn't overridden it yet\r\n , get_URL = function () {\r\n return view.URL || view.webkitURL || view;\r\n }\r\n , save_link = doc.createElementNS(\"http://www.w3.org/1999/xhtml\", \"a\")\r\n , can_use_save_link = \"download\" in save_link\r\n , click = function (node) {\r\n var event = new MouseEvent(\"click\");\r\n node.dispatchEvent(event);\r\n }\r\n , is_safari = /constructor/i.test(view.HTMLElement) || view.safari\r\n , is_chrome_ios = /CriOS\\/[\\d]+/.test(navigator.userAgent)\r\n , throw_outside = function (ex) {\r\n (view.setImmediate || view.setTimeout)(function () {\r\n throw ex;\r\n }, 0);\r\n }\r\n , force_saveable_type = \"application/octet-stream\"\r\n // the Blob API is fundamentally broken as there is no \"downloadfinished\" event to subscribe to\r\n , arbitrary_revoke_timeout = 1000 * 40 // in ms\r\n , revoke = function (file) {\r\n var revoker = function () {\r\n if (typeof file === \"string\") { // file is an object URL\r\n get_URL().revokeObjectURL(file);\r\n } else { // file is a File\r\n file.remove();\r\n }\r\n };\r\n setTimeout(revoker, arbitrary_revoke_timeout);\r\n }\r\n , dispatch = function (filesaver, event_types, event) {\r\n event_types = [].concat(event_types);\r\n var i = event_types.length;\r\n while (i--) {\r\n var listener = filesaver[\"on\" + event_types[i]];\r\n if (typeof listener === \"function\") {\r\n try {\r\n listener.call(filesaver, event || filesaver);\r\n } catch (ex) {\r\n throw_outside(ex);\r\n }\r\n }\r\n }\r\n }\r\n , auto_bom = function (blob) {\r\n // prepend BOM for UTF-8 XML and text/* types (including HTML)\r\n // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF\r\n if (/^\\s*(?:text\\/\\S*|application\\/xml|\\S*\\/\\S*\\+xml)\\s*;.*charset\\s*=\\s*utf-8/i.test(blob.type)) {\r\n return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type });\r\n }\r\n return blob;\r\n }\r\n , FileSaver = function (blob, name, no_auto_bom) {\r\n if (!no_auto_bom) {\r\n blob = auto_bom(blob);\r\n }\r\n // First try a.download, then web filesystem, then object URLs\r\n var\r\n filesaver = this\r\n , type = blob.type\r\n , force = type === force_saveable_type\r\n , object_url\r\n , dispatch_all = function () {\r\n dispatch(filesaver, \"writestart progress write writeend\".split(\" \"));\r\n }\r\n // on any filesys errors revert to saving with object URLs\r\n , fs_error = function () {\r\n if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {\r\n // Safari doesn't allow downloading of blob urls\r\n var reader = new FileReader();\r\n reader.onloadend = function () {\r\n var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');\r\n var popup = view.open(url, '_blank');\r\n if (!popup) view.location.href = url;\r\n url = undefined; // release reference before dispatching\r\n filesaver.readyState = filesaver.DONE;\r\n dispatch_all();\r\n };\r\n reader.readAsDataURL(blob);\r\n filesaver.readyState = filesaver.INIT;\r\n return;\r\n }\r\n // don't create more object URLs than needed\r\n if (!object_url) {\r\n object_url = get_URL().createObjectURL(blob);\r\n }\r\n if (force) {\r\n view.location.href = object_url;\r\n } else {\r\n var opened = view.open(object_url, \"_blank\");\r\n if (!opened) {\r\n // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html\r\n view.location.href = object_url;\r\n }\r\n }\r\n filesaver.readyState = filesaver.DONE;\r\n dispatch_all();\r\n revoke(object_url);\r\n }\r\n ;\r\n filesaver.readyState = filesaver.INIT;\r\n\r\n if (can_use_save_link) {\r\n object_url = get_URL().createObjectURL(blob);\r\n setTimeout(function () {\r\n save_link.href = object_url;\r\n save_link.download = name;\r\n click(save_link);\r\n dispatch_all();\r\n revoke(object_url);\r\n filesaver.readyState = filesaver.DONE;\r\n });\r\n return;\r\n }\r\n\r\n fs_error();\r\n }\r\n , FS_proto = FileSaver.prototype\r\n , saveAs = function (blob, name, no_auto_bom) {\r\n return new FileSaver(blob, name || blob.name || \"download\", no_auto_bom);\r\n }\r\n ;\r\n // IE 10+ (native saveAs)\r\n if (typeof navigator !== \"undefined\" && navigator.msSaveOrOpenBlob) {\r\n return function (blob, name, no_auto_bom) {\r\n name = name || blob.name || \"download\";\r\n\r\n if (!no_auto_bom) {\r\n blob = auto_bom(blob);\r\n }\r\n return navigator.msSaveOrOpenBlob(blob, name);\r\n };\r\n }\r\n\r\n FS_proto.abort = function () { };\r\n FS_proto.readyState = FS_proto.INIT = 0;\r\n FS_proto.WRITING = 1;\r\n FS_proto.DONE = 2;\r\n\r\n FS_proto.error =\r\n FS_proto.onwritestart =\r\n FS_proto.onprogress =\r\n FS_proto.onwrite =\r\n FS_proto.onabort =\r\n FS_proto.onerror =\r\n FS_proto.onwriteend =\r\n null;\r\n\r\n return saveAs;\r\n}(\r\n typeof self !== \"undefined\" && self\r\n || typeof window !== \"undefined\" && window\r\n || Function('return typeof this === \"object\" && this.content')()\r\n));\r\n// `self` is undefined in Firefox for Android content script context\r\n// while `this` is nsIContentFrameMessageManager\r\n// with an attribute `content` that corresponds to the window\r\n\r\nexport { saveAs };","import window from 'window';\r\nimport document from 'document';\r\nimport { saveAs } from '../../utilities/file-saver';\r\n\r\nexport const downloadAgreement = () => {\r\n\r\n const selectors = {\r\n downloadInput: '.download-agreement__btn',\r\n };\r\n\r\n const config = {\r\n url: '/api/orders/downloadagreement',\r\n disabled: 'disabled',\r\n contentype: 'content-type',\r\n blob: 'blob',\r\n formtype: 'application/x-www-form-urlencoded',\r\n octettype: 'application/octet-stream',\r\n jsontype: 'application/json',\r\n texttype: 'text/plain'\r\n };\r\n\r\n var _download = function (e) {\r\n const el = e.currentTarget;\r\n el.disabled = config.disabled;\r\n var url = config.url + '?agreementnumber=' + el.dataset.agreementno + '&proposalnumber=' + el.dataset.proposalnumber;\r\n\r\n var xhttp = new XMLHttpRequest();\r\n xhttp.open('GET', url, true);\r\n xhttp.setRequestHeader(config.contentype, config.formtype);\r\n xhttp.responseType = config.blob;\r\n xhttp.onreadystatechange = function () {\r\n if (xhttp.readyState == 4) {\r\n el.disabled = false;\r\n if (xhttp.status == 401) {\r\n window.location.replace(this.getResponseHeader(\"LoginPage\"));\r\n }\r\n if (xhttp.status == 200) {\r\n var isJson = this.getResponseHeader(config.contentype).indexOf(config.jsontype) != -1;\r\n if (isJson) {\r\n var myblob = new Blob([this.response], { type: config.texttype });\r\n var reader = new FileReader();\r\n reader.onload = function () { alert(reader.result); }\r\n reader.readAsText(myblob);\r\n } else {\r\n saveAs(new Blob([this.response], { type: config.octettype }), el.dataset.filename);\r\n }\r\n }\r\n }\r\n };\r\n xhttp.send();\r\n };\r\n\r\n return {\r\n init: function () {\r\n const input = document.querySelectorAll(selectors.downloadInput);\r\n if (input.length) {\r\n input.forEach((element) => element.addEventListener('click', _download));\r\n }\r\n }\r\n };\r\n};","import document from 'document';\r\nimport { eventBus } from '../../foundation/eventBus';\r\nimport { events as formEvents } from '../../feature/form/enable-form';\r\n\r\nexport const postcodeLookup = () => {\r\n\r\n const selectors = {\r\n root: '.form',\r\n searchField: '.js-postcode-lookup',\r\n field: '.form-field-group__input',\r\n postcodeField: '.form-field-group__postcode',\r\n addressList: 'js-address-list',\r\n searchButton: '.find-address-btn',\r\n resultsContainer: '.address-search',\r\n errorMessage: '.form-field-group__error',\r\n warningMessageLocation: '.form-section',\r\n warningMessage: '.form-field-group__warningMessage',\r\n manualAddressButton: '.manual-address-link',\r\n hiddenField: '.js-hidden-fields',\r\n visibleFieldToggle: 'form-field-group__hidden-fields--visible'\r\n };\r\n\r\n var config = {};\r\n\r\n var _makeRequest = function (service, dataObj, func)\r\n {\r\n service.url += '?' + ( new URLSearchParams( dataObj ) ).toString();\r\n var method = 'GET'\r\n let options = {\r\n method\r\n };\r\n fetch(service.url, options)\r\n .then((response) => response.json())\r\n .then((result) => {\r\n if (result.Items && result.Items[0].Error) {\r\n var dataMessage = result.Items[0];\r\n console.warn(`Error: ${dataMessage.Error}, ${dataMessage.Description}, ${dataMessage.Cause}, ${dataMessage.Resolution}`);\r\n }\r\n else\r\n {\r\n if (typeof func != 'function')\r\n {\r\n console.warn(func + ' is not a function');\r\n }\r\n else\r\n {\r\n func(result);\r\n }\r\n }\r\n })\r\n .catch((error) => {\r\n console.warn('Error:', error);\r\n });\r\n }\r\n\r\n var _findAddress = function (text) {\r\n if (text != null || text != '') {\r\n var findArr = [\r\n {\r\n url: config.findService\r\n },\r\n {\r\n key: config.key,\r\n text: text,\r\n countries: config.country // this can be GB (single) or GB, US, CA\r\n },\r\n function (data) {\r\n _findListOfAddress(data);\r\n }\r\n ];\r\n _makeRequest.apply(null, findArr);\r\n }\r\n };\r\n\r\n var _findListOfAddress = function (data) {\r\n if (data.Items.length) {\r\n var item = data.Items[0];\r\n if (item.Type === \"Postcode\" || item.Type === 'BuildingNumber' || item.Type === 'Street') {\r\n var findArr = [\r\n {\r\n url: config.findService\r\n },\r\n {\r\n key: config.key,\r\n text: item.Text,\r\n countries: config.country, // this can be GB (single) or GB, US, CA\r\n container: item.Id // id is recieved from previous ajax request\r\n },\r\n\r\n function createResultList(data) {\r\n var items = data.Items.map(function (suggestion) {\r\n return {\r\n label: suggestion.Text + \", \" + suggestion.Description,\r\n value: '',\r\n data: suggestion\r\n };\r\n });\r\n\r\n _createHtmlResultsContainer(items);\r\n }\r\n ];\r\n _makeRequest.apply(null, findArr);\r\n } else {\r\n var items = data.Items.map(function (suggestion) {\r\n return {\r\n label: suggestion.Text + \", \" + suggestion.Description,\r\n value: '',\r\n data: suggestion\r\n };\r\n });\r\n _createHtmlResultsContainer(items);\r\n }\r\n }\r\n };\r\n\r\n var _retrieveAddress = function (id) {\r\n if (id != null || id != '') {\r\n var retrieveArr = [\r\n {\r\n url: config.receiveService\r\n },\r\n {\r\n key: config.key,\r\n id: id,\r\n field1format: config.customfield\r\n },\r\n function clearResults(data) {\r\n if (data.Items.length) {\r\n _clearResultsContainer();\r\n _removeEnterManualField();\r\n _showHiddenField();\r\n _populateAddress(data);\r\n }\r\n }\r\n ];\r\n _makeRequest.apply(null, retrieveArr);\r\n }\r\n };\r\n\r\n var _createHtmlResultsContainer = function (resultItems) {\r\n var resultsContainer = document.querySelector(selectors.resultsContainer);\r\n if (resultItems.length) {\r\n resultsContainer.innerHTML = `