@webqit/oohtml 1.10.2 → 1.10.4

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.
@@ -1,384 +1,384 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import { _isEmpty, _internals } from '@webqit/util/js/index.js';
6
- import { _remove, _from as _arrFrom } from '@webqit/util/arr/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 } from '../util.js';
10
-
11
- /**
12
- * ---------------------------
13
- * Named Templates
14
- * ---------------------------
15
- */
16
-
17
- /**
18
- * @init
19
- *
20
- * @param Object config
21
- */
22
- export default function init( _config = {} ) {
23
-
24
- const WebQit = domInit.call( this );
25
- if ( _config.onDomReady ) {
26
- WebQit.DOM.ready( () => {
27
- init.call( this, { ..._config, onDomReady: false } );
28
- } );
29
- return;
30
- }
31
-
32
- const window = WebQit.window;
33
- const document = WebQit.window.document;
34
- const mutations = WebQit.DOM.mutations;
35
-
36
- const _meta = config.call(this, {
37
- element: {
38
- template: '',
39
- export: 'export',
40
- import: 'import',
41
- },
42
- attr: {
43
- moduleid: 'name',
44
- moduleref: 'template',
45
- exportid: 'name',
46
- exportgroup: 'exportgroup',
47
- },
48
- api: {
49
- templateClass: '',
50
- templates: 'templates',
51
- exports: 'exports',
52
- moduleref: 'template',
53
- },
54
- }, _config.params );
55
-
56
- const templateSelector = 'template' + (_meta.get('element.template') ? '[is="' + _meta.get('element.template') + '"]' : '') + '[' + window.CSS.escape(_meta.get('attr.moduleid')) + ']';
57
- var TemplateElementClass = window.HTMLTemplateElement;
58
- if (_meta.get('api.templateClass')) {
59
- if (!window[_meta.get('api.templateClass')]) {
60
- throw new Error('The custom element class "' + _meta.get('api.templateClass') + '" is not defined!');
61
- }
62
- TemplateElementClass = window[_meta.get('api.templateClass')];
63
- }
64
-
65
- // ----------------------
66
- // Capture template elements
67
- // ----------------------
68
-
69
- const fireDocumentTemplateEvent = (type, value, path) => {
70
- if (type === 'templatemutation') {
71
- ['addedExports', 'removedExports'].forEach(listType => {
72
- Object.defineProperty(value, listType, {value: Object.keys(value[listType]).map(name => ({name, items: value[listType][name]}))});
73
- });
74
- ['addedTemplates', 'removedTemplates'].forEach(listType => {
75
- Object.defineProperty(value, listType, {value: Object.keys(value[listType]).map(name => ({name, item: value[listType][name]}))});
76
- });
77
- }
78
- Object.defineProperty(value, 'path', {value: path});
79
- document.dispatchEvent(new window.CustomEvent(type, {detail: value}));
80
- };
81
-
82
- const loadTemplateContent = (template, path) => {
83
- const fireTemplateEvent = type => {
84
- template.dispatchEvent(new window.CustomEvent(type, {detail: {path}}));
85
- };
86
- var src = template.getAttribute('src');
87
- return new Promise((resolve, reject) => {
88
- // Missing in jsdom
89
- if (window.fetch) {
90
- window.fetch(src).then(response => {
91
- return response.ok ? response.text() : Promise.reject(response.statusText);
92
- }).then(content => {
93
- template.innerHTML = content;
94
- fireTemplateEvent('load');
95
- fireDocumentTemplateEvent('templatecontentloaded', {template}, path);
96
- resolve(template);
97
- }).catch(error => {
98
- console.error('Error fetching the bundle at ' + src + '. (' + error + ')');
99
- // Dispatch the event.
100
- template.innerHTML = '';
101
- fireTemplateEvent('loaderror');
102
- fireDocumentTemplateEvent('templatecontentloaderror', {template}, path);
103
- resolve(template);
104
- });
105
- } else {
106
- resolve();
107
- console.error('Error fetching the bundle at ' + src + '. (window.fetch() not supported by browser.)');
108
- }
109
- });
110
- };
111
-
112
- const discoverContents = (node, contentNode, path, mutationType = null, fireEvents = true) => {
113
-
114
- // -----------------------
115
- // Templates and exports
116
- const manageComponent = (el, eventsObject, mutationType, fireEvents) => {
117
- if (!el.matches) {
118
- // Not an element child
119
- return;
120
- }
121
- var templateName, exportId;
122
- if (el.matches(templateSelector) && (templateName = el.getAttribute(_meta.get('attr.moduleid'))) && validateModuleName(templateName)) {
123
- var _path = (path ? path + '/' : '') + templateName;
124
- if (mutationType === 'removed') {
125
- _internals(node, 'oohtml', 'templates').delete(templateName)
126
- if (_internals(el, 'oohtml').get('parentTemplate') === node) {
127
- _internals(el, 'oohtml').delete('parentTemplate');
128
- }
129
- if (eventsObject) {
130
- eventsObject.removedTemplates[templateName] = el;
131
- }
132
- } else if (mutationType === 'added') {
133
- _internals(node, 'oohtml', 'templates').set(templateName, el);
134
- _internals(el, 'oohtml').set('parentTemplate', node);
135
- if (eventsObject) {
136
- eventsObject.addedTemplates[templateName] = el;
137
- }
138
- }
139
- // Recurse
140
- discoverContents(el, el.content, _path, mutationType, fireEvents);
141
- } else {
142
- const manageExportItem = exportItem => {
143
- var exportId = exportItem.getAttribute(_meta.get('attr.exportgroup')) || 'default';
144
- if (mutationType === 'removed') {
145
- if (_internals(node, 'oohtml', 'exports').has(exportId)) {
146
- _remove(_internals(node, 'oohtml', 'exports').get(exportId), exportItem);
147
- if (!_internals(node, 'oohtml', 'exports').has(exportId).length) {
148
- _internals(node, 'oohtml', 'exports').delete(exportId);
149
- }
150
- if (eventsObject) {
151
- if (!eventsObject.removedExports[exportId]) {
152
- eventsObject.removedExports[exportId] = [];
153
- }
154
- eventsObject.removedExports[exportId].push(exportItem);
155
- }
156
- }
157
- } else if (mutationType === 'added') {
158
- if (!_internals(node, 'oohtml', 'exports').has(exportId)) {
159
- _internals(node, 'oohtml', 'exports').set(exportId, []);
160
- }
161
- _internals(node, 'oohtml', 'exports').get(exportId).push(exportItem);
162
- if (eventsObject) {
163
- if (!eventsObject.addedExports[exportId]) {
164
- eventsObject.addedExports[exportId] = [];
165
- }
166
- eventsObject.addedExports[exportId].push(exportItem);
167
- }
168
- }
169
- };
170
- if (el.matches(_meta.get('element.export'))) {
171
- var exportId = el.getAttribute(_meta.get('attr.exportid')) || 'default';
172
- _arrFrom(el.children).forEach(exportItem => {
173
- exportItem.setAttribute(_meta.get('attr.exportgroup'), exportId);
174
- manageExportItem(exportItem);
175
- });
176
- } else {
177
- manageExportItem(el);
178
- }
179
- }
180
- };
181
-
182
- // -----------------------
183
- // Run...
184
- node.modulemutationsType = mutationType;
185
- const eventsObject = { addedTemplates: Object.create(null), removedTemplates: Object.create(null), addedExports: Object.create(null), removedExports: Object.create(null), };
186
- _arrFrom(contentNode.children).forEach(el => manageComponent(el, eventsObject, mutationType, fireEvents));
187
- if (fireEvents) {
188
- fireDocumentTemplateEvent('templatemutation', eventsObject, path);
189
- }
190
-
191
- // -----------------------
192
- // Handle content loading
193
- if (mutationType === 'added' && !_internals(node, 'oohtml').get('onLiveMode')) {
194
- _internals(node, 'oohtml').set('onLiveMode', true);
195
- const honourSrc = () => {
196
- if (node.content.children.length) return;
197
- _internals(node, 'oohtml').delete('queryCallback');
198
- return loadTemplateContent(node, path);
199
- };
200
- if (node.getAttribute('src')) {
201
- if (node.getAttribute('loading') === 'lazy') {
202
- _internals(node, 'oohtml').set('queryCallback', honourSrc);
203
- } else {
204
- loadingTemplates.push(honourSrc());
205
- }
206
- }
207
- mutations.onAttrChange(node, mr => {
208
- if (mr[0].target.getAttribute(mr[0].attributeName) === mr[0].oldValue) return;
209
- if (node.getAttribute('loading') === 'lazy') {
210
- _internals(node, 'oohtml').set('queryCallback', honourSrc);
211
- } else if (mr[0].attributeName === 'loading') {
212
- _internals(node, 'oohtml').delete('queryCallback');
213
- } else {
214
- honourSrc();
215
- }
216
- }, ['src', 'loading']);
217
-
218
- // -----------------------
219
- // Watch mutations
220
- var mo = new window.MutationObserver(mutations => {
221
- const eventsObject = { addedTemplates: Object.create(null), removedTemplates: Object.create(null), addedExports: Object.create(null), removedExports: Object.create(null), };
222
- mutations.forEach(mutation => {
223
- mutation.addedNodes.forEach(el => manageComponent(el, eventsObject, 'added', true));
224
- mutation.removedNodes.forEach(el => manageComponent(el, eventsObject, 'removed', true));
225
- });
226
- fireDocumentTemplateEvent('templatemutation', eventsObject, path);
227
- });
228
- mo.observe(contentNode, {childList: true});
229
- }
230
-
231
- };
232
-
233
- // ----------------------
234
- // Define the global "templates" object
235
- // ----------------------
236
-
237
- if (_meta.get('api.templates') in document) {
238
- throw new Error('document already has a "' + _meta.get('api.templates') + '" property!');
239
- }
240
- const loadingTemplates = [];
241
- Object.defineProperty(document, _meta.get('api.templates'), {
242
- get: function() {
243
- return mapToObject(_internals(document, 'oohtml', 'templates'));
244
- }
245
- });
246
-
247
- // ----------------------
248
- // Define the "templates" and "exports" properties on HTMLTemplateElement.prototype
249
- // ----------------------
250
-
251
- if (_meta.get('api.templates') in TemplateElementClass.prototype) {
252
- throw new Error('The "HTMLTemplateElement" class already has a "' + _meta.get('api.templates') + '" property!');
253
- }
254
- Object.defineProperty(TemplateElementClass.prototype, _meta.get('api.templates'), {
255
- get: function() {
256
- if (_internals(this, 'oohtml').has('queryCallback')) {
257
- _internals(this, 'oohtml').get('queryCallback')();
258
- }
259
- return mapToObject(_internals(this, 'oohtml', 'templates'));
260
- }
261
- });
262
- if (_meta.get('api.exports') in TemplateElementClass.prototype) {
263
- throw new Error('The "HTMLTemplateElement" class already has a "' + _meta.get('api.exports') + '" property!');
264
- }
265
- Object.defineProperty(TemplateElementClass.prototype, _meta.get('api.exports'), {
266
- get: function() {
267
- if (_internals(this, 'oohtml').has('queryCallback')) {
268
- _internals(this, 'oohtml').get('queryCallback')();
269
- }
270
- return mapToObject(_internals(this, 'oohtml', 'exports'));
271
- }
272
- });
273
-
274
- const mapToObject = map => {
275
- return Object.defineProperties({}, Array.from(map.keys()).reduce((desc, name) => {
276
- desc[name] = {get: () => map.get(name)};
277
- return desc;
278
- }, {}));
279
- };
280
-
281
- const validateModuleName = name => {
282
- var invalidCharacterMatch;
283
- if (invalidCharacterMatch = name.match(/([^a-zA-Z0-9\_\-\@])/)) {
284
- console.error(`Invalid character "${invalidCharacterMatch}" in the module name: ${name}.`);
285
- return false;
286
- }
287
- return true;
288
- };
289
-
290
- const templatesQuery = query => {
291
- var _module = document.createElement('template');
292
- // -----------------
293
- scopeQuery([document], query, function(host, prop) {
294
- var collection = _internals(host, 'oohtml', 'templates');
295
- if (arguments.length === 1) return collection;
296
- if (prop.startsWith(':')) return _internals(host, 'oohtml', 'exports').get(prop.substr(1));
297
- return collection.get(prop);
298
- }).forEach($module => {
299
- _internals($module, 'oohtml', 'templates').forEach((template, moduleId) => {
300
- _internals(_module, 'oohtml', 'templates').set(moduleId, template);
301
- });
302
- _internals($module, 'oohtml', 'exports').forEach((exports, exportId) => {
303
- if (!_internals(_module, 'oohtml', 'exports').has(exportId)) {
304
- _internals(_module, 'oohtml', 'exports').set(exportId, []);
305
- }
306
- _internals(_module, 'oohtml', 'exports').get(exportId).push(...exports);
307
- });
308
- });
309
- return _module;
310
- };
311
-
312
- _arrFrom(document.querySelectorAll(templateSelector)).forEach(async el => {
313
- var name = el.getAttribute(_meta.get('attr.moduleid'));
314
- if (!el.closest(_meta.get('element.import')) && validateModuleName(name)) {
315
- _internals(document, 'oohtml', 'templates').set(name, el);
316
- discoverContents(el, el.content, name, 'added', false);
317
- }
318
- });
319
- mutations.onPresenceChange(templateSelector, async (els, presence) => {
320
- const eventsObject = { addedTemplates: Object.create(null), removedTemplates: Object.create(null), addedExports: Object.create(null), removedExports: Object.create(null), };
321
- els.forEach(el => {
322
- var name = el.getAttribute(_meta.get('attr.moduleid'));
323
- if (el.closest(_meta.get('element.import')) || !validateModuleName(name)) return;
324
- if (presence) {
325
- _internals(document, 'oohtml', 'templates').set(name, el);
326
- discoverContents(el, el.content, name, 'added');
327
- eventsObject.addedTemplates[name] = el;
328
- } else {
329
- if (_internals(document, 'oohtml', 'templates').get(name) === el) {
330
- _internals(document, 'oohtml', 'templates').delete(name);
331
- }
332
- discoverContents(el, el.content, name, 'removed');
333
- eventsObject.removedTemplates[name] = el;
334
- }
335
- });
336
- fireDocumentTemplateEvent('templatemutation', eventsObject, '');
337
- });
338
-
339
- // ----------------------
340
- // Capture import elements
341
- // ----------------------
342
-
343
- mutations.onPresent(_meta.get('element.import'), el => {
344
- discoverContents(el, el, '', 'added', false);
345
- });
346
-
347
- // ----------------------
348
- // Define the "template" property on Element.prototype
349
- // ----------------------
350
-
351
- if (_meta.get('api.moduleref') in window.Element.prototype) {
352
- throw new Error('The "Element" class already has a "' + _meta.get('api.moduleref') + '" property!');
353
- }
354
- Object.defineProperty(window.Element.prototype, _meta.get('api.moduleref'), {
355
- get: function() {
356
- var templateId;
357
- if (!_internals(this, 'oohtml').has('module')
358
- && (templateId = this.getAttribute(_meta.get('attr.moduleref')))) {
359
- var _module = templatesQuery(templateId);
360
- _internals(this, 'oohtml').set('module', _module)
361
- }
362
- return _internals(this, 'oohtml').get('module');
363
- },
364
- });
365
-
366
- // ----------------------
367
- // Hydrate
368
- // ----------------------
369
-
370
- //Object.defineProperty(document, 'templatesQuery', { value: templatesQuery });
371
- var templatesReadyState = loadingTemplates.length ? 'loading' : 'indeterminate';
372
- Object.defineProperty(document, 'templatesReadyState', { get: () => templatesReadyState });
373
- WebQit.DOM.ready.call(WebQit, () => {
374
- loadingTemplates.forEach(promise => {
375
- promise && promise.catch(error => {
376
- console.warn(error);
377
- });
378
- });
379
- return Promise.all(loadingTemplates).then(() => {
380
- templatesReadyState = 'complete';
381
- document.dispatchEvent(new window.Event('templatesreadystatechange'));
382
- });
383
- });
384
- }
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import { _isEmpty, _internals } from '@webqit/util/js/index.js';
6
+ import { _remove, _from as _arrFrom } from '@webqit/util/arr/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 } from '../util.js';
10
+
11
+ /**
12
+ * ---------------------------
13
+ * Named Templates
14
+ * ---------------------------
15
+ */
16
+
17
+ /**
18
+ * @init
19
+ *
20
+ * @param Object config
21
+ */
22
+ export default function init( _config = {} ) {
23
+
24
+ const WebQit = domInit.call( this );
25
+ if ( _config.onDomReady ) {
26
+ WebQit.DOM.ready( () => {
27
+ init.call( this, { ..._config, onDomReady: false } );
28
+ } );
29
+ return;
30
+ }
31
+
32
+ const window = WebQit.window;
33
+ const document = WebQit.window.document;
34
+ const mutations = WebQit.DOM.mutations;
35
+
36
+ const _meta = config.call(this, {
37
+ element: {
38
+ template: '',
39
+ export: 'export',
40
+ import: 'import',
41
+ },
42
+ attr: {
43
+ moduleid: 'name',
44
+ moduleref: 'template',
45
+ exportid: 'name',
46
+ exportgroup: 'exportgroup',
47
+ },
48
+ api: {
49
+ templateClass: '',
50
+ templates: 'templates',
51
+ exports: 'exports',
52
+ moduleref: 'template',
53
+ },
54
+ }, _config.params );
55
+
56
+ const templateSelector = 'template' + (_meta.get('element.template') ? '[is="' + _meta.get('element.template') + '"]' : '') + '[' + window.CSS.escape(_meta.get('attr.moduleid')) + ']';
57
+ var TemplateElementClass = window.HTMLTemplateElement;
58
+ if (_meta.get('api.templateClass')) {
59
+ if (!window[_meta.get('api.templateClass')]) {
60
+ throw new Error('The custom element class "' + _meta.get('api.templateClass') + '" is not defined!');
61
+ }
62
+ TemplateElementClass = window[_meta.get('api.templateClass')];
63
+ }
64
+
65
+ // ----------------------
66
+ // Capture template elements
67
+ // ----------------------
68
+
69
+ const fireDocumentTemplateEvent = (type, value, path) => {
70
+ if (type === 'templatemutation') {
71
+ ['addedExports', 'removedExports'].forEach(listType => {
72
+ Object.defineProperty(value, listType, {value: Object.keys(value[listType]).map(name => ({name, items: value[listType][name]}))});
73
+ });
74
+ ['addedTemplates', 'removedTemplates'].forEach(listType => {
75
+ Object.defineProperty(value, listType, {value: Object.keys(value[listType]).map(name => ({name, item: value[listType][name]}))});
76
+ });
77
+ }
78
+ Object.defineProperty(value, 'path', {value: path});
79
+ document.dispatchEvent(new window.CustomEvent(type, {detail: value}));
80
+ };
81
+
82
+ const loadTemplateContent = (template, path) => {
83
+ const fireTemplateEvent = type => {
84
+ template.dispatchEvent(new window.CustomEvent(type, {detail: {path}}));
85
+ };
86
+ var src = template.getAttribute('src');
87
+ return new Promise((resolve, reject) => {
88
+ // Missing in jsdom
89
+ if (window.fetch) {
90
+ window.fetch(src).then(response => {
91
+ return response.ok ? response.text() : Promise.reject(response.statusText);
92
+ }).then(content => {
93
+ template.innerHTML = content;
94
+ fireTemplateEvent('load');
95
+ fireDocumentTemplateEvent('templatecontentloaded', {template}, path);
96
+ resolve(template);
97
+ }).catch(error => {
98
+ console.error('Error fetching the bundle at ' + src + '. (' + error + ')');
99
+ // Dispatch the event.
100
+ template.innerHTML = '';
101
+ fireTemplateEvent('loaderror');
102
+ fireDocumentTemplateEvent('templatecontentloaderror', {template}, path);
103
+ resolve(template);
104
+ });
105
+ } else {
106
+ resolve();
107
+ console.error('Error fetching the bundle at ' + src + '. (window.fetch() not supported by browser.)');
108
+ }
109
+ });
110
+ };
111
+
112
+ const discoverContents = (node, contentNode, path, mutationType = null, fireEvents = true) => {
113
+
114
+ // -----------------------
115
+ // Templates and exports
116
+ const manageComponent = (el, eventsObject, mutationType, fireEvents) => {
117
+ if (!el.matches) {
118
+ // Not an element child
119
+ return;
120
+ }
121
+ var templateName, exportId;
122
+ if (el.matches(templateSelector) && (templateName = el.getAttribute(_meta.get('attr.moduleid'))) && validateModuleName(templateName)) {
123
+ var _path = (path ? path + '/' : '') + templateName;
124
+ if (mutationType === 'removed') {
125
+ _internals(node, 'oohtml', 'templates').delete(templateName)
126
+ if (_internals(el, 'oohtml').get('parentTemplate') === node) {
127
+ _internals(el, 'oohtml').delete('parentTemplate');
128
+ }
129
+ if (eventsObject) {
130
+ eventsObject.removedTemplates[templateName] = el;
131
+ }
132
+ } else if (mutationType === 'added') {
133
+ _internals(node, 'oohtml', 'templates').set(templateName, el);
134
+ _internals(el, 'oohtml').set('parentTemplate', node);
135
+ if (eventsObject) {
136
+ eventsObject.addedTemplates[templateName] = el;
137
+ }
138
+ }
139
+ // Recurse
140
+ discoverContents(el, el.content, _path, mutationType, fireEvents);
141
+ } else {
142
+ const manageExportItem = exportItem => {
143
+ var exportId = exportItem.getAttribute(_meta.get('attr.exportgroup')) || 'default';
144
+ if (mutationType === 'removed') {
145
+ if (_internals(node, 'oohtml', 'exports').has(exportId)) {
146
+ _remove(_internals(node, 'oohtml', 'exports').get(exportId), exportItem);
147
+ if (!_internals(node, 'oohtml', 'exports').has(exportId).length) {
148
+ _internals(node, 'oohtml', 'exports').delete(exportId);
149
+ }
150
+ if (eventsObject) {
151
+ if (!eventsObject.removedExports[exportId]) {
152
+ eventsObject.removedExports[exportId] = [];
153
+ }
154
+ eventsObject.removedExports[exportId].push(exportItem);
155
+ }
156
+ }
157
+ } else if (mutationType === 'added') {
158
+ if (!_internals(node, 'oohtml', 'exports').has(exportId)) {
159
+ _internals(node, 'oohtml', 'exports').set(exportId, []);
160
+ }
161
+ _internals(node, 'oohtml', 'exports').get(exportId).push(exportItem);
162
+ if (eventsObject) {
163
+ if (!eventsObject.addedExports[exportId]) {
164
+ eventsObject.addedExports[exportId] = [];
165
+ }
166
+ eventsObject.addedExports[exportId].push(exportItem);
167
+ }
168
+ }
169
+ };
170
+ if (el.matches(_meta.get('element.export'))) {
171
+ var exportId = el.getAttribute(_meta.get('attr.exportid')) || 'default';
172
+ _arrFrom(el.children).forEach(exportItem => {
173
+ exportItem.setAttribute(_meta.get('attr.exportgroup'), exportId);
174
+ manageExportItem(exportItem);
175
+ });
176
+ } else {
177
+ manageExportItem(el);
178
+ }
179
+ }
180
+ };
181
+
182
+ // -----------------------
183
+ // Run...
184
+ node.modulemutationsType = mutationType;
185
+ const eventsObject = { addedTemplates: Object.create(null), removedTemplates: Object.create(null), addedExports: Object.create(null), removedExports: Object.create(null), };
186
+ _arrFrom(contentNode.children).forEach(el => manageComponent(el, eventsObject, mutationType, fireEvents));
187
+ if (fireEvents) {
188
+ fireDocumentTemplateEvent('templatemutation', eventsObject, path);
189
+ }
190
+
191
+ // -----------------------
192
+ // Handle content loading
193
+ if (mutationType === 'added' && !_internals(node, 'oohtml').get('onLiveMode')) {
194
+ _internals(node, 'oohtml').set('onLiveMode', true);
195
+ const honourSrc = () => {
196
+ if (node.content.children.length) return;
197
+ _internals(node, 'oohtml').delete('queryCallback');
198
+ return loadTemplateContent(node, path);
199
+ };
200
+ if (node.getAttribute('src')) {
201
+ if (node.getAttribute('loading') === 'lazy') {
202
+ _internals(node, 'oohtml').set('queryCallback', honourSrc);
203
+ } else {
204
+ loadingTemplates.push(honourSrc());
205
+ }
206
+ }
207
+ mutations.onAttrChange(node, mr => {
208
+ if (mr[0].target.getAttribute(mr[0].attributeName) === mr[0].oldValue) return;
209
+ if (node.getAttribute('loading') === 'lazy') {
210
+ _internals(node, 'oohtml').set('queryCallback', honourSrc);
211
+ } else if (mr[0].attributeName === 'loading') {
212
+ _internals(node, 'oohtml').delete('queryCallback');
213
+ } else {
214
+ honourSrc();
215
+ }
216
+ }, ['src', 'loading']);
217
+
218
+ // -----------------------
219
+ // Watch mutations
220
+ var mo = new window.MutationObserver(mutations => {
221
+ const eventsObject = { addedTemplates: Object.create(null), removedTemplates: Object.create(null), addedExports: Object.create(null), removedExports: Object.create(null), };
222
+ mutations.forEach(mutation => {
223
+ mutation.addedNodes.forEach(el => manageComponent(el, eventsObject, 'added', true));
224
+ mutation.removedNodes.forEach(el => manageComponent(el, eventsObject, 'removed', true));
225
+ });
226
+ fireDocumentTemplateEvent('templatemutation', eventsObject, path);
227
+ });
228
+ mo.observe(contentNode, {childList: true});
229
+ }
230
+
231
+ };
232
+
233
+ // ----------------------
234
+ // Define the global "templates" object
235
+ // ----------------------
236
+
237
+ if (_meta.get('api.templates') in document) {
238
+ throw new Error('document already has a "' + _meta.get('api.templates') + '" property!');
239
+ }
240
+ const loadingTemplates = [];
241
+ Object.defineProperty(document, _meta.get('api.templates'), {
242
+ get: function() {
243
+ return mapToObject(_internals(document, 'oohtml', 'templates'));
244
+ }
245
+ });
246
+
247
+ // ----------------------
248
+ // Define the "templates" and "exports" properties on HTMLTemplateElement.prototype
249
+ // ----------------------
250
+
251
+ if (_meta.get('api.templates') in TemplateElementClass.prototype) {
252
+ throw new Error('The "HTMLTemplateElement" class already has a "' + _meta.get('api.templates') + '" property!');
253
+ }
254
+ Object.defineProperty(TemplateElementClass.prototype, _meta.get('api.templates'), {
255
+ get: function() {
256
+ if (_internals(this, 'oohtml').has('queryCallback')) {
257
+ _internals(this, 'oohtml').get('queryCallback')();
258
+ }
259
+ return mapToObject(_internals(this, 'oohtml', 'templates'));
260
+ }
261
+ });
262
+ if (_meta.get('api.exports') in TemplateElementClass.prototype) {
263
+ throw new Error('The "HTMLTemplateElement" class already has a "' + _meta.get('api.exports') + '" property!');
264
+ }
265
+ Object.defineProperty(TemplateElementClass.prototype, _meta.get('api.exports'), {
266
+ get: function() {
267
+ if (_internals(this, 'oohtml').has('queryCallback')) {
268
+ _internals(this, 'oohtml').get('queryCallback')();
269
+ }
270
+ return mapToObject(_internals(this, 'oohtml', 'exports'));
271
+ }
272
+ });
273
+
274
+ const mapToObject = map => {
275
+ return Object.defineProperties({}, Array.from(map.keys()).reduce((desc, name) => {
276
+ desc[name] = {get: () => map.get(name)};
277
+ return desc;
278
+ }, {}));
279
+ };
280
+
281
+ const validateModuleName = name => {
282
+ var invalidCharacterMatch;
283
+ if (invalidCharacterMatch = name.match(/([^a-zA-Z0-9\_\-\@])/)) {
284
+ console.error(`Invalid character "${invalidCharacterMatch}" in the module name: ${name}.`);
285
+ return false;
286
+ }
287
+ return true;
288
+ };
289
+
290
+ const templatesQuery = query => {
291
+ var _module = document.createElement('template');
292
+ // -----------------
293
+ scopeQuery([document], query, function(host, prop) {
294
+ var collection = _internals(host, 'oohtml', 'templates');
295
+ if (arguments.length === 1) return collection;
296
+ if (prop.startsWith(':')) return _internals(host, 'oohtml', 'exports').get(prop.substr(1));
297
+ return collection.get(prop);
298
+ }).forEach($module => {
299
+ _internals($module, 'oohtml', 'templates').forEach((template, moduleId) => {
300
+ _internals(_module, 'oohtml', 'templates').set(moduleId, template);
301
+ });
302
+ _internals($module, 'oohtml', 'exports').forEach((exports, exportId) => {
303
+ if (!_internals(_module, 'oohtml', 'exports').has(exportId)) {
304
+ _internals(_module, 'oohtml', 'exports').set(exportId, []);
305
+ }
306
+ _internals(_module, 'oohtml', 'exports').get(exportId).push(...exports);
307
+ });
308
+ });
309
+ return _module;
310
+ };
311
+
312
+ _arrFrom(document.querySelectorAll(templateSelector)).forEach(async el => {
313
+ var name = el.getAttribute(_meta.get('attr.moduleid'));
314
+ if (!el.closest(_meta.get('element.import')) && validateModuleName(name)) {
315
+ _internals(document, 'oohtml', 'templates').set(name, el);
316
+ discoverContents(el, el.content, name, 'added', false);
317
+ }
318
+ });
319
+ mutations.onPresenceChange(templateSelector, async (els, presence) => {
320
+ const eventsObject = { addedTemplates: Object.create(null), removedTemplates: Object.create(null), addedExports: Object.create(null), removedExports: Object.create(null), };
321
+ els.forEach(el => {
322
+ var name = el.getAttribute(_meta.get('attr.moduleid'));
323
+ if (el.closest(_meta.get('element.import')) || !validateModuleName(name)) return;
324
+ if (presence) {
325
+ _internals(document, 'oohtml', 'templates').set(name, el);
326
+ discoverContents(el, el.content, name, 'added');
327
+ eventsObject.addedTemplates[name] = el;
328
+ } else {
329
+ if (_internals(document, 'oohtml', 'templates').get(name) === el) {
330
+ _internals(document, 'oohtml', 'templates').delete(name);
331
+ }
332
+ discoverContents(el, el.content, name, 'removed');
333
+ eventsObject.removedTemplates[name] = el;
334
+ }
335
+ });
336
+ fireDocumentTemplateEvent('templatemutation', eventsObject, '');
337
+ });
338
+
339
+ // ----------------------
340
+ // Capture import elements
341
+ // ----------------------
342
+
343
+ mutations.onPresent(_meta.get('element.import'), el => {
344
+ discoverContents(el, el, '', 'added', false);
345
+ });
346
+
347
+ // ----------------------
348
+ // Define the "template" property on Element.prototype
349
+ // ----------------------
350
+
351
+ if (_meta.get('api.moduleref') in window.Element.prototype) {
352
+ throw new Error('The "Element" class already has a "' + _meta.get('api.moduleref') + '" property!');
353
+ }
354
+ Object.defineProperty(window.Element.prototype, _meta.get('api.moduleref'), {
355
+ get: function() {
356
+ var templateId;
357
+ if (!_internals(this, 'oohtml').has('module')
358
+ && (templateId = this.getAttribute(_meta.get('attr.moduleref')))) {
359
+ var _module = templatesQuery(templateId);
360
+ _internals(this, 'oohtml').set('module', _module)
361
+ }
362
+ return _internals(this, 'oohtml').get('module');
363
+ },
364
+ });
365
+
366
+ // ----------------------
367
+ // Hydrate
368
+ // ----------------------
369
+
370
+ //Object.defineProperty(document, 'templatesQuery', { value: templatesQuery });
371
+ var templatesReadyState = loadingTemplates.length ? 'loading' : 'indeterminate';
372
+ Object.defineProperty(document, 'templatesReadyState', { get: () => templatesReadyState });
373
+ WebQit.DOM.ready.call(WebQit, () => {
374
+ loadingTemplates.forEach(promise => {
375
+ promise && promise.catch(error => {
376
+ console.warn(error);
377
+ });
378
+ });
379
+ return Promise.all(loadingTemplates).then(() => {
380
+ templatesReadyState = 'complete';
381
+ document.dispatchEvent(new window.Event('templatesreadystatechange'));
382
+ });
383
+ });
384
+ }