|
const DIRECT_ATTRIBUTE_MAP = { |
|
cellpadding: 'cellPadding', |
|
cellspacing: 'cellSpacing', |
|
colspan: 'colSpan', |
|
frameborder: 'frameBorder', |
|
height: 'height', |
|
maxlength: 'maxLength', |
|
nonce: 'nonce', |
|
role: 'role', |
|
rowspan: 'rowSpan', |
|
type: 'type', |
|
usemap: 'useMap', |
|
valign: 'vAlign', |
|
width: 'width', |
|
}; |
|
const RGX_NUMERIC_STYLE_UNIT = 'px'; |
|
const RGX_NUMERIC_STYLE = /^((max|min)?(width|height)|margin|padding|(margin|padding)?(left|top|bottom|right)|fontsize|borderwidth)$/i; |
|
const RGX_DEFAULT_VALUE_PROP = /input|textarea|select/i; |
|
function localAssertNotFalsy(input, errorMsg = `Input is not of type.`) { |
|
if (input == null) { |
|
throw new Error(errorMsg); |
|
} |
|
return input; |
|
} |
|
const RGX_STRING_VALID = '[a-z0-9_-]'; |
|
const RGX_TAG = new RegExp(`^([a-z]${RGX_STRING_VALID}*)(\\.|\\[|\\#|$)`, 'i'); |
|
const RGX_ATTR_ID = new RegExp(`#(${RGX_STRING_VALID}+)`, 'gi'); |
|
const RGX_ATTR_CLASS = new RegExp(`(^|\\S)\\.([a-z0-9_\\-\\.]+)`, 'gi'); |
|
const RGX_STRING_CONTENT_TO_SQUARES = '(.*?)(\\[|\\])'; |
|
const RGX_ATTRS_MAYBE_OPEN = new RegExp(`\\[${RGX_STRING_CONTENT_TO_SQUARES}`, 'gi'); |
|
const RGX_ATTRS_FOLLOW_OPEN = new RegExp(`^${RGX_STRING_CONTENT_TO_SQUARES}`, 'gi'); |
|
export function query(selectors, parent = document) { |
|
return Array.from(parent.querySelectorAll(selectors)).filter(n => !!n); |
|
} |
|
export function queryOne(selectors, parent = document) { |
|
var _a; |
|
return (_a = parent.querySelector(selectors)) !== null && _a !== void 0 ? _a : null; |
|
} |
|
export function createText(text) { |
|
return document.createTextNode(text); |
|
} |
|
export function getClosestOrSelf(element, query) { |
|
const el = element; |
|
return ((el === null || el === void 0 ? void 0 : el.closest) && (el.matches(query) && el || el.closest(query))) || null; |
|
} |
|
export function containsOrSelf(parent, contained) { |
|
var _a; |
|
return parent === contained || ((_a = parent === null || parent === void 0 ? void 0 : parent.contains) === null || _a === void 0 ? void 0 : _a.call(parent, contained)) || false; |
|
} |
|
export function createElement(selectorOrMarkup, attrs) { |
|
const frag = getHtmlFragment(selectorOrMarkup); |
|
let element = frag === null || frag === void 0 ? void 0 : frag.firstElementChild; |
|
let selector = ""; |
|
if (!element) { |
|
selector = selectorOrMarkup.replace(/[\r\n]\s*/g, ""); |
|
const tag = getSelectorTag(selector) || "div"; |
|
element = document.createElement(tag); |
|
selector = selector.replace(RGX_TAG, "$2"); |
|
selector = selector.replace(RGX_ATTR_ID, '[id="$1"]'); |
|
selector = selector.replace(RGX_ATTR_CLASS, (match, p1, p2) => `${p1}[class="${p2.replace(/\./g, " ")}"]`); |
|
} |
|
const selectorAttrs = getSelectorAttributes(selector); |
|
if (selectorAttrs) { |
|
for (const attr of selectorAttrs) { |
|
let matches = attr.substring(1, attr.length - 1).split("="); |
|
let key = localAssertNotFalsy(matches.shift()); |
|
let value = matches.join("="); |
|
if (value === undefined) { |
|
setAttribute(element, key, true); |
|
} |
|
else { |
|
value = value.replace(/^['"](.*)['"]$/, "$1"); |
|
setAttribute(element, key, value); |
|
} |
|
} |
|
} |
|
if (attrs) { |
|
setAttributes(element, attrs); |
|
} |
|
return element; |
|
} |
|
export const $el = createElement; |
|
function getSelectorTag(str) { |
|
return tryMatch(str, RGX_TAG); |
|
} |
|
function getSelectorAttributes(selector) { |
|
RGX_ATTRS_MAYBE_OPEN.lastIndex = 0; |
|
let attrs = []; |
|
let result; |
|
while (result = RGX_ATTRS_MAYBE_OPEN.exec(selector)) { |
|
let attr = result[0]; |
|
if (attr.endsWith(']')) { |
|
attrs.push(attr); |
|
} |
|
else { |
|
attr = result[0] |
|
+ getOpenAttributesRecursive(selector.substr(RGX_ATTRS_MAYBE_OPEN.lastIndex), 2); |
|
RGX_ATTRS_MAYBE_OPEN.lastIndex += (attr.length - result[0].length); |
|
attrs.push(attr); |
|
} |
|
} |
|
return attrs; |
|
} |
|
function getOpenAttributesRecursive(selectorSubstring, openCount) { |
|
let matches = selectorSubstring.match(RGX_ATTRS_FOLLOW_OPEN); |
|
let result = ''; |
|
if (matches && matches.length) { |
|
result = matches[0]; |
|
openCount += result.endsWith(']') ? -1 : 1; |
|
if (openCount > 0) { |
|
result += getOpenAttributesRecursive(selectorSubstring.substr(result.length), openCount); |
|
} |
|
} |
|
return result; |
|
} |
|
function tryMatch(str, rgx, index = 1) { |
|
var _a; |
|
let found = ''; |
|
try { |
|
found = ((_a = str.match(rgx)) === null || _a === void 0 ? void 0 : _a[index]) || ''; |
|
} |
|
catch (e) { |
|
found = ''; |
|
} |
|
return found; |
|
} |
|
export function setAttributes(element, data) { |
|
let attr; |
|
for (attr in data) { |
|
if (data.hasOwnProperty(attr)) { |
|
setAttribute(element, attr, data[attr]); |
|
} |
|
} |
|
} |
|
function getHtmlFragment(value) { |
|
if (value.match(/^\s*<.*?>[\s\S]*<\/[a-z0-9]+>\s*$/)) { |
|
return document.createRange().createContextualFragment(value.trim()); |
|
} |
|
return null; |
|
} |
|
function getChild(value) { |
|
if (value instanceof Node) { |
|
return value; |
|
} |
|
if (typeof value === 'string') { |
|
let child = getHtmlFragment(value); |
|
if (child) { |
|
return child; |
|
} |
|
if (getSelectorTag(value)) { |
|
return createElement(value); |
|
} |
|
return createText(value); |
|
} |
|
if (value && typeof value.toElement === 'function') { |
|
return value.toElement(); |
|
} |
|
return null; |
|
} |
|
export function setAttribute(element, attribute, value) { |
|
let isRemoving = value == null; |
|
if (attribute === 'default') { |
|
attribute = RGX_DEFAULT_VALUE_PROP.test(element.nodeName) ? 'value' : 'text'; |
|
} |
|
if (attribute === 'text') { |
|
empty(element).appendChild(createText(value != null ? String(value) : '')); |
|
} |
|
else if (attribute === 'html') { |
|
empty(element).innerHTML += value != null ? String(value) : ''; |
|
} |
|
else if (attribute == 'style') { |
|
if (typeof value === 'string') { |
|
element.style.cssText = isRemoving ? '' : (value != null ? String(value) : ''); |
|
} |
|
else { |
|
for (const [styleKey, styleValue] of Object.entries(value)) { |
|
element.style[styleKey] = styleValue; |
|
} |
|
} |
|
} |
|
else if (attribute == 'events') { |
|
for (const [key, fn] of Object.entries(value)) { |
|
addEvent(element, key, fn); |
|
} |
|
} |
|
else if (attribute === 'parent') { |
|
value.appendChild(element); |
|
} |
|
else if (attribute === 'child' || attribute === 'children') { |
|
if (typeof value === 'string' && /^\[[^\[\]]+\]$/.test(value)) { |
|
const parseable = value.replace(/^\[([^\[\]]+)\]$/, '["$1"]').replace(/,/g, '","'); |
|
try { |
|
const parsed = JSON.parse(parseable); |
|
value = parsed; |
|
} |
|
catch (e) { |
|
console.error(e); |
|
} |
|
} |
|
if (attribute === 'children') { |
|
empty(element); |
|
} |
|
let children = value instanceof Array ? value : [value]; |
|
for (let child of children) { |
|
child = getChild(child); |
|
if (child instanceof Node) { |
|
if (element instanceof HTMLTemplateElement) { |
|
element.content.appendChild(child); |
|
} |
|
else { |
|
element.appendChild(child); |
|
} |
|
} |
|
} |
|
} |
|
else if (attribute == 'for') { |
|
element.htmlFor = value != null ? String(value) : ''; |
|
if (isRemoving) { |
|
element.removeAttribute('for'); |
|
} |
|
} |
|
else if (attribute === 'class' || attribute === 'className' || attribute === 'classes') { |
|
element.className = isRemoving ? '' : Array.isArray(value) ? value.join(' ') : String(value); |
|
} |
|
else if (attribute === 'dataset') { |
|
if (typeof value !== 'object') { |
|
console.error('Expecting an object for dataset'); |
|
return; |
|
} |
|
for (const [key, val] of Object.entries(value)) { |
|
element.dataset[key] = String(val); |
|
} |
|
} |
|
else if (attribute.startsWith('on') && typeof value === 'function') { |
|
element.addEventListener(attribute.substring(2), value); |
|
} |
|
else if (['checked', 'disabled', 'readonly', 'required', 'selected'].includes(attribute)) { |
|
element[attribute] = !!value; |
|
if (!value) { |
|
element.removeAttribute(attribute); |
|
} |
|
else { |
|
element.setAttribute(attribute, attribute); |
|
} |
|
} |
|
else if (DIRECT_ATTRIBUTE_MAP.hasOwnProperty(attribute)) { |
|
if (isRemoving) { |
|
element.removeAttribute(DIRECT_ATTRIBUTE_MAP[attribute]); |
|
} |
|
else { |
|
element.setAttribute(DIRECT_ATTRIBUTE_MAP[attribute], String(value)); |
|
} |
|
} |
|
else if (isRemoving) { |
|
element.removeAttribute(attribute); |
|
} |
|
else { |
|
let oldVal = element.getAttribute(attribute); |
|
if (oldVal !== value) { |
|
element.setAttribute(attribute, String(value)); |
|
} |
|
} |
|
} |
|
function addEvent(element, key, fn) { |
|
element.addEventListener(key, fn); |
|
} |
|
function setStyles(element, styles = null) { |
|
if (styles) { |
|
for (let name in styles) { |
|
setStyle(element, name, styles[name]); |
|
} |
|
} |
|
return element; |
|
} |
|
function setStyle(element, name, value) { |
|
name = (name.indexOf('float') > -1 ? 'cssFloat' : name); |
|
if (name.indexOf('-') != -1) { |
|
name = name.replace(/-\D/g, (match) => { |
|
return match.charAt(1).toUpperCase(); |
|
}); |
|
} |
|
if (value == String(Number(value)) && RGX_NUMERIC_STYLE.test(name)) { |
|
value = value + RGX_NUMERIC_STYLE_UNIT; |
|
} |
|
if (name === 'display' && typeof value !== 'string') { |
|
value = !!value ? null : 'none'; |
|
} |
|
element.style[name] = value === null ? null : String(value); |
|
return element; |
|
} |
|
; |
|
export function empty(element) { |
|
while (element.firstChild) { |
|
element.removeChild(element.firstChild); |
|
} |
|
return element; |
|
} |
|
export function appendChildren(el, children) { |
|
children = !Array.isArray(children) ? [children] : children; |
|
for (let child of children) { |
|
child = getChild(child); |
|
if (child instanceof Node) { |
|
if (el instanceof HTMLTemplateElement) { |
|
el.content.appendChild(child); |
|
} |
|
else { |
|
el.appendChild(child); |
|
} |
|
} |
|
} |
|
} |
|
|