@webqit/oohtml 4.1.10 → 4.1.12
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/dist/bindings-api.js +1 -1
- package/dist/bindings-api.js.map +2 -2
- package/dist/context-api.js +1 -1
- package/dist/context-api.js.map +2 -2
- package/dist/data-binding.js +1 -1
- package/dist/data-binding.js.map +2 -2
- package/dist/html-imports.js +1 -1
- package/dist/html-imports.js.map +2 -2
- package/dist/main.js +8 -8
- package/dist/main.js.map +2 -2
- package/dist/main.lite.js +5 -5
- package/dist/main.lite.js.map +2 -2
- package/dist/namespaced-html.js +1 -1
- package/dist/namespaced-html.js.map +2 -2
- package/dist/scoped-css.js +1 -1
- package/dist/scoped-css.js.map +2 -2
- package/dist/scoped-js.js +1 -1
- package/dist/scoped-js.js.map +2 -2
- package/package.json +2 -2
- package/src/context-api/DOMContexts.js +2 -0
- package/src/data-binding/index.js +1 -1
- package/src/html-imports/HTMLImportsContext.js +4 -1
- package/src/html-imports/HTMLModule.js +26 -28
- package/src/html-imports/_HTMLImportElement.js +51 -49
- package/src/html-imports/index.js +100 -94
- package/test/imports.test.js +2 -1
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"wicg-proposal"
|
|
15
15
|
],
|
|
16
16
|
"homepage": "https://webqit.io/tooling/oohtml",
|
|
17
|
-
"version": "4.1.
|
|
17
|
+
"version": "4.1.12",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@webqit/quantum-js": "^4.5.8",
|
|
43
|
-
"@webqit/realdom": "^2.1.
|
|
43
|
+
"@webqit/realdom": "^2.1.25",
|
|
44
44
|
"@webqit/util": "^0.8.11"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -84,6 +84,8 @@ export default class DOMContexts {
|
|
|
84
84
|
args[ args.indexOf( options ) ] = { ...options, signal: responseInstance.signal };
|
|
85
85
|
}
|
|
86
86
|
const event = new ( _DOMContextRequestEvent() )( ...args );
|
|
87
|
+
// Initally
|
|
88
|
+
event.meta.target = this[ '#' ].host;
|
|
87
89
|
|
|
88
90
|
const rootNode = this[ '#' ].host.getRootNode();
|
|
89
91
|
const temp = event => {
|
|
@@ -45,7 +45,7 @@ function realtime( config ) {
|
|
|
45
45
|
realdom.realtime( window.document ).query( config.attrSelector, record => {
|
|
46
46
|
cleanup.call( this, ...record.exits );
|
|
47
47
|
mountInlineBindings.call( window, config, ...record.entrants );
|
|
48
|
-
}, { live: true, subtree: 'cross-roots', timing: 'sync', staticSensitivity: true } );
|
|
48
|
+
}, { live: true, subtree: 'cross-roots', timing: 'sync', eventDetails: true, staticSensitivity: true } );
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function createDynamicScope( config, root ) {
|
|
@@ -90,14 +90,17 @@ export default class HTMLImportsContext extends DOMContext {
|
|
|
90
90
|
// Any existing this.refdSourceController? Abort!
|
|
91
91
|
this.refdSourceController?.disconnect();
|
|
92
92
|
const realdom = this.host.ownerDocument.defaultView.webqit.realdom;
|
|
93
|
+
let prevRef;
|
|
93
94
|
this.refdSourceController = realdom.realtime( this.host ).attr( $config.attr.importscontext, ( record, { signal } ) => {
|
|
95
|
+
if (record.value === prevRef) return;
|
|
96
|
+
prevRef = record.value;
|
|
94
97
|
// No importscontext attr set. But we're still watching
|
|
95
98
|
if ( !record.value ) {
|
|
96
99
|
this.contextModules = undefined;
|
|
97
100
|
return update();
|
|
98
101
|
}
|
|
99
102
|
// This superModules contextrequest is automatically aborted by the injected signal below
|
|
100
|
-
const request = { ...this.constructor.createRequest( record.value.trim() ), live: true, signal };
|
|
103
|
+
const request = { ...this.constructor.createRequest( record.value.trim() ), live: true, signal, diff: true };
|
|
101
104
|
this.host.parentNode[ this.configs.CONTEXT_API.api.contexts ].request( request, response => {
|
|
102
105
|
this.contextModules = !( response && Object.getPrototypeOf( response ) ) ? response : getDefs( response );
|
|
103
106
|
update();
|
|
@@ -70,38 +70,36 @@ export default class HTMLModule {
|
|
|
70
70
|
expose( entries, isConnected ) {
|
|
71
71
|
const { window } = env, { webqit: { Observer } } = window;
|
|
72
72
|
let dirty, allFragments = this.defs[ '#' ] || [];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
new HTMLModule( entry, this.host, this.level + 1 );
|
|
81
|
-
} else {
|
|
82
|
-
allFragments.push( entry );
|
|
83
|
-
dirty = true;
|
|
84
|
-
if ( typeof requestIdleCallback === 'function' ) {
|
|
85
|
-
requestIdleCallback( () => {
|
|
86
|
-
this.config.idleCompilers?.forEach( callback => callback.call( this.window, entry ) );
|
|
87
|
-
} );
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
if ( defId ) {
|
|
91
|
-
this.validateDefId( defId );
|
|
92
|
-
Observer.set( this.defs, ( !isTemplate && '#' || '' ) + defId, entry );
|
|
93
|
-
}
|
|
73
|
+
entries.forEach( entry => {
|
|
74
|
+
if ( entry.nodeType !== 1 ) return;
|
|
75
|
+
const isTemplate = entry.matches( this.config.templateSelector );
|
|
76
|
+
const defId = ( entry.getAttribute( isTemplate ? this.config.attr.def : this.config.attr.fragmentdef ) || '' ).trim();
|
|
77
|
+
if ( isConnected ) {
|
|
78
|
+
if ( isTemplate && defId ) {
|
|
79
|
+
new HTMLModule( entry, this.host, this.level + 1 );
|
|
94
80
|
} else {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
81
|
+
allFragments.push( entry );
|
|
82
|
+
dirty = true;
|
|
83
|
+
if ( typeof requestIdleCallback === 'function' ) {
|
|
84
|
+
requestIdleCallback( () => {
|
|
85
|
+
this.config.idleCompilers?.forEach( callback => callback.call( this.window, entry ) );
|
|
86
|
+
} );
|
|
99
87
|
}
|
|
100
|
-
if ( defId ) Observer.deleteProperty( this.defs, ( !isTemplate && '#' || '' ) + defId );
|
|
101
88
|
}
|
|
102
|
-
|
|
103
|
-
|
|
89
|
+
if ( defId ) {
|
|
90
|
+
this.validateDefId( defId );
|
|
91
|
+
Observer.set( this.defs, ( !isTemplate && '#' || '' ) + defId, entry );
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
if ( isTemplate && defId ) { HTMLModule.instance( entry ).dispose(); }
|
|
95
|
+
else {
|
|
96
|
+
allFragments = allFragments.filter( x => x !== entry );
|
|
97
|
+
dirty = true;
|
|
98
|
+
}
|
|
99
|
+
if ( defId ) Observer.deleteProperty( this.defs, ( !isTemplate && '#' || '' ) + defId );
|
|
100
|
+
}
|
|
104
101
|
} );
|
|
102
|
+
if ( dirty ) Observer.set( this.defs, '#', allFragments );
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
/**
|
|
@@ -49,51 +49,61 @@ export default function() {
|
|
|
49
49
|
return anchorNode;
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
priv.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
52
|
+
priv.live = callback => {
|
|
53
|
+
if ( priv.liveImportsRealtime ) throw new Error(`Import element already in live mode.`);
|
|
54
|
+
const parentNode = this.el.isConnected ? this.el.parentNode : priv.anchorNode.parentNode;
|
|
55
|
+
priv.liveImportsRealtime = realdom.realtime( this.el ).attr( configs.HTML_IMPORTS.attr.ref, ( record, { signal } ) => {
|
|
56
|
+
priv.moduleRef = record.value;
|
|
57
|
+
const moduleRef = priv.moduleRef.includes( '#' ) ? priv.moduleRef : `${ priv.moduleRef }#`/* for live children */;
|
|
58
|
+
const request = { ...HTMLImportsContext.createRequest( moduleRef ), live: signal && true, signal, diff: !moduleRef.endsWith('#') };
|
|
59
|
+
parentNode[ configs.CONTEXT_API.api.contexts ].request( request, response => {
|
|
60
|
+
callback( ( response instanceof window.HTMLTemplateElement ? [ ...response.content.children ] : (
|
|
61
|
+
Array.isArray( response ) ? response : response && [ response ]
|
|
62
|
+
) ) || [] );
|
|
63
|
+
} );
|
|
64
|
+
}, { live: true, timing: 'sync', lifecycleSignals: true } );
|
|
65
|
+
priv.autoDestroyRealtime = realdom.realtime( window.document ).track( parentNode, () => {
|
|
66
|
+
priv.die();
|
|
67
|
+
}, { subtree: 'cross-roots', timing: 'sync', generation: 'exits' } );
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
priv.die = () => {
|
|
71
|
+
priv.autoDestroyRealtime?.disconnect();
|
|
72
|
+
priv.liveImportsRealtime?.disconnect();
|
|
73
|
+
priv.liveImportsRealtime = null;
|
|
59
74
|
};
|
|
60
75
|
|
|
61
76
|
priv.hydrate = ( anchorNode, slottedElements ) => {
|
|
62
|
-
// ----------------
|
|
63
|
-
priv.moduleRef = ( this.el.getAttribute( configs.HTML_IMPORTS.attr.ref ) || '' ).trim();
|
|
64
77
|
anchorNode.replaceWith( priv.setAnchorNode( this.createAnchorNode() ) );
|
|
65
|
-
priv.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} );
|
|
70
|
-
} );
|
|
71
|
-
// ----------------
|
|
72
|
-
priv.hydrationImportRequest = new AbortController;
|
|
73
|
-
priv.importRequest( fragments => {
|
|
74
|
-
if ( priv.originalsRemapped ) { return this.fill( fragments ); }
|
|
78
|
+
priv.live( fragments => {
|
|
79
|
+
// The default action
|
|
80
|
+
if ( priv.originalsRemapped ) return this.fill( fragments );
|
|
81
|
+
// Initial remap action
|
|
75
82
|
const identifiersMap = fragments.map( ( fragment, i ) => ( { el: fragment, fragmentDef: fragment.getAttribute( configs.HTML_IMPORTS.attr.fragmentdef ) || '', tagName: fragment.tagName, i } ) );
|
|
76
|
-
|
|
77
|
-
slottedElements.forEach( slottedElement => {
|
|
83
|
+
slottedElements.forEach( ( slottedElement, i ) => {
|
|
78
84
|
const tagName = slottedElement.tagName, fragmentDef = slottedElement.getAttribute( configs.HTML_IMPORTS.attr.fragmentdef ) || '';
|
|
79
85
|
const originalsMatch = ( i ++, identifiersMap.find( fragmentIdentifiers => fragmentIdentifiers.tagName === tagName && fragmentIdentifiers.fragmentDef === fragmentDef && fragmentIdentifiers.i === i ) );
|
|
80
|
-
if (
|
|
81
|
-
_( slottedElement ).set( '
|
|
86
|
+
if ( originalsMatch ) _( slottedElement ).set( 'original@imports', originalsMatch.el ); // Or should we throw integrity error here?
|
|
87
|
+
_( slottedElement ).set( 'slot@imports', this.el );
|
|
88
|
+
priv.slottedElements.add( slottedElement );
|
|
82
89
|
} );
|
|
83
90
|
priv.originalsRemapped = true;
|
|
84
|
-
}
|
|
91
|
+
});
|
|
85
92
|
};
|
|
86
93
|
|
|
87
94
|
priv.autoRestore = ( callback = null ) => {
|
|
88
95
|
priv.autoRestoreRealtime?.disconnect();
|
|
89
96
|
if ( callback ) callback();
|
|
90
97
|
const restore = () => {
|
|
91
|
-
|
|
92
|
-
priv.anchorNode = null;
|
|
98
|
+
if (this.el.isConnected) return;
|
|
93
99
|
this.el.setAttribute( 'data-nodecount', 0 );
|
|
100
|
+
this.el.slottingAction = true;
|
|
101
|
+
priv.anchorNode.replaceWith( this.el );
|
|
102
|
+
this.el.slottingAction = false;
|
|
103
|
+
priv.setAnchorNode( null );
|
|
94
104
|
};
|
|
95
105
|
if ( !priv.slottedElements.size ) return restore();
|
|
96
|
-
const autoRestoreRealtime = realdom.realtime(
|
|
106
|
+
const autoRestoreRealtime = realdom.realtime( priv.anchorNode.parentNode ).observe( [ ...priv.slottedElements ], record => {
|
|
97
107
|
record.exits.forEach( outgoingNode => {
|
|
98
108
|
_( outgoingNode ).delete( 'slot@imports' );
|
|
99
109
|
priv.slottedElements.delete( outgoingNode );
|
|
@@ -109,26 +119,13 @@ export default function() {
|
|
|
109
119
|
};
|
|
110
120
|
|
|
111
121
|
priv.connectedCallback = () => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Totally initialize this instance?
|
|
115
|
-
if ( priv.moduleRefRealtime ) return;
|
|
116
|
-
priv.moduleRefRealtime = realdom.realtime( this.el ).attr( configs.HTML_IMPORTS.attr.ref, ( record, { signal } ) => {
|
|
117
|
-
priv.moduleRef = record.value;
|
|
118
|
-
// Below, we ignore first restore from hydration
|
|
119
|
-
priv.importRequest( fragments => !priv.hydrationImportRequest && this.fill( fragments ), signal );
|
|
120
|
-
}, { live: true, timing: 'sync', lifecycleSignals: true } );
|
|
121
|
-
// Must come after
|
|
122
|
-
priv.hydrationImportRequest?.abort();
|
|
123
|
-
priv.hydrationImportRequest = null;
|
|
122
|
+
if ( this.el.slottingAction ) return;
|
|
123
|
+
priv.live( fragments => this.fill( fragments ) );
|
|
124
124
|
};
|
|
125
125
|
|
|
126
126
|
priv.disconnectedCallback = () => {
|
|
127
|
-
|
|
128
|
-
priv.
|
|
129
|
-
if ( priv.anchorNode?.isConnected ) return;
|
|
130
|
-
priv.moduleRefRealtime?.disconnect();
|
|
131
|
-
priv.moduleRefRealtime = null;
|
|
127
|
+
if ( this.el.slottingAction ) return;
|
|
128
|
+
priv.die();
|
|
132
129
|
};
|
|
133
130
|
}
|
|
134
131
|
|
|
@@ -153,7 +150,11 @@ export default function() {
|
|
|
153
150
|
*
|
|
154
151
|
* @return void
|
|
155
152
|
*/
|
|
156
|
-
fill( slottableElements ) {
|
|
153
|
+
fill( slottableElements, r ) {
|
|
154
|
+
if (!this.el.isConnected && (!this[ '#' ].anchorNode || !this[ '#' ].anchorNode.isConnected)) {
|
|
155
|
+
// LiveImports must be responding to an event that just removed the subtree from DOM
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
157
158
|
if ( Array.isArray( slottableElements ) ) { slottableElements = new Set( slottableElements ) }
|
|
158
159
|
// This state must be set before the diffing below and the serialization done at createAnchorNode()
|
|
159
160
|
this.el.setAttribute( 'data-nodecount', slottableElements.size );
|
|
@@ -172,10 +173,11 @@ export default function() {
|
|
|
172
173
|
} );
|
|
173
174
|
// Make sure anchor node is what's in place...
|
|
174
175
|
// not the import element itslef - but all only when we have slottableElements.size
|
|
175
|
-
if ( slottableElements.size ) {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
if ( slottableElements.size && this.el.isConnected ) {
|
|
177
|
+
const newAnchorNode = this[ '#' ].setAnchorNode( this.createAnchorNode() );
|
|
178
|
+
this.el.slottingAction = true;
|
|
179
|
+
this.el.replaceWith( newAnchorNode );
|
|
180
|
+
this.el.slottingAction = false;
|
|
179
181
|
}
|
|
180
182
|
// Insert slottables now
|
|
181
183
|
slottableElements.forEach( slottableElement => {
|
|
@@ -14,26 +14,26 @@ import { _, _init } from '../util.js';
|
|
|
14
14
|
*
|
|
15
15
|
* @return Void
|
|
16
16
|
*/
|
|
17
|
-
export default function init(
|
|
18
|
-
const { config, window } = _init.call(
|
|
17
|
+
export default function init($config = {}) {
|
|
18
|
+
const { config, window } = _init.call(this, 'html-imports', $config, {
|
|
19
19
|
elements: { import: 'import', },
|
|
20
20
|
attr: { def: 'def', extends: 'extends', inherits: 'inherits', ref: 'ref', importscontext: 'importscontext', },
|
|
21
21
|
api: { def: 'def', defs: 'defs', import: 'import' },
|
|
22
|
-
}
|
|
23
|
-
if (
|
|
24
|
-
config.templateSelector = `template[${
|
|
25
|
-
config.importsContextSelector = `[${
|
|
26
|
-
config.slottedElementsSelector = `[${
|
|
27
|
-
const anchorNodeMatch = (
|
|
28
|
-
const starting = `starts-with(., "${
|
|
29
|
-
const ending = `substring(., string-length(.) - string-length("${
|
|
30
|
-
return `${
|
|
22
|
+
});
|
|
23
|
+
if (!config.attr.fragmentdef) { config.attr.fragmentdef = config.attr.def; }
|
|
24
|
+
config.templateSelector = `template[${window.CSS.escape(config.attr.def)}]`;
|
|
25
|
+
config.importsContextSelector = `[${window.CSS.escape(config.attr.importscontext)}]`;
|
|
26
|
+
config.slottedElementsSelector = `[${window.CSS.escape(config.attr.fragmentdef)}]:not(template)`;
|
|
27
|
+
const anchorNodeMatch = (start, end) => {
|
|
28
|
+
const starting = `starts-with(., "${start}")`;
|
|
29
|
+
const ending = `substring(., string-length(.) - string-length("${end}") + 1) = "${end}"`;
|
|
30
|
+
return `${starting} and ${ending}`;
|
|
31
31
|
}
|
|
32
|
-
config.anchorNodeSelector = `comment()[${
|
|
32
|
+
config.anchorNodeSelector = `comment()[${anchorNodeMatch(`<${config.elements.import}`, `</${config.elements.import}>`)}]`;
|
|
33
33
|
window.webqit.HTMLImportsContext = HTMLImportsContext;
|
|
34
34
|
window.webqit.HTMLImportElement = _HTMLImportElement();
|
|
35
|
-
exposeAPIs.call(
|
|
36
|
-
realtime.call(
|
|
35
|
+
exposeAPIs.call(window, config);
|
|
36
|
+
realtime.call(window, config);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -44,12 +44,12 @@ export default function init( $config = {} ) {
|
|
|
44
44
|
*
|
|
45
45
|
* @return Object
|
|
46
46
|
*/
|
|
47
|
-
export function getDefs(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
export function getDefs(node, autoCreate = true) {
|
|
48
|
+
if (!_(node).has('defs') && autoCreate) {
|
|
49
|
+
const defs = Object.create(null);
|
|
50
|
+
_(node).set('defs', defs);
|
|
51
|
+
}
|
|
52
|
+
return _(node).get('defs');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
@@ -59,44 +59,50 @@ export function getDefs( node, autoCreate = true ) {
|
|
|
59
59
|
*
|
|
60
60
|
* @return Void
|
|
61
61
|
*/
|
|
62
|
-
function exposeAPIs(
|
|
62
|
+
function exposeAPIs(config) {
|
|
63
63
|
const window = this, { webqit: { oohtml: { configs } } } = window;
|
|
64
64
|
// The "def" & "defs" properties
|
|
65
|
-
if (
|
|
66
|
-
if (
|
|
65
|
+
if (config.api.def in window.HTMLTemplateElement.prototype) { throw new Error(`The "HTMLTemplateElement" prototype already has a "${config.api.def}" API!`); }
|
|
66
|
+
if (config.api.defs in window.HTMLTemplateElement.prototype) { throw new Error(`The "HTMLTemplateElement" prototype already has a "${config.api.defs}" API!`); }
|
|
67
67
|
// No-conflict assertions
|
|
68
|
-
Object.defineProperty(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
Object.defineProperty(window.HTMLTemplateElement.prototype, config.api.def, {
|
|
69
|
+
get: function () {
|
|
70
|
+
return this.getAttribute(config.attr.def);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
Object.defineProperty(window.HTMLTemplateElement.prototype, config.api.defs, {
|
|
74
|
+
get: function () {
|
|
75
|
+
return getDefs(this);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
74
78
|
// The "scoped" property
|
|
75
|
-
Object.defineProperty(
|
|
79
|
+
Object.defineProperty(window.HTMLTemplateElement.prototype, 'scoped', {
|
|
76
80
|
configurable: true,
|
|
77
|
-
get() { return this.hasAttribute(
|
|
78
|
-
set(
|
|
79
|
-
}
|
|
81
|
+
get() { return this.hasAttribute('scoped'); },
|
|
82
|
+
set(value) { this.toggleAttribute('scoped', value); },
|
|
83
|
+
});
|
|
80
84
|
// The Import API
|
|
81
|
-
[
|
|
85
|
+
[window.Document.prototype, window.Element.prototype, window.ShadowRoot.prototype].forEach(prototype => {
|
|
82
86
|
// No-conflict assertions
|
|
83
|
-
const type = prototype === window.Document.prototype ? 'Document' : (
|
|
84
|
-
if (
|
|
87
|
+
const type = prototype === window.Document.prototype ? 'Document' : (prototype === window.ShadowRoot.prototype ? 'ShadowRoot' : 'Element');
|
|
88
|
+
if (config.api.import in prototype) { throw new Error(`The ${type} prototype already has a "${config.api.import}" API!`); }
|
|
85
89
|
// Definitions
|
|
86
|
-
Object.defineProperty(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
Object.defineProperty(prototype, config.api.import, {
|
|
91
|
+
value: function (ref, live = false, callback = null) {
|
|
92
|
+
return importRequest(this, ...arguments);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
function importRequest(context, ref, live = false, callback = null) {
|
|
91
97
|
let options = {};
|
|
92
|
-
if (
|
|
98
|
+
if (typeof live === 'function') {
|
|
93
99
|
callback = live;
|
|
94
100
|
live = false;
|
|
95
|
-
} else if (
|
|
101
|
+
} else if (typeof live === 'object' && live) {
|
|
96
102
|
options = { ...live, ...options };
|
|
97
103
|
} else { options = { live }; }
|
|
98
|
-
const request = { ...HTMLImportsContext.createRequest(
|
|
99
|
-
return context[
|
|
104
|
+
const request = { ...HTMLImportsContext.createRequest(ref), ...options };
|
|
105
|
+
return context[configs.CONTEXT_API.api.contexts].request(request, callback);
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
108
|
|
|
@@ -108,83 +114,83 @@ function exposeAPIs( config ) {
|
|
|
108
114
|
*
|
|
109
115
|
* @return Void
|
|
110
116
|
*/
|
|
111
|
-
function realtime(
|
|
117
|
+
function realtime(config) {
|
|
112
118
|
const window = this, { webqit: { Observer, realdom, oohtml: { configs }, HTMLImportElement, HTMLImportsContext } } = window;
|
|
113
|
-
|
|
119
|
+
|
|
114
120
|
// ------------
|
|
115
121
|
// MODULES
|
|
116
122
|
// ------------
|
|
117
123
|
const attachImportsContext = host => {
|
|
118
|
-
const contextsApi = host[
|
|
119
|
-
if (
|
|
120
|
-
contextsApi.attach(
|
|
124
|
+
const contextsApi = host[configs.CONTEXT_API.api.contexts];
|
|
125
|
+
if (!contextsApi.find(HTMLImportsContext.kind)) {
|
|
126
|
+
contextsApi.attach(new HTMLImportsContext);
|
|
121
127
|
}
|
|
122
128
|
};
|
|
123
129
|
const detachImportsContext = host => {
|
|
124
|
-
const contextsApi = host[
|
|
125
|
-
const ctx = contextsApi.find(
|
|
126
|
-
if (
|
|
127
|
-
!host.matches?.(
|
|
128
|
-
)
|
|
129
|
-
contextsApi.detach(
|
|
130
|
+
const contextsApi = host[configs.CONTEXT_API.api.contexts];
|
|
131
|
+
const ctx = contextsApi.find(HTMLImportsContext.kind);
|
|
132
|
+
if (ctx && ( /* disconnect? */!host.isConnected || /* not inheriting && no localModules? */(
|
|
133
|
+
!host.matches?.(config.importsContextSelector) && !Object.keys(ctx.localModules).length
|
|
134
|
+
))) {
|
|
135
|
+
contextsApi.detach(ctx);
|
|
130
136
|
}
|
|
131
137
|
};
|
|
132
138
|
// ------------
|
|
133
|
-
realdom.realtime(
|
|
134
|
-
record.entrants.forEach(
|
|
135
|
-
if (
|
|
136
|
-
const htmlModule = HTMLModule.instance(
|
|
139
|
+
realdom.realtime(window.document).query([config.templateSelector, config.importsContextSelector], record => {
|
|
140
|
+
record.entrants.forEach(entry => {
|
|
141
|
+
if (entry.matches(config.templateSelector)) {
|
|
142
|
+
const htmlModule = HTMLModule.instance(entry);
|
|
137
143
|
htmlModule.ownerContext = entry.scoped ? entry.parentNode : entry.getRootNode();
|
|
138
|
-
const ownerContextModulesObj = getDefs(
|
|
139
|
-
if (
|
|
144
|
+
const ownerContextModulesObj = getDefs(htmlModule.ownerContext);
|
|
145
|
+
if (htmlModule.defId) { Observer.set(ownerContextModulesObj, htmlModule.defId, entry); }
|
|
140
146
|
// The ownerContext's defs - ownerContextModulesObj - has to be populated
|
|
141
147
|
// Before attaching a context instance to it, to give the just created context something to use for
|
|
142
148
|
// fullfiling reclaimed requests.
|
|
143
|
-
attachImportsContext(
|
|
149
|
+
attachImportsContext(htmlModule.ownerContext);
|
|
144
150
|
} else {
|
|
145
|
-
attachImportsContext(
|
|
151
|
+
attachImportsContext(entry);
|
|
146
152
|
}
|
|
147
|
-
}
|
|
148
|
-
record.exits.forEach(
|
|
149
|
-
if (
|
|
150
|
-
const htmlModule = HTMLModule.instance(
|
|
151
|
-
const ownerContextModulesObj = getDefs(
|
|
152
|
-
if (
|
|
153
|
-
detachImportsContext(
|
|
153
|
+
});
|
|
154
|
+
record.exits.forEach(entry => {
|
|
155
|
+
if (entry.matches(config.templateSelector)) {
|
|
156
|
+
const htmlModule = HTMLModule.instance(entry);
|
|
157
|
+
const ownerContextModulesObj = getDefs(htmlModule.ownerContext);
|
|
158
|
+
if (htmlModule.defId && htmlModule.ownerContext.isConnected) { Observer.deleteProperty(ownerContextModulesObj, htmlModule.defId); }
|
|
159
|
+
detachImportsContext(htmlModule.ownerContext);
|
|
154
160
|
} else {
|
|
155
|
-
detachImportsContext(
|
|
161
|
+
detachImportsContext(entry);
|
|
156
162
|
}
|
|
157
|
-
}
|
|
158
|
-
}, { live: true, subtree: 'cross-roots', timing: 'sync', staticSensitivity: true, eventDetails: true }
|
|
159
|
-
|
|
163
|
+
});
|
|
164
|
+
}, { live: true, subtree: 'cross-roots', timing: 'sync', staticSensitivity: true, eventDetails: true });
|
|
165
|
+
|
|
160
166
|
// ------------
|
|
161
167
|
// IMPORTS
|
|
162
168
|
// ------------
|
|
163
|
-
realdom.realtime(
|
|
164
|
-
record.entrants.forEach(
|
|
165
|
-
record.exits.forEach(
|
|
166
|
-
}, { live: true, subtree: 'cross-roots', timing: 'sync' }
|
|
167
|
-
function handleRealtime(
|
|
168
|
-
const elInstance = HTMLImportElement.instance(
|
|
169
|
-
if (
|
|
170
|
-
else { elInstance[
|
|
169
|
+
realdom.realtime(window.document).query(config.elements.import, record => {
|
|
170
|
+
record.entrants.forEach(node => handleRealtime(node, true, record));
|
|
171
|
+
record.exits.forEach(node => handleRealtime(node, false, record));
|
|
172
|
+
}, { live: true, subtree: 'cross-roots', timing: 'sync' });
|
|
173
|
+
function handleRealtime(entry, connectedState) {
|
|
174
|
+
const elInstance = HTMLImportElement.instance(entry);
|
|
175
|
+
if (connectedState) { elInstance['#'].connectedCallback(); }
|
|
176
|
+
else { elInstance['#'].disconnectedCallback(); }
|
|
171
177
|
}
|
|
172
178
|
// Hydration
|
|
173
|
-
if (
|
|
174
|
-
realdom.realtime(
|
|
175
|
-
record.entrants.forEach(
|
|
176
|
-
if (
|
|
177
|
-
const reviver = window.document.createElement(
|
|
179
|
+
if (window.webqit.env === 'server') return;
|
|
180
|
+
realdom.realtime(window.document).query(`(${config.anchorNodeSelector})`, record => {
|
|
181
|
+
record.entrants.forEach(anchorNode => {
|
|
182
|
+
if (_(anchorNode).get('isAnchorNode')) return; // Doubling up on the early return above! Ignoring every just created anchorNode
|
|
183
|
+
const reviver = window.document.createElement('div');
|
|
178
184
|
reviver.innerHTML = anchorNode.nodeValue;
|
|
179
185
|
reviver.innerHTML = reviver.firstChild.textContent;
|
|
180
186
|
const importEl = reviver.firstChild;
|
|
181
|
-
let nodecount = parseInt(
|
|
187
|
+
let nodecount = parseInt(importEl.getAttribute('data-nodecount'));
|
|
182
188
|
const slottedElements = new Set;
|
|
183
189
|
let slottedElement = anchorNode;
|
|
184
|
-
while (
|
|
185
|
-
slottedElements.add(
|
|
190
|
+
while ((slottedElement = slottedElement.previousElementSibling) && slottedElement.matches(config.slottedElementsSelector) && nodecount--) {
|
|
191
|
+
slottedElements.add(slottedElement);
|
|
186
192
|
}
|
|
187
|
-
HTMLImportElement.instance(
|
|
188
|
-
}
|
|
189
|
-
}, { live: true, subtree: 'cross-roots', timing: 'sync' }
|
|
193
|
+
HTMLImportElement.instance(importEl)['#'].hydrate(anchorNode, slottedElements);
|
|
194
|
+
});
|
|
195
|
+
}, { live: true, subtree: 'cross-roots', timing: 'sync' });
|
|
190
196
|
}
|
package/test/imports.test.js
CHANGED
|
@@ -15,7 +15,7 @@ describe(`HTML Imports`, function() {
|
|
|
15
15
|
<p>Hellort</p>
|
|
16
16
|
</template>`;
|
|
17
17
|
const body = `
|
|
18
|
-
<import ref="temp0"></import>`;
|
|
18
|
+
<import ref="temp0#"></import>`;
|
|
19
19
|
const { document } = createDocument( head, body );
|
|
20
20
|
|
|
21
21
|
it ( `The document object and <template> elements should expose an "import" property`, async function() {
|
|
@@ -31,6 +31,7 @@ describe(`HTML Imports`, function() {
|
|
|
31
31
|
const templateEl = document.querySelector( 'template' );
|
|
32
32
|
let added = document.createElement( 'div' );
|
|
33
33
|
templateEl.content.appendChild( added );
|
|
34
|
+
console.log('\n\n\n\n', document.body.outerHTML);
|
|
34
35
|
expect( document.body.children ).to.have.length( 3 );
|
|
35
36
|
} );
|
|
36
37
|
} );
|