h5ai/src/_h5ai/public/js/lib/util/dom.js

278 lines
6.3 KiB
JavaScript

const {each, filter, hasLength, is, isStr, map, isInstanceOf, toArray} = require('./lo');
const win = global.window;
const doc = win.document;
const parse_html = (() => {
const create = name => doc.createElement(name);
const rules = [
[/^<t(head|body|foot)|^<c(ap|olg)/i, create('table')],
[/^<col/i, create('colgroup')],
[/^<tr/i, create('tbody')],
[/^<t[dh]/i, create('tr')]
];
const div = create('div');
const findContainer = str => {
for (const [re, el] of rules) {
if (re.test(str)) {
return el;
}
}
return div;
};
return str => {
const container = findContainer(str);
container.innerHTML = str;
const res = toArray(container.childNodes);
each(res, el => container.removeChild(el));
container.innerHTML = '';
return res;
};
})();
const query_all = (selector, context = doc) => {
try {
return toArray(context.querySelectorAll(selector));
} catch (err) {
return [];
}
};
const is_el = x => isInstanceOf(x, win.Element);
const is_doc = x => isInstanceOf(x, win.Document);
const is_win = x => is(x) && x.window === x && is_doc(x.document);
const is_el_doc_win = x => is_el(x) || is_doc(x) || is_win(x);
const add_listener = (el, type, fn) => el.addEventListener(type, fn);
const remove_listener = (el, type, fn) => el.removeEventListener(type, fn);
const ready_promise = new Promise(resolve => {
if ((/^(i|c|loade)/).test(doc.readyState)) {
resolve();
} else {
add_listener(doc, 'DOMContentLoaded', () => resolve());
}
});
const await_ready = () => ready_promise;
const load_promise = new Promise(resolve => {
add_listener(win, 'load', () => resolve());
});
const await_load = () => load_promise;
const dom = arg => {
if (isInstanceOf(arg, dom)) {
return arg;
}
let els;
if (isStr(arg)) {
arg = arg.trim();
els = arg[0] === '<' ? parse_html(arg) : query_all(arg);
} else if (is_el_doc_win(arg)) {
els = [arg];
} else {
els = hasLength(arg) ? arg : [arg];
}
els = filter(els, is_el_doc_win);
return Object.assign(Object.create(dom.prototype), els, {length: els.length});
};
dom.prototype = {
constructor: dom,
each(fn) {
each(this, fn);
return this;
},
map(fn) {
return map(this, fn);
},
find(selector) {
return dom([].concat(...this.map(el => query_all(selector, el))));
},
on(type, fn) {
return this.each(el => add_listener(el, type, fn));
},
off(type, fn) {
return this.each(el => remove_listener(el, type, fn));
},
attr(key, value) {
if (value === undefined) {
return this.length ? this[0].getAttribute(key) : undefined;
}
return this.each(el => el.setAttribute(key, value));
},
rmAttr(key) {
return this.each(el => el.removeAttribute(key));
},
prop(key, value) {
if (value === undefined) {
return this.length ? this[0][key] : undefined;
}
return this.each(el => {el[key] = value;});
},
rmProp(key) {
return this.each(el => delete el[key]);
},
val(value) {
if (value === undefined) {
return this.length ? this[0].value : undefined;
}
return this.each(el => {
el.value = value;
});
},
html(str) {
if (str === undefined) {
return this.map(el => el.innerHTML).join('');
}
return this.each(el => {
el.innerHTML = str;
});
},
text(str) {
if (str === undefined) {
return this.map(el => el.textContent).join('');
}
return this.each(el => {
el.textContent = str;
});
},
clr() {
return this.html('');
},
rm() {
return this.each(el => {
const parent = el.parentNode;
if (parent) {
parent.removeChild(el);
}
});
},
rpl(arg) {
return this.each(el => {
const parent = el.parentNode;
if (parent) {
parent.replaceChild(dom(arg)[0], el);
}
});
},
app(arg) {
return this.each(el => {
dom(arg).each(child => el.appendChild(child));
});
},
appTo(arg) {
dom(arg).app(this);
return this;
},
pre(arg) {
return this.each(el => {
dom(arg).each(child => {
const firstChild = el.firstChild;
if (!firstChild) {
el.appendChild(child);
} else {
el.insertBefore(child, firstChild);
}
});
});
},
preTo(arg) {
dom(arg).pre(this);
return this;
},
cls(...names) {
if (!names.length) {
return this.length ? toArray(this[0].classList) : [];
}
this.each(el => {el.className = '';});
return this.addCls(...names);
},
hasCls(name) {
return toArray(this).every(el => el.classList.contains(name));
},
addCls(...names) {
return this.each(el => {
for (const name of names) {
el.classList.add(name);
}
});
},
rmCls(...names) {
return this.each(el => {
for (const name of names) {
el.classList.remove(name);
}
});
},
tglCls(...names) {
return this.each(el => {
for (const name of names) {
if (el.classList.contains(name)) {
el.classList.remove(name);
} else {
el.classList.add(name);
}
}
});
},
parent() {
return dom(this.map(el => el.parentNode));
},
children() {
return dom([].concat(...this.map(el => toArray(el.children))));
},
hide() {
return this.addCls('hidden');
},
show() {
return this.rmCls('hidden');
},
isHidden() {
return this.hasCls('hidden');
},
css(styles) {
return this.each(el => Object.assign(el.style, styles));
}
};
module.exports = {
awaitReady: await_ready,
awaitLoad: await_load,
dom
};