{"version":3,"file":"script.min.js","sources":["../../../../../resources/js/script.js","../../../../../resources/js/vendor/splide.js"],"sourcesContent":["\"use strict\";\n\nvar _doc = document;\nvar _win = window;\n\n// Global components\nvar EB = EB || {};\n\nvar App = function()\n{\n\tfunction Init()\n\t{\n\t\t// General\n\t\tInitHeader();\n\t\tDisableOutlines();\n\t\tDisableHover();\n\t\tShowCookiePreferences();\n\n\t\t// Components\n\t\tInitCarousels();\n\t\tInitSliders();\n\t\tInitVideoModals();\n\t\tInitForms();\n\t\tInitSelects();\n\t\tInitSavingsCalculator();\n\t\tInitSubnav();\n\n\t\t// Home\n\t\tInitHeroPowerUp();\n\t\tInitFeatureSlides();\n\t\tInitUpdateTabs();\n\n\t\t// Products\n\t\tInitAvitiFaqs();\n\t\tInitVideoPlayOnHover();\n\n\t\t// Resources\n\t\tInitResourceSearch();\n\t\tInitResourceLayoutToggles();\n\t\tInitResourceLinkPreviews();\n\n\t\t// Company\n\t\tInitTimelineSlider();\n\t\tInitTestimonials();\n\t\tInitOpeningFilters();\n\t}\n\n\t// General\n\n\tfunction Breakpoint()\n\t{\n\t\treturn parseFloat(\n\t\t\tgetComputedStyle(_doc.documentElement, null)\n\t\t\t\t.getPropertyValue('font-family')\n\t\t\t\t.replace(/[^0-9.]+/g, '')\n\t\t);\n\t}\n\n\tfunction DisableOutlines()\n\t{\n\t\tvar outlineClass = '-no-outline';\n\t\tvar el = null;\n\n\t\tvar fn = {\n\t\t\tdelegate: function(e) {\n\t\t\t\tel = e.target;\n\n\t\t\t\twhile (el && el.tagName !== 'A') {\n\t\t\t\t\tel = el.parentNode;\n\t\t\t\t}\n\n\t\t\t\tif (el) {\n\t\t\t\t\tel.addEventListener('blur', fn.blur);\n\t\t\t\t\tel.classList.add(outlineClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\tblur: function() {\n\t\t\t\tel.removeEventListener('blur', fn.blur);\n\t\t\t\tel.classList.remove(outlineClass)\n\t\t\t}\n\t\t};\n\n\t\t_doc.addEventListener('click', fn.delegate);\n\t}\n\n\tfunction DisableHover()\n\t{\n\t\tvar html = _doc.documentElement;\n\t\tvar clickClass = '-click';\n\t\tvar touchClass = '-touch';\n\n\t\tvar fn = {\n\t\t\ttoggle: function() {\n\t\t\t\tif (\n\t\t\t\t\t('ontouchstart' in _win) ||\n\t\t\t\t\t(navigator.maxTouchPoints > 0) ||\n\t\t\t\t\t(navigator.msMaxTouchPoints > 0)\n\t\t\t\t) {\n\t\t\t\t\thtml.classList.remove(clickClass);\n\t\t\t\t\thtml.classList.add(touchClass);\n\t\t\t\t} else {\n\t\t\t\t\thtml.classList.remove(touchClass);\n\t\t\t\t\thtml.classList.add(clickClass);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t_doc.addEventListener('click', fn.toggle);\n\n\t\tfn.toggle();\n\t}\n\n\tfunction ShowCookiePreferences()\n\t{\n\t\tvar $link = _doc.querySelectorAll('[data-ref=\"osano-cookie-preferences\"]');\n\n\t\t// If there's no testimonials to control, return\n\t\tif (! $link.length || typeof _win.Osano == 'undefined') {\n\t\t\treturn;\n\t\t}\n\n\t\tvar handle = {\n\t\t\tclick: function(e) {\n\t\t\t\t_win.Osano.cm.showDrawer('osano-cm-dom-info-dialog-open');\n\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t};\n\n\t\t$link[0].addEventListener('click', handle.click);\n\t}\n\n\t// Components\n\n\tfunction InitTestimonials()\n\t{\n\t\tvar els = _doc.querySelectorAll('[data-ref=\"testimonial\"]');\n\n\t\t// If there's no testimonials to control, return\n\t\tif (! els.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar prev = _doc.querySelectorAll('[data-ref=\"testimonial-prev\"]');\n\t\tvar next = _doc.querySelectorAll('[data-ref=\"testimonial-next\"]');\n\t\tvar visibleClass = '-is-visible';\n\t\tvar activeIndex = 0;\n\n\t\tvar fn = {\n\t\t\t// Show the previous testimonial\n\t\t\tprev: function() {\n\t\t\t\t// Remove the currently active testimonials active class\n\t\t\t\tels[activeIndex].classList.remove(visibleClass);\n\n\t\t\t\t// If the current index is the first jump to \n\t\t\t\t// the last or show the previous index\n\t\t\t\tif (activeIndex === 0) {\n\t\t\t\t\tactiveIndex = els.length - 1;\n\t\t\t\t} else {\n\t\t\t\t\tactiveIndex--;\n\t\t\t\t}\n\n\t\t\t\t// Add the active class to the prev index\n\t\t\t\tels[activeIndex].classList.add(visibleClass);\n\n\t\t\t\t// Update the count\n\t\t\t\tfn.count(activeIndex);\n\t\t\t},\n\t\t\t// Show the previous testimonial\n\t\t\tnext: function() {\n\t\t\t\t// Remove the currently active testimonials active class\n\t\t\t\tels[activeIndex].classList.remove(visibleClass);\n\n\t\t\t\t// If the current index is the last jump to \n\t\t\t\t// the first or show the next index\n\t\t\t\tif (activeIndex === els.length - 1) {\n\t\t\t\t\tactiveIndex = 0;\n\t\t\t\t} else {\n\t\t\t\t\tactiveIndex++\n\t\t\t\t}\n\n\t\t\t\t// Add the active class to the next index\n\t\t\t\tels[activeIndex].classList.add(visibleClass);\n\t\t\t}\n\t\t};\n\n\t\tprev[0].addEventListener('click', fn.prev);\n\t\tnext[0].addEventListener('click', fn.next);\n\n\t\t// Show the first testimonial and hide the rest\n\t\tels[0].classList.add(visibleClass);\n\t}\n\n\tfunction InitCarousels()\n\t{\n\t\tvar carousels = _doc.querySelectorAll('[data-ref=\"carousel\"]');\n\n\t\t// If there's no carousels to control, return\n\t\tif (! carousels.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar animatingClass = '-is-animating';\n\t\tvar dragOffset;\n\t\tvar dragStart;\n\t\tvar dragging;\n\n\t\tvar fn = {\n\t\t\tstopAnimation: function(e) {\n\t\t\t\tvar el = e.currentTarget;\n\n\t\t\t\t// Switch from animated to scrollable\n\t\t\t\tif (el.classList.contains(animatingClass)) {\n\t\t\t\t\tvar pageLeft = _doc.querySelectorAll('[data-ref=\"page\"]')[0]\n\t\t\t\t\t\t.getBoundingClientRect()\n\t\t\t\t\t\t.left;\n\t\t\t\t\tvar carouselLeft = el.getBoundingClientRect().left;\n\n\t\t\t\t\t// Toggle the animating and scrollable classes\n\t\t\t\t\tel.classList.remove(animatingClass);\n\t\t\t\t\tel.classList.add('-is-scrollable');\n\n\t\t\t\t\t// Set the scroll offset equal to the animation offset\n\t\t\t\t\tel.scrollLeft = (carouselLeft - pageLeft) * -1;\n\n\t\t\t\t\t// Remove the touch start event to prevent calling\n\t\t\t\t\t// this method more than once on mobile\n\t\t\t\t\tel.removeEventListener('touchstart', fn.stopAnimation)\n\t\t\t\t}\n\t\t\t},\n\t\t\tdragStart: function(e) {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\n\t\t\t\tfn.stopAnimation(e);\n\n\t\t\t\tvar el = e.currentTarget;\n\n\t\t\t\t// Capture the starting carousel and mouse offset positions\n\t\t\t\tdragOffset = el.scrollLeft;\n\t\t\t\tdragStart = e.pageX - el.offsetLeft;\n\n\t\t\t\tdragging = true;\n\t\t\t},\n\t\t\tdragMove: function(e) {\n\t\t\t\te.preventDefault();\n\n\t\t\t\t// If the user isn't currently dragging, return\n\t\t\t\tif (! dragging) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tvar walk = ((e.pageX - this.offsetLeft) - dragStart) * 2; \n\n\t\t\t\tthis.scrollLeft = dragOffset - walk;\n\t\t\t},\n\t\t\tdragStop: function(e) {\n\t\t\t\t// If the user hasn't started dragging yet, return\n\t\t\t\tif (typeof dragging === 'undefined') {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tdragging = false;\n\t\t\t}\n\t\t};\n\n\t\t// https://stackoverflow.com/a/38118591\n\t\tfor (var i = 0; i < carousels.length; i++) {\n\t\t\tvar length = carousels[i].offsetWidth;\n\t\t\tvar duration = length / 100;\n\n\t\t\tcarousels[i].style.animationDuration = duration + \"s\";\n\t\t\tcarousels[i].classList.add(animatingClass);\n\n\t\t\t// For mobile, stop the animation and use native scrolling\n\t\t\tcarousels[i].addEventListener('touchstart', fn.stopAnimation);\n\n\t\t\tcarousels[i].addEventListener('mousedown', fn.dragStart);\n\t\t\tcarousels[i].addEventListener('mousemove', fn.dragMove);\n\t\t\tcarousels[i].addEventListener('mouseup', fn.dragStop);\n\t\t\tcarousels[i].addEventListener('mouseleave', fn.dragStop);\n\t\t}\n\t}\n\n\t// Initialize custom Splide JS sliders\n\tfunction InitSliders()\n\t{\n\t\tvar pagination = _doc.querySelectorAll('[data-ref=\"slider-pagination\"]');\n\t\tvar bullets = _doc.querySelectorAll('[data-ref=\"slider-bullet\"]');\n\t\tvar slides = _doc.querySelectorAll('[data-ref=\"slider-slides\"]');\n\n\t\t// If there's no bullets or slides to control, return\n\t\tif (! bullets.length || ! slides.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar sliderPrev = _doc.querySelectorAll('[data-ref=\"slider-prev\"]')[0];\n\t\tvar sliderNext = _doc.querySelectorAll('[data-ref=\"slider-next\"]')[0];\n\t\tvar breakpoint = Breakpoint();\n\t\tvar activeIndex = 0;\n\t\tvar resizeTimer;\n\n\t\tvar handle = {\n\t\t\t// Update splide sliders for mobile, tablet, and\n\t\t\t// desktop based on the current breakpoint\n\t\t\tresizeEvent: function() {\n\t\t\t\t// Debounce resize events\n\t\t\t\t// https://css-tricks.com/snippets/jquery/done-resizing-event/\n\t\t\t\tclearTimeout(resizeTimer);\n\t\t\t\tresizeTimer = setTimeout(function() {\n\t\t\t\t\tbreakpoint = Breakpoint();\n\t\t\t\t\tfn.updateBulletCount();\n\t\t\t\t}, 100);\n\t\t\t}\n\t\t};\n\n\t\tvar fn = {\n\t\t\t// Update the number of visible bullets\n\t\t\t// https://splidejs.com/apis/\n\t\t\tupdateBulletCount: function() {\n\t\t\t\tswitch (breakpoint) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\tpaginationSlider.options = {\n\t\t\t\t\t\t\tperPage: bullets.length <= 3 ? bullets.length : 3\n\t\t\t\t\t\t};\n\t\t\t\t\t\tslidesSlider.options = { gap: '0' };\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\tpaginationSlider.options = {\n\t\t\t\t\t\t\tperPage: bullets.length <= 5 ? bullets.length : 5\n\t\t\t\t\t\t};\n\t\t\t\t\t\tslidesSlider.options = { gap: '0' };\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tpaginationSlider.options = {\n\t\t\t\t\t\t\tperPage: bullets.length <= 6 ? bullets.length : 6,\n\t\t\t\t\t\t\tfocus: 0,\n\t\t\t\t\t\t};\n\t\t\t\t\t\tslidesSlider.options = { gap: '5%' };\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Navigate to the previous slide unless\n\t\t\t// the first slide is active\n\t\t\tprev: function() {\n\t\t\t\tif (activeIndex === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tactiveIndex -= 1;\n\n\t\t\t\tpaginationSlider.go(activeIndex);\n\t\t\t\tslidesSlider.go(activeIndex);\n\t\t\t},\n\t\t\t// Navigate to the next slide unless\n\t\t\t// the last slide is active\n\t\t\tnext: function() {\n\t\t\t\tif (activeIndex === bullets.length) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tactiveIndex += 1;\n\n\t\t\t\tpaginationSlider.go(activeIndex);\n\t\t\t\tslidesSlider.go(activeIndex);\n\t\t\t}\n\t\t};\n\n\t\t// Initialize pagination slider\n\t\t// https://splidejs.com/guides/options/\n\t\tvar paginationSlider = new Splide(pagination[0], {\n\t\t\tisNavigation: true,\n\t\t\tpagination: false,\n\t\t\tarrows: false,\n\t\t\ttrimSpace: true,\n\t\t\tfocus: 'center',\n\t\t\tdrag: false,\n\t\t\tperPage: 3\n\t\t}).mount();\n\n\t\tpaginationSlider.on('click', function(slide) {\n\t\t\tpaginationSlider.go(slide.index);\n\t\t})\n\t\tpaginationSlider.on('move', function(index) {\n\t\t\tactiveIndex = index;\n\t\t});\n\n\t\t// Initialize slides slider\n\t\t// https://splidejs.com/guides/options/\n\t\tvar slidesSlider = new Splide(slides[0], {\n\t\t\tpagination: false,\n\t\t\tarrows: false,\n\t\t\tgap: '0'\n\t\t})\n\t\t.sync(paginationSlider)\n\t\t.mount();\n\n\t\tslidesSlider.on('mount', fn.updateBulletCount());\n\t\tslidesSlider.on('move', function(index) {\n\t\t\tactiveIndex = index;\n\t\t});\n\n\t\t// Listen from previous and next pagination click events\n\t\tsliderPrev.addEventListener('click', fn.prev);\n\t\tsliderNext.addEventListener('click', fn.next);\n\n\t\t_win.addEventListener('resize', handle.resizeEvent, true);\n\t}\n\n\tfunction InitVideoModals()\n\t{\n\t\tvar links = _doc.querySelectorAll('[data-ref=\"video-link\"]');\n\n\t\t// If there are no video links to control, return\n\t\tif (! links.length) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Create a modal if one doesn't already exist\n\t\tInitModal();\n\n\t\t// Create cloneable iframe embed element\n\t\tvar $embed = _doc.createElement('iframe');\n\n\t\t$embed.setAttribute('src', '');\n\t\t$embed.setAttribute('width', '1280px');\n\t\t$embed.setAttribute('height', '720px');\n\t\t$embed.setAttribute('frameborder', '0');\n\t\t$embed.setAttribute('allow', 'autoplay; fullscreen');\n\t\t$embed.setAttribute('allowFullScreen', '');\n\n\t\t$embed.classList.add('video');\n\n\t\tvar fn = {\n\t\t\tshow: function(e) {\n\t\t\t\tvar link = e.currentTarget;\n\n\t\t\t\t// Clone the embed element and add the current links src\n\t\t\t\tvar $iframe = $embed.cloneNode(true);\n\t\t\t\tvar src = link.dataset.src;\n\n\t\t\t\t$iframe.src = src + (src.indexOf('?') !== -1 ? '&' : '?') + 'autoplay=1';\n\n\t\t\t\tEB.Modal.show($iframe);\n\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t}\n\n\t\t// Add link event listeners\n\t\tfor (var i = 0; i < links.length; i++) {\n\t\t\tlinks[i].addEventListener('click', fn.show);\n\t\t}\n\t}\n\n\tfunction InitModal()\n\t{\n\t\t// If a modal already exists, return it\n\t\tif (EB.Modal) {\n\t\t\treturn EB.Modal;\n\t\t}\n\n\t\t// Create a new modal element\n\t\tvar $modal = _doc.createElement('div');\n\t\tvar $mask = _doc.createElement('div');\n\t\tvar $content = _doc.createElement('div');\n\t\tvar $close = _doc.createElement('div');\n\n\t\t$modal.classList.add('modal');\n\t\t$mask.classList.add('modal__mask');\n\t\t$content.classList.add('modal__content');\n\t\t$close.classList.add('modal__close');\n\n\t\t$modal.append($mask);\n\t\t$modal.append($content);\n\t\t$modal.append($close);\n\n\t\t// Add modal methods and event listeners\n\t\tvar visibleClass = '-is-visible';\n\t\tvar noScrollClass = '-no-scroll';\n\t\tvar scrollY;\n\n\t\tvar fn = {\n\t\t\thide: function() {\n\t\t\t\t$modal.classList.remove(visibleClass);\n\t\t\t\t$content.innerHTML = '';\n\n\t\t\t\t_doc.body.classList.remove(noScrollClass);\n\t\t\t\t_win.scrollTo(0, scrollY);\n\n\t\t\t\t// Set scrollY to undefined\n\t\t\t\t// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void\n\t\t\t\tscrollY = void 0;\n\t\t\t}\n\t\t}\n\n\t\t$close.addEventListener('click', fn.hide);\n\n\t\tEB.Modal = {\n\t\t\tshow: function(content) {\n\t\t\t\t$content.append(content);\n\t\t\t\t$modal.classList.add(visibleClass);\n\n\t\t\t\tscrollY = _win.scrollY;\n\n\t\t\t\t_doc.body.classList.add(noScrollClass);\n\t\t\t}\n\t\t};\n\n\t\t_doc.body.append($modal);\n\t}\n\n\t// Static Page Caching / CDN / Blitz\n\t// https://docs.solspace.com/craft/freeform/v3/templating/caching.html#static-page-caching-cdn-blitz\n\tfunction InitForms()\n\t{\n\t\tvar forms = _doc.querySelectorAll('[data-ref=\"form\"]');\n\n\t\t// If there's no sliders to control, return\n\t\tif (! forms.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar fn = {\n\t\t\twatch: function(form) {\n\t\t\t\tvar inputs = form.querySelectorAll(\"[required]\");\n\n\t\t\t\t// If there aren't any required inputs,\n\t\t\t\t// go ahead and initialize the form\n\t\t\t\tif (! inputs.length) {\n\t\t\t\t\tfn.init(form, '/form?handle=' + form.dataset.handle);\n\t\t\t\t}\n\n\t\t\t\tvar input = function(e) {\n\t\t\t\t\tfn.init(form, '/form?handle=' + form.dataset.handle);\n\n\t\t\t\t\tfor (var i = 0; i < inputs.length; i++) {\n\t\t\t\t\t\tinputs[i].removeEventListener('input', input);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Listen for input events\n\t\t\t\tfor (var i = 0; i < inputs.length; i++) {\n\t\t\t\t\tinputs[i].addEventListener('input', input);\n\t\t\t\t}\n\t\t\t},\n\t\t\tinit: function(form, url) {\n\t\t\t\tvar xmlhttp = new XMLHttpRequest();\n\n\t\t\t\txmlhttp.onreadystatechange = function() {\n\t\t\t\t\tif (xmlhttp.readyState == XMLHttpRequest.DONE) {\n\t\t\t\t\t\tif (xmlhttp.status == 200) {\n\t\t\t\t\t\t\tfn.update(form, xmlhttp.responseText);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// TODO: handle errors\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\txmlhttp.open(\"POST\", url, true);\n\t\t\t\txmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\t\t\t\txmlhttp.setRequestHeader('Accept', 'application/json');\n\t\t\t\txmlhttp.send();\n\t\t\t},\n\t\t\tupdate: function(form, data) {\n\t\t\t\tvar json = JSON.parse(data);\n\n\t\t\t\t// Update the Form Hash\n\t\t\t\tform.querySelector('input[name=formHash]').value = json.hash;\n\n\t\t\t\t// Update the Form Payload\n\t\t\t\tform.querySelector('input[name=freeform_payload]').value = json.payload;\n\n\t\t\t\t// Update the Honeypot\n\t\t\t\tvar honeypot = json.honeypot;\n\t\t\t\tvar honeypotInput = form.querySelector('input[name^=\"' + honeypot.name.split('_')[0] + '\"]');\n\t\t\t\thoneypotInput.setAttribute('id', honeypot.name);\n\t\t\t\thoneypotInput.setAttribute('name', honeypot.name);\n\t\t\t\thoneypotInput.value = honeypot.hash;\n\n\t\t\t\t// Locate and update the CSRF input\n\t\t\t\tvar csrf = json.csrf;\n\t\t\t\tform.querySelector('input[name=' + csrf.name + ']').value = csrf.value;\n\t\t\t}\n\t\t};\n\n\t\tfor (var i = 0; i < forms.length; i++) {\n\t\t\tfn.watch(forms[i]);\n\t\t}\n\t}\n\n\tfunction InitSelects()\n\t{\n\t\tvar selects = _doc.querySelectorAll('[data-ref=\"select\"]');\n\t\tvar options = _doc.querySelectorAll('[data-ref=\"select-option\"]');\n\t\tvar activeClass = '-is-active';\n\n\t\t// If there are no select elements or options to control, return\n\t\tif (! selects.length || ! options.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar fn = {\n\t\t\ttoggleOptions: function(e) {\n\t\t\t\te.currentTarget.classList.toggle(activeClass);\n\n\t\t\t\t// Close other active selects\n\t\t\t\tfor (var i = 0; i < selects.length; i++) {\n\t\t\t\t\tif (selects[i] !== e.currentTarget) {\n\t\t\t\t\t\tselects[i].classList.remove(activeClass);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Prevent the hideOptions event from triggering\n\t\t\t\t// when toggling the options\n\t\t\t\te.stopPropagation();\n\t\t\t},\n\t\t\thideOptions: function(e) {\n\t\t\t\t// Hide all options if a click event occurs on\n\t\t\t\t// any element other than a select option\n\t\t\t\tif (e.target.dataset.ref !== 'select-option') {\n\t\t\t\t\tfor (var i = 0; i < selects.length; i++) {\n\t\t\t\t\t\tselects[i].classList.remove(activeClass);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tupdateInput: function(e) {\n\t\t\t\tvar option = e.currentTarget;\n\t\t\t\tvar group = _doc.getElementsByName(option.name);\n\t\t\t\tvar label = '';\n\n\t\t\t\t// Create a string containing values for all of the checked options\n\t\t\t\tfor (var i = 0; i < group.length; i++) {\n\t\t\t\t\tif (group[i].dataset.label && group[i].checked) {\n\t\t\t\t\t\tlabel += label.length ? ', ' + group[i].dataset.label : group[i].dataset.label;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Traverse the DOM until the closest select element is found\n\t\t\t\tvar el = option.parentNode;\n\n\t\t\t\twhile (el && el.dataset.ref !== 'select') {\n\t\t\t\t\tel = el.parentNode;\n\t\t\t\t}\n\n\t\t\t\t// Set the selected options parent input value using the label value\n\t\t\t\tvar input = el.querySelectorAll('[data-ref=\"select-input\"]')[0];\n\n\t\t\t\tinput.value = label;\n\t\t\t}\n\t\t}\n\n\t\tfor (var i = 0; i < selects.length; i++) {\n\t\t\tselects[i].addEventListener('click', fn.toggleOptions);\n\t\t}\n\n\t\tfor (var i = 0; i < options.length; i++) {\n\t\t\toptions[i].addEventListener('change', fn.updateInput);\n\t\t}\n\n\t\t_doc.addEventListener('click', fn.hideOptions);\n\t}\n\n\tfunction InitSavingsCalculator()\n\t{\n\t\tvar $calculatorForm = _doc.querySelectorAll('[data-ref=\"savings-calculator\"]');\n\n\t\t// If the calculator is missing, return\n\t\t// before querying the require elements\n\t\tif (! $calculatorForm) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Shared inputs\n\t\tvar $sharedCompetitorFlowCellsPerMonth = _doc.querySelectorAll('[data-ref=\"shared-competitor-flow-cells-per-month\"]')[0];\n\n\t\t// Our inputs\n\t\tvar $ourMachineCost = _doc.querySelectorAll('[data-ref=\"our-machine-cost\"]')[0];\n\t\tvar $ourMachineDiscount = _doc.querySelectorAll('[data-ref=\"our-machine-discount\"]')[0];\n\t\tvar $ourFlowCellCost = _doc.querySelectorAll('[data-ref=\"our-flow-cell-cost\"]')[0];\n\t\tvar $ourFlowCellDiscount = _doc.querySelectorAll('[data-ref=\"our-flow-cell-discount\"]')[0];\n\t\tvar $ourGbPerFlowCell = _doc.querySelectorAll('[data-ref=\"our-gb-per-flow-cell\"]')[0];\n\n\t\t// Their inputs\n\t\tvar $theirMachineCost = _doc.querySelectorAll('[data-ref=\"their-machine-cost\"]')[0];\n\t\tvar $theirMachineDiscount = _doc.querySelectorAll('[data-ref=\"their-machine-discount\"]')[0];\n\t\tvar $theirFlowCellCost = _doc.querySelectorAll('[data-ref=\"their-flow-cell-cost\"]')[0];\n\t\tvar $theirFlowCellDiscount = _doc.querySelectorAll('[data-ref=\"their-flow-cell-discount\"]')[0];\n\t\tvar $theirGbPerFlowCell = _doc.querySelectorAll('[data-ref=\"their-gb-per-flow-cell\"]')[0];\n\n\t\t// Our calculated\n\t\tvar $ourMachineActualCost = _doc.querySelectorAll('[data-ref=\"our-machine-actual-cost\"]')[0];\n\t\tvar $ourFlowCellActualCost = _doc.querySelectorAll('[data-ref=\"our-flow-cell-actual-cost\"]')[0];\n\t\tvar $ourFlowCellsPerMonth = _doc.querySelectorAll('[data-ref=\"our-flow-cells-per-month\"]')[0];\n\t\tvar $ourFlowCellCostPerMonth = _doc.querySelectorAll('[data-ref=\"our-flow-cell-cost-per-month\"]')[0];\n\n\t\t// Their calculated\n\t\tvar $theirMachineActualCost = _doc.querySelectorAll('[data-ref=\"their-machine-actual-cost\"]')[0];\n\t\tvar $theirFlowCellActualCost = _doc.querySelectorAll('[data-ref=\"their-flow-cell-actual-cost\"]')[0];\n\t\tvar $theirFlowCellsPerMonth = _doc.querySelectorAll('[data-ref=\"their-flow-cells-per-month\"]')[0];\n\t\tvar $theirFlowCellCostPerMonth = _doc.querySelectorAll('[data-ref=\"their-flow-cell-cost-per-month\"]')[0];\n\n\t\t// Calculated savings\n\t\tvar $yearOneCostSavings = _doc.querySelectorAll('[data-ref=\"year-one-cost-savings\"]')[0];\n\t\tvar $yearTwoCostSavings = _doc.querySelectorAll('[data-ref=\"year-two-cost-savings\"]')[0];\n\t\tvar $yearThreeCostSavings = _doc.querySelectorAll('[data-ref=\"year-three-cost-savings\"]')[0];\n\t\tvar $breakEventMonth = _doc.querySelectorAll('[data-ref=\"break-even-month\"]')[0];\n\n\t\t// Calculated summary\n\t\tvar $summaryBenefits = _doc.querySelectorAll('[data-ref=\"summary-benefits\"]')[0];\n\n\t\t// Form actions\n\t\tvar $reset = _doc.querySelectorAll('[data-ref=\"reset-calculator\"]')[0];\n\n\t\t// If required elemets are missing, return\n\t\tif (\n\t\t\t! $sharedCompetitorFlowCellsPerMonth &&\n\t\t\t! $ourMachineCost &&\n\t\t\t! $ourMachineDiscount &&\n\t\t\t! $ourFlowCellCost &&\n\t\t\t! $ourFlowCellDiscount &&\n\t\t\t! $ourGbPerFlowCell &&\n\t\t\t! $theirMachineCost &&\n\t\t\t! $theirMachineDiscount &&\n\t\t\t! $theirFlowCellCost &&\n\t\t\t! $theirFlowCellDiscount &&\n\t\t\t! $theirGbPerFlowCell\n\t\t\t// TODO: Add calculated elements\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Shared calculated\n\t\tvar sharedCompetitorFlowCellsPerMonth = 0;\n\n\t\t// Our calculated\n\t\tvar ourMachineActualCost = 0;\n\t\tvar ourFlowCellActualCost = 0;\n\t\tvar ourFlowCellsPerMonth = 0;\n\t\tvar ourFlowCellCostPerMonth = 0;\n\n\t\t// Their calculated\n\t\tvar theirMachineActualCost = 0;\n\t\tvar theirFlowCellActualCost = 0;\n\t\tvar theirFlowCellsPerMonth = 0;\n\t\tvar theirFlowCellCostPerMonth = 0;\n\n\t\t// Calculated savings\n\t\tvar yearOneCostSavings = 0;\n\t\tvar yearTwoCostSavings = 0;\n\t\tvar yearThreeCostSavings = 0;\n\t\tvar breakEventMonth = 0;\n\n\t\t// Calculated summary\n\t\tvar summaryMachineMultiplier = 0;\n\t\tvar summaryFlowCellMultiplier = 0;\n\t\tvar summaryFlowCellMultiplierMonths = 0;\n\n\t\t// Default input type clamp values\n\t\tvar clamp = {\n\t\t\tcurrency: {\n\t\t\t\tmin: 1,\n\t\t\t\tmax: 1e9 // 1,000,000,000 (1 Billion)\n\t\t\t},\n\t\t\tpercent: {\n\t\t\t\tmin: 0,\n\t\t\t\tmax: 100\n\t\t\t},\n\t\t\tnumeric: {\n\t\t\t\tmin: 1,\n\t\t\t\tmax: 1e9 // 1,000,000,000 (1 Billion)\n\t\t\t}\n\t\t};\n\n\t\t// Savings\n\t\tvar savingsYears = 3;\n\t\tvar savingsMonths = savingsYears * 12;\n\n\t\t// TODO:\n\t\t// For now, `separator`s will always be \",\", and\n\t\t// the `decimalSeparator` will always be \".\"\n\t\t//\n\t\t// If international support is ever needed,\n\t\t// this will need to be updated\n\t\tvar separator = ',';\n\t\tvar decimalSeparator = '.'\n\t\tvar floatPrecision = 2; // n^10, (tens, hundreds, thousands, etc.)\n\n\t\tvar fn = {\n\t\t\tinit: function() {\n\t\t\t\tfn.initInputs();\n\t\t\t\tfn.registerInputEvents();\n\t\t\t\tfn.calculate();\n\t\t\t},\n\t\t\tinitInputs: function() {\n\t\t\t\t// Shared inputs\n\t\t\t\t$sharedCompetitorFlowCellsPerMonth.dataset.type = 'numeric';\n\t\t\t\t$sharedCompetitorFlowCellsPerMonth.dataset.default = $sharedCompetitorFlowCellsPerMonth.value;\n\n\t\t\t\t// Our inputs\n\t\t\t\t$ourMachineCost.dataset.type = 'currency';\n\t\t\t\t$ourMachineCost.dataset.default = $ourMachineCost.value;\n\t\t\t\t$ourMachineDiscount.dataset.type = 'percent';\n\t\t\t\t$ourMachineDiscount.dataset.default = $ourMachineDiscount.value;\n\t\t\t\t$ourFlowCellCost.dataset.type = 'currency';\n\t\t\t\t$ourFlowCellCost.dataset.default = $ourFlowCellCost.value;\n\t\t\t\t$ourFlowCellDiscount.dataset.type = 'percent';\n\t\t\t\t$ourFlowCellDiscount.dataset.default = $ourFlowCellDiscount.value;\n\t\t\t\t$ourGbPerFlowCell.dataset.type = 'numeric';\n\t\t\t\t$ourGbPerFlowCell.dataset.default = $ourGbPerFlowCell.value;\n\n\t\t\t\t// Their inputs\n\t\t\t\t$theirMachineCost.dataset.type = 'currency';\n\t\t\t\t$theirMachineCost.dataset.default = $theirMachineCost.value;\n\t\t\t\t$theirMachineDiscount.dataset.type = 'percent';\n\t\t\t\t$theirMachineDiscount.dataset.default = $theirMachineDiscount.value;\n\t\t\t\t$theirFlowCellCost.dataset.type = 'currency';\n\t\t\t\t$theirFlowCellCost.dataset.default = $theirFlowCellCost.value;\n\t\t\t\t$theirFlowCellDiscount.dataset.type = 'percent';\n\t\t\t\t$theirFlowCellDiscount.dataset.default = $theirFlowCellDiscount.value;\n\t\t\t\t$theirGbPerFlowCell.dataset.type = 'numeric';\n\t\t\t\t$theirGbPerFlowCell.dataset.default = $theirGbPerFlowCell.value;\n\t\t\t},\n\t\t\tregisterInputEvents: function() {\n\t\t\t\t// Shared inputs\n\t\t\t\t$sharedCompetitorFlowCellsPerMonth.addEventListener('focus', handle.focus);\n\t\t\t\t$sharedCompetitorFlowCellsPerMonth.addEventListener('keydown', handle.keydown);\n\t\t\t\t$sharedCompetitorFlowCellsPerMonth.addEventListener('input', handle.input);\n\t\t\t\t$sharedCompetitorFlowCellsPerMonth.addEventListener('blur', handle.blur);\n\n\t\t\t\t// Our inputs\n\t\t\t\t$ourMachineCost.addEventListener('focus', handle.focus);\n\t\t\t\t$ourMachineCost.addEventListener('keydown', handle.keydown);\n\t\t\t\t$ourMachineCost.addEventListener('input', handle.input);\n\t\t\t\t$ourMachineCost.addEventListener('blur', handle.blur);\n\n\t\t\t\t$ourMachineDiscount.addEventListener('focus', handle.focus);\n\t\t\t\t$ourMachineDiscount.addEventListener('keydown', handle.keydown);\n\t\t\t\t$ourMachineDiscount.addEventListener('input', handle.input);\n\t\t\t\t$ourMachineDiscount.addEventListener('blur', handle.blur);\n\n\t\t\t\t$ourFlowCellCost.addEventListener('focus', handle.focus);\n\t\t\t\t$ourFlowCellCost.addEventListener('keydown', handle.keydown);\n\t\t\t\t$ourFlowCellCost.addEventListener('input', handle.input);\n\t\t\t\t$ourFlowCellCost.addEventListener('blur', handle.blur);\n\n\t\t\t\t$ourFlowCellDiscount.addEventListener('focus', handle.focus);\n\t\t\t\t$ourFlowCellDiscount.addEventListener('keydown', handle.keydown);\n\t\t\t\t$ourFlowCellDiscount.addEventListener('input', handle.input);\n\t\t\t\t$ourFlowCellDiscount.addEventListener('blur', handle.blur);\n\n\t\t\t\t$ourGbPerFlowCell.addEventListener('focus', handle.focus);\n\t\t\t\t$ourGbPerFlowCell.addEventListener('keydown', handle.keydown);\n\t\t\t\t$ourGbPerFlowCell.addEventListener('input', handle.input);\n\t\t\t\t$ourGbPerFlowCell.addEventListener('blur', handle.blur);\n\n\t\t\t\t// Their inputs\n\t\t\t\t$theirMachineCost.addEventListener('focus', handle.focus);\n\t\t\t\t$theirMachineCost.addEventListener('keydown', handle.keydown);\n\t\t\t\t$theirMachineCost.addEventListener('input', handle.input);\n\t\t\t\t$theirMachineCost.addEventListener('blur', handle.blur);\n\n\t\t\t\t$theirMachineDiscount.addEventListener('focus', handle.focus);\n\t\t\t\t$theirMachineDiscount.addEventListener('keydown', handle.keydown);\n\t\t\t\t$theirMachineDiscount.addEventListener('input', handle.input);\n\t\t\t\t$theirMachineDiscount.addEventListener('blur', handle.blur);\n\n\t\t\t\t$theirFlowCellCost.addEventListener('focus', handle.focus);\n\t\t\t\t$theirFlowCellCost.addEventListener('keydown', handle.keydown);\n\t\t\t\t$theirFlowCellCost.addEventListener('input', handle.input);\n\t\t\t\t$theirFlowCellCost.addEventListener('blur', handle.blur);\n\n\t\t\t\t$theirFlowCellDiscount.addEventListener('focus', handle.focus);\n\t\t\t\t$theirFlowCellDiscount.addEventListener('keydown', handle.keydown);\n\t\t\t\t$theirFlowCellDiscount.addEventListener('input', handle.input);\n\t\t\t\t$theirFlowCellDiscount.addEventListener('blur', handle.blur);\n\n\t\t\t\t$theirGbPerFlowCell.addEventListener('focus', handle.focus);\n\t\t\t\t$theirGbPerFlowCell.addEventListener('keydown', handle.keydown);\n\t\t\t\t$theirGbPerFlowCell.addEventListener('input', handle.input);\n\t\t\t\t$theirGbPerFlowCell.addEventListener('blur', handle.blur);\n\n\t\t\t\t// Form actions\n\t\t\t\t$reset.addEventListener('click', handle.reset);\n\t\t\t},\n\t\t\tcalculate: function() {\n\t\t\t\tfn.calculateMachineActualPrice();\n\t\t\t\tfn.calculateFlowCellAcutalCost();\n\t\t\t\tfn.calculateFlowCellPerMonthCost();\n\t\t\t\tfn.calculateCostSavings();\n\t\t\t\tfn.calculateSummary();\n\t\t\t},\n\t\t\tcalculateMachineActualPrice: function() {\n\t\t\t\tvar ourMachineCost = utils.sanitize($ourMachineCost.value) * 1;\n\t\t\t\tvar ourMachineDiscount = utils.sanitize($ourMachineDiscount.value) / 100;\n\n\t\t\t\tvar theirMachineCost = utils.sanitize($theirMachineCost.value) * 1;\n\t\t\t\tvar theirMachineDiscount = utils.sanitize($theirMachineDiscount.value) / 100;\n\n\t\t\t\tourMachineActualCost = utils.round(ourMachineCost - (ourMachineCost * ourMachineDiscount));\n\t\t\t\ttheirMachineActualCost = utils.round(theirMachineCost - (theirMachineCost * theirMachineDiscount));\n\n\t\t\t\t$ourMachineActualCost.innerHTML = '$' + utils.formatInputDecimalValue(ourMachineActualCost.toLocaleString());\n\t\t\t\t$theirMachineActualCost.innerHTML = '$' + utils.formatInputDecimalValue(theirMachineActualCost.toLocaleString());\n\t\t\t},\n\t\t\tcalculateFlowCellAcutalCost: function() {\n\t\t\t\tvar ourFlowCellCost = utils.sanitize($ourFlowCellCost.value) * 1;\n\t\t\t\tvar ourFlowCellDiscount = utils.sanitize($ourFlowCellDiscount.value) / 100;\n\n\t\t\t\tvar theirFlowCellCost = utils.sanitize($theirFlowCellCost.value) * 1;\n\t\t\t\tvar theirFlowCellDiscount = utils.sanitize($theirFlowCellDiscount.value) / 100;\n\n\t\t\t\tourFlowCellActualCost = utils.round(ourFlowCellCost - (ourFlowCellCost * ourFlowCellDiscount));\n\t\t\t\ttheirFlowCellActualCost = utils.round(theirFlowCellCost - (theirFlowCellCost * theirFlowCellDiscount));\n\n\t\t\t\t$ourFlowCellActualCost.innerHTML = '$' + utils.formatInputDecimalValue(ourFlowCellActualCost.toLocaleString());\n\t\t\t\t$theirFlowCellActualCost.innerHTML = '$' + utils.formatInputDecimalValue(theirFlowCellActualCost.toLocaleString());\n\t\t\t},\n\t\t\tcalculateFlowCellPerMonthCost: function() {\n\t\t\t\tsharedCompetitorFlowCellsPerMonth = utils.sanitize($sharedCompetitorFlowCellsPerMonth.value) * 1;\n\n\t\t\t\tvar ourGbPerFlowCell = utils.sanitize($ourGbPerFlowCell.value) * 1;\n\t\t\t\tvar theirGbPerFlowCell = utils.sanitize($theirGbPerFlowCell.value) * 1;\n\n\t\t\t\tourFlowCellsPerMonth = Math.ceil((theirGbPerFlowCell * sharedCompetitorFlowCellsPerMonth) / ourGbPerFlowCell);\n\t\t\t\ttheirFlowCellsPerMonth = Math.ceil((theirGbPerFlowCell * sharedCompetitorFlowCellsPerMonth) / theirGbPerFlowCell);\n\n\t\t\t\tif (! isNaN(ourFlowCellsPerMonth) && isFinite(ourFlowCellsPerMonth)) {\n\t\t\t\t\t$ourFlowCellsPerMonth.innerHTML = utils.formatInputDecimalValue(ourFlowCellsPerMonth.toLocaleString());\n\t\t\t\t} else {\n\t\t\t\t\t$ourFlowCellsPerMonth.innerHTML = '-';\n\t\t\t\t}\n\n\t\t\t\tif (! isNaN(theirFlowCellsPerMonth) && isFinite(theirFlowCellsPerMonth)) {\n\t\t\t\t\t$theirFlowCellsPerMonth.innerHTML = utils.formatInputDecimalValue(theirFlowCellsPerMonth.toLocaleString());\n\t\t\t\t} else {\n\t\t\t\t\t$theirFlowCellsPerMonth.innerHTML = '-';\n\t\t\t\t}\n\n\t\t\t\tourFlowCellCostPerMonth = utils.round(ourFlowCellsPerMonth * ourFlowCellActualCost);\n\t\t\t\ttheirFlowCellCostPerMonth = utils.round(theirFlowCellsPerMonth * theirFlowCellActualCost);\n\n\t\t\t\tif (! isNaN(ourFlowCellCostPerMonth) && isFinite(ourFlowCellCostPerMonth)) {\n\t\t\t\t\t$ourFlowCellCostPerMonth.innerHTML = '$' + utils.formatInputDecimalValue(ourFlowCellCostPerMonth.toLocaleString());\n\t\t\t\t} else {\n\t\t\t\t\t$ourFlowCellCostPerMonth.innerHTML = '-';\n\t\t\t\t}\n\n\t\t\t\tif (! isNaN(theirFlowCellCostPerMonth) && isFinite(theirFlowCellCostPerMonth)) {\n\t\t\t\t\t$theirFlowCellCostPerMonth.innerHTML = '$' + utils.formatInputDecimalValue(theirFlowCellCostPerMonth.toLocaleString());\n\t\t\t\t} else {\n\t\t\t\t\t$theirFlowCellsPerMonth.innerHTML = '-';\n\t\t\t\t}\n\t\t\t},\n\t\t\tcalculateCostSavings: function() {\n\t\t\t\tvar costPerMonth = [];\n\t\t\t\tvar crossIndex = -1;\n\n\t\t\t\tcostPerMonth[0] = {\n\t\t\t\t\ttheirCost: utils.round(theirMachineActualCost + theirFlowCellCostPerMonth),\n\t\t\t\t\tourCost: utils.round(ourMachineActualCost + ourFlowCellCostPerMonth),\n\t\t\t\t};\n\n\t\t\t\tfor (let i = 1; i < savingsMonths; i++) {\n\t\t\t\t\tcostPerMonth[i] = {\n\t\t\t\t\t\tourCost: utils.round(costPerMonth[i - 1].ourCost + ourFlowCellCostPerMonth),\n\t\t\t\t\t\ttheirCost: utils.round(costPerMonth[i - 1].theirCost + theirFlowCellCostPerMonth),\n\t\t\t\t\t};\n\n\t\t\t\t\tif (\n\t\t\t\t\t\t(costPerMonth[i - 1].ourCost > costPerMonth[i - 1].theirCost) &&\n\t\t\t\t\t\t(costPerMonth[i].ourCost < costPerMonth[i].theirCost)\n\t\t\t\t\t) {\n\t\t\t\t\t\tcrossIndex = i;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tvar savingsPerYear = [];\n\n\t\t\t\tfor (var i = 0; i < savingsYears; i++) {\n\t\t\t\t\tvar costPerMonthIndex = (12 * (i + 1)) - 1;\n\n\t\t\t\t\tsavingsPerYear[i] = utils.round(costPerMonth[costPerMonthIndex].theirCost - costPerMonth[costPerMonthIndex].ourCost);\n\t\t\t\t}\n\n\t\t\t\tyearOneCostSavings = savingsPerYear[0];\n\t\t\t\tyearTwoCostSavings = savingsPerYear[1];\n\t\t\t\tyearThreeCostSavings = savingsPerYear[2];\n\t\t\t\tbreakEventMonth = utils.clamp(crossIndex + 1, 1, false);\n\n\t\t\t\tvar formattedYearOneCostSavings = '$' + utils.formatInputDecimalValue(yearOneCostSavings.toLocaleString());\n\t\t\t\tvar formattedYearTwoCostSavings = '$' + utils.formatInputDecimalValue(yearTwoCostSavings.toLocaleString());\n\t\t\t\tvar formattedYearThreeCostSavings = '$' + utils.formatInputDecimalValue(yearThreeCostSavings.toLocaleString());\n\n\t\t\t\t$yearOneCostSavings.innerHTML = formattedYearOneCostSavings;\n\t\t\t\t$yearOneCostSavings.setAttribute('title', formattedYearOneCostSavings);\n\t\t\t\t$yearTwoCostSavings.innerHTML = formattedYearTwoCostSavings;\n\t\t\t\t$yearTwoCostSavings.setAttribute('title', formattedYearTwoCostSavings);\n\t\t\t\t$yearThreeCostSavings.innerHTML = formattedYearThreeCostSavings;\n\t\t\t\t$yearThreeCostSavings.setAttribute('title', formattedYearThreeCostSavings);\n\n\t\t\t\tif (yearThreeCostSavings <= 0) {\n\t\t\t\t\t$breakEventMonth.innerHTML = '-';\n\t\t\t\t\t$breakEventMonth.setAttribute('title', '-');\n\t\t\t\t} else {\n\t\t\t\t\t$breakEventMonth.innerHTML = 'Month ' + breakEventMonth;\n\t\t\t\t\t$breakEventMonth.setAttribute('title', 'Month ' + breakEventMonth);\n\t\t\t\t}\n\t\t\t},\n\t\t\tcalculateSummary: function() {\n\t\t\t\tvar ourGbPerFlowCell = utils.sanitize($ourGbPerFlowCell.value) * 1;\n\t\t\t\tvar theirGbPerFlowCell = utils.sanitize($theirGbPerFlowCell.value) * 1;\n\n\t\t\t\tsummaryMachineMultiplier = Math.round((yearThreeCostSavings / ourMachineActualCost) * 10) / 10;\n\t\t\t\tsummaryFlowCellMultiplier = Math.round((yearThreeCostSavings / ourFlowCellActualCost) * 10) / 10;\n\n\t\t\t\tif (isNaN(summaryMachineMultiplier) && ! isFinite(summaryMachineMultiplier)) {\n\t\t\t\t\tsummaryMachineMultiplier = '-';\n\t\t\t\t}\n\n\t\t\t\tif (isNaN(summaryFlowCellMultiplier) && ! isFinite(summaryFlowCellMultiplier)) {\n\t\t\t\t\tsummaryFlowCellMultiplier = '-';\n\t\t\t\t}\n\n\t\t\t\tsummaryMachineMultiplier = utils.clamp(summaryMachineMultiplier, 0, false);\n\t\t\t\tsummaryFlowCellMultiplier = utils.clamp(summaryFlowCellMultiplier, 0, false);\n\n\t\t\t\tvar formattedSummaryMachineMultiplier = summaryMachineMultiplier.toLocaleString();\n\t\t\t\tvar formattedSummaryFlowCellMultiplier = summaryFlowCellMultiplier.toLocaleString();\n\n\t\t\t\tvar summaryBenefitsText = '';\n\n\t\t\t\tsummaryBenefitsText += 'Bought ' + formattedSummaryMachineMultiplier + ' more AVITIs, ';\n\t\t\t\tsummaryBenefitsText += 'or run ' + formattedSummaryFlowCellMultiplier + ' more flow cells by the end of year three.';\n\n\t\t\t\t$summaryBenefits.innerHTML = summaryBenefitsText;\n\t\t\t}\n\t\t};\n\n\t\tvar handle = {\n\t\t\tinput: function(e) {\n\t\t\t\tvar pasteEvent = e.type == 'paste' ||\n\t\t\t\t\t(\n\t\t\t\t\t\te.type == 'input' &&\n\t\t\t\t\t\te.inputType &&\n\t\t\t\t\t\te.inputType == 'insertFromPaste'\n\t\t\t\t\t);\n\n\t\t\t\tvar $el = e.currentTarget;\n\t\t\t\tvar updated = utils.formatInputValue($el, pasteEvent);\n\n\t\t\t\t$el.value = updated.value;\n\n\t\t\t\t$el.setSelectionRange(\n\t\t\t\t\tupdated.cursorPosition,\n\t\t\t\t\tupdated.cursorPosition\n\t\t\t\t);\n\n\t\t\t\t$el.dataset.value = $el.value;\n\n\t\t\t\tfn.calculate();\n\t\t\t},\n\t\t\tfocus: function(e) {\n\t\t\t\tvar $el = e.currentTarget;\n\n\t\t\t\t$el.dataset.value = $el.value;\n\t\t\t},\n\t\t\tkeydown: function(e) {\n\t\t\t\tvar $el = e.currentTarget;\n\t\t\t\tvar allowedChars = '0123456789.-'.split('');\n\n\t\t\t\tallowedChars.push('Backspace');\n\t\t\t\tallowedChars.push('Delete');\n\t\t\t\tallowedChars.push('Tab');\n\t\t\t\tallowedChars.push('ArrowLeft');\n\t\t\t\tallowedChars.push('ArrowUp');\n\t\t\t\tallowedChars.push('ArrowRight');\n\t\t\t\tallowedChars.push('ArrowDown');\n\t\t\t\tallowedChars.push('PageUp');\n\t\t\t\tallowedChars.push('PageDown');\n\n\t\t\t\tif (! e.metaKey && allowedChars.indexOf(e.key) === -1) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\te.stopPropagation();\n\t\t\t\t}\n\n\t\t\t\t$el.dataset.value = $el.value;\n\t\t\t},\n\t\t\tblur: function(e) {\n\t\t\t\tvar $el = e.currentTarget;\n\t\t\t\tvar updated = utils.formatInputValue($el, true);\n\n\t\t\t\t$el.value = updated.value;\n\n\t\t\t\t// If the input loses focus and the last character entered\n\t\t\t\t// is a a decimal, or a decimal followed by a zero,\n\t\t\t\t// return and allow them to keep typing\n\t\t\t\tvar pattern = /^[\\d,]+?\\.0*?$/g;\n\n\t\t\t\tif (pattern.test($el.value)) {\n\t\t\t\t\t$el.value = $el.value.split(decimalSeparator)[0];\n\t\t\t\t}\n\n\t\t\t\t// If the value is empty, set the value\n\t\t\t\t// to zero and move the cursor to the end\n\t\t\t\tif ($el.value == '') {\n\t\t\t\t\tvar type = $el.dataset.type;\n\n\t\t\t\t\tif (type == 'currency') {\n\t\t\t\t\t\t$el.value = 1;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (type == 'percent') {\n\t\t\t\t\t\t$el.value = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (type == 'numeric') {\n\t\t\t\t\t\t$el.value = 1;\n\t\t\t\t\t}\n\n\t\t\t\t\t$el.setSelectionRange($el.value.length, $el.value.length);\n\t\t\t\t\t$el.dataset.value = $el.value;\n\n\t\t\t\t\tfn.calculate();\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t$el.value = utils.formatInputDecimalValue($el.value);\n\t\t\t\t$el.dataset.value = $el.value;\n\n\t\t\t\tfn.calculate();\n\t\t\t},\n\t\t\treset: function() {\n\t\t\t\t// Shared inputs\n\t\t\t\t$sharedCompetitorFlowCellsPerMonth.value = $sharedCompetitorFlowCellsPerMonth.dataset.default;\n\n\t\t\t\t// Our inputs\n\t\t\t\t$ourMachineCost.value = $ourMachineCost.dataset.default;\n\t\t\t\t$ourMachineDiscount.value = $ourMachineDiscount.dataset.default;\n\t\t\t\t$ourFlowCellCost.value = $ourFlowCellCost.dataset.default;\n\t\t\t\t$ourFlowCellDiscount.value = $ourFlowCellDiscount.dataset.default;\n\t\t\t\t$ourGbPerFlowCell.value = $ourGbPerFlowCell.dataset.default;\n\n\t\t\t\t// Their inputs\n\t\t\t\t$theirMachineCost.value = $theirMachineCost.dataset.default;\n\t\t\t\t$theirMachineDiscount.value = $theirMachineDiscount.dataset.default;\n\t\t\t\t$theirFlowCellCost.value = $theirFlowCellCost.dataset.default;\n\t\t\t\t$theirFlowCellDiscount.value = $theirFlowCellDiscount.dataset.default;\n\t\t\t\t$theirGbPerFlowCell.value = $theirGbPerFlowCell.dataset.default;\n\n\t\t\t\tfn.calculate();\n\t\t\t}\n\t\t};\n\n\t\tvar utils = {\n\t\t\tformatInputValue: function($el, pasteEvent) {\n\t\t\t\tvar cursorPosition = $el.selectionStart;\n\t\t\t\tvar value = $el.value;\n\n\t\t\t\t// TODO:\n\t\t\t\t// Validation needs to be reworked to better handle paste events\n\t\t\t\t//\n\t\t\t\t// Paste events should be bound to whole or decimal numbers\n\t\t\t\t// i.e. pasting 5.5 into 11,111.(5.5)11 should not result\n\t\t\t\t// in 111,115.51, it should result in 11,111.55\n\n\t\t\t\t// If the value can't be cast as a number, or the\n\t\t\t\t// user attempts to past exponent, return\n\t\t\t\tvar exponentPattern = new RegExp('\\\\de\\\\d|\\\\d\\\\*\\\\*\\\\d');\n\n\t\t\t\tif (\n\t\t\t\t\tisNaN(utils.toNum(utils.sanitize(value))) ||\n\t\t\t\t\texponentPattern.test(value)\n\t\t\t\t) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: $el.dataset.value,\n\t\t\t\t\t\tcursorPosition: cursorPosition + ($el.dataset.value.length - value.length)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If the input event was fired because the user added a decimal\n\t\t\t\t// point, or a decimal point followed by a decimal value less\n\t\t\t\t// than or equal to the specified `floatPrecision`,\n\t\t\t\t// return and allow additional decimal values\n\t\t\t\tvar pattern = new RegExp('^[\\\\d,]+?\\\\.\\\\d{0,' + floatPrecision + '}?$');\n\n\t\t\t\tif (pattern.test(value) && value.split(decimalSeparator)[1].length < floatPrecision) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tcursorPosition: cursorPosition\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// If adding a value after a decimal, prevent\n\t\t\t\t// the event if the precision is met\n\t\t\t\tif (\n\t\t\t\t\t! pasteEvent &&\n\t\t\t\t\t! pattern.test(value) &&\n\t\t\t\t\tvalue.indexOf(decimalSeparator) > 0 &&\n\t\t\t\t\tcursorPosition > value.indexOf(decimalSeparator)\n\t\t\t\t) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: $el.dataset.value,\n\t\t\t\t\t\tcursorPosition: cursorPosition + ($el.dataset.value.length - value.length)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Don't allow more than one decimal separator\n\t\t\t\tif (\n\t\t\t\t\tvalue.split(decimalSeparator).length > 1 &&\n\t\t\t\t\tvalue.split('')[cursorPosition - 1] == '.'\n\t\t\t\t) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: value.slice(0, -1),\n\t\t\t\t\t\tcursorPosition: cursorPosition\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Skip over the decimal separator when backspacing\n\t\t\t\tif ($el.dataset.value.split('')[cursorPosition] == decimalSeparator) {\n\t\t\t\t\tvalue = $el.dataset.value;\n\t\t\t\t}\n\n\t\t\t\t// If the input is empty or zero, return to move\n\t\t\t\t// the cursor to the end of the input value\n\t\t\t\tif (! value) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\tcursorPosition: cursorPosition\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// Sanitize the input value\n\t\t\t\tvalue = utils.sanitize(value);\n\n\t\t\t\t// Allow place values beyond the specified\n\t\t\t\t// `floatPrecision` to affect rounding\n\t\t\t\tif (! pasteEvent) {\n\t\t\t\t\tvar roundingSplit = value.split(decimalSeparator);\n\n\t\t\t\t\tif (roundingSplit > 1) {\n\t\t\t\t\t\t// Strip precision values beyond the hundreths place\n\t\t\t\t\t\tvalue = roundingSplit[0] + decimalSeparator + roundingSplit\n\t\t\t\t\t\t\t.slice(1) // Drop the whole numbers\n\t\t\t\t\t\t\t.join('') // Join the decimal numbers\n\t\t\t\t\t\t\t.split('') // Split the decimal number\n\t\t\t\t\t\t\t.slice(0, floatPrecision - 1) // Drop everything after the specified float precision place value\n\t\t\t\t\t\t\t.join(''); // Join the decimal numbers\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Convert the value to a number\n\t\t\t\tvalue = utils.toNum(value);\n\n\t\t\t\t// Clamp the input value based on the type\n\t\t\t\tvar inputType = $el.dataset.type;\n\t\t\t\tvar inputMin = $el.dataset.min;\n\t\t\t\tvar inputMax = $el.dataset.max;\n\n\t\t\t\tvalue = utils.clamp(\n\t\t\t\t\tvalue,\n\t\t\t\t\tinputMin || clamp[inputType].min,\n\t\t\t\t\tinputMax || clamp[inputType].max\n\t\t\t\t).toLocaleString();\n\n\t\t\t\tif (pasteEvent) {\n\t\t\t\t\tcursorPosition = cursorPosition - 1;\n\t\t\t\t}\n\n\t\t\t\tvar separatorsCount = $el.value.split(separator).length;\n\n\t\t\t\t// Move the cursor position forward if there are\n\t\t\t\t// fewer separators in the updated value and\n\t\t\t\t// the updated value wasn't a separator\n\t\t\t\tif (\n\t\t\t\t\tseparatorsCount < value.split(separator).length &&\n\t\t\t\t\tvalue.split('')[cursorPosition] != separator\n\t\t\t\t) {\n\t\t\t\t\tcursorPosition += 1;\n\t\t\t\t}\n\n\t\t\t\t// Move the cursor position backwards if there are\n\t\t\t\t// more separators in the updated value\n\t\t\t\tif (separatorsCount > value.split(separator).length) {\n\t\t\t\t\tcursorPosition -= 1;\n\t\t\t\t}\n\n\t\t\t\tvalue = utils.formatInputDecimalValue(value);\n\n\t\t\t\treturn {\n\t\t\t\t\tvalue: value,\n\t\t\t\t\tcursorPosition: cursorPosition\n\t\t\t\t};\n\t\t\t},\n\t\t\tformatInputDecimalValue: function(val) {\n\t\t\t\t// If there are fewer decimal place values than the specified\n\t\t\t\t// `floatPrecision`, append trailing zeros to satisfy\n\t\t\t\t// the float precision requirements\n\t\t\t\tvar split = val.split(decimalSeparator);\n\n\t\t\t\tif (\n\t\t\t\t\tsplit.length > 1 &&\n\t\t\t\t\tsplit[split.length - 1].length <= floatPrecision &&\n\t\t\t\t\tval.slice(-1) != '0'\n\t\t\t\t) {\n\t\t\t\t\treturn val + new Array(floatPrecision - split[split.length - 1].length + 1).join('0');\n\t\t\t\t}\n\n\t\t\t\treturn val;\n\t\t\t},\n\t\t\ttoNum: function(val) {\n\t\t\t\tif (val.indexOf(decimalSeparator) > -1) {\n\t\t\t\t\treturn utils.toFloat(val);\n\t\t\t\t}\n\n\t\t\t\treturn utils.toInt(val);\n\t\t\t},\n\t\t\ttoInt: function(val) {\n\t\t\t\tvar rounded = Math.round(val);\n\n\t\t\t\treturn rounded < 0 ? 0 : rounded;\n\t\t\t},\n\t\t\ttoFloat: function(val) {\n\t\t\t\treturn utils.round(val);\n\t\t\t},\n\t\t\tround: function(val) {\n\t\t\t\tvar precision = Math.pow(10, floatPrecision);\n\n\t\t\t\treturn Math.round(val * precision) / precision;\n\t\t\t},\n\t\t\tclamp: function(num, min, max) {\n\t\t\t\t// Only clamp the min value\n\t\t\t\tif (max === false) {\n\t\t\t\t\treturn num <= min ? min : num;\n\t\t\t\t}\n\n\t\t\t\t// Only clamp the max value\n\t\t\t\tif (min === false) {\n\t\t\t\t\treturn num >= max ? max : num;\n\t\t\t\t}\n\n\t\t\t\treturn num <= min ? min : (num >= max ? max : num);\n\t\t\t},\n\t\t\tsanitize: function(val) {\n\t\t\t\t// Strip all non numeric characters except for \".\" and \"-\"\n\t\t\t\tvar sanitized = val.replace(/[^\\d.-]/g, '');\n\n\t\t\t\tsanitized = sanitized.split(decimalSeparator);\n\n\t\t\t\t// Check for multiple decimal separators\n\t\t\t\t// and remove all but the first\n\t\t\t\tif (sanitized.length > 1) {\n\t\t\t\t\treturn sanitized.slice(0, -1).join('') + decimalSeparator + sanitized.slice(-1);\n\t\t\t\t}\n\n\t\t\t\treturn sanitized.join('');\n\t\t\t}\n\t\t};\n\n\t\tfn.init();\n\t}\n\n\t// Header\n\n\tfunction InitHeader()\n\t{\n\t\tvar header = _doc.querySelectorAll('[data-ref=\"header\"]');\n\t\tvar headerToggle = _doc.querySelectorAll('[data-ref=\"header-toggle\"]');\n\t\tvar headerGroups = _doc.querySelectorAll('[data-ref=\"header-group\"]');\n\t\tvar headerGroupLabels = _doc.querySelectorAll('[data-ref=\"header-group-label\"]');\n\t\tvar headerSubgroups = _doc.querySelectorAll('[data-ref=\"header-subgroup\"]');\n\t\tvar headerSubgroupLabels = _doc.querySelectorAll('[data-ref=\"header-subgroup-label\"]');\n\t\tvar headerLinks = _doc.querySelectorAll('[data-ref=\"header-links\"]');\n\t\tvar scrolledClass = '-is-scrolled';\n\t\tvar expandedClass = '-is-expanded';\n\t\tvar breakpoint = Breakpoint();\n\t\tvar isScrolled = _win.scrollY > 0;\n\t\tvar isMobile = breakpoint < 4;\n\n\t\tvar reset = {\n\t\t\theader: function() {\n\t\t\t\treset.headerClasses();\n\t\t\t\treset.headerEventListeners();\n\t\t\t},\n\t\t\theaderClasses: function() {\n\t\t\t\theader[0].classList.remove(expandedClass);\n\n\t\t\t\treset.headerGroupClasses();\n\t\t\t\treset.headerGroupLabelClasses();\n\n\t\t\t\treset.headerSubgroupClasses();\n\t\t\t\treset.headerSubgroupLabelClasses();\n\t\t\t},\n\t\t\theaderGroupClasses: function() {\n\t\t\t\tfor (var i = 0; i < headerGroups.length; i++) {\n\t\t\t\t\theaderGroups[i].classList.remove(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\theaderGroupLabelClasses: function() {\n\t\t\t\tfor (var i = 0; i < headerGroupLabels.length; i++) {\n\t\t\t\t\theaderGroupLabels[i].classList.remove(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\theaderSubgroupClasses: function() {\n\t\t\t\tfor (var i = 0; i < headerSubgroups.length; i++) {\n\t\t\t\t\theaderSubgroups[i].classList.remove(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\theaderSubgroupLabelClasses: function() {\n\t\t\t\tfor (var i = 0; i < headerSubgroupLabels.length; i++) {\n\t\t\t\t\theaderSubgroupLabels[i].classList.remove(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\theaderEventListeners: function() {\n\t\t\t\theader[0].removeEventListener('mouseleave', handle.headerMouseLeaveEvent);\n\t\t\t\theaderToggle[0].removeEventListener('click', handle.toggleEvent);\n\n\t\t\t\tfor (var i = 0; i < headerGroupLabels.length; i++) {\n\t\t\t\t\theaderGroupLabels[i].removeEventListener('click', handle.groupLabelClickEvent);\n\t\t\t\t\theaderGroupLabels[i].removeEventListener('mouseenter', handle.groupLabelMouseEvent);\n\t\t\t\t}\n\n\t\t\t\tfor (var i = 0; i < headerSubgroupLabels.length; i++) {\n\t\t\t\t\theaderSubgroupLabels[i].removeEventListener('click', handle.subgroupLabelClickEvent);\n\t\t\t\t\theaderSubgroupLabels[i].removeEventListener('mouseenter', handle.subgroupLabelMouseEvent);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tvar listen = {\n\t\t\ttoggleEvent: function() {\n\t\t\t\theaderToggle[0].addEventListener('click', handle.toggleEvent);\n\t\t\t},\n\t\t\tgroupLabelEventsMobile: function() {\n\t\t\t\tfor (var i = 0; i < headerGroupLabels.length; i++) {\n\t\t\t\t\theaderGroupLabels[i].addEventListener('click', handle.groupLabelClickEvent);\n\t\t\t\t}\n\t\t\t},\n\t\t\tsubgroupLabelEventsMobile: function() {\n\t\t\t\tfor (var i = 0; i < headerSubgroupLabels.length; i++) {\n\t\t\t\t\theaderSubgroupLabels[i].addEventListener('click', handle.subgroupLabelClickEvent);\n\t\t\t\t}\n\t\t\t},\n\t\t\tgroupLabelEventsDesktop: function() {\n\t\t\t\tfor (var i = 0; i < headerGroupLabels.length; i++) {\n\t\t\t\t\theaderGroupLabels[i].addEventListener('mouseenter', handle.groupLabelMouseEvent);\n\t\t\t\t}\n\t\t\t},\n\t\t\tsubgroupLabelEventsDesktop: function() {\n\t\t\t\tfor (var i = 0; i < headerSubgroupLabels.length; i++) {\n\t\t\t\t\theaderSubgroupLabels[i].addEventListener('mouseenter', handle.subgroupLabelMouseEvent);\n\t\t\t\t}\n\t\t\t},\n\t\t\theaderMouseLeaveEventDesktop: function() {\n\t\t\t\theader[0].addEventListener('mouseleave', handle.headerMouseLeaveEvent);\n\t\t\t}\n\t\t};\n\n\t\tvar handle = {\n\t\t\ttoggleEvent: function() {\n\t\t\t\theader[0].classList.toggle(expandedClass);\n\n\t\t\t\treset.headerGroupClasses();\n\t\t\t\treset.headerSubgroupClasses();\n\t\t\t},\n\t\t\t// Toggle header labels\n\t\t\tgroupLabelClickEvent: function() {\n\t\t\t\tvar $group = document.querySelector('[data-ref=\"header-group\"][data-id=\"' + this.dataset.group + '\"]');\n\n\t\t\t\tif ($group) {\n\t\t\t\t\t$group.classList.toggle(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Toggle header sublabels\n\t\t\tsubgroupLabelClickEvent: function() {\n\t\t\t\tvar $subgroup = document.querySelector('[data-ref=\"header-subgroup\"][data-id=\"' + this.dataset.subgroup + '\"]');\n\n\t\t\t\tif ($subgroup) {\n\t\t\t\t\t$subgroup.classList.toggle(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\tgroupLabelMouseEvent: function(e) {\n\t\t\t\treset.headerClasses();\n\n\t\t\t\t// Get the current group\n\t\t\t\tvar $group = document.querySelector('[data-ref=\"header-group\"][data-id=\"' + this.dataset.group + '\"]');\n\n\t\t\t\t// Get the first subgroup label and subgroup\n\t\t\t\tvar $subgroupLabel = $group.querySelector('[data-ref=\"header-subgroup-label\"]');\n\t\t\t\tvar $subgroup = $group.querySelector('[data-ref=\"header-subgroup\"]');\n\n\t\t\t\t$subgroupLabel.classList.add(expandedClass);\n\t\t\t\t$subgroup.classList.add(expandedClass);\n\t\t\t},\n\t\t\tsubgroupLabelMouseEvent: function(e) {\n\t\t\t\treset.headerClasses();\n\n\t\t\t\tvar $subgroupLabel = e.currentTarget;\n\t\t\t\tvar $subgroup = document.querySelector('[data-ref=\"header-subgroup\"][data-id=\"' + this.dataset.subgroup + '\"]');\n\n\t\t\t\tif ($subgroup) {\n\t\t\t\t\t$subgroupLabel.classList.add(expandedClass);\n\t\t\t\t\t$subgroup.classList.add(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\theaderMouseLeaveEvent: function() {\n\t\t\t\treset.headerClasses();\n\t\t\t},\n\t\t\tresizeEvent: function() {\n\t\t\t\tbreakpoint = Breakpoint();\n\n\t\t\t\tif (isMobile && breakpoint > 3) {\n\t\t\t\t\tisMobile = false;\n\t\t\t\t\treset.header();\n\t\t\t\t\tlisten.headerMouseLeaveEventDesktop();\n\t\t\t\t\tlisten.groupLabelEventsDesktop();\n\t\t\t\t\tlisten.subgroupLabelEventsDesktop();\n\t\t\t\t\theaderToggle[0].removeEventListener('click', handle.toggleEvent);\n\t\t\t\t}\n\n\t\t\t\tif (! isMobile && breakpoint < 4) {\n\t\t\t\t\tisMobile = true;\n\t\t\t\t\treset.header();\n\t\t\t\t\tlisten.toggleEvent();\n\t\t\t\t\tlisten.groupLabelEventsMobile();\n\t\t\t\t\tlisten.subgroupLabelEventsMobile();\n\t\t\t\t}\n\n\t\t\t\tutils.updateTransparency();\n\n\t\t\t},\n\t\t\tscrollEvent: function() {\n\t\t\t\tutils.updateTransparency();\n\t\t\t}\n\t\t};\n\n\t\tvar utils = {\n\t\t\tupdateTransparency: function() {\n\t\t\t\tisScrolled = _win.scrollY > 0;\n\n\t\t\t\tif (isScrolled && breakpoint > 3) {\n\t\t\t\t\theader[0].classList.add(scrolledClass);\n\t\t\t\t} else {\n\t\t\t\t\theader[0].classList.remove(scrolledClass);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tif (isScrolled && breakpoint > 3) {\n\t\t\theader[0].classList.add(scrolledClass);\n\t\t}\n\n\t\tif (breakpoint < 4) {\n\t\t\tlisten.toggleEvent();\n\t\t\tlisten.groupLabelEventsMobile();\n\t\t\tlisten.subgroupLabelEventsMobile();\n\t\t}\n\n\t\tif (breakpoint > 3) {\n\t\t\tlisten.headerMouseLeaveEventDesktop();\n\t\t\tlisten.groupLabelEventsDesktop();\n\t\t\tlisten.subgroupLabelEventsDesktop();\n\t\t}\n\n\t\t_win.addEventListener('resize', handle.resizeEvent, true);\n\t\t_win.addEventListener('scroll', handle.scrollEvent, true);\n\t}\n\n\tfunction InitSubnav()\n\t{\n\t\tvar $toggle = _doc.querySelectorAll('[data-ref=\"subnav-toggle\"]');\n\t\tvar $links = _doc.querySelectorAll('[data-ref=\"subnav-links\"]');\n\t\tvar $nav = _doc.querySelectorAll('[data-ref=\"subnav\"]');\n\n\t\t// If required elemets are missing, return\n\t\tif (! $toggle.length || ! $nav.length || ! $links.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar expandedClass = '-is-expanded';\n\t\tvar breakpoint = Breakpoint();\n\t\tvar isMobile = breakpoint < 4;\n\n\t\t// Reusable methods for adding sub nav event listeners\n\t\tvar subscribe = {\n\t\t\ttoggleEvent: function() {\n\t\t\t\t$toggle[0].addEventListener('click', handle.toggleEvent);\n\t\t\t},\n\t\t\tresizeEvent: function() {\n\t\t\t\t_win.addEventListener('resize', handle.resizeEvent, true);\n\t\t\t},\n\t\t\tscrollEvent: function() {\n\t\t\t\t_win.addEventListener('scroll', handle.scrollEvent, true);\n\t\t\t},\n\t\t\tclickEvent: function() {\n\t\t\t\t_doc.addEventListener('click', handle.clickEvent);\n\t\t\t}\n\t\t};\n\n\t\t// Reusable method for removing sub nav event listeners\n\t\tvar unsubscribe = {\n\t\t\ttoggleEvent: function() {\n\t\t\t\t$toggle[0].removeEventListener('click', handle.toggleEvent);\n\t\t\t},\n\t\t\tresizeEvent: function() {\n\t\t\t\t_win.removeEventListener('resize', handle.resizeEvent, true);\n\t\t\t},\n\t\t\tscrollEvent: function() {\n\t\t\t\t_win.removeEventListener('scroll', handle.scrollEvent, true);\n\t\t\t},\n\t\t\tclickEvent: function() {\n\t\t\t\t_doc.removeEventListener('click', handle.clickEvent);\n\t\t\t}\n\t\t};\n\n\t\tvar handle = {\n\t\t\t// Toggle the sub nav on mobile and tablet\n\t\t\ttoggleEvent: function() {\n\t\t\t\t$nav[0].classList.toggle(expandedClass);\n\t\t\t},\n\t\t\t// Update the sub nav for mobile, tablet, and\n\t\t\t// desktop based on the current breakpoint\n\t\t\tresizeEvent: function() {\n\t\t\t\tbreakpoint = Breakpoint();\n\n\t\t\t\t// Close the sub nav when switching from\n\t\t\t\t// mobile or tablet to desktop\n\t\t\t\tif (isMobile && breakpoint > 3) {\n\t\t\t\t\tisMobile = false;\n\t\t\t\t\t$nav[0].classList.remove(expandedClass);\n\n\t\t\t\t\tunsubscribe.toggleEvent();\n\t\t\t\t\tunsubscribe.scrollEvent();\n\t\t\t\t\tunsubscribe.clickEvent();\n\t\t\t\t}\n\n\t\t\t\t// Add toggle event listeners for mobile and tablet\n\t\t\t\tif (! isMobile && breakpoint < 4) {\n\t\t\t\t\tisMobile = true;\n\n\t\t\t\t\tsubscribe.toggleEvent();\n\t\t\t\t\tsubscribe.scrollEvent();\n\t\t\t\t\tsubscribe.clickEvent();\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Close the sub nav when it's scrolled off screen\n\t\t\tscrollEvent: function() {\n\t\t\t\tif (\n\t\t\t\t\t$nav[0].classList.contains(expandedClass) &&\n\t\t\t\t\t$links[0].getBoundingClientRect().bottom <= 0\n\t\t\t\t) {\n\t\t\t\t\t$nav[0].classList.remove(expandedClass);\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Close the sub nav when the any element\n\t\t\t// outside of the sub nav is clicked\n\t\t\tclickEvent: function(e) {\n\t\t\t\tvar $el = e.target;\n\n\t\t\t\twhile ($el && $el !== $nav[0]) {\n\t\t\t\t\t$el = $el.parentNode;\n\t\t\t\t}\n\n\t\t\t\tif (! $el) {\n\t\t\t\t\t$nav[0].classList.remove(expandedClass);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Only listen for events on tablet and mobile\n\t\tif (breakpoint < 4) {\n\t\t\tsubscribe.toggleEvent();\n\t\t\tsubscribe.scrollEvent();\n\t\t\tsubscribe.clickEvent();\n\t\t}\n\n\t\tsubscribe.resizeEvent();\n\t}\n\n\t// Home\n\n\tfunction InitHeroPowerUp()\n\t{\n\t\tvar hero = _doc.querySelectorAll('[data-ref=\"home-hero\"]');\n\n\t\t// If required elemets are missing, return\n\t\tif (! hero.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tsetTimeout(() => {\n\t\t\thero[0].classList.add('-aviti-loaded');\n\t\t}, 1000);\n\t}\n\n\tfunction InitFeatureSlides()\n\t{\n\t\tvar features = _doc.querySelectorAll('[data-ref=\"home-features\"]');\n\n\t\t// If there's no timeline to control, return\n\t\tif (! features.length) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Initialize timeline slider\n\t\t// https://splidejs.com/guides/options/\n\t\tvar featureSlider = new Splide(features[0], {\n\t\t\tarrows: false,\n\t\t\tautoplay: true,\n\t\t\tautoHeight: true,\n\t\t\tautoWidth: true,\n\t\t\tclasses: {\n\t\t\t\tpagination: 'splide__pagination home-features__bullets',\n\t\t\t\tpage: 'splide__pagination__page home-features__bullet',\n\t\t\t},\n\t\t\tinterval: 5000,\n\t\t\trewind: true,\n\t\t\ttype : 'fade'\n\t\t});\n\t\t\n\t\tfeatureSlider.mount();\n\t}\n\n\tfunction InitUpdateTabs()\n\t{\n\t\tvar tabs = _doc.querySelectorAll('[data-ref=\"home-updates-tab\"]');\n\t\tvar links = _doc.querySelectorAll('[data-ref=\"home-updates-links\"]');\n\n\t\t// If there are no tabs or links to control, return\n\t\tif (! tabs.length || ! links.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar fn = {\n\t\t\tupdateActiveTab: function(e) {\n\t\t\t\t// Update active tab\n\t\t\t\tfor (var i = 0; i < tabs.length; i++) {\n\t\t\t\t\ttabs[i].classList.toggle('-is-active', tabs[i].dataset.type == e.currentTarget.dataset.type);\n\t\t\t\t}\n\n\t\t\t\t// Update visible links\n\t\t\t\tfor (var i = 0; i < links.length; i++) {\n\t\t\t\t\tlinks[i].classList.toggle('-is-visible', links[i].dataset.type == e.currentTarget.dataset.type);\n\t\t\t\t}\n\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t};\n\n\t\tfor (var i = 0; i < tabs.length; i++) {\n\t\t\ttabs[i].addEventListener('click', fn.updateActiveTab);\n\t\t}\n\t}\n\n\t// Products\n\n\tfunction InitAvitiFaqs()\n\t{\n\t\tvar faqs = _doc.querySelectorAll('[data-ref=\"aviti-faq\"]');\n\n\t\t// If there's no testimonials to control, return\n\t\tif (! faqs.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar expandedClass = '-is-expanded';\n\n\t\tvar fn = {\n\t\t\ttoggle: function(e) {\n\t\t\t\te.currentTarget.classList.toggle(expandedClass);\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t};\n\n\t\tfor (var i = 0; i < faqs.length; i++) {\n\t\t\t// Delegate reset link click events\n\t\t\tfaqs[i].addEventListener('click', function(e) {\n\t\t\t\tif (e.target.dataset.ref === 'aviti-faq-question') {\n\t\t\t\t\tfn.toggle(e);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction InitVideoPlayOnHover()\n\t{\n\t\tvar videos = _doc.querySelectorAll('[data-ref=\"video-play-on-hover\"]');\n\n\t\t// If required elemets are missing, return\n\t\tif (! videos.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar fn = {\n\t\t\tplay: function(e) {\n\t\t\t\te.currentTarget.play();\n\t\t\t},\n\t\t\tpause: function(e) {\n\t\t\t\te.currentTarget.pause();\n\t\t\t}\n\t\t};\n\n\t\tfor (var i = 0; i < videos.length; i++) {\n\t\t\tvideos[i].addEventListener('mouseenter', fn.play);\n\t\t\tvideos[i].addEventListener('mouseleave', fn.pause);\n\n\t\t\tvideos[i].load();\n\t\t}\n\t}\n\n\t// Resources\n\n\tfunction InitResourceSearch()\n\t{\n\t\tvar searchForm = _doc.querySelectorAll('[data-ref=\"resources-search\"]');\n\t\tvar categoryLinks = _doc.querySelectorAll('[data-resources-category]');\n\t\tvar productFilters = _doc.querySelectorAll('[data-resources-product-filter]');\n\t\tvar categoryFilters = _doc.querySelectorAll('[data-resources-category-filter]');\n\n\t\t// If required elemets are missing, return\n\t\tif (\n\t\t\t! searchForm.length ||\n\t\t\t! categoryLinks.length ||\n\t\t\t! productFilters.length ||\n\t\t\t! categoryFilters.length\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar sortOrder = _doc.querySelectorAll('[data-resources-sort]');\n\n\t\tvar data = {\n\t\t\tfilters: {}\n\t\t};\n\n\t\tvar fn = {\n\t\t\tinit: function() {\n\t\t\t\tvar params = _win.location.search.replace(/\\?|[\\[\\]]/g, '').split('&');\n\n\t\t\t\t// Modified from:\n\t\t\t\t// https://stackoverflow.com/a/52959683/2769162\n\t\t\t\tparams = params.reduce(function(filters, param) {\n\t\t\t\t\tvar rawKeyValue = param.split('=');\n\n\t\t\t\t\t// Skip valueless params\n\t\t\t\t\tif (rawKeyValue.length === 1) {\n\t\t\t\t\t\treturn filters;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Decode the raw params\n\t\t\t\t\tvar key = decodeURIComponent(rawKeyValue[0]);\n\t\t\t\t\tvar value = decodeURIComponent(rawKeyValue[rawKeyValue.length - 1]);\n\n\t\t\t\t\t// Skip irrelevant params\n\t\t\t\t\tif (['category', 'product', 'keywords'].indexOf(key) === -1) {\n\t\t\t\t\t\treturn filters;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Collect array params\n\t\t\t\t\tif (filters[key] !== undefined) {\n\t\t\t\t\t\t// Make sure the key is an array\n\t\t\t\t\t\tif (! Array.isArray(filters[key])) {\n\t\t\t\t\t\t\tfilters[key] = [\n\t\t\t\t\t\t\t\tfilters[key]\n\t\t\t\t\t\t\t];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfilters[key].push(value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfilters[key] = value;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn filters;\n\t\t\t\t}, {});\n\n\t\t\t\tfor (var param in params) {\n\t\t\t\t\t// Convert all param key-values to arrays\n\t\t\t\t\tif (! Array.isArray(params[param])) {\n\t\t\t\t\t\tparams[param] = [\n\t\t\t\t\t\t\tparams[param]\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\n\t\t\t\t\t// Init the search input\n\t\t\t\t\tif (param === 'keywords') {\n\t\t\t\t\t\t_doc.querySelectorAll('[data-ref=\"resources-keywords\"]')[0].value = params[param];\n\n\t\t\t\t\t\tfn.updateSearch();\n\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Init filter inputs\n\t\t\t\t\tfor (var i = 0; i < params[param].length; i++) {\n\t\t\t\t\t\tvar filterInputs = _doc.querySelectorAll('[data-resources-' + param + '-filter=\"' + params[param][i] + '\"]');\n\n\t\t\t\t\t\tfor (var j = 0; j < filterInputs.length; j++) {\n\t\t\t\t\t\t\tvar input = filterInputs[j];\n\t\t\t\t\t\t\tvar value = input.getAttribute('data-resources-' + param + '-filter');\n\n\t\t\t\t\t\t\tinput.checked = true;\n\n\t\t\t\t\t\t\tdata.filters[value] = {\n\t\t\t\t\t\t\t\tlabel: input.dataset.label,\n\t\t\t\t\t\t\t\tvalue: value,\n\t\t\t\t\t\t\t\ttype: param\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\tfn.fetch();\n\n\t\t\t\tif (Object.keys(params).length) {\n\t\t\t\t\tfn.scrollToResults('instant');\n\t\t\t\t}\n\t\t\t},\n\t\t\tsetCategoryFilter: function(e) {\n\t\t\t\tvar filterInputs = _doc.querySelectorAll('[data-resources-category-filter]');\n\t\t\t\tvar category = e.currentTarget.dataset.resourcesCategory;\n\n\t\t\t\tfor (var i = 0; i < filterInputs.length; i++) {\n\t\t\t\t\tif (filterInputs[i].value === category) {\n\t\t\t\t\t\tfilterInputs[i].checked = true;\n\n\t\t\t\t\t\tdata.filters[category] = {\n\t\t\t\t\t\t\tlabel: filterInputs[i].dataset.label,\n\t\t\t\t\t\t\tvalue: category,\n\t\t\t\t\t\t\ttype: 'category'\n\t\t\t\t\t\t};\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfilterInputs[i].checked = false;\n\n\t\t\t\t\t\tdelete data.filters[filterInputs[i].value];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfn.fetch();\n\t\t\t\tfn.scrollToResults('smooth');\n\t\t\t},\n\t\t\tupdateProductFilters: function(e) {\n\t\t\t\tfn.updateFilters(e, 'product');\n\t\t\t},\n\t\t\tupdateCategoryFilters: function(e) {\n\t\t\t\tfn.updateFilters(e, 'category');\n\t\t\t},\n\t\t\tupdateFilters: function(e, type) {\n\t\t\t\tvar $input = e.currentTarget\n\t\t\t\tvar name = $input.getAttribute('data-resources-' + type + '-filter');\n\t\t\t\tvar filterInputs = _doc.querySelectorAll('[data-resources-' + type + '-filter=\"' + name + '\"]');\n\n\t\t\t\tfor (var i = 0; i < filterInputs.length; i++) {\n\t\t\t\t\tif (filterInputs[i] !== $input) {\n\t\t\t\t\t\tfilterInputs[i].checked = $input.checked;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (name in data.filters) {\n\t\t\t\t\tdelete data.filters[name];\n\t\t\t\t} else {\n\t\t\t\t\tdata.filters[name] = {\n\t\t\t\t\t\tlabel: $input.dataset.label,\n\t\t\t\t\t\tvalue: $input.value,\n\t\t\t\t\t\ttype: type\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tfn.fetch();\n\t\t\t},\n\t\t\tremoveFilter: function(e) {\n\t\t\t\tvar $btn = e.currentTarget;\n\t\t\t\tvar type = $btn.dataset.type;\n\t\t\t\tvar value = $btn.dataset.value;\n\n\t\t\t\tvar filterInputs = _doc.querySelectorAll('[data-resources-' + type + '-filter=\"' + value + '\"]');\n\n\t\t\t\tfor (var i = 0; i < filterInputs.length; i++) {\n\t\t\t\t\tfilterInputs[i].checked = false;\n\t\t\t\t}\n\n\t\t\t\tdelete data.filters[value];\n\n\t\t\t\tfn.fetch();\n\n\t\t\t\t$btn.removeEventListener('click', fn.removeFilter);\n\t\t\t\t$btn.remove();\n\t\t\t},\n\t\t\tresetFilters: function() {\n\t\t\t\tvar filterInputs = _doc.querySelectorAll(\n\t\t\t\t\t'[data-resources-product-filter], [data-resources-category-filter]'\n\t\t\t\t);\n\n\t\t\t\tfor (var i = 0; i < filterInputs.length; i++) {\n\t\t\t\t\tfilterInputs[i].checked = false;\n\t\t\t\t}\n\n\t\t\t\tdata.filters = {};\n\n\t\t\t\tfn.fetch();\n\t\t\t},\n\t\t\tupdateResultFilters: function() {\n\t\t\t\tvar $resultFiltersContainer = _doc.querySelectorAll('[data-ref=\"resources-results-filters\"]')[0];\n\n\t\t\t\tif ($resultFiltersContainer) {\n\t\t\t\t\t// var sortedFilters = utils.sortObjByKey(data.filters);\n\t\t\t\t\tvar sortedFilters = data.filters;\n\n\t\t\t\t\t$resultFiltersContainer.innerHTML = '';\n\n\t\t\t\t\tfor (var key in sortedFilters) {\n\t\t\t\t\t\tvar $btn = _doc.createElement('div');\n\n\t\t\t\t\t\t$btn.classList.add('resources-results__filter');\n\t\t\t\t\t\t$btn.setAttribute('data-type', sortedFilters[key].type);\n\t\t\t\t\t\t$btn.setAttribute('data-value', sortedFilters[key].value);\n\t\t\t\t\t\t$btn.innerHTML = sortedFilters[key].label;\n\n\t\t\t\t\t\t$btn.addEventListener('click', fn.removeFilter);\n\n\t\t\t\t\t\t$resultFiltersContainer.appendChild($btn);\n\t\t\t\t\t}\n\n\t\t\t\t\tvar $reset = _doc.createElement('div');\n\n\t\t\t\t\t$reset.classList.add('resources-results__clear');\n\t\t\t\t\t$reset.innerHTML = 'Clear Filters';\n\n\t\t\t\t\t$reset.addEventListener('click', fn.resetFilters);\n\n\t\t\t\t\t$resultFiltersContainer.appendChild($reset);\n\t\t\t\t}\n\t\t\t},\n\t\t\tupdatePadinationLinks: function() {\n\t\t\t\tvar links = _doc.querySelectorAll('[data-ref=\"pagination-link\"]');\n\n\t\t\t\tvar handle = {\n\t\t\t\t\tclick: function(e) {\n\t\t\t\t\t\tvar url = e.currentTarget.href;\n\n\t\t\t\t\t\tfn.fetch(url);\n\n\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Add pagination link event listeners\n\t\t\t\tfor (var i = 0; i < links.length; i++) {\n\t\t\t\t\tlinks[i].addEventListener('click',handle.click);\n\t\t\t\t}\n\t\t\t},\n\t\t\tupdateSortOrder: function(e) {\n\t\t\t\tdata.orderBy = e.currentTarget.value;\n\n\t\t\t\t// Only fetch results when we have filters or keywords set\n\t\t\t\tif (! utils.objIsEmpty(data.filters) || (typeof data.keywords === 'string' && data.keywords.length > 0)) {\n\t\t\t\t\tfn.fetch();\n\t\t\t\t}\n\t\t\t},\n\t\t\tupdateList: function(html) {\n\t\t\t\tvar $resources = _doc.querySelectorAll('[data-ref=\"resources-resources\"]')[0];\n\n\t\t\t\t$resources.innerHTML = html;\n\n\t\t\t\t$resources.classList.remove('-is-updating');\n\n\t\t\t\tfn.updateResultFilters();\n\t\t\t\tfn.updatePadinationLinks();\n\n\t\t\t\tInitResourceLinkPreviews();\n\t\t\t},\n\t\t\tupdateSearch: function() {\n\t\t\t\tvar $clearButton = _doc.querySelectorAll('[data-ref=\"resources-search-clear\"]')[0];\n\t\t\t\tvar $keywordsInput = _doc.querySelectorAll('[data-ref=\"resources-keywords\"]')[0];\n\n\t\t\t\tdata.keywords = $keywordsInput.value;\n\n\t\t\t\t$clearButton.addEventListener('click', fn.resetSearch);\n\t\t\t\t$clearButton.classList.add('-is-visible');\n\t\t\t},\n\t\t\tresetSearch: function() {\n\t\t\t\tvar $clearButton = _doc.querySelectorAll('[data-ref=\"resources-search-clear\"]')[0];\n\t\t\t\tvar $keywordsInput = _doc.querySelectorAll('[data-ref=\"resources-keywords\"]')[0];\n\n\t\t\t\t$keywordsInput.value = '';\n\n\t\t\t\tdelete data.keywords;\n\n\t\t\t\tfn.fetch();\n\n\t\t\t\t$clearButton.classList.remove('-is-visible');\n\t\t\t\t$clearButton.removeEventListener('click', fn.resetSearch);\n\t\t\t},\n\t\t\tsearch: function(e) {\n\t\t\t\tfn.updateSearch();\n\n\t\t\t\tif (data.keywords.trim() !== '') {\n\t\t\t\t\tfn.fetch();\n\t\t\t\t} else {\n\t\t\t\t\tfn.resetSearch();\n\t\t\t\t}\n\n\t\t\t\te.preventDefault();\n\t\t\t},\n\t\t\tscrollToResults: function(behavior) {\n\t\t\t\tvar $resourcesContent = _doc.querySelectorAll('[data-ref=\"resources-content\"]')[0];\n\t\t\t\tvar $header = _doc.querySelectorAll('[data-ref=\"header\"]')[0];\n\t\t\t\tvar breakpoint = Breakpoint();\n\n\t\t\t\t_win.scrollTo({\n\t\t\t\t\ttop: _win.pageYOffset + $resourcesContent.getBoundingClientRect().top - (breakpoint < 4 ? 0 : $header.offsetHeight),\n\t\t\t\t\tleft: 0,\n\t\t\t\t\tbehavior: behavior || 'auto'\n\t\t\t\t});\n\t\t\t},\n\t\t\tfetch: function(url) {\n\t\t\t\tvar query = '';\n\n\t\t\t\tfor (var filter in data.filters) {\n\t\t\t\t\tquery += (query ? '&' : '') + data.filters[filter].type + '[]=' + data.filters[filter].value\n\t\t\t\t}\n\n\t\t\t\tif (data.keywords) {\n\t\t\t\t\tquery += (query ? '&' : '') + 'keywords=' + data.keywords;\n\t\t\t\t}\n\n\t\t\t\tif (data.orderBy) {\n\t\t\t\t\tquery += (query ? '&' : '') + 'orderBy=' + data.orderBy;\n\t\t\t\t}\n\n\t\t\t\tvar xmlhttp = new XMLHttpRequest();\n\n\t\t\t\txmlhttp.onreadystatechange = function() {\n\t\t\t\t\tif (xmlhttp.readyState == XMLHttpRequest.DONE) {\n\t\t\t\t\t\tif (xmlhttp.status == 200) {\n\t\t\t\t\t\t\t// fn.update(form, xmlhttp.responseText);\n\t\t\t\t\t\t\t// console.log(xmlhttp.responseText);\n\t\t\t\t\t\t\tfn.updateList(xmlhttp.responseText);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// TODO: handle errors\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\txmlhttp.open(\"POST\", url || '/resources/search', true);\n\t\t\t\txmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\t\t\t\txmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n\t\t\t\t// xmlhttp.setRequestHeader('Accept', 'application/json');\n\t\t\t\txmlhttp.send(query);\n\n\t\t\t\t// TODO: Show loading indicator\n\t\t\t\t// fn.updateList('Loading...');\n\t\t\t\tvar $resources = _doc.querySelectorAll('[data-ref=\"resources-resources\"]')[0];\n\n\t\t\t\t$resources.classList.add('-is-updating');\n\n\t\t\t\t$resources.innerHTML = '
';\n\t\t\t}\n\t\t};\n\n\t\tvar utils = {\n\t\t\tobjIsEmpty: function(obj) {\n\t\t\t\tfor(var prop in obj) {\n\t\t\t\t\tif (obj.hasOwnProperty(prop)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tsortObjByKey: function(obj) {\n\t\t\t\tvar keys = [];\n\t\t\t\tvar sorted = {};\n\n\t\t\t\tfor (var key in obj) {\n\t\t\t\t\tkeys.push(key);\n\t\t\t\t}\n\n\t\t\t\tkeys.sort();\n\n\t\t\t\tfor (var i = 0; i < keys.length; i++) {\n\t\t\t\t\tsorted[keys[i]] = obj[keys[i]];\n\t\t\t\t}\n\n\t\t\t\treturn sorted;\n\t\t\t}\n\t\t};\n\n\t\tsearchForm[0].addEventListener('submit', fn.search);\n\n\t\tfor (var i = 0; i < categoryLinks.length; i++) {\n\t\t\tcategoryLinks[i].addEventListener('click', fn.setCategoryFilter);\n\t\t}\n\n\t\tfor (var i = 0; i < productFilters.length; i++) {\n\t\t\tproductFilters[i].addEventListener('input', fn.updateProductFilters);\n\t\t}\n\n\t\tfor (var i = 0; i < categoryFilters.length; i++) {\n\t\t\tcategoryFilters[i].addEventListener('input', fn.updateCategoryFilters);\n\t\t}\n\n\t\tfor (var i = 0; i < sortOrder.length; i++) {\n\t\t\tsortOrder[i].addEventListener('input', fn.updateSortOrder);\n\t\t}\n\n\t\tfn.init();\n\t}\n\n\tfunction InitResourceLinkPreviews()\n\t{\n\t\tvar links = _doc.querySelectorAll('[data-ref=\"resources-link\"]');\n\n\t\tvar $preview = null;\n\t\tvar visibleClass = '-is-visible';\n\t\tvar clientX = 0;\n\t\tvar clientY = 0;\n\n\t\tvar handle = {\n\t\t\tmouseenter: function(e) {\n\t\t\t\tvar breakpoint = Breakpoint();\n\n\t\t\t\tif (breakpoint < 4) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t$preview = e.currentTarget.querySelectorAll('[data-ref=\"resources-link-preview\"]')[0];\n\n\t\t\t\tif ($preview) {\n\t\t\t\t\t$preview.classList.add(visibleClass);\n\n\t\t\t\t\t_doc.addEventListener('mousemove', handle.mousemove, true);\n\t\t\t\t}\n\t\t\t},\n\t\t\tmouseleave: function() {\n\t\t\t\tfn.reset();\n\n\t\t\t\t$preview = null;\n\t\t\t},\n\t\t\tmousemove: function(e) {\n\t\t\t\tif ($preview) {\n\t\t\t\t\tclientX = e.clientX + 2;\n\t\t\t\t\tclientY = e.clientY + 8;\n\n\t\t\t\t\t_win.requestAnimationFrame(fn.update);\n\t\t\t\t}\n\t\t\t},\n\t\t\tresize: function() {\n\t\t\t\tvar breakpoint = Breakpoint();\n\n\t\t\t\tif (breakpoint < 4) {\n\t\t\t\t\tfn.reset();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tvar fn = {\n\t\t\tupdate: function() {\n\t\t\t\tif ($preview) {\n\t\t\t\t\t$preview.style.left = clientX + 'px';\n\t\t\t\t\t$preview.style.top = clientY + 'px';\n\t\t\t\t}\n\t\t\t},\n\t\t\treset: function() {\n\t\t\t\tif ($preview) {\n\t\t\t\t\t$preview.classList.remove(visibleClass);\n\n\t\t\t\t\tclientX = 0;\n\t\t\t\t\tclientY = 0;\n\n\t\t\t\t\t_doc.removeEventListener('mousemove', handle.mousemove, true);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tfor (var i = 0; i < links.length; i++) {\n\t\t\tlinks[i].addEventListener('mouseenter', handle.mouseenter);\n\t\t\tlinks[i].addEventListener('mouseleave', handle.mouseleave);\n\t\t}\n\n\t\t// Window events\n\t\t_win.addEventListener('resize', handle.resize, true);\n\t}\n\n\tfunction InitResourceLayoutToggles()\n\t{\n\t\tvar $content = _doc.querySelectorAll('[data-ref=\"resources-content\"]');\n\t\tvar $listToggle = _doc.querySelectorAll('[data-ref=\"resources-list\"]');\n\t\tvar $gridToggle = _doc.querySelectorAll('[data-ref=\"resources-grid\"]');\n\n\t\t// If required elemets are missing, return\n\t\tif (! $content.length || ! $listToggle.length || ! $gridToggle.length) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar activeToggleClass = '-is-active';\n\t\tvar gridClass = '-grid';\n\n\t\t// Active layout\n\t\tvar layout = 'list';\n\n\t\tvar fn = {\n\t\t\tshowList: function(e) {\n\t\t\t\t// Update the toggle buttons\n\t\t\t\t$gridToggle[0].classList.remove(activeToggleClass);\n\t\t\t\t$listToggle[0].classList.add(activeToggleClass);\n\n\t\t\t\t// Update the layout\n\t\t\t\t$content[0].classList.remove(gridClass);\n\n\t\t\t\t// Update the active layout\n\t\t\t\tlayout = 'list';\n\t\t\t},\n\t\t\tshowGrid: function() {\n\t\t\t\t// Update the toggle buttons\n\t\t\t\t$listToggle[0].classList.remove(activeToggleClass);\n\t\t\t\t$gridToggle[0].classList.add(activeToggleClass);\n\n\t\t\t\t// Update the layout\n\t\t\t\t$content[0].classList.add(gridClass);\n\n\t\t\t\t// Update the active layout\n\t\t\t\tlayout = 'grid';\n\t\t\t}\n\t\t};\n\n\t\tvar breakpoint = Breakpoint();\n\n\t\tvar handle = {\n\t\t\tresizeEvent: function() {\n\t\t\t\tbreakpoint = Breakpoint();\n\n\t\t\t\t// Reset the layout when transitioning\n\t\t\t\t// from desktop to mobile\n\t\t\t\tif (layout == 'grid' && breakpoint < 4) {\n\t\t\t\t\tfn.showList();\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\n\t\t// Toggle events\n\t\t$listToggle[0].addEventListener('click', fn.showList);\n\t\t$gridToggle[0].addEventListener('click', fn.showGrid);\n\n\t\t// Window events\n\t\t_win.addEventListener('resize', handle.resizeEvent, true);\n\t}\n\n\t// Company\n\n\t// Initialize custom Splide JS company timeline slider\n\tfunction InitTimelineSlider()\n\t{\n\t\tvar timeline = _doc.querySelectorAll('[data-ref=\"timeline\"]');\n\n\t\t// If there's no timeline to control, return\n\t\tif (! timeline.length) {\n\t\t\treturn;\n\t\t}\n\n\t\t// var sliderPrev = _doc.querySelectorAll('[data-ref=\"slider-prev\"]')[0];\n\t\t// var sliderNext = _doc.querySelectorAll('[data-ref=\"slider-next\"]')[0];\n\t\tvar activeIndex = 0;\n\n\t\tvar fn = {\n\t\t\t// Navigate to the previous slide unless\n\t\t\t// the first slide is active\n\t\t\tprev: function() {\n\t\t\t\tif (activeIndex === 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tactiveIndex -= 1;\n\n\t\t\t\ttimelineSlider.go(activeIndex);\n\t\t\t},\n\t\t\t// Navigate to the next slide unless\n\t\t\t// the last slide is active\n\t\t\tnext: function() {\n\t\t\t\tif (activeIndex === bullets.length) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tactiveIndex += 1;\n\n\t\t\t\ttimelineSlider.go(activeIndex);\n\t\t\t}\n\t\t};\n\n\t\t// Initialize timeline slider\n\t\t// https://splidejs.com/guides/options/\n\t\tvar timelineSlider = new Splide(timeline[0], {\n\t\t\tpagination: false,\n\t\t\tarrows: false,\n\t\t\tautoWidth: true,\n\t\t\tdrag: 'free',\n\t\t\tgap: '0'\n\t\t})\n\t\t.mount();\n\n\t\ttimelineSlider.on('move', function(index) {\n\t\t\tactiveIndex = index;\n\t\t});\n\n\t\t// Listen from previous and next pagination click events\n\t\t// sliderPrev.addEventListener('click', fn.prev);\n\t\t// sliderNext.addEventListener('click', fn.next);\n\t}\n\n\tfunction InitOpeningFilters()\n\t{\n\t\tvar filtersForm = _doc.querySelectorAll('[data-ref=\"filters\"]');\n\t\tvar filters = _doc.querySelectorAll('[data-ref=\"filter\"]');\n\t\tvar openings = _doc.querySelectorAll('[data-ref=\"openings\"]');\n\t\tvar reset = _doc.querySelectorAll('[data-ref=\"reset\"]');\n\n\t\t// If required elemets are missing, return\n\t\tif (\n\t\t\t! filtersForm.length ||\n\t\t\t! filters.length ||\n\t\t\t! openings.length ||\n\t\t\t! reset.length\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar filtered = false;\n\n\t\tvar fn = {\n\t\t\tsubmit: function(e) {\n\t\t\t\te.preventDefault();\n\n\t\t\t\tvar formData = new FormData();\n\n\t\t\t\tfiltered = false;\n\n\t\t\t\t// Add non-empty filters to the request\n\t\t\t\tfor (var i = 0; i < filters.length; i++) {\n\t\t\t\t\tvar value = filters[i].value.trim();\n\n\t\t\t\t\tif (value.length) {\n\t\t\t\t\t\tformData.append(filters[i].name, value);\n\t\t\t\t\t\tfiltered = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tvar xmlhttp = new XMLHttpRequest();\n\n\t\t\t\txmlhttp.onreadystatechange = function() {\n\t\t\t\t\tif (xmlhttp.readyState == XMLHttpRequest.DONE) {\n\t\t\t\t\t\tif (xmlhttp.status == 200) {\n\t\t\t\t\t\t\tfn.update(xmlhttp.responseText);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// TODO: handle errors\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\txmlhttp.open(\"POST\", filtersForm[0].action, true);\n\t\t\t\txmlhttp.setRequestHeader('X-PJAX', 'true');\n\t\t\t\txmlhttp.send(formData);\n\n\t\t\t\t// Show a loading spinner\n\t\t\t\topenings[0].firstChild.innerHTML = '
';\n\t\t\t},\n\t\t\tupdate: function(data) {\n\t\t\t\t// Parse the PJAX HTML response\n\t\t\t\t// http://youmightnotneedjquery.com/#parse_html\n\t\t\t\tvar html = _doc.implementation.createHTMLDocument();\n\t\t\t\thtml.body.innerHTML = data;\n\n\t\t\t\t// Update the openings\n\t\t\t\topenings[0].innerHTML = html.querySelectorAll('[data-ref=\"openings\"]')[0].innerHTML;\n\n\t\t\t\t// Toggle the reset button\n\t\t\t\treset[0].classList.toggle('js-hide', ! filtered);\n\t\t\t},\n\t\t\treset: function(e) {\n\t\t\t\te.preventDefault();\n\n\t\t\t\t// Reset the form\n\t\t\t\tfiltersForm[0].reset();\n\n\t\t\t\t// Trigger a form submit event\n\t\t\t\t// https://stackoverflow.com/a/44916384\n\t\t\t\tfiltersForm[0].dispatchEvent(\n\t\t\t\t\tnew Event('submit')\n\t\t\t\t);\n\t\t\t}\n\t\t};\n\n\t\t// Handle HTML form submission events\n\t\tfiltersForm[0].addEventListener('submit', fn.submit);\n\n\t\t// Submit the form whenever a filter value changes\n\t\tfor (var i = 0; i < filters.length; i++) {\n\t\t\tfilters[i].addEventListener('change', fn.submit);\n\t\t}\n\n\t\t// Delegate reset link click events\n\t\t_doc.addEventListener('click', function(e) {\n\t\t\tif (e.target.dataset.ref === 'reset') {\n\t\t\t\tfn.reset(e);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn {\n\t\tInit: Init\n\t}\n}();\n\nvar ready = function ()\n{\n\t_doc.addEventListener('DOMContentLoaded', ready);\n\tApp.Init();\n}\n\n_doc.readyState === 'complete' ?\n\tready() : _doc.addEventListener('DOMContentLoaded', ready);","function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\n\n/*!\n * Splide.js\n * Version : 4.1.2\n * License : MIT\n * Copyright: 2022 Naotoshi Fujita\n */\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Splide = factory());\n})(this, function () {\n 'use strict';\n\n var MEDIA_PREFERS_REDUCED_MOTION = \"(prefers-reduced-motion: reduce)\";\n var CREATED = 1;\n var MOUNTED = 2;\n var IDLE = 3;\n var MOVING = 4;\n var SCROLLING = 5;\n var DRAGGING = 6;\n var DESTROYED = 7;\n var STATES = {\n CREATED: CREATED,\n MOUNTED: MOUNTED,\n IDLE: IDLE,\n MOVING: MOVING,\n SCROLLING: SCROLLING,\n DRAGGING: DRAGGING,\n DESTROYED: DESTROYED\n };\n\n function empty(array) {\n array.length = 0;\n }\n\n function slice(arrayLike, start, end) {\n return Array.prototype.slice.call(arrayLike, start, end);\n }\n\n function apply(func) {\n return func.bind.apply(func, [null].concat(slice(arguments, 1)));\n }\n\n var nextTick = setTimeout;\n\n var noop = function noop() {};\n\n function raf(func) {\n return requestAnimationFrame(func);\n }\n\n function typeOf(type, subject) {\n return typeof subject === type;\n }\n\n function isObject(subject) {\n return !isNull(subject) && typeOf(\"object\", subject);\n }\n\n var isArray = Array.isArray;\n var isFunction = apply(typeOf, \"function\");\n var isString = apply(typeOf, \"string\");\n var isUndefined = apply(typeOf, \"undefined\");\n\n function isNull(subject) {\n return subject === null;\n }\n\n function isHTMLElement(subject) {\n try {\n return subject instanceof (subject.ownerDocument.defaultView || window).HTMLElement;\n } catch (e) {\n return false;\n }\n }\n\n function toArray(value) {\n return isArray(value) ? value : [value];\n }\n\n function forEach(values, iteratee) {\n toArray(values).forEach(iteratee);\n }\n\n function includes(array, value) {\n return array.indexOf(value) > -1;\n }\n\n function push(array, items) {\n array.push.apply(array, toArray(items));\n return array;\n }\n\n function toggleClass(elm, classes, add) {\n if (elm) {\n forEach(classes, function (name) {\n if (name) {\n elm.classList[add ? \"add\" : \"remove\"](name);\n }\n });\n }\n }\n\n function addClass(elm, classes) {\n toggleClass(elm, isString(classes) ? classes.split(\" \") : classes, true);\n }\n\n function append(parent, children) {\n forEach(children, parent.appendChild.bind(parent));\n }\n\n function before(nodes, ref) {\n forEach(nodes, function (node) {\n var parent = (ref || node).parentNode;\n\n if (parent) {\n parent.insertBefore(node, ref);\n }\n });\n }\n\n function matches(elm, selector) {\n return isHTMLElement(elm) && (elm[\"msMatchesSelector\"] || elm.matches).call(elm, selector);\n }\n\n function children(parent, selector) {\n var children2 = parent ? slice(parent.children) : [];\n return selector ? children2.filter(function (child) {\n return matches(child, selector);\n }) : children2;\n }\n\n function child(parent, selector) {\n return selector ? children(parent, selector)[0] : parent.firstElementChild;\n }\n\n var ownKeys = Object.keys;\n\n function forOwn(object, iteratee, right) {\n if (object) {\n (right ? ownKeys(object).reverse() : ownKeys(object)).forEach(function (key) {\n key !== \"__proto__\" && iteratee(object[key], key);\n });\n }\n\n return object;\n }\n\n function assign(object) {\n slice(arguments, 1).forEach(function (source) {\n forOwn(source, function (value, key) {\n object[key] = source[key];\n });\n });\n return object;\n }\n\n function merge(object) {\n slice(arguments, 1).forEach(function (source) {\n forOwn(source, function (value, key) {\n if (isArray(value)) {\n object[key] = value.slice();\n } else if (isObject(value)) {\n object[key] = merge({}, isObject(object[key]) ? object[key] : {}, value);\n } else {\n object[key] = value;\n }\n });\n });\n return object;\n }\n\n function omit(object, keys) {\n forEach(keys || ownKeys(object), function (key) {\n delete object[key];\n });\n }\n\n function removeAttribute(elms, attrs) {\n forEach(elms, function (elm) {\n forEach(attrs, function (attr) {\n elm && elm.removeAttribute(attr);\n });\n });\n }\n\n function setAttribute(elms, attrs, value) {\n if (isObject(attrs)) {\n forOwn(attrs, function (value2, name) {\n setAttribute(elms, name, value2);\n });\n } else {\n forEach(elms, function (elm) {\n isNull(value) || value === \"\" ? removeAttribute(elm, attrs) : elm.setAttribute(attrs, String(value));\n });\n }\n }\n\n function create(tag, attrs, parent) {\n var elm = document.createElement(tag);\n\n if (attrs) {\n isString(attrs) ? addClass(elm, attrs) : setAttribute(elm, attrs);\n }\n\n parent && append(parent, elm);\n return elm;\n }\n\n function style(elm, prop, value) {\n if (isUndefined(value)) {\n return getComputedStyle(elm)[prop];\n }\n\n if (!isNull(value)) {\n elm.style[prop] = \"\" + value;\n }\n }\n\n function display(elm, display2) {\n style(elm, \"display\", display2);\n }\n\n function focus(elm) {\n elm[\"setActive\"] && elm[\"setActive\"]() || elm.focus({\n preventScroll: true\n });\n }\n\n function getAttribute(elm, attr) {\n return elm.getAttribute(attr);\n }\n\n function hasClass(elm, className) {\n return elm && elm.classList.contains(className);\n }\n\n function rect(target) {\n return target.getBoundingClientRect();\n }\n\n function remove(nodes) {\n forEach(nodes, function (node) {\n if (node && node.parentNode) {\n node.parentNode.removeChild(node);\n }\n });\n }\n\n function parseHtml(html) {\n return child(new DOMParser().parseFromString(html, \"text/html\").body);\n }\n\n function prevent(e, stopPropagation) {\n e.preventDefault();\n\n if (stopPropagation) {\n e.stopPropagation();\n e.stopImmediatePropagation();\n }\n }\n\n function query(parent, selector) {\n return parent && parent.querySelector(selector);\n }\n\n function queryAll(parent, selector) {\n return selector ? slice(parent.querySelectorAll(selector)) : [];\n }\n\n function removeClass(elm, classes) {\n toggleClass(elm, classes, false);\n }\n\n function timeOf(e) {\n return e.timeStamp;\n }\n\n function unit(value) {\n return isString(value) ? value : value ? value + \"px\" : \"\";\n }\n\n var PROJECT_CODE = \"splide\";\n var DATA_ATTRIBUTE = \"data-\" + PROJECT_CODE;\n\n function assert(condition, message) {\n if (!condition) {\n throw new Error(\"[\" + PROJECT_CODE + \"] \" + (message || \"\"));\n }\n }\n\n var min = Math.min,\n max = Math.max,\n floor = Math.floor,\n ceil = Math.ceil,\n abs = Math.abs;\n\n function approximatelyEqual(x, y, epsilon) {\n return abs(x - y) < epsilon;\n }\n\n function between(number, x, y, exclusive) {\n var minimum = min(x, y);\n var maximum = max(x, y);\n return exclusive ? minimum < number && number < maximum : minimum <= number && number <= maximum;\n }\n\n function clamp(number, x, y) {\n var minimum = min(x, y);\n var maximum = max(x, y);\n return min(max(minimum, number), maximum);\n }\n\n function sign(x) {\n return +(x > 0) - +(x < 0);\n }\n\n function format(string, replacements) {\n forEach(replacements, function (replacement) {\n string = string.replace(\"%s\", \"\" + replacement);\n });\n return string;\n }\n\n function pad(number) {\n return number < 10 ? \"0\" + number : \"\" + number;\n }\n\n var ids = {};\n\n function uniqueId(prefix) {\n return \"\" + prefix + pad(ids[prefix] = (ids[prefix] || 0) + 1);\n }\n\n function EventBinder() {\n var listeners = [];\n\n function bind(targets, events, callback, options) {\n forEachEvent(targets, events, function (target, event, namespace) {\n var isEventTarget = (\"addEventListener\" in target);\n var remover = isEventTarget ? target.removeEventListener.bind(target, event, callback, options) : target[\"removeListener\"].bind(target, callback);\n isEventTarget ? target.addEventListener(event, callback, options) : target[\"addListener\"](callback);\n listeners.push([target, event, namespace, callback, remover]);\n });\n }\n\n function unbind(targets, events, callback) {\n forEachEvent(targets, events, function (target, event, namespace) {\n listeners = listeners.filter(function (listener) {\n if (listener[0] === target && listener[1] === event && listener[2] === namespace && (!callback || listener[3] === callback)) {\n listener[4]();\n return false;\n }\n\n return true;\n });\n });\n }\n\n function dispatch(target, type, detail) {\n var e;\n var bubbles = true;\n\n if (typeof CustomEvent === \"function\") {\n e = new CustomEvent(type, {\n bubbles: bubbles,\n detail: detail\n });\n } else {\n e = document.createEvent(\"CustomEvent\");\n e.initCustomEvent(type, bubbles, false, detail);\n }\n\n target.dispatchEvent(e);\n return e;\n }\n\n function forEachEvent(targets, events, iteratee) {\n forEach(targets, function (target) {\n target && forEach(events, function (events2) {\n events2.split(\" \").forEach(function (eventNS) {\n var fragment = eventNS.split(\".\");\n iteratee(target, fragment[0], fragment[1]);\n });\n });\n });\n }\n\n function destroy() {\n listeners.forEach(function (data) {\n data[4]();\n });\n empty(listeners);\n }\n\n return {\n bind: bind,\n unbind: unbind,\n dispatch: dispatch,\n destroy: destroy\n };\n }\n\n var EVENT_MOUNTED = \"mounted\";\n var EVENT_READY = \"ready\";\n var EVENT_MOVE = \"move\";\n var EVENT_MOVED = \"moved\";\n var EVENT_CLICK = \"click\";\n var EVENT_ACTIVE = \"active\";\n var EVENT_INACTIVE = \"inactive\";\n var EVENT_VISIBLE = \"visible\";\n var EVENT_HIDDEN = \"hidden\";\n var EVENT_REFRESH = \"refresh\";\n var EVENT_UPDATED = \"updated\";\n var EVENT_RESIZE = \"resize\";\n var EVENT_RESIZED = \"resized\";\n var EVENT_DRAG = \"drag\";\n var EVENT_DRAGGING = \"dragging\";\n var EVENT_DRAGGED = \"dragged\";\n var EVENT_SCROLL = \"scroll\";\n var EVENT_SCROLLED = \"scrolled\";\n var EVENT_OVERFLOW = \"overflow\";\n var EVENT_DESTROY = \"destroy\";\n var EVENT_ARROWS_MOUNTED = \"arrows:mounted\";\n var EVENT_ARROWS_UPDATED = \"arrows:updated\";\n var EVENT_PAGINATION_MOUNTED = \"pagination:mounted\";\n var EVENT_PAGINATION_UPDATED = \"pagination:updated\";\n var EVENT_NAVIGATION_MOUNTED = \"navigation:mounted\";\n var EVENT_AUTOPLAY_PLAY = \"autoplay:play\";\n var EVENT_AUTOPLAY_PLAYING = \"autoplay:playing\";\n var EVENT_AUTOPLAY_PAUSE = \"autoplay:pause\";\n var EVENT_LAZYLOAD_LOADED = \"lazyload:loaded\";\n var EVENT_SLIDE_KEYDOWN = \"sk\";\n var EVENT_SHIFTED = \"sh\";\n var EVENT_END_INDEX_CHANGED = \"ei\";\n\n function EventInterface(Splide2) {\n var bus = Splide2 ? Splide2.event.bus : document.createDocumentFragment();\n var binder = EventBinder();\n\n function on(events, callback) {\n binder.bind(bus, toArray(events).join(\" \"), function (e) {\n callback.apply(callback, isArray(e.detail) ? e.detail : []);\n });\n }\n\n function emit(event) {\n binder.dispatch(bus, event, slice(arguments, 1));\n }\n\n if (Splide2) {\n Splide2.event.on(EVENT_DESTROY, binder.destroy);\n }\n\n return assign(binder, {\n bus: bus,\n on: on,\n off: apply(binder.unbind, bus),\n emit: emit\n });\n }\n\n function RequestInterval(interval, onInterval, onUpdate, limit) {\n var now = Date.now;\n var startTime;\n var rate = 0;\n var id;\n var paused = true;\n var count = 0;\n\n function update() {\n if (!paused) {\n rate = interval ? min((now() - startTime) / interval, 1) : 1;\n onUpdate && onUpdate(rate);\n\n if (rate >= 1) {\n onInterval();\n startTime = now();\n\n if (limit && ++count >= limit) {\n return pause();\n }\n }\n\n id = raf(update);\n }\n }\n\n function start(resume) {\n resume || cancel();\n startTime = now() - (resume ? rate * interval : 0);\n paused = false;\n id = raf(update);\n }\n\n function pause() {\n paused = true;\n }\n\n function rewind() {\n startTime = now();\n rate = 0;\n\n if (onUpdate) {\n onUpdate(rate);\n }\n }\n\n function cancel() {\n id && cancelAnimationFrame(id);\n rate = 0;\n id = 0;\n paused = true;\n }\n\n function set(time) {\n interval = time;\n }\n\n function isPaused() {\n return paused;\n }\n\n return {\n start: start,\n rewind: rewind,\n pause: pause,\n cancel: cancel,\n set: set,\n isPaused: isPaused\n };\n }\n\n function State(initialState) {\n var state = initialState;\n\n function set(value) {\n state = value;\n }\n\n function is(states) {\n return includes(toArray(states), state);\n }\n\n return {\n set: set,\n is: is\n };\n }\n\n function Throttle(func, duration) {\n var interval = RequestInterval(duration || 0, func, null, 1);\n return function () {\n interval.isPaused() && interval.start();\n };\n }\n\n function Media(Splide2, Components2, options) {\n var state = Splide2.state;\n var breakpoints = options.breakpoints || {};\n var reducedMotion = options.reducedMotion || {};\n var binder = EventBinder();\n var queries = [];\n\n function setup() {\n var isMin = options.mediaQuery === \"min\";\n ownKeys(breakpoints).sort(function (n, m) {\n return isMin ? +n - +m : +m - +n;\n }).forEach(function (key) {\n register(breakpoints[key], \"(\" + (isMin ? \"min\" : \"max\") + \"-width:\" + key + \"px)\");\n });\n register(reducedMotion, MEDIA_PREFERS_REDUCED_MOTION);\n update();\n }\n\n function destroy(completely) {\n if (completely) {\n binder.destroy();\n }\n }\n\n function register(options2, query) {\n var queryList = matchMedia(query);\n binder.bind(queryList, \"change\", update);\n queries.push([options2, queryList]);\n }\n\n function update() {\n var destroyed = state.is(DESTROYED);\n var direction = options.direction;\n var merged = queries.reduce(function (merged2, entry) {\n return merge(merged2, entry[1].matches ? entry[0] : {});\n }, {});\n omit(options);\n set(merged);\n\n if (options.destroy) {\n Splide2.destroy(options.destroy === \"completely\");\n } else if (destroyed) {\n destroy(true);\n Splide2.mount();\n } else {\n direction !== options.direction && Splide2.refresh();\n }\n }\n\n function reduce(enable) {\n if (matchMedia(MEDIA_PREFERS_REDUCED_MOTION).matches) {\n enable ? merge(options, reducedMotion) : omit(options, ownKeys(reducedMotion));\n }\n }\n\n function set(opts, base, notify) {\n merge(options, opts);\n base && merge(Object.getPrototypeOf(options), opts);\n\n if (notify || !state.is(CREATED)) {\n Splide2.emit(EVENT_UPDATED, options);\n }\n }\n\n return {\n setup: setup,\n destroy: destroy,\n reduce: reduce,\n set: set\n };\n }\n\n var ARROW = \"Arrow\";\n var ARROW_LEFT = ARROW + \"Left\";\n var ARROW_RIGHT = ARROW + \"Right\";\n var ARROW_UP = ARROW + \"Up\";\n var ARROW_DOWN = ARROW + \"Down\";\n var RTL = \"rtl\";\n var TTB = \"ttb\";\n var ORIENTATION_MAP = {\n width: [\"height\"],\n left: [\"top\", \"right\"],\n right: [\"bottom\", \"left\"],\n x: [\"y\"],\n X: [\"Y\"],\n Y: [\"X\"],\n ArrowLeft: [ARROW_UP, ARROW_RIGHT],\n ArrowRight: [ARROW_DOWN, ARROW_LEFT]\n };\n\n function Direction(Splide2, Components2, options) {\n function resolve(prop, axisOnly, direction) {\n direction = direction || options.direction;\n var index = direction === RTL && !axisOnly ? 1 : direction === TTB ? 0 : -1;\n return ORIENTATION_MAP[prop] && ORIENTATION_MAP[prop][index] || prop.replace(/width|left|right/i, function (match, offset) {\n var replacement = ORIENTATION_MAP[match.toLowerCase()][index] || match;\n return offset > 0 ? replacement.charAt(0).toUpperCase() + replacement.slice(1) : replacement;\n });\n }\n\n function orient(value) {\n return value * (options.direction === RTL ? 1 : -1);\n }\n\n return {\n resolve: resolve,\n orient: orient\n };\n }\n\n var ROLE = \"role\";\n var TAB_INDEX = \"tabindex\";\n var DISABLED = \"disabled\";\n var ARIA_PREFIX = \"aria-\";\n var ARIA_CONTROLS = ARIA_PREFIX + \"controls\";\n var ARIA_CURRENT = ARIA_PREFIX + \"current\";\n var ARIA_SELECTED = ARIA_PREFIX + \"selected\";\n var ARIA_LABEL = ARIA_PREFIX + \"label\";\n var ARIA_LABELLEDBY = ARIA_PREFIX + \"labelledby\";\n var ARIA_HIDDEN = ARIA_PREFIX + \"hidden\";\n var ARIA_ORIENTATION = ARIA_PREFIX + \"orientation\";\n var ARIA_ROLEDESCRIPTION = ARIA_PREFIX + \"roledescription\";\n var ARIA_LIVE = ARIA_PREFIX + \"live\";\n var ARIA_BUSY = ARIA_PREFIX + \"busy\";\n var ARIA_ATOMIC = ARIA_PREFIX + \"atomic\";\n var ALL_ATTRIBUTES = [ROLE, TAB_INDEX, DISABLED, ARIA_CONTROLS, ARIA_CURRENT, ARIA_LABEL, ARIA_LABELLEDBY, ARIA_HIDDEN, ARIA_ORIENTATION, ARIA_ROLEDESCRIPTION];\n var CLASS_PREFIX = PROJECT_CODE + \"__\";\n var STATUS_CLASS_PREFIX = \"is-\";\n var CLASS_ROOT = PROJECT_CODE;\n var CLASS_TRACK = CLASS_PREFIX + \"track\";\n var CLASS_LIST = CLASS_PREFIX + \"list\";\n var CLASS_SLIDE = CLASS_PREFIX + \"slide\";\n var CLASS_CLONE = CLASS_SLIDE + \"--clone\";\n var CLASS_CONTAINER = CLASS_SLIDE + \"__container\";\n var CLASS_ARROWS = CLASS_PREFIX + \"arrows\";\n var CLASS_ARROW = CLASS_PREFIX + \"arrow\";\n var CLASS_ARROW_PREV = CLASS_ARROW + \"--prev\";\n var CLASS_ARROW_NEXT = CLASS_ARROW + \"--next\";\n var CLASS_PAGINATION = CLASS_PREFIX + \"pagination\";\n var CLASS_PAGINATION_PAGE = CLASS_PAGINATION + \"__page\";\n var CLASS_PROGRESS = CLASS_PREFIX + \"progress\";\n var CLASS_PROGRESS_BAR = CLASS_PROGRESS + \"__bar\";\n var CLASS_TOGGLE = CLASS_PREFIX + \"toggle\";\n var CLASS_SPINNER = CLASS_PREFIX + \"spinner\";\n var CLASS_SR = CLASS_PREFIX + \"sr\";\n var CLASS_INITIALIZED = STATUS_CLASS_PREFIX + \"initialized\";\n var CLASS_ACTIVE = STATUS_CLASS_PREFIX + \"active\";\n var CLASS_PREV = STATUS_CLASS_PREFIX + \"prev\";\n var CLASS_NEXT = STATUS_CLASS_PREFIX + \"next\";\n var CLASS_VISIBLE = STATUS_CLASS_PREFIX + \"visible\";\n var CLASS_LOADING = STATUS_CLASS_PREFIX + \"loading\";\n var CLASS_FOCUS_IN = STATUS_CLASS_PREFIX + \"focus-in\";\n var CLASS_OVERFLOW = STATUS_CLASS_PREFIX + \"overflow\";\n var STATUS_CLASSES = [CLASS_ACTIVE, CLASS_VISIBLE, CLASS_PREV, CLASS_NEXT, CLASS_LOADING, CLASS_FOCUS_IN, CLASS_OVERFLOW];\n var CLASSES = {\n slide: CLASS_SLIDE,\n clone: CLASS_CLONE,\n arrows: CLASS_ARROWS,\n arrow: CLASS_ARROW,\n prev: CLASS_ARROW_PREV,\n next: CLASS_ARROW_NEXT,\n pagination: CLASS_PAGINATION,\n page: CLASS_PAGINATION_PAGE,\n spinner: CLASS_SPINNER\n };\n\n function closest(from, selector) {\n if (isFunction(from.closest)) {\n return from.closest(selector);\n }\n\n var elm = from;\n\n while (elm && elm.nodeType === 1) {\n if (matches(elm, selector)) {\n break;\n }\n\n elm = elm.parentElement;\n }\n\n return elm;\n }\n\n var FRICTION = 5;\n var LOG_INTERVAL = 200;\n var POINTER_DOWN_EVENTS = \"touchstart mousedown\";\n var POINTER_MOVE_EVENTS = \"touchmove mousemove\";\n var POINTER_UP_EVENTS = \"touchend touchcancel mouseup click\";\n\n function Elements(Splide2, Components2, options) {\n var _EventInterface = EventInterface(Splide2),\n on = _EventInterface.on,\n bind = _EventInterface.bind;\n\n var root = Splide2.root;\n var i18n = options.i18n;\n var elements = {};\n var slides = [];\n var rootClasses = [];\n var trackClasses = [];\n var track;\n var list;\n var isUsingKey;\n\n function setup() {\n collect();\n init();\n update();\n }\n\n function mount() {\n on(EVENT_REFRESH, destroy);\n on(EVENT_REFRESH, setup);\n on(EVENT_UPDATED, update);\n bind(document, POINTER_DOWN_EVENTS + \" keydown\", function (e) {\n isUsingKey = e.type === \"keydown\";\n }, {\n capture: true\n });\n bind(root, \"focusin\", function () {\n toggleClass(root, CLASS_FOCUS_IN, !!isUsingKey);\n });\n }\n\n function destroy(completely) {\n var attrs = ALL_ATTRIBUTES.concat(\"style\");\n empty(slides);\n removeClass(root, rootClasses);\n removeClass(track, trackClasses);\n removeAttribute([track, list], attrs);\n removeAttribute(root, completely ? attrs : [\"style\", ARIA_ROLEDESCRIPTION]);\n }\n\n function update() {\n removeClass(root, rootClasses);\n removeClass(track, trackClasses);\n rootClasses = getClasses(CLASS_ROOT);\n trackClasses = getClasses(CLASS_TRACK);\n addClass(root, rootClasses);\n addClass(track, trackClasses);\n setAttribute(root, ARIA_LABEL, options.label);\n setAttribute(root, ARIA_LABELLEDBY, options.labelledby);\n }\n\n function collect() {\n track = find(\".\" + CLASS_TRACK);\n list = child(track, \".\" + CLASS_LIST);\n assert(track && list, \"A track/list element is missing.\");\n push(slides, children(list, \".\" + CLASS_SLIDE + \":not(.\" + CLASS_CLONE + \")\"));\n forOwn({\n arrows: CLASS_ARROWS,\n pagination: CLASS_PAGINATION,\n prev: CLASS_ARROW_PREV,\n next: CLASS_ARROW_NEXT,\n bar: CLASS_PROGRESS_BAR,\n toggle: CLASS_TOGGLE\n }, function (className, key) {\n elements[key] = find(\".\" + className);\n });\n assign(elements, {\n root: root,\n track: track,\n list: list,\n slides: slides\n });\n }\n\n function init() {\n var id = root.id || uniqueId(PROJECT_CODE);\n var role = options.role;\n root.id = id;\n track.id = track.id || id + \"-track\";\n list.id = list.id || id + \"-list\";\n\n if (!getAttribute(root, ROLE) && root.tagName !== \"SECTION\" && role) {\n setAttribute(root, ROLE, role);\n }\n\n setAttribute(root, ARIA_ROLEDESCRIPTION, i18n.carousel);\n setAttribute(list, ROLE, \"presentation\");\n }\n\n function find(selector) {\n var elm = query(root, selector);\n return elm && closest(elm, \".\" + CLASS_ROOT) === root ? elm : void 0;\n }\n\n function getClasses(base) {\n return [base + \"--\" + options.type, base + \"--\" + options.direction, options.drag && base + \"--draggable\", options.isNavigation && base + \"--nav\", base === CLASS_ROOT && CLASS_ACTIVE];\n }\n\n return assign(elements, {\n setup: setup,\n mount: mount,\n destroy: destroy\n });\n }\n\n var SLIDE = \"slide\";\n var LOOP = \"loop\";\n var FADE = \"fade\";\n\n function Slide$1(Splide2, index, slideIndex, slide) {\n var event = EventInterface(Splide2);\n var on = event.on,\n emit = event.emit,\n bind = event.bind;\n var Components = Splide2.Components,\n root = Splide2.root,\n options = Splide2.options;\n var isNavigation = options.isNavigation,\n updateOnMove = options.updateOnMove,\n i18n = options.i18n,\n pagination = options.pagination,\n slideFocus = options.slideFocus;\n var resolve = Components.Direction.resolve;\n var styles = getAttribute(slide, \"style\");\n var label = getAttribute(slide, ARIA_LABEL);\n var isClone = slideIndex > -1;\n var container = child(slide, \".\" + CLASS_CONTAINER);\n var destroyed;\n\n function mount() {\n if (!isClone) {\n slide.id = root.id + \"-slide\" + pad(index + 1);\n setAttribute(slide, ROLE, pagination ? \"tabpanel\" : \"group\");\n setAttribute(slide, ARIA_ROLEDESCRIPTION, i18n.slide);\n setAttribute(slide, ARIA_LABEL, label || format(i18n.slideLabel, [index + 1, Splide2.length]));\n }\n\n listen();\n }\n\n function listen() {\n bind(slide, \"click\", apply(emit, EVENT_CLICK, self));\n bind(slide, \"keydown\", apply(emit, EVENT_SLIDE_KEYDOWN, self));\n on([EVENT_MOVED, EVENT_SHIFTED, EVENT_SCROLLED], update);\n on(EVENT_NAVIGATION_MOUNTED, initNavigation);\n\n if (updateOnMove) {\n on(EVENT_MOVE, onMove);\n }\n }\n\n function destroy() {\n destroyed = true;\n event.destroy();\n removeClass(slide, STATUS_CLASSES);\n removeAttribute(slide, ALL_ATTRIBUTES);\n setAttribute(slide, \"style\", styles);\n setAttribute(slide, ARIA_LABEL, label || \"\");\n }\n\n function initNavigation() {\n var controls = Splide2.splides.map(function (target) {\n var Slide2 = target.splide.Components.Slides.getAt(index);\n return Slide2 ? Slide2.slide.id : \"\";\n }).join(\" \");\n setAttribute(slide, ARIA_LABEL, format(i18n.slideX, (isClone ? slideIndex : index) + 1));\n setAttribute(slide, ARIA_CONTROLS, controls);\n setAttribute(slide, ROLE, slideFocus ? \"button\" : \"\");\n slideFocus && removeAttribute(slide, ARIA_ROLEDESCRIPTION);\n }\n\n function onMove() {\n if (!destroyed) {\n update();\n }\n }\n\n function update() {\n if (!destroyed) {\n var curr = Splide2.index;\n updateActivity();\n updateVisibility();\n toggleClass(slide, CLASS_PREV, index === curr - 1);\n toggleClass(slide, CLASS_NEXT, index === curr + 1);\n }\n }\n\n function updateActivity() {\n var active = isActive();\n\n if (active !== hasClass(slide, CLASS_ACTIVE)) {\n toggleClass(slide, CLASS_ACTIVE, active);\n setAttribute(slide, ARIA_CURRENT, isNavigation && active || \"\");\n emit(active ? EVENT_ACTIVE : EVENT_INACTIVE, self);\n }\n }\n\n function updateVisibility() {\n var visible = isVisible();\n var hidden = !visible && (!isActive() || isClone);\n\n if (!Splide2.state.is([MOVING, SCROLLING])) {\n setAttribute(slide, ARIA_HIDDEN, hidden || \"\");\n }\n\n setAttribute(queryAll(slide, options.focusableNodes || \"\"), TAB_INDEX, hidden ? -1 : \"\");\n\n if (slideFocus) {\n setAttribute(slide, TAB_INDEX, hidden ? -1 : 0);\n }\n\n if (visible !== hasClass(slide, CLASS_VISIBLE)) {\n toggleClass(slide, CLASS_VISIBLE, visible);\n emit(visible ? EVENT_VISIBLE : EVENT_HIDDEN, self);\n }\n\n if (!visible && document.activeElement === slide) {\n var Slide2 = Components.Slides.getAt(Splide2.index);\n Slide2 && focus(Slide2.slide);\n }\n }\n\n function style$1(prop, value, useContainer) {\n style(useContainer && container || slide, prop, value);\n }\n\n function isActive() {\n var curr = Splide2.index;\n return curr === index || options.cloneStatus && curr === slideIndex;\n }\n\n function isVisible() {\n if (Splide2.is(FADE)) {\n return isActive();\n }\n\n var trackRect = rect(Components.Elements.track);\n var slideRect = rect(slide);\n var left = resolve(\"left\", true);\n var right = resolve(\"right\", true);\n return floor(trackRect[left]) <= ceil(slideRect[left]) && floor(slideRect[right]) <= ceil(trackRect[right]);\n }\n\n function isWithin(from, distance) {\n var diff = abs(from - index);\n\n if (!isClone && (options.rewind || Splide2.is(LOOP))) {\n diff = min(diff, Splide2.length - diff);\n }\n\n return diff <= distance;\n }\n\n var self = {\n index: index,\n slideIndex: slideIndex,\n slide: slide,\n container: container,\n isClone: isClone,\n mount: mount,\n destroy: destroy,\n update: update,\n style: style$1,\n isWithin: isWithin\n };\n return self;\n }\n\n function Slides(Splide2, Components2, options) {\n var _EventInterface2 = EventInterface(Splide2),\n on = _EventInterface2.on,\n emit = _EventInterface2.emit,\n bind = _EventInterface2.bind;\n\n var _Components2$Elements = Components2.Elements,\n slides = _Components2$Elements.slides,\n list = _Components2$Elements.list;\n var Slides2 = [];\n\n function mount() {\n init();\n on(EVENT_REFRESH, destroy);\n on(EVENT_REFRESH, init);\n }\n\n function init() {\n slides.forEach(function (slide, index) {\n register(slide, index, -1);\n });\n }\n\n function destroy() {\n forEach$1(function (Slide2) {\n Slide2.destroy();\n });\n empty(Slides2);\n }\n\n function update() {\n forEach$1(function (Slide2) {\n Slide2.update();\n });\n }\n\n function register(slide, index, slideIndex) {\n var object = Slide$1(Splide2, index, slideIndex, slide);\n object.mount();\n Slides2.push(object);\n Slides2.sort(function (Slide1, Slide2) {\n return Slide1.index - Slide2.index;\n });\n }\n\n function get(excludeClones) {\n return excludeClones ? filter(function (Slide2) {\n return !Slide2.isClone;\n }) : Slides2;\n }\n\n function getIn(page) {\n var Controller = Components2.Controller;\n var index = Controller.toIndex(page);\n var max = Controller.hasFocus() ? 1 : options.perPage;\n return filter(function (Slide2) {\n return between(Slide2.index, index, index + max - 1);\n });\n }\n\n function getAt(index) {\n return filter(index)[0];\n }\n\n function add(items, index) {\n forEach(items, function (slide) {\n if (isString(slide)) {\n slide = parseHtml(slide);\n }\n\n if (isHTMLElement(slide)) {\n var ref = slides[index];\n ref ? before(slide, ref) : append(list, slide);\n addClass(slide, options.classes.slide);\n observeImages(slide, apply(emit, EVENT_RESIZE));\n }\n });\n emit(EVENT_REFRESH);\n }\n\n function remove$1(matcher) {\n remove(filter(matcher).map(function (Slide2) {\n return Slide2.slide;\n }));\n emit(EVENT_REFRESH);\n }\n\n function forEach$1(iteratee, excludeClones) {\n get(excludeClones).forEach(iteratee);\n }\n\n function filter(matcher) {\n return Slides2.filter(isFunction(matcher) ? matcher : function (Slide2) {\n return isString(matcher) ? matches(Slide2.slide, matcher) : includes(toArray(matcher), Slide2.index);\n });\n }\n\n function style(prop, value, useContainer) {\n forEach$1(function (Slide2) {\n Slide2.style(prop, value, useContainer);\n });\n }\n\n function observeImages(elm, callback) {\n var images = queryAll(elm, \"img\");\n var length = images.length;\n\n if (length) {\n images.forEach(function (img) {\n bind(img, \"load error\", function () {\n if (! --length) {\n callback();\n }\n });\n });\n } else {\n callback();\n }\n }\n\n function getLength(excludeClones) {\n return excludeClones ? slides.length : Slides2.length;\n }\n\n function isEnough() {\n return Slides2.length > options.perPage;\n }\n\n return {\n mount: mount,\n destroy: destroy,\n update: update,\n register: register,\n get: get,\n getIn: getIn,\n getAt: getAt,\n add: add,\n remove: remove$1,\n forEach: forEach$1,\n filter: filter,\n style: style,\n getLength: getLength,\n isEnough: isEnough\n };\n }\n\n function Layout(Splide2, Components2, options) {\n var _EventInterface3 = EventInterface(Splide2),\n on = _EventInterface3.on,\n bind = _EventInterface3.bind,\n emit = _EventInterface3.emit;\n\n var Slides = Components2.Slides;\n var resolve = Components2.Direction.resolve;\n var _Components2$Elements2 = Components2.Elements,\n root = _Components2$Elements2.root,\n track = _Components2$Elements2.track,\n list = _Components2$Elements2.list;\n var getAt = Slides.getAt,\n styleSlides = Slides.style;\n var vertical;\n var rootRect;\n var overflow;\n\n function mount() {\n init();\n bind(window, \"resize load\", Throttle(apply(emit, EVENT_RESIZE)));\n on([EVENT_UPDATED, EVENT_REFRESH], init);\n on(EVENT_RESIZE, resize);\n }\n\n function init() {\n vertical = options.direction === TTB;\n style(root, \"maxWidth\", unit(options.width));\n style(track, resolve(\"paddingLeft\"), cssPadding(false));\n style(track, resolve(\"paddingRight\"), cssPadding(true));\n resize(true);\n }\n\n function resize(force) {\n var newRect = rect(root);\n\n if (force || rootRect.width !== newRect.width || rootRect.height !== newRect.height) {\n style(track, \"height\", cssTrackHeight());\n styleSlides(resolve(\"marginRight\"), unit(options.gap));\n styleSlides(\"width\", cssSlideWidth());\n styleSlides(\"height\", cssSlideHeight(), true);\n rootRect = newRect;\n emit(EVENT_RESIZED);\n\n if (overflow !== (overflow = isOverflow())) {\n toggleClass(root, CLASS_OVERFLOW, overflow);\n emit(EVENT_OVERFLOW, overflow);\n }\n }\n }\n\n function cssPadding(right) {\n var padding = options.padding;\n var prop = resolve(right ? \"right\" : \"left\");\n return padding && unit(padding[prop] || (isObject(padding) ? 0 : padding)) || \"0px\";\n }\n\n function cssTrackHeight() {\n var height = \"\";\n\n if (vertical) {\n height = cssHeight();\n assert(height, \"height or heightRatio is missing.\");\n height = \"calc(\" + height + \" - \" + cssPadding(false) + \" - \" + cssPadding(true) + \")\";\n }\n\n return height;\n }\n\n function cssHeight() {\n return unit(options.height || rect(list).width * options.heightRatio);\n }\n\n function cssSlideWidth() {\n return options.autoWidth ? null : unit(options.fixedWidth) || (vertical ? \"\" : cssSlideSize());\n }\n\n function cssSlideHeight() {\n return unit(options.fixedHeight) || (vertical ? options.autoHeight ? null : cssSlideSize() : cssHeight());\n }\n\n function cssSlideSize() {\n var gap = unit(options.gap);\n return \"calc((100%\" + (gap && \" + \" + gap) + \")/\" + (options.perPage || 1) + (gap && \" - \" + gap) + \")\";\n }\n\n function listSize() {\n return rect(list)[resolve(\"width\")];\n }\n\n function slideSize(index, withoutGap) {\n var Slide = getAt(index || 0);\n return Slide ? rect(Slide.slide)[resolve(\"width\")] + (withoutGap ? 0 : getGap()) : 0;\n }\n\n function totalSize(index, withoutGap) {\n var Slide = getAt(index);\n\n if (Slide) {\n var right = rect(Slide.slide)[resolve(\"right\")];\n var left = rect(list)[resolve(\"left\")];\n return abs(right - left) + (withoutGap ? 0 : getGap());\n }\n\n return 0;\n }\n\n function sliderSize(withoutGap) {\n return totalSize(Splide2.length - 1) - totalSize(0) + slideSize(0, withoutGap);\n }\n\n function getGap() {\n var Slide = getAt(0);\n return Slide && parseFloat(style(Slide.slide, resolve(\"marginRight\"))) || 0;\n }\n\n function getPadding(right) {\n return parseFloat(style(track, resolve(\"padding\" + (right ? \"Right\" : \"Left\")))) || 0;\n }\n\n function isOverflow() {\n return Splide2.is(FADE) || sliderSize(true) > listSize();\n }\n\n return {\n mount: mount,\n resize: resize,\n listSize: listSize,\n slideSize: slideSize,\n sliderSize: sliderSize,\n totalSize: totalSize,\n getPadding: getPadding,\n isOverflow: isOverflow\n };\n }\n\n var MULTIPLIER = 2;\n\n function Clones(Splide2, Components2, options) {\n var event = EventInterface(Splide2);\n var on = event.on;\n var Elements = Components2.Elements,\n Slides = Components2.Slides;\n var resolve = Components2.Direction.resolve;\n var clones = [];\n var cloneCount;\n\n function mount() {\n on(EVENT_REFRESH, remount);\n on([EVENT_UPDATED, EVENT_RESIZE], observe);\n\n if (cloneCount = computeCloneCount()) {\n generate(cloneCount);\n Components2.Layout.resize(true);\n }\n }\n\n function remount() {\n destroy();\n mount();\n }\n\n function destroy() {\n remove(clones);\n empty(clones);\n event.destroy();\n }\n\n function observe() {\n var count = computeCloneCount();\n\n if (cloneCount !== count) {\n if (cloneCount < count || !count) {\n event.emit(EVENT_REFRESH);\n }\n }\n }\n\n function generate(count) {\n var slides = Slides.get().slice();\n var length = slides.length;\n\n if (length) {\n while (slides.length < count) {\n push(slides, slides);\n }\n\n push(slides.slice(-count), slides.slice(0, count)).forEach(function (Slide, index) {\n var isHead = index < count;\n var clone = cloneDeep(Slide.slide, index);\n isHead ? before(clone, slides[0].slide) : append(Elements.list, clone);\n push(clones, clone);\n Slides.register(clone, index - count + (isHead ? 0 : length), Slide.index);\n });\n }\n }\n\n function cloneDeep(elm, index) {\n var clone = elm.cloneNode(true);\n addClass(clone, options.classes.clone);\n clone.id = Splide2.root.id + \"-clone\" + pad(index + 1);\n return clone;\n }\n\n function computeCloneCount() {\n var clones2 = options.clones;\n\n if (!Splide2.is(LOOP)) {\n clones2 = 0;\n } else if (isUndefined(clones2)) {\n var fixedSize = options[resolve(\"fixedWidth\")] && Components2.Layout.slideSize(0);\n var fixedCount = fixedSize && ceil(rect(Elements.track)[resolve(\"width\")] / fixedSize);\n clones2 = fixedCount || options[resolve(\"autoWidth\")] && Splide2.length || options.perPage * MULTIPLIER;\n }\n\n return clones2;\n }\n\n return {\n mount: mount,\n destroy: destroy\n };\n }\n\n function Move(Splide2, Components2, options) {\n var _EventInterface4 = EventInterface(Splide2),\n on = _EventInterface4.on,\n emit = _EventInterface4.emit;\n\n var set = Splide2.state.set;\n var _Components2$Layout = Components2.Layout,\n slideSize = _Components2$Layout.slideSize,\n getPadding = _Components2$Layout.getPadding,\n totalSize = _Components2$Layout.totalSize,\n listSize = _Components2$Layout.listSize,\n sliderSize = _Components2$Layout.sliderSize;\n var _Components2$Directio = Components2.Direction,\n resolve = _Components2$Directio.resolve,\n orient = _Components2$Directio.orient;\n var _Components2$Elements3 = Components2.Elements,\n list = _Components2$Elements3.list,\n track = _Components2$Elements3.track;\n var Transition;\n\n function mount() {\n Transition = Components2.Transition;\n on([EVENT_MOUNTED, EVENT_RESIZED, EVENT_UPDATED, EVENT_REFRESH], reposition);\n }\n\n function reposition() {\n if (!Components2.Controller.isBusy()) {\n Components2.Scroll.cancel();\n jump(Splide2.index);\n Components2.Slides.update();\n }\n }\n\n function move(dest, index, prev, callback) {\n if (dest !== index && canShift(dest > prev)) {\n cancel();\n translate(shift(getPosition(), dest > prev), true);\n }\n\n set(MOVING);\n emit(EVENT_MOVE, index, prev, dest);\n Transition.start(index, function () {\n set(IDLE);\n emit(EVENT_MOVED, index, prev, dest);\n callback && callback();\n });\n }\n\n function jump(index) {\n translate(toPosition(index, true));\n }\n\n function translate(position, preventLoop) {\n if (!Splide2.is(FADE)) {\n var destination = preventLoop ? position : loop(position);\n style(list, \"transform\", \"translate\" + resolve(\"X\") + \"(\" + destination + \"px)\");\n position !== destination && emit(EVENT_SHIFTED);\n }\n }\n\n function loop(position) {\n if (Splide2.is(LOOP)) {\n var index = toIndex(position);\n var exceededMax = index > Components2.Controller.getEnd();\n var exceededMin = index < 0;\n\n if (exceededMin || exceededMax) {\n position = shift(position, exceededMax);\n }\n }\n\n return position;\n }\n\n function shift(position, backwards) {\n var excess = position - getLimit(backwards);\n var size = sliderSize();\n position -= orient(size * (ceil(abs(excess) / size) || 1)) * (backwards ? 1 : -1);\n return position;\n }\n\n function cancel() {\n translate(getPosition(), true);\n Transition.cancel();\n }\n\n function toIndex(position) {\n var Slides = Components2.Slides.get();\n var index = 0;\n var minDistance = Infinity;\n\n for (var i = 0; i < Slides.length; i++) {\n var slideIndex = Slides[i].index;\n var distance = abs(toPosition(slideIndex, true) - position);\n\n if (distance <= minDistance) {\n minDistance = distance;\n index = slideIndex;\n } else {\n break;\n }\n }\n\n return index;\n }\n\n function toPosition(index, trimming) {\n var position = orient(totalSize(index - 1) - offset(index));\n return trimming ? trim(position) : position;\n }\n\n function getPosition() {\n var left = resolve(\"left\");\n return rect(list)[left] - rect(track)[left] + orient(getPadding(false));\n }\n\n function trim(position) {\n if (options.trimSpace && Splide2.is(SLIDE)) {\n position = clamp(position, 0, orient(sliderSize(true) - listSize()));\n }\n\n return position;\n }\n\n function offset(index) {\n var focus = options.focus;\n return focus === \"center\" ? (listSize() - slideSize(index, true)) / 2 : +focus * slideSize(index) || 0;\n }\n\n function getLimit(max) {\n return toPosition(max ? Components2.Controller.getEnd() : 0, !!options.trimSpace);\n }\n\n function canShift(backwards) {\n var shifted = orient(shift(getPosition(), backwards));\n return backwards ? shifted >= 0 : shifted <= list[resolve(\"scrollWidth\")] - rect(track)[resolve(\"width\")];\n }\n\n function exceededLimit(max, position) {\n position = isUndefined(position) ? getPosition() : position;\n var exceededMin = max !== true && orient(position) < orient(getLimit(false));\n var exceededMax = max !== false && orient(position) > orient(getLimit(true));\n return exceededMin || exceededMax;\n }\n\n return {\n mount: mount,\n move: move,\n jump: jump,\n translate: translate,\n shift: shift,\n cancel: cancel,\n toIndex: toIndex,\n toPosition: toPosition,\n getPosition: getPosition,\n getLimit: getLimit,\n exceededLimit: exceededLimit,\n reposition: reposition\n };\n }\n\n function Controller(Splide2, Components2, options) {\n var _EventInterface5 = EventInterface(Splide2),\n on = _EventInterface5.on,\n emit = _EventInterface5.emit;\n\n var Move = Components2.Move;\n var getPosition = Move.getPosition,\n getLimit = Move.getLimit,\n toPosition = Move.toPosition;\n var _Components2$Slides = Components2.Slides,\n isEnough = _Components2$Slides.isEnough,\n getLength = _Components2$Slides.getLength;\n var omitEnd = options.omitEnd;\n var isLoop = Splide2.is(LOOP);\n var isSlide = Splide2.is(SLIDE);\n var getNext = apply(getAdjacent, false);\n var getPrev = apply(getAdjacent, true);\n var currIndex = options.start || 0;\n var endIndex;\n var prevIndex = currIndex;\n var slideCount;\n var perMove;\n var perPage;\n\n function mount() {\n init();\n on([EVENT_UPDATED, EVENT_REFRESH, EVENT_END_INDEX_CHANGED], init);\n on(EVENT_RESIZED, onResized);\n }\n\n function init() {\n slideCount = getLength(true);\n perMove = options.perMove;\n perPage = options.perPage;\n endIndex = getEnd();\n var index = clamp(currIndex, 0, omitEnd ? endIndex : slideCount - 1);\n\n if (index !== currIndex) {\n currIndex = index;\n Move.reposition();\n }\n }\n\n function onResized() {\n if (endIndex !== getEnd()) {\n emit(EVENT_END_INDEX_CHANGED);\n }\n }\n\n function go(control, allowSameIndex, callback) {\n if (!isBusy()) {\n var dest = parse(control);\n var index = loop(dest);\n\n if (index > -1 && (allowSameIndex || index !== currIndex)) {\n setIndex(index);\n Move.move(dest, index, prevIndex, callback);\n }\n }\n }\n\n function scroll(destination, duration, snap, callback) {\n Components2.Scroll.scroll(destination, duration, snap, function () {\n var index = loop(Move.toIndex(getPosition()));\n setIndex(omitEnd ? min(index, endIndex) : index);\n callback && callback();\n });\n }\n\n function parse(control) {\n var index = currIndex;\n\n if (isString(control)) {\n var _ref = control.match(/([+\\-<>])(\\d+)?/) || [],\n indicator = _ref[1],\n number = _ref[2];\n\n if (indicator === \"+\" || indicator === \"-\") {\n index = computeDestIndex(currIndex + +(\"\" + indicator + (+number || 1)), currIndex);\n } else if (indicator === \">\") {\n index = number ? toIndex(+number) : getNext(true);\n } else if (indicator === \"<\") {\n index = getPrev(true);\n }\n } else {\n index = isLoop ? control : clamp(control, 0, endIndex);\n }\n\n return index;\n }\n\n function getAdjacent(prev, destination) {\n var number = perMove || (hasFocus() ? 1 : perPage);\n var dest = computeDestIndex(currIndex + number * (prev ? -1 : 1), currIndex, !(perMove || hasFocus()));\n\n if (dest === -1 && isSlide) {\n if (!approximatelyEqual(getPosition(), getLimit(!prev), 1)) {\n return prev ? 0 : endIndex;\n }\n }\n\n return destination ? dest : loop(dest);\n }\n\n function computeDestIndex(dest, from, snapPage) {\n if (isEnough() || hasFocus()) {\n var index = computeMovableDestIndex(dest);\n\n if (index !== dest) {\n from = dest;\n dest = index;\n snapPage = false;\n }\n\n if (dest < 0 || dest > endIndex) {\n if (!perMove && (between(0, dest, from, true) || between(endIndex, from, dest, true))) {\n dest = toIndex(toPage(dest));\n } else {\n if (isLoop) {\n dest = snapPage ? dest < 0 ? -(slideCount % perPage || perPage) : slideCount : dest;\n } else if (options.rewind) {\n dest = dest < 0 ? endIndex : 0;\n } else {\n dest = -1;\n }\n }\n } else {\n if (snapPage && dest !== from) {\n dest = toIndex(toPage(from) + (dest < from ? -1 : 1));\n }\n }\n } else {\n dest = -1;\n }\n\n return dest;\n }\n\n function computeMovableDestIndex(dest) {\n if (isSlide && options.trimSpace === \"move\" && dest !== currIndex) {\n var position = getPosition();\n\n while (position === toPosition(dest, true) && between(dest, 0, Splide2.length - 1, !options.rewind)) {\n dest < currIndex ? --dest : ++dest;\n }\n }\n\n return dest;\n }\n\n function loop(index) {\n return isLoop ? (index + slideCount) % slideCount || 0 : index;\n }\n\n function getEnd() {\n var end = slideCount - (hasFocus() || isLoop && perMove ? 1 : perPage);\n\n while (omitEnd && end-- > 0) {\n if (toPosition(slideCount - 1, true) !== toPosition(end, true)) {\n end++;\n break;\n }\n }\n\n return clamp(end, 0, slideCount - 1);\n }\n\n function toIndex(page) {\n return clamp(hasFocus() ? page : perPage * page, 0, endIndex);\n }\n\n function toPage(index) {\n return hasFocus() ? min(index, endIndex) : floor((index >= endIndex ? slideCount - 1 : index) / perPage);\n }\n\n function toDest(destination) {\n var closest = Move.toIndex(destination);\n return isSlide ? clamp(closest, 0, endIndex) : closest;\n }\n\n function setIndex(index) {\n if (index !== currIndex) {\n prevIndex = currIndex;\n currIndex = index;\n }\n }\n\n function getIndex(prev) {\n return prev ? prevIndex : currIndex;\n }\n\n function hasFocus() {\n return !isUndefined(options.focus) || options.isNavigation;\n }\n\n function isBusy() {\n return Splide2.state.is([MOVING, SCROLLING]) && !!options.waitForTransition;\n }\n\n return {\n mount: mount,\n go: go,\n scroll: scroll,\n getNext: getNext,\n getPrev: getPrev,\n getAdjacent: getAdjacent,\n getEnd: getEnd,\n setIndex: setIndex,\n getIndex: getIndex,\n toIndex: toIndex,\n toPage: toPage,\n toDest: toDest,\n hasFocus: hasFocus,\n isBusy: isBusy\n };\n }\n\n var XML_NAME_SPACE = \"http://www.w3.org/2000/svg\";\n var PATH = \"m15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z\";\n var SIZE = 40;\n\n function Arrows(Splide2, Components2, options) {\n var event = EventInterface(Splide2);\n var on = event.on,\n bind = event.bind,\n emit = event.emit;\n var classes = options.classes,\n i18n = options.i18n;\n var Elements = Components2.Elements,\n Controller = Components2.Controller;\n var placeholder = Elements.arrows,\n track = Elements.track;\n var wrapper = placeholder;\n var prev = Elements.prev;\n var next = Elements.next;\n var created;\n var wrapperClasses;\n var arrows = {};\n\n function mount() {\n init();\n on(EVENT_UPDATED, remount);\n }\n\n function remount() {\n destroy();\n mount();\n }\n\n function init() {\n var enabled = options.arrows;\n\n if (enabled && !(prev && next)) {\n createArrows();\n }\n\n if (prev && next) {\n assign(arrows, {\n prev: prev,\n next: next\n });\n display(wrapper, enabled ? \"\" : \"none\");\n addClass(wrapper, wrapperClasses = CLASS_ARROWS + \"--\" + options.direction);\n\n if (enabled) {\n listen();\n update();\n setAttribute([prev, next], ARIA_CONTROLS, track.id);\n emit(EVENT_ARROWS_MOUNTED, prev, next);\n }\n }\n }\n\n function destroy() {\n event.destroy();\n removeClass(wrapper, wrapperClasses);\n\n if (created) {\n remove(placeholder ? [prev, next] : wrapper);\n prev = next = null;\n } else {\n removeAttribute([prev, next], ALL_ATTRIBUTES);\n }\n }\n\n function listen() {\n on([EVENT_MOUNTED, EVENT_MOVED, EVENT_REFRESH, EVENT_SCROLLED, EVENT_END_INDEX_CHANGED], update);\n bind(next, \"click\", apply(go, \">\"));\n bind(prev, \"click\", apply(go, \"<\"));\n }\n\n function go(control) {\n Controller.go(control, true);\n }\n\n function createArrows() {\n wrapper = placeholder || create(\"div\", classes.arrows);\n prev = createArrow(true);\n next = createArrow(false);\n created = true;\n append(wrapper, [prev, next]);\n !placeholder && before(wrapper, track);\n }\n\n function createArrow(prev2) {\n var arrow = \"