jqlmodule
Version:
A JQuery alike es20xx module
549 lines (462 loc) • 20 kB
JavaScript
/**
generated (synced) file for:
- tinyDOM
- lifeCSS
- typeofAnything
Last updated on 03-01-2025 12:55:44
*/
/ * tinyDOM */
const defaultTinyDOM = tinyDOM();
const converts = { html: `innerHTML`, text: `textContent`, class: `className` };
function tinyDOM() {
const tinyDOMProxyGetter = { get(obj, key) {
const tag = String(key)?.toLowerCase();
switch(true) {
case tag in obj: return obj[tag];
case validateTag(tag): return createTagFunctionProperty(obj, tag, key);
default: return createTagFunctionProperty(obj, tag, key, true);
}
}, enumerable: false, configurable: false
};
return new Proxy({}, tinyDOMProxyGetter);
}
function createTagFunctionProperty(obj, tag, key, isError = false) {
Object.defineProperty(obj, tag, { get() { return isError ? _ => errorElement(key) : tag2FN(tag); } } );
return obj[tag];
}
function processNext(root, next, tagName) {
next = next?.isJQL && next.first() || next;
return maybe({
trial: _ => containsHTML(next) ? root.insertAdjacentHTML(`beforeend`, next) : root.append(next),
whenError: err => console.info(`${tagName} not created, reason\n`, err)
});
}
function tagFN(tagName, initial, ...nested) {
const elem = retrieveElementFromInitial(initial, tagName);
nested?.forEach(arg => processNext(elem, arg, tagName));
return elem;
}
function retrieveElementFromInitial(initial, tag) {
initial = isComment(tag) ? cleanupComment(initial) : initial;
switch(true) {
case IS(initial, String): return createElement(tag, containsHTML(initial, tag) ? {html: initial} : {text: initial});
case IS(initial, Node): return createElementAndAppend(tag, initial);
default: return createElement(tag, initial);
}
}
function cleanupProps(props) {
delete props.data;
if ( Object.keys(props).length < 1 ) { return props; }
Object.keys(props).forEach( key => {
const keyCI = key.toLowerCase();
keyCI in converts && (props[converts[keyCI]] = props[key]) && delete props[key]; } );
return props;
}
function createElementAndAppend(tag, element2Append) {
const elem = createElement(tag);
elem.append(element2Append);
return elem;
}
function createElement(tagName, props = {}) {
props = isObjectCheck(props, {});
const data = Object.entries(props.data ?? {});
const elem = Object.assign(
isComment(tagName) ? new Comment() : document.createElement(tagName),
cleanupProps( props ) );
data.length && data.forEach(([key, value]) => elem.dataset[key] = value);
return elem;
}
function isObjectCheck(someObject, defaultValue) {
return defaultValue
? IS(someObject, {isTypes: Object, notTypes: [Array, null, NaN, Proxy], defaultValue})
: IS(someObject, {isTypes: Object, notTypes: [Array, null, NaN, Proxy]});
}
function cleanupComment(initial) { return isObjectCheck(initial) ? initial?.text ?? initial?.textContent ?? `` : String(initial); }
function errorElement(key) { return createElement(`b`, {style:`color:red`,text:`'${key}' is not a valid HTML-tag`}); }
function containsHTML(str, tag) { return !isComment(tag) && IS(str, String) && /<.*>|&[#|0-9a-z]+[^;];/i.test(str); }
function isComment(tag) { return /comment/i.test(tag); }
function validateTag(name) { return !IS(createElement(name), HTMLUnknownElement); }
function tag2FN(tagName) { return (initial, ...args) => tagFN(tagName, initial, ...args); }
/* typeOfAnything */
const { IS, maybe, $Wrap, xProxy, isNothing } = TOAFactory();
function TOAFactory() {
Symbol.proxy = Symbol.for(`toa.proxy`);
Symbol.is = Symbol.for(`toa.is`);
Symbol.type = Symbol.for(`toa.type`);
Symbol.isSymbol = Symbol.for(`toa.isASymbol`);
addSymbols2Anything();
const maybe = maybeFactory();
const [$Wrap, xProxy] = [WrapAnyFactory(), setProxyFactory()];
xProxy.custom();
return {IS, maybe, $Wrap, isNothing, xProxy};
function IS(anything, ...shouldBe) {
if (maybe({trial: _ => `isTypes` in (shouldBe?.[0] ?? {})})) {
const isTypeObj = shouldBe[0];
return `defaultValue` in (isTypeObj)
? isOrDefault(anything, isTypeObj) : `notTypes` in isTypeObj
? isExcept(anything, isTypeObj) : IS(anything, ...[isTypeObj.isTypes].flat());
}
const input = typeof anything === `symbol` ? Symbol.isSymbol : anything;
return shouldBe.length > 1 ? ISOneOf(input, ...shouldBe) : determineType(anything, ...shouldBe);
}
function typeOf(anything) {
return anything?.[Symbol.proxy] ?? IS(anything);
}
function determineType(input, ...shouldBe) {
let {
noInput,
noShouldbe,
compareTo,
inputCTOR,
isNaN,
isInfinity,
shouldBeFirstElementIsNothing
} = processInput(input, ...shouldBe);
shouldBe = shouldBe.length && shouldBe[0];
switch (true) {
case shouldBeFirstElementIsNothing:
return String(input) === String(compareTo);
case input?.[Symbol.proxy] && noShouldbe:
return input[Symbol.proxy];
case isNaN:
return noShouldbe ? `NaN` : maybe({trial: _ => String(compareTo)}) === String(input);
case isInfinity:
return noShouldbe ? `Infinity` : maybe({trial: _ => String(compareTo)}) === String(input);
case noInput:
return noShouldbe ? String(input) : String(compareTo) === String(input);
case inputCTOR === Boolean:
return !shouldBe ? `Boolean` : inputCTOR === shouldBe;
default:
return getResult(input, shouldBe, noShouldbe, getMe(input, inputCTOR));
}
}
function getMe(input, inputCTOR) {
return input === 0 ? Number : input === `` ? String : !input ? {name: String(input)} : inputCTOR;
}
function processInput(input, ...shouldBe) {
const noShouldbe = shouldBe.length < 1;
const compareTo = !noShouldbe && shouldBe[0];
const shouldBeFirstElementIsNothing = !noShouldbe && isNothing(shouldBe[0]);
const noInput = input === undefined || input === null;
const inputCTOR = !noInput && Object.getPrototypeOf(input)?.constructor;
const isNaN = Number.isNaN(input);
const isInfinity = maybe({trial: _ => String(input)}) === `Infinity`;
return {noInput, noShouldbe, compareTo, inputCTOR, isNaN, isInfinity, shouldBeFirstElementIsNothing};
}
function getResult(input, compareWith, noShouldbe, me) {
switch(true) {
case (!noShouldbe && compareWith === input) ||
(input?.[Symbol.proxy] && compareWith === Proxy):
return true;
case maybe({trial: _ => String(compareWith)}) === `NaN`:
return String(input) === `NaN`;
case input?.[Symbol.toStringTag] && IS(compareWith, String):
return String(compareWith) === input[Symbol.toStringTag];
default:
return compareWith
? maybe({trial: _ => input instanceof compareWith,}) ||
compareWith === me || compareWith === Object.getPrototypeOf(me) ||
`${compareWith?.name}` === me?.name
: input?.[Symbol.toStringTag] && `[object ${input?.[Symbol.toStringTag]}]` ||
me?.name ||
String(me);
}
}
function ISOneOf(obj, ...params) {
return params.some(param => IS(obj, param));
}
function isNothing(maybeNothing, all = false) {
let nada = maybeNothing === null || maybeNothing === undefined;
nada = all ? nada || IS(maybeNothing, Infinity) || IS(maybeNothing, NaN) : nada;
return nada;
}
function maybeFactory() {
const tryFn = (maybeFn, maybeError) => maybeFn?.constructor === Function ? maybeFn(maybeError) : undefined;
return function ({trial, whenError = () => undefined} = {}) {
try {
return tryFn(trial)
} catch (err) {
return tryFn(whenError, err)
}
};
}
function WrapAnyFactory() {
return function (someObj) {
return Object.freeze({
get value() { return someObj; },
get [Symbol.type]() { return typeOf(someObj); },
get type() { return typeOf(someObj); },
[Symbol.is](...args) { return IS(someObj, ...args); },
is(...args) { return IS(someObj, ...args); }
});
}
}
function isOrDefault(input, {defaultValue, isTypes = [undefined], notTypes} = {}) {
isTypes = isTypes?.constructor !== Array ? [isTypes] : isTypes;
notTypes = notTypes && notTypes?.constructor !== Array ? [notTypes] : [];
return notTypes.length < 1
? IS(input, ...isTypes) ? input : defaultValue
: isExcept(input, {isTypes, notTypes}) ? input : defaultValue;
}
function isExcept(input, {isTypes = [undefined], notTypes = [undefined]} = {}) {
isTypes = isTypes?.constructor !== Array ? [isTypes] : isTypes;
notTypes = notTypes?.constructor !== Array ? [notTypes] : notTypes;
return IS(input, ...isTypes) && !IS(input, ...notTypes);
}
function addSymbols2Anything() {
if (!Object.getOwnPropertyDescriptors(Object.prototype)[Symbol.is]) {
Object.defineProperties(Object.prototype, {
[Symbol.type]: { get() { return typeOf(this); }, enumerable: false, configurable: false },
[Symbol.is]: { value: function (...args) { return IS(this, ...args); }, enumerable: false, configurable: false },
});
Object.defineProperties(Object, {
[Symbol.type]: { value(obj) { return typeOf(obj); }, enumerable: false, configurable: false },
[Symbol.is]: { value: function (obj, ...args) { return IS(obj, ...args); }, enumerable: false, configurable: false },
});
}
}
function ctor2String(obj) {
const str = String(Object.getPrototypeOf(obj)?.constructor);
return str.slice(str.indexOf(`ion`) + 3, str.indexOf(`(`)).trim();
}
function modifySetter(setterMethod2Modify) {
const oldSetter = setterMethod2Modify.set;
setterMethod2Modify.set = (target, key, value) => {
if (key === Symbol.proxy) {
return target[key] = value;
}
return oldSetter(target, key, value);
}
return setterMethod2Modify;
}
function setProxyFactory() {
const nativeProxy = Proxy;
return {
native() {
Proxy = nativeProxy;
},
custom() {
// adaptation of https://stackoverflow.com/a/53463589
Proxy = new nativeProxy(nativeProxy, {
construct(target, args) {
for (let item of args) {
if (item.set) {
item = modifySetter(item);
}
}
const wrappedProxy = new target(...args);
wrappedProxy[Symbol.proxy] = `Proxy (${ctor2String(args[0])})`;
return wrappedProxy;
}
})
}
}
}
}
/* lifeCSS */
function LifeStyleFactory({styleSheet, createWithId} = {}) {
const { tryParseAtOrNestedRules, ruleExists, checkParams,
sheet, removeRules, consider, currentSheetID } = sheetHelpers({styleSheet, createWithId});
function setRules4Selector(rule, properties) {
if (rule && properties.removeProperties) {
Object.keys(properties.removeProperties)
.forEach( prop => rule.style.removeProperty(toDashedNotation(prop)) );
return;
}
Object.entries(properties)
.forEach( ([prop, value]) => {
prop = toDashedNotation(prop.trim());
value = value.trim();
let priority;
if (/!important/.test(value)) {
value = value.slice(0, value.indexOf(`!important`)).trim();
priority = `important`;
}
if (!CSS.supports(prop, value)) {
return console.error(`StylingFactory ${currentSheetID} error: '${
prop}' with value '${value}' not supported (yet)`);
}
tryAndCatch( () => rule.style.setProperty(prop, value, priority),
`StylingFactory ${currentSheetID} (setRule4Selector) failed`);
});
}
function setRules(selector, styleRules, sheetOrMediaRules = sheet) {
selector = selector?.trim?.();
if (!IS(selector, String) || !selector.length || /[;,]$/g.test(selector)) {
return console.error(`StylingFactory ${currentSheetID} (setRules): [${
selector || `[no selector given]` }] is not a valid selector`);
}
if (styleRules.removeRule) {
return removeRules(selector);
}
const exists = ruleExists(selector, true);
const rule4Selector = exists
|| sheetOrMediaRules.cssRules[sheetOrMediaRules.insertRule(`${selector} {}`, sheetOrMediaRules.cssRules.length || 0)];
return consider( () => setRules4Selector(rule4Selector, styleRules), selector, exists );
}
function doParse(cssDeclarationString) {
const rule = cssDeclarationString.trim().split(/{/, 2);
const selector = rule.shift().trim();
if (!IS(selector, String) || !selector?.trim()?.length) {
return console.error(`StylingFactory ${currentSheetID} (doParse): no (valid) selector could be extracted from rule ${
shortenRule(cssDeclarationString)}`);
}
const cssRules = cssRuleFromText(rule.shift());
return tryAndCatch( () => setRules(selector, cssRules), `StylingFactory ${currentSheetID} (setRules) failed` );
}
function styleFromString(cssDeclarationString) {
const checkAts = tryParseAtOrNestedRules(cssDeclarationString);
return checkAts.done ? checkAts.existing : doParse(cssDeclarationString);
}
function styleFromObject(selector, rulesObj) {
if (selector.trim().startsWith(`@media`)) {
return styleFromString(atMedia2String(selector, rulesObj));
}
return setRules(selector, rulesObj);
}
return function(cssBlockOrSelector, rulesObj = {}) {
const checksOk = checkParams(cssBlockOrSelector, rulesObj);
return checksOk && (
Object.keys(rulesObj).length ?
styleFromObject(cssBlockOrSelector, rulesObj) :
styleFromString(cssBlockOrSelector) );
};
}
function sheetHelpers({styleSheet, createWithId}) {
const notification = `Note: The rule or some of its properties may not be supported by your browser (yet)`;
const currentSheetID = `for style#${createWithId}`;
styleSheet = createWithId ? retrieveOrCreateSheet(createWithId) : styleSheet;
function retrieveOrCreateSheet(id) {
const existingSheet = document.querySelector(`#${id}`)?.sheet;
if (existingSheet) { return existingSheet; }
const newSheet = Object.assign(document.createElement(`style`), { id });
document.head.insertAdjacentElement(`beforeend`, newSheet);
return newSheet.sheet;
}
function notSupported(rule) {
console.error(`StylingFactory ${currentSheetID} [rule: ${rule}]
=> @charset, @namespace and @import are not supported here`);
return {done: true};
}
function ruleExists(ruleFragOrSelector, isSelector) {
return [...styleSheet.rules].find( r =>
isSelector ?
compareSelectors((r.selectorText || ``), ruleFragOrSelector) :
createRE`${escape4RegExp(ruleFragOrSelector)}${[...`gim`]}`.test(r.cssText));
}
function tryParseAtOrNestedRules(cssDeclarationString) {
if (/^@charset|@import|namespace/i.test(cssDeclarationString.trim())) {
return notSupported(cssDeclarationString);
}
if (cssDeclarationString.match(/}/g)?.length > 1) {
return {existing: tryParse(cssDeclarationString, 1), done: true}
}
return { done: false };
}
function removeRules(selector) {
const rulesAt = [...styleSheet.cssRules].reduce( (acc, v, i) =>
compareSelectors(v.selectorText || ``, selector) && acc.concat(i) || acc, [] );
const len = rulesAt.length;
rulesAt.forEach(idx => styleSheet.deleteRule(idx));
return len > 0
? console.info(`✔ Removed ${len} instance${len > 1 ? `s` : ``} of selector ${
selector} from ${currentSheetID.slice(4)}`)
: console.info(`✔ Remove rule: selector ${selector} does not exist in ${
currentSheetID.slice(4)}`);
}
function checkParams(cssBlockOrSelector, rulesObj) {
return cssBlockOrSelector
&& IS(cssBlockOrSelector, String)
&& cssBlockOrSelector.trim().length > 0
&& IS(rulesObj, Object) ||
(console.error(`StylingFactory ${currentSheetID} called with invalid parameters`), false);
}
function tryParse(cssDeclarationString) {
cssDeclarationString = cssDeclarationString.trim();
const rule = cssDeclarationString.slice(0, cssDeclarationString.indexOf(`{`)).trim();
const exists = !!ruleExists(rule);
try {
return (styleSheet.insertRule(`${cssDeclarationString}`, styleSheet.cssRules.length), exists);
} catch(err) {
return (console.error(`StylingFactory ${currentSheetID} (tryParse) ${err.name} Error:\n${
err.message}\nRule: ${
shortenRule(cssDeclarationString)}\n${
notification}`),
exists);
}
}
function consider(fn, rule, existing) {
try {
return (fn(), existing);
} catch(err) {
return (
console.error(`StylingFactory ${currentSheetID} (tryAddOrModify) ${err.name} Error:\n${
err.message}\nRule: ${shortenRule(rule)}\n${notification}`),
existing
);
}
}
return {
sheet: styleSheet, removeRules, tryParseAtOrNestedRules,
ruleExists, checkParams, tryParse, consider,
currentSheetID };
}
function atMedia2String(selector, rulesObj) {
return `${selector.trim()} ${
Object.entries(rulesObj).map( ( [ selectr, rule] ) =>
`${selectr}: { ${stringifyMediaRule(rule) }` ) }`;
}
function shortenRule(rule) {
const shortRule = (rule || `NO RULE`).trim().slice(0, 50).replace(/\n/g, `\\n`).replace(/\s{2,}/g, ` `);
return rule.length > shortRule.length ? `${shortRule.trim()}...truncated` : shortRule;
}
function stringifyMediaRule(mediaObj) {
return Object.entries(mediaObj)
.map( ([key, value]) => `${key}: ${value.trim()}`).join(`;\n`);
}
function escape4RegExp(str) {
return str.replace(/([*\[\]()-+{}.$?\\])/g, a => `\\${a}`);
}
function createRE(regexStr, ...args) {
const flags = args.length && Array.isArray(args.slice(-1)) ? args.pop().join(``) : ``;
return new RegExp(
(args.length &&
regexStr.raw.reduce( (a, v, i ) => a.concat(args[i-1] || ``).concat(v), ``) ||
regexStr.raw.join(``))
.split(`\n`)
.map( line => line.replace(/\s|\/\/.*$/g, ``).trim().replace(/(@s!)/g, ` `) )
.join(``), flags);
}
function toDashedNotation(str2Convert) {
return str2Convert.replace(/[A-Z]/g, a => `-${a.toLowerCase()}`).replace(/[^--]^-|-$/, ``);
}
function tryAndCatch(fn, msg) {
try { return fn(); }
catch(err) { console.error( `${msg || `an error occured`}: ${err.message}` ); }
}
function prepareCssRuleFromText(rule) {
return rule
.replace(/\/\*.+?\*\//gm, ``)
.replace(/[}{\r\n]/g, ``)
.replace(/(data:.+?);/g, (_,b) => `${b}\\3b`)
.split(`;`)
.map(l => l.trim())
.join(`;\n`)
.replaceAll(`\\3b`, `;`)
.split(`\n`);
}
function toRuleObject(preparedRule) {
return preparedRule
.reduce( (acc, v) => {
const [key, value] = [
v.slice(0, v.indexOf(`:`)).trim(),
v.slice(v.indexOf(`:`) + 1).trim().replace(/;$|;.+(?=\/*).+\/$/, ``)];
return key && value ? {...acc, [key]: value} : acc; }, {} );
}
function cssRuleFromText(rule) {
return toRuleObject(prepareCssRuleFromText(rule));
}
function compareSelectors(s1, s2) {
return s1?.replace(`::`, `:`) === s2?.replace(`::`, `:`);
}
export {defaultTinyDOM as default, IS, maybe, LifeStyleFactory};