@twobirds/microcomponents 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci.yml +1 -0
- package/README.md +33 -14
- package/assets/documentation/MicroComponents.md +228 -0
- package/assets/my-tag.css +4 -0
- package/assets/test.html +5 -1
- package/dist/MC.d.ts +32 -0
- package/dist/MC.d.ts.map +1 -0
- package/dist/MC.min.js +414 -0
- package/dist/MC.ts +350 -0
- package/dist/MCbundle.d.ts +8 -0
- package/dist/MCbundle.d.ts.map +1 -0
- package/dist/MCbundle.min.js +1 -0
- package/dist/MCbundle.ts +8 -0
- package/dist/McEvent.d.ts +15 -0
- package/dist/McEvent.d.ts.map +1 -0
- package/dist/McEvent.ts +96 -0
- package/dist/autoloader.d.ts +2 -0
- package/dist/autoloader.d.ts.map +1 -0
- package/dist/autoloader.ts +98 -0
- package/dist/customElements.d.ts +5 -0
- package/dist/customElements.d.ts.map +1 -0
- package/dist/customElements.min.js +1 -0
- package/dist/customElements.ts +67 -0
- package/dist/customObservables.d.ts +3 -0
- package/dist/customObservables.d.ts.map +1 -0
- package/dist/customObservables.min.js +1 -0
- package/dist/customObservables.ts +340 -0
- package/dist/customSelectors.d.ts +108 -0
- package/dist/customSelectors.d.ts.map +1 -0
- package/dist/customSelectors.min.js +1 -0
- package/dist/customSelectors.ts +1634 -0
- package/dist/formValues.d.ts +4 -0
- package/dist/formValues.d.ts.map +1 -0
- package/dist/formValues.ts +275 -0
- package/dist/formvValues.min.js +1 -0
- package/dist/helpers.d.ts +11 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.ts +173 -0
- package/dist/microcomponents.es3.d.ts +1 -20
- package/dist/microcomponents.es3.d.ts.map +1 -1
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/package.json +11 -7
- package/public/MC.js +273 -0
- package/public/MC.min.js +414 -0
- package/public/MCbundle.js +8 -0
- package/public/MCbundle.min.js +1 -0
- package/public/McEvent.js +74 -0
- package/public/MicroComponents.svg +15 -0
- package/public/assets/search.js +1 -1
- package/public/autoloader.js +57 -0
- package/public/classes/MC.DC.html +247 -0
- package/public/classes/MC.MC.html +150 -0
- package/public/classes/MC.McBase.html +123 -0
- package/public/classes/MCbundle.McEvent.html +184 -0
- package/public/customElements.js +48 -0
- package/public/customElements.min.js +1 -0
- package/public/customObservables.js +255 -0
- package/public/customObservables.min.js +1 -0
- package/public/customSelectors.js +1088 -0
- package/public/customSelectors.min.js +1 -0
- package/public/documentation/MicroComponents.md +228 -0
- package/public/formValues.js +211 -0
- package/public/formvValues.min.js +1 -0
- package/public/functions/MC.addListener.html +73 -0
- package/public/functions/MC.removeListener.html +69 -0
- package/public/functions/MCbundle._d.html +85 -0
- package/public/functions/MCbundle._t.html +85 -0
- package/public/functions/MCbundle.debounce.html +85 -0
- package/public/functions/MCbundle.deepEqual.html +85 -0
- package/public/functions/MCbundle.defineCE.html +98 -0
- package/public/functions/MCbundle.formValues.html +89 -0
- package/public/functions/MCbundle.getCE.html +83 -0
- package/public/functions/MCbundle.htmlToElements.html +83 -0
- package/public/functions/MCbundle.isObject.html +83 -0
- package/public/functions/MCbundle.kebabToPascal.html +83 -0
- package/public/functions/MCbundle.makeCO.html +85 -0
- package/public/functions/MCbundle.nameSpace.html +89 -0
- package/public/functions/MCbundle.parse.html +85 -0
- package/public/helpers.js +128 -0
- package/public/index.html +22 -13
- package/public/interfaces/MC.HTMLMcElement.html +3605 -0
- package/public/interfaces/MCbundle.DSelector.html +1010 -0
- package/public/interfaces/MCbundle.LooseObject.html +80 -0
- package/public/interfaces/MCbundle.TSelector.html +709 -0
- package/public/microcomponents.es3.js +348 -0
- package/public/modules/MC.html +84 -0
- package/public/modules/MCbundle.html +133 -0
- package/public/modules.html +4 -4
- package/public/my-tag.css +4 -0
- package/public/test.html +5 -1
- package/public/test.js +100 -202
- package/public/types/MCbundle.selection.html +74 -0
- package/public/types/MCbundle.singleSelector.html +74 -0
- package/src/MC.ts +350 -0
- package/src/MCbundle.ts +8 -0
- package/src/McEvent.ts +96 -0
- package/src/autoloader.ts +98 -0
- package/src/customElements.ts +67 -0
- package/src/customObservables.ts +340 -0
- package/src/customSelectors.ts +1634 -0
- package/src/formValues.ts +275 -0
- package/src/helpers.ts +173 -0
- package/src/microcomponents.es3.js +353 -82
- package/src/test.js +137 -218
- package/tsconfig.json +2 -2
- package/typedoc.json +1 -1
- package/webpack.config.js +233 -0
- package/dist/microcomponents.d.ts +0 -26
- package/dist/microcomponents.d.ts.map +0 -1
- package/dist/microcomponents.es3.js +0 -96
- package/dist/microcomponents.es3.min.js +0 -15
- package/dist/microcomponents.js +0 -176
- package/dist/microcomponents.min.js +0 -38
- package/dist/microcomponents.ts +0 -216
- package/public/classes/microcomponents.MC.html +0 -313
- package/public/classes/microcomponents_es3.MC.html +0 -336
- package/public/functions/microcomponents_es3.__isEmpty.html +0 -60
- package/public/microcomponents.js +0 -176
- package/public/modules/microcomponents.html +0 -57
- package/public/modules/microcomponents_es3.html +0 -60
- package/src/microcomponents.ts +0 -216
package/src/McEvent.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var isBrowser=new Function("try {return this===window;}catch(e){ return false;}");
|
|
4
|
+
|
|
5
|
+
let nativeEventNames: Set<string> = new Set();
|
|
6
|
+
|
|
7
|
+
// HTMLElement native event names
|
|
8
|
+
if ( isBrowser() ){
|
|
9
|
+
Object.keys( HTMLElement.prototype ).filter( key => /^on/.test(key) ).forEach( eventName => nativeEventNames.add( eventName ) );
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isDOM(element: { [key:string]: any } ) {
|
|
13
|
+
return element && element instanceof HTMLElement;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface McEvent {
|
|
17
|
+
data: any;
|
|
18
|
+
bubble: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class McEvent{
|
|
22
|
+
|
|
23
|
+
#type: string;
|
|
24
|
+
#stopped: boolean = false;
|
|
25
|
+
#immediateStopped: boolean = false;
|
|
26
|
+
|
|
27
|
+
constructor( type: string, data: any = {}, bubble: string = 'l' ){
|
|
28
|
+
// bubble standard = local only, options = any combination of'uld' characters: up to root, local, down
|
|
29
|
+
this.#type = type;
|
|
30
|
+
this.data = data;
|
|
31
|
+
this.bubble = bubble;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get type(): string {
|
|
35
|
+
return this.#type;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get stopped(): boolean {
|
|
39
|
+
return this.#stopped;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get immediateStopped(): boolean {
|
|
43
|
+
return this.#immediateStopped;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
stopPropagation(){
|
|
47
|
+
this.#stopped = true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
stopImmediatePropagation(){
|
|
51
|
+
this.stopPropagation();
|
|
52
|
+
this.#immediateStopped = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// get all microcomponents from a list of HTMLElements
|
|
58
|
+
function getMicroComponents( elements: any[], search: string): any[] {
|
|
59
|
+
let mcValues: any[] = [];
|
|
60
|
+
|
|
61
|
+
elements.forEach( e => {
|
|
62
|
+
if (e) {
|
|
63
|
+
if (e instanceof HTMLElement) {
|
|
64
|
+
const _mc = ( e as any )?._mc;
|
|
65
|
+
if ( !search && _mc ) {
|
|
66
|
+
mcValues = mcValues.concat(Object.values(( e as any )._mc));
|
|
67
|
+
}
|
|
68
|
+
else if ( search && _mc && _mc[search] ) {
|
|
69
|
+
mcValues.push( _mc[search] );
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return mcValues;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// get all ancestor DOM nodes up to the document root
|
|
78
|
+
function traverseNodes(node: { [key:string]: any }, search: string, firstonly = true): any[] {
|
|
79
|
+
let mcValues: any[] = [];
|
|
80
|
+
while (node) {
|
|
81
|
+
if (node._mc) {
|
|
82
|
+
if (!search) {
|
|
83
|
+
mcValues = mcValues.concat(Object.values(node._mc));
|
|
84
|
+
}
|
|
85
|
+
else if (search && node._mc[search]) {
|
|
86
|
+
mcValues.push(node._mc[search]);
|
|
87
|
+
}
|
|
88
|
+
if (firstonly)
|
|
89
|
+
return mcValues;
|
|
90
|
+
}
|
|
91
|
+
node = node.parentNode;
|
|
92
|
+
}
|
|
93
|
+
return mcValues;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { McEvent };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { getCE } from './customElements.js'
|
|
4
|
+
|
|
5
|
+
// set of currently loading scripts
|
|
6
|
+
const loadingScripts: Set<string> = new Set();
|
|
7
|
+
|
|
8
|
+
const _makeLoadData = ( e: HTMLElement ): Array<any> | false => {
|
|
9
|
+
|
|
10
|
+
let
|
|
11
|
+
tagName = e?.localName ?? '',
|
|
12
|
+
isAttribute = e.getAttribute?.('is') || '',
|
|
13
|
+
isUBE = isAttribute ? true : false,
|
|
14
|
+
isCE = tagName.indexOf('-') !== -1 ? true : false,
|
|
15
|
+
ubeSelector = isUBE ? `${tagName}[is=\"${isAttribute}\"]:defined` : '',
|
|
16
|
+
ceSelector = isCE ? tagName + ':defined' : '',
|
|
17
|
+
fileBaseName = isUBE
|
|
18
|
+
? isAttribute
|
|
19
|
+
: isCE
|
|
20
|
+
? tagName
|
|
21
|
+
: '',
|
|
22
|
+
fileName = fileBaseName.length ? '/' + fileBaseName.split('-').join('/') + '.js' : '',
|
|
23
|
+
selector = isUBE
|
|
24
|
+
? ubeSelector
|
|
25
|
+
: isCE
|
|
26
|
+
? ceSelector
|
|
27
|
+
: ''
|
|
28
|
+
;
|
|
29
|
+
|
|
30
|
+
//console.log( 'makeLoadData()', tagName, isUBE, isCE, ubeSelector, ceSelector, fileBaseName, ' => ', (isUBE || isCE) ? [ e, selector, fileName ] : false );
|
|
31
|
+
return (isUBE || isCE) ? [ e, selector, fileName, fileBaseName ] : false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const _loadRequirements = ( node: HTMLTemplateElement | HTMLElement ): Promise<any> => {
|
|
35
|
+
|
|
36
|
+
let
|
|
37
|
+
undefinedSet: Set<any> = new Set(),
|
|
38
|
+
element: any = node instanceof HTMLTemplateElement ? (node as HTMLTemplateElement).content : node,
|
|
39
|
+
head = document.head,
|
|
40
|
+
allPromises: Array<any> = [],
|
|
41
|
+
e: HTMLElement;
|
|
42
|
+
;
|
|
43
|
+
|
|
44
|
+
if ( element instanceof HTMLTemplateElement === false ){
|
|
45
|
+
// if node itself is undefined
|
|
46
|
+
let data: any = _makeLoadData(element);
|
|
47
|
+
|
|
48
|
+
if ( data && !data[0].matches( data[1] ) ){
|
|
49
|
+
undefinedSet.add( data );
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// if some of the childNodes are undefined
|
|
54
|
+
Array.from( element.childNodes ).forEach(( node )=>{
|
|
55
|
+
let data: any = _makeLoadData(node as HTMLElement);
|
|
56
|
+
|
|
57
|
+
if ( data && !data[0].matches( data[1] ) ){
|
|
58
|
+
undefinedSet.add( data );
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if ( !!undefinedSet.size ){ // there are requirements
|
|
63
|
+
|
|
64
|
+
undefinedSet.forEach( (data) => {
|
|
65
|
+
if ( !getCE( data[3] ) && !loadingScripts.has( data[2]) ){
|
|
66
|
+
|
|
67
|
+
loadingScripts.add( data[2] );
|
|
68
|
+
|
|
69
|
+
const se: HTMLScriptElement = document.createElement('script');
|
|
70
|
+
|
|
71
|
+
se.setAttribute( 'src', data[2] );
|
|
72
|
+
se.setAttribute( 'blocking', 'render' );
|
|
73
|
+
se.setAttribute( 'type', 'module' );
|
|
74
|
+
let s = (data as any)[2];
|
|
75
|
+
se.onload = () => {
|
|
76
|
+
// console.info('loaded', s);
|
|
77
|
+
loadingScripts.delete( s );
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
head.append( se );
|
|
81
|
+
|
|
82
|
+
let e = (data as Array<any>)[0];
|
|
83
|
+
} else {
|
|
84
|
+
return ;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return Promise.all( allPromises );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
TBD
|
|
95
|
+
if ( template.content.childNodes.length ){
|
|
96
|
+
_loadRequirements(template);
|
|
97
|
+
}
|
|
98
|
+
*/
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { DC } from './MC.js';
|
|
4
|
+
import { kebabToPascal } from './helpers.js';
|
|
5
|
+
|
|
6
|
+
class CeBaseClass extends HTMLElement {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
connectedCallback() {
|
|
12
|
+
(DC as any).trigger(this, 'Connected');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
disconnectedCallback() {
|
|
16
|
+
(DC as any).trigger(this, 'Disconnected');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
adoptedCallback() {
|
|
20
|
+
(DC as any).trigger(this, 'Adopted');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
attributeChangedCallback(name: string, oldValue: any, newValue: any) {
|
|
24
|
+
(DC as any).trigger(this, 'AttributeChanged', { name, oldValue, newValue });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function createNamedClass(name: string, ClassBody: { new (...args: any[]): any } ) {
|
|
29
|
+
return Object.defineProperty( ClassBody, 'name', { value: name });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function defineCE( tagName: string, DefinitionClass: { new (...args: any[]): any }, observedAttributes: string[] = []){
|
|
33
|
+
if (customElements.get(tagName)) {
|
|
34
|
+
console.warn(`IGNORED: Custom element "${tagName}" is already defined!`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!tagName.includes('-')) {
|
|
39
|
+
throw new Error('createCustomElement: tagName must contain at least one hyphen ("-").');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let name = kebabToPascal(tagName);
|
|
43
|
+
|
|
44
|
+
customElements.define(
|
|
45
|
+
tagName,
|
|
46
|
+
createNamedClass(
|
|
47
|
+
name,
|
|
48
|
+
class extends CeBaseClass {
|
|
49
|
+
instance: any;
|
|
50
|
+
|
|
51
|
+
constructor( data?: any ) {
|
|
52
|
+
super();
|
|
53
|
+
let instance = new DefinitionClass(this, data);
|
|
54
|
+
( DC as any ).add(this, name, instance );
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static get observedAttributes() {
|
|
58
|
+
return observedAttributes;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export function getCE(tagName: string): CustomElementConstructor | undefined {
|
|
66
|
+
return customElements.get(tagName);
|
|
67
|
+
}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { LooseObject } from './helpers.js';
|
|
4
|
+
import { isObject, parse, debounce, deepEqual } from './helpers.js';
|
|
5
|
+
|
|
6
|
+
export const makeCO = ( target: LooseObject, propertyName?: string ) => {
|
|
7
|
+
|
|
8
|
+
let keys = !propertyName ? [ ...Object.getOwnPropertyNames(target) ] : [ propertyName ];
|
|
9
|
+
|
|
10
|
+
keys.forEach( (key) => {
|
|
11
|
+
// console.log( 'co propname', key, typeof target[key], target[key] );
|
|
12
|
+
if ( key[0] === '_' || target?.[key] === undefined ) return;
|
|
13
|
+
let
|
|
14
|
+
val: any = target[key],
|
|
15
|
+
Constructor: any
|
|
16
|
+
;
|
|
17
|
+
|
|
18
|
+
if ( !val.prototype ){
|
|
19
|
+
switch ( typeof val ){
|
|
20
|
+
case "string":
|
|
21
|
+
Constructor = String;
|
|
22
|
+
break;
|
|
23
|
+
case "number":
|
|
24
|
+
Constructor = Number;
|
|
25
|
+
break;
|
|
26
|
+
case "boolean":
|
|
27
|
+
Constructor = Boolean;
|
|
28
|
+
break;
|
|
29
|
+
case "object":
|
|
30
|
+
if ( val instanceof Array){
|
|
31
|
+
Constructor = Array;
|
|
32
|
+
} else {
|
|
33
|
+
Constructor = Object;
|
|
34
|
+
}
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let Observable = class extends Constructor{
|
|
43
|
+
|
|
44
|
+
constructor( val: any ){
|
|
45
|
+
super( val );
|
|
46
|
+
(this.constructor as LooseObject).callbacks = [];
|
|
47
|
+
if ( isObject(val) ){ // you cannot bind native values to the dom
|
|
48
|
+
(this.constructor as LooseObject).bound = [];
|
|
49
|
+
Object.getOwnPropertyNames( val ).forEach( (key) => {
|
|
50
|
+
this[key] = val[key];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
observe( f: Function ){
|
|
56
|
+
//console.log('observe');
|
|
57
|
+
(this.constructor as any).callbacks.push( f );
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
bind( target: HTMLElement | ShadowRoot ){
|
|
62
|
+
|
|
63
|
+
const
|
|
64
|
+
that = this,
|
|
65
|
+
iso = isObject( that.valueOf() ),
|
|
66
|
+
bound = (that.constructor as any).bound
|
|
67
|
+
;
|
|
68
|
+
|
|
69
|
+
//console.log('bind', iso, target);
|
|
70
|
+
|
|
71
|
+
if ( iso ){
|
|
72
|
+
// console.log('bind...', that.valueOf(), target );
|
|
73
|
+
|
|
74
|
+
function walk( element: Node ){
|
|
75
|
+
//console.log('-walk:', element, element.childNodes );
|
|
76
|
+
|
|
77
|
+
if ( !!element['nodeType'] && element.nodeType === 3 ){ // text node
|
|
78
|
+
//console.log('--type: textNode(3)');
|
|
79
|
+
|
|
80
|
+
var placeholders = Array.from( element.nodeValue && element.nodeValue.match( /\{[^\{\}]*\}/g ) || []);
|
|
81
|
+
|
|
82
|
+
if ( placeholders.length > 0 ){
|
|
83
|
+
//console.log('--placeholders:', placeholders);
|
|
84
|
+
var f=(function( template: string ){
|
|
85
|
+
//console.log('template', template);
|
|
86
|
+
return function( values: any ){
|
|
87
|
+
var t,
|
|
88
|
+
changed = false;
|
|
89
|
+
|
|
90
|
+
//console.log( 'placeholders:', placeholders, 'values:', values);
|
|
91
|
+
if ( placeholders ){
|
|
92
|
+
if ( iso ){
|
|
93
|
+
placeholders.forEach(function(placeholder){
|
|
94
|
+
if ( ( f as LooseObject).values[placeholder] !== values[placeholder] ){
|
|
95
|
+
(f as LooseObject).values[placeholder] = values[placeholder];
|
|
96
|
+
changed = true;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
} else { // it is a simple value
|
|
100
|
+
(f as LooseObject).values = values;
|
|
101
|
+
changed = true;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (changed){ // only reflow if changed
|
|
105
|
+
t = template;
|
|
106
|
+
//console.log('--template parse', t, (f as LooseObject).values );
|
|
107
|
+
if ( iso ){
|
|
108
|
+
element.nodeValue = parse(
|
|
109
|
+
t,
|
|
110
|
+
(f as LooseObject).values
|
|
111
|
+
);
|
|
112
|
+
} else { // it is a simple value -> replace every placeholder whatever the name wi
|
|
113
|
+
element.nodeValue = t.replace( /\{.*\}/g ,(f as LooseObject).values );
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return values;
|
|
117
|
+
};
|
|
118
|
+
})( element.nodeValue || '' );
|
|
119
|
+
|
|
120
|
+
(f as LooseObject).values = iso ? {} : undefined;
|
|
121
|
+
placeholders = Array.from( placeholders ).map((pKey) => pKey.replace(/[\{\}]/g, '').trim());
|
|
122
|
+
|
|
123
|
+
let initial: any = iso ? {} : undefined;
|
|
124
|
+
placeholders.forEach(function(pKey){
|
|
125
|
+
if ( iso ){
|
|
126
|
+
initial[pKey] = "";
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
bound.push(f);
|
|
131
|
+
// console.log( `--bound textNode`, placeholders, f );
|
|
132
|
+
//(observableFunction as Function)(initial);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
//console.log('--childNodes:', element.childNodes );
|
|
136
|
+
Array.from( element.childNodes )
|
|
137
|
+
.forEach(function( childNode ){
|
|
138
|
+
walk( childNode );
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
that.notify();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if ( !!element['nodeType'] && element.nodeType === 1 ){ // HTML element
|
|
145
|
+
//console.log('--type: HTMLElement(1)');
|
|
146
|
+
Array.from( (element as HTMLElement).attributes )
|
|
147
|
+
.forEach(
|
|
148
|
+
function( attributeNode ){
|
|
149
|
+
var placeholders = Array.from( attributeNode.value && attributeNode.value.match( /\{[^\{\}]*\}/g ) || []);
|
|
150
|
+
|
|
151
|
+
if ( placeholders.length > 0 ){
|
|
152
|
+
//console.log( `--[${attributeNode.value}] placeholders:`, placeholders );
|
|
153
|
+
|
|
154
|
+
var f=(function( template: string ){
|
|
155
|
+
return function( values: any ){
|
|
156
|
+
var t,
|
|
157
|
+
changed = false;
|
|
158
|
+
|
|
159
|
+
//console.log( 'placeholders:', placeholders, 'values:', values);
|
|
160
|
+
if ( placeholders ){
|
|
161
|
+
placeholders.forEach(function(pKey){
|
|
162
|
+
if ( ( f as LooseObject).values[pKey] !== values[pKey] ){
|
|
163
|
+
(f as LooseObject).values[pKey] = values[pKey];
|
|
164
|
+
changed = true;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (changed){ // only reflow if changed
|
|
169
|
+
t = template;
|
|
170
|
+
element.nodeValue = parse(
|
|
171
|
+
t,
|
|
172
|
+
(f as LooseObject).values
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
return values;
|
|
176
|
+
};
|
|
177
|
+
})( attributeNode.value || '' );
|
|
178
|
+
|
|
179
|
+
(f as LooseObject).values = {};
|
|
180
|
+
placeholders = Array.from( placeholders ).map((pKey) => pKey.replace(/[\{\}]/g, '').trim());
|
|
181
|
+
let initial: LooseObject = {};
|
|
182
|
+
placeholders.forEach(function(pKey){
|
|
183
|
+
initial[pKey] = "";
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
bound.push(f);
|
|
187
|
+
// console.log( `--bound element attribute`, placeholders, f );
|
|
188
|
+
// console.log( `--bound`, observableFunction.bound );
|
|
189
|
+
//(observableFunction as Function)(initial);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
//console.log('--childNodes:', element.childNodes );
|
|
195
|
+
Array.from( element.childNodes )
|
|
196
|
+
.forEach(function( childNode ){
|
|
197
|
+
walk( childNode );
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
that.notify();
|
|
201
|
+
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if ( !!element['nodeType'] && element.nodeType === 11 ){ // shadowRoot node
|
|
205
|
+
// console.log( element, element.childNodes );
|
|
206
|
+
Array.from( element.childNodes )
|
|
207
|
+
.forEach(function( childNode ){
|
|
208
|
+
walk( childNode );
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
walk( target );
|
|
214
|
+
|
|
215
|
+
if ( bound.length > 0 ){
|
|
216
|
+
that.notify();
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
console.warn( 'IGNORED - cannot bind a non-Object to the dom, IS:', typeof that.valueOf() );
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
notify(){
|
|
226
|
+
let
|
|
227
|
+
that = this,
|
|
228
|
+
data = isObject( that.valueOf() ) && ( that.valueOf() instanceof Array ) === false
|
|
229
|
+
? structuredClone( Object.assign({}, that) )
|
|
230
|
+
: that.valueOf()
|
|
231
|
+
;
|
|
232
|
+
|
|
233
|
+
// console.log('notify', that.constructor, (that.constructor as any).callbacks.length, (that.constructor as any)?.bound?.length, data );
|
|
234
|
+
(that.constructor as any).callbacks.forEach( (f: Function) => {
|
|
235
|
+
// console.log( 'callback', f, data);
|
|
236
|
+
f( data );
|
|
237
|
+
});
|
|
238
|
+
if ( isObject( that.valueOf() ) ){
|
|
239
|
+
// console.log('bound', this.constructor);
|
|
240
|
+
(that.constructor as any).bound.forEach( (f: Function) => {
|
|
241
|
+
// console.log( 'bound', f, data);
|
|
242
|
+
f( data );
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return this;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
//(Observable.constructor as LooseObject).callbacks = [];
|
|
250
|
+
let o = new Observable( val );
|
|
251
|
+
|
|
252
|
+
Object.defineProperty(
|
|
253
|
+
target,
|
|
254
|
+
key,
|
|
255
|
+
{
|
|
256
|
+
enumerable: true,
|
|
257
|
+
get(){
|
|
258
|
+
if ( isObject( o.valueOf() ) ){
|
|
259
|
+
let check = structuredClone( Object.assign( {}, o ) );
|
|
260
|
+
setTimeout(
|
|
261
|
+
debounce(
|
|
262
|
+
function checkChanges(){
|
|
263
|
+
let state = structuredClone( Object.assign( {}, o ) );
|
|
264
|
+
//console.log( 'check changes', state, check );
|
|
265
|
+
if ( !deepEqual( state, check ) ){
|
|
266
|
+
//console.log( '-> not equal!' );
|
|
267
|
+
o.notify();
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
0
|
|
271
|
+
),
|
|
272
|
+
0
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return o;
|
|
276
|
+
},
|
|
277
|
+
set( val: any ){
|
|
278
|
+
//console.log( 'SET:', isObject( val.valueOf() ), 'set:', val, 'to', isObject( o.valueOf() ), o, );
|
|
279
|
+
if ( typeof val === typeof o.valueOf() ){
|
|
280
|
+
|
|
281
|
+
let iso = isObject( o.valueOf() ) && ( o.valueOf() instanceof Array ) === false;
|
|
282
|
+
//let iso = o.constructor.prototype.__proto__.constructor === Object;
|
|
283
|
+
//console.log('target iso:', iso);
|
|
284
|
+
if ( iso ){
|
|
285
|
+
let
|
|
286
|
+
old = structuredClone( Object.assign( {}, o ) )
|
|
287
|
+
;
|
|
288
|
+
|
|
289
|
+
val = structuredClone( Object.assign( {}, val ) );
|
|
290
|
+
|
|
291
|
+
let
|
|
292
|
+
hasFormHandler = (o.constructor as LooseObject)?.hasFormHandler ?? false,
|
|
293
|
+
callbacks = (o.constructor as LooseObject).callbacks,
|
|
294
|
+
bound = (o.constructor as LooseObject).bound
|
|
295
|
+
;
|
|
296
|
+
|
|
297
|
+
o = new Observable( structuredClone( Object.assign( {}, val ) ) );
|
|
298
|
+
|
|
299
|
+
//console.log('after set o=', o, 'check', old);
|
|
300
|
+
(o.constructor as LooseObject).callbacks = hasFormHandler;
|
|
301
|
+
(o.constructor as LooseObject).callbacks = callbacks;
|
|
302
|
+
(o.constructor as LooseObject).bound = bound;
|
|
303
|
+
|
|
304
|
+
setTimeout( // check for changes
|
|
305
|
+
debounce(
|
|
306
|
+
function checkChanges(){
|
|
307
|
+
let state = structuredClone( Object.assign( {}, o ) );
|
|
308
|
+
//console.log( 'check changes', state, check );
|
|
309
|
+
if ( !deepEqual( state, old ) ){
|
|
310
|
+
//console.log( '-> not equal!' );
|
|
311
|
+
o.notify();
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
0
|
|
315
|
+
),
|
|
316
|
+
0
|
|
317
|
+
);
|
|
318
|
+
} else { // simple native var
|
|
319
|
+
let
|
|
320
|
+
old = o.valueOf(),
|
|
321
|
+
callbacks = (o.constructor as LooseObject).callbacks
|
|
322
|
+
;
|
|
323
|
+
o = new Observable( val );
|
|
324
|
+
(o.constructor as LooseObject).callbacks = callbacks;
|
|
325
|
+
if ( val !== old ){
|
|
326
|
+
o.notify();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
console.warn( 'IGNORED - cannot change observable types, IS:', typeof o.valueOf(), '- CANNOT BECOME:', typeof val );
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
target[key] = val;
|
|
337
|
+
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
}
|