UNPKG

deleight

Version:

A library with 9 modules for writing more expressive web applications with traditional HTML, CSS and JavaScript.

304 lines (303 loc) 9.56 kB
"use strict"; /** * Objects that select and manipulate elements when their properties or * methods are accessed. * * @module */ Object.defineProperty(exports, "__esModule", { value: true }); exports.method = exports.MethodSelector = exports.attr = exports.AttrSelector = exports.member = exports.MemberSelector = exports.selector = exports.Selector = void 0; const selectorHandler = { get(target, p) { return target.get(p); }, set(target, p, value) { target.set(p, value); return true; }, deleteProperty(target, p) { target.delete(p); return true; } }; /** * Returns a selection object that lazily represents an element within the `treespace` element (or document). * Calling [get]{@link Selector#get} returns the specified element. * Calling {@link Selector#set} replaces the element and calling {@link Selector#delete} * removes it. * * Selector instances are used as the target of the proxy object returned by {@link selector} * * @example * import { Selector } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * <main> * <p>P in MAIN</p> * </main> * `; * const slct = new Selector(document.body); * console.log(slct.get('article').textContent); // I am an article * console.log(slct.get('main & p').textContent); // P in MAIN * */ class Selector { #proxy; constructor(treespace) { if (treespace) this.treespace = treespace; } get(key) { let element = this.treespace || document; if (typeof key === "number") return element.children[key >= 0 ? key : element.children.length + key]; if (typeof key !== 'string') return; // not handled here. const selectors = (typeof key === 'string' && key.indexOf('&') >= 0) ? key.split('&') : [key]; let nKey; for (let key of selectors) { key = key.trim(); nKey = parseInt(key); if (isNaN(nKey)) { element = element.querySelector(key); } else element = element.children[nKey >= 0 ? nKey : element.children.length + nKey]; } return element; } set(key, value) { const currentElement = this.get(key); if (currentElement) { if (value instanceof Array) currentElement.replaceWith(...value); else currentElement.replaceWith(value); } } delete(key) { const currentElement = this.get(key); if (currentElement) currentElement.remove(); } proxy() { if (!this.#proxy) this.#proxy = new Proxy(this, selectorHandler); return this.#proxy; } } exports.Selector = Selector; /** * Returns a proxy object that selects an element when a property is requested from it. * Setting a property will also replace the selected element and deleting * a property will remove the selected element. * * By default it uses `querySelector` for string keys and `children` for * number keys. * * @example * import { selector } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * `; * const slct = selector(document.body); * console.log(slct.article.textContent); // I am an article * * * @param treespace * @returns */ function selector(treespace) { return new Selector(treespace).proxy(); } exports.selector = selector; /** * Returns a selection object that lazily represents a property with the name within the `treespace` element (or document). * Calling [get]{@link MemberSelector#get} returns the property in the specified element. * Calling {@link MemberSelector#set} updates the property and calling {@link MemberSelector#delete} * deletes it. * * MemberSelector instances are used as the target of the proxy object returned by {@link member} * * @example * import { MemberSelector } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * `; * const slct = new MemberSelector('textContent', document.body); * console.log(slct.get('div')); // I am a div * */ class MemberSelector extends Selector { constructor(name, treespace) { super(treespace); this.name = name; } get(key) { return super.get(key)?.[this.name]; } set(key, value) { super.get(key)[this.name] = value; } delete(key) { delete super.get(key)[this.name]; } } exports.MemberSelector = MemberSelector; /** * Returns an object that lazily represents a property with the name within the `treespace` (or document). * Getting properties returns the property in the specified element and setting or deleting properties * updates or deletes the property correspondingly. * * @example * import { member } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * `; * const slct = member('textContent', document.body); * console.log(slct.div); // I am a div * * * @param name * @param treespace * @returns */ function member(name, treespace) { return new MemberSelector(name, treespace).proxy(); } exports.member = member; /** * Returns a selection object that lazily represents an attribute with the name within the `treespace` element (or document). * Calling [get]{@link AttrSelector#get} returns the attribute in the specified element. * Calling {@link AttrSelector#set} updates the attribute and calling {@link AttrSelector#delete} * removes it. * * AttrSelector instances are also used as the target of the proxy object returned by {@link attr} * * @example * import { attr } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p class="main">I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * `; * const slct = new AttrSelector('class', document.body); * console.log(slct.get('p')); // main * */ class AttrSelector extends MemberSelector { get(key) { return Selector.prototype.get.call(this, key)?.getAttribute(this.name); } set(key, value) { Selector.prototype.get.call(this, key)?.setAttribute(this.name, value); } delete(key) { Selector.prototype.get.call(this, key)?.removeAttribute(this.name); } } exports.AttrSelector = AttrSelector; /** * Returns an object that lazily represents an attribute with the name within the `treespace` element (or document). * Getting properties returns the attribute in the specified element and setting or deleting properties * updates or removes the attribute correspondingly. * * @example * import { attr } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p class="main">I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * `; * const slct = attr('class', document.body); * console.log(slct.p); // main * * * @param name * @param treespace * @returns */ function attr(name, treespace) { return new AttrSelector(name, treespace).proxy(); } exports.attr = attr; /** * Returns a selection object that lazily represents a method with the name within the `treespace` element (or document). * Invoking [call]{@link AttrSelector#get} will call the corresponding method on the * element selected with the `key` argument. * * This is used as the target of the proxy object returned by {@link method} * * @example * import { MethodSelector } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * `; * const slct = new MethodSelector('remove', document.body); * slct.call('section'); * console.log(document.querySelector('section')); // null * */ class MethodSelector extends Selector { #proxy; constructor(name, treespace) { super(treespace); this.name = name; } call(key, ...args) { return super.get(key)?.[this.name](...args); } proxy() { if (!this.#proxy) this.#proxy = new Proxy(this, methodSelectorHandler); return this.#proxy; } } exports.MethodSelector = MethodSelector; const methodSelectorHandler = { get(target, p) { return (...args) => target.call(p, ...args); } }; /** * Returns an object that lazily represents a method with the name within the `treespace` (or document). * Calling its methods calls the corresponding method in the specified element. * * @example * import { method } from 'deleight/proxy/selector'; * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section</section> * <article>I am an article</article> * `; * const slct = method('remove', document.body); * slct.section(); * console.log(document.querySelector('section')); // null * * * @param name * @param treespace * @returns */ function method(name, treespace) { return new MethodSelector(name, treespace).proxy(); } exports.method = method;