{"version":3,"file":"all.min.js","sources":["../../node_modules/@progress-wad/data-attr-parser/index.mjs","../../src/js/modules/scroll-to-item.js","../../src/js/modules/dataloader.js","../../src/js/modules/support-css-property.js","../../src/js/modules/section-banner.js","../../src/js/modules/section-patch.js","../../src/js/modules/scroll-anchors.js","../../src/js/modules/scroll-to-form.js","../../node_modules/@progress-wad/social-share/src/social-share.js","../../src/js/modules/sharer.js","../../src/js/modules/toggle.js","../../src/js/modules/tabs.js","../../src/js/modules/site-specific.js","../../src/js/modules/replacement-query-parameter.js","../../src/js/modules/quiz.js","../../src/js/modules/randomized-slider.js","../../node_modules/dayjs/dayjs.min.js","../../src/js/modules/interactive.js","../../src/js/modules/local-date-time.js","../../node_modules/@progress-wad/litebox/src/litebox.js","../../node_modules/@progress-wad/siema-slider/src/siema.js","../../node_modules/@progress-wad/sticky-element/index.js","../../node_modules/@progress-wad/site-search/build/index.es6.js","../../node_modules/@progress-wad/site-search/build/init.es6.js","../../node_modules/@progress-wad/youtube-lite-embed/dist/youtube-picture-in-picture.mjs","../../node_modules/@progress-wad/youtube-lite-embed/dist/youtube-lite-embed.mjs","../../node_modules/@progress-wad/youtube-lite-embed/dist/index.mjs","../../node_modules/@progress-wad/multi-select/src/multi-select-item.js","../../node_modules/@progress-wad/multi-select/src/multi-select.js","../../src/js/all.mjs"],"sourcesContent":["const rejson = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/;\n\nexport function isNumber(num) {\n var number = +num;\n\n if ((number - number) !== 0) {\n // Discard Infinity and NaN\n return false;\n }\n\n if (number === num) {\n return true;\n }\n\n if (typeof num === 'string') {\n // String parsed, both a non-empty whitespace string and an empty string\n // will have been coerced to 0. If 0 trim the string and see if its empty.\n if (number === 0 && num.trim() === '') {\n return false;\n }\n return true;\n }\n return false;\n};\n\nexport function hyphenToCamelCase(hyphen) {\n return hyphen.replace(/-([a-z])/g, function(match) {\n return match[1].toUpperCase();\n });\n}\n\nexport function camelCaseToHyphen(camelCase) {\n return camelCase.replace(/[A-Z]/g, '-$1').toLowerCase();\n}\n\nexport function attributeToCamelCase(attribute) {\n return hyphenToCamelCase(attribute.replace(/^(data-)?(.*)/, '$2'));\n}\n\nexport function camelCaseToAttribute(camelCase) {\n return 'data-' + camelCaseToHyphen(camelCase);\n}\n\nexport function stringToCamelCase(string) {\n return string.replace(/(?:^\\w|[A-Z]|\\b\\w)/g, function(letter, index) {\n return index == 0 ? letter.toLowerCase() : letter.toUpperCase();\n }).replace(/\\s+/g, '');\n}\n\nexport class DataAttrParser {\n /**\n * @param {DOMStringMap} dataset\n * @param {Array} _prefixes\n */\n static parse(dataset, _prefixes = []) {\n let result = {};\n const keys = Object.keys(dataset);\n const grouping = _prefixes.length;\n const prefixes = _prefixes.map(hyphenToCamelCase);\n\n for (let i = 0; i < keys.length; i++) {\n let key = keys[i];\n let value = dataset[key];\n\n if (value === 'true') {\n result[key] = true;\n continue;\n }\n\n if (value === 'false') {\n result[key] = false;\n continue;\n }\n\n if (isNumber(value)) {\n result[key] = Number(value);\n continue;\n }\n\n if (value === 'null') {\n result[key] = null;\n continue;\n }\n\n if (rejson.test(value)) {\n result[key] = JSON.parse(value);\n continue;\n }\n\n // @String\n result[key] = value;\n }\n\n // Group result object\n prefixes.forEach(prefix => {\n let subResult = {};\n\n Object.keys(result)\n .filter(key => key.startsWith(prefix))\n .forEach(key => {\n let slicedKey = stringToCamelCase(key.replace(prefix, ''));\n if (slicedKey === '') slicedKey = prefix;\n subResult[slicedKey] = result[key];\n delete result[key];\n });\n\n result[prefix] = subResult;\n });\n\n return result;\n }\n}\n","export function scrollToItem(el) {\n if (el) {\n window.setTimeout( () => {\n let nav = document.querySelector('.Nav')\n let navHeight = (nav) ? nav.getBoundingClientRect().height : 0\n let scrollToPos = window.scrollY + el.getBoundingClientRect().top - navHeight\n\n window.scrollTo({\n top: scrollToPos,\n behavior: 'smooth'\n })\n }, 500)\n }\n}\n","import $ from 'jquery';\nimport { DataAttrParser } from '@progress-wad/data-attr-parser';\n\n/**\n * Init prgs/tlrk jquery plugins from data attrs\n *\n * @param {String} selector - data attr name - example \"data-tlrk-plugin\" or \"data-prgs-plugin\"\n */\nfunction init(selector) {\n const elements = Array.from(document.querySelectorAll(`[${selector}]`));\n\n elements.forEach(element => {\n const $element = $(element);\n const plugins = element.getAttribute(selector).split(' ');\n\n plugins.forEach(plugin => {\n if ($.isFunction($.fn[plugin])) {\n const group = plugin.toLowerCase();\n const options = DataAttrParser.parse(element.dataset, [group]);\n $element[plugin](options[group]);\n }\n });\n });\n}\n\n$(document).ready(() => {\n // init('data-tlrk-plugin');\n // init('data-prgs-plugin');\n // init('data-ipswitch-plugin');\n init('data-chef-plugin');\n});\n","import $ from 'jquery';\n\n/* eslint-disable */\n$.support.cssProperty = (function supportCssPropertyFactory() {\n return function supportCssProperty(a, e) {\n const d = (document.body || document.documentElement).style;\n let c;\n\n if (typeof d === 'undefined') {\n return !1;\n }\n\n if (typeof d[a] === 'string') {\n return e ? a : !0;\n }\n\n const b = 'Moz Webkit Khtml O ms Icab'.split(' ');\n\n for (a = a.charAt(0).toUpperCase() + a.substr(1), c = 0; c < b.length; c++) {\n if (typeof d[b[c] + a] === 'string') {\n return e ? b[c] + a : !0;\n }\n }\n };\n})();\n/* eslint-enable */\n","import $ from 'jquery';\n\n(() => {\n if (!$.support.cssProperty('object-fit')) {\n $('.Section-bg img, img.-contain').each(function each() {\n var $this = $(this);\n const $container = $this.parent();\n const imgUrl = $this.prop('src');\n const imgClasses = $this.attr('class');\n if (imgUrl) {\n if ($this.hasClass('-contain')) {\n $container\n .css('backgroundImage', 'url(' + imgUrl + ')')\n .addClass('compat-object-contain');\n } else {\n $container\n .css('backgroundImage', 'url(' + imgUrl + ')')\n .addClass('compat-object-fit')\n .addClass(imgClasses);\n // fix for ie's missing background color behind the banner image\n if ($container.hasClass('-bg-black')) {\n $container.parent().addClass('-tint-black');\n }\n }\n }\n });\n }\n})();\n","(() => {\n function findCousin(elements, nextOrPrev, selector) {\n // Reference the last of the selected elements\n const reference = elements[elements.length - 1];\n\n // Set of all elements matching the selector\n const set = Array.from(document.querySelectorAll(selector));\n\n // Fins the position of the reference element in the set of all elements\n const pos = set.indexOf(reference);\n\n // If the element does not match the selector for cousin elements return null\n if (set.length === (pos - 1)) return null;\n\n // Return the next or previous cousin to the reference\n return set[pos + (nextOrPrev === 'next' ? 1 : (-1))];\n }\n\n // do not execute in edit mode\n const url = window.location.href.toLowerCase();\n\n if (url.indexOf('/action/edit') > 0 || url.indexOf('/action/inedit') > 0) {\n return;\n }\n\n let resizing = false;\n\n function calculateOffsets() {\n document.querySelectorAll('.Section--patch').forEach(patch => {\n const patchHalfHeight = `${Math.ceil(patch.offsetHeight / 2)}px`;\n const isSmall = patch.classList.contains('Section--s');\n\n if (document.body.offsetWidth > (isSmall ? 1360 : 1460)) {\n const containers = patch.querySelectorAll('.container');\n const prevCousin = findCousin(containers, 'prev', '.container, .PRGS-container');\n const nextCousin = findCousin(containers, 'next', '.container, .PRGS-container');\n\n patch.classList.add('is-active');\n\n patch.setAttribute('style', `margin-top: -${patchHalfHeight}; margin-bottom: -${patchHalfHeight}`);\n\n const customPaddingClass = isSmall ? 'has-custom-padding-s' : 'has-custom-padding';\n\n if (prevCousin) {\n prevCousin.classList.add(customPaddingClass);\n prevCousin.style.paddingBottom = patchHalfHeight;\n }\n\n if (nextCousin) {\n nextCousin.classList.add(customPaddingClass);\n nextCousin.style.paddingTop = patchHalfHeight;\n }\n }\n });\n }\n\n if (!document.querySelector('.sfPageEditor') && document.querySelector('.Section--patch')) {\n window.addEventListener('resize', () => {\n resizing = true;\n });\n\n window.setInterval(() => {\n if (resizing) {\n calculateOffsets();\n resizing = false;\n }\n }, 500);\n\n calculateOffsets();\n }\n})();\n","import $ from 'jquery';\n\n(() => {\n const $anchorLinks = $(\"a[href^='#']:not([href='#'])\").not('.litebox, .js-noanchor, .js-noanchor a');\n let target = [];\n let hash = '';\n const $page = $('html, body');\n\n if (!$anchorLinks.length) {\n return;\n }\n\n $anchorLinks.on('click', function onClick(e) {\n const navHeight = (!!document.querySelector('.Nav')) ? document.querySelector('.Nav').getBoundingClientRect().height : 0;\n hash = this.getAttribute('href');\n\n if (hash.length >= 2) {\n target = $(hash).length ? $(hash) : $(`[name='${hash.slice(1)}']`);\n }\n\n if (target.length) {\n $page.not(':animated').animate({ scrollTop: (target.offset().top - navHeight) }, 500);\n e.preventDefault();\n }\n });\n})();\n","import { scrollToItem } from './scroll-to-item.js';\n\n(() => {\n let triggers = document.querySelectorAll('[data-sf-role=\"form-container\"]:not(.js-scroll-to-error-off) button')\n\n triggers.forEach(trigger => {\n trigger.addEventListener('click', e => {\n let el = e.target\n let closestForm = el.closest('.FormHolder') || el.closest('[data-sf-role=\"form-container\"]')\n scrollToItem(closestForm)\n });\n })\n\n})();\n","export const defaultOptions = {\n width: 560,\n height: 400,\n media: 'twitter',\n url: window.location.href,\n text: document.title,\n};\n\nclass SocialShare {\n /**\n * @constructor\n */\n constructor(options) {\n this.options = { ...defaultOptions, ...options };\n\n this.element = this.options.element;\n this.media = this.options.media;\n this.url = this.options.url || window.location.href;\n this.text = this.options.text || document.title;\n\n if (!this.element) {\n throw new Error('Social share anchor element is required');\n }\n\n this.populateLink();\n\n this.element.addEventListener('click', this.onLinkClick.bind(this));\n }\n\n /**\n * Populate the link href\n */\n populateLink() {\n const hashtag = this.options.hashtag ? `&hashtags=${this.options.hashtag}` : '';\n const encodedUrl = encodeURIComponent(this.url);\n const encodedText = encodeURIComponent(this.text);\n\n switch (this.media) {\n case 'facebook':\n this.element.href = `https://www.facebook.com/sharer.php?u=${encodedUrl}`;\n break;\n case 'twitter':\n this.element.href = `https://twitter.com/share?url=${encodedUrl}&text=${encodedText}${hashtag}`;\n break;\n case 'linkedin':\n this.element.href = `https://www.linkedin.com/shareArticle?mini=true&url=${encodedUrl}&title=${encodedText}`;\n break;\n case 'reddit':\n this.element.href = `https://reddit.com/submit?url=${encodedUrl}&title=${encodedText}`;\n break;\n case 'mail':\n case 'email':\n this.element.href = `mailto:?subject=${encodedText}&body=${encodedUrl}`;\n }\n }\n\n /**\n * @param {Event} - click event\n */\n onLinkClick(event) {\n const medias = ['facebook', 'twitter', 'linkedin', 'reddit'];\n\n if (medias.includes(this.media)) {\n event.preventDefault();\n this.openPopup(event.currentTarget.href);\n }\n }\n\n /**\n * open popup window\n */\n openPopup(url, title = 'Share') {\n const top = (window.innerHeight - this.options.height) / 2;\n const left = (window.innerWidth - this.options.width) / 2;\n const status = 1;\n const windowOptions = `status=${status},width=${this.options.width},height=${this.options.height},top=${top},left=${left}`;\n\n window.open(url, title, windowOptions);\n }\n}\n\nexport default SocialShare;\n","import SocialShare from '@progress-wad/social-share';\n\n(() => {\n const socialLinks = Array.from(document.querySelectorAll('[class*=\"js-social-\"]'));\n\n socialLinks.forEach(element => {\n const classList = element.classList + ' ';\n const media = classList.split('js-social-')[1].split(' ')[0];\n const url = element.hasAttribute('data-article-link') ? document.location.host + element.getAttribute('data-article-link') : null;\n const text = element.getAttribute('data-article-title');\n\n new SocialShare({\n element,\n media,\n ...url ? { url } : {},\n ...text ? { text } : {}\n });\n });\n})();\n","(() => {\n function listenForClicks(e) {\n e.addEventListener(\"click\", function(e) {\n let el = e.currentTarget;\n let newClass = el.getAttribute(\"class\").split(\"js-tglslf-\")[1].split(\" \")[0];\n el.classList.toggle(newClass);\n })\n }\n document.querySelectorAll(\"[class*='js-tglslf-']\").forEach(function(e) {\n listenForClicks(e)\n });\n})();\n","import { scrollToItem } from './scroll-to-item.js';\n\n(() => {\n const tabContent = document.querySelectorAll('.TabsContent .TabsContent-item')\n const tabNav = document.querySelector('.TabsNav')\n const tabBtns = document.querySelectorAll('.TabsNav > a')\n const activeClass = 'is-open'\n\n function loadMobileTabs() {\n let ind = 0\n tabBtns.forEach(tab => {\n let tabMobContent = ''\n tabContent[ind].insertAdjacentHTML('afterbegin', tabMobContent)\n ind++\n })\n }\n\n function initTabHash() {\n if (window.location.hash && tabNav) {\n const sel = window.location.hash.toString().replace('#', '')\n const newActiveEl = document.querySelector('[data-id=\"' + sel + '\"]')\n\n if (newActiveEl)\n newActiveEl.click()\n }\n }\n\n function updateTabs(e) {\n e.preventDefault()\n\n let el = e.target\n let scrollable = e.target.scrollParam\n let attr = el.getAttribute('data-id')\n\n if (el.classList.contains(activeClass))\n return -1\n\n let ind = (el.tagName == \"A\") ? [].indexOf.call(el.parentElement.children, el) : [].indexOf.call(el.parentNode.parentElement.children, el.parentNode)\n\n tabBtns.forEach( tab => {\n tab.classList.remove(activeClass);\n tab.removeAttribute('aria-label');\n })\n\n tabBtns[ind].classList.add(activeClass)\n tabBtns[ind].setAttribute('aria-label', 'selected')\n\n tabContent.forEach( item => item.classList.remove(activeClass) )\n tabContent[ind].classList.add(activeClass)\n\n tabBtnsMob.forEach( tabM => tabM.classList.remove(activeClass) )\n tabContent[ind].querySelector('button:not(.Btn)').classList.add(activeClass)\n\n if (scrollable == true) {\n scrollToItem(tabContent[ind])\n }\n\n if (tabNav.getAttribute('data-tabs-hash') === 'true') {\n window.location.hash = el.getAttribute('data-id')\n }\n }\n\n loadMobileTabs()\n const tabBtnsMob = document.querySelectorAll('.TabsContent button:not(.Btn)')\n\n tabBtns.forEach(tab => {\n tab.addEventListener('click', updateTabs)\n })\n\n tabBtnsMob.forEach(tabM => {\n tabM.addEventListener('click', updateTabs)\n tabM.scrollParam = true\n })\n\n initTabHash()\n\n})();\n","// required class for forms\n(() => {\n document.querySelectorAll('input[required], select[required], textarea[required]').forEach(function(e) {\n if (e.previousElementSibling) e.previousElementSibling.classList.add('required')\n });\n})();\n\n// detect ie\n(function detectIE() {\n const ua = window.navigator.userAgent;\n const msie = ua.indexOf('MSIE ');\n const trident = ua.indexOf('Trident/');\n\n if (msie > 0 || trident > 0) {\n document.body.classList.add('t-ie');\n }\n})();\n","function getParameterByName(name, url) {\n let currentURL = url;\n if (!currentURL) currentURL = window.location.href;\n let cleanedName = name.replace(/[\\[\\]]/g, '\\\\$&');\n const regex = new RegExp('[?&]' + cleanedName + '(=([^&#]*)|&|#|$)'),\n results = regex.exec(currentURL);\n if (!results) return null;\n if (!results[2]) return '';\n return decodeURIComponent(results[2].replace(/\\+/g, ' '));\n}\n\nconst queryParameterValue = getParameterByName(\"utm_term\");\nif (queryParameterValue) {\n let toBeReplaced = document.querySelector(\".js-to-be-replaced\");\n if (toBeReplaced) {\n toBeReplaced.innerText = queryParameterValue;\n }\n}\n","(() => {\n let qaStr = '?qa='\n\n if (window.location.search.indexOf(qaStr) == 0) {\n const sel = window.location.search.replace(qaStr, '.js-qa-')\n const el = document.querySelector(sel)\n if (el)\n el.classList.remove('-dn')\n }\n})();\n","(() => {\n let randomizedSlider = document.querySelector('.Slider--randomized')\n\n if (randomizedSlider) {\n let slides = document.querySelectorAll('.Slider--randomized > div')\n let slidesCount = 5\n let chosen = []\n\n while (chosen.length < slidesCount) {\n let rand = Math.floor(Math.random() * slides.length)\n if (chosen.indexOf(rand) === -1) chosen.push(rand);\n }\n\n slides.forEach( (slide, currentIndex) => {\n if ( !chosen.includes(currentIndex)) {\n slide.remove()\n }\n })\n\n randomizedSlider.classList.remove('-dn')\n }\n})();\n","!function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?module.exports=e():\"function\"==typeof define&&define.amd?define(e):(t=\"undefined\"!=typeof globalThis?globalThis:t||self).dayjs=e()}(this,(function(){\"use strict\";var t=1e3,e=6e4,n=36e5,r=\"millisecond\",i=\"second\",s=\"minute\",u=\"hour\",a=\"day\",o=\"week\",c=\"month\",f=\"quarter\",h=\"year\",d=\"date\",l=\"Invalid Date\",$=/^(\\d{4})[-/]?(\\d{1,2})?[-/]?(\\d{0,2})[Tt\\s]*(\\d{1,2})?:?(\\d{1,2})?:?(\\d{1,2})?[.:]?(\\d+)?$/,y=/\\[([^\\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M={name:\"en\",weekdays:\"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday\".split(\"_\"),months:\"January_February_March_April_May_June_July_August_September_October_November_December\".split(\"_\"),ordinal:function(t){var e=[\"th\",\"st\",\"nd\",\"rd\"],n=t%100;return\"[\"+t+(e[(n-20)%10]||e[n]||e[0])+\"]\"}},m=function(t,e,n){var r=String(t);return!r||r.length>=e?t:\"\"+Array(e+1-r.length).join(n)+t},v={s:m,z:function(t){var e=-t.utcOffset(),n=Math.abs(e),r=Math.floor(n/60),i=n%60;return(e<=0?\"+\":\"-\")+m(r,2,\"0\")+\":\"+m(i,2,\"0\")},m:function t(e,n){if(e.date()1)return t(u[0])}else{var a=e.name;D[a]=e,i=a}return!r&&i&&(g=i),i||!r&&g},O=function(t,e){if(S(t))return t.clone();var n=\"object\"==typeof e?e:{};return n.date=t,n.args=arguments,new _(n)},b=v;b.l=w,b.i=S,b.w=function(t,e){return O(t,{locale:e.$L,utc:e.$u,x:e.$x,$offset:e.$offset})};var _=function(){function M(t){this.$L=w(t.locale,null,!0),this.parse(t),this.$x=this.$x||t.x||{},this[p]=!0}var m=M.prototype;return m.parse=function(t){this.$d=function(t){var e=t.date,n=t.utc;if(null===e)return new Date(NaN);if(b.u(e))return new Date;if(e instanceof Date)return new Date(e);if(\"string\"==typeof e&&!/Z$/i.test(e)){var r=e.match($);if(r){var i=r[2]-1||0,s=(r[7]||\"0\").substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,s)}}return new Date(e)}(t),this.init()},m.init=function(){var t=this.$d;this.$y=t.getFullYear(),this.$M=t.getMonth(),this.$D=t.getDate(),this.$W=t.getDay(),this.$H=t.getHours(),this.$m=t.getMinutes(),this.$s=t.getSeconds(),this.$ms=t.getMilliseconds()},m.$utils=function(){return b},m.isValid=function(){return!(this.$d.toString()===l)},m.isSame=function(t,e){var n=O(t);return this.startOf(e)<=n&&n<=this.endOf(e)},m.isAfter=function(t,e){return O(t) {\n return new Promise((resolve) => {\n if (document.readyState !== 'loading') {\n resolve();\n }\n else {\n document.addEventListener('readystatechange', () => {\n if (document.readyState === 'interactive') {\n resolve();\n }\n });\n }\n });\n};\n","/**\n * time.js--datetime\n *\n * iso dates to local date time string converter\n * format time element iso date to local datetime string\n *\n * ## examples\n * \n * \n *\n * ## results\n * \n * \n *\n * ## example\n * \n *\n * ## docs\n * For more on format look here => https://day.js.org/docs/en/display/format\n *\n * ## usage on telerik.com\n * https://www.telerik.com/webinars/fiddler\n */\n\nimport dayjs from 'dayjs';\nimport { interactive } from './interactive.js';\n\n// fancy DOM ready\ninteractive().then(() => {\n const defaultFormat = 'MMMM D, YYYY, HH:mm a';\n const timeElements = Array.from(document.querySelectorAll('time.js--datetime'));\n\n // render/display formatted local datetime in element\n timeElements.forEach(element => {\n // time \"datetime\" el attr => datetime ISO string\n // example => datetime=\"2020-10-01T07:45:00.0000000Z\"\n const isoDateStr = element.dateTime;\n\n // time dataset format string (data-format)\n // example => data-format=\"MMMM D, YYYY, HH:mm a\"\n const dataFormatStr = element.dataset.format;\n\n // format the iso date to local datetime using the specified format via data attr\n // example result => \"September 1, 2020, 10:45 am\"\n const formatedLocalDateStr = dayjs(isoDateStr).format(dataFormatStr || defaultFormat);\n\n // update text of the time element\n element.innerText = formatedLocalDateStr;\n });\n});\n","const ANIMATIONEND = 'animationend';\nconst $window = $(window);\nconst $document = $(document);\nlet $overlay; let $wrapper; let $content; let $loader; let $close;\nconst DATA_ATTRIBS_MAP = {\n 'lite-info': 'info',\n 'lite-width': 'width',\n 'lite-height': 'height',\n 'lite-resize': 'resize',\n 'lite-noscroll': 'noscroll',\n 'lite-root': 'root'\n};\nconst idhash = {};\nlet liteboxLoaded = false;\n\nfunction resizeImage(obj) {\n const ww = $window.width(); const wh = $window.height(); const\n $obj = $(obj);\n\n if (obj.originalWidth > ww) {\n $obj.css('max-width', 0.84 * ww);\n }\n else {\n $obj.css('max-width', 'none');\n }\n\n if (obj.originalHeight > wh - 60) {\n $obj.css('max-height', 0.84 * wh);\n }\n else {\n $obj.css('max-height', 'none');\n }\n}\n\nfunction setWidthHeight($elem, elem) { // from data attributes\n $elem.css({\n width: elem.options.width,\n height: elem.options.height\n });\n}\n\nfunction buildImage(obj) {\n const dfd = $.Deferred();\n const $img = $('', { src: obj.elem.getAttribute('href') });\n\n $img.on('load', function() {\n if (obj.options.resize === true || Number(obj.options.resize) === 1 || obj.options.resize === 'yes') {\n this.originalWidth = this.width;\n this.originalHeight = this.height;\n resizeImage(this);\n }\n dfd.resolve(this);\n });\n\n setWidthHeight($img, obj);\n\n return dfd.promise();\n}\n\nfunction buildVideo(obj) {\n let href = obj.elem.getAttribute('href');\n\n if (href.indexOf('?') !== -1 && href.indexOf('wmode=transparent') === -1) href += '&wmode=transparent';\n if (href.indexOf('?') === -1 && href.indexOf('&') === -1 && href.indexOf('wmode=transparent') === -1) href += '?wmode=transparent';\n\n const $iframe = $('', {\n src: href,\n width: obj.options.width,\n height: obj.options.height,\n frameborder: 0,\n allowfullscreen: true,\n mozallowfullscreen: true,\n webkitallowfullscreen: true\n });\n\n return $iframe;\n}\n\nfunction buildContent(obj) {\n const id = obj.elem.getAttribute('href').split('#')[1] || obj.elem.dataset.liteId;\n\n if (!id) return;\n\n let $obj = $(`#${id}`);\n const $iframes = $obj.find('iframe');\n\n if ($obj[0]) {\n idhash[id] = obj;\n }\n else {\n $obj = idhash[id].cachedContent;\n }\n\n if ($iframes.length > 0) {\n $iframes.each(function each() {\n let source = this.src;\n if (source.indexOf('?') !== -1 && source.indexOf('wmode=transparent') === -1) source += '&wmode=transparent';\n if (source.indexOf('?') === -1 && source.indexOf('&') === -1 && source.indexOf('wmode=transparent') === -1) source += '?wmode=transparent';\n if (this.src !== source) this.src = source;\n });\n }\n\n $obj.removeClass(obj.options.csshide);\n setWidthHeight($obj, obj);\n\n return $obj;\n}\n\nfunction Litebox(elem, options) {\n this.elem = elem;\n this.$elem = $(elem);\n this.timer = 0;\n\n let attrib;\n const dataAttribs = {};\n\n for (attrib in DATA_ATTRIBS_MAP) {\n if (typeof this.$elem.data(attrib) !== 'undefined') dataAttribs[DATA_ATTRIBS_MAP[attrib]] = this.$elem.data(attrib);\n }\n\n this.options = $.extend({}, $.fn.litebox.defaults, options, dataAttribs);\n this.init();\n}\n\nLitebox.prototype = {\n init() {\n const self = this;\n self.$root = $(self.options.root).length ? $(self.options.root) : $('body');\n if (!$overlay) self.stage();\n self.$elem.on('click.litebox-click', $.proxy(self.show, self));\n },\n\n enable() {\n this.$elem.on('click.litebox-click', $.proxy(this.show, this));\n },\n\n disable() {\n this.$elem.off('click.litebox-click');\n },\n\n stage() {\n const self = this;\n\n $overlay = $('
', { id: 'tlrk-overlay', class: self.options.csshide });\n $wrapper = $('
', { id: 'tlrk-litebox', class: self.options.csshide });\n $content = $('
', { class: (`litebox-content ${self.options.csshide}`) });\n $loader = $('
', { class: 'litebox-loader' }).html('Loading ...'); // html() is faster on empty div, than text()\n $close = $('', { href: '#', class: 'litebox-close' }).html('×');\n\n // clicking on an element with .litebox-close-btn in the content will close the litebox\n $content.on('click', '.litebox-close-btn', e => {\n e.preventDefault();\n self.hide();\n });\n\n self.$root.append($overlay, $wrapper);\n $wrapper.append($content, $close);\n },\n\n populate() {\n const self = this;\n\n if (self.cachedContent === undefined) {\n $content.append($loader);\n\n if (self.options.info === 'image') {\n buildImage(self).done(img => {\n self.cachedContent = img;\n if (!self.opened) return;\n $content.html(self.cachedContent);\n self.appendTitle();\n if (self.options.center) self.center();\n });\n }\n else if (self.options.info === 'video') {\n self.cachedContent = buildVideo(self);\n $loader.remove();\n $content.html(self.cachedContent);\n self.appendTitle();\n }\n else if (self.options.info === 'content') {\n self.cachedContent = buildContent(self);\n $loader.remove();\n $content.html(self.cachedContent);\n self.appendTitle();\n }\n }\n else {\n if (self.options.info === 'image') resizeImage(self.cachedContent);\n $content.html(self.cachedContent);\n self.appendTitle();\n }\n if (self.options.center) self.center();\n },\n\n appendTitle() {\n const self = this;\n const title = self.elem.dataset.title;\n if (title) {\n const $p = $('

', { class: 'litebox-title' });\n $p.text(title);\n $content.append($p);\n }\n },\n\n destroy() {\n $content.empty();\n },\n\n show() {\n const self = this;\n self.opened = true;\n\n if (self.options.noscroll) self.$root.addClass('tlrk-litebox-noscroll');\n\n self.populate();\n\n $wrapper.add($close).removeClass(self.options.csshide);\n\n $overlay.removeClass(self.options.csshide).addClass(self.options.animIn);\n $content.removeClass(self.options.csshide).addClass(self.options.animInSec);\n\n if (self.options.center) self.center();\n\n // close\n $overlay.add($close).add($wrapper).one('click', $.proxy(self.hide, self));\n $content.on('click', e => {\n e.stopPropagation();\n });\n\n $document.on('keydown.litebox-keydown', e => {\n if (e.keyCode === 27 || (e.keyCode === 8 && !($(e.target).is('input') || $(e.target).is('textarea')))) {\n self.hide();\n e.preventDefault(); // ff and ie prevent .gif animation stop\n }\n });\n\n $window.on('resize.litebox-resize', () => {\n if (self.timer) clearTimeout(self.timer);\n self.timer = setTimeout($.proxy(self.resize, self), 150);\n });\n\n if (liteboxLoaded) {\n return false;\n }\n },\n\n hide() {\n const self = this;\n self.opened = false;\n $close.addClass(self.options.csshide);\n\n $overlay.removeClass(self.options.animIn).addClass(self.options.animOut);\n $content.removeClass(self.options.animInSec).addClass(self.options.animOutSec);\n\n $content.on(ANIMATIONEND, () => {\n if (self.options.noscroll) self.$root.removeClass('tlrk-litebox-noscroll');\n $overlay.removeClass(self.options.animOut).addClass(self.options.csshide);\n $content.removeClass(self.options.animOutSec).addClass(self.options.csshide);\n $wrapper.addClass(self.options.csshide);\n self.destroy();\n $content.off(ANIMATIONEND);\n });\n\n $document.off('keydown.litebox-keydown');\n $window.off('resize.litebox-resize');\n\n return false;\n },\n\n center() {\n $content.css({\n 'margin-top': -($content.height() / 2),\n 'margin-left': -($content.width() / 2),\n 'margin-right': ($content.width() / 2)\n });\n },\n\n resize() {\n const self = this;\n resizeImage(self.cachedContent);\n if (self.options.center) self.center\n }\n};\n\n$.fn.litebox = function liteboxPlugin(options) {\n return this.each(function each() {\n if (!$.data(this, 'litebox')) $.data(this, 'litebox', new Litebox(this, options));\n });\n};\n\n$.fn.litebox.defaults = {\n info: 'image',\n animIn: 'fadeIn',\n animOut: 'fadeOut',\n animInSec: 'fadeInScale',\n animOutSec: 'fadeOutScale',\n csshide: '-dn',\n resize: true,\n noscroll: false,\n root: 'body',\n center: false\n};\n\nfunction bindEvent(el, eventName, handler) {\n if (el.addEventListener) {\n el.addEventListener(eventName, handler);\n }\n else if (el.attachEvent) {\n el.attachEvent(`on${eventName}`, () => {\n handler.call(el);\n });\n }\n}\n\nlet timer = false;\nconst minDeviceWidth = 990;\n\nfunction createInstances(options) {\n if (timer) {\n window.clearTimeout(timer);\n timer = false;\n }\n if ($window.width() >= minDeviceWidth && !liteboxLoaded) {\n $('.litebox').litebox(options);\n liteboxLoaded = true;\n }\n if ($window.width() < minDeviceWidth) {\n $(\".litebox[data-lite-info='content']\").litebox(options);\n liteboxLoaded = false;\n }\n}\n\nexport function initialize(options) {\n createInstances(options);\n\n $.fn.litebox.defaults = {\n ...$.fn.litebox.defaults,\n ...options\n };\n\n bindEvent(window, 'resize', () => {\n if (!timer) {\n timer = window.setTimeout(() => createInstances(options), 1000);\n }\n });\n\n bindEvent(window, 'orientationchange', () => createInstances(options));\n}\n","export const defaultOptions = {\n selector: '.siema',\n duration: 200,\n easing: 'ease-out',\n perPage: 1,\n startIndex: 0,\n draggable: true,\n multipleDrag: true,\n slidePage: false,\n threshold: 20,\n loop: false,\n rtl: false,\n dotNav: false, // generate dots navigation, it is false by default for backward compatibility\n dotNavClass: 'siema-dot-nav',\n dotClass: 'siema-dot',\n automatedTimer: 10000,\n onInit: () => {},\n onChange: () => {},\n};\n\nexport default class Siema {\n /**\n * @param {Object} options\n */\n constructor(options) {\n this.config = { ...defaultOptions, ...options };\n\n // THIS IS BAD, because it is the actual element and not the selector itself, be careful\n // the provided \"selector\" should be always a string\n this.element = this.selector = typeof this.config.selector === 'string' ? document.querySelector(this.config.selector) : this.config.selector;\n\n if (this.element === null) {\n throw new Error('Something wrong with your selector');\n }\n\n // Update perPage attribute depending on the options and viewport\n this.resolveSlidesNumber();\n\n this.elementWidth = this.element.offsetWidth;\n this.innerElements = Array.from(this.element.children);\n this.currentSlide = this.config.loop ?\n this.config.startIndex % this.innerElements.length :\n Math.max(0, Math.min(this.config.startIndex, this.innerElements.length - this.perPage));\n if (this.config.loop) {\n this.loopSlidesPrepended = [];\n this.loopSlidesAppended = [];\n }\n this.focusableElements = 'a, input, select, textarea, button, iframe, [tabindex], [contentEditable]';\n this.hideA11y = (wrapper) => {\n wrapper.setAttribute('aria-hidden', 'true');\n wrapper.querySelectorAll(this.focusableElements).forEach(focusable => focusable.setAttribute('tabindex', '-1'));\n };\n this.showA11y = (wrapper) => {\n wrapper.removeAttribute('aria-hidden');\n wrapper.querySelectorAll(this.focusableElements).forEach(focusable => focusable.removeAttribute('tabindex'));\n };\n\n // Bind all event handlers for referencability\n [\n 'resizeHandler',\n 'touchstartHandler',\n 'touchendHandler',\n 'touchmoveHandler',\n 'mousedownHandler',\n 'mouseupHandler',\n 'mouseleaveHandler',\n 'mousemoveHandler',\n 'clickHandler',\n 'btnNextHandler',\n 'btnPrevHandler',\n 'dotClickHandler'\n ].forEach(method => {\n this[method] = this[method].bind(this);\n });\n\n if (!options.dotNav) {\n this.dots = options.dots ? Array.from(document.querySelectorAll(options.dots)) : null;\n }\n\n this.btnNext = options.btnNext ? document.querySelector(options.btnNext) : null;\n this.btnPrev = options.btnPrev ? document.querySelector(options.btnPrev) : null;\n\n // Build markup and apply required styling to elements\n this.init();\n }\n\n /**\n * Attaches listeners to required events.\n */\n attachEvents() {\n // Resize element on window resize\n window.addEventListener('resize', this.resizeHandler);\n\n // prev & next button support\n if (this.btnNext) this.btnNext.addEventListener('click', this.btnNextHandler);\n if (this.btnPrev) this.btnPrev.addEventListener('click', this.btnPrevHandler);\n\n // tab controls (dots)\n if (this.dots) {\n this.dots.forEach((dot, index) => dot.addEventListener('click', this.dotClickHandler(index)));\n }\n\n // If element is draggable / swipable, add event handlers\n if (this.config.draggable) {\n // Keep track pointer hold and dragging distance\n this.pointerDown = false;\n this.drag = {\n startX: 0,\n endX: 0,\n startY: 0,\n letItGo: null,\n preventClick: false,\n };\n\n // Touch events\n this.element.addEventListener('touchstart', this.touchstartHandler);\n this.element.addEventListener('touchend', this.touchendHandler);\n this.element.addEventListener('touchmove', this.touchmoveHandler);\n\n // Mouse events\n this.element.addEventListener('mousedown', this.mousedownHandler);\n this.element.addEventListener('mouseup', this.mouseupHandler);\n this.element.addEventListener('mouseleave', this.mouseleaveHandler);\n this.element.addEventListener('mousemove', this.mousemoveHandler);\n\n // Click\n this.element.addEventListener('click', this.clickHandler);\n }\n }\n\n /**\n * Detaches listeners from required events.\n */\n detachEvents() {\n window.removeEventListener('resize', this.resizeHandler);\n if (this.btnNext) this.btnNext.removeEventListener('click', this.btnNextHandler);\n if (this.btnPrev) this.btnPrev.removeEventListener('click', this.btnPrevHandler);\n this.element.removeEventListener('touchstart', this.touchstartHandler);\n this.element.removeEventListener('touchend', this.touchendHandler);\n this.element.removeEventListener('touchmove', this.touchmoveHandler);\n this.element.removeEventListener('mousedown', this.mousedownHandler);\n this.element.removeEventListener('mouseup', this.mouseupHandler);\n this.element.removeEventListener('mouseleave', this.mouseleaveHandler);\n this.element.removeEventListener('mousemove', this.mousemoveHandler);\n this.element.removeEventListener('click', this.clickHandler);\n }\n\n /**\n * Builds the markup and attaches listeners to required events.\n */\n init() {\n // hide everything out of selector's boundaries\n this.element.style.overflow = 'hidden';\n\n // rtl or ltr\n this.element.style.direction = this.config.rtl ? 'rtl' : 'ltr';\n\n // build a frame and slide to a currentSlide\n this.buildSliderFrame();\n\n // initialize automation\n if (this.config.automated) this.initAutomation();\n\n this.config.onInit.call(this);\n }\n\n btnNextHandler() {\n this.next();\n if (this.autoplay) this.autoplay = window.clearInterval(this.autoplay);\n }\n\n btnPrevHandler() {\n this.prev();\n if (this.autoplay) this.autoplay = window.clearInterval(this.autoplay);\n }\n\n dotClickHandler(index) {\n return (event) => {\n event.preventDefault();\n\n if (this.config.automated) {\n this.autoplay = window.clearInterval(this.autoplay);\n }\n\n if (this.config.slidePage) {\n this.goTo(index * this.perPage);\n return;\n }\n\n this.goTo(index);\n }\n }\n\n /**\n * Build a dot navigation dynamically\n */\n buildDotNavigation() {\n this.dotNav = document.createElement('div');\n\n this.dots = this.innerElements.flatMap((element, index) => {\n // If slidePage option is enabled create a single dot per page\n if (this.config.slidePage && this.perPage > 1 && (index + 1) % this.perPage !== 1) return [];\n \n if (!this.config.slidePage && index > this.innerElements.length - this.perPage) return [];\n\n const dot = document.createElement('a');\n dot.href = `#slide-${index + 1}`;\n dot.dataset.slide = index;\n dot.setAttribute('aria-label', `Go to slide ${index + 1}`);\n dot.classList.add(this.config.dotClass);\n return [dot];\n });\n\n this.dotNav.append(...this.dots);\n\n this.dotNav.classList.add(...[this.config.dotNavClass].flat());\n this.element.appendChild(this.dotNav);\n }\n\n /**\n * Build a sliderFrame and slide to a current item.\n */\n buildSliderFrame() {\n const widthItem = this.elementWidth / this.perPage;\n const itemsToBuild = this.config.loop ? this.innerElements.length + (2 * this.perPage) : this.innerElements.length;\n\n // Create frame and apply styling\n this.sliderFrame = document.createElement('div');\n this.sliderFrame.style.width = `${widthItem * itemsToBuild}px`;\n this.enableTransition();\n\n if (this.config.draggable) {\n this.element.style.cursor = 'grab';\n }\n\n // Create a document fragment to put slides into it\n const docFragment = document.createDocumentFragment();\n const createLoopSlide = (slide, loopSlides) => {\n const el = this.buildSliderFrameItem(slide.cloneNode(true));\n this.hideA11y(el);\n docFragment.appendChild(el);\n loopSlides.push(el);\n };\n\n // Loop through the slides, add styling and add them to document fragment\n if (this.config.loop) {\n for (let i = this.innerElements.length - this.perPage; i < this.innerElements.length; i++) {\n createLoopSlide(this.innerElements[i], this.loopSlidesPrepended);\n }\n }\n for (let i = 0; i < this.innerElements.length; i++) {\n const element = this.buildSliderFrameItem(this.innerElements[i]);\n docFragment.appendChild(element);\n }\n if (this.config.loop) {\n for (let i = 0; i < this.perPage; i++) {\n createLoopSlide(this.innerElements[i], this.loopSlidesAppended);\n }\n }\n\n // Add fragment to the frame\n this.sliderFrame.appendChild(docFragment);\n\n // Clear selector (just in case something is there) and insert a frame\n this.element.innerHTML = '';\n this.element.appendChild(this.sliderFrame);\n\n // build dots navigation\n if (this.config.dotNav) {\n this.buildDotNavigation();\n }\n\n this.detachEvents();\n this.attachEvents();\n\n // Go to currently active slide after initial build\n this.slideToCurrent();\n\n this.change();\n }\n\n buildSliderFrameItem(elm) {\n const elementContainer = document.createElement('div');\n elementContainer.style.cssFloat = this.config.rtl ? 'right' : 'left';\n elementContainer.style.float = this.config.rtl ? 'right' : 'left';\n elementContainer.style.width = `${this.config.loop ? 100 / (this.innerElements.length + (this.perPage * 2)) : 100 / (this.innerElements.length)}%`;\n elementContainer.appendChild(elm);\n return elementContainer;\n }\n\n /**\n * Determinates slides number accordingly to clients viewport.\n */\n resolveSlidesNumber() {\n if (typeof this.config.perPage === 'number') {\n this.perPage = this.config.perPage;\n }\n else if (typeof this.config.perPage === 'object') {\n this.perPage = 1;\n for (const viewport in this.config.perPage) {\n if (window.innerWidth >= viewport) {\n this.perPage = this.config.perPage[viewport];\n }\n }\n }\n }\n\n /**\n * Go to previous slide.\n * @param {number} howManySlides - How many items to slide backward.\n * @param {function} callback - Optional callback function.\n */\n prev(howManySlides, callback) {\n // early return when there is nothing to slide\n if (this.innerElements.length <= this.perPage) {\n return;\n }\n\n if (!howManySlides) {\n howManySlides = this.config.slidePage ? this.perPage : 1;\n }\n\n const beforeChange = this.currentSlide;\n\n if (this.config.loop) {\n const isNewIndexClone = this.currentSlide - howManySlides < 0;\n if (isNewIndexClone) {\n this.disableTransition();\n\n const mirrorSlideIndex = this.currentSlide + this.innerElements.length;\n const mirrorSlideIndexOffset = this.perPage;\n const moveTo = mirrorSlideIndex + mirrorSlideIndexOffset;\n const offset = (this.config.rtl ? 1 : -1) * moveTo * (this.elementWidth / this.perPage);\n const dragDistance = this.config.draggable ? this.drag.endX - this.drag.startX : 0;\n\n this.sliderFrame.style.transform = `translate3d(${offset + dragDistance}px, 0, 0)`;\n this.currentSlide = mirrorSlideIndex - howManySlides;\n }\n else {\n this.currentSlide = this.currentSlide - howManySlides;\n }\n }\n else {\n this.currentSlide = Math.max(this.currentSlide - howManySlides, 0);\n }\n\n if (beforeChange !== this.currentSlide) {\n this.slideToCurrent(this.config.loop);\n this.config.onChange.call(this);\n this.change();\n if (callback) {\n callback.call(this);\n }\n }\n }\n\n /**\n * Go to next slide.\n * @param {number} howManySlides - How many items to slide forward.\n * @param {function} callback - Optional callback function.\n */\n next(howManySlides, callback) {\n // early return when there is nothing to slide\n if (this.innerElements.length <= this.perPage) {\n return;\n }\n\n if (!howManySlides) {\n howManySlides = this.config.slidePage ? this.perPage : 1;\n }\n\n const beforeChange = this.currentSlide;\n\n if (this.config.loop) {\n const isNewIndexClone = this.currentSlide + howManySlides > this.innerElements.length - this.perPage;\n if (isNewIndexClone) {\n this.disableTransition();\n\n const mirrorSlideIndex = this.currentSlide - this.innerElements.length;\n const mirrorSlideIndexOffset = this.perPage;\n const moveTo = mirrorSlideIndex + mirrorSlideIndexOffset;\n const offset = (this.config.rtl ? 1 : -1) * moveTo * (this.elementWidth / this.perPage);\n const dragDistance = this.config.draggable ? this.drag.endX - this.drag.startX : 0;\n\n this.sliderFrame.style.transform = `translate3d(${offset + dragDistance}px, 0, 0)`;\n this.currentSlide = mirrorSlideIndex + howManySlides;\n }\n else {\n this.currentSlide = this.currentSlide + howManySlides;\n }\n }\n else {\n this.currentSlide = Math.min(this.currentSlide + howManySlides, this.innerElements.length - this.perPage);\n }\n if (beforeChange !== this.currentSlide) {\n this.slideToCurrent(this.config.loop);\n this.config.onChange.call(this);\n this.change();\n if (callback) {\n callback.call(this);\n }\n }\n }\n\n /**\n * Disable transition on sliderFrame.\n */\n disableTransition() {\n this.sliderFrame.style.transition = `all 0ms ${this.config.easing}`;\n }\n\n /**\n * Enable transition on sliderFrame.\n */\n enableTransition() {\n this.sliderFrame.style.transition = `all ${this.config.duration}ms ${this.config.easing}`;\n }\n\n /**\n * Go to slide with particular index\n * @param {number} index - Item index to slide to.\n * @param {function} callback - Optional callback function.\n */\n goTo(index, callback) {\n if (this.innerElements.length <= this.perPage) {\n return;\n }\n const beforeChange = this.currentSlide;\n this.currentSlide = this.config.loop ?\n index % this.innerElements.length :\n Math.min(Math.max(index, 0), this.innerElements.length - this.perPage);\n if (beforeChange !== this.currentSlide) {\n this.slideToCurrent();\n this.config.onChange.call(this);\n this.change();\n if (callback) {\n callback.call(this);\n }\n }\n }\n\n /**\n * Moves sliders frame to position of currently active slide\n */\n slideToCurrent(enableTransition) {\n const currentSlide = this.config.loop ? this.currentSlide + this.perPage : this.currentSlide;\n const offset = (this.config.rtl ? 1 : -1) * currentSlide * (this.elementWidth / this.perPage);\n\n if (enableTransition) {\n // This one is tricky, I know but this is a perfect explanation:\n // https://youtu.be/cCOL7MC4Pl0\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n this.enableTransition();\n this.sliderFrame.style.transform = `translate3d(${offset}px, 0, 0)`;\n });\n });\n }\n else {\n this.sliderFrame.style.transform = `translate3d(${offset}px, 0, 0)`;\n }\n }\n\n /**\n * Recalculate drag /swipe event and reposition the frame of a slider\n */\n updateAfterDrag() {\n const movement = (this.config.rtl ? -1 : 1) * (this.drag.endX - this.drag.startX);\n const movementDistance = Math.abs(movement);\n let howManySliderToSlide = this.config.multipleDrag ? Math.ceil(movementDistance / (this.elementWidth / this.perPage)) : 1;\n\n if (this.config.slidePage) {\n howManySliderToSlide = Math.floor(howManySliderToSlide / 3) * 3;\n }\n\n const slideToNegativeClone = movement > 0 && this.currentSlide - howManySliderToSlide < 0;\n const slideToPositiveClone = movement < 0 && this.currentSlide + howManySliderToSlide > this.innerElements.length - this.perPage;\n\n if (movement > 0 && movementDistance > this.config.threshold && this.innerElements.length > this.perPage) {\n this.prev(howManySliderToSlide);\n }\n else if (movement < 0 && movementDistance > this.config.threshold && this.innerElements.length > this.perPage) {\n this.next(howManySliderToSlide);\n }\n this.slideToCurrent(slideToNegativeClone || slideToPositiveClone);\n\n this.autoplay = window.clearInterval(this.autoplay);\n }\n\n /**\n * When window resizes, resize slider components as well\n */\n resizeHandler() {\n // update perPage number dependable of user value\n this.resolveSlidesNumber();\n\n // relcalculate currentSlide\n // prevent hiding items when browser width increases\n if (this.currentSlide + this.perPage > this.innerElements.length) {\n this.currentSlide = this.innerElements.length <= this.perPage ? 0 : this.innerElements.length - this.perPage;\n }\n\n this.elementWidth = this.element.offsetWidth;\n\n this.buildSliderFrame();\n }\n\n /**\n * Clear drag after touchend and mouseup event\n */\n clearDrag() {\n this.drag = {\n startX: 0,\n endX: 0,\n startY: 0,\n letItGo: null,\n preventClick: this.drag.preventClick\n };\n }\n\n touchstartHandler(e) {\n // Prevent dragging / swiping on inputs, selects and textareas\n const ignoreSiema = ['TEXTAREA', 'OPTION', 'INPUT', 'SELECT'].indexOf(e.target.nodeName) !== -1;\n if (ignoreSiema) {\n return;\n }\n\n e.stopPropagation();\n this.pointerDown = true;\n this.drag.startX = e.touches[0].pageX;\n this.drag.startY = e.touches[0].pageY;\n }\n\n touchendHandler(e) {\n e.stopPropagation();\n this.pointerDown = false;\n this.enableTransition();\n if (this.drag.endX) {\n this.updateAfterDrag();\n }\n this.clearDrag();\n }\n\n touchmoveHandler(e) {\n e.stopPropagation();\n\n if (this.drag.letItGo === null) {\n this.drag.letItGo = Math.abs(this.drag.startY - e.touches[0].pageY) < Math.abs(this.drag.startX - e.touches[0].pageX);\n }\n\n if (this.pointerDown && this.drag.letItGo) {\n e.preventDefault();\n this.drag.endX = e.touches[0].pageX;\n this.sliderFrame.style.transition = `all 0ms ${this.config.easing}`;\n\n const currentSlide = this.config.loop ? this.currentSlide + this.perPage : this.currentSlide;\n const currentOffset = currentSlide * (this.elementWidth / this.perPage);\n const dragOffset = (this.drag.endX - this.drag.startX);\n const offset = this.config.rtl ? currentOffset + dragOffset : currentOffset - dragOffset;\n this.sliderFrame.style.transform = `translate3d(${(this.config.rtl ? 1 : -1) * offset}px, 0, 0)`;\n }\n }\n\n mousedownHandler(e) {\n // Prevent dragging / swiping on inputs, selects and textareas\n const ignoreSiema = ['TEXTAREA', 'OPTION', 'INPUT', 'SELECT'].indexOf(e.target.nodeName) !== -1;\n if (ignoreSiema) {\n return;\n }\n\n e.preventDefault();\n e.stopPropagation();\n this.pointerDown = true;\n this.drag.startX = e.pageX;\n }\n\n mouseupHandler(e) {\n e.stopPropagation();\n this.pointerDown = false;\n this.element.style.cursor = 'grab';\n this.enableTransition();\n if (this.drag.endX) {\n this.updateAfterDrag();\n }\n this.clearDrag();\n }\n\n mousemoveHandler(e) {\n e.preventDefault();\n if (this.pointerDown) {\n // if dragged element is a link\n // mark preventClick prop as a true\n // to detemine about browser redirection later on\n if (e.target.nodeName === 'A') {\n this.drag.preventClick = true;\n }\n\n this.drag.endX = e.pageX;\n this.element.style.cursor = 'grabbing';\n this.sliderFrame.style.transition = `all 0ms ${this.config.easing}`;\n\n const currentSlide = this.config.loop ? this.currentSlide + this.perPage : this.currentSlide;\n const currentOffset = currentSlide * (this.elementWidth / this.perPage);\n const dragOffset = (this.drag.endX - this.drag.startX);\n const offset = this.config.rtl ? currentOffset + dragOffset : currentOffset - dragOffset;\n this.sliderFrame.style.transform = `translate3d(${(this.config.rtl ? 1 : -1) * offset}px, 0, 0)`;\n }\n }\n\n mouseleaveHandler(e) {\n if (this.pointerDown) {\n this.pointerDown = false;\n this.element.style.cursor = 'grab';\n this.drag.endX = e.pageX;\n this.drag.preventClick = false;\n this.enableTransition();\n this.updateAfterDrag();\n this.clearDrag();\n }\n }\n\n clickHandler(e) {\n // if the dragged element is a link\n // prevent browsers from folowing the link\n if (this.drag.preventClick) {\n e.preventDefault();\n }\n this.drag.preventClick = false;\n }\n\n /**\n * Remove item from carousel.\n * @param {number} index - Item index to remove.\n * @param {function} callback - Optional callback to call after remove.\n */\n remove(index, callback) {\n if (index < 0 || index >= this.innerElements.length) {\n throw new Error('Item to remove doesn\\'t exist');\n }\n\n // Shift sliderFrame back by one item when:\n // 1. Item with lower index than currenSlide is removed.\n // 2. Last item is removed.\n const lowerIndex = index < this.currentSlide;\n const lastItem = this.currentSlide + this.perPage - 1 === index;\n\n if (lowerIndex || lastItem) {\n this.currentSlide--;\n }\n\n this.innerElements.splice(index, 1);\n\n // build a frame and slide to a currentSlide\n this.buildSliderFrame();\n\n if (callback) {\n callback.call(this);\n }\n }\n\n /**\n * Insert item to carousel at particular index.\n * @param {HTMLElement} item - Item to insert.\n * @param {number} index - Index of new new item insertion.\n * @param {function} callback - Optional callback to call after insert.\n */\n insert(item, index, callback) {\n if (index < 0 || index > this.innerElements.length + 1) {\n throw new Error('Unable to inset it at this index');\n }\n if (this.innerElements.indexOf(item) !== -1) {\n throw new Error('The same item in a carousel? Really? Nope');\n }\n\n // Avoid shifting content\n const shouldItShift = index <= this.currentSlide > 0 && this.innerElements.length;\n this.currentSlide = shouldItShift ? this.currentSlide + 1 : this.currentSlide;\n\n this.innerElements.splice(index, 0, item);\n\n // build a frame and slide to a currentSlide\n this.buildSliderFrame();\n\n if (callback) {\n callback.call(this);\n }\n }\n\n /**\n * Prepernd item to carousel.\n * @param {HTMLElement} item - Item to prepend.\n * @param {function} callback - Optional callback to call after prepend.\n */\n prepend(item, callback) {\n this.insert(item, 0);\n if (callback) {\n callback.call(this);\n }\n }\n\n /**\n * Append item to carousel.\n * @param {HTMLElement} item - Item to append.\n * @param {function} callback - Optional callback to call after append.\n */\n append(item, callback) {\n this.insert(item, this.innerElements.length + 1);\n if (callback) {\n callback.call(this);\n }\n }\n\n /**\n * Removes listeners and optionally restores to initial markup\n * @param {boolean} restoreMarkup - Determinants about restoring an initial markup.\n * @param {function} callback - Optional callback function.\n */\n destroy(restoreMarkup = false, callback) {\n this.detachEvents();\n\n this.element.style.cursor = 'auto';\n\n if (restoreMarkup) {\n const slides = document.createDocumentFragment();\n for (let i = 0; i < this.innerElements.length; i++) {\n slides.appendChild(this.innerElements[i]);\n }\n this.element.innerHTML = '';\n this.element.appendChild(slides);\n this.element.removeAttribute('style');\n }\n\n if (callback) {\n callback.call(this);\n }\n }\n\n /**\n * Creates the automated interval\n */\n initAutomation() {\n this.autoplay = window.setInterval(() => {\n if (this.currentSlide === this.innerElements.length - 1) return this.goTo(0);\n this.next();\n }, this.config.automatedTimer);\n\n this.element.addEventListener('touchstart', () => {\n this.autoplay = window.clearInterval(this.autoplay);\n });\n }\n\n /**\n * Sets the active class on the currently active dot\n */\n setActiveDot() {\n if (!this.dots) return;\n\n const activeDot = this.dots.find(el => el.classList.contains('is-active'));\n if (activeDot) activeDot.classList.remove('is-active');\n\n let activeDotIndex = this.currentSlide;\n\n if (this.config.slidePage && this.perPage > 1) {\n activeDotIndex = Math.ceil((this.currentSlide + 1) / this.perPage) - 1;\n if ((this.currentSlide + 1) % this.perPage !== 1) activeDotIndex += 1;\n }\n\n if (this.perPage > 1) {\n if (activeDotIndex < 0) activeDotIndex = 0;\n if (activeDotIndex > this.dots.length - 1) activeDotIndex = this.dots.length - 1;\n }\n\n this.dots[activeDotIndex].classList.add('is-active');\n }\n\n /**\n * Handles side-effects on change\n */\n change() {\n this.setActiveDot();\n\n // Disable buttons\n if (this.btnNext && !this.config.loop) {\n this.btnNext.disabled = false;\n if (this.currentSlide + this.perPage === this.innerElements.length) this.btnNext.disabled = true;\n }\n\n if (this.btnPrev && !this.config.loop) {\n this.btnPrev.disabled = false;\n if (this.currentSlide === 0) this.btnPrev.disabled = true;\n }\n\n // accessibility \n this.innerElements.forEach((slide, i) => {\n if (i < this.currentSlide || i > this.currentSlide + this.perPage - 1) {\n this.hideA11y(slide.parentElement);\n } \n else {\n this.showA11y(slide.parentElement);\n }\n if (this.config.loop) {\n this.loopSlidesPrepended.concat(this.loopSlidesAppended).forEach(loopSlide => this.hideA11y(loopSlide));\n if (this.currentSlide < 0) { \n for (let i = this.loopSlidesPrepended.length + this.currentSlide; i < this.loopSlidesPrepended.length; i++) {\n this.showA11y(this.loopSlidesPrepended[i]);\n }\n }\n else if (this.currentSlide > this.innerElements.length - this.perPage) {\n for (let i = 0; i <= this.perPage - (this.innerElements.length - this.currentSlide) - 1; i++) {\n this.showA11y(this.loopSlidesAppended[i]);\n }\n }\n }\n });\n }\n}\n\n// Attach to jquery if present\nif (window.jQuery) {\n $.fn.siema = function(options) {\n return this.each(function() {\n if (!$.data(this, 'siema')) {\n $.data(this, 'siema', new Siema({\n ...options,\n selector: this\n }));\n }\n });\n };\n}\n","'use strict'\n\nfunction noop() {\n\n}\n\nfunction offset(element, from) {\n return element.getBoundingClientRect()[from] + (window.scrollY || window.pageYOffset)\n}\n\nfunction rect(element, key) {\n return element.getBoundingClientRect()[key]\n}\n\n/**\n * @type {object} defaults\n */\nconst defaults = {\n top: 0, /* Where to stick from top */\n bottom: 0, /* Extra added to stopper selector */\n breakpoint: 0, /* Where to stop or start */\n resize: false, /* Handle resize with js, suited for sidebars and responsive */\n classOnly: true, /* Sticky only with css class with position fixed */\n elevation: false, /* If sticky element is bigger than screen, turn it off */\n fixClass: 'is-fixed', /* Default sticky class */\n selector: '.js-sticky-element', /* The element selector to stick */\n placeholder: true, /* If false, no placeholder is created */\n placeholderClass: 'sticky-element-placeholder', /* Placeholder css class */\n onBreakpointAbove: noop, /* function to be called when above breakpoint */\n onBreakpointBelow: noop /* function to be called when bellow breakpoint */\n}\n\n/**\n * @class StickyElement\n * @constructor\n * @param {object} options - plugin options\n * @param {HTMLElement} element - html element\n */\nfunction StickyElement(options, element) {\n this.options = Object.assign({}, defaults, options)\n this.element = element || document.querySelector(this.options.selector)\n this.stopper = document.querySelector(this.options.stopSelector)\n this.fixed = false\n this.ticking = false\n this.pinned = false\n\n if (this.options.placeholder) {\n this.placeholder = document.createElement('div')\n this.placeholder.className = this.options.placeholderClass\n }\n\n this.scrollTop = window.scrollY || window.pageYOffset\n this.initial = this.bounds()\n\n this.turnOn = this.turnOn.bind(this)\n this.turnOff = this.turnOff.bind(this)\n\n this.boundOnScroll = this.onScroll.bind(this)\n this.boundOnResize = this.onResize.bind(this)\n this.boundSwitchHandler = this.switchHandler.bind(this)\n\n this.boundBreakpointHandler = this.breakpointHandler.bind(this)\n this.onBreakpointAbove = this.options.onBreakpointAbove.bind(this)\n this.onBreakpointBelow = this.options.onBreakpointBelow.bind(this)\n\n this.turnOn()\n}\n\nStickyElement.prototype.bounds = function() {\n return {\n top: offset(this.element, 'top'),\n bottom: offset(this.element, 'bottom'),\n width: rect(this.element, 'width'),\n height: rect(this.element, 'height'),\n position: this.element.style.position\n }\n}\n\n\nStickyElement.prototype.css = function(def) {\n Object.keys(def).forEach(function(key) {\n this.element.style[key] = def[key]\n }, this)\n}\n\n\nStickyElement.prototype.addPlaceholder = function() {\n if (this.options.placeholder) {\n this.placeholder.style.height = this.initial.height + 'px'\n this.element.parentElement.insertBefore(this.placeholder, this.element)\n }\n}\n\n\nStickyElement.prototype.fix = function() {\n this.addPlaceholder()\n\n if (!this.options.classOnly) {\n this.css({\n position: 'fixed',\n top: this.options.top + 'px',\n width: this.initial.width + 'px'\n })\n }\n\n this.element.classList.add(this.options.fixClass)\n this.fixed = true\n}\n\n\nStickyElement.prototype.unfix = function() {\n if (document.body.contains(this.placeholder)) {\n this.placeholder.parentElement.removeChild(this.placeholder)\n }\n\n if (!this.options.classOnly) {\n this.css({\n position: this.initial.position,\n top: 'auto',\n width: 'auto'\n })\n }\n\n this.element.classList.remove(this.options.fixClass)\n this.fixed = false\n}\n\n\nStickyElement.prototype.pin = function() {\n this.css({\n transform: `translate3d(0, ${this.diff}px, 0)`\n })\n this.pinned = true\n}\n\n\nStickyElement.prototype.unpin = function() {\n this.css({\n transform: 'translate3d(0, 0, 0)'\n })\n this.pinned = false\n}\n\n\nStickyElement.prototype.requestTick = function(method) {\n if (!this.ticking) {\n window.requestAnimationFrame(method.bind(this))\n }\n this.ticking = true\n}\n\n\nStickyElement.prototype.update = function() {\n if (this.scrollTop + this.options.top >= this.initial.top) {\n if (!this.fixed) {\n this.fix()\n }\n }\n else {\n if (this.fixed) {\n this.unfix()\n }\n }\n\n if (this.stopper) {\n this.diff <= 0 ? this.pin() : this.unpin()\n }\n\n this.ticking = false\n}\n\n\nStickyElement.prototype.calcDiff = function() {\n if (this.stopper) {\n let { top, bottom } = this.options\n this.diff = rect(this.stopper, 'top') - rect(this.element, 'height') - top - bottom\n }\n}\n\n\nStickyElement.prototype.reset = function() {\n this.unfix()\n this.unpin()\n this.initial = this.bounds()\n}\n\n\nStickyElement.prototype.onScroll = function() {\n this.calcDiff()\n this.scrollTop = window.scrollY || window.pageYOffset\n this.requestTick(this.update)\n}\n\n\nStickyElement.prototype.onResize = function() {\n this.reset()\n this.calcDiff()\n this.requestTick(this.update)\n}\n\n\n/*\n * Turn Sticky Behaviour on/off according to breakpoint and elevation options\n */\nStickyElement.prototype.switchHandler = function() {\n let { top, bottom, breakpoint, elevation } = this.options\n let stickyHeight = Math.floor(this.bounds()['height']) + top + bottom\n let clientWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)\n let clientHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)\n\n if (breakpoint && elevation) {\n if ((clientWidth >= breakpoint) && (stickyHeight <= clientHeight)) {\n setTimeout(this.turnOn)\n return\n }\n\n\n if ((clientWidth <= breakpoint) && (stickyHeight >= clientHeight)) {\n setTimeout(this.turnOff)\n return\n }\n\n if ((clientWidth <= breakpoint) && (stickyHeight <= clientHeight)) {\n setTimeout(this.turnOff)\n return\n }\n\n if ((clientWidth >= breakpoint) && (stickyHeight >= clientHeight)) {\n setTimeout(this.turnOff)\n return\n }\n\n return\n }\n\n if (breakpoint) {\n setTimeout(clientWidth <= breakpoint ? this.turnOff : this.turnOn)\n return\n }\n\n if (elevation) {\n setTimeout(stickyHeight >= clientHeight ? this.turnOff : this.turnOn)\n return\n }\n}\n\n\n/*\n * Handle client attached handlers below/above breakpoint\n */\nStickyElement.prototype.breakpointHandler = function() {\n let { breakpoint } = this.options\n let clientWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)\n\n if (!breakpoint) return\n\n if (clientWidth >= breakpoint) {\n if (!this._breakpointAboveHandlerExecuted) {\n this.onBreakpointAbove()\n this._breakpointAboveHandlerExecuted = true\n this._breakpointBelowHandlerExecuted = false\n }\n }\n else {\n if (!this._breakpointBelowHandlerExecuted) {\n this.onBreakpointBelow()\n this._breakpointBelowHandlerExecuted = true\n this._breakpointAboveHandlerExecuted = false\n }\n }\n}\n\n\n/*\n * Turn ON sticky behaviour\n */\nStickyElement.prototype.turnOn = function() {\n if (this._switchedOn) return\n\n let { resize, breakpoint, elevation, onBreakpointBelow, onBreakpointAbove } = this.options\n\n this.boundOnScroll()\n this.boundOnResize()\n this.boundSwitchHandler()\n this.boundBreakpointHandler()\n\n window.addEventListener('scroll', this.boundOnScroll, false)\n if (resize) window.addEventListener('resize', this.boundOnResize, false)\n if (breakpoint || elevation) window.addEventListener('resize', this.boundSwitchHandler, false)\n if (breakpoint && (onBreakpointBelow !== noop || onBreakpointAbove !== noop)) window.addEventListener('resize', this.boundBreakpointHandler, false)\n\n this.reset()\n this._switchedOn = true\n}\n\n\n/*\n * Turn OFF sticky behaviour\n */\nStickyElement.prototype.turnOff = function() {\n if (!this._switchedOn) return\n\n window.removeEventListener('scroll', this.boundOnScroll, false)\n window.removeEventListener('resize', this.boundOnResize, false)\n window.removeEventListener('resize', this.boundBreakpointHandler, false)\n\n this.reset()\n this._switchedOn = false\n}\n\n/*\n * Factory\n */\nStickyElement.create = function(options) {\n options = options || {}\n return new StickyElement(options)\n}\n\n// Attach to jquery if presented\nif (window.jQuery) {\n $.fn.StickyElement = function(options) {\n return this.each(function() {\n if (!$.data(this, 'StickyElement')) {\n $.data(this, 'StickyElement', new StickyElement(options, this))\n }\n })\n }\n\n // lowercase, so we can initialize via data attr parser\n $.fn.stickyelement = $.fn.StickyElement;\n}\n\n// Attach to window\nwindow.StickyElement = StickyElement\n\n// DEFAULT EXPORT\nexport default StickyElement\n\n","(function (exports) {\n 'use strict';\n\n const rejson = /^(?:\\{[\\w\\W]*\\}|\\[[\\w\\W]*\\])$/;\n\n function isNumber(num) {\n var number = +num;\n\n if ((number - number) !== 0) {\n // Discard Infinity and NaN\n return false;\n }\n\n if (number === num) {\n return true;\n }\n\n if (typeof num === 'string') {\n // String parsed, both a non-empty whitespace string and an empty string\n // will have been coerced to 0. If 0 trim the string and see if its empty.\n if (number === 0 && num.trim() === '') {\n return false;\n }\n return true;\n }\n return false;\n }\n function hyphenToCamelCase(hyphen) {\n return hyphen.replace(/-([a-z])/g, function(match) {\n return match[1].toUpperCase();\n });\n }\n\n function stringToCamelCase(string) {\n return string.replace(/(?:^\\w|[A-Z]|\\b\\w)/g, function(letter, index) {\n return index == 0 ? letter.toLowerCase() : letter.toUpperCase();\n }).replace(/\\s+/g, '');\n }\n\n\n class Parser {\n static parse(dataset, _prefixes = []) {\n let result = {};\n const keys = Object.keys(dataset);\n const grouping = _prefixes.length;\n const prefixes = _prefixes.map(hyphenToCamelCase);\n\n for (let i = 0; i < keys.length; i++) {\n let key = keys[i];\n let value = dataset[key];\n\n if (value === 'true') {\n result[key] = true;\n continue;\n }\n\n if (value === 'false') {\n result[key] = false;\n continue;\n }\n\n if (isNumber(value)) {\n result[key] = Number(value);\n continue;\n }\n\n if (value === 'null') {\n result[key] = null;\n continue;\n }\n\n if (rejson.test(value)) {\n result[key] = JSON.parse(value);\n continue;\n }\n\n // @String\n result[key] = value;\n }\n\n // Group result object\n prefixes.forEach(prefix => {\n let subResult = {};\n\n Object.keys(result)\n .filter(key => key.startsWith(prefix))\n .forEach(key => {\n let slicedKey = stringToCamelCase(key.replace(prefix, ''));\n if (slicedKey === '') slicedKey = prefix;\n subResult[slicedKey] = result[key];\n delete result[key];\n });\n\n result[prefix] = subResult;\n });\n\n return result;\n }\n }\n\n const defaults = {\n maxPages: 10, // maximum total pages => example 100\n maxVisiblePages: 10, // maximum visible pages => example 10\n pageSize: 10, // maximum items per page\n totalItems: 0, // total items count on all pages\n showNextAndPrev: true,\n nextText: 'Next',\n prevText: 'Previous',\n pageURI: '', // example => \"search\", '/search', etc\n pageQueryKey: 'page',\n preserveQueryParams: true, // preserve current query params from current url in pager links\n wrapperClass: 'TK-Pager',\n nextWrapperClass: 'TK-Pager-Next',\n nextLinkClass: 'TK-Pager-Next-Link',\n prevWrapperClass: 'TK-Pager-Prev',\n prevLinkClass: 'TK-Pager-Prev-Link',\n pagesWrapperClass: 'TK-Pager-Links',\n pageLinkClass: 'TK-Pager-Link',\n callback: (instance, selectedPage) => console.log(selectedPage)\n };\n\n class Pager {\n constructor(element, options) {\n if (!(element instanceof HTMLElement)) {\n throw new Error('[Pager] DOM element is required for initialization');\n }\n\n this.wrapper = element;\n\n let dataset = Parser.parse(element.dataset, ['pager']);\n\n this.options = { ...defaults, ...dataset.pager, ...options };\n\n this._maxPages = Math.round(Number(this.options.maxPages));\n this._maxVisiblePages = Math.round(Number(this.options.maxVisiblePages));\n this._pageSize = Math.round(Number(this.options.pageSize));\n this._totalItems = Math.round(Number(this.options.totalItems));\n\n this._current = 1;\n\n this.wrapper.classList.add(this.options.wrapperClass);\n this.wrapper.addEventListener('click', this._delegatedPageClick.bind(this), false);\n }\n\n getActivePage() {\n return this._current;\n }\n\n setPage(page = 1) {\n this._current = Math.floor(Number(page));\n return this;\n }\n\n setMaxPages(n) {\n this._maxPages = Math.round(Number(n));\n return this;\n }\n\n setPageSize(n) {\n this._pageSize = Math.round(Number(n));\n return this;\n }\n\n setTotalItems(n) {\n this._totalItems = Math.round(Number(n));\n return this;\n }\n\n setMaxVisiblePages(n) {\n this._maxVisiblePages = Math.round(Number(n));\n return this;\n }\n\n calcTotalPages() {\n const { _pageSize, _totalItems } = this;\n let pages = Math.floor(_totalItems / _pageSize);\n\n if (_totalItems % _pageSize > 0) {\n pages++;\n }\n\n return Math.min(pages, this._maxPages);\n }\n\n calcAndSetTotalPages() {\n // this._maxPages = this.calcTotalPages();\n this._totalPages = this.calcTotalPages();\n return this;\n }\n\n hasPrev() {\n return this._current !== 1;\n }\n\n hasNext() {\n if (this._totalPages < this._maxPages) {\n return this._current < this._totalPages;\n }\n return this._current < this._maxPages;\n }\n\n getPageInterval(page = 1) {\n const index = this.getPageIntervalIndex(page);\n\n let interval = Array.from(Array(this._maxVisiblePages))\n .fill(index * this._maxVisiblePages)\n .map((n, i) => (n + i + 1));\n\n const lastPageIndex = interval.indexOf(this._totalPages);\n\n if (lastPageIndex >= 0) {\n interval.splice(lastPageIndex + 1);\n }\n\n return interval;\n }\n\n getPageIntervalIndex(page = 1) {\n return Math.floor((page - 1) / this._maxVisiblePages);\n }\n\n\n _html() {\n const { prevText, nextText, pageURI, pageQueryKey, preserveQueryParams, prevWrapperClass, nextWrapperClass, pagesWrapperClass, prevLinkClass, nextLinkClass, pageLinkClass } = this.options;\n\n const url = new URL(window.location.href);\n url.searchParams.delete(pageQueryKey);\n\n const page = preserveQueryParams && url.search ? `${pageURI}${url.search}&${pageQueryKey}` : `${pageURI}?${pageQueryKey}`;\n const interval = this.getPageInterval(this._current);\n\n const prevArrow = ``;\n const nextArrow = ``;\n\n const prev = `

`;\n const next = ``;\n\n const links = interval.map(n => n === this._current ?\n `${n}` :\n `${n}`\n ).join('');\n\n let html = '';\n\n // if (this.hasPrev()) {\n html += prev;\n // };\n\n html += `
${links}
`;\n\n // if (this.hasNext()) {\n html += next;\n // };\n\n return html;\n }\n\n empty() {\n this.wrapper.innerHTML = '';\n }\n\n render() {\n this.calcAndSetTotalPages();\n this.wrapper.innerHTML = this._html();\n this.wrapper.classList.toggle('TK-Pager--Has-Prev', this.hasPrev());\n this.wrapper.classList.toggle('TK-Pager--Has-Next', this.hasNext());\n }\n\n _delegatedPageClick(event) {\n const { target } = event;\n const match = target.nodeName.toLowerCase() === 'a' && Boolean(target.dataset.page);\n\n if (!match) return;\n\n event.preventDefault();\n\n const { callback } = this.options;\n const page = Number(target.dataset.page);\n\n if (typeof callback === 'function') {\n callback(this, page);\n }\n }\n }\n\n const defaults$1 = {\n key: '',\n value: '',\n display: '',\n tagClass: 'TK-Tag', /* css class for the tag */\n tagCloseClass: 'TK-Tag-X', /* css class for the close button in tag */\n };\n\n class Tag {\n constructor(options) {\n const { key, value, display } = options;\n\n if (!key) {\n throw new Error('[Tag] key is required');\n }\n\n if (!value) {\n throw new Error('[Tag] value is required');\n }\n\n if (!display) {\n throw new Error('[Tag] display name is required')\n }\n\n this.options = { ...defaults$1, ...options };\n\n this.key = key.trim(); // Key and value should be stored in its original form, otherwise the query does not work\n this.value = value.trim();\n this.display = display.trim();\n\n this.element = this.render();\n }\n\n render() {\n const { key, value, display } = this.valueOf();\n\n const span = document.createElement('span');\n const close = document.createElement('span');\n\n close.innerHTML = `✖`;\n close.dataset ? close.dataset.role = 'remove' : close.setAttribute('data-role', 'remove');\n close.classList.add(this.options.tagCloseClass);\n\n span.innerText = display;\n span.append(close);\n\n if (span.dataset) {\n span.dataset.key = key;\n span.dataset.value = value;\n span.dataset.display = display;\n }\n else {\n span.setAttribute('data-key', key);\n span.setAttribute('data-value', value);\n span.setAttribute('data-display', display);\n }\n\n span.classList.add(this.options.tagClass);\n\n return span;\n }\n\n valueOf() {\n return {\n key: this.key,\n value: this.value,\n display: this.display\n }\n }\n\n static validate(options) {\n const { key, value, display } = options;\n\n if (!key) {\n throw new Error('[Tag] key is required');\n }\n\n if (!value) {\n throw new Error('[Tag] value is required');\n }\n\n if (!display) {\n throw new Error('[Tag] display name is required')\n }\n }\n\n static create(options = defaults$1) {\n return new Tag(options);\n }\n }\n\n const strip = (html = '') => new DOMParser().parseFromString(html, 'text/html').body.textContent.trim();\n const escape = (html = '') => html ? document.createElement('div').appendChild(document.createTextNode(html)).parentNode.innerHTML : html;\n const unescape = (html = '') => html ? new DOMParser().parseFromString(html, 'text/html').documentElement.textContent : html;\n\n const defaults$2 = {\n /*\n * Array of Tags for initial tags\n * Tag { key: String, value: String, display: String }\n */\n tags: [],\n\n /*\n * Input properties\n * If prefill is true it will prefill the input with the value from the query parameter\n */\n prefill: true,\n maxlength: 200, /* Max length for input */\n autofocus: false, /* Auto focus the input on initialization */\n placeholder: 'Looking for...', /* Placeholder for the input */\n\n /*\n * CSS Classes For Styling\n */\n tagClass: 'TK-Tag', /* css class for the tag */\n tagCloseClass: 'TK-Tag-X', /* css class for the close button in tag */\n tagsWrapperClass: 'TK-Tags-Wrapper', /* css class for div wrapping the tags */\n mainWrapperClass: 'TK-Tag-Input-Wrapper', /* css class for main wrapper, wraps all dom elements associated with this plugin */\n mainWrapperFocusClass: 'is-focus', /* css class to add when input is focused, class is added to the main wrapper */\n flexWrapperClass: 'TK-Tag-Input-Flex-Wrapper', /* css class for wrapper, wraps tags and input field */\n inputClass: 'TK-Tag-Input', /* css class for the input element */\n\n /*\n * Optional button to use\n */\n button: true, /* id selector to find associated button with the tag input */\n buttonText: '',\n buttonIcon: true,\n buttonIconSvg: '', /* add custom html for the button */\n buttonClass: 'TK-Tag-Input-Button', /* css class to add to the button */\n\n /*\n * User provided callback executed on enter press or button click\n * The callback is called with TagInput instance as first agrument\n */\n callback: (instance) => console.log(instance.valueOf())\n };\n\n\n const KEYS = {\n ENTER: 13,\n ESCAPE: 27,\n BACKSPACE: 8\n };\n\n\n class TagInput {\n constructor(input, options) {\n if (!input) {\n throw new Error('[TagInput] Input element is required for initialization');\n }\n\n if (input.nodeName.toLowerCase() !== 'input') {\n throw new Error('[TagInput] TagInput should be initialized with `input` element');\n }\n\n this.input = input;\n this.options = { ...defaults$2, ...options };\n\n this.tags = new Map();\n this._state = new Map();\n\n this.options.tags.map(t => this.tags.set(t.display, Tag.create(t)));\n\n this.wrapper = document.createElement('div');\n this.flexWrapper = document.createElement('div');\n this.tagsWrapper = document.createElement('div');\n\n this.button = document.createElement('button');\n this.button.type = 'button';\n this.button.ariaLabel = 'Click to search';\n\n if (this.options.buttonIcon) {\n this.button.innerHTML = this.options.buttonIconSvg;\n }\n\n if (this.options.buttonText) {\n this.button.innerHTML += `${this.options.buttonText}`;\n }\n\n /** @Handle initial options **/\n /** ========================================== **/\n if (this.options.placeholder) {\n this.input.placeholder = this.options.placeholder;\n }\n\n if (this.options.maxlength && parseInt(this.options.maxlength)) {\n this.input.maxLength = parseInt(this.options.maxlength);\n }\n\n if (this.options.autofocus) {\n this.input.focus();\n this.input.autofocus = true;\n }\n\n if (this.options.prefill) {\n this.prefill();\n }\n\n /** @Init **/\n /** ========================================== **/\n this._render();\n this.saveState();\n\n /** @Attach Events **/\n /** ========================================== **/\n\n this.wrapper.addEventListener('click', () => this.input.focus(), false);\n this.input.addEventListener('focus', () => this.wrapper.classList.add(this.options.mainWrapperFocusClass), false);\n this.input.addEventListener('blur', () => this.wrapper.classList.remove(this.options.mainWrapperFocusClass), false);\n\n this.input.addEventListener('keydown', this._onEnterPress.bind(this), false);\n this.options.button && this.button.addEventListener('click', this._onButtonClick.bind(this), false);\n\n this.tagsWrapper.addEventListener('click', this._delegatedTagRemove.bind(this), false);\n }\n\n _render() {\n const { input, button, wrapper, flexWrapper, tagsWrapper, options } = this;\n\n input.classList.add(options.inputClass);\n options.button && button.classList.add(options.buttonClass);\n wrapper.classList.add(options.mainWrapperClass);\n flexWrapper.classList.add(options.flexWrapperClass);\n tagsWrapper.classList.add(options.tagsWrapperClass);\n\n input.parentElement.insertBefore(wrapper, input);\n wrapper.append(flexWrapper);\n flexWrapper.append(tagsWrapper);\n flexWrapper.append(input);\n options.button && wrapper.append(button);\n\n this.tags.forEach(t => tagsWrapper.append(t.element));\n }\n\n _delegatedTagRemove(event) {\n const { target } = event;\n const match = target.nodeName.toLowerCase() === 'span' && target.classList.contains(this.options.tagCloseClass);\n\n if (!match) return;\n\n const { display } = target.parentElement.dataset;\n\n this.removeTag(display);\n }\n\n prefill() {\n const url = new URL(window.location.href);\n const params = url.searchParams;\n const value = unescape(params.get('q'));\n this.input.value = value;\n }\n\n _onEnterPress(event) {\n if (event.keyCode !== KEYS.ENTER) {\n return;\n }\n event.preventDefault();\n\n if (typeof this.options.callback === 'function') {\n this.options.callback(this);\n }\n }\n\n _onButtonClick(event) {\n event.preventDefault();\n\n if (typeof this.options.callback === 'function') {\n this.options.callback(this);\n }\n }\n\n addTag(options) {\n const tag = Tag.create(options);\n if (!this.tags.has(tag.display)) {\n this.tags.set(tag.display, tag);\n this.tagsWrapper.append(tag.element);\n }\n }\n\n removeTag(display) {\n if (!display) {\n throw new Error('Display name of the tag is required');\n }\n\n if (typeof display !== 'string') {\n throw new Error('Display name should be string');\n }\n\n if (this.tags.has(display)) {\n this.tags.get(display).element.remove();\n this.tags.delete(display);\n return true;\n }\n else {\n return false;\n }\n }\n\n removeAllTags() {\n this.tags.forEach(tag => this.removeTag(tag.display));\n }\n\n valueOf() {\n let tags = [];\n let value = this.input.value.trim();\n\n this.tags.forEach(t => tags.push(t.valueOf()));\n\n return { value, tags };\n\n /*\n * Do not use iterators for now\n */\n\n // return {\n // value: this.input.value.trim(),\n // tags: [...this.tags.values()].map(t => t.valueOf())\n // }\n }\n\n saveState() {\n const url = new URL(location.href);\n this._state.set(url.href, this.valueOf());\n }\n\n restoreState() {\n const url = new URL(location.href);\n\n if (!this._state.has(url.href)) {\n return;\n }\n\n const { tags } = this._state.get(url.href);\n\n this.removeAllTags();\n tags.forEach(tag => this.addTag(tag));\n }\n\n\n static create(input, options = defaults$2) {\n return new TagInput(input, options);\n }\n }\n\n const defaults$3 = {\n v2: { siteKey: '' },\n v3: { siteKey: '', action: 'search' },\n scriptSrc: 'https://www.google.com/recaptcha/api.js'\n };\n\n class Recaptcha {\n constructor(options) {\n const { v2, v3 } = options;\n\n if (v2 && !v2.siteKey) {\n throw new Error('[Recaptcha] \"v2\" site key is required');\n }\n\n if (v3) {\n if (!v3.siteKey) {\n throw new Error('[Recaptcha] \"v3\" site key is required');\n }\n if (!v3.action) {\n v3.action = defaults$3.v3.action;\n }\n }\n \n this.options = { ...defaults$3, ...options };\n\n this._script = null;\n this._scriptReady = false;\n this._scriptLoading = false;\n\n /** @var {Promise} - recaptcha script tag loaded promise */\n this._scriptLoaded = this._loadRecaptchaScript();\n\n /** Total time (in milliseconds) to wait for Recaptcha api to load (v2 only) */\n this._timeout = 3000;\n\n /** Interval time (in milliseconds) to test if Recaptcha api is loaded (v2 only) */\n this._delay = 0;\n this._retryDelay = 200;\n\n /** Init recaptcha v2 */\n this._recaptchaInit = this._recaptchaInit.bind(this);\n this._recaptchaRender = this._recaptchaRender.bind(this);\n this._recaptchaCallback = this._recaptchaCallback.bind(this);\n\n if (this.options.v2) {\n /** @var {Promise} - recaptcha v2 initialized promise */\n this._recaptchaInitialized = this._scriptLoaded.then(this._recaptchaInit);\n }\n }\n\n /*\n * @private\n * Load recaptcha javascript\n * Use only one script tag for \"v2\" and \"v3\"\n * Init automatically recaptcha \"v3\" if siteKey (v3) is provided\n */\n _loadRecaptchaScript() {\n const { scriptSrc } = this.options;\n const { siteKey } = this.options.v3;\n\n return new Promise((resolve, reject) => {\n if (this._scriptReady) {\n return grecaptcha.ready(resolve);\n }\n\n if (this._scriptLoading) {\n return reject(new Error('[Recaptcha] recaptcha script should be loaded only once'));\n }\n\n let script = document.createElement('script');\n\n script.defer = true;\n script.async = true;\n script.src = siteKey ? `${scriptSrc}?render=${siteKey}` : scriptSrc;\n script.id = `js-recaptcha-script-${Math.round(Math.random() * 10000)}-${Date.now()}`;\n\n script.addEventListener('load', (event) => {\n this._scriptReady = true;\n this._scriptLoading = false;\n return grecaptcha.ready(resolve);\n });\n\n script.addEventListener('error', (event) => {\n this._scriptReady = false;\n this._scriptLoading = false;\n return reject(new Error('[Recaptcha] script did not load'));\n });\n\n this._script = script;\n this._scriptLoading = true;\n\n document.body.append(script);\n });\n }\n\n /**\n * @private\n * This will render the widget when grecaptcha (v2) api is loaded\n * @return {Promise} - Promise containing the id of the recaptcha rendered widget\n */\n _recaptchaInit() {\n return new Promise((resolve, reject) => {\n if (!window.grecaptcha) {\n // check if we can try again according to timeout\n if (this._delay < this._timeout) {\n this._delay += this._retryDelay;\n setTimeout(this._recaptchaInit, this._retryDelay);\n }\n else {\n reject(new Error('Recaptcha script timeout'));\n }\n }\n else {\n grecaptcha.ready(() => {\n this._recaptchaRender();\n resolve(this._recaptchaId);\n });\n }\n });\n }\n\n /**\n * @private\n * Render Recaptcha v2 widget\n */\n _recaptchaRender() {\n const { siteKey } = this.options.v2;\n\n const recaptchaId = grecaptcha.render(this._script.id, {\n sitekey: siteKey,\n size: 'invisible',\n callback: this._recaptchaCallback\n });\n\n this._recaptchaId = recaptchaId;\n }\n\n /**\n * @private\n * User provided callback for recaptcha v2\n */\n _recaptchaCallback(token) {\n grecaptcha.reset(this._recaptchaId);\n this._script.dispatchEvent(new CustomEvent('token', { detail: { token } }));\n }\n\n /**\n * @private\n * Get Recaptcha v2 token\n */\n _getV2Token() {\n return this._recaptchaInitialized.then(() => {\n const { siteKey } = this.options.v2;\n\n grecaptcha.execute(this._recaptchaId);\n\n return new Promise((resolve, reject) => {\n const onToken = (event) => {\n const { token } = event.detail;\n this._script.removeEventListener('token', onToken);\n resolve(token);\n };\n\n this._script.addEventListener('token', onToken);\n });\n });\n }\n\n /**\n * @private\n * Get Recaptcha v3 token\n */\n _getV3Token() {\n const { action, siteKey } = this.options.v3;\n return this._scriptLoaded.then(() => grecaptcha.execute(siteKey, { action }));\n }\n\n /**\n * @public\n * @param {String} version - 'v2' || 'v3'\n * @return {Promise} - promise containing the token string\n */\n getToken(version) {\n if (version === 'v2') return this._getV2Token();\n if (version === 'v3') return this._getV3Token();\n return Promise.reject(new Error('Version \"v2\" or \"v3\" is required'));\n }\n }\n\n const SPAM_VALIDATION_ERROR = 'spam_validation';\n\n const defaults$4 = {\n recaptcha: {\n v2: false, // set object { siteKey: '' } to enable\n v3: false // set object { siteKey: '', action: '' } to enable\n },\n endpoint: '/search-v2/search/api',\n headers: {\n 'accept': 'application/json',\n 'content-type': 'application/json'\n },\n credentials: 'same-origin' // include cookies\n };\n\n class SearchApiService {\n constructor(options) {\n this.options = { ...defaults$4, ...options };\n\n const { v2, v3 } = this.options.recaptcha;\n\n if (v2 || v3) {\n this.recaptcha = new Recaptcha(this.options.recaptcha);\n }\n }\n\n /*\n * Do not use async/await for now\n */\n\n /**\n * @private\n */\n _fetch(payload) {\n const { endpoint, headers, credentials } = this.options;\n\n return fetch(endpoint, {\n method: 'POST',\n headers,\n credentials,\n body: JSON.stringify(payload)\n })\n .then(res => {\n if (res.ok) {\n return res.json().then(d => this._validateRecaptchaResponse(d));\n }\n else if (res.status == 400)\n {\n return res.json().then(d => this._validateRecaptchaResponse(d)).then(() => { throw new Error(res.statusText); });\n }\n else {\n throw new Error(res.statusText);\n }\n });\n }\n\n /**\n * @public\n * @param {Object} payload\n */\n fetch(payload) {\n if (this.recaptcha) {\n const { v2, v3 } = this.recaptcha.options;\n\n // if both recaptcha v2 and v3 are enabled\n if (v2 && v3) {\n return this.recaptcha.getToken('v3')\n .then(token => this._fetch({ ...payload, recaptchaToken: token, recaptchaVersion: 'v3' }))\n .catch(e => {\n if (e && e.message === SPAM_VALIDATION_ERROR) {\n return this.recaptcha.getToken('v2')\n .then(token => this._fetch({ ...payload, recaptchaToken: token, recaptchaVersion: 'v2' }));\n }\n\n throw e;\n });\n }\n\n // if only recaptcha v2 is enabled\n if (v2) {\n return this.recaptcha.getToken('v2')\n .then(token => this._fetch({ ...payload, recaptchaToken: token, recaptchaVersion: 'v2' }));\n }\n\n // if only recaptcha v3 is enabled\n if (v3) {\n return this.recaptcha.getToken('v3')\n .then(token => this._fetch({ ...payload, recaptchaToken: token, recaptchaVersion: 'v3' }));\n }\n }\n\n return this._fetch(payload);\n }\n\n _validateRecaptchaResponse(data) {\n if (this._isRecaptchaError(data)) {\n throw new Error(SPAM_VALIDATION_ERROR);\n }\n\n return data;\n }\n\n _isRecaptchaError(data) {\n if (Array.isArray(data)) {\n const recaptchaValidationItem = data.filter(x => x.key === 'captchaValidation')[0];\n if (recaptchaValidationItem && recaptchaValidationItem.errors) {\n return true;\n }\n }\n else if (data && data.ModelState && data.ModelState.captchaValidation) {\n return true;\n }\n\n return false;\n }\n }\n\n class SearchUrlService {\n /*\n * Search results page URL\n * @example -> `` -> Default is empty string -> The input is placed on the same page as the search results. It will append query parameters to the current page url.\n * @example -> `search` -> Relative child page path to the page where the input is placed -> www.example.com/page-with-tag-input/search.\n * @example -> `/search` -> Absolute page for the site -> www.example.com/search -> Will redirect to page with absolute url for the site.\n * @example -> `https://www.telerik.com/search` -> Other site, if the value starts with `http` or `https` it will redirect to another site\n */\n static generate({ uri = '', value = '', page = 0, tags = []}) {\n let url;\n const { origin, pathname } = window.location;\n\n if (!value) {\n throw new Error('Value should be provided');\n }\n\n if (typeof value !== 'string') {\n throw new Error('Value should be string');\n }\n\n if (value.length < 2) {\n throw new Error('Value should be minimum 2 characters')\n }\n\n // On the same page, only append query parameters to the current page\n if (uri === '') {\n url = new URL(`${origin}/${pathname}`);\n }\n\n // On child page relative to the current page\n if (!uri.startsWith('/')) {\n url = new URL(`${origin}/${pathname}/${uri}`);\n }\n\n // On absolute page for the same site\n if (uri.startsWith('/')) {\n url = new URL(`${origin}/${uri}`);\n }\n\n // On another website\n if (uri.startsWith('http')) {\n url = new URL(uri);\n }\n\n url.href = url.href.split('///').join('/').split('//').join('/').replace(':/', '://');\n\n url.searchParams.set('q', escape(value));\n\n tags.forEach(t => url.searchParams.append(t.key, t.value));\n\n // Set paging\n if (page > 0) {\n url.searchParams.set('page', page);\n }\n\n url.searchParams.sort();\n\n return url;\n }\n\n static parse(url = location.href) {\n const _url = new URL(url);\n const params = _url.searchParams;\n\n const result = {\n q: unescape(params.get('q')),\n filter: params.getAll('filter'),\n context: params.getAll('context'),\n page: Number(params.get('page')) || 1\n };\n\n return result;\n }\n\n static validate(url) {\n let result = false;\n\n try {\n new URL(url);\n result = true;\n }\n catch (error) {\n result = false;\n }\n\n return result;\n }\n\n static redirect(url) {\n if (!SearchUrlService.validate(url)) {\n throw new Error('Please provide valid URL');\n }\n\n window.location.href = url;\n }\n\n static push(url) {\n if (!SearchUrlService.validate(url)) {\n throw new Error('Please provide valid URL');\n }\n\n window.history.pushState(null, null, url.search);\n }\n\n static replace(url) {\n if (!SearchUrlService.validate(url)) {\n throw new Error('Please provide valid URL');\n }\n\n window.history.replaceState(null, null, url.search);\n }\n }\n\n const defaults$5 = {\n wrapperClass: 'TK-Search-Results-Spellcheck-Wrapper',\n didYouMeanText: 'Did you mean: {0}',\n };\n\n class SpellCheck {\n constructor(element, options) {\n if (!(element instanceof HTMLElement)) {\n throw new Error('[SpellCheck] DOM element is required for initialization');\n }\n\n this._spellCheckCorrectionWord = '';\n\n this.options = { ...defaults$5, ...options };\n\n this.wrapper = element;\n this.wrapper.classList.add(this.options.wrapperClass);\n\n this.container = document.createElement('div');\n this.render = this.render.bind(this);\n this.hide = this.hide.bind(this);\n }\n\n render(settings) {\n let newUrl = '';\n\n if (settings.redirect) {\n newUrl = SearchUrlService.generate({ value: this._spellCheckCorrectionWord, tags: settings.tags, page: 1, uri: settings.redirectUri });\n }\n else {\n newUrl = SearchUrlService.generate({ value: this._spellCheckCorrectionWord, tags: settings.tags, page: 1 });\n }\n \n const didYouMeanLabel = this.options.didYouMeanText.replace('{0}', `${escape(this._spellCheckCorrectionWord)}`);\n this.container.innerHTML = `${didYouMeanLabel} `;\n this.wrapper.innerHTML = '';\n this.wrapper.removeAttribute('hidden');\n this.wrapper.append(this.container);\n }\n\n hide() {\n this.wrapper.setAttribute('hidden', true); \n }\n\n setSpellCheckCorrectionWord(spellCheckCorrectionWord) {\n this._spellCheckCorrectionWord = spellCheckCorrectionWord;\n return this;\n }\n\n static create(element, options = defaults$5) {\n return new SpellCheck(element, options);\n }\n }\n\n const defaults$6 = {\n url: false,\n text: false,\n hashtag: false,\n popupWidth: 580,\n popupHeight: 400,\n wrapperClass: 'TK-Search-Social-Share',\n navClass: 'TK-Search-Social-Share-Nav',\n buttonClass: 'TK-Search-Social-Share-Button',\n buttonText: 'Share',\n networks: ['twitter', 'facebook', 'linkedin', 'reddit']\n };\n\n class SocialShare {\n static html(options) {\n const opts = { ...defaults$6, ...options };\n const { url, text, wrapperClass, navClass, buttonText, buttonClass, networks } = opts;\n\n const links = networks.map(media => {\n return ``;\n }).join('');\n\n const html = `\n \n `;\n\n return html;\n }\n\n static generateMediaURL({ media, url, text }) {\n let result = '';\n const _url = encodeURIComponent(url);\n const _text = encodeURIComponent(strip(text));\n\n switch (media) {\n case 'facebook':\n result = `http://www.facebook.com/sharer.php?u=${_url}`;\n break;\n case 'linkedin':\n result = `https://www.linkedin.com/shareArticle?mini=true&url=${_url}&summary=${_text}`;\n break;\n case 'reddit':\n result = `https://www.reddit.com/login?redirect=https://www.reddit.com/submit?url=${_url}`;\n break;\n case 'twitter':\n default:\n result = `https://twitter.com/share?url=${_url}&text=${_text}`;\n break;\n }\n\n return result;\n }\n\n static _delegatedToggleShare(event) {\n const { target } = event;\n const match = target.nodeName.toLowerCase() === 'button' && target.className === defaults$6.buttonClass;\n\n if (!match) {\n return;\n }\n\n target.previousSibling.classList.toggle('is-visible');\n\n event.preventDefault();\n event.stopPropagation();\n }\n\n static _delegatedOpenMediaPopup(event) {\n const { target } = event;\n const match = target.nodeName.toLowerCase() === 'a' && Boolean(target.dataset.media);\n\n if (!match) {\n return;\n }\n\n const { popupWidth, popupHeight } = defaults$6;\n\n const w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;\n const h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;\n\n const top = (h - popupHeight) / 2;\n const left = (w - popupWidth) / 2;\n\n const options = `status=1,width=${popupWidth},height=${popupHeight},top=${top},left=${left}`;\n\n event.preventDefault();\n event.stopPropagation();\n\n window.open(target.href, 'Share', options);\n }\n }\n\n const defaults$7 = {\n socialShare: false,\n socialShareButtonText: '',\n wrapperClass: 'TK-Search-Results-List-Wrapper',\n noResultsClass: 'TK-Search-Results-Zero',\n errorResultsClass: 'TK-Search-Results-Error',\n searchQueryClass: 'TK-Search-Results-Query',\n listClass: 'TK-Search-Results-List',\n listItemClass: 'TK-Search-Results-List-Item',\n itemHeadingClass: 'TK-Search-Results-List-Item-H',\n itemDescriptionClass: 'TK-Search-Results-List-Item-P',\n itemLastLinkClass: 'TK-Search-Results-List-Item-A',\n tryMoreGeneralText: 'Try more general keywords.',\n tryDiffKeywordText: 'Try different keywords.',\n goBackText: 'Go back',\n allWordsSpelledCorrectText: 'Make sure all words are spelled correctly.',\n searchDidNotMatchText: `Your search \"{0}\" did not match any documents. Search suggestions:`,\n somethingsNotRightText: \"Oh, no! Something's not right, but we're sorting it out.\"\n };\n\n class SearchResultsList {\n constructor(element, options) {\n if (!(element instanceof HTMLElement)) {\n throw new Error('[SearchResultsList] DOM element is required for initialization');\n }\n\n this.options = { ...defaults$7, ...options };\n\n this.wrapper = element;\n this.wrapper.classList.add(this.options.wrapperClass);\n\n this.list = document.createElement('ul');\n this.list.classList.add(this.options.listClass);\n this.list.addEventListener('click', this._resultClickHandler.bind(this), false);\n\n if (this.options.socialShare) {\n this.socialShare = true;\n this.wrapper.addEventListener('click', SocialShare._delegatedToggleShare);\n this.wrapper.addEventListener('click', SocialShare._delegatedOpenMediaPopup);\n }\n\n this.render = this.render.bind(this);\n this._itemHTML = this._itemHTML.bind(this);\n }\n\n empty() {\n this.list.innerHTML = '';\n this.wrapper.innerHTML = '';\n }\n\n render(options) {\n const { results, query } = options;\n\n if (options instanceof Error) {\n this.wrapper.innerHTML = this._errorResultsHTML();\n return;\n }\n\n if (!results) {\n throw new Error('[SearchResultsList] \"options.results\" is required');\n }\n\n if (!Array.isArray(results)) {\n throw new Error('[SearchResultsList] \"options.results\" should be array');\n }\n\n const html = results.map(this._itemHTML).join('');\n\n this.list.innerHTML = '';\n this.wrapper.innerHTML = '';\n\n if (results.length) {\n this.list.innerHTML = html;\n this.wrapper.append(this.list);\n }\n else {\n this.wrapper.innerHTML = this._noResultsHTML({ query });\n }\n }\n\n _itemHTML(options) {\n const { title, description, url } = options;\n const { listItemClass, itemHeadingClass, itemDescriptionClass, itemLastLinkClass, socialShareButtonText } = this.options;\n const socialShareHTML = this.socialShare && SocialShare.html({ url, text: title, buttonText: socialShareButtonText }) || '';\n\n const html = `\n
  • \n

    ${title}

    \n

    ${description}

    \n
    \n ${url}\n ${socialShareHTML}\n
    \n
  • \n `;\n\n const result = html.trim().split('\\n').map(x => x.trim()).join('');\n\n return result;\n }\n\n _noResultsHTML(options) {\n const { query } = options;\n const { noResultsClass, searchQueryClass, tryMoreGeneralText, allWordsSpelledCorrectText, tryDiffKeywordText, goBackText } = this.options;\n const searchDidNotMatchLabel = this.options.searchDidNotMatchText.replace('{0}', `${escape(query)}`);\n\n const html = `\n
    \n

    ${searchDidNotMatchLabel}

    \n
      \n
    • ${allWordsSpelledCorrectText}
    • \n
    • ${tryDiffKeywordText}
    • \n
    • ${tryMoreGeneralText}
    • \n
    \n
    \n ${goBackText}\n
    \n `;\n\n const result = html.trim().split('\\n').map(x => x.trim()).join('');\n\n return result;\n }\n\n _errorResultsHTML() {\n const { errorResultsClass, somethingsNotRightText, allWordsSpelledCorrectText, tryDiffKeywordText, tryMoreGeneralText, goBackText } = this.options;\n\n const html = `\n
    \n

    ${somethingsNotRightText}

    \n
      \n
    • ${allWordsSpelledCorrectText}
    • \n
    • ${tryDiffKeywordText}
    • \n
    • ${tryMoreGeneralText}
    • \n
    \n
    \n ${goBackText}\n
    \n `;\n\n const result = html.trim().split('\\n').map(x => x.trim()).join('');\n\n return result;\n }\n\n _resultClickHandler(event) {\n const target = event.target;\n if (target.nodeName.toLowerCase() !== 'a') {\n return;\n }\n\n const eventData = {\n text: target.innerText,\n url: target.href\n };\n\n this.wrapper.dispatchEvent(new CustomEvent('search-result-open', { detail: eventData }));\n }\n }\n\n const defaults$8 = {\n wrapperClass: 'TK-Search-Results-Count-Wrapper',\n searchQueryClass: 'TK-Search-Results-Query',\n maxPageSize: 10,\n resultForCurrentSearchText: `{0}-{1} of {2} results for \"{3}\"`\n };\n\n class SearchResultsCount {\n constructor(element, options) {\n if (!(element instanceof HTMLElement)) {\n throw new Error('[SearchResultsCount] DOM element is required for initialization');\n }\n\n this._totalResultsCount = 0;\n this._pageSize = 0;\n this._searchQuery = '';\n\n this.options = { ...defaults$8, ...options };\n\n this.wrapper = element;\n this.wrapper.classList.add(this.options.wrapperClass);\n\n this.container = document.createElement('div');\n this.render = this.render.bind(this);\n }\n\n empty() {\n this.wrapper.innerHTML = '';\n }\n\n render(page) {\n this.container.innerHTML = this._generateInnerHtml(page);\n\n this.wrapper.innerHTML = '';\n\n if (this._totalResultsCount > 0) {\n this.wrapper.append(this.container);\n }\n }\n\n setTotalResultsCount(totalResultsCount) {\n this._totalResultsCount = totalResultsCount;\n return this;\n }\n\n setPageSize(pageSize) {\n if (pageSize > this.options.maxPageSize) {\n this._pageSize = this.options.maxPageSize;\n }\n\n this._pageSize = pageSize;\n return this;\n }\n\n setSearchQuery(searchQuery) {\n const encodedSearchQuery = escape(searchQuery);\n this._searchQuery = encodedSearchQuery;\n return this;\n }\n\n _calcRange(page) {\n let leftPart = -1;\n let rightPart = -1;\n\n if (this._totalResultsCount < this.options.maxPageSize) {\n leftPart = 1;\n rightPart = this._totalResultsCount;\n }\n else {\n leftPart = (page - 1) * this._pageSize + 1;\n rightPart = page * this._pageSize;\n }\n\n return {\n leftPart: leftPart,\n rightPart: rightPart\n };\n }\n\n _generateInnerHtml(page) {\n let html = '';\n\n if (this._totalResultsCount > 0) {\n let range = this._calcRange(page);\n\n const resultsCountLabel = this.options.resultForCurrentSearchText\n .replace(\"{0}\", range.leftPart)\n .replace(\"{1}\", range.rightPart)\n .replace(\"{2}\", this._totalResultsCount)\n .replace(\"{3}\", `${this._searchQuery}`);\n\n html = resultsCountLabel;\n }\n\n return html;\n }\n\n static create(element, options = defaults$8) {\n return new SearchResultsCount(element, options);\n }\n }\n\n window.dataLayer = window.dataLayer || [];\n\n class TrackingEvent {\n constructor(name) {\n this.name = name;\n this.category = null;\n this.action = null;\n this.label = null;\n }\n\n valueOf() {\n const eventObject = {\n 'event': this.name,\n 'eventCategory': this.category,\n 'eventAction': this.action\n };\n\n if (this.label) {\n eventObject.eventLabel = this.label;\n }\n\n return eventObject;\n }\n }\n\n class SearchEventBase extends TrackingEvent {\n constructor(category) {\n super('search');\n this.category = category;\n }\n\n stringifyTags(tags) {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n const tagsString = tags.map(t => `${t.key}: ${t.display}`).join();\n\n return tagsString;\n }\n }\n\n class SearchEvent extends SearchEventBase {\n constructor(tags, query) {\n super('website-search-terms');\n\n this.action = this.stringifyTags(tags);\n this.label = query;\n }\n }\n\n class SearchResultEvent extends SearchEventBase {\n constructor(tags, query, result) {\n super('website-search-result');\n\n this.action = query;\n\n const serializedTags = this.stringifyTags(tags);\n if (serializedTags) {\n this.action = `${serializedTags} - ${this.action}`;\n }\n\n this.label = result;\n }\n }\n\n class EventTracker {\n constructor() {\n this._dataLayer = window.dataLayer;\n }\n\n track(event) {\n if (!event) {\n return;\n }\n\n var eventObject = event.valueOf();\n\n this._dataLayer.push(eventObject);\n }\n }\n\n document.createElement('tk-site-search');\n\n const defaults$9 = {\n endpoint: '/webapi/search/do' // c# backend api\n };\n\n const scrollingTypes = ['auto', 'instant', 'smooth'];\n\n class SiteSearchElement {\n constructor(element, options) {\n if (!(element instanceof HTMLElement)) {\n throw new Error('[SiteSearchElement] DOM element is required for initialization');\n }\n\n this.element = element;\n this.element.dataset.version = '3.9.5';\n this.container = document.querySelector(element.dataset.containerSelector) || this.element; // Where to append additional elements (results, pager, etc...)\n\n let dataset = Parser.parse(element.dataset, ['list', 'pager', 'tag-input', 'recaptcha-v2', 'recaptcha-v3', 'spellcheck']);\n\n if (!Array.isArray(dataset.tagInput.tags)) {\n dataset.tagInput.tags = [];\n }\n\n this.options = { ...defaults$9, ...dataset, ...options };\n\n if (this.options.scrollIntoView) {\n if (scrollingTypes.indexOf(this.options.scrollIntoViewBehavior) === -1) {\n this.options.scrollIntoViewBehavior = scrollingTypes[0];\n }\n }\n\n this.input = document.createElement('input');\n this.input.type = 'search';\n this.element.append(this.input);\n\n this.tagInput = new TagInput(this.input, {\n ...this.options.tagInput,\n callback: (tagInput) => this._onSearch(tagInput)\n });\n\n if (this.options.hasCount) {\n this.resultsCountWrapper = document.createElement('div');\n this.container.append(this.resultsCountWrapper);\n this.resultsCount = new SearchResultsCount(this.resultsCountWrapper, this.options.list);\n }\n\n if (this.options.hasSpellcheck) {\n this.spellCheckWrapper = document.createElement('div');\n this.container.append(this.spellCheckWrapper);\n this.spellcheck = new SpellCheck(this.spellCheckWrapper, this.options.spellcheck);\n }\n\n if (this.options.hasList) {\n this.listWrapper = document.createElement('div');\n this.container.append(this.listWrapper);\n this.list = new SearchResultsList(this.listWrapper, this.options.list);\n this.listWrapper.addEventListener('search-result-open', this._onSearchResultOpen.bind(this), false);\n }\n\n if (this.options.hasPager) {\n this.pagerWrapper = document.createElement('div');\n this.container.append(this.pagerWrapper);\n this.pager = new Pager(this.pagerWrapper, {\n ...this.options.pager,\n callback: (pager, page) => this._onPageChanged(pager, page)\n });\n }\n\n const recaptchaOptions = {\n v2: this.options.hasRecaptchaV2 ? this.options.recaptchaV2 : false,\n v3: this.options.hasRecaptchaV3 ? this.options.recaptchaV3 : false\n };\n\n if (this.options.recaptchaScriptSrc) {\n recaptchaOptions.scriptSrc = this.options.recaptchaScriptSrc;\n }\n \n this.api = new SearchApiService({\n endpoint: this.options.endpoint,\n recaptcha: recaptchaOptions\n });\n\n if (this.list && this.pager) {\n this.tagInput.saveState();\n history.scrollRestoration = 'manual';\n window.addEventListener('popstate', (event) => this._onPopState(event));\n }\n\n this.eventTracker = new EventTracker();\n\n this._onInitialRender();\n }\n\n /*\n * Executed on initial render, on popstate event and on page changed\n */\n _onInitialRender() {\n const payloadFromURL = SearchUrlService.parse(location.href);\n const { q, page } = payloadFromURL;\n\n if (!q) {\n this._empty();\n return;\n }\n\n this.list && this.pager && this.api\n .fetch(payloadFromURL)\n .then(data => this._onOkApiFetch(data, page))\n .catch(error => this._onFailApiFetch(error))\n .finally(() => this._scrollIntoView());\n }\n\n /*\n * @param `widget` is TagInput or Pager instance\n * @param `page` is selected page if widget is instace of Pager\n * Executed on user interaction with TagInput or Pager\n */\n _onSearch(tagInput) {\n const { redirect, redirectUri } = this.options;\n const { value, tags } = this.tagInput.valueOf();\n\n if (value.length < 2) {\n console.warn('[SiteSearchElement] # _onSearch => TagInput value should be at least 2 characters => Early Exit');\n return;\n }\n\n this.eventTracker.track(new SearchEvent(tags, value));\n\n if (redirect) {\n SearchUrlService.redirect(SearchUrlService.generate({ value, tags, uri: redirectUri }));\n return;\n }\n\n const payload = {\n // page: 1,\n q: value,\n filter: tags.filter(t => t.key === 'filter').map(t => t.value),\n context: tags.filter(t => t.key === 'context').map(t => t.value)\n };\n\n SearchUrlService.push(SearchUrlService.generate({ value, tags }));\n\n this.tagInput.saveState();\n\n this.list && this.pager && this.api\n .fetch(payload)\n .then(data => this._onOkApiFetch(data, 1))\n .catch(error => this._onFailApiFetch(error))\n .finally(() => this._scrollIntoView());\n }\n\n _onPageChanged(pager, page = 1) {\n let url = new URL(location.href);\n url.searchParams.set('page', page);\n SearchUrlService.push(url);\n this.tagInput.prefill();\n this.tagInput.saveState();\n this._onInitialRender();\n }\n\n _onOkApiFetch(data, page) {\n const query = data.SearchQuery;\n const pageSize = data.PageSize;\n const totalResults = data.TotalResults;\n const spellCheckPhrase = data.SpellCheckPhrase;\n\n if (Array.isArray(data)) {\n if (data[0].errors) {\n throw new Error(data[0].errors[0]);\n }\n }\n\n const results = data.SearchResultData.map(x => ({\n url: x.Url,\n title: x.Title,\n description: x.Summary,\n query: data.SearchQuery\n }));\n\n if (this.resultsCount) {\n this.resultsCount\n .setPageSize(pageSize)\n .setTotalResultsCount(totalResults)\n .setSearchQuery(query)\n .render(page);\n }\n\n if (this.spellcheck) {\n if (spellCheckPhrase && page === 1) {\n const { tags } = this.tagInput.valueOf();\n const { redirect, redirectUri } = this.options;\n\n this.spellcheck\n .setSpellCheckCorrectionWord(spellCheckPhrase)\n .render({ redirect, redirectUri, tags });\n }\n else {\n this.spellcheck.hide();\n }\n }\n\n if (this.list) {\n this.list.render({ results, query });\n }\n\n if (this.pager) {\n if (totalResults <= pageSize) {\n this.pager.empty();\n }\n else {\n this.pager\n .setPageSize(pageSize)\n .setTotalItems(totalResults)\n .setPage(page)\n .render();\n }\n }\n\n this.element.dispatchEvent(new CustomEvent('search', { detail: { query, pageSize, totalResults, spellCheckPhrase, results } }));\n }\n\n _onFailApiFetch(error) {\n if (this.list) {\n this.list.render(error);\n }\n\n this.element.dispatchEvent(new CustomEvent('error', { detail: { error } }));\n }\n\n _onPopState(event) {\n this.tagInput.prefill();\n this.tagInput.restoreState();\n this._onInitialRender();\n }\n\n _scrollIntoView() {\n const { scrollIntoView, scrollIntoViewBehavior } = this.options;\n\n if (scrollIntoView) {\n this.element.parentElement.scrollIntoView({ behavior: scrollIntoViewBehavior, block: 'start' });\n }\n }\n\n _empty() {\n this.resultsCount && this.resultsCount.empty();\n this.spellcheck && this.spellcheck.hide();\n this.list && this.list.empty();\n this.pager && this.pager.empty();\n }\n\n _onSearchResultOpen(event) {\n const { value, tags } = this.tagInput.valueOf();\n this.eventTracker.track(new SearchResultEvent(tags, value, event.detail.url));\n }\n }\n\n /*\n * Attached to window with rollup iife\n *\n * window.biz.Pager\n * window.biz.TagInput\n * window.biz.Recaptcha\n * window.biz.SiteSearchElement\n * window.biz.SearchApiService\n * window.biz.SearchUrlService\n */\n\n exports.Pager = Pager;\n exports.TagInput = TagInput;\n exports.Recaptcha = Recaptcha;\n exports.SearchApiService = SearchApiService;\n exports.SearchUrlService = SearchUrlService;\n exports.SiteSearchElement = SiteSearchElement;\n\n}((this.biz = this.biz || {})));\n//# sourceMappingURL=index.es6.js.map\n","(function (biz) {\n 'use strict';\n\n (() => {\n /*\n * Init Custom Elements\n */\n const elements = Array.from(document.querySelectorAll('tk-site-search'));\n const searches = elements.map(element => new biz.SiteSearchElement(element));\n })();\n\n}(biz));\n//# sourceMappingURL=init.es6.js.map\n","// YouTube Iframe API Player States enum\r\n// Reference:\r\n// https://developers.google.com/youtube/iframe_api_reference#:~:text=of%20the%20player.-,Possible%20values%20are%3A,-%2D1%20%E2%80%93%20unstarted\r\nconst playerStates = {\r\n ENDED: 0,\r\n PAUSED: 2,\r\n};\r\n\r\nconst defaultOptions = {\r\n activeClass: 'is-active',\r\n wrapperClass: 'pip-wrapper',\r\n placeholderClass: 'pip-placeholder',\r\n};\r\n\r\nclass YTPictureInPicture {\r\n constructor(videoElement, player, options) {\r\n this.options = {\r\n ...defaultOptions,\r\n ...options,\r\n };\r\n\r\n this.videoElement = videoElement;\r\n this.player = player;\r\n this.isActive = false;\r\n\r\n this.createWrapper();\r\n this.createControls();\r\n\r\n let intersectionOptions = {\r\n threshold: 0.1,\r\n };\r\n\r\n const callback = (entries) => {\r\n entries.forEach((entry) => {\r\n if (entry.isIntersecting) {\r\n this.deactivate(false);\r\n return;\r\n }\r\n\r\n this.activate();\r\n });\r\n };\r\n\r\n const observer = new IntersectionObserver(callback, intersectionOptions);\r\n\r\n observer.observe(this.placeholder);\r\n\r\n window.dispatchEvent(new Event('YTPictureInPicture_initialized'));\r\n }\r\n\r\n createControls() {\r\n this.closeBtn = document.createElement('button');\r\n this.closeBtn.classList.add('pip-close-btn');\r\n this.closeBtn.setAttribute('type', 'button');\r\n this.closeBtn.addEventListener('click', this.deactivate.bind(this, true));\r\n this.wrapper.append(this.closeBtn);\r\n }\r\n\r\n createWrapper() {\r\n this.wrapper = document.createElement('div');\r\n this.wrapper.classList.add(this.options.wrapperClass);\r\n this.placeholder = document.createElement('div');\r\n this.placeholder.classList.add(this.options.placeholderClass);\r\n const paddingBottom =\r\n this.videoElement.offsetHeight / this.videoElement.offsetWidth;\r\n this.placeholder.style.paddingBottom = `${paddingBottom * 100}%`;\r\n this.placeholder.append(this.wrapper);\r\n this.videoElement.parentNode.insertBefore(\r\n this.placeholder,\r\n this.videoElement\r\n );\r\n this.wrapper.appendChild(this.videoElement);\r\n }\r\n\r\n activate() {\r\n if (this.player.getPlayerState() === playerStates.ENDED \r\n || this.player.getPlayerState() === playerStates.PAUSED) return;\r\n this.wrapper.classList.add(this.options.activeClass);\r\n this.isActive = true;\r\n }\r\n\r\n deactivate(shouldPause = false) {\r\n this.wrapper.classList.remove(this.options.activeClass);\r\n this.isActive = false;\r\n if (shouldPause && this.player && this.player.pauseVideo) {\r\n this.player.pauseVideo();\r\n }\r\n }\r\n}\r\n\r\n// expose in window\r\nwindow.YTPictureInPicture = YTPictureInPicture;\r\n\r\nexport default YTPictureInPicture;\r\n","import YTPictureInPicture from './youtube-picture-in-picture.mjs';\r\nimport { DataAttrParser } from '@progress-wad/data-attr-parser/index.mjs';\r\n\r\nconst defaultOptions = {\r\n btnClass: 'yl-btn',\r\n activeClass: 'is-active',\r\n};\r\n\r\nclass YouTubeLiteEmbed {\r\n constructor(element, options) {\r\n this.element = element;\r\n this.options = {\r\n ...defaultOptions,\r\n ...options,\r\n ...DataAttrParser.parse(element.dataset, [ 'youtube' ]).youtube\r\n };\r\n\r\n this.videoId = this.options.id;\r\n\r\n // Generate unique id for the video instance, necessary for the iframe API init\r\n this.id = `video_${crypto.randomUUID().slice(0, 4)}`;\r\n\r\n this.iframe = document.createElement('div');\r\n this.iframe.id = this.id;\r\n this.element.append(this.iframe);\r\n\r\n this.params = {\r\n ...Object.fromEntries(\r\n new URLSearchParams(this.options.params || [])\r\n ),\r\n autoplay: 1,\r\n createdByApi: 1\r\n };\r\n\r\n this.addThumbnail();\r\n this.addPlayButton();\r\n this.attachEventListeners();\r\n }\r\n\r\n static addPreconnect(url) {\r\n const linkEl = document.createElement('link');\r\n linkEl.rel = 'preconnect';\r\n linkEl.href = url;\r\n document.head.append(linkEl);\r\n }\r\n\r\n static preconnectToDomains() {\r\n if (YouTubeLiteEmbed.arePreconnectsAdded) return;\r\n YouTubeLiteEmbed.addPreconnect('https://www.youtube.com');\r\n YouTubeLiteEmbed.addPreconnect('https://www.google.com');\r\n YouTubeLiteEmbed.arePreconnectsAdded = true;\r\n }\r\n\r\n static loadIframeAPI() {\r\n if (YouTubeLiteEmbed.iframeAPILoaded) return;\r\n\r\n let existingHandler = () => {};\r\n\r\n if (window.onYouTubeIframeAPIReady && typeof window.onYouTubeIframeAPIReady === 'function') {\r\n existingHandler = window.onYouTubeIframeAPIReady;\r\n }\r\n\r\n window.onYouTubeIframeAPIReady = () => {\r\n YouTubeLiteEmbed.iframeAPILoaded = true;\r\n window.dispatchEvent(new Event('YouTubeIframeAPIReady'));\r\n existingHandler();\r\n };\r\n\r\n const tag = document.createElement('script');\r\n tag.src = 'https://www.youtube.com/iframe_api';\r\n document.head.append(tag);\r\n }\r\n\r\n attachEventListeners() {\r\n this.element.addEventListener(\r\n 'pointerover',\r\n YouTubeLiteEmbed.preconnectToDomains,\r\n { once: true }\r\n );\r\n\r\n this.element.addEventListener('click', this.clickEventHandler.bind(this), {\r\n once: true,\r\n });\r\n }\r\n\r\n addThumbnail() {\r\n if (!this.options.poster || this.options.poster.toLowerCase() === 'hd') {\r\n const quality = this.options.poster === 'hd' ? 'maxresdefault' : 'hqdefault';\r\n this.element.style.backgroundImage = `url(https://i.ytimg.com/vi/${this.videoId}/${quality}.jpg)`;\r\n return;\r\n }\r\n\r\n this.element.style.backgroundImage = `url(${this.options.poster})`;\r\n }\r\n\r\n addPlayButton() {\r\n let btnElement = document.createElement('button');\r\n btnElement.type = 'button';\r\n btnElement.classList.add(this.options.btnClass);\r\n btnElement.setAttribute(\r\n 'aria-label',\r\n `Play video${this.options.label ? `: ${this.options.label}` : ''}`\r\n );\r\n this.element.append(btnElement);\r\n }\r\n\r\n onPlayerReady() {\r\n this.play();\r\n this.player.getIframe().player = this.player;\r\n }\r\n\r\n createPlayer() {\r\n this.player = new YT.Player(this.id, {\r\n videoId: this.videoId,\r\n playerVars: this.params,\r\n events: {\r\n onReady: this.onPlayerReady.bind(this)\r\n }\r\n });\r\n\r\n if (this.options.behaviour && this.options.behaviour.toLowerCase() === 'pip') {\r\n window.YTPictureInPicture.activeInstance = new YTPictureInPicture(this.element, this.player);\r\n }\r\n }\r\n\r\n play() {\r\n if (this.player.playVideo) this.player.playVideo();\r\n this.element.classList.add(this.options.activeClass);\r\n this.iframe.focus();\r\n }\r\n\r\n pause() {\r\n this.player.pauseVideo();\r\n this.element.classList.remove(this.options.activeClass);\r\n }\r\n\r\n clickEventHandler() {\r\n if (!YouTubeLiteEmbed.iframeAPILoaded) {\r\n window.addEventListener('YouTubeIframeAPIReady', this.createPlayer.bind(this));\r\n YouTubeLiteEmbed.loadIframeAPI();\r\n return;\r\n }\r\n\r\n this.createPlayer();\r\n }\r\n}\r\n\r\nexport default YouTubeLiteEmbed;\r\n","import YouTubeLiteEmbed from './youtube-lite-embed.mjs';\r\n\r\nwindow.addEventListener('DOMContentLoaded', () => {\r\n document.querySelectorAll('[data-youtube-id]').forEach((element) => {\r\n new YouTubeLiteEmbed(element);\r\n });\r\n});\r\n\r\n","/**\n * @class MultiSelectItem - holds label, checkbox and mirror view\n */\nexport class MultiSelectItem {\n /**\n * @constructor\n * @parma {HTMLLabelElement} label -