UNPKG

baseframe-js

Version:

Baseframe JS is a comprehensive suite of modular plugins and utilities designed for front-end development

1,329 lines (1,302 loc) 126 kB
function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var baseElemJs$1 = {exports: {}}; var baseElemJs = baseElemJs$1.exports; var hasRequiredBaseElemJs; function requireBaseElemJs () { if (hasRequiredBaseElemJs) return baseElemJs$1.exports; hasRequiredBaseElemJs = 1; (function (module, exports) { (function (global, factory) { module.exports = factory() ; })(baseElemJs, (function () { // Dom shortcuts const w = window, d = document, body = d.body, root = d.documentElement, toType = (object) => ({}).toString.call(object).match(/\s([a-zA-Z]+)/)[1].toLowerCase(), oa = Object.assign, af = Array.from, isArr = Array.isArray, isStr = (str) => typeof str === 'string', isObj = (obj) => toType(obj) === 'object'; const eventFnCache = new WeakMap(); const cssSplitRgx = /([\.\#][\w-_\s]+)/g; const // region Props CSS_ACTION_STATES = Object.freeze({ active: 'active', starting: 'starting', ending: 'ending' }), cssActionStates = (nameSpace, baseObj = CSS_ACTION_STATES) => { const retObj = {}; const prefix = nameSpace ? nameSpace + '-' : ''; for (const key in baseObj) { if (Object.hasOwn(baseObj, key)) { oa(retObj, { [key]: prefix + baseObj[key] }); } } return retObj; }, // // region Finding Elems // findBy$1 = (type, find, base = d) => { return type === 'class' ? af(base.getElementsByClassName(find)) : type === 'id' ? d.getElementById(find) : type === 'tag' ? af(base.getElementsByTagName(find)) : null; }, find$1 = (el, base = d) => { return af(base.querySelectorAll(el)); }, findOne$1 = (el, base = d) => { return base.querySelector(el); }, map$1 = (elems, fn, unique = false) => { let i = 0, elem, prevRes; const retElems = []; while (elem = elems[i]) { const res = fn(elem, i++); if (!res) continue; // only truthy value if (unique) { if (res !== prevRes || !elems.find(el => el === res)) retElems.push(res); prevRes = res; } else retElems.push(res); } return retElems; }, parents$1 = (elem, selector, untilElem = root) => { const untilIsStr = isStr(untilElem); const retElems = []; while (elem = elem.parentElement) { if (elem.matches(selector)) retElems.unshift(elem); if (untilIsStr ? elem.matches(untilElem) : elem === untilElem) break; } return retElems; }, siblings$1 = (elem, selector, includeKeyEl = false) => { const siblings = af(elem.parentElement.childNodes).filter(el => el instanceof HTMLElement && (selector ? el.matches(selector) : true) && (includeKeyEl ? true : el !== elem)); return siblings; }, next$1 = (elem, selector) => { let nextElem = elem.nextElementSibling; if (selector) while (nextElem) { if (nextElem.matches(selector)) break; nextElem = nextElem.nextElementSibling; } // always return an element return (nextElem ?? elem); }, prev$1 = (elem, selector) => { let prevElem = elem.previousElementSibling; if (selector) while (prevElem) { if (prevElem.matches(selector)) break; prevElem = prevElem.previousElementSibling; } // always return an element return (prevElem ?? elem); }, // // region CSS and Attrs // attr$1 = (elem, attrs) => { if (isStr(attrs)) { if (attrs === 'value') return elem.value; return elem.getAttribute(attrs); } else { for (const key in attrs) { const val = attrs[key]; val === null ? elem.removeAttribute(key) : elem.setAttribute(key, val); } } }, css$1 = (elem, attrs) => { if (isStr(attrs)) { const styles = getComputedStyle(elem); return styles.getPropertyValue(attrs); } else { for (const key in attrs) { const val = attrs[key]; if (key.startsWith('--')) { elem.style.setProperty(key, val); } else { elem.style[key] = val; } } } return ''; }, _cl = (elem) => elem.classList, addClass$1 = (elem, cssNames) => { const cl = _cl(elem); isArr(cssNames) ? cl.add(...cssNames) : cl.add(cssNames); }, rmClass$1 = (elem, cssNames) => { const cl = _cl(elem); isArr(cssNames) ? cl.remove(...cssNames) : cl.remove(cssNames); }, tgClass$1 = (elem, cssNames, toggle) => { const cl = _cl(elem); if (isArr(cssNames)) { for (const cssName of cssNames) { cl.toggle(cssName, toggle); } } else { cl.toggle(cssNames, toggle); } }, hasClass$1 = (elem, cssNames) => { const cl = _cl(elem); return isArr(cssNames) ? cssNames.every(cls => cl.contains(cls)) : cl.contains(cssNames); }, elemRects$1 = (elem) => { return elem.getBoundingClientRect(); }, offset$1 = (elem) => { const rects = elemRects$1(elem); return { top: rects.top + w.pageYOffset, left: rects.left + w.pageXOffset }; }, // // region Element Creation / Removal // make = (selector, propsOrHtml = {}, html) => { const [tag, ...attrs] = selector.split(cssSplitRgx), baseAttrs = getTagAttrs(attrs), propsIsHtml = isArr(propsOrHtml) || propsOrHtml instanceof HTMLElement, propsIsContent = isStr(propsOrHtml) || propsIsHtml, makeProps = propsIsContent ? {} : propsOrHtml, props = oa(baseAttrs, makeProps), { style } = props, elem = oa(d.createElement(tag), props), htmlToUse = propsIsContent && !html ? propsOrHtml : (html ? html : ''); if (style && isObj(style)) css$1(elem, style); if (htmlToUse) elem.append(...(isStr(htmlToUse) ? htmlParse$1(htmlToUse) : isArr(htmlToUse) ? htmlToUse : [htmlToUse])); return elem; }, htmlParse$1 = (htmlStr) => { const root = (new DOMParser()).parseFromString(htmlStr, 'text/html').body; // remove scripts, even though they're non-exectable with the DOMParser const scripts = find$1('script', root); if (scripts) scripts.forEach(s => s.remove()); const elList = af(root.childNodes); return elList; }, empty$1 = (elem) => { while (elem.firstChild) { elem.removeChild(elem.firstChild); } }, merge = (configOrTarget, ...objects) => { const options = isArr(configOrTarget) ? configOrTarget : toType(configOrTarget) === 'string' || toType(configOrTarget) === 'boolean' ? [configOrTarget] : [], // end options; hasOptions = options.length > 0, getOpt = (configOpt) => hasOptions ? options.some(opt => opt === configOpt) : false, deep = getOpt('deep') || getOpt(true), noNull = getOpt('noNull'), noFalsy = getOpt('noFalsy'), target = hasOptions ? objects.shift() : configOrTarget; let mergeObj, i = 0; while (mergeObj = objects[i++]) { for (const key in mergeObj) { const value = mergeObj[key]; if (deep && isObj(value)) { const targetObj = target[key] ? target[key] : target[key] = {}; merge(options, targetObj, value); } else { target[key] = value; if ((noNull && value === null) || (noFalsy && !value)) { delete target[key]; } } } } return target; }, // // region Event // on$1 = (baseEl = body, evtName, fn, delegateEl = null, config = false) => { const evt = evtName.split('.')[0]; const evtFn = (e) => { const data = e['___cdata'] || []; if (delegateEl && baseEl instanceof HTMLElement) { const delegateElems = isStr(delegateEl) ? find$1(delegateEl, baseEl) : delegateEl; // if isTrusted then its a native click const elTarget = e.isTrusted ? e.target : (e['___synthTarget'] ?? e.target); // const delegateElem = getDelegatedElem(baseEl, delegateElems, elTarget); const delegateElem = delegateElems.find(elem => elem === elTarget || elem.contains(elTarget)); if (delegateElem) fn(e, delegateElem, ...data); } else { fn(e, baseEl, ...data); } }; if (eventFnCache.has(baseEl)) { eventFnCache.get(baseEl).set(evtName, evtFn); } else { eventFnCache.set(baseEl, new Map([[evtName, evtFn]])); } baseEl.addEventListener(evt, evtFn, config); }, off$1 = (target = body, evtName, config = false) => { if (eventFnCache.has(target)) { const tgtFnMap = eventFnCache.get(target), evt = evtName.split('.')[0], fn = tgtFnMap.get(evtName); target.removeEventListener(evt, fn, config); tgtFnMap.delete(evtName); } }, trigger$1 = (target = body, evtName, delegateEl = null, data = [], config = false) => { evtName = evtName.trim(); const delegateEls = delegateEl ? find$1(delegateEl, target instanceof Window ? body : target) : null; if (delegateEls && delegateEls.length === 0) return; const elTarget = delegateEls ? delegateEls[0] : target, [evt, nameSpace = ''] = evtName.split('.'), evtIsSynthetic = /^\[.+\]$/.test(evtName), elEvt = new Event(evtIsSynthetic ? evtName : evt); if (data.length) oa(elEvt, { ___cdata: data }); if (nameSpace || evtIsSynthetic) { const cache = eventFnCache.get(target); if (!cache) return; const fn = cache.get(evtName); oa(elEvt, { ___synthTarget: elTarget }); if (fn) { if (evtIsSynthetic) { target.addEventListener(evtName, fn, config); target.dispatchEvent(elEvt); target.removeEventListener(evtName, fn, config); } else fn(elEvt, elTarget); } } else target.dispatchEvent(elEvt); }, // // region Transition // useTransition = () => { let inTransition = false; let tto = null; let currEndTransitionFn = () => { }; return (startFn, endFn, duration = 300) => { if (inTransition) { currEndTransitionFn(); clearTimeout(tto); } startFn(); currEndTransitionFn = endFn; inTransition = true; tto = setTimeout(() => { currEndTransitionFn(); inTransition = false; }, duration); }; }, useCssAnimate = (elems, baseCss = '') => { const transition = useTransition(); const cssState = cssActionStates(baseCss); const cssAnimate = (start, duration = 400, endFn = () => { }) => { const cssTarget = start ? cssState.starting : cssState.ending; const startAnimate = (elem) => { elem.offsetHeight; //reflow addClass$1(elem, cssTarget); rmClass$1(elem, cssState.active); }; const endAnimate = (elem) => { rmClass$1(elem, cssTarget); tgClass$1(elem, cssState.active, start); }; transition(() => { isArr(elems) ? elems.forEach(startAnimate) : startAnimate(elems); }, () => { isArr(elems) ? elems.forEach(endAnimate) : endAnimate(elems); endFn(); }, duration); }; return [ cssAnimate, cssState ]; }, animateByFrame = (fn, fps = 60) => { fps = Math.min(Math.max(1, fps), 60); // clamp fps between 1 and 60 let requestAnimate = null; let msPrev = performance.now(); const msPerFrame = 1000 / fps; const cancelAnimation = () => cancelAnimationFrame(requestAnimate); const refresh = (msNow) => { requestAnimate = window.requestAnimationFrame(refresh); const msPassed = msNow - msPrev; if (msPassed < msPerFrame) return; const excessTime = msPassed % msPerFrame; msPrev = msNow - excessTime; fn(cancelAnimation); }; // Initial call fn(cancelAnimation); refresh(msPrev); return cancelAnimation; }, // region visibility isVisible = (el) => { const vis = el.offsetParent !== null || !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); return vis; }, isHidden = (el) => !isVisible(el); // // Helpers // const getTagAttr = (attrList, rm) => attrList.filter(e => e.startsWith(rm)).map(e => e.substring(1)); const getTagAttrs = (attrList) => { const attrs = {}, classes = getTagAttr(attrList, '.'), id = getTagAttr(attrList, '#'); for (const val of attrList) { if (classes.length) oa(attrs, { className: classes.join(' ') }); if (id.length) oa(attrs, { id: id[0] }); } return attrs; }; const BaseStatic = { CSS_ACTION_STATES, addClass: addClass$1, attr: attr$1, css: css$1, cssActionStates, empty: empty$1, find: find$1, findBy: findBy$1, findOne: findOne$1, hasClass: hasClass$1, htmlParse: htmlParse$1, isVisible, isHidden, make, map: map$1, merge, next: next$1, parents: parents$1, prev: prev$1, siblings: siblings$1, elemRects: elemRects$1, offset: offset$1, off: off$1, on: on$1, trigger: trigger$1, rmClass: rmClass$1, tgClass: tgClass$1, useTransition, useCssAnimate, animateByFrame, //utils af, isArr, isStr, isObj, oa, toType }; const { addClass, attr, css, elemRects, offset, empty, find, findBy, findOne, map, next, prev, hasClass, htmlParse, off, on, parents, siblings, trigger, rmClass, tgClass } = BaseStatic; const noElemMsg = 'Element is not defined with index: '; class BaseElem { elem = []; constructor(selector, base) { if (isStr(selector)) { this.elem = find(selector, base); } else if (selector instanceof BaseElem) { this.elem = [...selector.elem]; } else { //not checking type for HTMLElement[] if (selector) { this.elem = isArr(selector) ? selector : [selector]; } } return this; } // --------------- // region Private Methods // --------------- #iterate(fn) { let i = 0, elem; while (elem = this.elem[i]) { fn(elem, i++); } } #strOrObj(attrs, fn = () => '') { if (isStr(attrs)) { const elem = this.elem[0]; return fn(elem, attrs); } else { this.#iterate(elem => fn(elem, attrs)); } return ''; } #content(fn, content) { if (content) { this.#iterate((elem) => { empty(elem); const nodes = fn(content); isArr(nodes) ? elem.append(...nodes) : elem.append(nodes); }); } else { // return copy return this.elem[0]; } return null; } // --------- // region Selectors // --------- find(selector, filter) { if (isStr(selector)) { const elems = this.elem.map(elem => find(selector, elem)).flat(); return new BaseElem(filter ? elems.filter(filter) : elems); } else { return new BaseElem(this.map(selector, true)); } } findBy(type, selector, filter) { const elems = this.elem.map(elem => findBy(type, selector, elem)).flat(); return new BaseElem(filter ? elems.filter(filter) : elems); } findOne(selector) { const elem = this.elem.map(elem => findOne(selector, elem)).filter(Boolean); return new BaseElem(elem); } filter(fn) { // not error checking for the document or window return new BaseElem(this.elem.filter(fn)); } // ------------------- // region Array and iteration // ------------------- toArray() { return [...this.elem]; } map(fn, unique = false) { return map(this.elem, fn, unique); } parents(selector, untilElem) { if (untilElem instanceof BaseElem) untilElem = untilElem.elem[0]; const elems = map(this.elem, (elem) => parents(elem, selector, untilElem)); return new BaseElem(elems.flat()); } siblings(selector, includeKeyEl = false, index = 0) { return new BaseElem(siblings(this.elem[index], selector, includeKeyEl)); } next(selector) { return new BaseElem(next(this.elem[0], selector)); } prev(selector) { return new BaseElem(prev(this.elem[0], selector)); } get(index) { const elem = this.elem[index]; if (elem) return new BaseElem(elem); else console.warn(noElemMsg + index); } each(fn) { this.#iterate(fn); return this; } css(attrs) { const val = this.#strOrObj(attrs, css); if (val) return val; return this; } addClass(cssNames) { this.#iterate(elem => addClass(elem, cssNames)); return this; } rmClass(cssNames) { this.#iterate(elem => rmClass(elem, cssNames)); return this; } tgClass(cssNames, toggle) { this.#iterate(elem => tgClass(elem, cssNames, toggle)); return this; } hasClass(cssNames, method = 'some') { return !isArr(this.elem) ? hasClass(this.elem, cssNames) : method === 'some' ? this.elem.some(elem => hasClass(elem, cssNames)) : this.elem.every(elem => hasClass(elem, cssNames)); } attr(attrs) { const val = this.#strOrObj(attrs, attr); if (val) return val; return this; } #getOne(index = 0, fn) { const elem = this.elem[index]; if (elem) { return fn(elem); } else { console.warn(noElemMsg + index); return undefined; } } elemRects(index = 0) { return this.#getOne(index, elemRects); } offset(index = 0) { return this.#getOne(index, offset); } get hasEls() { return this.elem.length > 0; } get size() { return this.elem.length; } // ------------------------------ // region Removal and Appending Elements // ------------------------------ empty() { this.#iterate(empty); return this; } remove() { this.#iterate(elem => elem.remove()); return this; } insert(html, method = 'append') { this.#iterate((elem) => { const elems = (isArr(html) ? html.map(getElems).flat() : isStr(html) ? htmlParse(html) : getElems(html)); if (method === 'append') elem.append(...elems); if (method === 'prepend') elem.prepend(...elems); if (method === 'after') elem.after(...elems); if (method === 'before') elem.before(...elems); }); return this; } html(html) { const retEl = this.#content(htmlParse, html); if (retEl) return retEl.innerHTML.trim(); return this; } text(text) { const retEl = this.#content((text) => new Text(text), text); if (retEl) return retEl.textContent.trim(); return this; } // ------ // region Events // ------ on(evtName, fn, delegateEl = null, config = false) { if (isArr(evtName)) { for (const evName of evtName) { this.#iterate((elem) => on(elem, evName, fn, delegateEl, config)); } } else { this.#iterate((elem) => on(elem, evtName, fn, delegateEl, config)); } return this; } trigger(evtName, delgateEl, data) { this.#iterate((elem) => trigger(elem, evtName, delgateEl, data)); return this; } off(evtName, config = false) { if (isArr(evtName)) { for (const evName of evtName) { this.#iterate((elem) => off(elem, evName, config)); } } else { this.#iterate((elem) => off(elem, evtName, config)); } return this; } } // // Helper // const getElems = (el) => el instanceof BaseElem ? el.elem : [el]; const $be = (selector, base) => new BaseElem(selector, base); oa($be, BaseStatic); $be.BaseElem = BaseElem; return $be; })); } (baseElemJs$1, baseElemJs$1.exports)); return baseElemJs$1.exports; } var baseElemJsExports = requireBaseElemJs(); const $be = /*@__PURE__*/getDefaultExportFromCjs(baseElemJsExports); const parseObjectFromString = (options) => { let retObj = {}; if (typeof options === 'string') { retObj = JSON.parse(options.replace(/:\s*"([^"]*)"/g, function (match, p1) { return ': "' + p1.replace(/:/g, '@colon@') + '"'; }).replace(/:\s*'([^']*)'/g, function (match, p1) { return ': "' + p1.replace(/:/g, '@colon@') + '"'; }).replace(/(['"])?([a-z0-9A-Z_]+)(['"])?\s*:/g, '"$2": ') .replace(/@colon@/g, ':')); } return retObj; }; const { merge, toType, isArr, isStr, oa, af } = $be; // region DOM shortcuts const d = document, body = d.body, isFunc = (fn) => toType(fn) === 'function', reflow = (elem) => elem.offsetHeight, noop = () => { }; // region plugin helpers const setParams = (defaults, options, dataOptions) => { const useOptions = toType(options) === 'object' ? options : {}; return merge([true], {}, defaults, useOptions, dataOptions); }; const getDataOptions = (el, evtName) => parseObjectFromString(el.dataset[evtName + 'Options']); const camelCase = (str) => str.replace(/-./g, x => x.toUpperCase()[1]); const lowercaseFirstLetter = (str) => str.charAt(0).toLowerCase() + str.substring(1); // region device const isMobileOS = () => /Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent); const storeMap = new WeakMap(); const Store = (storeElem, key, value) => { const storeRecord = storeMap.get(storeElem) || storeMap.set(storeElem, {}); const keyExists = Reflect.has(storeRecord, key); if (keyExists) { const valueIsNull = value === null; if (valueIsNull) { delete storeRecord[key]; return null; } if (value) { storeRecord[key] = value; } } else { if (value && value !== null) { storeRecord[key] = value; } } return storeRecord[key]; }; const libraryExtend = (Plugins, Library = $be, notify = false) => { if (isArr(Plugins)) { Plugins.forEach(Plugin => libraryExtend(Plugin, Library, notify)); } else { const dataName = Plugins.pluginName, pluginName = lowercaseFirstLetter(dataName), isBaseElem = !!Library.BaseElem, $library = isBaseElem ? Library.BaseElem.prototype : Library.fn; const storeInstanceEach = (elem, index, params) => { const Instance = Store(elem, dataName); if (!Instance) Store(elem, dataName, new Plugins(elem, params, index)); else { const canUpdate = Instance.handleUpdate && $be.toType(Instance.handleUpdate) === 'function'; if (isStr(params)) { if (params === 'remove') Plugins.remove(elem); if (params === 'update' && canUpdate) Instance.handleUpdate(); return; } checkIfParamsExist(Instance.params, params, notify); $be.merge(Instance.params, params); if (canUpdate) Instance.handleUpdate(); } return Instance; }; const pluginFn = function (params) { const s = this; if (isBaseElem) { return s.each((elem, index) => { storeInstanceEach(elem, index, params); }); } else { //Cash or jQuery return s.each(function (index, elem) { storeInstanceEach(elem, index, params); }); } }; pluginFn.getInstance = (elem) => { return Store(elem, dataName); }; // to get it to print the function name const o = { [pluginName]: pluginFn }; $library[pluginName] = o[pluginName]; } }; const checkIfParamsExist = (setParams, params, notify = true) => { for (let k in params) { if (!({}).hasOwnProperty.call(setParams, k)) { if (notify) console.warn(`${k} is not a property that can be used`); delete params[k]; } } if (notify) console.log(`Params updated:`, params); return params; }; const EVENT_NAME$c = 'UrlState'; const urlStateMap = new Map([ ['search', new URLSearchParams(location.search.replace('?', ''))], ['hash', new URLSearchParams(location.hash.replace('#', ''))] ]); const toUrl = (state = 'replace') => { let vals = ''; const hash = urlStateMap.get('hash').toString(); const search = urlStateMap.get('search').toString(); (search !== '') ? vals += '?' + search : vals += location.href.split(/(\?|\#)/)[0]; if (hash !== '') vals += '#' + hash; // clean-up vals = vals.replace(/(\=\&)+/g, '&').replace(/\=$/, ''); state === "replace" ? history.replaceState(null, '', vals) : history.pushState(null, '', vals); }; const setHashVal = (value, state) => { const params = urlStateMap.get('hash'); for (const key of params.keys()) { params.delete(key); } if (value !== null) params.set(value, ''); if (state) { toUrl(state); } }; const set = (type, paramName, value, state) => { if (type === 'hashVal') { console.warn(`use 'setHashVal' method for setting only the hash val`); return; } const params = urlStateMap.get(type); if (value === null) { params.has(paramName) && params.delete(paramName); } else { const isArray = Array.isArray(value); const adjustedVal = isArray ? `[${value.join(',')}]` : value; params.set(paramName, adjustedVal); } if (state) { toUrl(state); } }; const get = (type, paramName) => { const params = urlStateMap.get(type); if (type === 'hashVal') { return location.hash.replace('#', ''); } if (params.has(paramName)) { const rawVal = params.get(paramName).trim(); if (rawVal.slice(0, 1) === '[' && rawVal.slice(-1) === ']') { const valSplits = rawVal.slice(1, -1).split(','); return valSplits.map(el => !(/\D/).test(el) ? +el : el); } else { return rawVal; } } return null; }; const refresh = (on = true) => { if (on) { $be(window).off(`popstate.${EVENT_NAME$c}`).on(`popstate.${EVENT_NAME$c}`, () => { urlStateMap.set('search', new URLSearchParams(location.search.replace('?', ''))); urlStateMap.set('hash', new URLSearchParams(location.hash.replace('#', ''))); }); } else { $be(window).off(`popstate.${EVENT_NAME$c}`); } }; // print URL params const print = (type, options) => { const params = urlStateMap.get(type), defaultOptions = { pattern: 'normal', brackets: true }, { pattern, brackets } = Object.assign(defaultOptions, options), bkts = brackets ? '[]' : ''; if (pattern === 'repeat') { return [...params.keys()].map((key) => { const val = get(type, key); if (Array.isArray(val)) { return val.map(el => `${key}${bkts}=${encodeURIComponent(el)}`).join('&'); } return `${key}=${val}`; }).join('&'); } return params.toString(); }; // run refresh initially refresh(); const UrlState = { refresh, print, toUrl, set, setHashVal, get }; const animate = (fn, fps = 60) => { let requestAnimate = null; let msPrev = performance.now(); const msPerFrame = 1000 / fps; const cancelAnimation = () => cancelAnimationFrame(requestAnimate); const refresh = (msNow) => { requestAnimate = window.requestAnimationFrame(refresh); const msPassed = msNow - msPrev; if (msPassed < msPerFrame) return; const excessTime = msPassed % msPerFrame; msPrev = msNow - excessTime; fn(cancelAnimation); }; refresh(msPrev); return cancelAnimation; }; const defaultConfig = { immediate: false, delay: 100 }; const debounceResize = (cb, namespace = 'debounceResize', immediate = false, delay = 100) => { const nm = namespace !== '' ? `.${namespace}` : ''; const events = [`resize${nm}`]; debounce(window, events, cb, { immediate, delay }); }; const debounce = (elem, event, cb, config) => { const $elem = $be(elem); const p = { ...defaultConfig, ...config }; let timer = null; $elem.on(event, (ev, elem) => { clearTimeout(timer); timer = setTimeout(cb, p.delay, ev, elem); }); if (p.immediate) { $elem.each((elem) => { const ev = new Event(typeof event === 'string' ? event : event[0]); setTimeout(cb, 0, ev, elem); }); } }; //from: https://gist.github.com/gre/1650294... inspired by: https://nicmulvaney.com/easing const easeInOutQuart = (t) => t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t; const easeInOutCubic = (t) => t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; const easeOutQuint = (t) => 1 + (--t) * t * t * t * t; const linear = (t) => t; const Easing = { // no easing, no acceleration linear, // acceleration until halfway, then deceleration easeInOutCubic, // acceleration until halfway, then deceleration easeInOutQuart, // decelerating to zero velocity easeOutQuint }; const EVENT_NAME$b = 'smoothScroll'; const userBreakEvents = [`wheel.${EVENT_NAME$b}`, `touchstart.${EVENT_NAME$b}`]; let activeScroll = false; function smoothScroll(scrollTargetY, duration = 500, easing = 'easeOutQuint', scrollEndFn) { if (activeScroll) return; let progress = 0; const startScrollY = window.scrollY, scrollDistance = Math.abs(startScrollY - scrollTargetY), scrollDir = startScrollY < scrollTargetY ? 1 : -1, msPerFrame = 1000 / 60, frames = duration / msPerFrame, progressSize = scrollDistance / frames; if (scrollDistance === 0) return; activeScroll = true; body.style.scrollBehavior = 'auto'; const easingFn = isFunc(easing) ? easing : Easing[easing]; const cancelAnimation = animate(() => { progress += progressSize; const scrollProgress = scrollDistance * easingFn(progress / scrollDistance); const yPos = startScrollY + scrollProgress * scrollDir; window.scroll(0, yPos); if (progress > scrollDistance) cancel(); }); const cancel = () => { if (isFunc(scrollEndFn)) scrollEndFn(); cancelAnimation(); $be(window).off([...userBreakEvents, `scroll.${EVENT_NAME$b}`]); body.style.scrollBehavior = null; activeScroll = false; }; $be(window).on(userBreakEvents, cancel); debounce(window, `scroll.${EVENT_NAME$b}`, cancel); } const Cookies = { get(name) { const arg = name + "="; const alen = arg.length; const clen = document.cookie.length; let i = 0; while (i < clen) { let j = i + alen; if (document.cookie.substring(i, j) == arg) { return getCookieVal(j); } i = document.cookie.indexOf(" ", i) + 1; if (i == 0) break; } return ""; }, set(name, value, props = {}) { const d = new Date(); d.setTime(d.getTime() + ((props.expires || 0) * 60 * 1000)); const expires = props.expires ? d.toUTCString() : null; document.cookie = name + "=" + encodeURI(value) + ((expires) ? "; expires=" + expires : "") + "; path=" + (props.path ? props.path : "/") + ((props.domain) ? "; domain=" + props.domain : "") + ((props.sameSite) ? "; sameSite=" + props.sameSite : "") + ((props.secure || props.sameSite && props.sameSite.toLowerCase() === "none") ? "; secure" : ""); }, remove(name, path, domain) { if (this.get(name)) { document.cookie = name + "=" + ((path) ? "; path=" + path : "") + ((domain) ? "; domain=" + domain : "") + "; expires=Thu, 01-Jan-70 00:00:01 GMT"; } }, }; //utility function getCookieVal(offset) { let endstr = document.cookie.indexOf(";", offset); if (endstr == -1) { endstr = document.cookie.length; } return decodeURI(document.cookie.substring(offset, endstr)); } const defaultProps = { focusFirst: true, nameSpace: 'focusTrap', focusable: ['button', 'a', 'input', 'select', 'textarea', '[tabindex]'] }; const canFocusEls = (el) => { const baseFocusableRules = $be.isVisible(el) && el.tabIndex !== -1; const nodeName = el.nodeName.toUpperCase(); if ((nodeName === 'BUTTON' || nodeName === 'INPUT')) { return baseFocusableRules && !el.disabled; } return baseFocusableRules && el.style.pointerEvents !== 'none'; }; const focusTrap = (elem, props) => { const d = document; const body = d.body; const { focusFirst, focusable, nameSpace } = { ...defaultProps, ...props }; const $trapElem = $be(elem); const focusableJoined = typeof focusable === 'string' ? focusable : focusable.join(','); const $firstFocusable = $trapElem.find(focusableJoined).filter(canFocusEls); let firstFocusable = $firstFocusable.hasEls ? $firstFocusable.elem[0] : null; if (focusFirst && firstFocusable) { firstFocusable.focus(); } $be(body).on(`keydown.${nameSpace}`, function (e) { const $focusable = $trapElem.find(focusableJoined).filter(canFocusEls); if (!$focusable.hasEls) return; const activeEl = d.activeElement; const lastFocusable = $focusable.elem[$focusable.size - 1]; const isTabPressed = e.key === 'Tab'; firstFocusable = $focusable.elem[0]; if (!isTabPressed) { return; } if (activeEl.nodeName.toUpperCase() === 'BODY') { // somehow we lost focus // this can happen if the last element is disabled if it was focused // so re-assign to the first element firstFocusable && firstFocusable.focus(); e.preventDefault(); } if (e.shiftKey) { // if shift key pressed for shift + tab combination if (activeEl && firstFocusable && d.activeElement === firstFocusable) { lastFocusable && lastFocusable.focus(); e.preventDefault(); } } else { // if tab key is pressed if (activeEl && lastFocusable && activeEl === lastFocusable) { firstFocusable && firstFocusable.focus(); e.preventDefault(); } } }); return { remove: () => { $be(body).off(`keydown.${nameSpace}`); } }; }; const KEYS = { esc: 'Escape', left: 'ArrowLeft', right: 'ArrowRight', down: 'ArrowDown', up: 'ArrowUp', enter: 'Enter', shift: 'Shift', space: 'Space', tab: 'Tab' }; const VERSION$a = "1.3.0", DATA_NAME$a = 'AccessibleMenu', EVENT_NAME$a = 'accessibleMenu', DEFAULTS$a = { keyDirections: ['horizontal', 'vertical', 'vertical'], focusCss: 'focus', focusInElems: 'a, [tabindex]', focusLeaveElems: 'a, [tabindex], select, button' }; class AccessibleMenu { static defaults = DEFAULTS$a; static version = VERSION$a; static pluginName = DATA_NAME$a; element; $element; params; $aeLiParents = null; activeElem = null; focusInElems = ''; constructor(element, options) { const s = this; const dataOptions = getDataOptions(element, EVENT_NAME$a); s.element = element; s.$element = $be(element); s.params = setParams(AccessibleMenu.defaults, options, dataOptions); s.#handleEvents(); return s; } static remove(element, plugin) { $be(element).each((elem) => { const s = plugin || Store(elem, DATA_NAME$a); s.$element.off([ `focusin.${EVENT_NAME$a}`, `mouseleave.${EVENT_NAME$a}`, `blur.${EVENT_NAME$a}`, `keydown.${EVENT_NAME$a}` ]); Store(elem, DATA_NAME$a, null); }); } #prev(e) { const s = this, p = s.params, l = s.$aeLiParents.elem.length - 1, key = e.key, dirs = p.keyDirections; if (key === KEYS.left && dirs[l] === "horizontal" || key === KEYS.up && dirs[l] === "vertical" || key === KEYS.left && dirs[l] === "vertical" && (l > 1 && dirs[l - 1] === "vertical" && s.$aeLiParents.hasEls)) { s.#focusListItem(true); e.preventDefault(); } } #next(e) { const s = this, p = s.params, l = s.$aeLiParents.size - 1, atRootUl = s.$aeLiParents.size === 1, key = e.key, keyIsRight = key === KEYS.right && p.keyDirections[l], keyIsDown = key === KEYS.down && p.keyDirections[l], activeLi = s.activeElem.closest('li'), $sliblingLis = $be(activeLi).siblings('li', true), isLastLi = $sliblingLis.elem.at(-1) === activeLi, isLastAtRoolLevel = atRootUl && isLastLi; //go to sibling <li> if (keyIsRight === "horizontal" || keyIsDown === "vertical") { if (!isLastAtRoolLevel) { s.#focusListItem(false); e.preventDefault(); } } if (key === KEYS.esc && isLastAtRoolLevel) { $be(s.activeElem.closest('li')).rmClass(p.focusCss); } //go to the nestled <li> if (keyIsRight === "vertical" || keyIsDown === "horizontal") { const $nestledUl = $be(s.activeElem.closest('li')).findOne('ul'); s.#focusFirstElem($nestledUl); e.preventDefault(); } } #handleEvents() { const s = this, p = s.params, $lis = s.$element.findBy('tag', 'li'), lis = $lis.toArray(); let focusOutDelay = null; s.$element.on(`focusin.${EVENT_NAME$a}`, () => { s.activeElem = document.activeElement; focusOutDelay && clearTimeout(focusOutDelay); $be(s.activeElem.closest('li')) .addClass(p.focusCss) .siblings('li').rmClass(p.focusCss); }, lis) .on(`focusout.${EVENT_NAME$a}`, () => { focusOutDelay = setTimeout(() => $lis.rmClass(p.focusCss), 200); }) .on(`mouseleave.${EVENT_NAME$a}`, () => { $lis.rmClass(p.focusCss); }) .on(`keydown.${EVENT_NAME$a}`, (e) => { s.$aeLiParents = $be(s.activeElem).parents('li', s.element); if (e.key == KEYS.esc) s.#focusFirstElem(s.$aeLiParents); s.#prev(e); s.#next(e); }); } #focusFirstElem($focusWrapEl, prev = false, index = 0) { if (!$focusWrapEl.hasEls) return; const s = this, p = s.params, $focusEls = $focusWrapEl.find(p.focusInElems, $be.isVisible); if ($focusEls.hasEls) { const focusEl = $focusEls.elem[index]; $be(focusEl.closest('li')).tgClass(p.focusCss, !prev); focusEl.focus(); } } #focusListItem(prev) { const s = this, p = s.params, currLi = s.activeElem.closest('li'), siblingLi = (prev ? currLi.previousElementSibling : currLi.nextElementSibling), $currLi = $be(currLi), $siblingLi = $be(siblingLi); if ($siblingLi.hasEls) { s.#focusFirstElem($siblingLi, prev); } else { // go to the parent <li> if there is no sibling <li> const nth = s.$aeLiParents.size > 2 ? -2 : 0; $currLi.rmClass(p.focusCss); s.#focusFirstElem($be(s.$aeLiParents.elem.at(nth)), prev); } } } const VERSION$9 = "4.0.0", DATA_NAME$9 = 'Collapse', EVENT_NAME$9 = 'collapse', DEFAULTS$9 = { cssPrefix: 'collapse', toggleDuration: 500, toggleGroup: false, moveToTopOnOpen: false, moveToTopOffset: 0, moveToTopDuration: 500, urlFilterType: 'hash', historyType: 'replace', locationFilter: null, loadLocation: true, afterOpen: noop, afterClose: noop, afterInit: noop }; class Collapse { $element; element; params; toggling; $btnElems; btnElems; $activeItem; initLoaded; static defaults = DEFAULTS$9; static version = VERSION$9; static pluginName = DATA_NAME$9; #transition = $be.useTransition(); constructor(element, options, index) { const s = this; s.element = element; s.$element = $be(element); const dataOptions = getDataOptions(element, EVENT_NAME$9); s.params = setParams(Collapse.defaults, options, dataOptions); s.toggling = false; s.$btnElems = s.$element.find(`.${s.params.cssPrefix}__btn`).attr({ 'aria-expanded': 'false' }); s.btnElems = s.$btnElems.toArray(); s.$activeItem = null; s.initLoaded = false; // init s.#loadFromUrl(); s.#handleEvents(); s.params.afterInit(element); Store(element, DATA_NAME$9, s); return s; } static remove(element, plugin) { $be(element).each((elem) => { const s = plugin || Store(elem, DATA_NAME$9); s.$element.off([`click.${EVENT_NAME$9}`, `[${EVENT_NAME$9}]`]); $be(window).off(`popstate.${EVENT_NAME$9}`); // delete the Store item Store(elem, DATA_NAME$9, null); }); } #handleEvents() { const s = this; const { cssPrefix } = s.params; s.$element.on([`click.${EVENT_NAME$9}`, `[${EVENT_NAME$9}]`], function (e, elem) { const elemId = $be(elem).attr('aria-controls') || elem.hash.substring(1); s.toggle(elemId); e.preventDefault(); }, `.${cssPrefix}__btn`); $be(window).on(`popstate.${EVENT_NAME$9}`, (e) => { if (s.params.historyType === 'push') { s.#loadFromUrl(); s.initLoaded = true; e.preventDefault(); } }); } #loadFromUrl() { const s = this; const p = s.params; const loadElem = (filterEl) => { const cssOpen = `${p.cssPrefix}--open`; const $tryElem = s.$element.find('#' + filterEl); if ($tryElem.elem.length) { s.$activeItem = $tryElem; s.$activeItem.addClass(cssOpen); s.$btnElems .filter(el => $be(el).attr('aria-controls') === filterEl) .attr({ 'aria-expanded': 'true' }); } }; if (p.locationFilter !== null && p.loadLocation && p.urlFilterType !== 'none') { const filterEl = UrlState.get(p.urlFilterType, p.locationFilter); const cssOpen = `${p.cssPrefix}--open`; s.$element.find(`.${p.cssPrefix}__body.${cssOpen}`).rmClass(cssOpen); s.$btnElems.attr({ 'aria-expanded': 'false' }); if (filterEl) { if (Array.isArray(filterEl)) { filterEl.forEach(loadElem); } else { loadElem(filterEl); } } } } #moveToTopOnOpen() { const s = this; const p = s.params; if (s.$activeItem) { const item = s.$activeItem.map(el => el.closest(`.${p.cssPrefix}__item`)); const offsetTop = typeof p.moveToTopOffset === 'function' ? p.moveToTopOffset() : p.moveToTopOffset; if (item.length && p.moveToTopOnOpen) { const top = (item[0].getBoundingClientRect().top + window.scrollY) - offsetTop; smoothScroll(top, p.moveToTopDuration); } } } toggle(currElemID, allAtOnce = false) { const s = this; const p = s.params; const activeItem = $be.findOne('#' + currElemID, s.element); if ((s.toggling && !allAtOnce) || currElemID === null || !activeItem) return; s.$activeItem = s.$element.find('#' + currElemID); const cssPrefix = `${p.cssPrefix}--`, cssOpen = `${cssPrefix}open`, cssToggling = `${cssPrefix}toggling`, cssOpening = `${cssPrefix}opening`, cssClosing = `${cssPrefix}closing`, cssBodyOpen = `.${p.cssPrefix}__body.${cssOpen}`, $currOpenItems = s.$element.find(cssBodyOpen), $itemsToClose = $currOpenItems.filter(el => p.toggleGroup || el.id === currElemID), activeAlreadyOpen = s.$activeItem.hasClass(cssOpen); $itemsToClose.each((elem) => $be.css(elem, { height: elem.scrollHeight + 'px' })); const start = () => { s.toggling = true; s.$btnElems.each((elem) => { const $btn = $be(elem); const current = $btn.attr('aria-controls') === currElemID; const expanded = $btn.attr('aria-expanded') === 'false'; if (p.toggleGroup) { $btn.attr({ 'aria-expanded': (current && expanded) + '' }); } else if (current) { $btn.attr({ 'aria-expanded': expanded + '' }); } }); $itemsToClose .rmClass(cssOpen)