Socket
Socket
Sign inDemoInstall

deleight

Package Overview
Dependencies
Maintainers
0
Versions
61
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

deleight - npm Package Compare versions

Comparing version 4.1.1 to 4.1.2

377

dist/actribute/cjs/actribute.js

@@ -1,1 +0,376 @@

"use strict";const t=Symbol("Open component"),e=Symbol("Closed component");function r(t,e,r="."){const s=e.split(r);let o=t[s[0].trim()];for(let e=1;e<s.length&&("object"==typeof o||"function"==typeof o);e++)o=(t=o)[s[e].trim()];return o}exports.Actribute=class{registry={};openAttr;closedAttr;constructor(t){this.openAttr=t?.open||"o-pen",this.closedAttr=t?.closed||"c-losed"}register(t){return Object.assign(this.registry,t)&&this}process(r,s){let o,n,i,c;"string"==typeof r?n=r:r instanceof Element?o=r:r instanceof Array?c=r:"object"==typeof r&&(o=r.el,n=r.attr,i=r.rattr,c=r.ctx),o||(o=document.body),n||(n="c-"),i||(i="r-"),c||(c=[]),s||(s=new Map);const h=o.attributes,p=h.length;let l,f,a,u,y,m=!1,g=o.hasAttribute(this.openAttr),d=o.hasAttribute(this.closedAttr);for([u,l]of s.entries())u(o,l,...c);for(f=0;f<p;f++)if(l=h[f],l&&(l.name.startsWith(n)||l.name.startsWith(i))&&l.name!==this.openAttr&&l.name!==this.closedAttr){if(m=!0,a=l.name.substring(n.length),this.registry.hasOwnProperty(a))u=this.registry[a],l.name.startsWith(i)&&!s.has(u)&&s.set(u,l),y=u(o,l,...c);else{if(!this.registry.hasOwnProperty("*"))throw new Error(`The component "${a}" was not found in the registry.`);y=this.registry["*"](o,l,...c)}y===t?g=!0:y===e&&(d=!0)}if((!m||s.size||g)&&!d){let t=o.firstElementChild;const e=[];for(s.size&&e.push(s);t;)this.process({el:t,attr:n,rattr:i,ctx:c},...e),t=t.nextElementSibling}return this}},exports.closed=e,exports.join=function(t,e){return(r,s,...o)=>{let n;for(let e of t)n=e(r,s,...o);return e||n}},exports.open=t,exports.props=function(t,e,s){"string"!=typeof t&&(t=t.toString());const o=[],n=e.length;let i,c,h;for(i of(t=t.trim()).split(s||" "))if(i=i.trim(),""!==i){for(c=void 0,h=-1;void 0===c&&++h<n;)c=r(e[h],i);if(void 0===c)throw new TypeError(`The property "${i}" was not found in any of the sources.`);o.push(c)}return o};
'use strict';
/**
* This module exports the {@link Actribute} class which provides a structured way to attach behavior to HTML elements. The library
* enables us to attach meaning to attributes in markup. The main motivation for its original
* development was adding behavior to built-in elements without creating complexity and accessibility issues. However it can
* be used more generally to implement a component and/or reactive system based on attributes. This has more flexibility and
* composability than using tag names for components. It can also save a lot of typing when we use recursive components.
* Bear in mind this can lead to messy code if too much complex behavior is embedded
* in markup. Use with discretion.
*
* The attributes here name the components and are passed to them along with the element. Components can use the
* element, attribute and context they receive in any way appropriate for their functions. Simple components may
* use the attribute values literally or call {@link props()} with them to extract properties from objects (often context objects)
* which they then use for their operation. More complex components could even interprete the values
* as code (but this is not recommended).
*
* @example
* import { Actribute, props } from 'deleight/actribute';
*
* // initialize actribute:
* const act = new Actribute();
*
* // register components:
* act.register({
* comp1: (element, attr, singleContext) => element.textContent = attr.value,
* comp2: (element, attr, singleContext) => element.style.left = props(attr.value, [singleContext])
* });
*
*
* // process an element:
* document.body.innerHTML = `
* <header></header>
* <article c-comp1='I replace all the children here anyway' > <!-- using a raw value -->
* <p>[comp1] is not applied to me</p>
* <p>[comp1] is not applied to me</p>
* </article>
* <article r-comp2='a'> <!-- using a prop -->
* <p>[comp2] is applied to me!</p>
* <p>[comp2] is applied to me!/p>
* <p>[comp2] is not applied to me!</p>
* </article>
* `;
* const data = { a: '100px', b: 2, c: 3 };
* act.process([{el: document.body, ctx: data}]);
*
* // unregister a component:
* delete act.registry.comp2;
*
* @module
*/
/**
* A component can declare itself `open` by returning this symbol. This means
* that { @link Actribute#process } will process its children after it.
*/
const open = Symbol('Open component');
/**
* A component can also declare itself closed by returning this symbol. This means
* that { @link Actribute#process } will not process its children after it.
*/
const closed = Symbol('Closed component');
/**
* An Actribute class. Instances can be used to register components and process
* elements. This is almost like a custom elements registry.
*
* @example
* import { Actribute, props } from 'deleight/actribute';
* const act = new Actribute();
*
*/
class Actribute {
/**
* The object that holds all registered components. The keys are the
* component names and the values are the component functions.
*/
registry = {};
/**
* The attribute used to specify that the tree of an element with
* components is open to nested processing.
*/
openAttr;
/**
* The attribute used to specify that the tree of an element with
* components is not open to nested processing.
*/
closedAttr;
/**
*
* Construct a new Actribute instance.
*
* A component specifier is of the form [attrPrefix][componentName]="..."
*
* When a component specifier is encountered, the component's function will be
* invoked with the element and the matching attribute as arguments.
*
* The attribute can be string (where at least 1 property name is specified),
* or boolean (where no property is specified).
*
* An 'open' element means its children are processed after it and a `closed`
* element means they are not. Elements are 'open' by default when there are no
* matching components on them and 'closed' otherwise. The optional `init` object
* can be used to set the attribute names that are used to override this behavior.
* The default open attribute is `o-pen` and the one for closed is `c-losed`.
*
* @example
* import { Actribute } from 'deleight/actribute';
* const init = {open: 'o-pen', closed: 'c-losed'};
* const act = new Actribute(init);
*
* document.body.innerHTML = `
* <header></header>
* <article >
* <p>I am processed</p>
* <p>I am processed</p>
* </article>
* <article c-losed>
* <p>I am not processed</p>
* <p>I am not processed</p>
* <p>I am not processed</p>
* </article>
* `;
*
* act.process(document.body)
*
* @param {IActributeInit} init The value to assign to attrPrefix. Defaults to 'c-'
* @constructor
*/
constructor(init) {
this.openAttr = init?.open || 'o-pen';
this.closedAttr = init?.closed || 'c-losed';
}
/**
* Registers multiple components at once using an object that maps
* component names to component functions.
*
* @example
* import { Actribute } from 'deleight/actribute';
* const act = new Actribute();
* act.register({
* comp1: (element, attr, singleContext) => element.textContent = attr.value,
* comp2: (element, attr, singleContext) => element.style.left = props(attr.value, [singleContext])
* });
*
* @param registerMap
* @returns
*/
register(registerMap) {
return Object.assign(this.registry, registerMap) && this;
}
/**
* Recursively processes `options.el` (or `document.body` by default) to
* identify and apply components. Attributes with names starting with
* `options.attr` (or `c-` by default) or `options.rattr` (or `r-` by default)
* are treated as component specifiers.
*
* At elements where any components are encountered, the components
* are called with the element, the attribute and any specified
* context objects (`...(options.context || [])`).
*
* Where a component is encountered, decendants are not processed unless `this.open`
* attribute is present on the element. At elements without a component, the descendants
* are processed recursively, except `this.closed` boolean attribute is
* specified. These are supposed to echo the semantics of the Shadow DOM API.
*
* If a component is not found and a wild-card component is registered (with '*'),
* the widcard component is called instead.
*
* `options.attr` prefixes a component that is applied once while `options.rattr`
* prefixes one that is applied recursively on all descendant elements which are not
* within `closed` elements. Recursive attributes can save a lot of typing.
*
* Returns the same actribute to support call chaining.
*
* @example
* import { Actribute, props } from 'deleight/actribute';
* const act = new Actribute();
* act.register({
* comp1: (element, attr, singleContext) => element.textContent = attr.value,
* comp2: (element, attr, singleContext) => element.style.left = props(attr.value, [singleContext][0])
* });
* document.body.innerHTML = `
* <header></header>
* <article c-comp1='I replace all the children here anyway' > <!-- using a raw value -->
* <p>[comp1] is not applied to me</p>
* <p>[comp1] is not applied to me</p>
* </article>
* <article r-comp2='a'> <!-- using a prop -->
* <p>[comp2] is applied to me!</p>
* <p>[comp2] is applied to me!/p>
* <p>[comp2] is not applied to me!</p>
* </article>
* `;
* const data = { a: '100px', b: 2, c: 3 };
* act.process([{el: document.body, ctx: data}]);
*
* @param {IProcessOptions} [options]
* @returns {Actribute}
*/
process(options, recursiveComps) {
let element, attrPrefix, rattrPrefix, context;
if (typeof options === 'string')
attrPrefix = options;
else if (options instanceof Element)
element = options;
else if (options instanceof Array)
context = options;
else if (typeof options === "object") {
element = options.el;
attrPrefix = options.attr;
rattrPrefix = options.rattr;
context = options.ctx;
}
if (!element)
element = document.body;
if (!attrPrefix)
attrPrefix = 'c-';
if (!rattrPrefix)
rattrPrefix = 'r-';
if (!context)
context = [];
if (!recursiveComps)
recursiveComps = new Map();
const attrs = element.attributes, length = attrs.length;
let attr, i, comp, compF, processed = false, localOpen = element.hasAttribute(this.openAttr), localClosed = element.hasAttribute(this.closedAttr), compResult;
for ([compF, attr] of recursiveComps.entries()) {
compF(element, attr, ...context);
}
for (i = 0; i < length; i++) {
attr = attrs[i];
if (!attr)
continue; // attr can get deleted by a component!
if ((attr.name.startsWith(attrPrefix) || attr.name.startsWith(rattrPrefix)) &&
attr.name !== this.openAttr && attr.name !== this.closedAttr) {
processed = true;
comp = attr.name.substring(attrPrefix.length);
if (this.registry.hasOwnProperty(comp)) {
compF = this.registry[comp];
if (attr.name.startsWith(rattrPrefix) && !(recursiveComps.has(compF))) {
recursiveComps.set(compF, attr);
}
compResult = compF(element, attr, ...context);
}
else if (this.registry.hasOwnProperty('*')) {
compResult = this.registry['*'](element, attr, ...context);
}
else {
throw new Error(`The component "${comp}" was not found in the registry.`);
}
if (compResult === open)
localOpen = true;
else if (compResult === closed)
localClosed = true;
}
}
if ((!processed || recursiveComps.size || localOpen) && !localClosed) {
// local closed takes precedence over local open if they are both specified.
let child = element.firstElementChild;
const args = [];
if (recursiveComps.size)
args.push(recursiveComps);
while (child) {
this.process({
el: child, attr: attrPrefix, rattr: rattrPrefix, ctx: context
}, ...args);
child = child.nextElementSibling;
}
}
return this;
}
}
/**
* Searches the object for the given prop.
* prop can be specified as a dot-separated string so that
* the lookup scheme is similar to a nested property access.
* prop may contain any character except the propSep (space by default)
* passed to the `process` method.
*
* @param {T} obj
* @param {string} prop
*/
function get(obj, prop, propSep = '.') {
const props = prop.split(propSep);
let result = obj[props[0].trim()];
for (let i = 1; i < props.length &&
(typeof result === "object" || typeof result === "function"); i++) {
obj = result;
result = obj[props[i].trim()];
}
return result;
}
/**
* Obtain 1 or more properties from the specified sources. Specify the property names
* separated by a separator (`" "` by default). Returns an array of values for
* each specified property in the names. Each property is returned from the
* first object containing it.
*
* @example
* const fbProps = { f: 20, g: 7 };
* const mainProps = { a: 1, b: { c: 1, d: 2 }, e: 3, f: 100 };
* console.log(props('e f g', [mainProps, fbProps])); // [3, 100, 7]
*
* @param names
* @param scopes
* @param sep
* @returns
*/
function props(names, scopes, sep) {
if (typeof names !== 'string')
names = names.toString();
const results = [], length = scopes.length;
names = names.trim();
let name, val, i;
for (name of names.split(sep || ' ')) {
name = name.trim();
if (name === "")
continue; // just too much space between prop names/keys.
val = undefined;
i = -1;
while (val === undefined && ++i < length)
val = get(scopes[i], name);
if (val !== undefined)
results.push(val);
else {
throw new TypeError(`The property "${name}" was not found in any of the sources.`);
}
}
return results;
}
/**
* Join multiple components, so that they can be applied as one component.
* Can reduce repetitive markup in many cases. If you specify a returnValue
* the new component will return it, else it will return the return value of
* the last joined component.
*
* @example
*
* import ( join, Actribute ) from 'deleight/actribute';
*
* document.body.innerHTML = `
* <div>I am not a component</div>
* <main c-comp><p>I am inside a joined component</p></main>
* <section>I am not a component</section>
* <article>I am not a component</article>
* `;
*
* const act = new Actribute();
* const comps = [];
* const baseComp = (node) => comps.push(node.tagName);
* const comp = join([baseComp, baseComp, baseComp]);
* act.register({ comp });
*
* act.process(document.body);
*
* console.log(comps.length); // 3
* console.log(comps[0]); // "MAIN"
* console.log(comps[1]); // "MAIN"
* console.log(comps[2]); // "MAIN"
*
* @param components
* @param returnValue
* @returns
*/
function join(components, returnValue) {
return (element, attr, ...context) => {
let lastValue;
for (let comp of components)
lastValue = comp(element, attr, ...context);
return returnValue || lastValue;
};
}
exports.Actribute = Actribute;
exports.closed = closed;
exports.join = join;
exports.open = open;
exports.props = props;

@@ -1,1 +0,370 @@

const t=Symbol("Open component"),e=Symbol("Closed component");class r{registry={};openAttr;closedAttr;constructor(t){this.openAttr=t?.open||"o-pen",this.closedAttr=t?.closed||"c-losed"}register(t){return Object.assign(this.registry,t)&&this}process(r,s){let o,n,i,h;"string"==typeof r?n=r:r instanceof Element?o=r:r instanceof Array?h=r:"object"==typeof r&&(o=r.el,n=r.attr,i=r.rattr,h=r.ctx),o||(o=document.body),n||(n="c-"),i||(i="r-"),h||(h=[]),s||(s=new Map);const c=o.attributes,l=c.length;let f,a,p,u,y,m=!1,g=o.hasAttribute(this.openAttr),d=o.hasAttribute(this.closedAttr);for([u,f]of s.entries())u(o,f,...h);for(a=0;a<l;a++)if(f=c[a],f&&(f.name.startsWith(n)||f.name.startsWith(i))&&f.name!==this.openAttr&&f.name!==this.closedAttr){if(m=!0,p=f.name.substring(n.length),this.registry.hasOwnProperty(p))u=this.registry[p],f.name.startsWith(i)&&!s.has(u)&&s.set(u,f),y=u(o,f,...h);else{if(!this.registry.hasOwnProperty("*"))throw new Error(`The component "${p}" was not found in the registry.`);y=this.registry["*"](o,f,...h)}y===t?g=!0:y===e&&(d=!0)}if((!m||s.size||g)&&!d){let t=o.firstElementChild;const e=[];for(s.size&&e.push(s);t;)this.process({el:t,attr:n,rattr:i,ctx:h},...e),t=t.nextElementSibling}return this}}function s(t,e,r="."){const s=e.split(r);let o=t[s[0].trim()];for(let e=1;e<s.length&&("object"==typeof o||"function"==typeof o);e++)o=(t=o)[s[e].trim()];return o}function o(t,e,r){"string"!=typeof t&&(t=t.toString());const o=[],n=e.length;let i,h,c;for(i of(t=t.trim()).split(r||" "))if(i=i.trim(),""!==i){for(h=void 0,c=-1;void 0===h&&++c<n;)h=s(e[c],i);if(void 0===h)throw new TypeError(`The property "${i}" was not found in any of the sources.`);o.push(h)}return o}function n(t,e){return(r,s,...o)=>{let n;for(let e of t)n=e(r,s,...o);return e||n}}export{r as Actribute,e as closed,n as join,t as open,o as props};
/**
* This module exports the {@link Actribute} class which provides a structured way to attach behavior to HTML elements. The library
* enables us to attach meaning to attributes in markup. The main motivation for its original
* development was adding behavior to built-in elements without creating complexity and accessibility issues. However it can
* be used more generally to implement a component and/or reactive system based on attributes. This has more flexibility and
* composability than using tag names for components. It can also save a lot of typing when we use recursive components.
* Bear in mind this can lead to messy code if too much complex behavior is embedded
* in markup. Use with discretion.
*
* The attributes here name the components and are passed to them along with the element. Components can use the
* element, attribute and context they receive in any way appropriate for their functions. Simple components may
* use the attribute values literally or call {@link props()} with them to extract properties from objects (often context objects)
* which they then use for their operation. More complex components could even interprete the values
* as code (but this is not recommended).
*
* @example
* import { Actribute, props } from 'deleight/actribute';
*
* // initialize actribute:
* const act = new Actribute();
*
* // register components:
* act.register({
* comp1: (element, attr, singleContext) => element.textContent = attr.value,
* comp2: (element, attr, singleContext) => element.style.left = props(attr.value, [singleContext])
* });
*
*
* // process an element:
* document.body.innerHTML = `
* <header></header>
* <article c-comp1='I replace all the children here anyway' > <!-- using a raw value -->
* <p>[comp1] is not applied to me</p>
* <p>[comp1] is not applied to me</p>
* </article>
* <article r-comp2='a'> <!-- using a prop -->
* <p>[comp2] is applied to me!</p>
* <p>[comp2] is applied to me!/p>
* <p>[comp2] is not applied to me!</p>
* </article>
* `;
* const data = { a: '100px', b: 2, c: 3 };
* act.process([{el: document.body, ctx: data}]);
*
* // unregister a component:
* delete act.registry.comp2;
*
* @module
*/
/**
* A component can declare itself `open` by returning this symbol. This means
* that { @link Actribute#process } will process its children after it.
*/
const open = Symbol('Open component');
/**
* A component can also declare itself closed by returning this symbol. This means
* that { @link Actribute#process } will not process its children after it.
*/
const closed = Symbol('Closed component');
/**
* An Actribute class. Instances can be used to register components and process
* elements. This is almost like a custom elements registry.
*
* @example
* import { Actribute, props } from 'deleight/actribute';
* const act = new Actribute();
*
*/
class Actribute {
/**
* The object that holds all registered components. The keys are the
* component names and the values are the component functions.
*/
registry = {};
/**
* The attribute used to specify that the tree of an element with
* components is open to nested processing.
*/
openAttr;
/**
* The attribute used to specify that the tree of an element with
* components is not open to nested processing.
*/
closedAttr;
/**
*
* Construct a new Actribute instance.
*
* A component specifier is of the form [attrPrefix][componentName]="..."
*
* When a component specifier is encountered, the component's function will be
* invoked with the element and the matching attribute as arguments.
*
* The attribute can be string (where at least 1 property name is specified),
* or boolean (where no property is specified).
*
* An 'open' element means its children are processed after it and a `closed`
* element means they are not. Elements are 'open' by default when there are no
* matching components on them and 'closed' otherwise. The optional `init` object
* can be used to set the attribute names that are used to override this behavior.
* The default open attribute is `o-pen` and the one for closed is `c-losed`.
*
* @example
* import { Actribute } from 'deleight/actribute';
* const init = {open: 'o-pen', closed: 'c-losed'};
* const act = new Actribute(init);
*
* document.body.innerHTML = `
* <header></header>
* <article >
* <p>I am processed</p>
* <p>I am processed</p>
* </article>
* <article c-losed>
* <p>I am not processed</p>
* <p>I am not processed</p>
* <p>I am not processed</p>
* </article>
* `;
*
* act.process(document.body)
*
* @param {IActributeInit} init The value to assign to attrPrefix. Defaults to 'c-'
* @constructor
*/
constructor(init) {
this.openAttr = init?.open || 'o-pen';
this.closedAttr = init?.closed || 'c-losed';
}
/**
* Registers multiple components at once using an object that maps
* component names to component functions.
*
* @example
* import { Actribute } from 'deleight/actribute';
* const act = new Actribute();
* act.register({
* comp1: (element, attr, singleContext) => element.textContent = attr.value,
* comp2: (element, attr, singleContext) => element.style.left = props(attr.value, [singleContext])
* });
*
* @param registerMap
* @returns
*/
register(registerMap) {
return Object.assign(this.registry, registerMap) && this;
}
/**
* Recursively processes `options.el` (or `document.body` by default) to
* identify and apply components. Attributes with names starting with
* `options.attr` (or `c-` by default) or `options.rattr` (or `r-` by default)
* are treated as component specifiers.
*
* At elements where any components are encountered, the components
* are called with the element, the attribute and any specified
* context objects (`...(options.context || [])`).
*
* Where a component is encountered, decendants are not processed unless `this.open`
* attribute is present on the element. At elements without a component, the descendants
* are processed recursively, except `this.closed` boolean attribute is
* specified. These are supposed to echo the semantics of the Shadow DOM API.
*
* If a component is not found and a wild-card component is registered (with '*'),
* the widcard component is called instead.
*
* `options.attr` prefixes a component that is applied once while `options.rattr`
* prefixes one that is applied recursively on all descendant elements which are not
* within `closed` elements. Recursive attributes can save a lot of typing.
*
* Returns the same actribute to support call chaining.
*
* @example
* import { Actribute, props } from 'deleight/actribute';
* const act = new Actribute();
* act.register({
* comp1: (element, attr, singleContext) => element.textContent = attr.value,
* comp2: (element, attr, singleContext) => element.style.left = props(attr.value, [singleContext][0])
* });
* document.body.innerHTML = `
* <header></header>
* <article c-comp1='I replace all the children here anyway' > <!-- using a raw value -->
* <p>[comp1] is not applied to me</p>
* <p>[comp1] is not applied to me</p>
* </article>
* <article r-comp2='a'> <!-- using a prop -->
* <p>[comp2] is applied to me!</p>
* <p>[comp2] is applied to me!/p>
* <p>[comp2] is not applied to me!</p>
* </article>
* `;
* const data = { a: '100px', b: 2, c: 3 };
* act.process([{el: document.body, ctx: data}]);
*
* @param {IProcessOptions} [options]
* @returns {Actribute}
*/
process(options, recursiveComps) {
let element, attrPrefix, rattrPrefix, context;
if (typeof options === 'string')
attrPrefix = options;
else if (options instanceof Element)
element = options;
else if (options instanceof Array)
context = options;
else if (typeof options === "object") {
element = options.el;
attrPrefix = options.attr;
rattrPrefix = options.rattr;
context = options.ctx;
}
if (!element)
element = document.body;
if (!attrPrefix)
attrPrefix = 'c-';
if (!rattrPrefix)
rattrPrefix = 'r-';
if (!context)
context = [];
if (!recursiveComps)
recursiveComps = new Map();
const attrs = element.attributes, length = attrs.length;
let attr, i, comp, compF, processed = false, localOpen = element.hasAttribute(this.openAttr), localClosed = element.hasAttribute(this.closedAttr), compResult;
for ([compF, attr] of recursiveComps.entries()) {
compF(element, attr, ...context);
}
for (i = 0; i < length; i++) {
attr = attrs[i];
if (!attr)
continue; // attr can get deleted by a component!
if ((attr.name.startsWith(attrPrefix) || attr.name.startsWith(rattrPrefix)) &&
attr.name !== this.openAttr && attr.name !== this.closedAttr) {
processed = true;
comp = attr.name.substring(attrPrefix.length);
if (this.registry.hasOwnProperty(comp)) {
compF = this.registry[comp];
if (attr.name.startsWith(rattrPrefix) && !(recursiveComps.has(compF))) {
recursiveComps.set(compF, attr);
}
compResult = compF(element, attr, ...context);
}
else if (this.registry.hasOwnProperty('*')) {
compResult = this.registry['*'](element, attr, ...context);
}
else {
throw new Error(`The component "${comp}" was not found in the registry.`);
}
if (compResult === open)
localOpen = true;
else if (compResult === closed)
localClosed = true;
}
}
if ((!processed || recursiveComps.size || localOpen) && !localClosed) {
// local closed takes precedence over local open if they are both specified.
let child = element.firstElementChild;
const args = [];
if (recursiveComps.size)
args.push(recursiveComps);
while (child) {
this.process({
el: child, attr: attrPrefix, rattr: rattrPrefix, ctx: context
}, ...args);
child = child.nextElementSibling;
}
}
return this;
}
}
/**
* Searches the object for the given prop.
* prop can be specified as a dot-separated string so that
* the lookup scheme is similar to a nested property access.
* prop may contain any character except the propSep (space by default)
* passed to the `process` method.
*
* @param {T} obj
* @param {string} prop
*/
function get(obj, prop, propSep = '.') {
const props = prop.split(propSep);
let result = obj[props[0].trim()];
for (let i = 1; i < props.length &&
(typeof result === "object" || typeof result === "function"); i++) {
obj = result;
result = obj[props[i].trim()];
}
return result;
}
/**
* Obtain 1 or more properties from the specified sources. Specify the property names
* separated by a separator (`" "` by default). Returns an array of values for
* each specified property in the names. Each property is returned from the
* first object containing it.
*
* @example
* const fbProps = { f: 20, g: 7 };
* const mainProps = { a: 1, b: { c: 1, d: 2 }, e: 3, f: 100 };
* console.log(props('e f g', [mainProps, fbProps])); // [3, 100, 7]
*
* @param names
* @param scopes
* @param sep
* @returns
*/
function props(names, scopes, sep) {
if (typeof names !== 'string')
names = names.toString();
const results = [], length = scopes.length;
names = names.trim();
let name, val, i;
for (name of names.split(sep || ' ')) {
name = name.trim();
if (name === "")
continue; // just too much space between prop names/keys.
val = undefined;
i = -1;
while (val === undefined && ++i < length)
val = get(scopes[i], name);
if (val !== undefined)
results.push(val);
else {
throw new TypeError(`The property "${name}" was not found in any of the sources.`);
}
}
return results;
}
/**
* Join multiple components, so that they can be applied as one component.
* Can reduce repetitive markup in many cases. If you specify a returnValue
* the new component will return it, else it will return the return value of
* the last joined component.
*
* @example
*
* import ( join, Actribute ) from 'deleight/actribute';
*
* document.body.innerHTML = `
* <div>I am not a component</div>
* <main c-comp><p>I am inside a joined component</p></main>
* <section>I am not a component</section>
* <article>I am not a component</article>
* `;
*
* const act = new Actribute();
* const comps = [];
* const baseComp = (node) => comps.push(node.tagName);
* const comp = join([baseComp, baseComp, baseComp]);
* act.register({ comp });
*
* act.process(document.body);
*
* console.log(comps.length); // 3
* console.log(comps[0]); // "MAIN"
* console.log(comps[1]); // "MAIN"
* console.log(comps[2]); // "MAIN"
*
* @param components
* @param returnValue
* @returns
*/
function join(components, returnValue) {
return (element, attr, ...context) => {
let lastValue;
for (let comp of components)
lastValue = comp(element, attr, ...context);
return returnValue || lastValue;
};
}
export { Actribute, closed, join, open, props };

