{"version":3,"sources":["scripts.js","components/clean-accordion.js"],"names":["document","addEventListener","event","window","NodeList","prototype","forEach","CleanAccordion","cleanAccordionGroups","arguments","length","undefined","options","_classCallCheck","this","_objectSpread","_name","_version","singleOpen","beforeOpen","afterOpen","beforeClose","afterClose","debounce","handleResize","bind","debounceTime","openClose","init","defaultOptions","accordion","_this","handleAccordionClick","target","parentNode","bindEvents","cleanAccordion","cleanAccordionGroup","querySelectorAll","content","_this2","calculateContentHeight","debounceFallback","classList","timestamp","Date","now","existingTimeStamp","contents","dataset","timeSinceLastOpenClose","contains","style","maxHeight","concat","scrollHeight","tempContent","closest","element","computedStyle","getComputedStyle","height","replace","parseInt","marginBottom","closeAll","querySelector","add","remove","resetContentHeight","accordionGroup","dataOptions","_this3","close","hasAttribute","JSON","parse","func","wait","immediate","timeout","args","getDataOptions","callNow","clearTimeout","setTimeout","context","apply"],"mappings":"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","file":"clean-accordion.min.js","sourcesContent":["document.addEventListener('DOMContentLoaded', function(event) {\r\n\r\n // Polyfills\r\n if (window.NodeList && !NodeList.prototype.forEach) {\r\n NodeList.prototype.forEach = Array.prototype.forEach;\r\n }\r\n\r\n});\r\n","// TODO: Add aria support\r\n\r\n/**\r\n * Entry point to the plugin.\r\n */\r\nclass CleanAccordion {\r\n constructor(cleanAccordionGroups = [], options = {}) {\r\n this.cleanAccordionGroups = cleanAccordionGroups;\r\n if (this.cleanAccordionGroups.length <= 0) return;\r\n\r\n const defaultOptions = {\r\n _name: \"CleanAccordion\",\r\n _version: \"1.0.0\",\r\n singleOpen: true, // Should only one accordion be open at a time?\r\n beforeOpen: (accordion) => {},\r\n afterOpen: (accordion) => {},\r\n beforeClose: (accordion) => {},\r\n afterClose: (accordion) => {}\r\n }\r\n\r\n this.options = { ...defaultOptions, ...options }\r\n this.debounceTime = 100;\r\n this.handleResize = this.debounce(this.handleResize.bind(this), this.debounceTime);\r\n this.openClose = this.debounce(this.openClose.bind(this), this.debounceTime);\r\n this.init();\r\n }\r\n\r\n init() {\r\n this.bindEvents();\r\n }\r\n\r\n bindEvents() {\r\n\r\n // cleanAccordionGroup click events\r\n this.cleanAccordionGroups.forEach( (cleanAccordion) => {\r\n cleanAccordion.addEventListener('click', (event) => { this.handleAccordionClick(event) });\r\n });\r\n\r\n // Resizing\r\n window.addEventListener('resize', () => { this.handleResize() })\r\n }\r\n\r\n // TODO: Event is being called more than once when a nested accordion\r\n handleAccordionClick(event) {\r\n let target = event.target;\r\n let accordion = target.parentNode\r\n\r\n // 1. Check if the title was clicked\r\n if (target.hasAttribute('data-control')) {\r\n\r\n // 2. Open/Close the accordion\r\n this.openClose(accordion);\r\n\r\n }\r\n }\r\n\r\n handleResize() {\r\n this.cleanAccordionGroups.forEach( (cleanAccordionGroup) => {\r\n const contents = cleanAccordionGroup.querySelectorAll('[data-content]');\r\n contents.forEach( content => {\r\n this.calculateContentHeight(content);\r\n });\r\n });\r\n }\r\n\r\n openClose(accordion) {\r\n let timeSinceLastOpenClose = this.debounceFallback(accordion);\r\n\r\n if (timeSinceLastOpenClose < this.debounceTime) return;\r\n\r\n if (accordion.classList.contains('open')) {\r\n this.close(accordion);\r\n } else {\r\n this.open(accordion);\r\n }\r\n }\r\n\r\n /**\r\n * \r\n */\r\n debounceFallback(accordion) {\r\n let timestamp = Date.now();\r\n let existingTimeStamp = false;\r\n\r\n if (accordion.hasAttribute('data-timestamp')) {\r\n existingTimeStamp = accordion.dataset.timestamp;\r\n accordion.dataset.timestamp = timestamp;\r\n } else {\r\n accordion.dataset.timestamp = timestamp;\r\n }\r\n\r\n return timestamp - existingTimeStamp;\r\n }\r\n\r\n calculateContentHeight(content) {\r\n if (!content.parentNode.classList.contains('open')) return;\r\n\r\n // 1. Set content height\r\n content.style.maxHeight = `${content.scrollHeight}px`;\r\n\r\n // 2. If this is a nested accordion group recalculate all parent [data-content]\r\n let tempContent = content.parentNode.closest('[data-content]');\r\n while (tempContent !== null) {\r\n tempContent.style.maxHeight = `${tempContent.scrollHeight + content.scrollHeight}px`;\r\n tempContent = tempContent.parentNode.closest('[data-content]');\r\n }\r\n\r\n }\r\n \r\n /**\r\n * Returns the computed height including margin of the passed element\r\n * @param {object} element \r\n */\r\n getComputedHeight(element) {\r\n let height = element.scrollHeight\r\n let computedStyle = window.getComputedStyle(element);\r\n let marginTop = parseInt(computedStyle.marginTop.replace('px', ''));\r\n let marginBottom = parseInt(computedStyle.marginBottom.replace('px', ''));\r\n return height + marginTop + marginBottom;\r\n }\r\n\r\n resetContentHeight(content) {\r\n content.style.maxHeight = '';\r\n }\r\n\r\n /**\r\n * Open an accordion\r\n * @param {*} accordion The accordion to open \r\n */\r\n open(accordion) {\r\n // 1. beforeOpen callback\r\n this.options.beforeOpen(accordion);\r\n\r\n // 2. Check option conditionals.\r\n this.closeAll(accordion);\r\n\r\n // 3. Open accordion\r\n let content = accordion.querySelector('[data-content]');\r\n accordion.classList.add('open');\r\n this.calculateContentHeight(content);\r\n \r\n // 4. afterOpen callback\r\n this.options.afterOpen(accordion);\r\n }\r\n\r\n /**\r\n * Close an accordion\r\n * @param {*} accordion The accordion to close\r\n */\r\n close(accordion) {\r\n\r\n // 1. beforeClose callback\r\n this.options.beforeClose(accordion);\r\n\r\n // 2. Close accordion\r\n let content = accordion.querySelector('[data-content]');\r\n accordion.classList.remove('open');\r\n this.resetContentHeight(content);\r\n\r\n // 3. afterClose callback\r\n this.options.afterClose(accordion);\r\n }\r\n\r\n /**\r\n * Closes each accordion.\r\n * @param {*} accordion The current accordion.\r\n */\r\n closeAll(accordion) {\r\n\r\n // 1. Check if data options were passed.\r\n let accordionGroup = accordion.parentNode;\r\n let dataOptions = this.getDataOptions(accordionGroup);\r\n\r\n if ('singleOpen' in dataOptions) {\r\n if (dataOptions.singleOpen === false) return;\r\n } else {\r\n if (!this.options.singleOpen) return;\r\n }\r\n\r\n // 2. Proceed closing accordions\r\n let accordions = accordionGroup.querySelectorAll('[data-accordion]');\r\n\r\n accordions.forEach( (accordion) => {\r\n this.close(accordion);\r\n });\r\n }\r\n\r\n getDataOptions(accordionGroup) {\r\n let dataOptions = {};\r\n if (accordionGroup.hasAttribute('data-options')) {\r\n dataOptions = JSON.parse(accordionGroup.dataset.options);\r\n }\r\n return dataOptions;\r\n }\r\n\r\n // Returns a function, that, as long as it continues to be invoked, will not\r\n // be triggered. The function will be called after it stops being called for\r\n // N milliseconds. If `immediate` is passed, trigger the function on the\r\n // leading edge, instead of the trailing.\r\n debounce(func, wait, immediate) {\r\n var timeout;\r\n return function() {\r\n var context = this, args = arguments;\r\n var later = function() {\r\n timeout = null;\r\n if (!immediate) func.apply(context, args);\r\n };\r\n var callNow = immediate && !timeout;\r\n clearTimeout(timeout);\r\n timeout = setTimeout(later, wait);\r\n if (callNow) func.apply(context, args);\r\n };\r\n };\r\n}"]}