export function $(expr, con) { return typeof expr === 'string' ? (con || document).querySelector(expr) : expr || null; } export function createSVG(tag, attrs) { const elem = document.createElementNS('http://www.w3.org/2000/svg', tag); for (let attr in attrs) { if (attr === 'append_to') { const parent = attrs.append_to; parent.appendChild(elem); } else if (attr === 'innerHTML') { elem.innerHTML = attrs.innerHTML; } else { elem.setAttribute(attr, attrs[attr]); } } return elem; } export function animateSVG(svgElement, attr, from, to) { const animatedSvgElement = getAnimationElement(svgElement, attr, from, to); if (animatedSvgElement === svgElement) { // triggered 2nd time programmatically // trigger artificial click event const event = document.createEvent('HTMLEvents'); event.initEvent('click', true, true); event.eventName = 'click'; animatedSvgElement.dispatchEvent(event); } } function getAnimationElement( svgElement, attr, from, to, dur = '0.4s', begin = '0.1s' ) { const animEl = svgElement.querySelector('animate'); if (animEl) { $.attr(animEl, { attributeName: attr, from, to, dur, begin: 'click + ' + begin, // artificial click }); return svgElement; } const animateElement = createSVG('animate', { attributeName: attr, from, to, dur, begin, calcMode: 'spline', values: from + ';' + to, keyTimes: '0; 1', keySplines: cubic_bezier('ease-out'), }); svgElement.appendChild(animateElement); return svgElement; } function cubic_bezier(name) { return { ease: '.25 .1 .25 1', linear: '0 0 1 1', 'ease-in': '.42 0 1 1', 'ease-out': '0 0 .58 1', 'ease-in-out': '.42 0 .58 1', }[name]; } $.on = (element, event, selector, callback) => { if (!callback) { callback = selector; $.bind(element, event, callback); } else { $.delegate(element, event, selector, callback); } }; $.off = (element, event, handler) => { element.removeEventListener(event, handler); }; $.bind = (element, event, callback) => { event.split(/\s+/).forEach(function (event) { element.addEventListener(event, callback); }); }; $.delegate = (element, event, selector, callback) => { element.addEventListener(event, function (e) { const delegatedTarget = e.target.closest(selector); if (delegatedTarget) { e.delegatedTarget = delegatedTarget; callback.call(this, e, delegatedTarget); } }); }; $.closest = (selector, element) => { if (!element) return null; if (element.matches(selector)) { return element; } return $.closest(selector, element.parentNode); }; $.attr = (element, attr, value) => { if (!value && typeof attr === 'string') { return element.getAttribute(attr); } if (typeof attr === 'object') { for (let key in attr) { $.attr(element, key, attr[key]); } return; } element.setAttribute(attr, value); }; $.style = (element, attr, value) => { if (!value && typeof attr === 'string') { return element.getAttribute(attr); } if (typeof attr === 'object') { for (let key in attr) { $.style(element, key, attr[key]); } return; } element.style[attr] = value; };