@@ -1,1 +0,194 @@

"use strict";function e(e,t,n){const r=e.split(",").map((e=>e.trim())),o=[];let l;for(let e of Array.from(t.sheet?.cssRules||[]))for(l of r)if(e.cssText.startsWith(l)&&(o.push(e),n))return o;return o}function t(t,n){return e(t,n,!0)[0]}function n(n,o,l,s){let c;o||(o=document.body),c=s?o instanceof HTMLStyleElement?e=>t(e,o):o.querySelector.bind(o):o instanceof HTMLStyleElement?t=>e(t,o):o.querySelectorAll.bind(o);const f=o instanceof HTMLStyleElement?o.sheet?.cssRules:o.children;let i;for(let[e,t]of Object.entries(n))i=parseInt(e),isNaN(i)?r(c(e),t,l):r(f[i>=0?i:f.length+i],t,l)}function r(e,t,r,o){let l,s;for(s of((e instanceof Element||e instanceof CSSRule)&&(e=[e]),t instanceof Array||(t=[t]),t))if(s instanceof Function)if(r)for(l of e)s(l);else s(...e);else for(l of e)n(s,l,r,o)}exports.apply=n,exports.applyTo=r,exports.parentSelector=function(e,t){let n=e.parentElement;for(;n&&!n.matches(t);)n=n.parentElement;return n},exports.querySelectors=function(e,t){return t||(t=document.body),"string"==typeof e&&(e=e.split(",")),e.map((e=>t.querySelector(e.trim())))},exports.ruleSelector=t,exports.ruleSelectorAll=e;
'use strict';
/**
* This module exports the {@link apply}, {@link applyTo}, {@link parentSelector} {@link ruleSelector},
* {@link ruleSelectorAll} and {@link querySelectors} for succinctly selecting and operating on DOM elements and CSSOM rules.
*
* @module
*/
/**
* Functions similarly to querySelectorAll, but for selecting style rules in
* a CSS stylesheet object. All rules that start with any of the selectors are
* selected.
* @example
* import { ruleSelectorAll } from 'deleight/appliance';
* const firstSpanRule = ruleSelectorAll('span', document.getElementsByTagName('style')[0], true)[0];
*
* @param {string} selectors
* @param {HTMLStyleElement} styleElement
* @param {boolean} [firstOnly]
* @returns {Array<CSSRule>}
*/
function ruleSelectorAll(selectors, styleElement, firstOnly) {
const arrSelectors = selectors.split(",").map((item) => item.trim());
const result = [];
let selector;
for (let rule of Array.from(styleElement.sheet?.cssRules || [])) {
for (selector of arrSelectors) {
if (rule.cssText.startsWith(selector)) {
result.push(rule);
if (firstOnly)
return result;
}
}
}
return result;
}
/**
* Similar to querySelector in the same way ruleSelectorAll is similar to
* querySelectorAll.
* @example
* import { ruleSelector } from 'deleight/appliance';
* const firstSpanRule = ruleSelector('span', document.getElementsByTagName('style')[0])
*
*
* @param {string} selectors
* @param {HTMLStyleElement} styleElement
* @returns {CSSRule}
*/
function ruleSelector(selectors, styleElement) {
return ruleSelectorAll(selectors, styleElement, true)[0];
}
/**
* Return the first ancestor that matches the selector.
* @example
* import { parentSelector } from 'deleight/appliance';
* const removeListener = (e) => {
* table.removeChild(component.beforeRemove(parentSelector(e.target, 'tr')));
* };
*
* @param {Node} node
* @param {string} selector
* @returns {Element}
*/
function parentSelector(node, selector) {
let parent = node.parentElement;
while (parent && !parent.matches(selector))
parent = parent.parentElement;
return parent;
}
/**
* Selects the first elements corresponding to each of the selector args.
* This calls qcontainingElement.uerySelector on each of the selectors,
* so that only first elements are selected. It is different from
* querySelectorAll which will select *all* elements.
*
* The containing element defaults to document.body.
*
* @example
* import { select } from 'deleight/appliance'
* const [firstDiv, firstP, firstSpan] = select('div, p, span');
*
*
* @param { string[] } selectors
* @param { Element } [containingElement]
*
*/
function querySelectors(selectors, containingElement) {
if (!containingElement)
containingElement = document.body;
if (typeof selectors === 'string')
selectors = selectors.split(',');
return selectors.map(s => containingElement.querySelector(s.trim()));
}
/**
* Select the elements matching the keys in applyMap and run the functions given by the values over them.
* This eliminates the many calls to querySelectorAll, which is quite verbose.
*
* If a key is a number instead of
* a string, the child element at the index is selected instead. Negative indices can also be used to count from
* the end backwards.
*
* If a value in `applyMap` is an object but not an array, apply is called recursively on all selected
* elements for it with the object used as the applyMap.
*
* Without the third argument (`atomic`), all selected elements are
* passed to the functions at once. With the argument given as a truthy value,
* the elements are passed one by one, so that the behavior is almost like that
* of web components.
*
* A 4th argument (`selectFirst`) may also be specified to limit each selection to only the first matching
* elements. In this case (and in all cases where there is only 1 match), the value of `atomic` will be irrelevant.
*
* @example
* import { apply } from 'deleight/appliance';
* apply({
* main: main => {
* const newContent = [...range(101, 120)].map(i => `My index is now ${i}`);
* const lastChildren = Array.from(main.children).map(c => c.lastElementChild);
* set(lastChildren, {textContent: newContent});
* }
* });
*
* @param {IApplyMap } applyMap
* @param {HTMLElement} [containerElement]
* @param {boolean|number} [atomic]
* @param {boolean|number} [selectFirst]
*/
function apply(applyMap, containerElement, atomic, selectFirst) {
if (!containerElement)
containerElement = document.body;
let selectorAll;
if (!selectFirst) {
selectorAll =
containerElement instanceof HTMLStyleElement
? (selectors) => ruleSelectorAll(selectors, containerElement)
: containerElement.querySelectorAll.bind(containerElement);
}
else {
selectorAll =
containerElement instanceof HTMLStyleElement
? (selectors) => ruleSelector(selectors, containerElement)
: containerElement.querySelector.bind(containerElement);
}
const children = containerElement instanceof HTMLStyleElement ? containerElement.sheet?.cssRules : containerElement.children;
let index;
for (let [selectors, functions] of Object.entries(applyMap)) {
index = parseInt(selectors);
if (isNaN(index))
applyTo(selectorAll(selectors), functions, atomic);
else
applyTo(children[index >= 0 ? index : children.length + index], functions, atomic);
}
}
/**
* Applies the given functions to the specified elements (or CSS rules).
*
* asComponent specifies whether the functions should be applied to each
* element. If falsy/not specified, all the elements are passed to the functions
* at once.
* @example
* import { applyTo } from 'deleight/appliance';
* applyTo(Array.from(document.body.children), (...bodyChildren) => console.log(bodyChildren.length));
*
* @param {(Element|CSSRule)[]} elements
* @param {IComponents} components
* @param {boolean|undefined} [atomic]
*/
function applyTo(elements, components, atomic, selectFirst) {
let element, comp;
if (elements instanceof Element || elements instanceof CSSRule)
elements = [elements];
if (!(components instanceof Array))
components = [components];
for (comp of components) {
if (comp instanceof Function) {
if (atomic)
for (element of elements)
comp(element);
else
comp(...elements);
}
else {
for (element of elements)
apply(comp, element, atomic, selectFirst);
}
}
}
exports.apply = apply;
exports.applyTo = applyTo;
exports.parentSelector = parentSelector;
exports.querySelectors = querySelectors;
exports.ruleSelector = ruleSelector;
exports.ruleSelectorAll = ruleSelectorAll;

@@ -1,1 +0,187 @@

function e(e,t,n){const o=e.split(",").map((e=>e.trim())),r=[];let f;for(let e of Array.from(t.sheet?.cssRules||[]))for(f of o)if(e.cssText.startsWith(f)&&(r.push(e),n))return r;return r}function t(t,n){return e(t,n,!0)[0]}function n(e,t){let n=e.parentElement;for(;n&&!n.matches(t);)n=n.parentElement;return n}function o(e,t){return t||(t=document.body),"string"==typeof e&&(e=e.split(",")),e.map((e=>t.querySelector(e.trim())))}function r(n,o,r,s){let l;o||(o=document.body),l=s?o instanceof HTMLStyleElement?e=>t(e,o):o.querySelector.bind(o):o instanceof HTMLStyleElement?t=>e(t,o):o.querySelectorAll.bind(o);const i=o instanceof HTMLStyleElement?o.sheet?.cssRules:o.children;let c;for(let[e,t]of Object.entries(n))c=parseInt(e),isNaN(c)?f(l(e),t,r):f(i[c>=0?c:i.length+c],t,r)}function f(e,t,n,o){let f,s;for(s of((e instanceof Element||e instanceof CSSRule)&&(e=[e]),t instanceof Array||(t=[t]),t))if(s instanceof Function)if(n)for(f of e)s(f);else s(...e);else for(f of e)r(s,f,n,o)}export{r as apply,f as applyTo,n as parentSelector,o as querySelectors,t as ruleSelector,e as ruleSelectorAll};
/**
* This module exports the {@link apply}, {@link applyTo}, {@link parentSelector} {@link ruleSelector},
* {@link ruleSelectorAll} and {@link querySelectors} for succinctly selecting and operating on DOM elements and CSSOM rules.
*
* @module
*/
/**
* Functions similarly to querySelectorAll, but for selecting style rules in
* a CSS stylesheet object. All rules that start with any of the selectors are
* selected.
* @example
* import { ruleSelectorAll } from 'deleight/appliance';
* const firstSpanRule = ruleSelectorAll('span', document.getElementsByTagName('style')[0], true)[0];
*
* @param {string} selectors
* @param {HTMLStyleElement} styleElement
* @param {boolean} [firstOnly]
* @returns {Array<CSSRule>}
*/
function ruleSelectorAll(selectors, styleElement, firstOnly) {
const arrSelectors = selectors.split(",").map((item) => item.trim());
const result = [];
let selector;
for (let rule of Array.from(styleElement.sheet?.cssRules || [])) {
for (selector of arrSelectors) {
if (rule.cssText.startsWith(selector)) {
result.push(rule);
if (firstOnly)
return result;
}
}
}
return result;
}
/**
* Similar to querySelector in the same way ruleSelectorAll is similar to
* querySelectorAll.
* @example
* import { ruleSelector } from 'deleight/appliance';
* const firstSpanRule = ruleSelector('span', document.getElementsByTagName('style')[0])
*
*
* @param {string} selectors
* @param {HTMLStyleElement} styleElement
* @returns {CSSRule}
*/
function ruleSelector(selectors, styleElement) {
return ruleSelectorAll(selectors, styleElement, true)[0];
}
/**
* Return the first ancestor that matches the selector.
* @example
* import { parentSelector } from 'deleight/appliance';
* const removeListener = (e) => {
* table.removeChild(component.beforeRemove(parentSelector(e.target, 'tr')));
* };
*
* @param {Node} node
* @param {string} selector
* @returns {Element}
*/
function parentSelector(node, selector) {
let parent = node.parentElement;
while (parent && !parent.matches(selector))
parent = parent.parentElement;
return parent;
}
/**
* Selects the first elements corresponding to each of the selector args.
* This calls qcontainingElement.uerySelector on each of the selectors,
* so that only first elements are selected. It is different from
* querySelectorAll which will select *all* elements.
*
* The containing element defaults to document.body.
*
* @example
* import { select } from 'deleight/appliance'
* const [firstDiv, firstP, firstSpan] = select('div, p, span');
*
*
* @param { string[] } selectors
* @param { Element } [containingElement]
*
*/
function querySelectors(selectors, containingElement) {
if (!containingElement)
containingElement = document.body;
if (typeof selectors === 'string')
selectors = selectors.split(',');
return selectors.map(s => containingElement.querySelector(s.trim()));
}
/**
* Select the elements matching the keys in applyMap and run the functions given by the values over them.
* This eliminates the many calls to querySelectorAll, which is quite verbose.
*
* If a key is a number instead of
* a string, the child element at the index is selected instead. Negative indices can also be used to count from
* the end backwards.
*
* If a value in `applyMap` is an object but not an array, apply is called recursively on all selected
* elements for it with the object used as the applyMap.
*
* Without the third argument (`atomic`), all selected elements are
* passed to the functions at once. With the argument given as a truthy value,
* the elements are passed one by one, so that the behavior is almost like that
* of web components.
*
* A 4th argument (`selectFirst`) may also be specified to limit each selection to only the first matching
* elements. In this case (and in all cases where there is only 1 match), the value of `atomic` will be irrelevant.
*
* @example
* import { apply } from 'deleight/appliance';
* apply({
* main: main => {
* const newContent = [...range(101, 120)].map(i => `My index is now ${i}`);
* const lastChildren = Array.from(main.children).map(c => c.lastElementChild);
* set(lastChildren, {textContent: newContent});
* }
* });
*
* @param {IApplyMap } applyMap
* @param {HTMLElement} [containerElement]
* @param {boolean|number} [atomic]
* @param {boolean|number} [selectFirst]
*/
function apply(applyMap, containerElement, atomic, selectFirst) {
if (!containerElement)
containerElement = document.body;
let selectorAll;
if (!selectFirst) {
selectorAll =
containerElement instanceof HTMLStyleElement
? (selectors) => ruleSelectorAll(selectors, containerElement)
: containerElement.querySelectorAll.bind(containerElement);
}
else {
selectorAll =
containerElement instanceof HTMLStyleElement
? (selectors) => ruleSelector(selectors, containerElement)
: containerElement.querySelector.bind(containerElement);
}
const children = containerElement instanceof HTMLStyleElement ? containerElement.sheet?.cssRules : containerElement.children;
let index;
for (let [selectors, functions] of Object.entries(applyMap)) {
index = parseInt(selectors);
if (isNaN(index))
applyTo(selectorAll(selectors), functions, atomic);
else
applyTo(children[index >= 0 ? index : children.length + index], functions, atomic);
}
}
/**
* Applies the given functions to the specified elements (or CSS rules).
*
* asComponent specifies whether the functions should be applied to each
* element. If falsy/not specified, all the elements are passed to the functions
* at once.
* @example
* import { applyTo } from 'deleight/appliance';
* applyTo(Array.from(document.body.children), (...bodyChildren) => console.log(bodyChildren.length));
*
* @param {(Element|CSSRule)[]} elements
* @param {IComponents} components
* @param {boolean|undefined} [atomic]
*/
function applyTo(elements, components, atomic, selectFirst) {
let element, comp;
if (elements instanceof Element || elements instanceof CSSRule)
elements = [elements];
if (!(components instanceof Array))
components = [components];
for (comp of components) {
if (comp instanceof Function) {
if (atomic)
for (element of elements)
comp(element);
else
comp(...elements);
}
else {
for (element of elements)
apply(comp, element, atomic, selectFirst);
}
}
}
export { apply, applyTo, parentSelector, querySelectors, ruleSelector, ruleSelectorAll };

