@twobirds/microcomponents 0.0.66 → 0.0.67

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.
Files changed (93) hide show
  1. package/README.md +10 -6
  2. package/assets/{test.html → test/test.html} +3 -3
  3. package/package.json +4 -4
  4. package/public/documentation/MC.md +588 -0
  5. package/public/documentation/ajaxian-1.png +0 -0
  6. package/public/documentation/ajaxian-2.png +0 -0
  7. package/public/documentation/ajaxian-3.png +0 -0
  8. package/public/documentation/elements.md +278 -0
  9. package/public/documentation/functional_requirements.md +8 -0
  10. package/public/documentation/history.md +436 -0
  11. package/public/documentation/magic.md +29 -0
  12. package/public/documentation/observables.md +107 -0
  13. package/public/documentation/rnd0023-1.jpg +0 -0
  14. package/public/documentation/rnd0023-2.jpg +0 -0
  15. package/public/documentation/rnd0023-3.jpg +0 -0
  16. package/public/documentation/tB_react.md +207 -0
  17. package/public/documentation/usage.md +142 -0
  18. package/public/documentation/why.md +25 -0
  19. package/public/fiddle/MicroComponents.svg +15 -0
  20. package/public/fiddle/fiddle.css +0 -0
  21. package/public/fiddle/fiddle.js +0 -0
  22. package/public/fiddle/index.html +0 -0
  23. package/public/src/MC.ts +605 -0
  24. package/public/src/McEvent.ts +96 -0
  25. package/public/src/elements.ts +76 -0
  26. package/public/src/formValues.ts +233 -0
  27. package/public/src/helpers.ts +149 -0
  28. package/public/src/microcomponents.ts +8 -0
  29. package/public/src/observables.ts +401 -0
  30. package/public/src/selectors.ts +1634 -0
  31. package/public/src/test.ts +193 -0
  32. package/public/test/child.js +15 -0
  33. package/public/test/child.js.map +1 -0
  34. package/public/test/grandchild.js +15 -0
  35. package/public/test/grandchild.js.map +1 -0
  36. package/public/test/grandparent.css +13 -0
  37. package/public/test/grandparent.js +1 -0
  38. package/public/test/grandparent.js.map +1 -0
  39. package/public/test/greatgrandchild.js +29 -0
  40. package/public/test/greatgrandchild.js.map +1 -0
  41. package/public/test/my-tag.css +19 -0
  42. package/public/test/parent.js +15 -0
  43. package/public/test/parent.js.map +1 -0
  44. package/public/test/test.html +27 -0
  45. package/public/test/testform.js +1 -0
  46. package/public/test/testform.js.map +1 -0
  47. package/public/test/testmc.js +10 -0
  48. package/public/test/testmc.js.map +1 -0
  49. package/public/typedoc/.nojekyll +1 -0
  50. package/public/{classes → typedoc/classes}/MC.DC.html +2 -2
  51. package/public/{classes → typedoc/classes}/MC.MC.html +2 -2
  52. package/public/{classes → typedoc/classes}/MC.McBase.html +2 -2
  53. package/public/{classes → typedoc/classes}/MC.McEvent.html +2 -2
  54. package/public/{functions → typedoc/functions}/MC.addListener.html +1 -1
  55. package/public/{functions → typedoc/functions}/MC.autoload.html +1 -1
  56. package/public/{functions → typedoc/functions}/MC.removeListener.html +1 -1
  57. package/public/{functions → typedoc/functions}/elements.defineCE.html +1 -1
  58. package/public/{functions → typedoc/functions}/elements.getCE.html +1 -1
  59. package/public/{functions → typedoc/functions}/formValues.formValues.html +1 -1
  60. package/public/{functions → typedoc/functions}/observables.observe.html +1 -1
  61. package/public/{functions → typedoc/functions}/selectors._d.html +1 -1
  62. package/public/{functions → typedoc/functions}/selectors._t.html +1 -1
  63. package/public/{index.html → typedoc/index.html} +8 -6
  64. package/public/{interfaces → typedoc/interfaces}/MC.HTMLMcElement.html +2 -2
  65. package/public/{interfaces → typedoc/interfaces}/selectors.DSelector.html +2 -2
  66. package/public/{interfaces → typedoc/interfaces}/selectors.TSelector.html +2 -2
  67. package/public/typedoc/media/MicroComponents.svg +15 -0
  68. package/public/{modules → typedoc/modules}/MC.html +1 -1
  69. package/public/{modules → typedoc/modules}/elements.html +1 -1
  70. package/public/{modules → typedoc/modules}/formValues.html +1 -1
  71. package/public/{modules → typedoc/modules}/observables.html +1 -1
  72. package/public/{modules → typedoc/modules}/selectors.html +1 -1
  73. package/public/{types → typedoc/types}/observables.Observable.html +1 -1
  74. package/public/{types → typedoc/types}/selectors.selection.html +1 -1
  75. package/public/{types → typedoc/types}/selectors.singleSelector.html +1 -1
  76. package/src/test/testmc.ts +0 -6
  77. package/typedoc.json +1 -1
  78. package/webpack.config.js +1 -1
  79. package/assets/3d.html +0 -42
  80. package/public/microcomponents.min.js +0 -1
  81. /package/assets/{grandparent.css → test/grandparent.css} +0 -0
  82. /package/assets/{my-tag.css → test/my-tag.css} +0 -0
  83. /package/dist/{microcomponents.min.js → microcomponents.js} +0 -0
  84. /package/public/{media/MicroComponents.svg → MicroComponents.svg} +0 -0
  85. /package/public/{assets → typedoc/assets}/highlight.css +0 -0
  86. /package/public/{assets → typedoc/assets}/icons.js +0 -0
  87. /package/public/{assets → typedoc/assets}/icons.svg +0 -0
  88. /package/public/{assets → typedoc/assets}/main.js +0 -0
  89. /package/public/{assets → typedoc/assets}/navigation.js +0 -0
  90. /package/public/{assets → typedoc/assets}/search.js +0 -0
  91. /package/public/{assets → typedoc/assets}/style.css +0 -0
  92. /package/public/{hierarchy.html → typedoc/hierarchy.html} +0 -0
  93. /package/public/{modules.html → typedoc/modules.html} +0 -0
