@webqit/oohtml 3.1.8 → 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 +132 -32
- 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 +4 -4
- 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 +13 -13
- package/dist/main.js.map +3 -3
- package/dist/main.lite.js +12 -12
- 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 +2 -2
- package/src/bindings-api/index.js +4 -4
- package/src/data-binding/index.js +3 -5
- 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 ];
|
|
@@ -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,7 +44,7 @@ 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 ) {
|
|
@@ -188,7 +186,7 @@ function compileInlineBindings( config, str ) {
|
|
|
188
186
|
const indices = kind === 'in' ? production[ 2 ] : ( production[ 1 ] || '$index__' );
|
|
189
187
|
return `
|
|
190
188
|
let $iteratee__ = ${ iteratee };
|
|
191
|
-
let $import__ = this.${ config.HTML_IMPORTS.
|
|
189
|
+
let $import__ = this.${ config.HTML_IMPORTS.api.import }( ${ importSpec.trim() }, true );
|
|
192
190
|
this.$oohtml_internal_databinding_signals?.push( $import__ );
|
|
193
191
|
|
|
194
192
|
if ( $import__.value && $iteratee__ ) {
|
|
@@ -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
|
}
|
package/test/imports.test.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
5
|
import { expect } from 'chai';
|
|
6
|
-
import { createDocument, mockRemoteFetch, delay } from './index.js';
|
|
6
|
+
import { createDocument, createDocumentPrefixed, mockRemoteFetch, delay } from './index.js';
|
|
7
7
|
|
|
8
8
|
describe(`HTML Imports`, function() {
|
|
9
9
|
|
|
@@ -38,73 +38,73 @@ describe(`HTML Imports`, function() {
|
|
|
38
38
|
describe( `Dynamic...`, function() {
|
|
39
39
|
|
|
40
40
|
const head = `
|
|
41
|
-
<template def="temp0">
|
|
41
|
+
<template wq-def="temp0">
|
|
42
42
|
<!-- ------- -->
|
|
43
43
|
<p>Hello world Export</p>
|
|
44
44
|
<p>Hellort</p>
|
|
45
|
-
<input def="input" />
|
|
46
|
-
<template def="temp1">
|
|
47
|
-
<textarea def="input"></textarea>
|
|
48
|
-
<template def="temp2">
|
|
49
|
-
<select def="input"></select>
|
|
45
|
+
<input wq-def="input" />
|
|
46
|
+
<template wq-def="temp1">
|
|
47
|
+
<textarea wq-def="input"></textarea>
|
|
48
|
+
<template wq-def="temp2">
|
|
49
|
+
<select wq-def="input"></select>
|
|
50
50
|
</template>
|
|
51
51
|
</template>
|
|
52
52
|
<!-- ------- -->
|
|
53
|
-
<template def="_landing1">
|
|
54
|
-
<div def="main.html">a</div>
|
|
55
|
-
<template def="_landing2">
|
|
56
|
-
<div def="main.html">b</div>
|
|
57
|
-
<template def="_docs">
|
|
58
|
-
<div def="main.html">c</div>
|
|
53
|
+
<template wq-def="_landing1">
|
|
54
|
+
<div wq-def="main.html">a</div>
|
|
55
|
+
<template wq-def="_landing2">
|
|
56
|
+
<div wq-def="main.html">b</div>
|
|
57
|
+
<template wq-def="_docs">
|
|
58
|
+
<div wq-def="main.html">c</div>
|
|
59
59
|
</template>
|
|
60
60
|
</template>
|
|
61
61
|
</template>
|
|
62
62
|
<!-- ------- -->
|
|
63
|
-
<template def="landing1" extends="_landing1">
|
|
64
|
-
<div def="README.md">1</div>
|
|
65
|
-
<template def="landing2" extends="_landing2">
|
|
66
|
-
<div def="README.md">2</div>
|
|
67
|
-
<template def="docs" extends="_docs">
|
|
68
|
-
<div def="README.md">3</div>
|
|
63
|
+
<template wq-def="landing1" wq-extends="_landing1">
|
|
64
|
+
<div wq-def="README.md">1</div>
|
|
65
|
+
<template wq-def="landing2" wq-extends="_landing2">
|
|
66
|
+
<div wq-def="README.md">2</div>
|
|
67
|
+
<template wq-def="docs" wq-extends="_docs">
|
|
68
|
+
<div wq-def="README.md">3</div>
|
|
69
69
|
</template>
|
|
70
70
|
</template>
|
|
71
71
|
</template>
|
|
72
72
|
<!-- ------- -->
|
|
73
73
|
</template>`;
|
|
74
74
|
const body = `
|
|
75
|
-
<import ref="temp0/uuu"></import>`;
|
|
76
|
-
const { document } =
|
|
77
|
-
const importEl = document.querySelector( 'import' );
|
|
75
|
+
<wq-import wq-ref="temp0/uuu"></wq-import>`;
|
|
76
|
+
const { document } = createDocumentPrefixed( 'wq', head, body );
|
|
77
|
+
const importEl = document.querySelector( 'wq-import' );
|
|
78
78
|
|
|
79
79
|
it ( `<import> element should not be resolved: no match for given import ID...`, async function() {
|
|
80
|
-
expect( document.body.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
80
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'WQ-IMPORT' );
|
|
81
81
|
} );
|
|
82
82
|
|
|
83
83
|
it ( `<import> element should be automatically resolved: new import ID is set...`, async function() {
|
|
84
|
-
importEl.setAttribute( 'ref', 'temp0#input' );
|
|
84
|
+
importEl.setAttribute( 'wq-ref', 'temp0#input' );
|
|
85
85
|
expect( document.body.firstElementChild.nodeName ).to.eq( 'INPUT' );
|
|
86
86
|
} );
|
|
87
87
|
|
|
88
88
|
it ( `<import> element should be automatically resolved: new moduleref is set - nested...`, async function() {
|
|
89
|
-
importEl.setAttribute( 'ref', 'temp0/temp1#input' );
|
|
89
|
+
importEl.setAttribute( 'wq-ref', 'temp0/temp1#input' );
|
|
90
90
|
expect( document.body.firstElementChild.nodeName ).to.eq( 'TEXTAREA' );
|
|
91
91
|
} );
|
|
92
92
|
|
|
93
93
|
it ( `<import> element should be automatically resolved: moduleref is unset - should now be inherited from <body>...`, async function() {
|
|
94
|
-
importEl.setAttribute( 'ref', '#input' );
|
|
95
|
-
expect( document.body.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
96
|
-
document.body.setAttribute( 'importscontext', 'temp0/temp1/temp2' );
|
|
94
|
+
importEl.setAttribute( 'wq-ref', '#input' );
|
|
95
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'WQ-IMPORT' );
|
|
96
|
+
document.body.setAttribute( 'wq-importscontext', 'temp0/temp1/temp2' );
|
|
97
97
|
expect( document.body.firstElementChild.nodeName ).to.eq( 'SELECT' );
|
|
98
98
|
} );
|
|
99
99
|
|
|
100
100
|
it ( `<import> element should be automatically resolved: moduleref at <body> is changed...`, async function() {
|
|
101
|
-
document.body.setAttribute( 'importscontext', 'temp0' );
|
|
101
|
+
document.body.setAttribute( 'wq-importscontext', 'temp0' );
|
|
102
102
|
expect( document.body.firstElementChild.nodeName ).to.eq( 'INPUT' );
|
|
103
103
|
} );
|
|
104
104
|
|
|
105
105
|
it ( `<import> element should be automatically RESTORED: slotted element is removed from DOM...`, async function() {
|
|
106
106
|
document.body.querySelector( 'input' ).remove();
|
|
107
|
-
expect( document.body.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
107
|
+
expect( document.body.firstElementChild.nodeName ).to.eq( 'WQ-IMPORT' );
|
|
108
108
|
} );
|
|
109
109
|
|
|
110
110
|
} );
|
|
@@ -170,7 +170,7 @@ describe(`HTML Imports`, function() {
|
|
|
170
170
|
const routingElement = document.body.firstElementChild;
|
|
171
171
|
expect( routingElement.firstElementChild.nodeName ).to.eq( 'TEXTAREA' );
|
|
172
172
|
document.import( 'temp0/temp1', temp1 => {
|
|
173
|
-
const textarea = temp1.
|
|
173
|
+
const textarea = temp1.defs[ '#input' ];
|
|
174
174
|
textarea.remove();
|
|
175
175
|
expect( routingElement.firstElementChild.nodeName ).to.eq( 'IMPORT' );
|
|
176
176
|
temp1.content.prepend( textarea );
|