@@ -1,1 +0,12 @@

"use strict";var e=require("apption");Object.keys(e).forEach((function(t){"default"===t||Object.prototype.hasOwnProperty.call(exports,t)||Object.defineProperty(exports,t,{enumerable:!0,get:function(){return e[t]}})}));
'use strict';
var apption = require('apption');
Object.keys(apption).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return apption[k]; }
});
});

2

dist/apption/esm/apption.js

@@ -1,1 +0,1 @@

export*from"apption";
export * from 'apption';

@@ -1,1 +0,268 @@

"use strict";async function e(e,...t){const n=[];for(let[e,r]of Array.from(t.entries()))r instanceof Promise?n.push(r):n.push(Promise.resolve(r));const r=await Promise.all(n);return r.map(((t,n)=>`${e[n]}${t}`)).join("")+e[r.length]}function t(e){return new Proxy(e,new n)}class n{children={};get(e,n){if(this.children.hasOwnProperty(n))return this.children[n];const r=e[n];return this.children[n]="string"==typeof r?r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"):"object"==typeof r?t(r):r}}exports.asyncTemplate=function(t,n,r){if(n||(n=["arg"]),r||(r="T"),n.includes(r))throw new Error(`The tag name ${r} clashes with the name of one of the arguments. \n Please change the tag name or the argument name to resolve this.`);const o=Function(r,...n,`return ${r}\`${t}\`;`);return(...t)=>o(e,...t)},exports.asyncTemplates=function(t,n,r,o){if(n||(n=[]),r||(r="item"),o||(o="T"),r===o)throw new Error(`The tag name ${o} is the same as the item name. \n Please change the tag name or the item name to resolve this.`);if(n.includes(o))throw new Error(`The tag name ${o} clashes with the name of one of the arguments. \n Please change the tag name or the argument name to resolve this.`);const a=Function(`\n function* gen(${o}, arr, ${n.join(", ")}) {\n for (let ${r} of arr) yield ${o}\`${t}\`;\n }\n return gen;`)();return(t,...n)=>a(e,t,...n)},exports.createFragment=function(e){const t=document.createElement("template");t.innerHTML=e;let n=t.content;return 1===n.children.length?n.children[0]:n},exports.elements=function*(e){for(let t of e.replace(",","").split(" "))yield document.createElement(t.trim())},exports.esc=t,exports.get=async function(e,t,n){let r=fetch(e,n).then((e=>e.text()));return t&&(r=r.catch((e=>""))),r},exports.tag=e,exports.template=function(e,t){return t||(t=["arg"]),Function(...t,`return \`${e}\`;`)},exports.templates=function(e,t,n){return t||(t=[]),n||(n="item"),Function(`\n function* gen(arr, ${t.join(", ")}) {\n for (let ${n} of arr) yield \`${e}\`;\n }\n return gen;`)()};
'use strict';
/**
* This module exports primitives for building DOM from text.
*
* @module
*/
/**
* A template tag that will resolve only after all
* interpolated promises have been resolved, finally returning the
* intended string.
*
* @example
* import { tag } from 'deleight/apriori';
* const t = tag`I will wait for this ${Promise.resolve("promise")}!!!`
* // t === 'I will wait for this promise!!!'
*
* @param {Array<string>} strings
* @param {...any} expressions
* @returns {Promise<string>}
*/
async function tag(strings, ...expressions) {
const promiseExpressions = [];
for (let [i, exp] of Array.from(expressions.entries())) {
if (exp instanceof Promise)
promiseExpressions.push(exp);
else
promiseExpressions.push(Promise.resolve(exp));
}
const resolvedExpressions = await Promise.all(promiseExpressions);
return (resolvedExpressions.map((exp, i) => `${strings[i]}${exp}`).join("") +
strings[resolvedExpressions.length]);
}
/**
* Effectively creates a template literal out of an existing template string and wraps it in a function
* which can be called multiple times to 'render' the template with the given arguments.
*
* @example
* import { template } from 'deleight/apriori';
* const t = template('I will render this ${"guy"} immediately!!!')();
* // t === 'I will render this guy immediately!!!'
*
* @param {string} templateStr the template string
* @param {string[]} argNames The names of the parameters of the returned function (which can be 'seen' inside the template string).
* Defaults to: ['arg'].
* @returns {(...any): string}
*/
function template(templateStr, argNames) {
if (!argNames)
argNames = ['arg'];
return Function(...argNames, `return \`${templateStr}\`;`);
}
/**
* Similar to template but the built template is also 'promise-aware' and will allow them to resolve to string values
* before interpolating them.
*
* @example
* import { asyncTemplate } from 'deleight/apriori';
* const t = await asyncTemplate('I will wait for this ${Promise.resolve("promise")}!!!')();
* // t === 'I will wait for this promise!!!'
*
*
* @param {string} templateStr the template string
* @param {Array<string>} argNames The names of the parameters of the returned function (which can be 'seen' inside the template string).
* Defaults to: ['arg'].
* @param {string} tagName Supply a tagName argument to change the name of the tag function inside the template string if
* the default name (T) is present in argNames.
* @returns {(...any): Promise<string>}
*/
function asyncTemplate(templateStr, argNames, tagName) {
if (!argNames)
argNames = ['arg'];
if (!tagName)
tagName = "T";
if (argNames.includes(tagName)) {
throw new Error(`The tag name ${tagName} clashes with the name of one of the arguments.
Please change the tag name or the argument name to resolve this.`);
}
const f = Function(tagName, ...argNames, `return ${tagName}\`${templateStr}\`;`);
return (...args) => f(tag, ...args);
}
/**
* Similar to template, but will render an iterable (such as array) of items together instead
* of rendering each item individually. It improves efficiency in these scenarios because only 1 rendering
* function is called.
*
* @example
* import { templates } from 'deleight/apriori';
* const t = arrayTemplate('I will render this ${it}/${other} immediately!!!', ['other'], 'it')([1, 2, 3, 4, 5], '(shared)').join(' & ');
* // t === 'I will render this 1/(shared) immediately!!! & I will render this 2/(shared) immediately!!! & I will render this 3/(shared) immediately!!! & I will render this 4/(shared) immediately!!! & I will render this 5/(shared) immediately!!!'
*
* @param {string} templateStr The template string
* @param {Array<string>} argNames The names of the parameters (after the iterable) of the returned function (which can be 'seen' inside the template string)
* @param {string} itemName The name of the current item of the iterable as seen inside the template string. Defaults
* to 'item'
* Defaults to the empty string.
* @returns {ITemplates}
*/
function templates(templateStr, argNames, itemName) {
if (!argNames)
argNames = [];
if (!itemName)
itemName = "item";
return (Function(`
function* gen(arr, ${argNames.join(', ')}) {
for (let ${itemName} of arr) yield \`${templateStr}\`;
}
return gen;`))();
}
/**
* Async equivalent of arrayTemplate. The async template tag ('T' by default)
* is applied to the template string. Use this when there are promises
* among the arguents that will be passed to the returned function.
*
* @example
* import { asyncTemplates } from 'deleight/apriori';
* let t = asyncTemplates('I will async render this ${item}')([1, 2, 3, 4, 5].map(i => Promise.resolve(i)));
* console.log(t instanceof Promise); // true
* t = (await t).join(' ')
* // t === 'I will async render this 1 I will async render this 2 I will async render this 3 I will async render this 4 I will async render this 5'
*
* @param {string} templateStr The template string
* @param {Array<string>} argNames The names of the parameters (after the iterable) of the returned function (which can be 'seen' inside the template string)
* @param {string} itemName The name of the current item of the iterable as seen inside the template string. Defaults
* to 'item'
* @param {string} tagName Supply a tagName argument to change the name of the tag function inside the template string if
* the default name (T) is present in argNames.
* @returns {IAsyncTemplates}
*/
function asyncTemplates(templateStr, argNames, itemName, tagName) {
if (!argNames)
argNames = [];
if (!itemName)
itemName = "item";
if (!tagName)
tagName = "T";
if (itemName === tagName) {
throw new Error(`The tag name ${tagName} is the same as the item name.
Please change the tag name or the item name to resolve this.`);
}
if (argNames.includes(tagName)) {
throw new Error(`The tag name ${tagName} clashes with the name of one of the arguments.
Please change the tag name or the argument name to resolve this.`);
}
const f = (Function(`
function* gen(${tagName}, arr, ${argNames.join(', ')}) {
for (let ${itemName} of arr) yield ${tagName}\`${templateStr}\`;
}
return gen;`))();
return (arr, ...args) => f(tag, arr, ...args);
}
/**
* Fetches text (typically markup) from the url. This is only a shorthand
* for using `fetch`.
*
* @example
* import { get } from 'deleight/apriori';
* const markup = await get('./something.html')
*
*
* @param {string} url The url to pass to `fetch`
* @param {boolean} [suppressErrors] Whether to return the empty string if an error occurs.
* @param {RequestInit} [init] The `init` argument for `fetch`
* @returns {Promise<string>}
*/
async function get(url, suppressErrors, init) {
let result = fetch(url, init).then((r) => r.text());
if (suppressErrors)
result = result.catch((r) => "");
return result;
}
/**
* Shorthand for creating a DocumentFragment from markup. If the
* fragment has only one child, the child is returned instead.
* So this is also a shorthand for creating single elements.
*
* @example
* import { createFragment } from 'deleight/apriori';
* const frag1 = createFragment(`
* <p>Para 1</p>
* <p>Para 2</p>
*`)
* // <p>Para 1</p><p>Para 2</p>
*
* @param {string} markup The `outerHTML` of what to create
* @returns {Node}
*/
const createFragment = function (markup) {
const temp = document.createElement("template");
temp.innerHTML = markup;
let result = temp.content;
if (result.children.length === 1)
return result.children[0];
return result;
};
/**
* A generator for elements with the specified tag names.
* The names are space-separated just like in a class attribute.
* You can also separate with commma if you prefer.
*
* @example
* import { elements } from 'deleight/apriori';
* const [div, p, span] = elements('div p span');
* const [div2, p2, span2] = elements('div, p, span');
*
* @param {string} tagNames
*/
function* elements(tagNames) {
for (let tagName of tagNames.replace(',', '').split(' '))
yield document.createElement(tagName.trim());
}
/**
* Returns an object which escapes properties sourced from it. Escaping markup is a key component of template rendering,
* so this is an important function to have here.
*
* NB: there are no tests yet. Please report any bugs.
*
* @example
* import { esc } from 'deleight/apriori'
* const obj = { a: 1, b: 'no special chars', c: '<p>But I am a paragraph</p>', d: { e: '<p>"esc" will still work here</p>' } }
* const escObj = esc(obj);
* console.log(escObj.c); // &lt;p&gt;But I am a paragraph&lt;/p&gt;
* console.log(escObj.d.e); // &lt;p&gt;&quot;esc&quot; will still work here&lt;/p&gt;
*
*
* @param {*} rawObject
*/
function esc(rawObject) {
return new Proxy(rawObject, new EscTrap());
}
/**
* Credits 'bjornd' (https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript)
*
* @param {*} unsafe
* @returns
*/
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
class EscTrap {
children = {};
get(target, p) {
if (this.children.hasOwnProperty(p))
return this.children[p];
const result = target[p];
if (typeof result === 'string')
return this.children[p] = escapeHtml(result);
else if (typeof result === 'object')
return this.children[p] = esc(result);
else
return this.children[p] = result;
}
}
exports.asyncTemplate = asyncTemplate;
exports.asyncTemplates = asyncTemplates;
exports.createFragment = createFragment;
exports.elements = elements;
exports.esc = esc;
exports.get = get;
exports.tag = tag;
exports.template = template;
exports.templates = templates;

@@ -1,1 +0,258 @@

async function e(e,...n){const t=[];for(let[e,r]of Array.from(n.entries()))r instanceof Promise?t.push(r):t.push(Promise.resolve(r));const r=await Promise.all(t);return r.map(((n,t)=>`${e[t]}${n}`)).join("")+e[r.length]}function n(e,n){return n||(n=["arg"]),Function(...n,`return \`${e}\`;`)}function t(n,t,r){if(t||(t=["arg"]),r||(r="T"),t.includes(r))throw new Error(`The tag name ${r} clashes with the name of one of the arguments. \n Please change the tag name or the argument name to resolve this.`);const o=Function(r,...t,`return ${r}\`${n}\`;`);return(...n)=>o(e,...n)}function r(e,n,t){return n||(n=[]),t||(t="item"),Function(`\n function* gen(arr, ${n.join(", ")}) {\n for (let ${t} of arr) yield \`${e}\`;\n }\n return gen;`)()}function o(n,t,r,o){if(t||(t=[]),r||(r="item"),o||(o="T"),r===o)throw new Error(`The tag name ${o} is the same as the item name. \n Please change the tag name or the item name to resolve this.`);if(t.includes(o))throw new Error(`The tag name ${o} clashes with the name of one of the arguments. \n Please change the tag name or the argument name to resolve this.`);const a=Function(`\n function* gen(${o}, arr, ${t.join(", ")}) {\n for (let ${r} of arr) yield ${o}\`${n}\`;\n }\n return gen;`)();return(n,...t)=>a(e,n,...t)}async function a(e,n,t){let r=fetch(e,t).then((e=>e.text()));return n&&(r=r.catch((e=>""))),r}const i=function(e){const n=document.createElement("template");n.innerHTML=e;let t=n.content;return 1===t.children.length?t.children[0]:t};function*c(e){for(let n of e.replace(",","").split(" "))yield document.createElement(n.trim())}function h(e){return new Proxy(e,new s)}class s{children={};get(e,n){if(this.children.hasOwnProperty(n))return this.children[n];const t=e[n];return this.children[n]="string"==typeof t?t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;"):"object"==typeof t?h(t):t}}export{t as asyncTemplate,o as asyncTemplates,i as createFragment,c as elements,h as esc,a as get,e as tag,n as template,r as templates};
/**
* This module exports primitives for building DOM from text.
*
* @module
*/
/**
* A template tag that will resolve only after all
* interpolated promises have been resolved, finally returning the
* intended string.
*
* @example
* import { tag } from 'deleight/apriori';
* const t = tag`I will wait for this ${Promise.resolve("promise")}!!!`
* // t === 'I will wait for this promise!!!'
*
* @param {Array<string>} strings
* @param {...any} expressions
* @returns {Promise<string>}
*/
async function tag(strings, ...expressions) {
const promiseExpressions = [];
for (let [i, exp] of Array.from(expressions.entries())) {
if (exp instanceof Promise)
promiseExpressions.push(exp);
else
promiseExpressions.push(Promise.resolve(exp));
}
const resolvedExpressions = await Promise.all(promiseExpressions);
return (resolvedExpressions.map((exp, i) => `${strings[i]}${exp}`).join("") +
strings[resolvedExpressions.length]);
}
/**
* Effectively creates a template literal out of an existing template string and wraps it in a function
* which can be called multiple times to 'render' the template with the given arguments.
*
* @example
* import { template } from 'deleight/apriori';
* const t = template('I will render this ${"guy"} immediately!!!')();
* // t === 'I will render this guy immediately!!!'
*
* @param {string} templateStr the template string
* @param {string[]} argNames The names of the parameters of the returned function (which can be 'seen' inside the template string).
* Defaults to: ['arg'].
* @returns {(...any): string}
*/
function template(templateStr, argNames) {
if (!argNames)
argNames = ['arg'];
return Function(...argNames, `return \`${templateStr}\`;`);
}
/**
* Similar to template but the built template is also 'promise-aware' and will allow them to resolve to string values
* before interpolating them.
*
* @example
* import { asyncTemplate } from 'deleight/apriori';
* const t = await asyncTemplate('I will wait for this ${Promise.resolve("promise")}!!!')();
* // t === 'I will wait for this promise!!!'
*
*
* @param {string} templateStr the template string
* @param {Array<string>} argNames The names of the parameters of the returned function (which can be 'seen' inside the template string).
* Defaults to: ['arg'].
* @param {string} tagName Supply a tagName argument to change the name of the tag function inside the template string if
* the default name (T) is present in argNames.
* @returns {(...any): Promise<string>}
*/
function asyncTemplate(templateStr, argNames, tagName) {
if (!argNames)
argNames = ['arg'];
if (!tagName)
tagName = "T";
if (argNames.includes(tagName)) {
throw new Error(`The tag name ${tagName} clashes with the name of one of the arguments.
Please change the tag name or the argument name to resolve this.`);
}
const f = Function(tagName, ...argNames, `return ${tagName}\`${templateStr}\`;`);
return (...args) => f(tag, ...args);
}
/**
* Similar to template, but will render an iterable (such as array) of items together instead
* of rendering each item individually. It improves efficiency in these scenarios because only 1 rendering
* function is called.
*
* @example
* import { templates } from 'deleight/apriori';
* const t = arrayTemplate('I will render this ${it}/${other} immediately!!!', ['other'], 'it')([1, 2, 3, 4, 5], '(shared)').join(' & ');
* // t === 'I will render this 1/(shared) immediately!!! & I will render this 2/(shared) immediately!!! & I will render this 3/(shared) immediately!!! & I will render this 4/(shared) immediately!!! & I will render this 5/(shared) immediately!!!'
*
* @param {string} templateStr The template string
* @param {Array<string>} argNames The names of the parameters (after the iterable) of the returned function (which can be 'seen' inside the template string)
* @param {string} itemName The name of the current item of the iterable as seen inside the template string. Defaults
* to 'item'
* Defaults to the empty string.
* @returns {ITemplates}
*/
function templates(templateStr, argNames, itemName) {
if (!argNames)
argNames = [];
if (!itemName)
itemName = "item";
return (Function(`
function* gen(arr, ${argNames.join(', ')}) {
for (let ${itemName} of arr) yield \`${templateStr}\`;
}
return gen;`))();
}
/**
* Async equivalent of arrayTemplate. The async template tag ('T' by default)
* is applied to the template string. Use this when there are promises
* among the arguents that will be passed to the returned function.
*
* @example
* import { asyncTemplates } from 'deleight/apriori';
* let t = asyncTemplates('I will async render this ${item}')([1, 2, 3, 4, 5].map(i => Promise.resolve(i)));
* console.log(t instanceof Promise); // true
* t = (await t).join(' ')
* // t === 'I will async render this 1 I will async render this 2 I will async render this 3 I will async render this 4 I will async render this 5'
*
* @param {string} templateStr The template string
* @param {Array<string>} argNames The names of the parameters (after the iterable) of the returned function (which can be 'seen' inside the template string)
* @param {string} itemName The name of the current item of the iterable as seen inside the template string. Defaults
* to 'item'
* @param {string} tagName Supply a tagName argument to change the name of the tag function inside the template string if
* the default name (T) is present in argNames.
* @returns {IAsyncTemplates}
*/
function asyncTemplates(templateStr, argNames, itemName, tagName) {
if (!argNames)
argNames = [];
if (!itemName)
itemName = "item";
if (!tagName)
tagName = "T";
if (itemName === tagName) {
throw new Error(`The tag name ${tagName} is the same as the item name.
Please change the tag name or the item name to resolve this.`);
}
if (argNames.includes(tagName)) {
throw new Error(`The tag name ${tagName} clashes with the name of one of the arguments.
Please change the tag name or the argument name to resolve this.`);
}
const f = (Function(`
function* gen(${tagName}, arr, ${argNames.join(', ')}) {
for (let ${itemName} of arr) yield ${tagName}\`${templateStr}\`;
}
return gen;`))();
return (arr, ...args) => f(tag, arr, ...args);
}
/**
* Fetches text (typically markup) from the url. This is only a shorthand
* for using `fetch`.
*
* @example
* import { get } from 'deleight/apriori';
* const markup = await get('./something.html')
*
*
* @param {string} url The url to pass to `fetch`
* @param {boolean} [suppressErrors] Whether to return the empty string if an error occurs.
* @param {RequestInit} [init] The `init` argument for `fetch`
* @returns {Promise<string>}
*/
async function get(url, suppressErrors, init) {
let result = fetch(url, init).then((r) => r.text());
if (suppressErrors)
result = result.catch((r) => "");
return result;
}
/**
* Shorthand for creating a DocumentFragment from markup. If the
* fragment has only one child, the child is returned instead.
* So this is also a shorthand for creating single elements.
*
* @example
* import { createFragment } from 'deleight/apriori';
* const frag1 = createFragment(`
* <p>Para 1</p>
* <p>Para 2</p>
*`)
* // <p>Para 1</p><p>Para 2</p>
*
* @param {string} markup The `outerHTML` of what to create
* @returns {Node}
*/
const createFragment = function (markup) {
const temp = document.createElement("template");
temp.innerHTML = markup;
let result = temp.content;
if (result.children.length === 1)
return result.children[0];
return result;
};
/**
* A generator for elements with the specified tag names.
* The names are space-separated just like in a class attribute.
* You can also separate with commma if you prefer.
*
* @example
* import { elements } from 'deleight/apriori';
* const [div, p, span] = elements('div p span');
* const [div2, p2, span2] = elements('div, p, span');
*
* @param {string} tagNames
*/
function* elements(tagNames) {
for (let tagName of tagNames.replace(',', '').split(' '))
yield document.createElement(tagName.trim());
}
/**
* Returns an object which escapes properties sourced from it. Escaping markup is a key component of template rendering,
* so this is an important function to have here.
*
* NB: there are no tests yet. Please report any bugs.
*
* @example
* import { esc } from 'deleight/apriori'
* const obj = { a: 1, b: 'no special chars', c: '<p>But I am a paragraph</p>', d: { e: '<p>"esc" will still work here</p>' } }
* const escObj = esc(obj);
* console.log(escObj.c); // &lt;p&gt;But I am a paragraph&lt;/p&gt;
* console.log(escObj.d.e); // &lt;p&gt;&quot;esc&quot; will still work here&lt;/p&gt;
*
*
* @param {*} rawObject
*/
function esc(rawObject) {
return new Proxy(rawObject, new EscTrap());
}
/**
* Credits 'bjornd' (https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript)
*
* @param {*} unsafe
* @returns
*/
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
class EscTrap {
children = {};
get(target, p) {
if (this.children.hasOwnProperty(p))
return this.children[p];
const result = target[p];
if (typeof result === 'string')
return this.children[p] = escapeHtml(result);
else if (typeof result === 'object')
return this.children[p] = esc(result);
else
return this.children[p] = result;
}
}
export { asyncTemplate, asyncTemplates, createFragment, elements, esc, get, tag, template, templates };

@@ -1,1 +0,250 @@

"use strict";class e{listener;constructor(e){this.listener=e}listen(e,t,n){for(let r of t)r.addEventListener(e,this.listener,n)}remove(e,...t){for(let n of t)n.removeEventListener(e,this.listener)}}const t={running:!1};function n(e,n){let s;return n||(n=t),e instanceof Array||(e=[e]),function(t){if(n.running)return;let o;for(s of(n.running=!0,e))if(o=s(t,n),n.result=o,o===r)break;return o instanceof Promise?o.then((()=>n.running=!1),(e=>{throw e})):n.running=!1,o}}const r=Symbol();function s(e,t){const r={};for(let[s,o]of Object.entries(e))if(t||o instanceof Array){let e;o instanceof Array&&"function"!=typeof o.at(-1)||(e=[o,null]),r[s]=e?n(e[0],e[1]):n(o[0],o[1])}else r[s]=o;return function(e){for(let[t,n]of Object.entries(r))if(e.target.matches&&e.target.matches(t))return n(e)}}const o=e=>t=>t.key!==e?r:"",i={enter:"Enter"},c=o(i.enter);exports.END=r,exports.EventListener=class extends e{constructor(e,t){super(n(e,t))}},exports.Listener=e,exports.MatchListener=class extends e{constructor(e,t){super(s(e,t))}},exports.eventListener=n,exports.keys=i,exports.matchListener=s,exports.onEnter=c,exports.onKey=o,exports.preventDefault=e=>e.preventDefault(),exports.stopPropagation=e=>e.stopPropagation();
'use strict';
/**
* This module exports event handling helpers.
*
* @module
*/
/**
* Base class for EventListener and MatchListener. This can be used to
* wrap any listeners which will be shared by many elements. Call the
* `listen` or `remove` method to add or remove the listener to/from
* the given elements.
*
* @example
* import { Listener } from 'deleight/eutility';
* listener = new Listener(() => login(input.value));
* listener.listen('keyup', [window.input1, window.input2])
*
*/
class Listener {
listener;
constructor(listener) {
this.listener = listener;
}
;
listen(eventName, elements, options) {
for (let element of elements)
element.addEventListener(eventName, this.listener, options);
}
remove(eventName, ...elements) {
for (let element of elements)
element.removeEventListener(eventName, this.listener);
}
}
const defaultRunContext = { running: false };
/**
* Composes a listener function from the functions in ops. ops may be
* a single function or an array of functions.
*
* The functions are called in the same order when handling events. They
* will receive as erguments the event object and a run context.
*
* The run context can be passed as the second argument to this function
* (or it will default to the global runContext). The context can be used
* for communication and it also maintains a running property which will
* ensure that no 2 handlers using it will run concurrently (providing the
* API is respected).
*
* The API is as follows:
* If a handler function returns a promise, it is assumed to still be running
* until the promise resolves or rejects. Neither the handler nor any other
* handlers using the same run context can start until it stops running.
*
* Individual op functions can also terminate event handling by returning the
* END symbol.
*
* @example
* import { eventListener } from 'deleight/eutility';
* input.onkeyup = eventListener([onEnter, () => login(input.value), preventDefault]);
* apply({
* '#loginButton': button => {
* button.onclick = eventListener(() => login(input.value));
* }
* }, form);
*
*
* @param {Function[] | Function} ops The function or functions making up the handler
* @param {any} [runContext] The optional run context.
* @returns
*/
function eventListener(ops, runContext) {
if (!runContext)
runContext = defaultRunContext;
if (!(ops instanceof Array))
ops = [ops];
let op;
function listener(e) {
if (runContext.running)
return;
runContext.running = true;
let result;
for (op of ops) {
result = op(e, runContext); // might be a promise. up to the next op whether or not to await it.
runContext.result = result;
if (result === END)
break;
}
// we only await a final promise:
if (result instanceof Promise)
result.then(() => runContext.running = false, err => { throw err; });
else
runContext.running = false;
return result;
}
return listener;
}
/**
* Similar to eventListener function but has methods for attaching
* and removing itself from multiple elements at the same time.
*
* This gives the listener a 'personality' and promotes its reuse
* (good practice).
*
* @example
* import { eventListener } from 'deleight/eutility';
* listener = new EventListener([onEnter, () => login(input.value), preventDefault]);
* listener.listen('keyup', [window.input1, window.input2])
*/
class EventListener extends Listener {
constructor(ops, runContext) {
super(eventListener(ops, runContext));
}
}
/**
* Symbol which will terminate event handling if returned by any of
* the functions in the ops chain of an event handler created with
* `eventHandler`.
*
* @example
* import { END } from 'deleight/eutility';
* const keyEventBreaker = (e: KeyboardEvent) => (e.key !== key)? END: '';
*/
const END = Symbol();
/**
* Takes advantage of event bubbling to listen for events on descendant
* elements to reduce the number of listeners to create.
*
* @example
* import { matchListener } from 'deleight/eutility';
* window.mainTable.onclick = matchListener({
* 'a.lbl': e => select(e.target.parentNode.parentNode),
* 'span.remove': [removeListener, preventDefault, stopPropagation]
* }, true);
*
* @param {IMatcher} matcher Map of event target matcher to associated handler function
* @param {boolean} wrapListeners Whether to werap the matcher functions with `eventListener`.
*/
function matchListener(matcher, wrapListeners) {
const listenerMap = {};
for (let [selector, args] of Object.entries(matcher)) {
if (wrapListeners || args instanceof Array) {
let args2;
if (!(args instanceof Array) || typeof args.at(-1) === "function") {
args2 = [args, null];
}
listenerMap[selector] = args2
? eventListener(args2[0], args2[1])
: eventListener(args[0], args[1]);
}
else
listenerMap[selector] = args;
}
function listener(e) {
for (let [selector, fn] of Object.entries(listenerMap)) {
if (e.target.matches && e.target.matches(selector))
return fn(e);
}
}
return listener;
}
/**
* Similar to matchListener function but has methods for attaching
* and removing itself from multiple elements at the same time.
*
* This gives the listener a 'personality' and promotes its reuse
* (good practice).
*
* @example
* import { MatchListener } from 'deleight/eutility';
* const listener = new MatchListener({
* 'a.lbl': e => select(e.target.parentNode.parentNode),
* 'span.remove': [removeListener, preventDefault, stopPropagation]
* }, true);
*
* listener.listen('click', [window.table1, window.table2], eventOptions)
*/
class MatchListener extends Listener {
constructor(matcher, wrapListeners) {
super(matchListener(matcher, wrapListeners));
}
}
/**
* Simply calls `stopPropagation` on the event. Useful for creating one-liner
* event handlers.
*
* @example
* import { eventListener, stopPropagation } from 'deleight/eutility';
* window.firstButton.onclick = eventListener([stopPropagation, (e, runContext) => {
* // handle event here.
* }]);
*
* @param e The event object
* @returns
*/
const stopPropagation = (e) => e.stopPropagation();
/**
* Simply calls `preventDefault` on the event. Useful for creating one-liner
* event handlers.
*
* @example
* import { eventListener, preventDefault } from 'deleight/eutility';
* window.firstButton.onclick = eventListener([preventDefault, (e, runContext) => {
* // handle event here.
* }]);
*
* @param e The event object
* @returns
*/
const preventDefault = (e) => e.preventDefault();
/**
* This returns a function which will stop an event handler run (typically for keyup,
* keydown etc) from continuing if it has not been triggered by the specified key.
* The returned functions are to be placed before the main handler functions in the `ops`
* array passed to `eventListener`.
*
* @example
* import { eventListener, onKey } from 'deleight/eutility';
* const aKeyGuard = onKey('a');
* window.firstInput.keyup = eventListener([aKeyGuard, (e, runContext) => {
* // handle event here.
* }]);
*
* @returns {Function}
*/
const onKey = (key) => (e) => e.key !== key ? END : "";
const keys = { enter: "Enter" };
/**
* This will stop a key(up or down...) event handler run from continuing if
* it has not been triggered by the enter key.
*
* @example
* import { eventListener, onEnter } from 'deleight/eutility';
* window.firstInput.keyup = eventListener([onEnter, (e, runContext) => {
* // handle event here.
* }]);
*
*/
const onEnter = onKey(keys.enter);
exports.END = END;
exports.EventListener = EventListener;
exports.Listener = Listener;
exports.MatchListener = MatchListener;
exports.eventListener = eventListener;
exports.keys = keys;
exports.matchListener = matchListener;
exports.onEnter = onEnter;
exports.onKey = onKey;
exports.preventDefault = preventDefault;
exports.stopPropagation = stopPropagation;

@@ -1,1 +0,238 @@

class e{listener;constructor(e){this.listener=e}listen(e,t,n){for(let r of t)r.addEventListener(e,this.listener,n)}remove(e,...t){for(let n of t)n.removeEventListener(e,this.listener)}}const t={running:!1};function n(e,n){let r;return n||(n=t),e instanceof Array||(e=[e]),function(t){if(n.running)return;let s;for(r of(n.running=!0,e))if(s=r(t,n),n.result=s,s===o)break;return s instanceof Promise?s.then((()=>n.running=!1),(e=>{throw e})):n.running=!1,s}}class r extends e{constructor(e,t){super(n(e,t))}}const o=Symbol();function s(e,t){const r={};for(let[o,s]of Object.entries(e))if(t||s instanceof Array){let e;s instanceof Array&&"function"!=typeof s.at(-1)||(e=[s,null]),r[o]=e?n(e[0],e[1]):n(s[0],s[1])}else r[o]=s;return function(e){for(let[t,n]of Object.entries(r))if(e.target.matches&&e.target.matches(t))return n(e)}}class i extends e{constructor(e,t){super(s(e,t))}}const c=e=>e.stopPropagation(),f=e=>e.preventDefault(),u=e=>t=>t.key!==e?o:"",l={enter:"Enter"},a=u(l.enter);export{o as END,r as EventListener,e as Listener,i as MatchListener,n as eventListener,l as keys,s as matchListener,a as onEnter,u as onKey,f as preventDefault,c as stopPropagation};
/**
* This module exports event handling helpers.
*
* @module
*/
/**
* Base class for EventListener and MatchListener. This can be used to
* wrap any listeners which will be shared by many elements. Call the
* `listen` or `remove` method to add or remove the listener to/from
* the given elements.
*
* @example
* import { Listener } from 'deleight/eutility';
* listener = new Listener(() => login(input.value));
* listener.listen('keyup', [window.input1, window.input2])
*
*/
class Listener {
listener;
constructor(listener) {
this.listener = listener;
}
;
listen(eventName, elements, options) {
for (let element of elements)
element.addEventListener(eventName, this.listener, options);
}
remove(eventName, ...elements) {
for (let element of elements)
element.removeEventListener(eventName, this.listener);
}
}
const defaultRunContext = { running: false };
/**
* Composes a listener function from the functions in ops. ops may be
* a single function or an array of functions.
*
* The functions are called in the same order when handling events. They
* will receive as erguments the event object and a run context.
*
* The run context can be passed as the second argument to this function
* (or it will default to the global runContext). The context can be used
* for communication and it also maintains a running property which will
* ensure that no 2 handlers using it will run concurrently (providing the
* API is respected).
*
* The API is as follows:
* If a handler function returns a promise, it is assumed to still be running
* until the promise resolves or rejects. Neither the handler nor any other
* handlers using the same run context can start until it stops running.
*
* Individual op functions can also terminate event handling by returning the
* END symbol.
*
* @example
* import { eventListener } from 'deleight/eutility';
* input.onkeyup = eventListener([onEnter, () => login(input.value), preventDefault]);
* apply({
* '#loginButton': button => {
* button.onclick = eventListener(() => login(input.value));
* }
* }, form);
*
*
* @param {Function[] | Function} ops The function or functions making up the handler
* @param {any} [runContext] The optional run context.
* @returns
*/
function eventListener(ops, runContext) {
if (!runContext)
runContext = defaultRunContext;
if (!(ops instanceof Array))
ops = [ops];
let op;
function listener(e) {
if (runContext.running)
return;
runContext.running = true;
let result;
for (op of ops) {
result = op(e, runContext); // might be a promise. up to the next op whether or not to await it.
runContext.result = result;
if (result === END)
break;
}
// we only await a final promise:
if (result instanceof Promise)
result.then(() => runContext.running = false, err => { throw err; });
else
runContext.running = false;
return result;
}
return listener;
}
/**
* Similar to eventListener function but has methods for attaching
* and removing itself from multiple elements at the same time.
*
* This gives the listener a 'personality' and promotes its reuse
* (good practice).
*
* @example
* import { eventListener } from 'deleight/eutility';
* listener = new EventListener([onEnter, () => login(input.value), preventDefault]);
* listener.listen('keyup', [window.input1, window.input2])
*/
class EventListener extends Listener {
constructor(ops, runContext) {
super(eventListener(ops, runContext));
}
}
/**
* Symbol which will terminate event handling if returned by any of
* the functions in the ops chain of an event handler created with
* `eventHandler`.
*
* @example
* import { END } from 'deleight/eutility';
* const keyEventBreaker = (e: KeyboardEvent) => (e.key !== key)? END: '';
*/
const END = Symbol();
/**
* Takes advantage of event bubbling to listen for events on descendant
* elements to reduce the number of listeners to create.
*
* @example
* import { matchListener } from 'deleight/eutility';
* window.mainTable.onclick = matchListener({
* 'a.lbl': e => select(e.target.parentNode.parentNode),
* 'span.remove': [removeListener, preventDefault, stopPropagation]
* }, true);
*
* @param {IMatcher} matcher Map of event target matcher to associated handler function
* @param {boolean} wrapListeners Whether to werap the matcher functions with `eventListener`.
*/
function matchListener(matcher, wrapListeners) {
const listenerMap = {};
for (let [selector, args] of Object.entries(matcher)) {
if (wrapListeners || args instanceof Array) {
let args2;
if (!(args instanceof Array) || typeof args.at(-1) === "function") {
args2 = [args, null];
}
listenerMap[selector] = args2
? eventListener(args2[0], args2[1])
: eventListener(args[0], args[1]);
}
else
listenerMap[selector] = args;
}
function listener(e) {
for (let [selector, fn] of Object.entries(listenerMap)) {
if (e.target.matches && e.target.matches(selector))
return fn(e);
}
}
return listener;
}
/**
* Similar to matchListener function but has methods for attaching
* and removing itself from multiple elements at the same time.
*
* This gives the listener a 'personality' and promotes its reuse
* (good practice).
*
* @example
* import { MatchListener } from 'deleight/eutility';
* const listener = new MatchListener({
* 'a.lbl': e => select(e.target.parentNode.parentNode),
* 'span.remove': [removeListener, preventDefault, stopPropagation]
* }, true);
*
* listener.listen('click', [window.table1, window.table2], eventOptions)
*/
class MatchListener extends Listener {
constructor(matcher, wrapListeners) {
super(matchListener(matcher, wrapListeners));
}
}
/**
* Simply calls `stopPropagation` on the event. Useful for creating one-liner
* event handlers.
*
* @example
* import { eventListener, stopPropagation } from 'deleight/eutility';
* window.firstButton.onclick = eventListener([stopPropagation, (e, runContext) => {
* // handle event here.
* }]);
*
* @param e The event object
* @returns
*/
const stopPropagation = (e) => e.stopPropagation();
/**
* Simply calls `preventDefault` on the event. Useful for creating one-liner
* event handlers.
*
* @example
* import { eventListener, preventDefault } from 'deleight/eutility';
* window.firstButton.onclick = eventListener([preventDefault, (e, runContext) => {
* // handle event here.
* }]);
*
* @param e The event object
* @returns
*/
const preventDefault = (e) => e.preventDefault();
/**
* This returns a function which will stop an event handler run (typically for keyup,
* keydown etc) from continuing if it has not been triggered by the specified key.
* The returned functions are to be placed before the main handler functions in the `ops`
* array passed to `eventListener`.
*
* @example
* import { eventListener, onKey } from 'deleight/eutility';
* const aKeyGuard = onKey('a');
* window.firstInput.keyup = eventListener([aKeyGuard, (e, runContext) => {
* // handle event here.
* }]);
*
* @returns {Function}
*/
const onKey = (key) => (e) => e.key !== key ? END : "";
const keys = { enter: "Enter" };
/**
* This will stop a key(up or down...) event handler run from continuing if
* it has not been triggered by the enter key.
*
* @example
* import { eventListener, onEnter } from 'deleight/eutility';
* window.firstInput.keyup = eventListener([onEnter, (e, runContext) => {
* // handle event here.
* }]);
*
*/
const onEnter = onKey(keys.enter);
export { END, EventListener, Listener, MatchListener, eventListener, keys, matchListener, onEnter, onKey, preventDefault, stopPropagation };

@@ -1,1 +0,234 @@

"use strict";function e(e){return e.next?e:e[Symbol.iterator]()}function*t(e,t,o){let n=t;for(o&&(yield o,n--);n-- >0;)yield e.next().value}const o=new WeakMap;exports.flat=function*(...t){const o=t.length;let n,r;for(t=t.map((t=>e(t)));;)for(n=0;n<o;n++){if(r=t[n].next(),r.done)return;yield r.value}},exports.getLength=function(e){return o.get(e)||e.length},exports.group=function*(o,n){const r=e(o);let l=r.next();for(;!l.done;)yield[...t(r,n,l.value)],l=r.next()},exports.items=function*(e,t){for(let o of t)yield e[o]},exports.iter=e,exports.iterLengths=o,exports.next=t,exports.range=function*(e,t,o){o||(o=1),null==t&&e&&(t=e,e=0);for(let n=e;n<t;n+=o)yield n},exports.repeat=function*(e,t){let o;if(void 0===t){const t=[];for(let o of e)t.push(o),yield o;for(;;)for(o of t)yield o}else for(let n=0;n<t;n++)for(o of e)yield o},exports.setLength=function(e,t){return o.set(e,t),e},exports.uItems=function*(e){const t=[...e];for(let e=t.length-1;e>=0;e--)yield t.splice(Math.round(Math.random()*e),1)[0]};
'use strict';
/**
* This module exports many useful generators like `range` and `repeat`.
*
* @module
*/
/**
* Forcs any iterable to become an iterator. Will throw
* if not possible.
*
* @example
* import { iter } from 'deleight/generational';
* const it = iter([1, 2, 3, 4, 5]);
*
* @param { any } it
*/
function iter(it) {
return (it.next) ? it : it[Symbol.iterator]();
}
/**
* Fast and 'costless' range function for javascript based on generators.
*
* @example
* import { range } from 'deleight/generational';
* const arr1000 = [...range(0, 1000)];
* // creates an array with 1000 items counting from 0 to 999.
*
* @param {number} start
* @param {number} [end]
* @param {number} [step]
*/
function* range(start, end, step) {
if (!step)
step = 1;
if ((end === undefined || end === null) && start) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step)
yield i;
}
/**
* Returns a generator which iterates over the subset of the
* 'arrayLike' object that matches the provided index.
*
* @example
* import { items, range } from 'deleight/generational';
* const tenth = []...items(range(1000), range(0, 1000, 10))];
* // selects every 10th item in the array.
*
* @param {any} arrayLike
* @param {Iterable<any>} index
*/
function* items(arrayLike, index) {
for (let i of index)
yield arrayLike[i];
}
/**
* Returns a generator that yields first argument (`what`) the number of
* times specified by the second argument (`times`). If `times` is not
* given, `what` is repeated indefinitely.
*
* @example
* import { repeat } from 'deleight/generational';
* const repeated = [...repeat([1, 2, 3], 4)];
* // [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
*
* @param {Iterable<any>} what
* @param {number} [times]
*/
function* repeat(what, times) {
let item;
if (times === undefined) {
const what2 = [];
for (let item of what) {
what2.push(item);
yield item;
}
while (true)
for (item of what2)
yield item;
}
else
for (let i = 0; i < times; i++)
for (item of what)
yield item;
}
/**
* Returns an iterator over the next 'count' items of the iterator.
*
* Note that, for performance reasons, this only accepts an
* iterator as the 'it' argument. All the other module functions accept
* iterables.
*
* You can convert any iterable to an iterator using the `iter` funtion
* as shown in the following example.
*
* If a firstValue is specified, it will be yielded first.
*
* @example
* import { nextItems, iter } from 'deleight/generational';
* const it = iter([1, 'a', 2, 'b', 3, 'c', 4, 'd']);
* const [num, let] = next(it, 2);
*
* @param {Iterator<any>} it
* @param {number} count
* @param { any } [firstValue]
*/
function* next(it, count, firstValue) {
let count2 = count;
if (firstValue) {
yield firstValue;
count2--;
}
while (count2-- > 0)
yield it.next().value;
}
/**
* Returns an iterator of iterators over the next 'count' items of
* the given iterable
*
* @example
* import { next } from 'deleight/generational';
* const o1 = [1, 2, 3, 4];
* const o2 = ['a', 'b', 'c', 'd'];
* const zip = group(flat(o1, o2), 2);
*
* @param { Iterable<any> } it
* @param { number } count
*/
function* group(it, count) {
const it2 = iter(it);
let nextItem = it2.next();
while (!nextItem.done) {
yield [...next(it2, count, nextItem.value)];
nextItem = it2.next();
}
}
/**
* Returns an iterator over the items of all the input iterators, starting from
* the zero index to the maximum index of the first argument. The
* effective length of the iterator is the multiple of the length of thr smallest
* iterator and the number of iterators (number of args).
*
* Can be used to join arrays in a way no supported by `concat`, `push`, etc.
* To pass an array as an iterator, call array.values().
*
* @example
* import { flat } from 'deleight/generational';
* for (let i of flat(range(10, range(15)))) {
* console.log(i); // 0, 0, 1, 1, 2, 2, .... till smallest iterable (10) is exhausted.
* }
*
* @param {...any[]} args
*/
function* flat(...args) {
const count = args.length;
args = args.map(arg => iter(arg));
let i, nextItem;
while (true) {
for (i = 0; i < count; i++) {
nextItem = args[i].next();
if (nextItem.done)
return;
else
yield nextItem.value;
}
}
}
/**
* Returns an unordered/random iterator over the input array..
*
* @example
* import { uItems } from 'deleight/generational';
* const unOrdered = uItems([1, 2, 3, 4]); // [4, 1, 3, 2]
*
* @param {Iterable<any>} it
*/
function* uItems(it) {
const arr = [...it];
for (let i = arr.length - 1; i >= 0; i--) {
yield arr.splice(Math.round(Math.random() * i), 1)[0];
}
}
/**
* Call to get the length of an object. The object must either
* have a length property of be previously passed in a call to`setLength`.
*
* @example
* import { getLength, setLength } from 'deleight/generational';
* const myRange = range(12);
* setLength(myRange, 12);
* getLength(myRange); // returns 12.
*
* @param {any} it
*/
function getLength(it) {
return iterLengths.get(it) || it.length;
}
/**
* Stores the 'fake' lenghts of iterables passed in calls to `setLength`.
* Can also be modified manually.
*/
const iterLengths = new WeakMap();
/**
* Attaches a 'fake' length to an object (likely iterable or iterator)
* which does not have a length property, so that it can work well with
* functions that use `getLength`.
*
* @example
* import { getLength, setLength } from 'deleight/generational';
* const myRange = range(12);
* setLength(myRange, 12);
* getLength(myRange); // returns 12.
*
* @param {any} it
*/
function setLength(it, length) {
iterLengths.set(it, length);
return it;
}
exports.flat = flat;
exports.getLength = getLength;
exports.group = group;
exports.items = items;
exports.iter = iter;
exports.iterLengths = iterLengths;
exports.next = next;
exports.range = range;
exports.repeat = repeat;
exports.setLength = setLength;
exports.uItems = uItems;

@@ -1,1 +0,222 @@

function e(e){return e.next?e:e[Symbol.iterator]()}function*n(e,n,t){t||(t=1),null==n&&e&&(n=e,e=0);for(let o=e;o<n;o+=t)yield o}function*t(e,n){for(let t of n)yield e[t]}function*o(e,n){let t;if(void 0===n){const n=[];for(let t of e)n.push(t),yield t;for(;;)for(t of n)yield t}else for(let o=0;o<n;o++)for(t of e)yield t}function*l(e,n,t){let o=n;for(t&&(yield t,o--);o-- >0;)yield e.next().value}function*f(n,t){const o=e(n);let f=o.next();for(;!f.done;)yield[...l(o,t,f.value)],f=o.next()}function*i(...n){const t=n.length;let o,l;for(n=n.map((n=>e(n)));;)for(o=0;o<t;o++){if(l=n[o].next(),l.done)return;yield l.value}}function*r(e){const n=[...e];for(let e=n.length-1;e>=0;e--)yield n.splice(Math.round(Math.random()*e),1)[0]}function u(e){return c.get(e)||e.length}const c=new WeakMap;function d(e,n){return c.set(e,n),e}export{i as flat,u as getLength,f as group,t as items,e as iter,c as iterLengths,l as next,n as range,o as repeat,d as setLength,r as uItems};
/**
* This module exports many useful generators like `range` and `repeat`.
*
* @module
*/
/**
* Forcs any iterable to become an iterator. Will throw
* if not possible.
*
* @example
* import { iter } from 'deleight/generational';
* const it = iter([1, 2, 3, 4, 5]);
*
* @param { any } it
*/
function iter(it) {
return (it.next) ? it : it[Symbol.iterator]();
}
/**
* Fast and 'costless' range function for javascript based on generators.
*
* @example
* import { range } from 'deleight/generational';
* const arr1000 = [...range(0, 1000)];
* // creates an array with 1000 items counting from 0 to 999.
*
* @param {number} start
* @param {number} [end]
* @param {number} [step]
*/
function* range(start, end, step) {
if (!step)
step = 1;
if ((end === undefined || end === null) && start) {
end = start;
start = 0;
}
for (let i = start; i < end; i += step)
yield i;
}
/**
* Returns a generator which iterates over the subset of the
* 'arrayLike' object that matches the provided index.
*
* @example
* import { items, range } from 'deleight/generational';
* const tenth = []...items(range(1000), range(0, 1000, 10))];
* // selects every 10th item in the array.
*
* @param {any} arrayLike
* @param {Iterable<any>} index
*/
function* items(arrayLike, index) {
for (let i of index)
yield arrayLike[i];
}
/**
* Returns a generator that yields first argument (`what`) the number of
* times specified by the second argument (`times`). If `times` is not
* given, `what` is repeated indefinitely.
*
* @example
* import { repeat } from 'deleight/generational';
* const repeated = [...repeat([1, 2, 3], 4)];
* // [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
*
* @param {Iterable<any>} what
* @param {number} [times]
*/
function* repeat(what, times) {
let item;
if (times === undefined) {
const what2 = [];
for (let item of what) {
what2.push(item);
yield item;
}
while (true)
for (item of what2)
yield item;
}
else
for (let i = 0; i < times; i++)
for (item of what)
yield item;
}
/**
* Returns an iterator over the next 'count' items of the iterator.
*
* Note that, for performance reasons, this only accepts an
* iterator as the 'it' argument. All the other module functions accept
* iterables.
*
* You can convert any iterable to an iterator using the `iter` funtion
* as shown in the following example.
*
* If a firstValue is specified, it will be yielded first.
*
* @example
* import { nextItems, iter } from 'deleight/generational';
* const it = iter([1, 'a', 2, 'b', 3, 'c', 4, 'd']);
* const [num, let] = next(it, 2);
*
* @param {Iterator<any>} it
* @param {number} count
* @param { any } [firstValue]
*/
function* next(it, count, firstValue) {
let count2 = count;
if (firstValue) {
yield firstValue;
count2--;
}
while (count2-- > 0)
yield it.next().value;
}
/**
* Returns an iterator of iterators over the next 'count' items of
* the given iterable
*
* @example
* import { next } from 'deleight/generational';
* const o1 = [1, 2, 3, 4];
* const o2 = ['a', 'b', 'c', 'd'];
* const zip = group(flat(o1, o2), 2);
*
* @param { Iterable<any> } it
* @param { number } count
*/
function* group(it, count) {
const it2 = iter(it);
let nextItem = it2.next();
while (!nextItem.done) {
yield [...next(it2, count, nextItem.value)];
nextItem = it2.next();
}
}
/**
* Returns an iterator over the items of all the input iterators, starting from
* the zero index to the maximum index of the first argument. The
* effective length of the iterator is the multiple of the length of thr smallest
* iterator and the number of iterators (number of args).
*
* Can be used to join arrays in a way no supported by `concat`, `push`, etc.
* To pass an array as an iterator, call array.values().
*
* @example
* import { flat } from 'deleight/generational';
* for (let i of flat(range(10, range(15)))) {
* console.log(i); // 0, 0, 1, 1, 2, 2, .... till smallest iterable (10) is exhausted.
* }
*
* @param {...any[]} args
*/
function* flat(...args) {
const count = args.length;
args = args.map(arg => iter(arg));
let i, nextItem;
while (true) {
for (i = 0; i < count; i++) {
nextItem = args[i].next();
if (nextItem.done)
return;
else
yield nextItem.value;
}
}
}
/**
* Returns an unordered/random iterator over the input array..
*
* @example
* import { uItems } from 'deleight/generational';
* const unOrdered = uItems([1, 2, 3, 4]); // [4, 1, 3, 2]
*
* @param {Iterable<any>} it
*/
function* uItems(it) {
const arr = [...it];
for (let i = arr.length - 1; i >= 0; i--) {
yield arr.splice(Math.round(Math.random() * i), 1)[0];
}
}
/**
* Call to get the length of an object. The object must either
* have a length property of be previously passed in a call to`setLength`.
*
* @example
* import { getLength, setLength } from 'deleight/generational';
* const myRange = range(12);
* setLength(myRange, 12);
* getLength(myRange); // returns 12.
*
* @param {any} it
*/
function getLength(it) {
return iterLengths.get(it) || it.length;
}
/**
* Stores the 'fake' lenghts of iterables passed in calls to `setLength`.
* Can also be modified manually.
*/
const iterLengths = new WeakMap();
/**
* Attaches a 'fake' length to an object (likely iterable or iterator)
* which does not have a length property, so that it can work well with
* functions that use `getLength`.
*
* @example
* import { getLength, setLength } from 'deleight/generational';
* const myRange = range(12);
* setLength(myRange, 12);
* getLength(myRange); // returns 12.
*
* @param {any} it
*/
function setLength(it, length) {
iterLengths.set(it, length);
return it;
}
export { flat, getLength, group, items, iter, iterLengths, next, range, repeat, setLength, uItems };

@@ -1,1 +0,524 @@

"use strict";const e=Symbol(),t=Symbol();class n{one;map;constructor(e,t){this.one=e,this.map=t}#e(t){const n={};if(t.hasOwnProperty(e)){let s;for(let[r,o]of Object.entries(t[e]))s=this.map[r],n.hasOwnProperty(s)||(n[s]={[e]:{}}),n[s][e][r]=o}else for(let[s,r]of Object.entries(this.map))n[r]={[e]:{[s]:t}};return n}get(){return this.one.get(this.map)}set(e){return this.one.set(this.#e(e))}delete(){return this.one.delete(this.map)}call(e){return this.one.call(this.#e(e))}}class s{many;constructor(e){this.many=e}extend(e){let t;for(let[n,r]of Object.entries(e))this.many.hasOwnProperty(n)?(t=this.many[n],t instanceof s?t.extend(r):this.many[n]=r):this.many[n]=r;return this}contract(...e){for(let t of e)delete this.many[t];return this}slice(...e){const t={};for(let n of e)t[n]=this.many[n]||{};return new s(t)}view(e){return new n(this,e)}get(e,t){const n={};let r,o,i,f,a;if("string"==typeof e)for([o,f]of Object.entries(this.many))f instanceof s?n[o]=f.get(e,t):(r=f[e],t&&r instanceof s&&(r=r.many[t]),n[o]=r);else if(e instanceof Array)for([o,f]of Object.entries(this.many))if(f instanceof s)n[o]=f.get(e,t);else for(i of(n[o]=r={},e))a=f[i],t&&a instanceof s&&(a=a.many[t]),r[i]=a;else if("object"==typeof e)for(let[o,c]of Object.entries(e))if(f=this.many[o],f instanceof s)n[o]=f.get(c,t);else if(c instanceof Array)for(i of(n[o]=r={},c))a=f[i],t&&a instanceof s&&(a=a.many[t]),r[i]=a;else r=f[c],t&&r instanceof s&&(r=r.many[t]),n[o]=r;return n}set(t,n){let r,o,i,f;for(let[a,c]of Object.entries(t))if(c.hasOwnProperty(e))for([r,i]of Object.entries(c[e]))o=this.many[r],o instanceof s?o.set({prop:i},n):n?(f=o[a],f instanceof s&&f.many.hasOwnProperty(n)&&(f.many[n]=i)):o[a]=i;else for([r,o]of Object.entries(this.many))o instanceof s?o.set({prop:c},n):n?(f=o[a],f instanceof s&&f.many.hasOwnProperty(n)&&(f.many[n]=c)):o[a]=c;return this}delete(e){let t,n,r;if("string"==typeof e)for([t,r]of Object.entries(this.many))r instanceof s?r.delete(e):delete r[e];else if(e instanceof Array)for([t,r]of Object.entries(this.many))if(r instanceof s)r.delete(e);else for(n of e)delete r[n];else if("object"==typeof e)for(let[t,o]of Object.entries(e))if(r=this.many[t],r instanceof s)r.delete(o);else if(o instanceof Array)for(n of o)delete r[n];else delete r[o];return this}call(n){let r,o,i;const f={};let a;if("string"==typeof n&&(n=[n]),n instanceof Array)for(let e of n)for([r,o]of Object.entries(this.many))f[r]=o[e]?.();else for(let[c,l]of Object.entries(n))if(f[c]=a={},l.hasOwnProperty(e))for([r,i]of Object.entries(l[e]))o=this.many[r],o instanceof s?a[r]=o.call({prop:i}):i.hasOwnProperty(t)?a[r]=o[c]?.(...i[t]):a[r]=o[c]?.(i);else for([r,o]of Object.entries(this.many))o instanceof s?a[r]=o.call({prop:l}):l.hasOwnProperty(t)?a[r]=o[c]?.(...l[t]):a[r]=o[c]?.(l);return f}}function r(e){return new Proxy(e,i)}const o=Symbol(),i={get:(n,s)=>s===o?n:Object.assign(((...r)=>{let o;return 1===r.length&&"object"==typeof(o=r[0])&&o.hasOwnProperty(e)?n.call({[s]:o}):n.call({[s]:{[t]:r}})}),{get value(){return n.get(s)}}),set:(e,t,n)=>(e.set({[t]:n}),!0),deleteProperty:(e,t)=>(e.delete(t),!0)};exports.One=s,exports.View=n,exports.args=t,exports.map=e,exports.one=function(e){return r(new s(e))},exports.unwrap=function(e){return e[o]},exports.wrap=r;
'use strict';
/**
* This module enables reactivity by exporting primitives for multiplying the effects of single operations.
*
* @module
*/
const map = Symbol(), args = Symbol();
class View {
one;
map;
/**
* Represents a single view of a `One` instance. A view can be described as an
* object comprising one property each from the different objects that are part of
* the `One` object. We can perform actions on a view without explicitly specifying
* the properties.
*
* @example
* import { One, View, args } from "deleight/onetomany";
* const one = new One({ a1: [], a2: [1, 2, 3, 4, 5] });
*
* const view = new View(one, { a1: 'push', a2: 'shift' })
* // push one array and simultaneously shift the other
* // to transfer content...
*
* view.call({ [args]: one.many.a2 })
*
* @param one
* @param map
*
* @constructor
*/
constructor(one, map) { this.one = one; this.map = map; }
#oneWhat(what) {
const oneWhat = {};
if (what.hasOwnProperty(map)) {
let prop;
for (let [key, val] of Object.entries(what[map])) {
prop = this.map[key];
if (!oneWhat.hasOwnProperty(prop))
oneWhat[prop] = { [map]: {} };
oneWhat[prop][map][key] = val;
}
}
else {
for (let [key, prop] of Object.entries(this.map))
oneWhat[prop] = { [map]: { [key]: what } };
}
return oneWhat;
}
get() { return this.one.get(this.map); }
set(what) { return this.one.set(this.#oneWhat(what)); }
delete() { return this.one.delete(this.map); }
call(what) { return this.one.call(this.#oneWhat(what)); }
}
class One {
many;
/**
* Creates a single object that propagates actions on it to multiple objects.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
*
* // set the same value on all objects
* o1.set({ p1: 78 });
*
* @param many
*/
constructor(many) { this.many = many; }
/**
* Joins `many` with `this.many` and returns `this`. The benefit of
* using this function instead of something like `Object.assign` is to handle some
* special cases and ensure that the updated `One` is (almost) correctly typed during
* development.
*
* If the same key exists in the joined objects, the new one will overwrite the
* current one, except if the current value is a `One` instance when `extend` will
* be called on it instead.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* const o2 = o1.extend({ fourth });
* // One({ first, second, third, fourth })
*
* @param many
*/
extend(many) {
let currentVal;
for (let [key, value] of Object.entries(many)) {
if (this.many.hasOwnProperty(key)) {
currentVal = this.many[key];
if (currentVal instanceof One) {
currentVal.extend(value);
}
else
this.many[key] = value;
}
else
this.many[key] = value;
}
return this;
}
/**
* The opposite of extend. Removes the specified keys from `this.many` and
* returns `this` typed differently.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* const o2 = o1.contract('first', 'third');
* // One({ second })
*
* @param keys
* @returns
*/
contract(...keys) {
for (let k of keys)
delete this.many[k];
return this;
}
/**
* Creates and returns another instance of `One` containing only the
* objects with these names. If a name is not present in `this.many`,
* a new object is created for it in the returned `One`.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* const o2 = o1.slice('first', 'second')
* // One({ first, second })
*
* @param names
*/
slice(...names) {
const many = {};
for (let name of names)
many[name] = this.many[name] || {};
return new One(many);
}
;
/**
* Creates and returns an instance of `View` for only the
* specified properties. `what` is an object mapping object keys (in `this.many`)
* to the property names.
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex } });
* o1.view({first: 'p1', second: 'p2'});
* view.get(); // { first: 78, second: 12 }
*
* @param map
*/
view(map) { return new View(this, map); }
;
/**
* Gets the property (or properties) with the specified name(s) from all the
* objects in `this.many`.
*
* 1. if `what` is a string, returns only the property specified (as an object mapping
* object key to property value).
* 2. if `what` is an array of strings, returns 'sub-objects' of all the objects
* consisting of the specified property names (also mapped like 1).
* 3. if what is an object, it is treated as a map of object names to property
* name(s) to return.
* 4. if an object in `this.many` is a `One` instance, its `get` method is
* called for its property value(s).
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex } });
*
* o1.get('p1');
* // { first: 78, second: 78, third: 78 }
*
* o1.get('p1', 'p2');
* // { first: { p1: 78, p2: 56 }, second: { p1: 78, p2: 12 }, third: { p1: 78, p2: 12 } }
*
* o1.get({ first: 'p2', second: 'p1' });
* // { first: 56, second: 78 }
*
* @param what
*/
get(what, valueKey) {
const result = {};
let val, key, item, obj, subVal;
if (typeof what === 'string') {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
result[key] = obj.get(what, valueKey);
else {
val = obj[what];
if (valueKey && val instanceof One)
val = val.many[valueKey];
result[key] = val;
}
}
}
else if (what instanceof Array) {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
result[key] = obj.get(what, valueKey);
else {
result[key] = val = {};
for (item of what) {
subVal = obj[item];
if (valueKey && subVal instanceof One)
subVal = subVal.many[valueKey];
val[item] = subVal;
}
}
}
}
else if (typeof what === 'object') {
for (let [key, props] of Object.entries(what)) {
obj = this.many[key];
if (obj instanceof One)
result[key] = obj.get(props, valueKey);
else {
if (props instanceof Array) {
result[key] = val = {};
for (item of props) {
subVal = obj[item];
if (valueKey && subVal instanceof One)
subVal = subVal.many[valueKey];
val[item] = subVal;
}
}
else {
val = obj[props];
if (valueKey && val instanceof One)
val = val.many[valueKey];
result[key] = val;
}
}
}
}
return result;
}
;
/**
* Sets one or more properties on all the objects in this instance.
* `what` is an object mapping property names to property values.
*
* Each value in `what` is set on all the objects in this `One`.
* The same value will be set unless the value is an object containing the `[map]` property.
* In such a case, the value of the property is treated as a map of object key (in `this.many`)
* to object property value. That is, the objects with corresponding
* keys will have their property values set to the corresponding values.
*
* This will call `set` on any nested `One` objects accordingly.
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex }, p3: complex });
*
* o1.get('p2', 'p3');
* // { first: { p2: 56, p3: complex }, second: { p2: 12, p3: complex }, third: { p2: 12, p3: complex } }
*
* @param what
*/
set(what, valueKey) {
let key, obj, subValue, propVal;
for (let [prop, value] of Object.entries(what)) {
if (value.hasOwnProperty(map)) {
for ([key, subValue] of Object.entries(value[map])) {
obj = this.many[key];
if (obj instanceof One)
obj.set({ prop: subValue }, valueKey);
else {
if (valueKey) {
propVal = obj[prop];
if (propVal instanceof One && propVal.many.hasOwnProperty(valueKey)) {
propVal.many[valueKey] = subValue;
}
}
else
obj[prop] = subValue;
}
}
}
else {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
obj.set({ prop: value }, valueKey);
else {
if (valueKey) {
propVal = obj[prop];
if (propVal instanceof One && propVal.many.hasOwnProperty(valueKey)) {
propVal.many[valueKey] = value;
}
}
else
obj[prop] = value;
}
}
}
}
return this;
}
;
/**
* Performs `delete` on the objects in this `One`. The argument is
* treated the same way as in `get`.
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex }, p3: complex });
*
* o1.delete('p1', 'p2');
* // One({ first: { p3: complex }, second: { p3: complex }, third: { p3: complex } })
*
* @param what
*/
delete(what) {
let key, item, obj;
if (typeof what === 'string') {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
obj.delete(what);
else
delete obj[what];
}
}
else if (what instanceof Array) {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
obj.delete(what);
else
for (item of what)
delete obj[item];
}
}
else if (typeof what === 'object') {
for (let [key, props] of Object.entries(what)) {
obj = this.many[key];
if (obj instanceof One)
obj.delete(props);
else {
if (props instanceof Array)
for (item of props)
delete obj[item];
else
delete obj[props];
}
}
}
return this;
}
;
/**
* Calls corresponding methods in a similar way to set. In this
* case, call arguments are interpreted instead of property values.
*
* Multiple arguments should be provided as an object with the `[arg]` property whose value
* should be an array containing the arguments. A regular array is considered a single argument.
*
* Nested one objects will have their call method invoked correspondingly.
*
* @example
* import { One, map, args } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const o4 = new One({ arr1, arr2, arr3 });
* o4.call({ push: 78 });
* o4.call({ push: { [args]: [56, 57] } });
* // arr1 = arr2 = arr3 = [78, 56, 57]
*
* @param what
*/
call(what) {
let key, obj, subValue;
const result = {};
let propResult;
if (typeof what === 'string')
what = [what];
if (what instanceof Array) {
for (let prop of what) {
for ([key, obj] of Object.entries(this.many)) {
result[key] = obj[prop]?.();
}
}
}
else {
for (let [prop, value] of Object.entries(what)) {
result[prop] = propResult = {};
if (value.hasOwnProperty(map)) {
for ([key, subValue] of Object.entries(value[map])) {
obj = this.many[key];
if (obj instanceof One)
propResult[key] = obj.call({ prop: subValue });
else {
if (subValue.hasOwnProperty(args))
propResult[key] = obj[prop]?.(...subValue[args]);
else
propResult[key] = obj[prop]?.(subValue);
}
}
}
else {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
propResult[key] = obj.call({ prop: value });
else {
if (value.hasOwnProperty(args))
propResult[key] = obj[prop]?.(...value[args]);
else
propResult[key] = obj[prop]?.(value);
}
}
}
}
}
return result;
}
}
/**
* A simple wrapper around `One` for a more concise syntax.
*
* @example
* import { one, map, args } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const o4 = one({ arr1, arr2, arr3 });
* o4.push(78, 56, 57);
* o4.push({ [map]: { arr1: 66, arr2: { [args]: [77, 88] }, arr3: 99 } });
* // arr1 === [78, 56, 57, 66]
* // arr2 === [78, 56, 57, 77, 88]
* // arr3 === [78, 56, 57, 99]
*
* @param many
* @returns
*/
function one(many) { return wrap(new One(many)); }
/**
* Wraps a pre-existing `One`
*
* @example
* import { One, wrap } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const o4 = new One({ arr1, arr2, arr3 });
* const wo4 = wrap(o4);
*
* @param one
* @returns
*/
function wrap(one) {
return new Proxy(one, oneTrap);
}
/**
* The opposite of `wrap`
*
* @example
* import { one, unwrap } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const wo4 = one({ arr1, arr2, arr3 });
* const o4 = unwrap(wo4);
*
* @param wrapped
* @returns
*/
function unwrap(wrapped) { return wrapped[self]; }
const self = Symbol();
const oneTrap = {
get(target, p) {
if (p === self)
return target;
return Object.assign((...methodArgs) => {
let arg1;
if (methodArgs.length === 1 && typeof (arg1 = methodArgs[0]) === 'object' &&
arg1.hasOwnProperty(map)) {
return target.call({ [p]: arg1 });
}
else
return target.call({ [p]: { [args]: methodArgs } });
}, {
get value() { return target.get(p); }
});
},
set(target, p, value) {
target.set({ [p]: value });
return true;
},
deleteProperty(target, p) {
target.delete(p);
return true;
}
};
exports.One = One;
exports.View = View;
exports.args = args;
exports.map = map;
exports.one = one;
exports.unwrap = unwrap;
exports.wrap = wrap;

@@ -1,1 +0,516 @@

const e=Symbol(),t=Symbol();class n{one;map;constructor(e,t){this.one=e,this.map=t}#e(t){const n={};if(t.hasOwnProperty(e)){let s;for(let[r,o]of Object.entries(t[e]))s=this.map[r],n.hasOwnProperty(s)||(n[s]={[e]:{}}),n[s][e][r]=o}else for(let[s,r]of Object.entries(this.map))n[r]={[e]:{[s]:t}};return n}get(){return this.one.get(this.map)}set(e){return this.one.set(this.#e(e))}delete(){return this.one.delete(this.map)}call(e){return this.one.call(this.#e(e))}}class s{many;constructor(e){this.many=e}extend(e){let t;for(let[n,r]of Object.entries(e))this.many.hasOwnProperty(n)?(t=this.many[n],t instanceof s?t.extend(r):this.many[n]=r):this.many[n]=r;return this}contract(...e){for(let t of e)delete this.many[t];return this}slice(...e){const t={};for(let n of e)t[n]=this.many[n]||{};return new s(t)}view(e){return new n(this,e)}get(e,t){const n={};let r,o,i,f,a;if("string"==typeof e)for([o,f]of Object.entries(this.many))f instanceof s?n[o]=f.get(e,t):(r=f[e],t&&r instanceof s&&(r=r.many[t]),n[o]=r);else if(e instanceof Array)for([o,f]of Object.entries(this.many))if(f instanceof s)n[o]=f.get(e,t);else for(i of(n[o]=r={},e))a=f[i],t&&a instanceof s&&(a=a.many[t]),r[i]=a;else if("object"==typeof e)for(let[o,c]of Object.entries(e))if(f=this.many[o],f instanceof s)n[o]=f.get(c,t);else if(c instanceof Array)for(i of(n[o]=r={},c))a=f[i],t&&a instanceof s&&(a=a.many[t]),r[i]=a;else r=f[c],t&&r instanceof s&&(r=r.many[t]),n[o]=r;return n}set(t,n){let r,o,i,f;for(let[a,c]of Object.entries(t))if(c.hasOwnProperty(e))for([r,i]of Object.entries(c[e]))o=this.many[r],o instanceof s?o.set({prop:i},n):n?(f=o[a],f instanceof s&&f.many.hasOwnProperty(n)&&(f.many[n]=i)):o[a]=i;else for([r,o]of Object.entries(this.many))o instanceof s?o.set({prop:c},n):n?(f=o[a],f instanceof s&&f.many.hasOwnProperty(n)&&(f.many[n]=c)):o[a]=c;return this}delete(e){let t,n,r;if("string"==typeof e)for([t,r]of Object.entries(this.many))r instanceof s?r.delete(e):delete r[e];else if(e instanceof Array)for([t,r]of Object.entries(this.many))if(r instanceof s)r.delete(e);else for(n of e)delete r[n];else if("object"==typeof e)for(let[t,o]of Object.entries(e))if(r=this.many[t],r instanceof s)r.delete(o);else if(o instanceof Array)for(n of o)delete r[n];else delete r[o];return this}call(n){let r,o,i;const f={};let a;if("string"==typeof n&&(n=[n]),n instanceof Array)for(let e of n)for([r,o]of Object.entries(this.many))f[r]=o[e]?.();else for(let[c,l]of Object.entries(n))if(f[c]=a={},l.hasOwnProperty(e))for([r,i]of Object.entries(l[e]))o=this.many[r],o instanceof s?a[r]=o.call({prop:i}):i.hasOwnProperty(t)?a[r]=o[c]?.(...i[t]):a[r]=o[c]?.(i);else for([r,o]of Object.entries(this.many))o instanceof s?a[r]=o.call({prop:l}):l.hasOwnProperty(t)?a[r]=o[c]?.(...l[t]):a[r]=o[c]?.(l);return f}}function r(e){return o(new s(e))}function o(e){return new Proxy(e,a)}function i(e){return e[f]}const f=Symbol(),a={get:(n,s)=>s===f?n:Object.assign(((...r)=>{let o;return 1===r.length&&"object"==typeof(o=r[0])&&o.hasOwnProperty(e)?n.call({[s]:o}):n.call({[s]:{[t]:r}})}),{get value(){return n.get(s)}}),set:(e,t,n)=>(e.set({[t]:n}),!0),deleteProperty:(e,t)=>(e.delete(t),!0)};export{s as One,n as View,t as args,e as map,r as one,i as unwrap,o as wrap};
/**
* This module enables reactivity by exporting primitives for multiplying the effects of single operations.
*
* @module
*/
const map = Symbol(), args = Symbol();
class View {
one;
map;
/**
* Represents a single view of a `One` instance. A view can be described as an
* object comprising one property each from the different objects that are part of
* the `One` object. We can perform actions on a view without explicitly specifying
* the properties.
*
* @example
* import { One, View, args } from "deleight/onetomany";
* const one = new One({ a1: [], a2: [1, 2, 3, 4, 5] });
*
* const view = new View(one, { a1: 'push', a2: 'shift' })
* // push one array and simultaneously shift the other
* // to transfer content...
*
* view.call({ [args]: one.many.a2 })
*
* @param one
* @param map
*
* @constructor
*/
constructor(one, map) { this.one = one; this.map = map; }
#oneWhat(what) {
const oneWhat = {};
if (what.hasOwnProperty(map)) {
let prop;
for (let [key, val] of Object.entries(what[map])) {
prop = this.map[key];
if (!oneWhat.hasOwnProperty(prop))
oneWhat[prop] = { [map]: {} };
oneWhat[prop][map][key] = val;
}
}
else {
for (let [key, prop] of Object.entries(this.map))
oneWhat[prop] = { [map]: { [key]: what } };
}
return oneWhat;
}
get() { return this.one.get(this.map); }
set(what) { return this.one.set(this.#oneWhat(what)); }
delete() { return this.one.delete(this.map); }
call(what) { return this.one.call(this.#oneWhat(what)); }
}
class One {
many;
/**
* Creates a single object that propagates actions on it to multiple objects.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
*
* // set the same value on all objects
* o1.set({ p1: 78 });
*
* @param many
*/
constructor(many) { this.many = many; }
/**
* Joins `many` with `this.many` and returns `this`. The benefit of
* using this function instead of something like `Object.assign` is to handle some
* special cases and ensure that the updated `One` is (almost) correctly typed during
* development.
*
* If the same key exists in the joined objects, the new one will overwrite the
* current one, except if the current value is a `One` instance when `extend` will
* be called on it instead.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* const o2 = o1.extend({ fourth });
* // One({ first, second, third, fourth })
*
* @param many
*/
extend(many) {
let currentVal;
for (let [key, value] of Object.entries(many)) {
if (this.many.hasOwnProperty(key)) {
currentVal = this.many[key];
if (currentVal instanceof One) {
currentVal.extend(value);
}
else
this.many[key] = value;
}
else
this.many[key] = value;
}
return this;
}
/**
* The opposite of extend. Removes the specified keys from `this.many` and
* returns `this` typed differently.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* const o2 = o1.contract('first', 'third');
* // One({ second })
*
* @param keys
* @returns
*/
contract(...keys) {
for (let k of keys)
delete this.many[k];
return this;
}
/**
* Creates and returns another instance of `One` containing only the
* objects with these names. If a name is not present in `this.many`,
* a new object is created for it in the returned `One`.
*
* @example
* import { One } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* const o2 = o1.slice('first', 'second')
* // One({ first, second })
*
* @param names
*/
slice(...names) {
const many = {};
for (let name of names)
many[name] = this.many[name] || {};
return new One(many);
}
;
/**
* Creates and returns an instance of `View` for only the
* specified properties. `what` is an object mapping object keys (in `this.many`)
* to the property names.
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex } });
* o1.view({first: 'p1', second: 'p2'});
* view.get(); // { first: 78, second: 12 }
*
* @param map
*/
view(map) { return new View(this, map); }
;
/**
* Gets the property (or properties) with the specified name(s) from all the
* objects in `this.many`.
*
* 1. if `what` is a string, returns only the property specified (as an object mapping
* object key to property value).
* 2. if `what` is an array of strings, returns 'sub-objects' of all the objects
* consisting of the specified property names (also mapped like 1).
* 3. if what is an object, it is treated as a map of object names to property
* name(s) to return.
* 4. if an object in `this.many` is a `One` instance, its `get` method is
* called for its property value(s).
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex } });
*
* o1.get('p1');
* // { first: 78, second: 78, third: 78 }
*
* o1.get('p1', 'p2');
* // { first: { p1: 78, p2: 56 }, second: { p1: 78, p2: 12 }, third: { p1: 78, p2: 12 } }
*
* o1.get({ first: 'p2', second: 'p1' });
* // { first: 56, second: 78 }
*
* @param what
*/
get(what, valueKey) {
const result = {};
let val, key, item, obj, subVal;
if (typeof what === 'string') {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
result[key] = obj.get(what, valueKey);
else {
val = obj[what];
if (valueKey && val instanceof One)
val = val.many[valueKey];
result[key] = val;
}
}
}
else if (what instanceof Array) {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
result[key] = obj.get(what, valueKey);
else {
result[key] = val = {};
for (item of what) {
subVal = obj[item];
if (valueKey && subVal instanceof One)
subVal = subVal.many[valueKey];
val[item] = subVal;
}
}
}
}
else if (typeof what === 'object') {
for (let [key, props] of Object.entries(what)) {
obj = this.many[key];
if (obj instanceof One)
result[key] = obj.get(props, valueKey);
else {
if (props instanceof Array) {
result[key] = val = {};
for (item of props) {
subVal = obj[item];
if (valueKey && subVal instanceof One)
subVal = subVal.many[valueKey];
val[item] = subVal;
}
}
else {
val = obj[props];
if (valueKey && val instanceof One)
val = val.many[valueKey];
result[key] = val;
}
}
}
}
return result;
}
;
/**
* Sets one or more properties on all the objects in this instance.
* `what` is an object mapping property names to property values.
*
* Each value in `what` is set on all the objects in this `One`.
* The same value will be set unless the value is an object containing the `[map]` property.
* In such a case, the value of the property is treated as a map of object key (in `this.many`)
* to object property value. That is, the objects with corresponding
* keys will have their property values set to the corresponding values.
*
* This will call `set` on any nested `One` objects accordingly.
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex }, p3: complex });
*
* o1.get('p2', 'p3');
* // { first: { p2: 56, p3: complex }, second: { p2: 12, p3: complex }, third: { p2: 12, p3: complex } }
*
* @param what
*/
set(what, valueKey) {
let key, obj, subValue, propVal;
for (let [prop, value] of Object.entries(what)) {
if (value.hasOwnProperty(map)) {
for ([key, subValue] of Object.entries(value[map])) {
obj = this.many[key];
if (obj instanceof One)
obj.set({ prop: subValue }, valueKey);
else {
if (valueKey) {
propVal = obj[prop];
if (propVal instanceof One && propVal.many.hasOwnProperty(valueKey)) {
propVal.many[valueKey] = subValue;
}
}
else
obj[prop] = subValue;
}
}
}
else {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
obj.set({ prop: value }, valueKey);
else {
if (valueKey) {
propVal = obj[prop];
if (propVal instanceof One && propVal.many.hasOwnProperty(valueKey)) {
propVal.many[valueKey] = value;
}
}
else
obj[prop] = value;
}
}
}
}
return this;
}
;
/**
* Performs `delete` on the objects in this `One`. The argument is
* treated the same way as in `get`.
*
* @example
* import { One, map } from "deleight/onetomany";
* const first = {}, second = {}, third = {}, fourth = {};
* const many = { first, second, third };
* const o1 = new One(many);
* o1.set({ p1: 78 });
* const complex = { first: 56, second: 12, third: 12 };
* o1.set({ p2: { [map]: complex }, p3: complex });
*
* o1.delete('p1', 'p2');
* // One({ first: { p3: complex }, second: { p3: complex }, third: { p3: complex } })
*
* @param what
*/
delete(what) {
let key, item, obj;
if (typeof what === 'string') {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
obj.delete(what);
else
delete obj[what];
}
}
else if (what instanceof Array) {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
obj.delete(what);
else
for (item of what)
delete obj[item];
}
}
else if (typeof what === 'object') {
for (let [key, props] of Object.entries(what)) {
obj = this.many[key];
if (obj instanceof One)
obj.delete(props);
else {
if (props instanceof Array)
for (item of props)
delete obj[item];
else
delete obj[props];
}
}
}
return this;
}
;
/**
* Calls corresponding methods in a similar way to set. In this
* case, call arguments are interpreted instead of property values.
*
* Multiple arguments should be provided as an object with the `[arg]` property whose value
* should be an array containing the arguments. A regular array is considered a single argument.
*
* Nested one objects will have their call method invoked correspondingly.
*
* @example
* import { One, map, args } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const o4 = new One({ arr1, arr2, arr3 });
* o4.call({ push: 78 });
* o4.call({ push: { [args]: [56, 57] } });
* // arr1 = arr2 = arr3 = [78, 56, 57]
*
* @param what
*/
call(what) {
let key, obj, subValue;
const result = {};
let propResult;
if (typeof what === 'string')
what = [what];
if (what instanceof Array) {
for (let prop of what) {
for ([key, obj] of Object.entries(this.many)) {
result[key] = obj[prop]?.();
}
}
}
else {
for (let [prop, value] of Object.entries(what)) {
result[prop] = propResult = {};
if (value.hasOwnProperty(map)) {
for ([key, subValue] of Object.entries(value[map])) {
obj = this.many[key];
if (obj instanceof One)
propResult[key] = obj.call({ prop: subValue });
else {
if (subValue.hasOwnProperty(args))
propResult[key] = obj[prop]?.(...subValue[args]);
else
propResult[key] = obj[prop]?.(subValue);
}
}
}
else {
for ([key, obj] of Object.entries(this.many)) {
if (obj instanceof One)
propResult[key] = obj.call({ prop: value });
else {
if (value.hasOwnProperty(args))
propResult[key] = obj[prop]?.(...value[args]);
else
propResult[key] = obj[prop]?.(value);
}
}
}
}
}
return result;
}
}
/**
* A simple wrapper around `One` for a more concise syntax.
*
* @example
* import { one, map, args } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const o4 = one({ arr1, arr2, arr3 });
* o4.push(78, 56, 57);
* o4.push({ [map]: { arr1: 66, arr2: { [args]: [77, 88] }, arr3: 99 } });
* // arr1 === [78, 56, 57, 66]
* // arr2 === [78, 56, 57, 77, 88]
* // arr3 === [78, 56, 57, 99]
*
* @param many
* @returns
*/
function one(many) { return wrap(new One(many)); }
/**
* Wraps a pre-existing `One`
*
* @example
* import { One, wrap } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const o4 = new One({ arr1, arr2, arr3 });
* const wo4 = wrap(o4);
*
* @param one
* @returns
*/
function wrap(one) {
return new Proxy(one, oneTrap);
}
/**
* The opposite of `wrap`
*
* @example
* import { one, unwrap } from "deleight/onetomany";
* const arr1 = [], arr2 = [], arr3 = [];
* const wo4 = one({ arr1, arr2, arr3 });
* const o4 = unwrap(wo4);
*
* @param wrapped
* @returns
*/
function unwrap(wrapped) { return wrapped[self]; }
const self = Symbol();
const oneTrap = {
get(target, p) {
if (p === self)
return target;
return Object.assign((...methodArgs) => {
let arg1;
if (methodArgs.length === 1 && typeof (arg1 = methodArgs[0]) === 'object' &&
arg1.hasOwnProperty(map)) {
return target.call({ [p]: arg1 });
}
else
return target.call({ [p]: { [args]: methodArgs } });
}, {
get value() { return target.get(p); }
});
},
set(target, p, value) {
target.set({ [p]: value });
return true;
},
deleteProperty(target, p) {
target.delete(p);
return true;
}
};
export { One, View, args, map, one, unwrap, wrap };

