New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

polylib

Package Overview
Dependencies
Maintainers
2
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

polylib - npm Package Compare versions

Comparing version
1.1.13
to
1.2.0
+40
-14
common.js

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

import { PlSVGElement } from './pl-element.js';
/**

@@ -8,10 +10,11 @@ *

export function setAttrValue(node, attr, val) {
if ( node.isSVGCustomElement ) node = node.root;
if (val !== undefined && val !== false)
if (node.isSVGCustomElement) node = node.root;
if (val !== undefined && val !== false) {
node.setAttribute(attr, val === true ? '' : val);
else
} else {
node.removeAttribute(attr);
}
}
export function getAttrValue(node, attr) {
if ( node instanceof PlSVGElement) node = node.root;
if (node instanceof PlSVGElement) node = node.root;
return node.getAttribute(attr);

@@ -44,3 +47,3 @@ }

if (path.length > 0 && obj) {
path.forEach( p => obj = obj?.[p] );
path.forEach(p => obj = obj?.[p]);
}

@@ -57,4 +60,4 @@ return obj;

export function isSubPath(a, b) {
let ax = a + '.';
let bx = b + '.';
const ax = a + '.';
const bx = b + '.';
return ax.startsWith(bx.slice(0, ax.length));

@@ -65,9 +68,9 @@ }

root.querySelectorAll('template')
.forEach( t => {
.forEach((t) => {
cb(t);
forEachTemplateRecursive(t.content, cb)
forEachTemplateRecursive(t.content, cb);
});
}
export function fromDashed (str) {
export function fromDashed(str) {
return str.replace(/-[a-z\u00E0-\u00F6\u00F8-\u00FE-]/g, function (match) {

@@ -90,3 +93,3 @@ return match.slice(1).toUpperCase();

export function getNPath(root, node) {
let path = [];
const path = [];
while (node && node !== root) {

@@ -99,7 +102,7 @@ path.unshift([...node.parentNode.childNodes].indexOf(node));

export function findByNPath(node, path) {
return path.reduce((n, i) => n.childNodes[i] , node);
return path.reduce((n, i) => n.childNodes[i], node);
}
export function getRandomId() {
return (Math.random() + 1).toString(36).substring(2)
return (Math.random() + 1).toString(36).substring(2);
}

@@ -109,2 +112,25 @@

return string.replace(/([a-z\d])([A-Z-])/g, '$1-$2').toLowerCase();
}
}
export function getBindValue(bind) {
const dv = bind.depend.map((p) => {
if (/['"]/.test(p)) {
return p.replace(/["']/g, '');
} else {
return bind.initiator[p]?.get(p);
}
});
if (dv.length > 1) {
const [fn, ...args] = dv;
if (!fn) {
console.error('Function not found in context: %s(%s)', bind.depend[0], bind.depend.slice(1).join(','));
return;
}
// TODO: ctx for function
return fn.apply(bind.initiator[bind.depend[0]], args);
} else {
return dv[0];
}
}
export function css(str) {
if (document.adoptedStyleSheets) {
let sheet = new CSSStyleSheet();
const sheet = new CSSStyleSheet();
sheet.replaceSync(str);
return sheet;
} else {
let sheet = document.createElement('style');
const sheet = document.createElement('style');
sheet.innerText = str;
return sheet;
}
}
}

@@ -1,2 +0,5 @@

import {getProp, isSubPath, normalizePath, stringPath} from "../../common.js";
import { getProp, isSubPath, normalizePath, stringPath } from '../../common.js';
const WMH_MAX_VALUE = 2 ** 32;
let wmh = 0;

@@ -7,11 +10,15 @@ export const ContextMixin = s => class dataContext extends s {

set(path, value, wmh) {
let xpath = normalizePath(path);
let x = xpath.pop();
let obj = getProp(this, xpath);
if(obj == null) return;
let oldValue = obj[x];
//TODO: move _props to props mixin
if (obj._props?.[x]) obj._props[x] = value; else obj[x] = value;
if (value === oldValue/* && xl === 1*/) return;
this.notifyChange({ action: 'upd', path, value, oldValue, wmh});
const xpath = normalizePath(path);
const x = xpath.pop();
const obj = getProp(this, xpath);
if (obj === null || obj === undefined) return;
const oldValue = obj[x];
// TODO: move _props to props mixin
if (obj._props?.[x]) {
obj._props[x] = value;
} else {
obj[x] = value;
}
if (value === oldValue/* && xl === 1 */) return;
this.notifyChange({ action: 'upd', path, value, oldValue, wmh });
}

