@webqit/oohtml 3.1.7 → 3.1.9
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/README.md +144 -44
- package/dist/bindings-api.js +1 -1
- package/dist/bindings-api.js.map +3 -3
- package/dist/context-api.js +1 -1
- package/dist/context-api.js.map +3 -3
- package/dist/data-binding.js +7 -6
- package/dist/data-binding.js.map +3 -3
- package/dist/html-imports.js +1 -1
- package/dist/html-imports.js.map +3 -3
- package/dist/main.js +16 -15
- package/dist/main.js.map +3 -3
- package/dist/main.lite.js +10 -9
- package/dist/main.lite.js.map +3 -3
- package/dist/namespaced-html.js +1 -1
- package/dist/namespaced-html.js.map +3 -3
- package/dist/scoped-css.js +3 -3
- package/dist/scoped-css.js.map +3 -3
- package/dist/scoped-js.js +1 -1
- package/dist/scoped-js.js.map +3 -3
- package/package.json +3 -3
- package/src/bindings-api/index.js +4 -4
- package/src/context-api/DOMContext.js +1 -1
- package/src/data-binding/index.js +26 -21
- package/src/html-imports/HTMLImportsContext.js +3 -3
- package/src/html-imports/HTMLModule.js +20 -20
- package/src/html-imports/_HTMLImportElement.js +10 -10
- package/src/html-imports/index.js +26 -27
- package/src/namespaced-html/index.js +15 -17
- package/src/util.js +22 -4
- package/test/imports.test.js +31 -31
- package/test/index.js +5 -0
- package/test/modules.test.js +33 -33
- package/test/namespace-api.test.js +1 -2
|
@@ -12,7 +12,7 @@ import { _, _init } from '../util.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export default function init( $config = {} ) {
|
|
14
14
|
const { config, window } = _init.call( this, 'bindings-api', $config, {
|
|
15
|
-
|
|
15
|
+
attr: { bindingsreflection: 'bindings' },
|
|
16
16
|
api: { bind: 'bind', bindings: 'bindings', },
|
|
17
17
|
} );
|
|
18
18
|
window.webqit.DOMBindingsContext = DOMBindingsContext;
|
|
@@ -34,11 +34,11 @@ function getBindings( config, node ) {
|
|
|
34
34
|
// Reflection
|
|
35
35
|
const props = Object.keys( bindingsObj );
|
|
36
36
|
const targetNode = node === window.document ? window.document.documentElement : node;
|
|
37
|
-
const bindingsReflection = config.
|
|
37
|
+
const bindingsReflection = config.attr.bindingsreflection;
|
|
38
38
|
if ( props.length && bindingsReflection ) {
|
|
39
|
-
targetNode.setAttribute( config.
|
|
39
|
+
targetNode.setAttribute( config.attr.bindingsreflection, props.join( ' ') );
|
|
40
40
|
} else if ( bindingsReflection ) {
|
|
41
|
-
targetNode.toggleAttribute( config.
|
|
41
|
+
targetNode.toggleAttribute( config.attr.bindingsreflection, false );
|
|
42
42
|
}
|
|
43
43
|
// Re: DOMBindingsContext
|
|
44
44
|
const contextsApi = node[ ctxConfig.api.contexts ];
|
|
@@ -101,7 +101,7 @@ export default class DOMContext {
|
|
|
101
101
|
host.addEventListener( 'contextrequest', this );
|
|
102
102
|
host.addEventListener( 'contextclaim', this );
|
|
103
103
|
const { value: claims } = DOMContexts.instance( host ).request( 'contextclaim', { kind: this.constructor.kind, detail: this } );
|
|
104
|
-
claims
|
|
104
|
+
claims?.forEach( subscriptionEvent => {
|
|
105
105
|
this.subscriptions.add( subscriptionEvent );
|
|
106
106
|
this.subscribed( subscriptionEvent );
|
|
107
107
|
this.handle( subscriptionEvent );
|
|
@@ -12,11 +12,9 @@ import { _, _init } from '../util.js';
|
|
|
12
12
|
* @return Void
|
|
13
13
|
*/
|
|
14
14
|
export default function init( $config = {} ) {
|
|
15
|
-
const { config, window } = _init.call( this, '
|
|
15
|
+
const { config, window } = _init.call( this, 'data-binding', $config, {
|
|
16
16
|
attr: { expr: 'expr', itemIndex: 'data-key' },
|
|
17
17
|
tokens: { nodeType: 'processing-instruction', tagStart: '?{', tagEnd: '}?', stateStart: '; [=', stateEnd: ']' },
|
|
18
|
-
staticsensitivity: true,
|
|
19
|
-
isomorphic: true,
|
|
20
18
|
} );
|
|
21
19
|
( { CONTEXT_API: config.CONTEXT_API, BINDINGS_API: config.BINDINGS_API, HTML_IMPORTS: config.HTML_IMPORTS } = window.webqit.oohtml.configs );
|
|
22
20
|
config.attrSelector = `[${ window.CSS.escape( config.attr.expr ) }]`;
|
|
@@ -46,13 +44,19 @@ function realtime( config ) {
|
|
|
46
44
|
realdom.realtime( window.document ).subtree( config.attrSelector, record => {
|
|
47
45
|
cleanup.call( this, ...record.exits );
|
|
48
46
|
mountInlineBindings.call( this, config, ...record.entrants );
|
|
49
|
-
}, { live: true, timing: 'sync', staticSensitivity:
|
|
47
|
+
}, { live: true, timing: 'sync', staticSensitivity: true } );
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
function createDynamicScope( config, root ) {
|
|
53
|
-
const { webqit: { Observer, DOMBindingsContext } } = this;
|
|
51
|
+
const { webqit: { realdom, Observer, DOMBindingsContext } } = this;
|
|
54
52
|
if ( _( root ).has( 'data-binding' ) ) return _( root ).get( 'data-binding' );
|
|
55
53
|
const scope = Object.create( null ), abortController = new AbortController;
|
|
54
|
+
scope[ '$exec__' ] = ( target, prop, ...args ) => {
|
|
55
|
+
realdom.schedule( 'write', () => target[ prop ]( ...args ) );
|
|
56
|
+
};
|
|
57
|
+
scope[ '$assign__' ] = ( target, prop, val ) => {
|
|
58
|
+
realdom.schedule( 'write', () => (target[ prop ] = val) );
|
|
59
|
+
};
|
|
56
60
|
Observer.intercept( scope, {
|
|
57
61
|
get: ( e, recieved, next ) => {
|
|
58
62
|
if ( !( e.key in scope ) ) {
|
|
@@ -130,10 +134,10 @@ const discreteParseCache = new Map;
|
|
|
130
134
|
function compileDiscreteBindings( config, str ) {
|
|
131
135
|
if ( discreteParseCache.has( str ) ) return discreteParseCache.get( str );
|
|
132
136
|
let source = `let content = ((${ str }) ?? '') + '';`;
|
|
133
|
-
source +=
|
|
134
|
-
source += `if ( this.$oohtml_internal_databinding_anchorNode ) { this.$oohtml_internal_databinding_anchorNode
|
|
135
|
-
const { webqit: {
|
|
136
|
-
const compiled = new
|
|
137
|
+
source += `$assign__(this, 'nodeValue', content);`;
|
|
138
|
+
source += `if ( this.$oohtml_internal_databinding_anchorNode ) { $assign__(this.$oohtml_internal_databinding_anchorNode, 'nodeValue', "${ config.tokens.tagStart }${ escDouble( str ) }${ config.tokens.stateStart }" + content.length + "${ config.tokens.stateEnd } ${ config.tokens.tagEnd }"); }`;
|
|
139
|
+
const { webqit: { QuantumModule } } = this;
|
|
140
|
+
const compiled = new QuantumModule( source );
|
|
137
141
|
discreteParseCache.set( str, compiled );
|
|
138
142
|
return compiled;
|
|
139
143
|
}
|
|
@@ -157,19 +161,19 @@ function compileInlineBindings( config, str ) {
|
|
|
157
161
|
const directive = left[ 0 ], param = left.slice( 1 ).trim();
|
|
158
162
|
const arg = `(${ right })`, $arg = `(${ arg } ?? '')`;
|
|
159
163
|
if ( directive === '&' ) {
|
|
160
|
-
if ( param.startsWith( '--' ) ) return
|
|
161
|
-
return
|
|
164
|
+
if ( param.startsWith( '--' ) ) return `$exec__(this.style, 'setProperty', "${ escDouble( param ) }", ${ $arg });`;
|
|
165
|
+
return `$assign__(this.style, "${ escDouble( param ) }", ${ $arg });`;
|
|
162
166
|
}
|
|
163
|
-
if ( directive === '%' ) return
|
|
167
|
+
if ( directive === '%' ) return `$exec__(this.classList, 'toggle', "${ escDouble( param ) }", !!${ arg });`;
|
|
164
168
|
if ( directive === '~' ) {
|
|
165
|
-
if ( param.startsWith( '?' ) ) return
|
|
166
|
-
return
|
|
169
|
+
if ( param.startsWith( '?' ) ) return `$exec__(this, 'toggleAttribute', "${ escDouble( param.substring( 1 ).trim() ) }", !!${ arg });`;
|
|
170
|
+
return `$exec__(this, 'setAttribute', "${ escDouble( param ) }", ${ $arg });`;
|
|
167
171
|
}
|
|
168
172
|
if ( directive === '@' ) {
|
|
169
173
|
if ( validation[ param ] ) throw new Error( `Duplicate binding: ${ left }.` );
|
|
170
174
|
validation[ param ] = true;
|
|
171
|
-
if ( param === 'text' ) return
|
|
172
|
-
if ( param === 'html' ) return
|
|
175
|
+
if ( param === 'text' ) return `$assign__(this, 'textContent', ${ $arg });`;
|
|
176
|
+
if ( param === 'html' ) return `$exec__(this, 'setHTML', ${ $arg });`;
|
|
173
177
|
if ( param === 'items' ) {
|
|
174
178
|
const [ iterationSpec, importSpec ] = splitOuter( right, '/' );
|
|
175
179
|
if ( !importSpec ) throw new Error( `Invalid ${ directive }items spec: ${ str }; no import specifier.` );
|
|
@@ -182,7 +186,7 @@ function compileInlineBindings( config, str ) {
|
|
|
182
186
|
const indices = kind === 'in' ? production[ 2 ] : ( production[ 1 ] || '$index__' );
|
|
183
187
|
return `
|
|
184
188
|
let $iteratee__ = ${ iteratee };
|
|
185
|
-
let $import__ = this.${ config.HTML_IMPORTS.
|
|
189
|
+
let $import__ = this.${ config.HTML_IMPORTS.api.import }( ${ importSpec.trim() }, true );
|
|
186
190
|
this.$oohtml_internal_databinding_signals?.push( $import__ );
|
|
187
191
|
|
|
188
192
|
if ( $import__.value && $iteratee__ ) {
|
|
@@ -200,13 +204,14 @@ function compileInlineBindings( config, str ) {
|
|
|
200
204
|
let $itemNode__ = $existing__.get( $key___ );
|
|
201
205
|
if ( $itemNode__ ) {
|
|
202
206
|
$existing__.delete( $key___ );
|
|
207
|
+
$exec__($itemNode__, '${ config.BINDINGS_API.api.bind }', $itemBinding__ );
|
|
203
208
|
} else {
|
|
204
209
|
$itemNode__ = ( Array.isArray( $import__.value ) ? $import__.value[ 0 ] : ( $import__.value instanceof window.HTMLTemplateElement ? $import__.value.content.firstElementChild : $import__.value ) ).cloneNode( true );
|
|
205
210
|
$itemNode__.setAttribute( "${ config.attr.itemIndex }", $key___ );
|
|
206
|
-
|
|
211
|
+
$exec__($itemNode__, '${ config.BINDINGS_API.api.bind }', $itemBinding__ );
|
|
212
|
+
$exec__(this, 'appendChild', $itemNode__ );
|
|
207
213
|
}
|
|
208
214
|
|
|
209
|
-
$itemNode__.${ config.BINDINGS_API.api.bind }( $itemBinding__ );
|
|
210
215
|
if ( ${ kind === 'in' ? `!( ${ production[ 0 ] } in $iteratee__ )` : `typeof ${ production[ 0 ] } === 'undefined'` } ) { $itemNode__.remove(); }
|
|
211
216
|
}
|
|
212
217
|
$existing__.forEach( x => x.remove() );
|
|
@@ -216,8 +221,8 @@ function compileInlineBindings( config, str ) {
|
|
|
216
221
|
}
|
|
217
222
|
if ( str.trim() ) throw new Error( `Invalid binding: ${ str }.` );
|
|
218
223
|
} ).join( `\n` );
|
|
219
|
-
const { webqit: {
|
|
220
|
-
const compiled = new
|
|
224
|
+
const { webqit: { QuantumModule } } = this;
|
|
225
|
+
const compiled = new QuantumModule( source );
|
|
221
226
|
inlineParseCache.set( str, compiled );
|
|
222
227
|
return compiled;
|
|
223
228
|
}
|
|
@@ -46,7 +46,7 @@ export default class HTMLImportsContext extends DOMContext {
|
|
|
46
46
|
if ( ( event.detail || '' ).trim() === '/' ) return event.respondWith( this.localModules );
|
|
47
47
|
const $config = this.configs.HTML_IMPORTS;
|
|
48
48
|
let path = ( event.detail || '' ).split( /\/|(?<=\w)(?=#)/g ).map( x => x.trim() ).filter( x => x );
|
|
49
|
-
if ( path.length ) { path = path.join( `/${ $config.
|
|
49
|
+
if ( path.length ) { path = path.join( `/${ $config.api.defs }/` )?.split( '/' ) || []; }
|
|
50
50
|
// No detail?
|
|
51
51
|
if ( !path.length ) return event.respondWith();
|
|
52
52
|
|
|
@@ -89,11 +89,11 @@ export default class HTMLImportsContext extends DOMContext {
|
|
|
89
89
|
};
|
|
90
90
|
// ----------------
|
|
91
91
|
const $config = this.configs.HTML_IMPORTS;
|
|
92
|
-
if ( !this.host.matches || !$config.
|
|
92
|
+
if ( !this.host.matches || !$config.attr.importscontext ) return;
|
|
93
93
|
// Any existing this.refdSourceController? Abort!
|
|
94
94
|
this.refdSourceController?.disconnect();
|
|
95
95
|
const realdom = this.host.ownerDocument.defaultView.webqit.realdom;
|
|
96
|
-
this.refdSourceController = realdom.realtime( this.host ).attr( $config.
|
|
96
|
+
this.refdSourceController = realdom.realtime( this.host ).attr( $config.attr.importscontext, ( record, { signal } ) => {
|
|
97
97
|
// No importscontext attr set. But we're still watching
|
|
98
98
|
if ( !record.value ) {
|
|
99
99
|
this.contextModules = undefined;
|
|
@@ -12,7 +12,7 @@ export default class HTMLModule {
|
|
|
12
12
|
* @instance
|
|
13
13
|
*/
|
|
14
14
|
static instance( host ) {
|
|
15
|
-
return _( host ).get( '
|
|
15
|
+
return _( host ).get( 'defsmanager::instance' ) || new this( host );
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -20,14 +20,14 @@ export default class HTMLModule {
|
|
|
20
20
|
*/
|
|
21
21
|
constructor( host, parent = null, level = 0 ) {
|
|
22
22
|
const { window } = env, { webqit: { realdom, oohtml: { configs } } } = window;
|
|
23
|
-
_( host ).get( `
|
|
24
|
-
_( host ).set( `
|
|
23
|
+
_( host ).get( `defsmanager::instance` )?.dispose();
|
|
24
|
+
_( host ).set( `defsmanager::instance`, this );
|
|
25
25
|
this.host = host;
|
|
26
26
|
this.config = configs.HTML_IMPORTS;
|
|
27
27
|
this.parent = parent;
|
|
28
28
|
this.level = level;
|
|
29
|
-
this.
|
|
30
|
-
this.defId = ( this.host.getAttribute( this.config.
|
|
29
|
+
this.defs = getExports( this.host );
|
|
30
|
+
this.defId = ( this.host.getAttribute( this.config.attr.def ) || '' ).trim();
|
|
31
31
|
this.validateDefId( this.defId );
|
|
32
32
|
// ----------
|
|
33
33
|
this.realtimeA = realdom.realtime( this.host.content ).children( record => {
|
|
@@ -60,7 +60,7 @@ export default class HTMLModule {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* Maps module contents as
|
|
63
|
+
* Maps module contents as defs.
|
|
64
64
|
*
|
|
65
65
|
* @param Array entries
|
|
66
66
|
* @param Bool isConnected
|
|
@@ -69,12 +69,12 @@ export default class HTMLModule {
|
|
|
69
69
|
*/
|
|
70
70
|
export( entries, isConnected ) {
|
|
71
71
|
const { window } = env, { webqit: { Observer } } = window;
|
|
72
|
-
let dirty, allFragments = this.
|
|
73
|
-
Observer.batch( this.
|
|
72
|
+
let dirty, allFragments = this.defs[ '#' ] || [];
|
|
73
|
+
Observer.batch( this.defs, () => {
|
|
74
74
|
entries.forEach( entry => {
|
|
75
75
|
if ( entry.nodeType !== 1 ) return;
|
|
76
76
|
const isTemplate = entry.matches( this.config.templateSelector );
|
|
77
|
-
const defId = ( entry.getAttribute( isTemplate ? this.config.
|
|
77
|
+
const defId = ( entry.getAttribute( isTemplate ? this.config.attr.def : this.config.attr.fragmentdef ) || '' ).trim();
|
|
78
78
|
if ( isConnected ) {
|
|
79
79
|
if ( isTemplate && defId ) { new HTMLModule( entry, this.host, this.level + 1 ); }
|
|
80
80
|
else {
|
|
@@ -83,7 +83,7 @@ export default class HTMLModule {
|
|
|
83
83
|
}
|
|
84
84
|
if ( defId ) {
|
|
85
85
|
this.validateDefId( defId );
|
|
86
|
-
Observer.set( this.
|
|
86
|
+
Observer.set( this.defs, ( !isTemplate && '#' || '' ) + defId, entry );
|
|
87
87
|
}
|
|
88
88
|
} else {
|
|
89
89
|
if ( isTemplate && defId ) { HTMLModule.instance( entry ).dispose(); }
|
|
@@ -91,10 +91,10 @@ export default class HTMLModule {
|
|
|
91
91
|
allFragments = allFragments.filter( x => x !== entry );
|
|
92
92
|
dirty = true;
|
|
93
93
|
}
|
|
94
|
-
if ( defId ) Observer.deleteProperty( this.
|
|
94
|
+
if ( defId ) Observer.deleteProperty( this.defs, ( !isTemplate && '#' || '' ) + defId );
|
|
95
95
|
}
|
|
96
96
|
} );
|
|
97
|
-
if ( dirty ) Observer.set( this.
|
|
97
|
+
if ( dirty ) Observer.set( this.defs, '#', allFragments );
|
|
98
98
|
} );
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -114,7 +114,7 @@ export default class HTMLModule {
|
|
|
114
114
|
$loadingPromise = promise.then( () => interception.remove() ); // Set
|
|
115
115
|
};
|
|
116
116
|
const loading = ( record2.value || '' ).trim();
|
|
117
|
-
const interception = Observer.intercept( this.
|
|
117
|
+
const interception = Observer.intercept( this.defs, 'get', async ( descriptor, recieved, next ) => {
|
|
118
118
|
if ( loading === 'lazy' ) { loadingPromise( this.load( src, true ) ); }
|
|
119
119
|
await loadingPromise();
|
|
120
120
|
return next();
|
|
@@ -162,22 +162,22 @@ export default class HTMLModule {
|
|
|
162
162
|
evalInheritance( ) {
|
|
163
163
|
if ( !this.parent ) return [];
|
|
164
164
|
const { window: { webqit: { Observer } } } = env;
|
|
165
|
-
let extendedId = ( this.host.getAttribute( this.config.
|
|
166
|
-
let inheritedIds = ( this.host.getAttribute( this.config.
|
|
165
|
+
let extendedId = ( this.host.getAttribute( this.config.attr.extends ) || '' ).trim();
|
|
166
|
+
let inheritedIds = ( this.host.getAttribute( this.config.attr.inherits ) || '' ).trim();
|
|
167
167
|
const handleInherited = records => {
|
|
168
168
|
records.forEach( record => {
|
|
169
|
-
if ( Observer.get( this.
|
|
169
|
+
if ( Observer.get( this.defs, record.key ) !== record.oldValue ) return;
|
|
170
170
|
if ( [ 'get'/*initial get*/, 'set', 'def' ].includes( record.type ) ) {
|
|
171
|
-
Observer[ record.type.replace( 'get', 'set' ) ]( this.
|
|
171
|
+
Observer[ record.type.replace( 'get', 'set' ) ]( this.defs, record.key, record.value );
|
|
172
172
|
} else if ( record.type === 'delete' ) {
|
|
173
|
-
Observer.deleteProperty( this.
|
|
173
|
+
Observer.deleteProperty( this.defs, record.key );
|
|
174
174
|
}
|
|
175
175
|
} );
|
|
176
176
|
};
|
|
177
177
|
const realtimes = [];
|
|
178
178
|
const parentExportsObj = getExports( this.parent );
|
|
179
179
|
if ( extendedId ) {
|
|
180
|
-
realtimes.push( Observer.reduce( parentExportsObj, [ extendedId, this.config.
|
|
180
|
+
realtimes.push( Observer.reduce( parentExportsObj, [ extendedId, this.config.api.defs, Infinity ], Observer.get, handleInherited, { live: true } ) );
|
|
181
181
|
}
|
|
182
182
|
if ( ( inheritedIds = inheritedIds.split( ' ' ).map( id => id.trim() ).filter( x => x ) ).length ) {
|
|
183
183
|
realtimes.push( Observer.get( parentExportsObj, inheritedIds, handleInherited, { live: true } ) );
|
|
@@ -194,7 +194,7 @@ export default class HTMLModule {
|
|
|
194
194
|
this.realtimeA.disconnect();
|
|
195
195
|
this.realtimeB.disconnect();
|
|
196
196
|
this.realtimeC.forEach( r => ( r instanceof Promise ? r.then( r => r.abort() ) : r.abort() ) );
|
|
197
|
-
Object.entries( this.
|
|
197
|
+
Object.entries( this.defs ).forEach( ( [ key, entry ] ) => {
|
|
198
198
|
if ( key.startsWith( '#' ) ) return;
|
|
199
199
|
HTMLModule.instance( entry ).dispose();
|
|
200
200
|
} );
|
|
@@ -13,9 +13,9 @@ import { _, env } from '../util.js';
|
|
|
13
13
|
* @return HTMLImportElement
|
|
14
14
|
*/
|
|
15
15
|
export default function() {
|
|
16
|
-
const { window } = env, { webqit } = window, {
|
|
16
|
+
const { window } = env, { webqit } = window, { realdom, oohtml: { configs } } = webqit;
|
|
17
17
|
if ( webqit.HTMLImportElement ) return webqit.HTMLImportElement;
|
|
18
|
-
const BaseElement = configs.HTML_IMPORTS.import.
|
|
18
|
+
const BaseElement = configs.HTML_IMPORTS.elements.import.includes( '-' ) ? window.HTMLElement : class {};
|
|
19
19
|
class HTMLImportElement extends BaseElement {
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -26,7 +26,7 @@ export default function() {
|
|
|
26
26
|
* @returns
|
|
27
27
|
*/
|
|
28
28
|
static instance( node ) {
|
|
29
|
-
if ( configs.HTML_IMPORTS.import.
|
|
29
|
+
if ( configs.HTML_IMPORTS.elements.import.includes( '-' ) && ( node instanceof this ) ) return node;
|
|
30
30
|
return _( node ).get( 'import::instance' ) || new this( node );
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -60,7 +60,7 @@ export default function() {
|
|
|
60
60
|
|
|
61
61
|
priv.hydrate = ( anchorNode, slottedElements ) => {
|
|
62
62
|
// ----------------
|
|
63
|
-
priv.moduleRef = ( this.el.getAttribute( configs.HTML_IMPORTS.
|
|
63
|
+
priv.moduleRef = ( this.el.getAttribute( configs.HTML_IMPORTS.attr.ref ) || '' ).trim();
|
|
64
64
|
anchorNode.replaceWith( priv.setAnchorNode( this.createAnchorNode() ) );
|
|
65
65
|
priv.autoRestore( () => {
|
|
66
66
|
slottedElements.forEach( slottedElement => {
|
|
@@ -72,10 +72,10 @@ export default function() {
|
|
|
72
72
|
priv.hydrationImportRequest = new AbortController;
|
|
73
73
|
priv.importRequest( fragments => {
|
|
74
74
|
if ( priv.originalsRemapped ) { return this.fill( fragments ); }
|
|
75
|
-
const identifiersMap = fragments.map( ( fragment, i ) => ( { el: fragment, fragmentDef: fragment.getAttribute( configs.HTML_IMPORTS.
|
|
75
|
+
const identifiersMap = fragments.map( ( fragment, i ) => ( { el: fragment, fragmentDef: fragment.getAttribute( configs.HTML_IMPORTS.attr.fragmentdef ) || '', tagName: fragment.tagName, i } ) );
|
|
76
76
|
let i = -1;
|
|
77
77
|
slottedElements.forEach( slottedElement => {
|
|
78
|
-
const tagName = slottedElement.tagName, fragmentDef = slottedElement.getAttribute( configs.HTML_IMPORTS.
|
|
78
|
+
const tagName = slottedElement.tagName, fragmentDef = slottedElement.getAttribute( configs.HTML_IMPORTS.attr.fragmentdef ) || '';
|
|
79
79
|
const originalsMatch = ( i ++, identifiersMap.find( fragmentIdentifiers => fragmentIdentifiers.tagName === tagName && fragmentIdentifiers.fragmentDef === fragmentDef && fragmentIdentifiers.i === i ) );
|
|
80
80
|
if ( !originalsMatch ) return; // Or should we throw integrity error?
|
|
81
81
|
_( slottedElement ).set( 'original@imports', originalsMatch.el );
|
|
@@ -113,7 +113,7 @@ export default function() {
|
|
|
113
113
|
if ( priv.slottedElements.size ) throw new Error( `Illegal reinsertion into the DOM; import slot is not empty!` );
|
|
114
114
|
// Totally initialize this instance?
|
|
115
115
|
if ( priv.moduleRefRealtime ) return;
|
|
116
|
-
priv.moduleRefRealtime = realdom.realtime( this.el ).attr( configs.HTML_IMPORTS.
|
|
116
|
+
priv.moduleRefRealtime = realdom.realtime( this.el ).attr( configs.HTML_IMPORTS.attr.ref, ( record, { signal } ) => {
|
|
117
117
|
priv.moduleRef = record.value;
|
|
118
118
|
// Below, we ignore first restore from hydration
|
|
119
119
|
priv.importRequest( fragments => !priv.hydrationImportRequest && this.fill( fragments ), signal );
|
|
@@ -182,8 +182,8 @@ export default function() {
|
|
|
182
182
|
// Clone each slottable element and give it a reference to its original
|
|
183
183
|
const slottableElementClone = slottableElement.cloneNode( true );
|
|
184
184
|
// The folllowing references must be set before adding to DODM
|
|
185
|
-
if ( !slottableElementClone.hasAttribute( configs.HTML_IMPORTS.
|
|
186
|
-
slottableElementClone.toggleAttribute( configs.HTML_IMPORTS.
|
|
185
|
+
if ( !slottableElementClone.hasAttribute( configs.HTML_IMPORTS.attr.fragmentdef ) ) {
|
|
186
|
+
slottableElementClone.toggleAttribute( configs.HTML_IMPORTS.attr.fragmentdef, true );
|
|
187
187
|
}
|
|
188
188
|
_( slottableElementClone ).set( 'original@imports', slottableElement );
|
|
189
189
|
_( slottableElementClone ).set( 'slot@imports', this.el );
|
|
@@ -221,7 +221,7 @@ export default function() {
|
|
|
221
221
|
*/
|
|
222
222
|
get slottedElements() { return this[ '#' ].slottedElements; }
|
|
223
223
|
}
|
|
224
|
-
if ( configs.HTML_IMPORTS.import.
|
|
224
|
+
if ( configs.HTML_IMPORTS.elements.import.includes( '-' ) ) { customElements.define( configs.HTML_IMPORTS.elements.import, HTMLImportElement ); }
|
|
225
225
|
webqit.HTMLImportElement = HTMLImportElement;
|
|
226
226
|
return HTMLImportElement;
|
|
227
227
|
}
|
|
@@ -15,22 +15,21 @@ import { _, _init } from '../util.js';
|
|
|
15
15
|
* @return Void
|
|
16
16
|
*/
|
|
17
17
|
export default function init( $config = {} ) {
|
|
18
|
-
const { config,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
staticsensitivity: true,
|
|
23
|
-
isomorphic: true,
|
|
18
|
+
const { config, window } = _init.call( this, 'html-imports', $config, {
|
|
19
|
+
elements: { import: 'import', },
|
|
20
|
+
attr: { def: 'def', extends: 'extends', inherits: 'inherits', ref: 'ref', importscontext: 'importscontext', },
|
|
21
|
+
api: { def: 'def', defs: 'defs', import: 'import' },
|
|
24
22
|
} );
|
|
25
|
-
config.
|
|
26
|
-
config.
|
|
27
|
-
config.
|
|
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)`;
|
|
28
27
|
const anchorNodeMatch = ( start, end ) => {
|
|
29
28
|
const starting = `starts-with(., "${ start }")`;
|
|
30
29
|
const ending = `substring(., string-length(.) - string-length("${ end }") + 1) = "${ end }"`;
|
|
31
30
|
return `${ starting } and ${ ending }`;
|
|
32
31
|
}
|
|
33
|
-
config.anchorNodeSelector = `comment()[${ anchorNodeMatch( `<${ config.import
|
|
32
|
+
config.anchorNodeSelector = `comment()[${ anchorNodeMatch( `<${ config.elements.import }`, `</${ config.elements.import }>` ) }]`;
|
|
34
33
|
window.webqit.HTMLImportsContext = HTMLImportsContext;
|
|
35
34
|
window.webqit.HTMLImportElement = _HTMLImportElement();
|
|
36
35
|
exposeAPIs.call( window, config );
|
|
@@ -38,7 +37,7 @@ export default function init( $config = {} ) {
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
/**
|
|
41
|
-
* Returns the "
|
|
40
|
+
* Returns the "defs" object associated with the given node.
|
|
42
41
|
*
|
|
43
42
|
* @param Element node
|
|
44
43
|
* @param Bool autoCreate
|
|
@@ -46,11 +45,11 @@ export default function init( $config = {} ) {
|
|
|
46
45
|
* @return Object
|
|
47
46
|
*/
|
|
48
47
|
export function getExports( node, autoCreate = true ) {
|
|
49
|
-
if ( !_( node ).has( '
|
|
50
|
-
const
|
|
51
|
-
_( node ).set( '
|
|
48
|
+
if ( !_( node ).has( 'defs' ) && autoCreate ) {
|
|
49
|
+
const defs = Object.create( null );
|
|
50
|
+
_( node ).set( 'defs', defs );
|
|
52
51
|
}
|
|
53
|
-
return _( node ).get( '
|
|
52
|
+
return _( node ).get( 'defs' );
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
/**
|
|
@@ -63,21 +62,21 @@ export function getExports( node, autoCreate = true ) {
|
|
|
63
62
|
function exposeAPIs( config ) {
|
|
64
63
|
const window = this, { webqit: { oohtml: { configs } } } = window;
|
|
65
64
|
// Assertions
|
|
66
|
-
if ( config.
|
|
67
|
-
if ( config.
|
|
68
|
-
if ( config.
|
|
69
|
-
if ( config.
|
|
65
|
+
if ( config.api.defs in window.HTMLTemplateElement.prototype ) { throw new Error( `The "HTMLTemplateElement" class already has a "${ config.api.defs }" property!` ); }
|
|
66
|
+
if ( config.api.def in window.HTMLTemplateElement.prototype ) { throw new Error( `The "HTMLTemplateElement" class already has a "${ config.api.def }" property!` ); }
|
|
67
|
+
if ( config.api.import in window.document ) { throw new Error( `document already has a "${ config.api.import }" property!` ); }
|
|
68
|
+
if ( config.api.import in window.HTMLElement.prototype ) { throw new Error( `The "HTMLElement" class already has a "${ config.api.import }" property!` ); }
|
|
70
69
|
// Definitions
|
|
71
|
-
Object.defineProperty( window.HTMLTemplateElement.prototype, config.
|
|
70
|
+
Object.defineProperty( window.HTMLTemplateElement.prototype, config.api.defs, { get: function() {
|
|
72
71
|
return getExports( this );
|
|
73
72
|
} } );
|
|
74
|
-
Object.defineProperty( window.HTMLTemplateElement.prototype, config.
|
|
75
|
-
return this.getAttribute( config.
|
|
73
|
+
Object.defineProperty( window.HTMLTemplateElement.prototype, config.api.def, { get: function() {
|
|
74
|
+
return this.getAttribute( config.attr.def );
|
|
76
75
|
} } );
|
|
77
|
-
Object.defineProperty( window.document, config.
|
|
76
|
+
Object.defineProperty( window.document, config.api.import, { value: function( ref, live = false, callback = null ) {
|
|
78
77
|
return importRequest( window.document, ...arguments );
|
|
79
78
|
} } );
|
|
80
|
-
Object.defineProperty( window.HTMLElement.prototype, config.
|
|
79
|
+
Object.defineProperty( window.HTMLElement.prototype, config.api.import, { value: function( ref, live = false, callback = null ) {
|
|
81
80
|
return importRequest( this, ...arguments );
|
|
82
81
|
} } );
|
|
83
82
|
function importRequest( context, ref, live = false, callback = null ) {
|
|
@@ -130,7 +129,7 @@ function realtime( config ) {
|
|
|
130
129
|
htmlModule.ownerContext = entry.scoped ? record.target : window.document;
|
|
131
130
|
const ownerContextModulesObj = getExports( htmlModule.ownerContext );
|
|
132
131
|
if ( htmlModule.defId ) { Observer.set( ownerContextModulesObj, htmlModule.defId, entry ); }
|
|
133
|
-
// The ownerContext's
|
|
132
|
+
// The ownerContext's defs - ownerContextModulesObj - has to be populated
|
|
134
133
|
// Before attaching a context instance to it, to give the just created context something to use for
|
|
135
134
|
// fullfiling reclaimed requests.
|
|
136
135
|
attachImportsContext( htmlModule.ownerContext );
|
|
@@ -148,11 +147,11 @@ function realtime( config ) {
|
|
|
148
147
|
detachImportsContext( entry );
|
|
149
148
|
}
|
|
150
149
|
} );
|
|
151
|
-
}, { live: true, timing: 'sync', staticSensitivity:
|
|
150
|
+
}, { live: true, timing: 'sync', staticSensitivity: true } );
|
|
152
151
|
// ------------
|
|
153
152
|
// IMPORTS
|
|
154
153
|
// ------------
|
|
155
|
-
realdom.realtime( window.document ).subtree/*instead of observe(); reason: jsdom timing*/( config.import
|
|
154
|
+
realdom.realtime( window.document ).subtree/*instead of observe(); reason: jsdom timing*/( config.elements.import, record => {
|
|
156
155
|
record.entrants.forEach( node => handleRealtime( node, true, record ) );
|
|
157
156
|
record.exits.forEach( node => handleRealtime( node, false, record ) );
|
|
158
157
|
}, { live: true, timing: 'sync' } );
|
|
@@ -11,14 +11,12 @@ import { _, _init } from '../util.js';
|
|
|
11
11
|
*/
|
|
12
12
|
export default function init( $config = {} ) {
|
|
13
13
|
const { config, window } = _init.call( this, 'namespaced-html', $config, {
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
attr: { namespace: 'namespace', id: 'id', },
|
|
15
|
+
api: { namespace: 'namespace', eagermode: true, },
|
|
16
16
|
target: { attr: ':target', event: ':target', scrolling: true },
|
|
17
|
-
staticsensitivity: true,
|
|
18
|
-
eagermode: true,
|
|
19
17
|
} );
|
|
20
|
-
config.idSelector = `[${ window.CSS.escape( config.id
|
|
21
|
-
config.namespaceSelector = `[${ window.CSS.escape( config.namespace
|
|
18
|
+
config.idSelector = `[${ window.CSS.escape( config.attr.id ) }]`;
|
|
19
|
+
config.namespaceSelector = `[${ window.CSS.escape( config.attr.namespace ) }]`;
|
|
22
20
|
exposeAPIs.call( window, config );
|
|
23
21
|
realtime.call( window, config );
|
|
24
22
|
}
|
|
@@ -35,8 +33,8 @@ function getNamespaceObject( node, config ) {
|
|
|
35
33
|
if ( !_( node ).has( 'namespace' ) ) {
|
|
36
34
|
const namespaceObj = Object.create( null );
|
|
37
35
|
Observer.intercept( namespaceObj, 'get', ( event, receiver, next ) => {
|
|
38
|
-
if ( Observer.has( namespaceObj, event.key ) || !config.eagermode ) return next();
|
|
39
|
-
const selector = `[${ window.CSS.escape( config.id
|
|
36
|
+
if ( Observer.has( namespaceObj, event.key ) || !config.api.eagermode ) return next();
|
|
37
|
+
const selector = `[${ window.CSS.escape( config.attr.id ) }="${ event.key }"]`;
|
|
40
38
|
const resultNode = Array.from( node.querySelectorAll( selector ) ).filter( idNode => {
|
|
41
39
|
const ownerRoot = idNode.parentNode.closest( config.namespaceSelector );
|
|
42
40
|
if ( node === window.document ) {
|
|
@@ -63,13 +61,13 @@ function getNamespaceObject( node, config ) {
|
|
|
63
61
|
function exposeAPIs( config ) {
|
|
64
62
|
const window = this, { webqit: { Observer } } = window;
|
|
65
63
|
// Assertions
|
|
66
|
-
if ( config.namespace
|
|
67
|
-
if ( config.namespace
|
|
64
|
+
if ( config.api.namespace in window.document ) { throw new Error( `document already has a "${ config.api.namespace }" property!` ); }
|
|
65
|
+
if ( config.api.namespace in window.Element.prototype ) { throw new Error( `The "Element" class already has a "${ config.api.namespace }" property!` ); }
|
|
68
66
|
// Definitions
|
|
69
|
-
Object.defineProperty( window.document, config.namespace
|
|
67
|
+
Object.defineProperty( window.document, config.api.namespace, { get: function() {
|
|
70
68
|
return Observer.proxy( getNamespaceObject.call( window, window.document, config ) );
|
|
71
69
|
} });
|
|
72
|
-
Object.defineProperty( window.Element.prototype, config.namespace
|
|
70
|
+
Object.defineProperty( window.Element.prototype, config.api.namespace, { get: function() {
|
|
73
71
|
return Observer.proxy( getNamespaceObject.call( window, this, config ) );
|
|
74
72
|
} } );
|
|
75
73
|
}
|
|
@@ -85,7 +83,7 @@ function realtime( config ) {
|
|
|
85
83
|
const window = this, { webqit: { Observer, realdom } } = window;
|
|
86
84
|
// ----------------
|
|
87
85
|
const handle = ( target, entry, incoming ) => {
|
|
88
|
-
const identifier = entry.getAttribute( config.id
|
|
86
|
+
const identifier = entry.getAttribute( config.attr.id );
|
|
89
87
|
const ownerRoot = target.closest( config.namespaceSelector ) || _( entry ).get( 'ownerNamespace' ) || window.document;
|
|
90
88
|
const namespaceObj = getNamespaceObject.call( window, ownerRoot, config );
|
|
91
89
|
if ( incoming ) {
|
|
@@ -101,10 +99,10 @@ function realtime( config ) {
|
|
|
101
99
|
realdom.realtime( window.document ).subtree/*instead of observe(); reason: jsdom timing*/( config.idSelector, record => {
|
|
102
100
|
record.entrants.forEach( entry => handle( record.target, entry, true ) );
|
|
103
101
|
record.exits.forEach( entry => handle( record.target, entry, false ) );
|
|
104
|
-
}, { live: true, timing: 'sync', staticSensitivity:
|
|
102
|
+
}, { live: true, timing: 'sync', staticSensitivity: true } );
|
|
105
103
|
// ----------------
|
|
106
|
-
if ( config.staticsensitivity ) {
|
|
107
|
-
realdom.realtime( window.document, 'attr' ).observe( config.namespace
|
|
104
|
+
if ( true/*config.staticsensitivity*/ ) {
|
|
105
|
+
realdom.realtime( window.document, 'attr' ).observe( config.attr.namespace, record => {
|
|
108
106
|
const ownerRoot = record.target.parentNode?.closest( config.namespaceSelector ) || _( record.target ).get( 'ownerNamespace' ) || window.document;
|
|
109
107
|
const ownerRootNamespaceObj = getNamespaceObject.call( window, ownerRoot, config );
|
|
110
108
|
const namespaceObj = getNamespaceObject.call( window, record.target, config );
|
|
@@ -126,7 +124,7 @@ function realtime( config ) {
|
|
|
126
124
|
let prevTarget;
|
|
127
125
|
const activateTarget = () => {
|
|
128
126
|
const path = window.location.hash?.substring( 1 ).split( '/' ).map( s => s.trim() ).filter( s => s ) || [];
|
|
129
|
-
const currTarget = path.reduce( ( prev, segment ) => prev && prev[ config.namespace
|
|
127
|
+
const currTarget = path.reduce( ( prev, segment ) => prev && prev[ config.api.namespace ][ segment ], window.document );
|
|
130
128
|
if ( prevTarget && config.target.attr ) { prevTarget.toggleAttribute( config.target.attr, false ); }
|
|
131
129
|
if ( currTarget && currTarget !== window.document ) {
|
|
132
130
|
if ( config.target.attr ) { currTarget.toggleAttribute( config.target.attr, true ); }
|
package/src/util.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import realdomInit from '@webqit/realdom';
|
|
6
6
|
import { _internals } from '@webqit/util/js/index.js';
|
|
7
7
|
import { _merge } from '@webqit/util/obj/index.js';
|
|
8
|
+
import { _toTitle } from '@webqit/util/str/index.js';
|
|
8
9
|
|
|
9
10
|
export const _ = ( ...args ) => _internals( 'oohtml', ...args );
|
|
10
11
|
|
|
@@ -12,14 +13,31 @@ export const env = {};
|
|
|
12
13
|
|
|
13
14
|
export function _init( name, $config, $defaults ) {
|
|
14
15
|
const window = this, realdom = realdomInit.call( window );
|
|
16
|
+
env.window = window;
|
|
17
|
+
if ( !window.webqitConfig ) {
|
|
18
|
+
window.webqitConfig = realdom.meta( 'webqit' ).json();
|
|
19
|
+
}
|
|
15
20
|
window.webqit || ( window.webqit = {} );
|
|
16
21
|
window.webqit.oohtml || ( window.webqit.oohtml = {} );
|
|
17
22
|
window.webqit.oohtml.configs || ( window.webqit.oohtml.configs = {} );
|
|
18
|
-
const configKey = name.toUpperCase().replace( '-', '_' );
|
|
19
|
-
window.webqit.oohtml.configs[ configKey ] || ( window.webqit.oohtml.configs[ configKey ] = {} );
|
|
20
|
-
env.window = window;
|
|
21
23
|
// ---------------------
|
|
22
|
-
|
|
24
|
+
const configKey = name.toUpperCase().replace( '-', '_' );
|
|
25
|
+
if ( !window.webqit.oohtml.configs[ configKey ] ) {
|
|
26
|
+
window.webqit.oohtml.configs[ configKey ] = {};
|
|
27
|
+
const config = window.webqit.oohtml.configs[ configKey ];
|
|
28
|
+
_merge( 2, config, $defaults, $config, realdom.meta( name ).json() );
|
|
29
|
+
if ( window.webqitConfig.prefix ) {
|
|
30
|
+
Object.keys( config ).forEach( main => {
|
|
31
|
+
Object.keys( config[ main ] ).forEach( key => {
|
|
32
|
+
if ( main === 'api' && typeof config[ main ][ key ] === 'string' ) {
|
|
33
|
+
config[ main ][ key ] = `${ window.webqitConfig.prefix }${ _toTitle( config[ main ][ key ] ) }`
|
|
34
|
+
} else if ( [ 'attr', 'elements' ].includes( main ) && config[ main ][ key ]?.startsWith( 'data-' ) === false ) {
|
|
35
|
+
config[ main ][ key ] = `${ window.webqitConfig.prefix }-${ config[ main ][ key ] }`
|
|
36
|
+
}
|
|
37
|
+
} );
|
|
38
|
+
} );
|
|
39
|
+
}
|
|
40
|
+
}
|
|
23
41
|
// ---------------------
|
|
24
42
|
return { config: window.webqit.oohtml.configs[ configKey ], realdom, window };
|
|
25
43
|
}
|