@@ -1,1 +0,204 @@

"use strict";const e={before(e,t){t.parentNode?.insertBefore(e,t)},append(e,t){t.appendChild(e)}};exports.insert=function(t,r,o){o||(o=e.append);let n=t;n.next||(n=t[Symbol.iterator]());for(let e of r)o(e,n.next().value);return[t,r]},exports.inserter=e,exports.remove=function(e,t){t||(e=[...e]);for(let t of e)t.parentNode?.removeChild(t);return e},exports.set=function(e,t,r){const o={},n={};let s,l,i,f,a=new Map;for(let[e,r]of Object.entries(t))a.has(r)?n[e]=a.get(r):(s=r,s.next||(s=r[Symbol.iterator]()),o[e]=s,a.set(r,e));let p,c={};for(let t of e){for([l,i]of Object.entries(o))f=i.next().value,r&&void 0===f&&(f=""),c[l]=f,l.startsWith("_")?(l=l.slice(1),t.setAttribute(l,f)):t[l]=f;for([l,p]of Object.entries(n))l.startsWith("_")?(l=l.slice(1),t.setAttribute(l,c[p])):t[l]=c[p]}return[e,t]},exports.update=function(e,t,r){let o,n;const s=document.createComment(""),l=[];r||(e=Array.from(e),t=Array.from(t));for(let t of e)o=t.parentNode,n=s.cloneNode(!1),o?.replaceChild(n,t),l.push([n,o]);const i=l.values();let f;for(let e of t)f=i.next(),f.done?o.appendChild(e):([n,o]=f.value,o?.replaceChild(e,n));for(;!(f=i.next()).done;)[n,o]=f.value,o.removeChild(n);return[e,t]};
'use strict';
/**
* This module exports primitives for 'bulk' manipulation of the DOM.
*
* @module
*/
/**
* Insert the values using the elements as target. The way they are inserted
* depend on the inserter. If not provided, the default inserter will append the values
* to the corresponding elements.
*
* @example
* // Insert a span into all the children of the first main element:
* import { insert } from 'deleight/queryoperator';
* const span = document.createElement('span');
* const main = document.querySelector('main');
* insert(main.children, main.children.map(() => span.cloneNode()))
*
*
* @param {Iterable<Node>} elements The target nodes.
* @param {Iterable<Node>} values The new nodes to insert.
* @param {IInserter} [insertWith] The insertion function
*/
function insert(elements, values, insertWith) {
if (!insertWith)
insertWith = inserter.append; // the default inserter
let elements2 = elements;
if (!elements2.next)
elements2 = elements[Symbol.iterator]();
for (let value of values) {
insertWith(value, elements2.next().value);
}
return [elements, values];
}
/**
* Default inserters for use with `insert`
*/
const inserter = {
/**
* Inserts the node before the target using `insertBefore`
* @param {Node} node
* @param {Node} target
*/
before(node, target) {
target.parentNode?.insertBefore(node, target);
},
/**
* Append the node to the target using `appendChild`
* @param {Node} node
* @param {Node} target
*/
append(node, target) {
target.appendChild(node);
},
};
/**
* Set specified properties and/or attributes on the specified elements
* (or their children). Pass an iterable of elements (often an array) as the
* first arg and an object mapping property names to value iterables to be matched against
* the elenments at corresponding indices.
*
* If a key in `values` starts with the underscore (`_`), the attribute with
* the name following the underscore will be set.
*
*
* @example
* // Shuffle the class attributes of all the children of the first main element:
* import { set } from 'deleight/queryoperator';
* import { uItems } from 'deleight/generational';
* const main = document.querySelector('main');
* const values = uItems(main.children.map(c => c.className));
* set(main.children, {_class: values});
*
* @param {(Element|CSSStyleRule)[]} elements
* @param {ISetMap} values
* @param { boolean } undefinedIsEmpty
*/
function set(elements, values, undefinedIsEmpty) {
const localMemberValues = {};
const deps = {};
let memberValues2;
let allValues = new Map();
for (let [key, memberValues] of Object.entries(values)) {
if (allValues.has(memberValues))
deps[key] = allValues.get(memberValues);
else {
memberValues2 = memberValues;
if (!(memberValues2.next))
memberValues2 = memberValues[Symbol.iterator]();
localMemberValues[key] = memberValues2;
allValues.set(memberValues, key);
}
}
let member, memberValues, memberValue;
let currentValues = {}, dep;
for (let element of elements) {
for ([member, memberValues] of Object.entries(localMemberValues)) {
memberValue = memberValues.next().value;
if (undefinedIsEmpty && memberValue === undefined)
memberValue = '';
currentValues[member] = memberValue;
if (member.startsWith("_")) {
member = member.slice(1);
element.setAttribute(member, memberValue);
}
else {
element[member] = memberValue;
}
}
for ([member, dep] of Object.entries(deps)) {
if (member.startsWith("_")) {
member = member.slice(1);
element.setAttribute(member, currentValues[dep]);
}
else {
element[member] = currentValues[dep];
}
}
}
return [elements, values];
}
/**
* Correctly replace the specified nodes with corresponding values.
*
* This will materialize `elements` and `values` unless `lazy`
* is supplied as a truthy value.
*
* @example
* // Safely shuffle all the children of the first main element:
* import { update } from 'deleight/queryoperator';
* import { uItems } from 'deleight/generational';
* const main = document.querySelector('main');
* update(main.children, uItems(main.children))
*
* @param {Iterable<Node>} elements The nodes to replace.
* @param {Iterable<Node>} values The replacement nodes.
* @param { boolean } [lazy]
*/
function update(elements, values, lazy) {
let parentNode, tempNode;
const template = document.createComment(""); // document.createElement('template');
const temps = [];
if (!lazy) {
elements = Array.from(elements);
values = Array.from(values);
}
for (let element of elements) {
parentNode = element.parentNode;
tempNode = template.cloneNode(false);
parentNode?.replaceChild(tempNode, element);
temps.push([tempNode, parentNode]);
}
/* at this point we have replaced what we want to replace with temporary values */
const temps2 = temps.values();
let nextTemp;
for (let value of values) {
nextTemp = temps2.next();
if (!nextTemp.done) {
[tempNode, parentNode] = nextTemp.value;
parentNode?.replaceChild(value, tempNode);
}
else
parentNode.appendChild(value);
// this will allow us replace fewer nodes with more nodes if necessary.
}
while (!(nextTemp = temps2.next()).done) {
[tempNode, parentNode] = nextTemp.value;
parentNode.removeChild(tempNode);
} // this will allow us to replace more nodes with fewer nodes if necessary:
return [elements, values]; // we can, eg run cleanups or inits on either of these.
}
/**
* Remove the elements from their parent nodes.
*
* This will materialize `elements` unless `lazy`
* is supplied and its value is truthy.
*
* @example
* import { update } from 'deleight/queryoperator';
* const main = document.querySelector('main');
* remoove(main.children);
*
* @param {Iterable<Node>} elements
* @param { boolean } [lazy]
*/
function remove(elements, lazy) {
if (!lazy)
elements = [...elements];
for (let element of elements)
element.parentNode?.removeChild(element);
return elements; // we can, eg run cleanups on these.
}
/**
* notes:
* 1. add tests for `undefinedIsEmpty` parameter in `set`.
* 2. add tests for `update` using elements and values of different sizes.
*/
exports.insert = insert;
exports.inserter = inserter;
exports.remove = remove;
exports.set = set;
exports.update = update;