@@ -23,7 +30,8 @@

}
push(path,value) {
let target = this.get(path);
push(path, value) {
const target = this.get(path);
if (Array.isArray(target)) {
if (!Array.isArray(value)) value = [value]
let len = target.push(...value);
if (!Array.isArray(value)) value = [value];
const len = target.push(...value);
this.notifyChange({ action: 'splice', path, target, index: target.length - value.length, addedCount: value.length, added: value });

@@ -33,8 +41,16 @@ return len;

}
splice(path, index, deletedCount, ...added) {
let target = this.get(path);
let deleted = target.splice(index, deletedCount, ...added);
this.notifyChange({ action: 'splice', path, target, index: index, deletedCount, addedCount: added?.length, added, deleted });
const target = this.get(path);
const deleted = target.splice(index, deletedCount, ...added);
this.notifyChange({ action: 'splice', path, target, index, deletedCount, addedCount: added?.length, added, deleted });
}
assign(path, object) {
Object.entries(object)
.forEach(([property, value]) => {
this.set(path + '.' + property, value);
});
}
/** @typedef {Object} DataMutation

@@ -58,9 +74,10 @@ * @property {String} action

notifyChange(m) {
let path = normalizePath(m.path);
const path = normalizePath(m.path);
const textPath = path.join('.');
m.wmh = m.wmh || getNextWM();
if (this.wmh[path.join('.')] >= m.wmh ) return;
this.wmh[path.join('.')] = m.wmh;
if (this.wmh[textPath] >= m.wmh && this.wmh[textPath] - m.wmh < WMH_MAX_VALUE / 2) return;
this.wmh[textPath] = m.wmh;
if (m.value === m.oldValue && m.action === 'upd' && path.length === 1) return;
this.applyEffects(m);
let name = path[0];
const name = path[0];
// Порядок важен, чтобы вызывались сначала внутренние обсерверы компонента, а потом остальные

@@ -72,18 +89,17 @@ if (this._dp?.[name]?.observer) {

this.dispatchEvent(new CustomEvent(name + '-changed', { detail: m }));
// Таймаут нужен, чтобы дождаться выполнения всех кольцевых зависимостей
setTimeout(() => {
delete this.wmh[path.join('.')];
}, 0);
}
forwardNotify(mutation, from, to) {
let r = new RegExp(`^(${from})(\..)?`);
const r = new RegExp(`^(${from})(\\..)?`);
let path = mutation.path;
if (Array.isArray(path)) path = path.join('.');
path = path.replace(r, to+'$2');
mutation = {...mutation, path};
path = path.replace(r, to + '$2');
mutation = { ...mutation, path };
this.notifyChange(mutation);
}
hasProp(name) {
return name in this;
}
addEffect(path, effect) {

@@ -96,2 +112,3 @@ if (this._em[path]) {

}
/**

@@ -102,13 +119,31 @@ *

applyEffects(m) {
let effectMap = this._em;
effectMap && Object.keys(effectMap).forEach( k => {
const effectMap = this._em;
effectMap && Object.keys(effectMap).forEach((k) => {
if (!m || isSubPath(stringPath(m.path), k)) {
effectMap?.[k]?.forEach( f => f(m) );
effectMap?.[k]?.forEach(f => f(m));
}
});
}
}
_hooks = new Map();
registerHook(hook, cb) {
if (!this._hooks.has(hook)) this._hooks.set(hook, new Set());
this._hooks.get(hook).add(cb);
}
runHooks(hook) {
this._hooks.get(hook)?.forEach(hook => hook());
}
disconnectedCallback() {
this.runHooks('disconnected');
}
};
export function getNextWM() {
return wmh++;
}
wmh++;
if (wmh >= WMH_MAX_VALUE) {
wmh = 0;
}
return wmh;
}

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

import {findByNPath} from "../../common.js";
import {createBind, getBackApl} from "./template.js";
import { findByNPath, getBindValue } from '../../common.js';
import { createBind, getBackApl } from './template.js';

@@ -8,45 +8,49 @@ export class TemplateInstance {

constructor(template) {
this.tpl = template
this.tpl = template;
this.clone = this.tpl.getClone();
this.tpl.svgCE.forEach( path => {
let node = findByNPath(this.clone, path);
let constr = customElements.get(node.getAttribute('is'));
node.ctx = new constr({lightDom: true, root: node});
this.tpl.afterStampHooks.push( { path , hook: node => node.ctx.connectedCallback() } )
})
this.tpl.svgCE.forEach((path) => {
const node = findByNPath(this.clone, path);
const constr = customElements.get(node.getAttribute('is'));
node.ctx = new constr({ lightDom: true, root: node });
this.tpl.afterStampHooks.push({ path, hook: node => node.ctx.connectedCallback() });
});
// save clone node list to future detach
this._nodes = [...this.clone.childNodes];
this.binds = this.tpl.binds.map( b => ({...b, node: findByNPath(this.clone, b.path) }));
this._nodes = [...this.clone.childNodes];
this.binds = this.tpl.binds.map(b => ({ ...b, node: findByNPath(this.clone, b.path) }));
}
attach(target, before, context) {
this.ctx = Array.isArray(context) ? context : [context];
let tw = document.createTreeWalker(this.clone, NodeFilter.SHOW_COMMENT, { acceptNode: n => n._tpl ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT } );
while( tw.nextNode() ) tw.currentNode._hctx = this.ctx;
const tw = document.createTreeWalker(this.clone, NodeFilter.SHOW_COMMENT, { acceptNode: n => n._tpl ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT });
while (tw.nextNode()) tw.currentNode._hctx = this.ctx;
this.tpl.stampHooks.forEach( h => h.hook(findByNPath(this.clone, h.path), this.ctx));
this.tpl.stampHooks.forEach(h => h.hook(findByNPath(this.clone, h.path), this.ctx));
// build effect map
this.binds.forEach(b => {
this.binds.forEach((b) => {
this.attachBind(b);
})
});
// apply binds
this.applyBinds();
this.insert(target, before);
this.tpl.afterStampHooks.forEach( h => h.hook(findByNPath({childNodes: this._nodes}, h.path), this.ctx));
this.tpl.afterStampHooks.forEach(h => h.hook(findByNPath({ childNodes: this._nodes }, h.path), this.ctx));
return this;
}
insert(target, before) {
target = before?.parentNode ?? target;
if (before)
if (before) {
target.insertBefore(this.clone, before);
else
} else {
target.appendChild(this.clone);
}
}
attachBind(bind) {
let node = bind.node;
const node = bind.node;
if (!node) return;
bind.f = (m) => this.applyBind(bind,m);
bind.depend?.forEach(d => {
if (!bind.initiator) bind.initiator = {};
bind.f = m => this.applyBind(bind, m);
if (!bind.initiator) bind.initiator = {};
bind.depend?.forEach((d) => {
bind.initiator[d] = this.addEffect(d, bind.f);
})
});
if (bind.twoSide) {

@@ -56,46 +60,31 @@ getBackApl(bind)(bind.node, this.ctx);

}
applyBind(bind, m) {
let node = bind.node;
const node = bind.node;
if (!node) return;
let val = this.getBindValue(bind);
let val = getBindValue(bind);
val = bind.negate ? !val : val;
bind.apl(node.ctx || node , this.ctx, m, val, bind.initiator[bind.depend[0]] );
bind.apl(node.ctx || node, this.ctx, m, val, bind.initiator[bind.depend[0]]);
}
applyBinds() {
this.binds.forEach(bind => {
this.binds.forEach((bind) => {
this.applyBind(bind);
});
}
getBindValue(bind) {
let dv = bind.depend.map(p => {
if (/['"]/.test(p)) {
return p.replace(/["']/g, "")
} else {
return bind.initiator[p]?.get(p);
}
});
if (dv.length > 1) {
let [fn,...args] = dv;
if (!fn) {
console.error('Function not found in context: %s(%s)', bind.depend[0], bind.depend.slice(1).join(','));
return;
detach() {
this.nti.forEach(t => t.detach());
// TODO: detach property effects, events etc...
this.binds.forEach((b) => {
if (b.initiator) {
Object.entries(b.initiator).forEach(([k, i]) => {
const ind = i?._em[k].indexOf(b.f);
if (ind >= 0) i._em[k].splice(ind, 1);
});
}
//TODO: ctx for function
return fn.apply(bind.initiator[bind.depend[0]], args);
} else {
return dv[0];
}
}
detach() {
this.nti.forEach(t => t.detach() );
//TODO: detach property effects, events etc...
this.binds.forEach(b => {
if (b.initiator) Object.entries(b.initiator).forEach( ([k,i]) => {
let ind = i?._em[k].indexOf(b.f);
if (ind >=0) i._em[k].splice(ind,1);
})
});
this._nodes.forEach( n => n.remove() )
this._nodes.forEach(n => n.remove());
}
/**

@@ -108,3 +97,6 @@ *

// find dataContext for property
let ctx = this.ctx.find( c => c.hasProp?.(path.split('.')[0]) );
const prop = path.split('.')[0];
const ctx = this.ctx.find((c) => {
return c.hasProp?.(prop);
});
ctx?.addEffect(path, cb);

@@ -115,19 +107,19 @@ return ctx;

removeBind(path, prop) {
let targetNode = findByNPath({childNodes: this._nodes}, path);
const targetNode = findByNPath({ childNodes: this._nodes }, path);
if (!targetNode) return;
let tib = this.binds;
let binds = tib.filter( i => i.node === targetNode && i.name === prop );
const tib = this.binds;
const binds = tib.filter(i => i.node === targetNode && i.name === prop);
//check and remove current bind if exist
binds.forEach( b => {
// check and remove current bind if exist
binds.forEach((b) => {
if (b.twoSide) {
targetNode.removeEventListener(b.eventBB, b.funcBB);
}
b.depend.forEach( d => {
let em = (b.initiator[d]?._em ?? this._em)[d];
if (em ) {
let ind = em.indexOf(b.f);
if (ind >= 0) em.splice(ind,1);
b.depend.forEach((d) => {
const em = (b.initiator[d]?._em ?? this._em)[d];
if (em) {
const ind = em.indexOf(b.f);
if (ind >= 0) em.splice(ind, 1);
}
})
});
delete tib[tib.indexOf(b)];

@@ -139,13 +131,14 @@ });

this.removeBind(path, property);
let bind = createBind(property, value);
const bind = createBind(property, value);
bind.path = path;
this.binds.push(bind);
this.attachBind(bind);
//ti.addEffect(bindVal.name2 ?? bindVal.name, bind.f);
// ti.addEffect(bindVal.name2 ?? bindVal.name, bind.f);
// call apply to set prop value via property effect
this.applyBind(bind);
}
querySelector(selector) {
return this.clone.querySelector(selector);
}
}
}

@@ -1,4 +0,18 @@

import {setAttrValue, toDashed} from "../../common.js";
import {getNextWM} from "./ctx.js";
import { setAttrValue, toDashed } from '../../common.js';
import { getNextWM } from './ctx.js';
let timer = null;
let pending = [];
function addPendingInitialization(f) {
if (!timer) {
timer = setTimeout(() => {
timer = null;
const current = pending.slice();
pending = [];
current.forEach(f => f());
}, 0);
}
pending.push(f);
}
export const PropertiesMixin = s => class PropMixin extends s {

@@ -10,50 +24,55 @@ _props = {};

let inst = this.constructor;
let pi = [];
const pi = [];
while (inst) {
if (inst.hasOwnProperty('properties')) pi.unshift(inst.properties);
if (Object.prototype.hasOwnProperty.call(inst, 'properties')) pi.unshift(inst.properties);
inst = inst.__proto__;
}
this._dp = {};
//copy props from static properties with destruction to avoid future change default value in prototype
pi.forEach( i => Object.entries(i).forEach( ([k,v]) => this._dp[k] = {...v}));
Object.keys(this._dp).forEach( p => {
// copy props from static properties with destruction to avoid future change default value in prototype
pi.forEach(i => Object.entries(i).forEach(([k, v]) => this._dp[k] = { ...v }));
Object.keys(this._dp).forEach((p) => {
// возможно значение уже назначено снаружи или задано в атрибуте, запоминаем и используем его вместо дефолтного
let attrVal = (config?.root ?? this).getAttribute?.(toDashed(p));
const attrVal = (config?.root ?? this).getAttribute?.(toDashed(p));
// убираем атрибуты для свойств, если они не отображаются в атрибуты
if (attrVal !== null && !this._dp[p].reflectToAttribute) this.removeAttribute?.(toDashed(p));
let val = (this.hasOwnProperty(p) ? this[p] : undefined) ?? (this._dp[p].type === Boolean ? (attrVal !== null ? attrVal !== 'false' : undefined) : attrVal);
const val = (Object.prototype.hasOwnProperty.call(this, p) ? this[p] : undefined) ?? (this._dp[p].type === Boolean ? (attrVal !== null ? attrVal !== 'false' : undefined) : attrVal);
Object.defineProperty(this, p, {
get: () => this._props[p],
set: (value) => {
let oldValue = this._props[p];
const oldValue = this._props[p];
this._props[p] = value;
if (oldValue !== value) this.notifyChange({ action: 'upd', path: p, value, oldValue });
},
}
});
if (typeof this._dp[p].value === 'function') this._dp[p].value = this._dp[p].value();
if (typeof this._dp[p].value === 'function') this._dp[p].value = this._dp[p].value();
this._props[p] = val ?? this._dp[p].value;
if (this._dp[p].reflectToAttribute) {
this.addEventListener(p+'-changed', () => this.reflectToAttribute(p, this._props[p]) );
this.addEventListener(p + '-changed', () => this.reflectToAttribute(p, this._props[p]));
}
});
setTimeout( () => {
Object.keys(this._props).forEach( p => {
if (this._props[p] !== this._dp[p]?.value) this.notifyChange({ action: 'upd', path: p, value: this._props[p], init: true, wmh: getNextWM() })
})
})
addPendingInitialization(() => {
Object.keys(this._props).forEach((p) => {
if (this._props[p] !== this._dp[p]?.value) {
this.notifyChange({action: 'upd', path: p, value: this._props[p], init: true, wmh: getNextWM()});
}
});
});
}
connectedCallback() {
super.connectedCallback?.();
Object.keys(this._dp).forEach( p => {
Object.keys(this._dp).forEach((p) => {
if (this._dp[p].reflectToAttribute) {
//TODO: думается надо делать property effect
this.reflectToAttribute(p,this._props[p]);
// TODO: думается надо делать property effect
this.reflectToAttribute(p, this._props[p]);
}
});
}
reflectToAttribute(name, val) {
if (this._dp[name].type === Boolean)
val = !!val;
setAttrValue(/** @type HTMLElement */this,toDashed(name),val);
if (this._dp[name].type === Boolean) {
val = Boolean(val);
}
setAttrValue(/** @type HTMLElement */this, toDashed(name), val);
}
}
};

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

