@webqit/oohtml 1.10.3 → 2.0.0

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 (53) hide show
  1. package/.gitignore +3 -3
  2. package/LICENSE +20 -20
  3. package/README.md +399 -396
  4. package/dist/context-api.js +2 -0
  5. package/dist/context-api.js.map +7 -0
  6. package/dist/html-imports.js +1 -2
  7. package/dist/html-imports.js.map +3 -3
  8. package/dist/html-modules.js +1 -2
  9. package/dist/html-modules.js.map +3 -3
  10. package/dist/main.js +26 -14
  11. package/dist/main.js.map +3 -3
  12. package/dist/namespaced-html.js +1 -2
  13. package/dist/namespaced-html.js.map +3 -3
  14. package/dist/scoped-js.js +27 -0
  15. package/dist/scoped-js.js.map +7 -0
  16. package/dist/state-api.js +1 -2
  17. package/dist/state-api.js.map +3 -3
  18. package/package.json +76 -76
  19. package/src/context-api/HTMLContext.js +158 -0
  20. package/src/context-api/HTMLContextManager.js +77 -0
  21. package/src/context-api/_ContextRequestEvent.js +26 -0
  22. package/src/context-api/index.js +53 -0
  23. package/src/{namespaced-html/browser-entry.js → context-api/targets.browser.js} +9 -9
  24. package/src/html-imports/_HTMLImportElement.js +216 -0
  25. package/src/html-imports/index.js +92 -557
  26. package/src/{browser-entry.js → html-imports/targets.browser.js} +10 -10
  27. package/src/html-modules/HTMLExportsManager.js +191 -0
  28. package/src/html-modules/_HTMLImportsContext.js +114 -0
  29. package/src/html-modules/index.js +133 -384
  30. package/src/{html-imports/browser-entry.js → html-modules/targets.browser.js} +9 -9
  31. package/src/index.js +34 -39
  32. package/src/namespaced-html/index.js +130 -144
  33. package/src/namespaced-html/targets.browser.js +10 -0
  34. package/src/scoped-js/index.js +382 -0
  35. package/src/scoped-js/targets.browser.js +10 -0
  36. package/src/state-api/index.js +55 -142
  37. package/src/state-api/targets.browser.js +10 -0
  38. package/src/{html-modules/browser-entry.js → targets.browser.js} +10 -10
  39. package/src/util.js +20 -180
  40. package/test/imports.test.js +194 -0
  41. package/test/index.js +119 -0
  42. package/test/modules.test.js +198 -0
  43. package/test/namespaced-html.test.js +50 -0
  44. package/test/scoped-js.js +57 -0
  45. package/test/state-api.test.js +34 -0
  46. package/test/test.html +69 -0
  47. package/dist/subscript.js +0 -15
  48. package/dist/subscript.js.map +0 -7
  49. package/src/state-api/browser-entry.js +0 -10
  50. package/src/subscript/Element.js +0 -103
  51. package/src/subscript/browser-entry.js +0 -10
  52. package/src/subscript/index.js +0 -70
  53. package/test/all.test.js +0 -0