@@ -1,1 +0,198 @@

function e(e,o,r){r||(r=t.append);let n=e;n.next||(n=e[Symbol.iterator]());for(let e of o)r(e,n.next().value);return[e,o]}const t={before(e,t){t.parentNode?.insertBefore(e,t)},append(e,t){t.appendChild(e)}};function o(e,t,o){const r={},n={};let l,f,a,i,s=new Map;for(let[e,o]of Object.entries(t))s.has(o)?n[e]=s.get(o):(l=o,l.next||(l=o[Symbol.iterator]()),r[e]=l,s.set(o,e));let c,d={};for(let t of e){for([f,a]of Object.entries(r))i=a.next().value,o&&void 0===i&&(i=""),d[f]=i,f.startsWith("_")?(f=f.slice(1),t.setAttribute(f,i)):t[f]=i;for([f,c]of Object.entries(n))f.startsWith("_")?(f=f.slice(1),t.setAttribute(f,d[c])):t[f]=d[c]}return[e,t]}function r(e,t,o){let r,n;const l=document.createComment(""),f=[];o||(e=Array.from(e),t=Array.from(t));for(let t of e)r=t.parentNode,n=l.cloneNode(!1),r?.replaceChild(n,t),f.push([n,r]);const a=f.values();let i;for(let e of t)i=a.next(),i.done?r.appendChild(e):([n,r]=i.value,r?.replaceChild(e,n));for(;!(i=a.next()).done;)[n,r]=i.value,r.removeChild(n);return[e,t]}function n(e,t){t||(e=[...e]);for(let t of e)t.parentNode?.removeChild(t);return e}export{e as insert,t as inserter,n as remove,o as set,r as update};
/**
* This module exports primitives for 'bulk' manipulation of the DOM.
*
* @module
*/
/**
* Insert the values using the elements as target. The way they are inserted
* depend on the inserter. If not provided, the default inserter will append the values
* to the corresponding elements.
*
* @example
* // Insert a span into all the children of the first main element:
* import { insert } from 'deleight/queryoperator';
* const span = document.createElement('span');
* const main = document.querySelector('main');
* insert(main.children, main.children.map(() => span.cloneNode()))
*
*
* @param {Iterable<Node>} elements The target nodes.
* @param {Iterable<Node>} values The new nodes to insert.
* @param {IInserter} [insertWith] The insertion function
*/
function insert(elements, values, insertWith) {
if (!insertWith)
insertWith = inserter.append; // the default inserter
let elements2 = elements;
if (!elements2.next)
elements2 = elements[Symbol.iterator]();
for (let value of values) {
insertWith(value, elements2.next().value);
}
return [elements, values];
}
/**
* Default inserters for use with `insert`
*/
const inserter = {
/**
* Inserts the node before the target using `insertBefore`
* @param {Node} node
* @param {Node} target
*/
before(node, target) {
target.parentNode?.insertBefore(node, target);
},
/**
* Append the node to the target using `appendChild`
* @param {Node} node
* @param {Node} target
*/
append(node, target) {
target.appendChild(node);
},
};
/**
* Set specified properties and/or attributes on the specified elements
* (or their children). Pass an iterable of elements (often an array) as the
* first arg and an object mapping property names to value iterables to be matched against
* the elenments at corresponding indices.
*
* If a key in `values` starts with the underscore (`_`), the attribute with
* the name following the underscore will be set.
*
*
* @example
* // Shuffle the class attributes of all the children of the first main element:
* import { set } from 'deleight/queryoperator';
* import { uItems } from 'deleight/generational';
* const main = document.querySelector('main');
* const values = uItems(main.children.map(c => c.className));
* set(main.children, {_class: values});
*
* @param {(Element|CSSStyleRule)[]} elements
* @param {ISetMap} values
* @param { boolean } undefinedIsEmpty
*/
function set(elements, values, undefinedIsEmpty) {
const localMemberValues = {};
const deps = {};
let memberValues2;
let allValues = new Map();
for (let [key, memberValues] of Object.entries(values)) {
if (allValues.has(memberValues))
deps[key] = allValues.get(memberValues);
else {
memberValues2 = memberValues;
if (!(memberValues2.next))
memberValues2 = memberValues[Symbol.iterator]();
localMemberValues[key] = memberValues2;
allValues.set(memberValues, key);
}
}
let member, memberValues, memberValue;
let currentValues = {}, dep;
for (let element of elements) {
for ([member, memberValues] of Object.entries(localMemberValues)) {
memberValue = memberValues.next().value;
if (undefinedIsEmpty && memberValue === undefined)
memberValue = '';
currentValues[member] = memberValue;
if (member.startsWith("_")) {
member = member.slice(1);
element.setAttribute(member, memberValue);
}
else {
element[member] = memberValue;
}
}
for ([member, dep] of Object.entries(deps)) {
if (member.startsWith("_")) {
member = member.slice(1);
element.setAttribute(member, currentValues[dep]);
}
else {
element[member] = currentValues[dep];
}
}
}
return [elements, values];
}
/**
* Correctly replace the specified nodes with corresponding values.
*
* This will materialize `elements` and `values` unless `lazy`
* is supplied as a truthy value.
*
* @example
* // Safely shuffle all the children of the first main element:
* import { update } from 'deleight/queryoperator';
* import { uItems } from 'deleight/generational';
* const main = document.querySelector('main');
* update(main.children, uItems(main.children))
*
* @param {Iterable<Node>} elements The nodes to replace.
* @param {Iterable<Node>} values The replacement nodes.
* @param { boolean } [lazy]
*/
function update(elements, values, lazy) {
let parentNode, tempNode;
const template = document.createComment(""); // document.createElement('template');
const temps = [];
if (!lazy) {
elements = Array.from(elements);
values = Array.from(values);
}
for (let element of elements) {
parentNode = element.parentNode;
tempNode = template.cloneNode(false);
parentNode?.replaceChild(tempNode, element);
temps.push([tempNode, parentNode]);
}
/* at this point we have replaced what we want to replace with temporary values */
const temps2 = temps.values();
let nextTemp;
for (let value of values) {
nextTemp = temps2.next();
if (!nextTemp.done) {
[tempNode, parentNode] = nextTemp.value;
parentNode?.replaceChild(value, tempNode);
}
else
parentNode.appendChild(value);
// this will allow us replace fewer nodes with more nodes if necessary.
}
while (!(nextTemp = temps2.next()).done) {
[tempNode, parentNode] = nextTemp.value;
parentNode.removeChild(tempNode);
} // this will allow us to replace more nodes with fewer nodes if necessary:
return [elements, values]; // we can, eg run cleanups or inits on either of these.
}
/**
* Remove the elements from their parent nodes.
*
* This will materialize `elements` unless `lazy`
* is supplied and its value is truthy.
*
* @example
* import { update } from 'deleight/queryoperator';
* const main = document.querySelector('main');
* remoove(main.children);
*
* @param {Iterable<Node>} elements
* @param { boolean } [lazy]
*/
function remove(elements, lazy) {
if (!lazy)
elements = [...elements];
for (let element of elements)
element.parentNode?.removeChild(element);
return elements; // we can, eg run cleanups on these.
}
/**
* notes:
* 1. add tests for `undefinedIsEmpty` parameter in `set`.
* 2. add tests for `update` using elements and values of different sizes.
*/
export { insert, inserter, remove, set, update };