import {getNPath, getRandomId, normalizePath} from "../../common.js";
import {createBind, DIRECTIVE_SCHEME, Template} from "./template.js";
import {ContextMixin} from "./ctx.js";
import {PropertiesMixin} from "./properties.js";
import {TemplateInstance} from "./instance.js";
import { getNPath, getRandomId, normalizePath } from '../../common.js';
import { createBind, DIRECTIVE_SCHEME, Template } from './template.js';
import { ContextMixin } from './ctx.js';
import { PropertiesMixin } from './properties.js';
import { TemplateInstance } from './instance.js';

@@ -14,22 +14,25 @@ class Repeater extends PropertiesMixin(ContextMixin(EventTarget)) {

anchor: { type: Object }
}
constructor(tpl,anchor) {
};
constructor(tpl, anchor) {
super();
this.tpl = tpl;
this.anchor = anchor;
if(this.items) {
this.renderItems(this.items,anchor)
if (this.items) {
this.renderItems(this.items, anchor);
}
}
renderItems(items, sibling) {
return items && items.map( i => this.renderItem(i, sibling) );
return items && items.map(i => this.renderItem(i, sibling));
}
renderItem(item, sibling) {
if (!this.tpl) return;
let inst = new TemplateInstance(this.tpl);
const inst = new TemplateInstance(this.tpl);
let itemContext = new RepeatItem(item, this.as, (ctx, m) => this.onItemChanged(ctx, m));
itemContext._ti = inst
const itemContext = new RepeatItem(item, this.as, (ctx, m) => this.onItemChanged(ctx, m));
itemContext._ti = inst;
inst._nodes.forEach(i => i._item = itemContext);
inst.attach(null, sibling || this.anchor, [itemContext, this, ...this.pti ]);
inst.attach(null, sibling || this.anchor, [itemContext, this, ...this.pti]);
return itemContext;

@@ -45,3 +48,3 @@ }

onChangeData(val, old, mutation) {
let [, index, ...rest] = normalizePath(mutation.path);
const [, index, ...rest] = normalizePath(mutation.path);
switch (mutation.action) {

@@ -51,3 +54,3 @@ case 'splice':

// ensure that path for this repeater
//TODO: update clone indexes on splices
// TODO: update clone indexes on splices
if (mutation.path === 'items') {

@@ -59,6 +62,5 @@ for (let ind = mutation.index; ind < mutation.deletedCount + mutation.index; ind++) {

// added
let sibling = this.clones[mutation.index]?._ti._nodes[0];
let clones = this.renderItems(mutation.added, sibling, mutation.index);
const sibling = this.clones[mutation.index]?._ti._nodes[0];
const clones = this.renderItems(mutation.added, sibling, mutation.index);
this.clones.splice(mutation.index, 0, ...clones);
this.anchor.dispatchEvent(new CustomEvent('dom-changed', { bubbles: true, composed: true }));
break;

@@ -68,14 +70,14 @@ }

case 'upd':
if (Number.isInteger(+index) && +index >= 0) {
if (Number.isInteger(Number(index)) && Number(index) >= 0) {
// ищем клон
let clone = this.clones[+index];
let path = [this.as, ...rest].join('.');
const clone = this.clones[Number(index)];
const path = [this.as, ...rest].join('.');
if (path === this.as) {
clone.set(this.as, mutation.value);
} else {
clone.applyEffects({...mutation, path});
clone.applyEffects({ ...mutation, path });
}
} else if (index === undefined) {
if (old !== val) {
let items = this.items?.slice?.() || [];
const items = this.items?.slice?.() || [];
let i = 0;

@@ -86,9 +88,8 @@ while (items.length && i < this.clones.length) {

}
if ( i < this.clones.length ) {
let deleted = this.clones.splice(i, this.clones.length - i);
deleted.forEach( c => this.detachClone(c));
if (i < this.clones.length) {
const deleted = this.clones.splice(i, this.clones.length - i);
deleted.forEach(c => this.detachClone(c));
}
if ( items.length ) this.clones.push(...this.renderItems(items, this.anchor));
if (items.length) this.clones.push(...this.renderItems(items, this.anchor));
}
this.anchor.dispatchEvent(new CustomEvent('dom-changed', { bubbles: true, composed: true }));
}

@@ -98,5 +99,5 @@ break;

}
onItemChanged(ctx, m) {
let ind = this.clones.findIndex( i => i[this.as] === ctx[this.as]);
const ind = this.clones.findIndex(i => i[this.as] === ctx[this.as]);
if (ind < 0) console.warn('repeat item not found');

@@ -106,14 +107,16 @@ if (m.path === this.as) {

} else {
this.forwardNotify(m,this.as, 'items.'+ind);
this.forwardNotify(m, this.as, 'items.' + ind);
}
}
}
dirtyRefresh() {
this.clones.forEach(c => this.detachClone(c));
this.clones = this.renderItems(this.items, this.anchor) ||[];
this.clones = this.renderItems(this.items, this.anchor) || [];
}
detachClone(clone) {
clone._ti.detach();
//clone.dom.forEach(n => n.remove())
// clone.dom.forEach(n => n.remove())
}
detach() {

@@ -129,4 +132,5 @@ this.clones.forEach(c => this.detachClone(c));

this[as] = item;
this.addEffect(as, m => cb(this, m))
this.addEffect(as, m => cb(this, m));
}
get model() {

@@ -138,18 +142,22 @@ return this[this.as];

export default function repeatDirective(node, template) {
let content = template.svg ? template.tpl.content.childNodes[0] : template.tpl.content;
let nPath = getNPath(content, node);
const content = template.svg ? template.tpl.content.childNodes[0] : template.tpl.content;
const nPath = getNPath(content, node);
// add 'as' bind first to ensure it assigned before 'items'
let as = node.getAttribute(DIRECTIVE_SCHEME+':as') ?? 'item';
const as = node.getAttribute(DIRECTIVE_SCHEME + ':as') ?? 'item';
let b = createBind('as', as, nPath);
if (b) template.binds.push(b); else node.as = node.getAttribute(DIRECTIVE_SCHEME+':as');
b = createBind('items', node.getAttribute(DIRECTIVE_SCHEME+':repeat'), nPath);
if (b) {
template.binds.push(b);
} else {
node.as = node.getAttribute(DIRECTIVE_SCHEME + ':as');
}
b = createBind('items', node.getAttribute(DIRECTIVE_SCHEME + ':repeat'), nPath);
if (b) template.binds.push(b);
node.removeAttribute(DIRECTIVE_SCHEME+':repeat');
node.removeAttribute(DIRECTIVE_SCHEME+':as');
let id = getRandomId();
let ph = document.createComment('repeat:'+id);
node.removeAttribute(DIRECTIVE_SCHEME + ':repeat');
node.removeAttribute(DIRECTIVE_SCHEME + ':as');
const id = getRandomId();
const ph = document.createComment('repeat:' + id);
node.parentNode.replaceChild(ph, node);
let tpl = new Template(node);
const tpl = new Template(node);
template.nestedTemplates.set(id, tpl);
template.stampHooks.push({path: nPath, hook: stampRepeater});
template.stampHooks.push({ path: nPath, hook: stampRepeater });
tpl.id = id;

@@ -167,2 +175,2 @@ tpl.as = as;

ctx[0]._ti.nti.push(node.ctx);
}
}

@@ -1,5 +0,4 @@

import {fixText, fromDashed, getRandomId, normalizePath, setAttrValue} from '../../common.js'
import {TemplateInstance} from "./instance.js";
import { fixText, fromDashed, getRandomId, normalizePath, setAttrValue } from '../../common.js';
import { TemplateInstance } from './instance.js';
export const DIRECTIVE_SCHEME = 'd';

@@ -14,3 +13,3 @@ export const Directives = {};

if (Array.isArray(str.raw)) str = str.raw.join('');
return new Template( str, { svg: true });
return new Template(str, { svg: true });
}

@@ -26,3 +25,3 @@

this.svg = opt?.svg === true;
/** @type: HTMLTemplateElement */
/** @type HTMLTemplateElement */
let node;

@@ -33,11 +32,10 @@ if (tpl instanceof HTMLTemplateElement) {

node = document.createElement('template');
if (tpl instanceof Element)
if (tpl instanceof Element) {
node.content.replaceChildren(tpl);
else {
} else {
if (this.svg) {
let svg = document.createElementNS("http://www.w3.org/2000/svg",'svg');
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
node.content.appendChild(svg);
svg.innerHTML = tpl;
} else
node.innerHTML = tpl;
} else { node.innerHTML = tpl; }
}

@@ -48,12 +46,13 @@ }

this.init(this.svg ? node.content.childNodes[0] : node.content);
}
}
init(content) {
this.walkNode(content, []);
}
walkNode(node, path) {
if (node.attributes && [...node.attributes].find( n => n.name.indexOf(':') >=0 && n.name.split(':')[0] === DIRECTIVE_SCHEME)) {
if (node.attributes && [...node.attributes].find(n => n.name.indexOf(':') >= 0 && n.name.split(':')[0] === DIRECTIVE_SCHEME)) {
// ..directive
[...node.attributes].forEach( a => {
let d = Directives[a.name.split(':')[1]];
[...node.attributes].forEach((a) => {
const d = Directives[a.name.split(':')[1]];
d?.(node, this);

@@ -63,5 +62,5 @@ });

if (node.nodeName === 'TEMPLATE') {
let id = getRandomId();
let ph = document.createComment('tpl:'+id);
let tpl = new Template(node);
const id = getRandomId();
const ph = document.createComment('tpl:' + id);
const tpl = new Template(node);
this.nestedTemplates.set(id, tpl);

@@ -72,10 +71,10 @@ tpl.id = id;

}
//TODO: move to form component
// TODO: move to form component
if (node.localName?.match(/\w+-/)) {
if (node.namespaceURI === 'http://www.w3.org/2000/svg') {
let svg = document.createElementNS("http://www.w3.org/2000/svg",'svg');
if (node.namespaceURI === 'http://www.w3.org/2000/svg') {
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.replaceChildren(...node.childNodes);
let tpl = new Template(svg, { svg: true });
let newNode = document.createElementNS('http://www.w3.org/2000/svg', 'g');
[...node.attributes].forEach( i => newNode.setAttribute(i.name, node.getAttribute(i.name)) );
const tpl = new Template(svg, { svg: true });
const newNode = document.createElementNS('http://www.w3.org/2000/svg', 'g');
[...node.attributes].forEach(i => newNode.setAttribute(i.name, node.getAttribute(i.name)));
newNode.setAttribute('is', node.localName);

@@ -87,32 +86,33 @@ node.parentNode.replaceChild(newNode, node);

}
for ( let i of node.childNodes ) {
this.walkNode(i, [...path, [...node.childNodes].indexOf(i)])
for (const i of node.childNodes) {
this.walkNode(i, [...path, [...node.childNodes].indexOf(i)]);
}
if (node instanceof Element) {
let bind = getAttrBinds(node, path);
const bind = getAttrBinds(node, path);
if (bind) this.binds.push(...bind);
} else if (node.nodeType === document.TEXT_NODE) {
let bind = getTextBind(node, path);
const bind = getTextBind(node, path);
if (bind) this.binds.push(...bind);
}
}
}
stamp(context) {
console.log('stamp template, deprecated', this);
let instance = new TemplateInstance(this);
console.warn('stamp template, deprecated', this);
const instance = new TemplateInstance(this);
instance.attach(context.root);
return instance;
}
getClone() {
let clone = this.tpl.content.cloneNode(true);
if (this.svg && clone.childNodes[0]?.nodeName==='svg') {
let nodes = clone.childNodes[0].childNodes
const clone = this.tpl.content.cloneNode(true);
if (this.svg && clone.childNodes[0]?.nodeName === 'svg') {
const nodes = clone.childNodes[0].childNodes;
clone.replaceChildren(...nodes);
}
if (this.nestedTemplates.size > 0) {
let nodeIterator = document.createNodeIterator(clone, NodeFilter.SHOW_COMMENT);
const nodeIterator = document.createTreeWalker(clone, NodeFilter.SHOW_COMMENT);
let node;
while (node = nodeIterator.nextNode()) {
let id = (node.textContent.split(":")[1] || "").trim();
while ((node = nodeIterator.nextNode())) {
const id = (node.textContent.split(':')[1] || '').trim();
node._tpl = this.nestedTemplates.get(id);

@@ -125,5 +125,4 @@ }

let pattern = /(\[\[.*?]]|{{.*?}})/gm;
const pattern = /(\[\[.*?]]|{{.*?}})/gm;
/**

@@ -137,10 +136,11 @@ *

function getAttrBinds(node, path) {
if (node.attributes)
if (node.attributes) {
return [...node.attributes]
.map( attr => {
let b = createBind(attr.nodeName, attr.nodeValue, path);
.map((attr) => {
const b = createBind(attr.nodeName, attr.nodeValue, path);
if (b) node.removeAttribute(attr.nodeName);
return b;
})
.filter( i => i );
.filter(i => i);
}
}

@@ -151,11 +151,11 @@

// split text node and replace original node with parts
let parts = node.textContent.match(/(\[\[.+?]])|(.+?(?=\[\[))|.+/mg);
let binds = [];
let base = path.at(-1);
const parts = node.textContent.match(/(\[\[.+?]])|(.+?(?=\[\[))|.+/mg);
const binds = [];
const base = path.at(-1);
/** @type Node[] */
let nodes = parts.map( (p,index) => {
const nodes = parts.map((p, index) => {
if (p[0] === '[') {
let negate = !!p.match(/\[\[!/);
let depend = getDependence(p);
let bind = { path: path.slice(0,-1).concat([base+index]), type: 'text', depend, textContent: p, negate };
const negate = Boolean(p.match(/\[\[!/));
const depend = getDependence(p);
const bind = { path: path.slice(0, -1).concat([base + index]), type: 'text', depend, textContent: p, negate };
bind.apl = getTextApl(bind);

@@ -167,4 +167,4 @@ binds.push(bind);

}
})
nodes.forEach( n => {
});
nodes.forEach((n) => {
node.parentNode.insertBefore(n, node);

@@ -189,7 +189,7 @@ });

export function createBind(attrName, attrValue, path) {
let match = attrValue.match(/(\[\[(.*?)]]|{{(.*?)}})/gm);
const match = attrValue.match(/(\[\[(.*?)]]|{{(.*?)}})/gm);
if (match) {
let depend = getDependence(attrValue);
const depend = getDependence(attrValue);
/** @type TemplateBind */
let sbnd = {
const sbnd = {
path,

@@ -203,3 +203,3 @@ type: null,

if (attrName.indexOf('on-') === 0) {
sbnd.type = 'event'
sbnd.type = 'event';
sbnd.name = fromDashed(attrName.slice(3));

@@ -223,3 +223,3 @@ sbnd.apl = getEventApl(sbnd);

* Expression can be:
* {{prop}} - two way bind
* {{prop}} - two-way bind
* [[!prop]] - bind with negation

@@ -231,9 +231,9 @@ * [[function(prop,'constString')]] - function with arguments

export function getDependence(expr) {
let match = expr.match(/(\[\[(.*?)]]|{{(.*?)(:(?<evt>.*?))?}})/m);
const match = expr.match(/(\[\[(.*?)]]|{{(.*?)(:(?<evt>.*?))?}})/m);
if (match) {
let prop = (match[2] || match[3]).trim();
if (prop[0] === '!') prop = prop.slice(1);
let fm = prop.match(/(?<fname>[\w\d]+)\((?<args>.*)\)/)
if( fm ) {
return [fm.groups.fname,...fm.groups.args?.split(',').map(i => i.trim())];
const fm = prop.match(/(?<fname>[\w\d]+)\((?<args>.*)\)/);
if (fm) {
return [fm.groups.fname, ...fm.groups.args.split(',').map(i => i.trim())];
} else {

@@ -243,3 +243,3 @@ return [prop];

} else {
console.log('unsupported expression:', expr);
console.warn('unsupported expression:', expr);
}

@@ -252,12 +252,14 @@ }

if (typeof val === 'object' && mutation) {
if(b.depend.length === 1)
if (b.depend.length === 1) {
node.forwardNotify?.(mutation, b.depend[0], b.name);
else
} else {
node.notifyChange?.({ path: b.name, action: 'upd', wmh: mutation.wmh, value: val });
}
}
} else {
if (node.set)
if (node.set) {
node.set(b.name, val, mutation?.wmh);
else
} else {
node[b.name] = val;
}
}

@@ -273,3 +275,3 @@ };

}
let fn = val;
const fn = val;
!node._listeners && (node._listeners = {});

@@ -280,17 +282,18 @@ if (node._listeners[b.name]) {

}
let f = e => {
let model = {};
const f = (e) => {
const model = {};
let found = false;
ctx.forEach( ctxModel => {
ctx.forEach((ctxModel) => {
// поднимаемся наверх по всем вложенным контекстам, собираем полную модель
if (ctxModel.model) {
if (ctxModel.as)
model[ctxModel.as] = ctxModel.model;
else
Object.assign( model, ctxModel.model);
found = true;
if (ctxModel.model) {
if (ctxModel.as) {
model[ctxModel.as] = ctxModel.model;
} else {
Object.assign(model, ctxModel.model);
}
found = true;
}
});
if (found) e.model = Object.assign( e.model ?? {}, model);
if (found) e.model = Object.assign(e.model ?? {}, model);
fn.call(self || ctx[0], e);

@@ -318,6 +321,6 @@ };

if (content instanceof Template) {
//TODO: move this to instance
let instance = new TemplateInstance(content);
// TODO: move this to instance
const instance = new TemplateInstance(content);
node._ti = instance;
instance.attach(null, node, [node, ...ctx, ...(content._hctx)??[]]);
instance.attach(null, node, [node, ...ctx, ...(content._hctx) ?? []]);
ctx[0]._ti.nti.push(instance);

@@ -336,3 +339,3 @@ content = '';

if (event.detail.wmh <= b.initiator[b.depend[0]]?.wmh[b.name] || event.detail.init) return;
if (typeof node[b.name] === 'object' && event.detail.value === event.detail.oldValue || normalizePath(event.detail.path).length > 1) {
if ((typeof node[b.name] === 'object' && event.detail.value === event.detail.oldValue) || normalizePath(event.detail.path).length > 1) {
b.initiator[b.depend[0]].forwardNotify?.(event.detail, b.name, b.depend[0]);

@@ -343,4 +346,4 @@ } else {

};
(node.ctx || node).addEventListener( b.eventBB, b.funcBB);
(node.ctx || node).addEventListener(b.eventBB, b.funcBB);
};
}
}

@@ -1,9 +0,8 @@

export { PlElement, PlSVGElement } from "./pl-element.js"
export { Template, html, svg } from "./engine/v1/template.js";
export { css } from "./engine/v1/css.js"
export { TemplateInstance } from "./engine/v1/instance.js"
export { PlElement, PlSVGElement } from './pl-element.js';
export { Template, html, svg } from './engine/v1/template.js';
export { css } from './engine/v1/css.js';
export { TemplateInstance } from './engine/v1/instance.js';
import {Directives} from "./engine/v1/template.js";
import repeat from "./engine/v1/repeat.js";
Directives['repeat'] = repeat;
import { Directives } from './engine/v1/template.js';
import repeat from './engine/v1/repeat.js';
Directives['repeat'] = repeat;
{
"name": "polylib",
"description": "A simple library for creating fast, lightweight web components.",
"version": "1.1.13",
"version": "1.2.0",
"license": "MIT",

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

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

import {PlTemplateMixin} from "./template-mixin.js";
import {PropertiesMixin} from "./engine/v1/properties.js";
import {ContextMixin} from "./engine/v1/ctx.js";
import { PlTemplateMixin } from './template-mixin.js';
import { PropertiesMixin } from './engine/v1/properties.js';
import { ContextMixin } from './engine/v1/ctx.js';
export class PlElement extends PlTemplateMixin(PropertiesMixin(ContextMixin(HTMLElement))) {
//static template;
// static template;
_$ = {};

@@ -16,18 +16,21 @@ /**

this.$ = new Proxy(this._$, {
get: (target,name) => {
get: (target, name) => {
if (!(name in target)) {
target[name] = this.root.querySelector('#'+name) ?? this._ti.querySelector('#'+name);
target[name] = this.root.querySelector('#' + name) ?? this._ti.querySelector('#' + name);
target.registerHook?.('disconnected', () => {
delete target[name];
});
}
return target[name];
}
})
});
}
connectedCallback() {
super.connectedCallback()
super.connectedCallback();
}
}
export class PlSVGElement extends PlTemplateMixin(PropertiesMixin(ContextMixin(EventTarget))) {
//static template;
// static template;
_$ = {};

@@ -43,10 +46,10 @@ isSVGCustomElement = true;

this.$ = new Proxy(this._$, {
get: (target,name) => {
get: (target, name) => {
if (!(name in target)) {
target[name] = this.root.querySelector('#'+name) ?? this._ti.querySelector('#'+name);
target[name] = this.root.querySelector('#' + name) ?? this._ti.querySelector('#' + name);
}
return target[name];
}
})
});
}
}
}

@@ -1,4 +0,6 @@

import {TemplateInstance} from "./index.js";
import { TemplateInstance } from './index.js';
import { getBindValue } from './common.js';
const PlTemplateMixin = s => class plTplMixin extends s {
_observersBinds = [];
/**

@@ -9,32 +11,77 @@ * @constructor

* @param {boolean} [config.delegatesFocus] - delegatesFocus flag for shadowRoot
* @param {boolean} [config.root] - alternate root for lightDom
*/
constructor(config) {
super(config);
this.root = config?.lightDom ? config?.root ?? this : this.attachShadow({ mode: 'open', delegatesFocus: config?.delegatesFocus });
// setup observers
if (this.constructor.observers?.length > 0) {
this.createObserversBinds();
}
this.root = config?.lightDom
? config?.root ?? this
: this.attachShadow({ mode: 'open', delegatesFocus: config?.delegatesFocus });
}
createObserversBinds() {
this.constructor.observers.forEach((observer) => {
const match = observer.match(/(?<fname>[\w\d]+)\((?<args>.*)\)/);
if (match) {
const args = match.groups.args.split(',').map(i => i.trim());
const depend = [match.groups.fname, ...args];
const bind = {
type: 'observer',
depend
};
this._observersBinds.push(bind);
}
});
}
connectedCallback() {
super.connectedCallback();
let tpl = this.constructor.template;
const tpl = this.constructor.template;
if (tpl) {
let inst = new TemplateInstance(tpl);
const inst = new TemplateInstance(tpl);
this._ti = inst;
inst.attach(this.root, undefined, this);
}
// attach observers effects
this._observersBinds.forEach((bind) => {
attachObserversBind(bind, [this]);
});
// append styles
if (this.constructor.css) {
if (this.constructor.css instanceof CSSStyleSheet) {
if (this.root.adoptedStyleSheets)
if (this.root.adoptedStyleSheets) {
this.root.adoptedStyleSheets = [...this.root.adoptedStyleSheets, this.constructor.css];
else
} else {
this.root.getRootNode().adoptedStyleSheets = [...this.root.getRootNode().adoptedStyleSheets, this.constructor.css];
}
} else {
this.root.append(this.constructor.css.cloneNode(true))
this.root.append(this.constructor.css.cloneNode(true));
}
}
}
disconnectedCallback() {
super.disconnectedCallback();
this._ti?.detach();
}
};
export function attachObserversBind(bind, contexts) {
bind.f = () => getBindValue(bind);
if (!bind.initiator) bind.initiator = {};
bind.depend?.forEach((d) => {
const ctx = contexts.find((c) => {
return c.hasProp?.(d);
});
bind.initiator[d] = ctx;
ctx.addEffect(d, bind.f);
});
}
export {PlTemplateMixin};
export { PlTemplateMixin };