@@ -0,0 +1,605 @@
1
+ 'use strict';
2
+
3
+ import { LooseObject } from './helpers.js';
4
+ import { McEvent } from './McEvent.js';
5
+
6
+ var isBrowser = new Function(
7
+ 'try {return this===window;}catch(e){ return false;}'
8
+ );
9
+
10
+ let nativeEventNames: Set<string> = new Set();
11
+
12
+ // HTMLElement native event names
13
+ if (isBrowser()) {
14
+ Object.keys(HTMLElement.prototype)
15
+ .filter((key) => /^on/.test(key))
16
+ .forEach((eventName) => nativeEventNames.add(eventName));
17
+ }
18
+
19
+ export interface HTMLMcElement extends HTMLElement {
20
+ _mc: LooseObject;
21
+ }
22
+
23
+ // get all microcomponents from a list of HTMLElements
24
+ function getMicroComponents(elements: any[], search: string): any[] {
25
+ let mcValues: any[] = [];
26
+
27
+ elements.forEach((e) => {
28
+ if (e) {
29
+ if (e instanceof HTMLElement) {
30
+ const _mc = (e as any)?._mc;
31
+ if (!search && _mc) {
32
+ mcValues = mcValues.concat(Object.values((e as any)._mc));
33
+ } else if (search && _mc && _mc[search]) {
34
+ mcValues.push(_mc[search]);
35
+ }
36
+ }
37
+ }
38
+ });
39
+ return mcValues;
40
+ }
41
+
42
+ // get all ancestor DOM nodes up to the document root
43
+ function traverseNodes(
44
+ node: { [key: string]: any },
45
+ search: string,
46
+ firstonly = true
47
+ ): any[] {
48
+ let mcValues: any[] = [];
49
+ while (node) {
50
+ if (node._mc) {
51
+ if (!search) {
52
+ mcValues = mcValues.concat(Object.values(node._mc));
53
+ } else if (search && node._mc[search]) {
54
+ mcValues.push(node._mc[search]);
55
+ }
56
+ if (firstonly) return mcValues;
57
+ }
58
+ node = node.parentNode;
59
+ }
60
+ return mcValues;
61
+ }
62
+
63
+ function isDOM(element: { [key: string]: any }) {
64
+ return element && element instanceof HTMLElement;
65
+ }
66
+
67
+ function addListener(
68
+ domNode: Node,
69
+ eventName: string,
70
+ handler: EventListenerOrEventListenerObject | null,
71
+ capture: boolean = false,
72
+ once: boolean = false
73
+ ) {
74
+ let options: EventListenerOptions & { once: boolean } = { once: false };
75
+ if (capture) {
76
+ options.capture = capture;
77
+ }
78
+
79
+ if (once) {
80
+ options.once = once;
81
+ }
82
+
83
+ //console.log( 'addEventListener', eventName, ( handler as Function ).name, options );
84
+ domNode.addEventListener(eventName, handler, options);
85
+ }
86
+
87
+ function removeListener(
88
+ domNode: Node,
89
+ eventName: string,
90
+ handler: EventListenerOrEventListenerObject | null
91
+ ) {
92
+ domNode.removeEventListener(eventName, handler);
93
+ }
94
+
95
+ // add a microcomponent to a target DOM node
96
+ function add(target: HTMLElement, key: string, element = {}): any {
97
+ let node = target as HTMLMcElement;
98
+
99
+ // create the _mc property if necessary
100
+ if (!node._mc) {
101
+ let debug = (DC as any)?.debug;
102
+ (DC as any).debug = false;
103
+ node._mc = new (DC as any)(target);
104
+ (DC as any).debug = debug;
105
+ delete node._mc._mc; // NOT NEEDED on the top level _mc property of a DOM HTMLElement
106
+ }
107
+
108
+ const _mc = node._mc;
109
+
110
+ // add the element with the given property key to the _mc property
111
+ if (key.length && !_mc[key]) {
112
+ _mc[key] = element;
113
+ // add attribute if HTMLElement
114
+ if (target instanceof HTMLElement && ! target.getAttribute('_mc') ) {
115
+ target.setAttribute('_mc', '');
116
+ }
117
+ }
118
+ return element;
119
+ }
120
+
121
+ // remove a micro component from the DOM
122
+ function remove(target: HTMLMcElement, key: string) {
123
+ if (!key) return; // silent fail
124
+
125
+ const _mc = target?._mc || {};
126
+
127
+ // delete the property from the HTMLElement _mc collection
128
+ if (_mc && _mc[key]) {
129
+ delete _mc[key];
130
+ }
131
+
132
+ // remove the _mc attribute if the DOM collection is empty
133
+ if (
134
+ !Object.keys(_mc).length &&
135
+ target instanceof HTMLElement &&
136
+ target.hasAttribute('_mc')
137
+ ) {
138
+ target.removeAttribute('_mc');
139
+ }
140
+ return;
141
+ }
142
+
143
+ // trigger an event
144
+ function trigger(
145
+ target: HTMLElement,
146
+ ev: McEvent | string,
147
+ data: any = {},
148
+ bubble: string = 'l'
149
+ ) {
150
+ let _mc = (target as any)?._mc;
151
+ if (!_mc) return;
152
+
153
+ let event =
154
+ ev instanceof McEvent
155
+ ? new McEvent(ev.type, ev.data, 'l')
156
+ : new McEvent(ev, data, 'l'),
157
+ mcEvent: McEvent =
158
+ typeof ev !== 'string' ? (ev as McEvent) : new McEvent(ev, data, bubble);
159
+
160
+ [...Object.values(_mc)].forEach((mc: any) => {
161
+ //console.log( 'mc [', mc.constructor.name, ']', mc.constructor.prototype?.oneInit );
162
+ if ((mc as any)?.trigger && typeof (mc as any)?.trigger === 'function') {
163
+ (mc as any)?.trigger(event);
164
+ }
165
+ });
166
+
167
+ if (/[ud]/.test(mcEvent.bubble)) {
168
+ bubbleEvent(target, mcEvent);
169
+ }
170
+
171
+ return;
172
+ }
173
+
174
+ // run the "init" lifecycle event on the complete _mc structure
175
+ function init(element: MC | DC) {
176
+ if (element.constructor === DC) return;
177
+
178
+ setTimeout(() => {
179
+ element.trigger('Init');
180
+ /*
181
+ let keys = Object.keys( ( element as any)?._mc );
182
+ if ( keys.length ){
183
+ //console.log( 'inner Inits on [', mc.constructor.name, ']', keys );
184
+ keys?.forEach( (key: string) => {
185
+ let mc = ( element as any)?._mc?.[key];
186
+ //console.log( key, typeof mc?.oneInit, mc );
187
+ if ( ( mc as any )['oneInit'] && typeof ( mc as any ).oneInit == 'function' ){
188
+ ( mc as any ).oneInit( new McEvent('Init') );
189
+ }
190
+ });
191
+ };
192
+ */
193
+ }, 0);
194
+ }
195
+
196
+ function autoAttachListeners(element: MC | DC) {
197
+ if (!isBrowser() || element.constructor === DC) return;
198
+
199
+ const target = element.target;
200
+
201
+ if (target instanceof HTMLElement) {
202
+ let that: LooseObject = element;
203
+ Object.getOwnPropertyNames(that.constructor.prototype)
204
+ .filter(
205
+ (key) =>
206
+ /^on[A-Z]|one[A-Z]/.test(key) &&
207
+ typeof that.constructor.prototype[key] === 'function'
208
+ )
209
+ .forEach((key) => {
210
+ let once = /^one/.test(key),
211
+ withoutOnOne = key.replace(/^on[e]{0,1}/, ''), // preserves upper/lowercase for non-native events
212
+ nativeEventName = withoutOnOne.toLowerCase(),
213
+ listener = (ev: McEvent | Event | CustomEvent) => {
214
+ that.trigger(withoutOnOne, ev);
215
+ };
216
+ if (once) {
217
+ // console.log( '...is only called once', that.constructor.prototype[key] );
218
+ Object.defineProperty(element, 'on' + withoutOnOne, {
219
+ configurable: true,
220
+ enumerable: false,
221
+ writable: true,
222
+ value: (ev: any) => {
223
+ // console.log( 'onceHandler execute:', that.constructor.prototype[key] );
224
+ that.constructor.prototype[key].bind(that)(ev);
225
+ },
226
+ });
227
+ }
228
+
229
+ if (nativeEventNames.has('on' + nativeEventName)) {
230
+ // native event defined in HTMLElement
231
+ // console.log('added as native listener', target, nativeEventName );
232
+ addListener(target, nativeEventName, listener, false, once);
233
+ }
234
+ });
235
+ }
236
+ }
237
+
238
+ function bubbleEvent(target: HTMLElement, ev: McEvent) {
239
+ // at this point, either 'u' or 'd' bubbling should be indicated
240
+ // mcEvent bubbling is asynchronous, and the order of target MC execution in case of multiple matches is not guaranteed!
241
+ if (/[ud]/.test(ev.bubble)) {
242
+ setTimeout(() => {
243
+ // bubbling must include 'l' for local, or else no handlers are executed'
244
+ if (ev.bubble.indexOf('l') === -1) {
245
+ ev.bubble += 'l';
246
+ }
247
+
248
+ // bubble up towards root
249
+ if (ev.bubble.indexOf('u') > -1) {
250
+ let parent = traverseNodes(target, '', true)?.[0].target;
251
+ if (parent) DC.trigger(parent, ev);
252
+ }
253
+
254
+ // bubble down
255
+ if (ev.bubble.indexOf('d') > -1) {
256
+ let targets = new Set<HTMLMcElement>(
257
+ (target as HTMLMcElement)._mc
258
+ .children()
259
+ .map((c: unknown) => (c as any)?.target)
260
+ );
261
+ targets.forEach((target: HTMLMcElement) => {
262
+ if (target) DC.trigger(target, ev);
263
+ });
264
+ }
265
+ }, 0);
266
+ }
267
+ }
268
+
269
+ class McBase {
270
+ #target: WeakRef<any>; // avoid circular reference
271
+
272
+ constructor(target?: { [key: string]: any }, debug: boolean = false) {
273
+ let that: unknown = this,
274
+ element = that as MC | DC;
275
+
276
+ this.#target = new WeakRef(target || {});
277
+
278
+ init(element); // async lifecycle event
279
+
280
+ autoAttachListeners(element);
281
+ }
282
+
283
+ get target(): any {
284
+ return this.#target.deref();
285
+ }
286
+ }
287
+
288
+ class MC extends McBase {
289
+ _mc?: LooseObject = {};
290
+
291
+ constructor(target?: any) {
292
+ super(target);
293
+ }
294
+
295
+ static trigger = trigger;
296
+
297
+ trigger(ev: McEvent | string, data: any = {}, bubble: string = 'l') {
298
+ let that = this;
299
+
300
+ //console.log( that, '.trigger(', ev, data, bubble, ')' );
301
+
302
+ // run all triggers
303
+ if (that.constructor === MC || that.constructor === DC) {
304
+ let event =
305
+ ev instanceof McEvent
306
+ ? new McEvent(ev.type, ev.data, 'l')
307
+ : new McEvent(ev, data, 'l'),
308
+ mcEvent: McEvent =
309
+ typeof ev !== 'string'
310
+ ? (ev as McEvent)
311
+ : new McEvent(ev, data, bubble);
312
+ [...Object.getOwnPropertyNames(that)].forEach((mc) => {
313
+ if (
314
+ (that as any)[mc]?.trigger &&
315
+ typeof (that as any)[mc]?.trigger === 'function'
316
+ ) {
317
+ (that as any)[mc]?.trigger(event);
318
+ }
319
+ });
320
+
321
+ if (/[ud]/.test(mcEvent.bubble)) {
322
+ bubbleEvent(that.target, mcEvent);
323
+ }
324
+
325
+ return; // must return here!
326
+ }
327
+
328
+ // only this instance
329
+ if ((that as any) instanceof MC || (that as any) instanceof DC) {
330
+ const that = this,
331
+ mcEvent: McEvent =
332
+ typeof ev !== 'string'
333
+ ? (ev as McEvent)
334
+ : new McEvent(ev, data, bubble),
335
+ handlerName =
336
+ 'on' + mcEvent.type[0].toUpperCase() + mcEvent.type.slice(1);
337
+ // console.log('trigger', handlerName, mcEvent, data, bubble );
338
+
339
+ if (mcEvent.bubble.indexOf('l') > -1) {
340
+ if (
341
+ !mcEvent.immediateStopped &&
342
+ typeof (that as any)[handlerName] === 'function'
343
+ ) {
344
+ // console.log( 'execute handler:', handlerName );
345
+ (that as any)[handlerName](mcEvent);
346
+ if ((that as any).hasOwnProperty(handlerName)) {
347
+ // it is a once handler
348
+ delete (that as any)[handlerName]; // remove after first execution
349
+ }
350
+ }
351
+ }
352
+
353
+ // if event stopped or local only, handling is cancelled
354
+ if (mcEvent.stopped || mcEvent.bubble === 'l') {
355
+ return that;
356
+ }
357
+
358
+ //console.log('bubble', mcEvent);
359
+ if (/[ud]/.test(mcEvent.bubble)) {
360
+ bubbleEvent(that.target, mcEvent);
361
+ }
362
+ }
363
+ }
364
+ }
365
+
366
+ class DC extends McBase {
367
+ _mc?: LooseObject = {};
368
+
369
+ constructor(target: HTMLElement, debug: boolean = false) {
370
+ super(target);
371
+ }
372
+
373
+ static add: Function = add;
374
+ static remove: Function = remove;
375
+ static trigger: Function = trigger;
376
+
377
+ trigger(ev: McEvent | string, data: any = {}, bubble?: string) {
378
+ MC.prototype.trigger.call(this, ev, data, bubble);
379
+ }
380
+
381
+ parent(search: string = ''): any[] {
382
+ let target: HTMLElement = (this as any).target;
383
+ if (!isDOM(target)) return [];
384
+ return traverseNodes((target as any).parentNode, search, true);
385
+ }
386
+
387
+ ancestors(search: string = ''): any[] {
388
+ let target: any = (this as any)?.target;
389
+ if (!isDOM(target)) return [];
390
+ return traverseNodes((this as any).target.parentNode, search, false);
391
+ }
392
+
393
+ children(search: string = ''): any[] {
394
+ let target: any = (this as any)?.target;
395
+ if (!isDOM(target)) return [];
396
+ const id = Math.random().toString().replace('.', '');
397
+ const selector = '[_mc]:not([temp_id="' + id + '"] [_mc] [_mc])';
398
+ target.setAttribute('temp_id', id);
399
+ const myDomElements = target.querySelectorAll(selector);
400
+ const mcValues = getMicroComponents([...myDomElements], search);
401
+ target.removeAttribute('temp_id');
402
+ return mcValues;
403
+ }
404
+
405
+ descendants(search: string = ''): any[] {
406
+ let target: any = (this as any)?.target;
407
+ if (!isDOM(target)) return [];
408
+ const myDomElements = target.querySelectorAll('*[_mc]');
409
+ return getMicroComponents([...myDomElements], search);
410
+ }
411
+ }
412
+
413
+ /*
414
+ unresolvedCEs (code below this comment):
415
+
416
+ a repository (a hashmap) of undefined custom elements:
417
+ the name is the tagname of the micro component (like "my-custom-component")
418
+ if they are loading, the value is a script element
419
+ if they are loaded, the entry is deleted and the browser instanciates it automatically
420
+ if they cannot be loaded, the browser will throw an error
421
+
422
+ If the custom element ( defined by its tagname, like "my-custom-component") is not defined, it will be on-demand loaded:
423
+ The file name is assumed to be "/my/custom/component.js"
424
+ The file must define a custom element via the defineCE() function included in the "elements" module.
425
+ This function is a wrapper for customElementRegistry.define(), which you can also use directly in the imported code.
426
+
427
+ In general this behaves like a custom runtime import for custom elements in the browser.
428
+ The only difference is that the micro component is loaded only when needed, and not on page load.
429
+ */
430
+
431
+ const CEs = new Map();
432
+
433
+ /*
434
+ loadedMCs (code below this comment):
435
+ a repository (a hashmap) of micro components:
436
+ the name is the name of the micro component (like "my-mc")
437
+ if they are loading, the value is a script element
438
+ if they are loaded, the value is the micro component class itself
439
+
440
+ This is mostly used for old style progressive enhancement:
441
+ If the micro component ( defined in the HTMLElements "_mc" attribute, like "my-mc") is not defined, it will be on-demand loaded:
442
+ The file name is assumed to be "/my/mc.js"
443
+ The file needs to have a default export, and it should be a class that extends the DC class ( since it is in a HTMLElements attribute )
444
+
445
+ In general this behaves like a custom runtime import for classes in the browser.
446
+ The only difference is that the micro component is loaded only when needed, and not on page load.
447
+ */
448
+ const MCs = new Map();
449
+
450
+ function makeLoadScript( element: string ): HTMLScriptElement {
451
+
452
+ const fileName: string = './' + element.split('-').join('/')+'.js';
453
+
454
+ const se: HTMLScriptElement = document.createElement('script');
455
+
456
+ se.setAttribute('src', fileName);
457
+ se.setAttribute('blocking', 'render');
458
+ se.async = true;
459
+ se.setAttribute('type', 'module');
460
+ se.setAttribute( 'name', element);
461
+ se.setAttribute( 'loading', '');
462
+
463
+ addListener( se, 'load', (ev) => {
464
+ // console.info('loaded MC', elementName);
465
+ se.removeAttribute( 'loading' );
466
+ se.setAttribute( 'loaded', '');
467
+ });
468
+
469
+ addListener( se, 'error', (ev) => {
470
+ console.error('could not load Custom Element code from', fileName);
471
+ });
472
+
473
+ return se;
474
+ }
475
+
476
+ function onLoadCallback( script: HTMLScriptElement ) {
477
+ // create callback script tag
478
+ // it is essential to do this in a timeout and via another script tag,
479
+ // to force it to be a top level module allowing for the use of "import"
480
+ setTimeout( function(){
481
+ // create callback script tag
482
+ const importFileName = script.getAttribute('src');
483
+ const elementName = script.getAttribute('name');
484
+
485
+ const se: HTMLScriptElement = document.createElement('script');
486
+
487
+ se.async = true;
488
+ se.setAttribute( 'type', 'module');
489
+
490
+ // --- autoload script
491
+ const code = /*javascript*/`
492
+
493
+ import _ from "${importFileName}";
494
+ import {DC} from "./microcomponents.js";
495
+
496
+ const elements = Array.from(document.querySelectorAll('[_mc*="${elementName}"]')).filter( element => element instanceof HTMLElement );
497
+
498
+ elements.forEach((element) => {
499
+ // add class to MCs repository
500
+ autoload.state.MCs.set( '${elementName}', _ );
501
+ // add instance to HTMLElement
502
+ DC.add(element, _.name, new _(element));
503
+ // remove instance name from elements "_mc" attribute
504
+ element.setAttribute('_mc', element.getAttribute('_mc').replace('${elementName}', '').replace( /\w\w/g, ' ').trim());
505
+ });
506
+
507
+ setTimeout( () => {
508
+ document.body.querySelector('script[name="${elementName}]"')?.remove();
509
+ },0);
510
+
511
+ `;
512
+ // --- end autoload script
513
+
514
+ se.innerHTML = code;
515
+ document.body.append(se);
516
+ }, 0);
517
+ }
518
+
519
+ function loadUndefinedMicroComponents() {
520
+
521
+ // load unresolved custom eleents
522
+ [ ...document.querySelectorAll(':not(:defined)') ] // undefined custom elements
523
+ .filter( (element) => !!element
524
+ && !customElements.get(element.tagName.toLowerCase()) // not in CustomElementsRegistry
525
+ && !CEs.has(element.tagName.toLowerCase()) // not in loading list
526
+ )
527
+ .forEach((el) => {
528
+ const elementName = el.tagName.toLowerCase(),
529
+ fileName: string = './'+elementName.split('-').join('/')+'.js';
530
+
531
+ const se: HTMLScriptElement = makeLoadScript( elementName );
532
+
533
+ addListener( se, 'load', () => {
534
+ CEs.set( elementName, customElements.get(el.tagName.toLowerCase()) );
535
+ });
536
+
537
+ CEs.set( elementName, 'loading');
538
+ document.head.append(se);
539
+
540
+ // instanciation of the micro component will be handled by the browser automatically
541
+ // module code must define the micro component class and add it to the customElementRegistry
542
+ });
543
+
544
+ [ ...document.querySelectorAll('[_mc]:not([_mc=""]') ] // undefined DOM components
545
+ .filter( (element) => !!element )
546
+ .forEach((el) => {
547
+ const elements = el.getAttribute('_mc')?.split(' ').filter( m => !!m ) || [];
548
+ const elementsToBeLoaded = elements.filter( m => !MCs.has( m ) );
549
+
550
+ // load MC components that do not exist yet
551
+ elementsToBeLoaded?.forEach( elementName => {
552
+ const se: HTMLScriptElement = makeLoadScript( elementName );
553
+
554
+ addListener( se, 'load', () => onLoadCallback( document.head.querySelector(`script[name="${elementName}"]`) as HTMLScriptElement) );
555
+
556
+ MCs.set( elementName, 'loading');
557
+
558
+ document.head.append(se);
559
+ });
560
+
561
+ // loading MC components already have callbacks attached
562
+ const elementsAlreadyLoaded = elements.filter( m => MCs.has( m ) && !MCs.get( m )!.length);
563
+
564
+ // MCs that are already loaded will trigger the onLoadCallback manually
565
+ elementsAlreadyLoaded?.forEach( elementName => {
566
+ setTimeout( ()=>{
567
+ //console.log('attaching', elementName, (autoload as any).state.MCs.get(elementName));
568
+ DC.add(el, elementName, new ( (autoload as any).state.MCs.get(elementName) as any)(el)); // add instance to DOM
569
+
570
+ const newAttribute = (el as any).getAttribute('_mc')
571
+ .replace('${elementName}', '')
572
+ .replace( /\w\w/g, ' ')
573
+ .trim() || '_mc';
574
+
575
+ (el as any).setAttribute('_mc', ); // set new "_mc" attribute
576
+ }, 5);
577
+ });
578
+ });
579
+ }
580
+
581
+ let autoloadEnabled: boolean = false;
582
+ let observer: MutationObserver | null = null;
583
+
584
+ export function autoload(enable: boolean = true): boolean {
585
+ if (!enable) {
586
+ observer?.disconnect();
587
+ observer = null;
588
+ return autoloadEnabled;
589
+ }
590
+
591
+ autoloadEnabled = enable;
592
+ observer = new MutationObserver(loadUndefinedMicroComponents);
593
+
594
+ setTimeout(() => {
595
+ observer!.observe(document.body, { childList: true, subtree: true });
596
+ loadUndefinedMicroComponents();
597
+ }, 5);
598
+
599
+ return autoloadEnabled;
600
+ }
601
+
602
+ (autoload as LooseObject).state = { CEs, MCs };
603
+
604
+ export { addListener, removeListener, McBase, MC, DC, McEvent };
605
+
@@ -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 };