@@ -1,1 +0,242 @@

"use strict";const e=e=>{let t,s=0;for(let n=0,o=e.length;n<o;n++)t=e.charCodeAt(n),s=(s<<5)-s+t,s|=0;return s};class t{css;constructor(e){this.css=e}style(...e){let t;const s=[];for(let t of e)!(t instanceof ShadowRoot)&&t instanceof DocumentFragment?s.push(...Array.from(t.children)):s.push(t);for(let e of s){if(e instanceof Document||e instanceof ShadowRoot)t=e;else{const s=Array.from(e.childNodes);t=e.shadowRoot||e.attachShadow({mode:"open"}),e.innerHTML="",t.append(...s)}t.adoptedStyleSheets?.includes(this.css)||(t.adoptedStyleSheets=[...t.adoptedStyleSheets||[],this.css])}}remove(...e){let t;const s=[];for(let t of e)!(t instanceof ShadowRoot)&&t instanceof DocumentFragment?s.push(...Array.from(t.children)):s.push(t);for(let e of s)t=e.shadowRoot||e,(t instanceof ShadowRoot||t instanceof Document)&&t.adoptedStyleSheets.includes(this.css)&&t.adoptedStyleSheets.splice(t.adoptedStyleSheets.indexOf(this.css))}}exports.Sophistry=class{styles={};process(s,n){const o=[],l=[];if(s instanceof HTMLLinkElement&&"stylesheet"===s.getAttribute("rel")||s instanceof HTMLStyleElement){const h=s.getAttribute("s-ophistry")||s.getAttribute("href")?.split(".")[0]||e(s.outerHTML);if(this.styles.hasOwnProperty(h)&&!n)o.push(this.styles[h]);else{let e,n;this.styles.hasOwnProperty(h)?(n=this.styles[h],e=n.css):(s instanceof HTMLLinkElement&&"stylesheet"===s.getAttribute("rel")?(e=new CSSStyleSheet,l.push(fetch(s.getAttribute("href")).then((e=>e.text())).then((t=>e.replaceSync(t))))):s instanceof HTMLStyleElement&&(e=new CSSStyleSheet,e.replaceSync(s.textContent)),n=new t(e),this.styles[h]=n),o.push(n)}s.parentNode?.removeChild(s)}else{let e,t,h,r=s.children[0];for(;r;)e=r.nextElementSibling,[t,h]=this.process(r,n),o.push(...t),l.push(...h),r=e}return[o,l]}import(e,s){const n=new CSSStyleSheet,o=new t(n);this.styles[s||e.split(".")[0]]=o;return[o,fetch(e).then((e=>e.text())).then((e=>n.replaceSync(e)))]}set(e,s){if(this.styles.hasOwnProperty(e))this.styles[e].css.replaceSync(s);else{const n=new CSSStyleSheet;n.replaceSync(s),this.styles[e]=new t(n)}}},exports.StyleSheet=t;
'use strict';
/**
* This module supports CSS loading, caching and 'localised' reuse.
* The article at https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#applying_styles_inside_the_shadow_dom
* stated that programmatically creating stylesheets facilitates
* selective reuse (as we would like when working with web components). Considering that CSS is
* traditionally written declaratively, it is worthwile to try to reduce the
* programming involved to use CSS with web components.
*
* The main export here is {@link Sophistry}.
*
*
* @module
*/
/**
* An instance of Sophistry can be used to obtain and cache CSS Stylesheets
* which can be shared by multiple DOM elements. It can typically be very useful within
* web components.
*
*
*/
class Sophistry {
/**
* An cache for created SophistryStyleSheets.
*/
styles = {};
/**
* Processes and 'pops' all style tags within the root.
* Ensures that the same CSSStyleSheet can be reused across document trees (maindocument
* and shadow roots) instead of duplicated even when they have been
* created declaratively.
*
* If `replace` is truthy, any cached stylesheets with the same name (or hash) as a
* styleshhet within the root will be replaced (reactively).
*
*
* @example
* import { Sophistry } from 'deleight/sophistry';
* import { createFragment } from 'deleight/apriori';
* const mySophistry = new Sophistry();
* const element = createFragment(apriori.get('markup.html'));
* const [styles, promises] = mySophistry.process(element);
* document.body.append(element);
* for (let style of styles) style.style(element, document.body.firstElementChild);
*
* @param {Element} root
* @param {boolean} [replace]
* @returns {[StyleSheet[], Promise<any>[]]}
*/
process(root, replace) {
const styleSheets = [];
const promises = [];
if ((root instanceof HTMLLinkElement &&
root.getAttribute("rel") === "stylesheet") ||
root instanceof HTMLStyleElement) {
const name = root.getAttribute("s-ophistry") ||
root.getAttribute("href")?.split('.')[0] ||
hash(root.outerHTML);
if (this.styles.hasOwnProperty(name) && !replace)
styleSheets.push(this.styles[name]);
else {
let st, st2;
if (this.styles.hasOwnProperty(name)) {
st2 = this.styles[name];
st = st2.css;
}
else {
if (root instanceof HTMLLinkElement &&
root.getAttribute("rel") === "stylesheet") {
st = new CSSStyleSheet();
promises.push(fetch(root.getAttribute("href"))
.then((r) => r.text())
.then((t) => st.replaceSync(t)));
}
else if (root instanceof HTMLStyleElement) {
st = new CSSStyleSheet(); // root.sheet will not work if style has not been added to DOM!!!
st.replaceSync(root.textContent);
}
st2 = new StyleSheet(st);
this.styles[name] = st2;
}
styleSheets.push(st2);
}
root.parentNode?.removeChild(root);
}
else {
let node = root.children[0], node2;
let nodeStyleSheets, nodePromises;
while (node) {
node2 = node.nextElementSibling;
[nodeStyleSheets, nodePromises] = this.process(node, replace);
styleSheets.push(...nodeStyleSheets);
promises.push(...nodePromises);
node = node2;
}
}
return [styleSheets, promises];
}
/**
* Import a stylesheet defined in an external CSS file. Optionally
* specify a name for the imported style in the Sophystry cach ({@link Sophistry#styles}).
* The name will default to the portion of the link before the first
* apostrophe...
*
* @example
* import { Sophistry } from 'deleight/sophistry';
* const mySophistry = new Sophistry();
* const [style, onImport] = mySophistry.import('style.css');
*
* @param {string} link
* @param {string} [name]
* @returns {[StyleSheet, Promise<any>]}
*/
import(link, name) {
const st = new CSSStyleSheet();
const st2 = new StyleSheet(st);
this.styles[name || link.split(".")[0]] = st2;
const promise = fetch(link)
.then((r) => r.text())
.then((t) => st.replaceSync(t));
return [st2, promise];
}
/**
* Replaces the text of an existing stylesheet in the cach. This is reactive.
*
* @example
* import { Sophistry } from 'deleight/sophistry';
* const mySophistry = new Sophistry();
* mySophistry.set('style.css', await apriori.get('new-style.css')); // override everything.
*
* @param {string} name
* @param {string} css
* @returns
*/
set(name, css) {
if (this.styles.hasOwnProperty(name))
this.styles[name].css.replaceSync(css);
else {
const st = new CSSStyleSheet();
st.replaceSync(css);
this.styles[name] = new StyleSheet(st);
}
}
}
const hash = (str) => {
let newHash = 0, chr;
for (let i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
newHash = (newHash << 5) - newHash + chr;
newHash |= 0; // convert to 32 bit int.
}
return newHash;
};
/**
* This is used to wrap a CSSStyleSheet to provide convenient methods
* for styling and 'unstyling' elements.
*
* @example
* import { StyleSheet } from 'deleight/sophistry';
* const sss = new StyleSheet(css);
*
*/
class StyleSheet {
/**
* The wrapped CSS stylesheet.
*/
css;
/**
* Creates a new Sophistry stylesheet.
*
* @param {CSSStyleSheet} cssStyleSheet
* @constructor
*/
constructor(cssStyleSheet) {
this.css = cssStyleSheet;
}
/**
* Styles the elements with the wrapped CSSStylesheets.
* If an element is not the document or a shadow root, an open shadow
* root is created for it and then the rrot is styled.
*
* @example
* sss.style(...Array.from(document.body.children))
*
* @param {...T} elements
*/
style(...elements) {
let root;
const allElements = [];
for (let element of elements) {
if (!(element instanceof ShadowRoot) && element instanceof DocumentFragment)
allElements.push(...Array.from(element.children));
else
allElements.push(element);
}
for (let element of allElements) {
if (!(element instanceof Document) && !(element instanceof ShadowRoot)) {
const childNodes = Array.from(element.childNodes);
root = element.shadowRoot || element.attachShadow({ mode: "open" });
element.innerHTML = "";
root.append(...childNodes);
}
else
root = element;
if (!root.adoptedStyleSheets?.includes(this.css))
root.adoptedStyleSheets = [
...(root.adoptedStyleSheets || []),
this.css,
];
}
}
/**
* Removes the wrapped stylesheet from the elements (or their shadow roots).
*
* @example
* sss.remove(...Array.from(document.body.children))
*
*
* @param {...T} elements
*/
remove(...elements) {
let root;
const allElements = [];
for (let element of elements) {
if (!(element instanceof ShadowRoot) && element instanceof DocumentFragment)
allElements.push(...Array.from(element.children));
else
allElements.push(element);
}
for (let element of allElements) {
root = element.shadowRoot || element;
if (root instanceof ShadowRoot || root instanceof Document) {
if (root.adoptedStyleSheets.includes(this.css))
root.adoptedStyleSheets.splice(root.adoptedStyleSheets.indexOf(this.css));
}
}
}
}
exports.Sophistry = Sophistry;
exports.StyleSheet = StyleSheet;

