accordion
Version:
Silky-smooth accordion widgets with no external dependencies.
145 lines (116 loc) • 4.1 kB
JavaScript
;
export const touchEnabled = "ontouchstart" in document.documentElement;
export const pressEvent = touchEnabled ? "touchend" : "click";
/**
* Name of the onTransitionEnd event supported by this browser.
* @const {String} transitionEnd
*/
export const transitionEnd = (function(){
const names = "transitionend webkitTransitionEnd oTransitionEnd otransitionend".split(" ");
for(let i = 0; i < 4; ++i)
if("on" + names[i].toLowerCase() in window)
return names[i];
return names[0];
}());
/**
* Conditionally add or remove a token from a token-list.
*
* @param {DOMTokenList} list
* @param {String} token
* @param {Boolean} enabled
*/
export function setToken(list, token, enabled){
enabled ? list.add(token) : list.remove(token);
}
/**
* Stop a function from firing too quickly.
*
* Returns a copy of the original function that runs only after the designated
* number of milliseconds have elapsed. Useful for throttling onResize handlers.
*
* @param {Function} fn - Function to debounce
* @param {Number} [limit=0] - Threshold to stall execution by, in milliseconds.
* @param {Boolean} [asap=false] - Call function *before* threshold elapses, not after.
* @return {Function}
*/
export function debounce(fn, limit = 0, asap = false){
let started, context, args, timing;
const delayed = function(){
const timeSince = Date.now() - started;
if(timeSince >= limit){
if(!asap) fn.apply(context, args);
if(timing) clearTimeout(timing);
timing = context = args = null;
}
else timing = setTimeout(delayed, limit - timeSince);
};
// Debounced copy of original function
return function(){
context = this,
args = arguments;
if(!limit)
return fn.apply(context, args);
started = Date.now();
if(!timing){
if(asap) fn.apply(context, args);
timing = setTimeout(delayed, limit);
}
};
}
export const uniqueID = (function(){
const IDs = {};
const indexes = {};
/**
* Generate a unique ID for a DOM element.
*
* By default, minimalist IDs like "_1" or "_2" are generated using internally
* tracked incrementation. Uglier, more collision-proof IDs can be generated by
* passing a truthy value to the function's first argument.
*
* Irrespective of whether values are being generated simply or randomly, the
* document tree is always consulted first to ensure a duplicate ID is never
* returned.
*
* @param {String} prefix - Prefix prepended to result. Default: "_"
* @param {Boolean} random - Generate collision-proof IDs using random symbols
* @param {Number} length - Length of random passwords. Default: 6
* @return {String}
*/
function uniqueID(prefix, complex, length){
length = +(length || 6);
let result = (prefix = prefix || "_");
// Simple IDs
if(!complex){
// Set this prefix's starting index if it's not been used yet
if(!indexes[prefix])
indexes[prefix] = 0;
result += ++indexes[prefix];
}
// Uglier, more collision-proof IDs
else{
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
result += chars[ Math.round(Math.random() * 51) ];
while(result.length < length)
result += chars[ Math.round(Math.random() * 61)];
}
return IDs[result] || document.getElementById(result)
? uniqueID(prefix, complex)
: (IDs[result] = true, result);
}
return uniqueID;
}());
// Name of the CSSOM property used by this browser for CSS transforms
export const cssTransform = (function(n){
s = document.documentElement.style;
if((prop = n.toLowerCase()) in s) return prop;
for(var prop, s, p = "Webkit Moz Ms O Khtml", p = (p.toLowerCase() + p).split(" "), i = 0; i < 10; ++i)
if((prop = p[i]+n) in s) return prop;
return "";
}("Transform"));
// Whether 3D transforms are supported by this browser
export const css3DSupported = (function(propName){
const e = document.createElement("div"), s = e.style;
const v = [["translateY(", ")"], ["translate3d(0,", ",0)"]];
try{ s[propName] = v[1].join("1px"); } catch(e){}
return v[+!!s[propName]] === v[1];
}(cssTransform));