@@ -1,557 +1,92 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import { _any, _remove, _unique, _difference, _from as _arrFrom } from '@webqit/util/arr/index.js';
6
- import { _internals } from '@webqit/util/js/index.js';
7
- import { _each } from '@webqit/util/obj/index.js';
8
- import domInit from '@webqit/browser-pie/src/dom/index.js';
9
- import { config, scopeQuery,
10
- parseScopeReferenceExpr, queryMatchPath
11
- } from '../util.js';
12
-
13
- /**
14
- * ---------------------------
15
- * HTML Partials
16
- * ---------------------------
17
- */
18
-
19
- /**
20
- * @init
21
- *
22
- * @param Object config
23
- */
24
- export default function init( _config = {} ) {
25
-
26
- const WebQit = domInit.call( this );
27
- if ( _config.onDomReady ) {
28
- WebQit.DOM.ready( () => {
29
- init.call( this, { ..._config, onDomReady: false } );
30
- } );
31
- return;
32
- }
33
-
34
- const window = WebQit.window;
35
- const document = WebQit.window.document;
36
- const mutations = WebQit.DOM.mutations;
37
-
38
- const importInertContexts = [];
39
- const _meta = config.call(this, {
40
- element: {
41
- import: 'import',
42
- },
43
- attr: {
44
- importid: 'name',
45
- exportsearch: 'exportsearch',
46
- },
47
- }, _config.params );
48
-
49
- _defaultNoInherits.push(_meta.get('attr.importid'), _meta.get('attr.moduleref'));
50
- const modulerefSelector = '[' + window.CSS.escape(_meta.get('attr.moduleref')) + ']';
51
- const exportgroupSelector = '[' + window.CSS.escape(_meta.get('attr.exportgroup')) + ']';
52
-
53
- // ----------------------
54
- // Capture slot elements
55
- // ----------------------
56
-
57
- let prev;
58
- const Import = class/* extends window.HTMLElement*/ {
59
-
60
- /*
61
- static create(el) {
62
- return el;
63
- }
64
- constructor(importEl) {
65
- super();
66
- this.el = this;
67
- }
68
- */
69
-
70
- static create(el) {
71
- return _internals(this.el, 'oohtml').get('instance') || new Import(el);
72
- }
73
- constructor(importEl) {
74
- this.el = importEl;
75
- _internals(this.el, 'oohtml').set('instance', this);
76
- const [ importID, modifiers ] = parseScopeReferenceExpr(importEl.getAttribute(_meta.get('attr.importid')) || 'default');
77
- _internals(this.el, 'oohtml').set('importID', importID);
78
- _internals(this.el, 'oohtml').set('importModifiers', modifiers);
79
- }
80
-
81
- /**
82
- * Called by the Slots hydrator.
83
- *
84
- * @param Comment anchorNode
85
- * @param array slottedElements
86
- * @param Element compositionBlock
87
- *
88
- * @return void
89
- */
90
- hydrate(anchorNode, slottedElements, compositionBlock) {
91
- _internals(this.el, 'oohtml').set('anchorNode', anchorNode);
92
- _internals(this.el, 'oohtml').set('slottedElements', slottedElements);
93
- _internals(this.el, 'oohtml').set('compositionBlock', compositionBlock);
94
- this._bindSlotted(slottedElements);
95
- this._connectToCompositionBlock();
96
- }
97
-
98
- /**
99
- * This triggers self-resolution
100
- *
101
- * @return void
102
- */
103
- connectedCallback() {
104
- if (!_internals(this.el, 'oohtml').has('anchorNode')) {
105
- _internals(this.el, 'oohtml').set('anchorNode', _meta.get('isomorphic')
106
- ? document.createComment(this.el.outerHTML)
107
- : document.createTextNode(''));
108
- _internals(this.el, 'oohtml').set('compositionBlock', !this.el.hasAttribute(_meta.get('attr.moduleref'))
109
- ? this.el.parentNode.closest(modulerefSelector) : (
110
- this.el.getAttribute(_meta.get('attr.moduleref')).trim().startsWith('~') ? this.el.parentNode.closest(exportgroupSelector) : null
111
- ));
112
- this._connectToCompositionBlock();
113
- }
114
- WebQit.DOM.ready.call(WebQit, () => {
115
- this.resolve('connected');
116
- });
117
- }
118
-
119
- /**
120
- * Connects the instance to the compositionBlock.
121
- */
122
- _connectToCompositionBlock() {
123
- if (this.compositionBlock) {
124
- // Now after the update slot ID
125
- _internals(this.compositionBlock, 'oohtml', 'imports').set(this.importID, this.el);
126
- }
127
- }
128
-
129
- /**
130
- * Bind a slotted element.
131
- *
132
- * @param array exports
133
- *
134
- * @return void
135
- */
136
- _bindSlotted(exports) {
137
- exports.forEach(_export => {
138
- _export.importReference = this.el;
139
- });
140
- _internals(this.el, 'oohtml').set('slottedObserver', mutations.onRemoved(exports, (removed, state, isTransient, addedState, removedState) => {
141
- if (removedState && removedState.size === exports.length) {
142
- _internals(this.el, 'oohtml').get('slottedObserver').disconnect();
143
- }
144
- removed.forEach(remd => {
145
- // Let's ensure this wasn't slotted againe
146
- if (!remd.parentNode) {
147
- _remove(this.slottedElements, remd);
148
- }
149
- // if the slotted hasnt been slotted somewhere
150
- if (remd.importReference === this.el) {
151
- delete remd.importReference;
152
- }
153
- });
154
- // If this was the last of the s,ottable in the same family of IDs,
155
- // we should restore the original slot
156
- if (!this.slottedElements.length) {
157
- // Must be assigned bu now
158
- // for it to be removed in the first place
159
- if (this.anchorNode.isConnected) {
160
- this.anchorNode.replaceWith(this.el);
161
- }
162
- }
163
- }, {maintainCallState: true, ignoreTransients: true}));
164
- }
165
-
166
- /**
167
- * Resolves the slot
168
- */
169
- resolve(reason = null) {
170
- if (_any(importInertContexts, inertContext => this.el.closest(inertContext))) {
171
- return;
172
- }
173
- var getExports = (contexts, moduleref, exportsearch) => {
174
- var importId = this.importID,
175
- modifiers = this.importModifiers,
176
- [ searchA, searchB ] = (('search' in modifiers) || exportsearch !== null) ? ('search' in modifiers ? modifiers.search : exportsearch).split('-').filter(a => a).map(a => parseInt(a || 0)).concat([0, 1000]) : [0, 0];
177
- const aggrExports = modules => modules.reduce((_exports, _module) => _exports.concat(_internals(_module, 'oohtml', 'exports').get(importId) || []), []);
178
- return scopeQuery(contexts, moduleref, function(host, prop) {
179
- var collection = _internals(host, 'oohtml', 'templates');
180
- if (arguments.length === 1) return collection;
181
- if (prop.startsWith(':')) return _internals(host, 'oohtml', 'exports').get(prop.substr(1));
182
- return collection.get(prop);
183
- }, function(_modules, level, isRewinding) {
184
- var exportsAggr = aggrExports(_modules);
185
- if (!exportsAggr.length && level > searchA && searchB) {
186
- searchB --;
187
- return -1;
188
- }
189
- return exportsAggr;
190
- });
191
- };
192
- // -----------------
193
- // Global import or scoped slot?
194
- var templateSource, exports;
195
- if (this.el.hasAttribute(_meta.get('attr.moduleref'))) {
196
- // Did we previously had a compositionBlock?
197
- // Let's remove ourself
198
- if (this.compositionBlock && _internals(this.compositionBlock, 'oohtml', 'imports').get(this.importID) === this.el) {
199
- _internals(this.compositionBlock, 'oohtml', 'imports').delete(this.importID);
200
- }
201
- templateSource = this.el;
202
- } else {
203
- if (!this.compositionBlock) {
204
- console.warn('Scoped slots must be found within template contexts. [' + this.importID + ']', this.el);
205
- return;
206
- }
207
- templateSource = this.compositionBlock;
208
- }
209
- var moduleref = templateSource.getAttribute(_meta.get('attr.moduleref')).trim();
210
- var exportsearch = this.el.getAttribute(_meta.get('attr.exportsearch'));
211
- if (templateSource && (exports = getExports([moduleref.startsWith('~') ? this.compositionBlock : document], moduleref, exportsearch)).length) {
212
- if (_difference(exports, _internals(this.el, 'oohtml').get('originalSlottedElements') || []).length) {
213
- _internals(this.el, 'oohtml').set('originalSlottedElements', exports);
214
- this.fill(exports);
215
- }
216
- } else {
217
- _internals(this.el, 'oohtml').set('originalSlottedElements', null);
218
- this.empty();
219
- }
220
- }
221
-
222
- /**
223
- * Fill slot with exports.
224
- *
225
- * @param array|Element exports
226
- *
227
- * @return void
228
- */
229
- fill(exports) {
230
- exports = _arrFrom(exports, false/* castObject */).map(_export => _export.cloneNode(true));
231
- // ---------------------
232
- // Discard previous slotted elements
233
- // But this intentional removal should not trigger slot restoration
234
- this.empty(true/* silently */);
235
- if (this.el.isConnected) {
236
- this.el.replaceWith(this.anchorNode);
237
- }
238
- // ---------------------
239
- // Slot-in the corresponding exports from template
240
- exports.forEach(_export => {
241
- // ---------------------
242
- // Implement the slot?
243
- _internals(_export, 'oohtml', 'templates').set('~', this.el);
244
- // Inherit attributes from the slot element before replacement
245
- mergeAttributes(_export, this.el);
246
- // ---------------------
247
- if (!_export.getAttribute(_meta.get('attr.exportgroup'))) {
248
- _export.setAttribute(_meta.get('attr.exportgroup'), this.importID);
249
- }
250
- // Place slottable
251
- this.anchorNode.before(_export);
252
- });
253
- this._bindSlotted(exports);
254
- // ---------------------
255
- // Updatate records
256
- this.slottedElements.push(...exports);
257
- }
258
-
259
- /**
260
- * Empty slot.
261
- *
262
- * @param bool sliently
263
- *
264
- * @return void
265
- */
266
- empty(silently = false) {
267
- if (this.slottedElements) {
268
- var slottedElements = this.slottedElements;
269
- if (silently && _internals(this.el, 'oohtml').has('slottedObserver')) {
270
- _internals(this.el, 'oohtml').get('slottedObserver').disconnect();
271
- slottedElements = this.slottedElements.splice(0);
272
- }
273
- slottedElements.forEach(slottedElement => slottedElement.remove());
274
- }
275
- }
276
-
277
- /**
278
- * Returns the slot's name.
279
- *
280
- * @return string
281
- */
282
- get importID() {
283
- return _internals(this.el, 'oohtml').get('importID');
284
- }
285
-
286
- /**
287
- * Returns the slot's import Modifiers.
288
- *
289
- * @return string
290
- */
291
- get importModifiers() {
292
- return _internals(this.el, 'oohtml').get('importModifiers');
293
- }
294
-
295
- /**
296
- * Returns the slot's anchorNode.
297
- *
298
- * @return array
299
- */
300
- get anchorNode() {
301
- return _internals(this.el, 'oohtml').get('anchorNode');
302
- }
303
-
304
- /**
305
- * Returns the slot's compositionBlock, if any.
306
- *
307
- * @return array
308
- */
309
- get compositionBlock() {
310
- return _internals(this.el, 'oohtml').get('compositionBlock');
311
- }
312
-
313
- /**
314
- * Returns the slot's slotted elements.
315
- *
316
- * @return array
317
- */
318
- get slottedElements() {
319
- if (!_internals(this.el, 'oohtml').has('slottedElements')) {
320
- _internals(this.el, 'oohtml').set('slottedElements', []);
321
- }
322
- return _internals(this.el, 'oohtml').get('slottedElements');
323
- }
324
-
325
- /**
326
- * Returns the slot's implementable exports
327
- *
328
- * @return array
329
- */
330
- get exports() {
331
- return _internals(this.el, 'oohtml').get('exports');
332
- }
333
-
334
- /**
335
- * The attributes we want to observe.
336
- *
337
- * @return array
338
- */
339
- static get observedAttributes() {
340
- return [_meta.get('attr.importid')];
341
- }
342
- };
343
-
344
- // ----------------------
345
- // Capture import elements
346
- // ----------------------
347
-
348
- mutations.onPresent(_meta.get('element.import'), el => {
349
- var importElInstance = Import.create(el);
350
- importElInstance.connectedCallback();
351
- });
352
- /**
353
- window.customElements.define(_meta.get('element.import'), Import);
354
- */
355
-
356
- // ----------------------
357
- // Progressive resolution
358
- // ----------------------
359
-
360
- const resolveSlots = (el, exportName, reason = null) => {
361
- const shouldResolve = (importElInstance, importName) => {
362
- return !exportName || importName === exportName || (
363
- exportName === true && ((importElInstance.importModifiers && ('search' in importElInstance.importModifiers)) || importElInstance.el.getAttribute(_meta.get('attr.exportsearch')))
364
- );
365
- };
366
- if (el.matches(_meta.get('element.import'))) {
367
- var importElInstance = Import.create(el);
368
- if (shouldResolve(importElInstance, importElInstance.importID)) {
369
- importElInstance.resolve(reason);
370
- }
371
- } else {
372
- _internals(el, 'oohtml', 'imports').forEach((importEl, name) => {
373
- var importElInstance = Import.create(importEl);
374
- if (shouldResolve(importElInstance, name)) {
375
- importElInstance.resolve(`Resolution scope: ${reason}`);
376
- }
377
- });
378
- }
379
- };
380
-
381
- mutations.onPresent(modulerefSelector, el => {
382
- if (_any(importInertContexts, inertContext => el.closest(inertContext))) {
383
- return;
384
- }
385
- // Imports resolve by themselves
386
- // But...
387
- // We resolve them again when reference to template changes
388
- mutations.onAttrChange(el, mr => {
389
- if (mr[0].target.getAttribute(mr[0].attributeName) !== mr[0].oldValue) {
390
- resolveSlots(el, null, `Attr-Change: ${mr[0].attributeName}`);
391
- }
392
- }, [_meta.get('attr.moduleref'), _meta.get('attr.importid')]);
393
- });
394
-
395
- document.addEventListener('templatemutation', e => {
396
- // Resolve slots when the referenced template changes
397
- if (!e.detail.path) {
398
- return;
399
- }
400
- _arrFrom(document.querySelectorAll('[' + window.CSS.escape(_meta.get('attr.moduleref')) + ']')).forEach(el => {
401
- if (queryMatchPath(el.getAttribute(_meta.get('attr.moduleref')), e.detail.path)) {
402
- resolveSlots(el, true/* resolve imports with search() */, `'templatemutation' event: ${e.detail.path}, search()`);
403
- e.detail.addedExports.concat(e.detail.removedExports).forEach(exportGroup => {
404
- resolveSlots(el, exportGroup.name, `'templatemutation' event: ${e.detail.path}, ${exportGroup.name}`);
405
- });
406
- }
407
- });
408
- });
409
-
410
- // ----------------------
411
- // Restore slots from snapshots
412
- // ----------------------
413
-
414
- const hydrateSlots = () => {
415
- _arrFrom(document.querySelectorAll(exportgroupSelector)).forEach(_export => {
416
- // Scan
417
- if (_internals(_export.parentNode, 'oohtml').get('importsCan')) return;
418
- // hydrateSlots() might be running AFTER certain <slots> have resolved
419
- // and _export might be a just-resolved node
420
- if (_export.importReference) return;
421
- var slottedElements = [];
422
- _export.parentNode.childNodes.forEach(node => {
423
- var nodeValue;
424
- if (node.nodeType === 1/** ELEMENT_NODE */ && node.matches(exportgroupSelector)) {
425
- slottedElements.push(node);
426
- } else if (node.nodeType === 8/** COMMENT_NODE */ && (nodeValue = node.nodeValue.trim())
427
- && nodeValue.startsWith('<' + _meta.get('element.import'))
428
- && nodeValue.endsWith('</' + _meta.get('element.import') + '>')) {
429
- var importEl, reviver = document.createElement('div');
430
- reviver.innerHTML = nodeValue;
431
- if ((importEl = reviver.firstChild).matches(_meta.get('element.import'))) {
432
- // Belongs to a composition block?
433
- var compositionBlock = !importEl.hasAttribute(_meta.get('attr.moduleref'))
434
- ? node.parentNode.closest(modulerefSelector) : (
435
- importEl.getAttribute(_meta.get('attr.moduleref')).trim().startsWith('~') ? node.parentNode.closest(exportgroupSelector) : null
436
- )
437
- var importElInstance = Import.create(importEl);
438
- importElInstance.hydrate(node, slottedElements, compositionBlock);
439
- // Empty basket
440
- slottedElements = [];
441
- }
442
- }
443
- });
444
- // Scanning is once for every parent
445
- _internals(_export.parentNode, 'oohtml').set('importsCan', true);
446
- });
447
- };
448
-
449
- // ----------------------
450
- // Hydrate
451
- // ----------------------
452
-
453
- WebQit.DOM.ready.call(WebQit, () => {
454
- if (_meta.get('isomorphic')) {
455
- hydrateSlots();
456
- }
457
- });
458
-
459
- };
460
-
461
- /**
462
- * Imports exports from from sourceEl into el.
463
- *
464
- * @param Element exportEl
465
- * @param Element superExportEl
466
- * @param array noinherit
467
- *
468
- * @return Element
469
- */
470
- export function mergePartials(exportEl, superExportEl, noinherit = []) {
471
- if (!superExportEl.exportsSlottables) {
472
- return exportEl;
473
- }
474
- _each(superExportEl.exportsSlottables, (slotId, slottable) => {
475
- if (exportEl.exportsSlottables && exportEl.exportsSlottables[slotId]) {
476
- // Simply inherit attributes from the search slottable
477
- // The export may however define a no-inherit directive for all its slottables
478
- var _noinherit = noinherit.concat((exportEl.getAttribute('noinherit') || '').split(' ').map(val => val.trim()));
479
- this.mergeAttributes(exportEl.exportsSlottables[slotId], slottable, _noinherit, false/*prioritize*/);
480
- } else {
481
- // Copy new slottables
482
- exportEl.append(slottable.clone(true));
483
- }
484
- });
485
- return exportEl;
486
- };
487
-
488
- /**
489
- * Imports attributes from sourceEl into el.
490
- *
491
- * @param Element el
492
- * @param Element sourceEl
493
- * @param array noinherit
494
- * @param bool prioritize
495
- *
496
- * @return Element
497
- */
498
- export function mergeAttributes(el, sourceEl, noinherit = [], prioritize = true) {
499
- // ----------------------------
500
- // Norecompose directive
501
- // ----------------------------
502
- noinherit = noinherit.concat(_defaultNoInherits);
503
- if (el.hasAttribute('noinherit')) {
504
- noinherit = noinherit.concat((el.getAttribute('noinherit') || '*').split(' ').map(val => val.trim()));
505
- }
506
- // ----------------------------
507
- // Merge list attributes...
508
- // ----------------------------
509
- var defaultListAttrs = _defaultListAttrs.concat(['role', 'class']);
510
- _unique(defaultListAttrs).forEach(type => {
511
- var b_attr, a_attr;
512
- if (!noinherit.includes(type) && !noinherit.includes('*') && (b_attr = sourceEl.getAttribute(type))) {
513
- if (a_attr = el.getAttribute(type)) {
514
- var jointList = !prioritize ? [b_attr, a_attr] : [a_attr, b_attr];
515
- } else {
516
- var jointList = [b_attr];
517
- }
518
- el.setAttribute(type, _unique(jointList.join(' ').split(' ').map(r => r.trim())).join(' '));
519
- noinherit.push(type);
520
- }
521
- });
522
- // ----------------------------
523
- // Merge key/val attributes...
524
- // ----------------------------
525
- _unique(_defaultKeyValAttrs.concat('style')).forEach(type => {
526
- var b_attr, a_attr;
527
- if (!noinherit.includes(type) && !noinherit.includes('*') && (b_attr = sourceEl.getAttribute(type))) {
528
- if (a_attr = el.getAttribute(type)) {
529
- var jointDefs = !prioritize ? [b_attr, a_attr] : [a_attr, b_attr];
530
- if (!jointDefs[0].trim().endsWith(';')) {
531
- jointDefs[0] = jointDefs[0] + ';';
532
- }
533
- } else {
534
- var jointDefs = [b_attr];
535
- }
536
- el.setAttribute(type, jointDefs.join(' '));
537
- noinherit.push(type);
538
- }
539
- });
540
- // ----------------------------
541
- // Port all other attributes...
542
- // ----------------------------
543
- if (!noinherit.includes('*')) {
544
- for (var i = 0; i < sourceEl.attributes.length; i ++) {
545
- var attr = sourceEl.attributes[i];
546
- if (!noinherit.includes(attr.name)
547
- && (!el.hasAttribute(attr.name) || prioritize)) {
548
- el.setAttribute(attr.name, attr.value);
549
- }
550
- }
551
- }
552
- return el;
553
- };
554
-
555
- const _defaultNoInherits = ['nocompose'],
556
- _defaultKeyValAttrs = [],
557
- _defaultListAttrs = [];
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import wqDom from '@webqit/dom';
6
+ import _HTMLImportElement from './_HTMLImportElement.js';
7
+ import { _ } from '../util.js';
8
+
9
+ /**
10
+ * Initializes HTML Modules.
11
+ *
12
+ * @param $params Object
13
+ *
14
+ * @return Void
15
+ */
16
+ export default function init( $params = { }) {
17
+ const window = this, dom = wqDom.call( window );
18
+ // -------
19
+ const params = dom.meta( 'oohtml' ).copyWithDefaults( $params, {
20
+ import: { tagName: 'import', attr: { moduleref: 'module' }, },
21
+ export: { attr: { exportid: 'exportid' }, },
22
+ isomorphic: true,
23
+ } );
24
+ params.slottedElementsSelector = `[${ window.CSS.escape( params.export.attr.exportid ) }]`;
25
+ params.HTMLImportElement = _HTMLImportElement.call( this, params );
26
+ // -------
27
+ dom.ready( () => hydration.call( this, params ) );
28
+ realtime.call( this, params );
29
+ }
30
+
31
+ /**
32
+ * Performs realtime capture of elements and their attributes
33
+ * and their module query results; then resolves the respective import elements.
34
+ *
35
+ * @param Object params
36
+ *
37
+ * @return Void
38
+ */
39
+ function realtime( params ) {
40
+ const window = this, { dom } = window.wq;
41
+ dom.realtime( window.document ).observe( params.import.tagName, record => {
42
+ record.entrants.forEach( node => handleRealtime( node, true, record ) );
43
+ record.exits.forEach( node => handleRealtime( node, false, record ) );
44
+ }, { subtree: true, timing: 'sync' } );
45
+ function handleRealtime( entry, connectedState, record ) {
46
+ const elInstance = params.HTMLImportElement.instance( entry );
47
+ if ( connectedState ) { elInstance[ '#' ].connectedCallback(); }
48
+ else { elInstance[ '#' ].disconnectedCallback(); }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Performs hydration for server-slotted elements.
54
+ *
55
+ * @param Object params
56
+ *
57
+ * @return Void
58
+ */
59
+ function hydration( params ) {
60
+ const window = this;
61
+ function scan( context ) {
62
+ const slottedElements = new Set;
63
+ context.childNodes.forEach( node => {
64
+ if ( node.nodeType === 1/** ELEMENT_NODE */ ) {
65
+ if ( !node.matches( params.slottedElementsSelector ) ) return;
66
+ if ( _( node ).get( 'slot@imports' ) ) return;
67
+ slottedElements.add( node );
68
+ } else if ( node.nodeType === 8/** COMMENT_NODE */ ) {
69
+ const nodeValue = node.nodeValue.trim();
70
+ if ( !nodeValue.startsWith( '<' + params.import.tagName ) ) return;
71
+ if ( !nodeValue.endsWith( '</' + params.import.tagName + '>' ) ) return;
72
+ const reviver = window.document.createElement( 'div' );
73
+ reviver.innerHTML = nodeValue;
74
+ const importEl = reviver.firstChild;
75
+ if ( !importEl.matches( params.import.tagName ) ) return;
76
+ params.HTMLImportElement.instance( importEl )[ '#' ].hydrate(
77
+ node/* the comment node */, slottedElements
78
+ );
79
+ slottedElements.clear();
80
+ }
81
+ } );
82
+ }
83
+ Array.from( window.document.querySelectorAll( params.slottedElementsSelector ) ).forEach( slottedElement => {
84
+ // hydration() might be running AFTER certain <slots> have resolved
85
+ // and slottedElement might be a just-resolved node
86
+ if ( _( slottedElement ).get( 'slot@imports' ) ) return;
87
+ if ( _( slottedElement.parentNode ).get( 'alreadyscanned@imports' ) ) return;
88
+ scan( slottedElement.parentNode );
89
+ // Scanning is once for every parent
90
+ _( slottedElement.parentNode ).set( 'alreadyscanned@imports', true );
91
+ } );
92
+ }
@@ -1,10 +1,10 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import init from './index.js';
6
-
7
- /**
8
- * @init
9
- */
10
- init.call(window);
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import init from './index.js';
6
+
7
+ /**
8
+ * @init
9
+ */
10
+ init.call( window );