@@ -1,1 +0,239 @@

class e{styles={};process(e,n){const o=[],l=[];if(e instanceof HTMLLinkElement&&"stylesheet"===e.getAttribute("rel")||e instanceof HTMLStyleElement){const h=e.getAttribute("s-ophistry")||e.getAttribute("href")?.split(".")[0]||t(e.outerHTML);if(this.styles.hasOwnProperty(h)&&!n)o.push(this.styles[h]);else{let t,n;this.styles.hasOwnProperty(h)?(n=this.styles[h],t=n.css):(e instanceof HTMLLinkElement&&"stylesheet"===e.getAttribute("rel")?(t=new CSSStyleSheet,l.push(fetch(e.getAttribute("href")).then((e=>e.text())).then((e=>t.replaceSync(e))))):e instanceof HTMLStyleElement&&(t=new CSSStyleSheet,t.replaceSync(e.textContent)),n=new s(t),this.styles[h]=n),o.push(n)}e.parentNode?.removeChild(e)}else{let t,s,h,c=e.children[0];for(;c;)t=c.nextElementSibling,[s,h]=this.process(c,n),o.push(...s),l.push(...h),c=t}return[o,l]}import(e,t){const n=new CSSStyleSheet,o=new s(n);this.styles[t||e.split(".")[0]]=o;return[o,fetch(e).then((e=>e.text())).then((e=>n.replaceSync(e)))]}set(e,t){if(this.styles.hasOwnProperty(e))this.styles[e].css.replaceSync(t);else{const n=new CSSStyleSheet;n.replaceSync(t),this.styles[e]=new s(n)}}}const t=e=>{let t,s=0;for(let n=0,o=e.length;n<o;n++)t=e.charCodeAt(n),s=(s<<5)-s+t,s|=0;return s};class s{css;constructor(e){this.css=e}style(...e){let t;const s=[];for(let t of e)!(t instanceof ShadowRoot)&&t instanceof DocumentFragment?s.push(...Array.from(t.children)):s.push(t);for(let e of s){if(e instanceof Document||e instanceof ShadowRoot)t=e;else{const s=Array.from(e.childNodes);t=e.shadowRoot||e.attachShadow({mode:"open"}),e.innerHTML="",t.append(...s)}t.adoptedStyleSheets?.includes(this.css)||(t.adoptedStyleSheets=[...t.adoptedStyleSheets||[],this.css])}}remove(...e){let t;const s=[];for(let t of e)!(t instanceof ShadowRoot)&&t instanceof DocumentFragment?s.push(...Array.from(t.children)):s.push(t);for(let e of s)t=e.shadowRoot||e,(t instanceof ShadowRoot||t instanceof Document)&&t.adoptedStyleSheets.includes(this.css)&&t.adoptedStyleSheets.splice(t.adoptedStyleSheets.indexOf(this.css))}}export{e as Sophistry,s as StyleSheet};
/**
* This module supports CSS loading, caching and 'localised' reuse.
* The article at https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#applying_styles_inside_the_shadow_dom
* stated that programmatically creating stylesheets facilitates
* selective reuse (as we would like when working with web components). Considering that CSS is
* traditionally written declaratively, it is worthwile to try to reduce the
* programming involved to use CSS with web components.
*
* The main export here is {@link Sophistry}.
*
*
* @module
*/
/**
* An instance of Sophistry can be used to obtain and cache CSS Stylesheets
* which can be shared by multiple DOM elements. It can typically be very useful within
* web components.
*
*
*/
class Sophistry {
/**
* An cache for created SophistryStyleSheets.
*/
styles = {};
/**
* Processes and 'pops' all style tags within the root.
* Ensures that the same CSSStyleSheet can be reused across document trees (maindocument
* and shadow roots) instead of duplicated even when they have been
* created declaratively.
*
* If `replace` is truthy, any cached stylesheets with the same name (or hash) as a
* styleshhet within the root will be replaced (reactively).
*
*
* @example
* import { Sophistry } from 'deleight/sophistry';
* import { createFragment } from 'deleight/apriori';
* const mySophistry = new Sophistry();
* const element = createFragment(apriori.get('markup.html'));
* const [styles, promises] = mySophistry.process(element);
* document.body.append(element);
* for (let style of styles) style.style(element, document.body.firstElementChild);
*
* @param {Element} root
* @param {boolean} [replace]
* @returns {[StyleSheet[], Promise<any>[]]}
*/
process(root, replace) {
const styleSheets = [];
const promises = [];
if ((root instanceof HTMLLinkElement &&
root.getAttribute("rel") === "stylesheet") ||
root instanceof HTMLStyleElement) {
const name = root.getAttribute("s-ophistry") ||
root.getAttribute("href")?.split('.')[0] ||
hash(root.outerHTML);
if (this.styles.hasOwnProperty(name) && !replace)
styleSheets.push(this.styles[name]);
else {
let st, st2;
if (this.styles.hasOwnProperty(name)) {
st2 = this.styles[name];
st = st2.css;
}
else {
if (root instanceof HTMLLinkElement &&
root.getAttribute("rel") === "stylesheet") {
st = new CSSStyleSheet();
promises.push(fetch(root.getAttribute("href"))
.then((r) => r.text())
.then((t) => st.replaceSync(t)));
}
else if (root instanceof HTMLStyleElement) {
st = new CSSStyleSheet(); // root.sheet will not work if style has not been added to DOM!!!
st.replaceSync(root.textContent);
}
st2 = new StyleSheet(st);
this.styles[name] = st2;
}
styleSheets.push(st2);
}
root.parentNode?.removeChild(root);
}
else {
let node = root.children[0], node2;
let nodeStyleSheets, nodePromises;
while (node) {
node2 = node.nextElementSibling;
[nodeStyleSheets, nodePromises] = this.process(node, replace);
styleSheets.push(...nodeStyleSheets);
promises.push(...nodePromises);
node = node2;
}
}
return [styleSheets, promises];
}
/**
* Import a stylesheet defined in an external CSS file. Optionally
* specify a name for the imported style in the Sophystry cach ({@link Sophistry#styles}).
* The name will default to the portion of the link before the first
* apostrophe...
*
* @example
* import { Sophistry } from 'deleight/sophistry';
* const mySophistry = new Sophistry();
* const [style, onImport] = mySophistry.import('style.css');
*
* @param {string} link
* @param {string} [name]
* @returns {[StyleSheet, Promise<any>]}
*/
import(link, name) {
const st = new CSSStyleSheet();
const st2 = new StyleSheet(st);
this.styles[name || link.split(".")[0]] = st2;
const promise = fetch(link)
.then((r) => r.text())
.then((t) => st.replaceSync(t));
return [st2, promise];
}
/**
* Replaces the text of an existing stylesheet in the cach. This is reactive.
*
* @example
* import { Sophistry } from 'deleight/sophistry';
* const mySophistry = new Sophistry();
* mySophistry.set('style.css', await apriori.get('new-style.css')); // override everything.
*
* @param {string} name
* @param {string} css
* @returns
*/
set(name, css) {
if (this.styles.hasOwnProperty(name))
this.styles[name].css.replaceSync(css);
else {
const st = new CSSStyleSheet();
st.replaceSync(css);
this.styles[name] = new StyleSheet(st);
}
}
}
const hash = (str) => {
let newHash = 0, chr;
for (let i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
newHash = (newHash << 5) - newHash + chr;
newHash |= 0; // convert to 32 bit int.
}
return newHash;
};
/**
* This is used to wrap a CSSStyleSheet to provide convenient methods
* for styling and 'unstyling' elements.
*
* @example
* import { StyleSheet } from 'deleight/sophistry';
* const sss = new StyleSheet(css);
*
*/
class StyleSheet {
/**
* The wrapped CSS stylesheet.
*/
css;
/**
* Creates a new Sophistry stylesheet.
*
* @param {CSSStyleSheet} cssStyleSheet
* @constructor
*/
constructor(cssStyleSheet) {
this.css = cssStyleSheet;
}
/**
* Styles the elements with the wrapped CSSStylesheets.
* If an element is not the document or a shadow root, an open shadow
* root is created for it and then the rrot is styled.
*
* @example
* sss.style(...Array.from(document.body.children))
*
* @param {...T} elements
*/
style(...elements) {
let root;
const allElements = [];
for (let element of elements) {
if (!(element instanceof ShadowRoot) && element instanceof DocumentFragment)
allElements.push(...Array.from(element.children));
else
allElements.push(element);
}
for (let element of allElements) {
if (!(element instanceof Document) && !(element instanceof ShadowRoot)) {
const childNodes = Array.from(element.childNodes);
root = element.shadowRoot || element.attachShadow({ mode: "open" });
element.innerHTML = "";
root.append(...childNodes);
}
else
root = element;
if (!root.adoptedStyleSheets?.includes(this.css))
root.adoptedStyleSheets = [
...(root.adoptedStyleSheets || []),
this.css,
];
}
}
/**
* Removes the wrapped stylesheet from the elements (or their shadow roots).
*
* @example
* sss.remove(...Array.from(document.body.children))
*
*
* @param {...T} elements
*/
remove(...elements) {
let root;
const allElements = [];
for (let element of elements) {
if (!(element instanceof ShadowRoot) && element instanceof DocumentFragment)
allElements.push(...Array.from(element.children));
else
allElements.push(element);
}
for (let element of allElements) {
root = element.shadowRoot || element;
if (root instanceof ShadowRoot || root instanceof Document) {
if (root.adoptedStyleSheets.includes(this.css))
root.adoptedStyleSheets.splice(root.adoptedStyleSheets.indexOf(this.css));
}
}
}
}
export { Sophistry, StyleSheet };

@@ -1,1 +0,100 @@

"use strict";const t=Symbol(),o=Symbol(),r=Symbol();function e(t){const o=Object.assign(((...o)=>{if(!o.length)return t;for(let t of o)t instanceof Function&&t(r);return r}),{obj:t}),r=new Proxy(o,n);return o.proxy=r,r}const n={get(n,s){if(s===r)return(...t)=>(Object.assign(n.obj,...t),n.proxy);if(s===o)return t=>{for(let[o,r]of Object.entries(t)){if(!n.obj.hasOwnProperty(o))throw new Error(`You cannot assign a new property (${o}) with this method.`);n.obj[o]=r}return n.proxy};if(s===t)return t=>{for(let[o,r]of Object.entries(t))r(e(n.obj[o]));return n.proxy};{const t=n.obj[s];return t instanceof Function?(...t)=>(n.obj[s](...t),n.proxy):t}}};exports.ASSIGN=r,exports.SET=o,exports.WITH=t,exports.With=e;
'use strict';
/**
* This module exports {@link With} function for creating more concise and structured code.
*
* @module
*/
/**
* Used to obtain a context (Recursive object) around a property of
* the currently wrapped object. This is to continue the chain inwards
* to increase the concision even further.
*
* @example
*With(obj)[WITH]({o1: o1 => {assert.equal(o1.c, 1);}, o2: o2 => {assert.equal(o2.c, 2);}})
*/
const WITH = Symbol();
/**
* Used to set existing properties on the wrapped object and return the same object.
*
* @example
* With(obj).set({knownA:1, knownB:2}).method1().method2('...')() // final call unwraps the object.
*/
const SET = Symbol();
/**
* Used to set any properties on the wrapped object and return the same object.
*
* @example
* With(obj)[SET]({prop3: 5, prop4: 6}).inc().prop2
*/
const ASSIGN = Symbol();
/**
* Behaves like the 'with' construct in many langusges. All method calls
* with return the same object and we can also access the special [assign]
* method to set properties without breaking the chain. Used for more
* concise syntax in some scenarios.
*
* @example
* import { With, ASSIGN } from 'deleight/withly';
* const el = With(document.createElement('div')).append().append()[ASSIGN]().append()().append();
*
* @param obj
* @returns
*/
function With(obj) {
const target = Object.assign((...args) => {
if (!args.length)
return obj;
for (let arg of args)
if (arg instanceof Function)
arg(proxy);
return proxy;
}, { obj });
const proxy = new Proxy(target, trap);
target.proxy = proxy;
return proxy;
}
const trap = {
get(target, p) {
if (p === ASSIGN) {
return (...objs) => {
Object.assign(target.obj, ...objs);
return target.proxy;
};
}
else if (p === SET) {
return (arg) => {
for (let [k, v] of Object.entries(arg)) {
if (target.obj.hasOwnProperty(k))
target.obj[k] = v;
else
throw new Error(`You cannot assign a new property (${k}) with this method.`);
}
return target.proxy;
};
}
else if (p === WITH) {
return (arg) => {
for (let [k, v] of Object.entries(arg))
v(With(target.obj[k]));
return target.proxy;
};
}
else {
const res = target.obj[p];
if (!(res instanceof Function))
return res;
return (...args) => {
target.obj[p](...args);
return target.proxy;
};
}
},
};
// const el1 = With(document.createElement('div')).append('').append()[SET]({className: 'cls1', textContent: 'Wow!'}).append('abc').append()();
// const el2 = With(document.createElement('div')).append().append()[WITH]({style: st => st[SET]({})});
exports.ASSIGN = ASSIGN;
exports.SET = SET;
exports.WITH = WITH;
exports.With = With;

@@ -1,1 +0,95 @@

const o=Symbol(),r=Symbol(),t=Symbol();function n(o){const r=Object.assign(((...r)=>{if(!r.length)return o;for(let o of r)o instanceof Function&&o(t);return t}),{obj:o}),t=new Proxy(r,e);return r.proxy=t,t}const e={get(e,i){if(i===t)return(...o)=>(Object.assign(e.obj,...o),e.proxy);if(i===r)return o=>{for(let[r,t]of Object.entries(o)){if(!e.obj.hasOwnProperty(r))throw new Error(`You cannot assign a new property (${r}) with this method.`);e.obj[r]=t}return e.proxy};if(i===o)return o=>{for(let[r,t]of Object.entries(o))t(n(e.obj[r]));return e.proxy};{const o=e.obj[i];return o instanceof Function?(...o)=>(e.obj[i](...o),e.proxy):o}}};export{t as ASSIGN,r as SET,o as WITH,n as With};
/**
* This module exports {@link With} function for creating more concise and structured code.
*
* @module
*/
/**
* Used to obtain a context (Recursive object) around a property of
* the currently wrapped object. This is to continue the chain inwards
* to increase the concision even further.
*
* @example
*With(obj)[WITH]({o1: o1 => {assert.equal(o1.c, 1);}, o2: o2 => {assert.equal(o2.c, 2);}})
*/
const WITH = Symbol();
/**
* Used to set existing properties on the wrapped object and return the same object.
*
* @example
* With(obj).set({knownA:1, knownB:2}).method1().method2('...')() // final call unwraps the object.
*/
const SET = Symbol();
/**
* Used to set any properties on the wrapped object and return the same object.
*
* @example
* With(obj)[SET]({prop3: 5, prop4: 6}).inc().prop2
*/
const ASSIGN = Symbol();
/**
* Behaves like the 'with' construct in many langusges. All method calls
* with return the same object and we can also access the special [assign]
* method to set properties without breaking the chain. Used for more
* concise syntax in some scenarios.
*
* @example
* import { With, ASSIGN } from 'deleight/withly';
* const el = With(document.createElement('div')).append().append()[ASSIGN]().append()().append();
*
* @param obj
* @returns
*/
function With(obj) {
const target = Object.assign((...args) => {
if (!args.length)
return obj;
for (let arg of args)
if (arg instanceof Function)
arg(proxy);
return proxy;
}, { obj });
const proxy = new Proxy(target, trap);
target.proxy = proxy;
return proxy;
}
const trap = {
get(target, p) {
if (p === ASSIGN) {
return (...objs) => {
Object.assign(target.obj, ...objs);
return target.proxy;
};
}
else if (p === SET) {
return (arg) => {
for (let [k, v] of Object.entries(arg)) {
if (target.obj.hasOwnProperty(k))
target.obj[k] = v;
else
throw new Error(`You cannot assign a new property (${k}) with this method.`);
}
return target.proxy;
};
}
else if (p === WITH) {
return (arg) => {
for (let [k, v] of Object.entries(arg))
v(With(target.obj[k]));
return target.proxy;
};
}
else {
const res = target.obj[p];
if (!(res instanceof Function))
return res;
return (...args) => {
target.obj[p](...args);
return target.proxy;
};
}
},
};
// const el1 = With(document.createElement('div')).append('').append()[SET]({className: 'cls1', textContent: 'Wow!'}).append('abc').append()();
// const el2 = With(document.createElement('div')).append().append()[WITH]({style: st => st[SET]({})});
export { ASSIGN, SET, WITH, With };
{
"name": "deleight",
"version": "4.1.1",
"version": "4.1.2",
"description": "A group of 10 libraries for writing accessible and joyfully interactive web applications with traditional HTML, CSS and JavaScript.",

@@ -5,0 +5,0 @@ "type": "module",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc