@webqit/oohtml 5.0.7 → 5.0.8

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/package.json CHANGED
@@ -14,7 +14,7 @@
14
14
  "wicg-proposal"
15
15
  ],
16
16
  "homepage": "https://webqit.io/tooling/oohtml",
17
- "version": "5.0.7",
17
+ "version": "5.0.8",
18
18
  "license": "MIT",
19
19
  "repository": {
20
20
  "type": "git",
@@ -39,12 +39,12 @@
39
39
  "version:next": "npm version prerelease --preid=next"
40
40
  },
41
41
  "dependencies": {
42
- "@webqit/realdom": "^2.1.35",
42
+ "@webqit/realdom": "^2.1.36",
43
43
  "@webqit/use-live": "^0.5.49",
44
44
  "@webqit/util": "^0.8.16"
45
45
  },
46
46
  "devDependencies": {
47
- "@webqit/oohtml-ssr": "^2.2.1",
47
+ "@webqit/oohtml-ssr": "^2.2.4",
48
48
  "chai": "^4.3.4",
49
49
  "coveralls": "^3.1.1",
50
50
  "esbuild": "^0.14.43",
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * @imports
4
4
  */
5
+ import { isElement } from '@webqit/realdom';
5
6
  import DOMBindingsContext from './DOMBindingsContext.js';
6
7
  import { _wq, _init, _splitOuter } from '../util.js';
7
8
 
@@ -10,13 +11,13 @@ import { _wq, _init, _splitOuter } from '../util.js';
10
11
  *
11
12
  * @param Object $config
12
13
  */
13
- export default function init( $config = {} ) {
14
- const { config, window } = _init.call( this, 'bindings-api', $config, {
14
+ export default function init($config = {}) {
15
+ const { config, window } = _init.call(this, 'bindings-api', $config, {
15
16
  attr: { bindingsreflection: 'bindings' },
16
17
  api: { bind: 'bind', bindings: 'bindings', },
17
- } );
18
+ });
18
19
  window.webqit.DOMBindingsContext = DOMBindingsContext;
19
- exposeAPIs.call( window, config );
20
+ exposeAPIs.call(window, config);
20
21
  realtime.call(window, config);
21
22
  }
22
23
 
@@ -26,38 +27,38 @@ export default function init( $config = {} ) {
26
27
  * The internal bindings object
27
28
  * within elements and the document object.
28
29
  */
29
- function getBindings( config, node ) {
30
+ function getBindings(config, node) {
30
31
  const window = this, { webqit: { Observer, oohtml: { configs: { CONTEXT_API: ctxConfig } } } } = window;
31
- if ( !_wq( node ).has( 'bindings' ) ) {
32
- const bindingsObj = Object.create( null );
33
- _wq( node ).set( 'bindings', bindingsObj );
34
- Observer.observe( bindingsObj, mutations => {
35
- if ( node instanceof window.Element ) {
36
- const bindingsParse = parseBindingsAttr( node.getAttribute( config.attr.bindingsreflection ) || '' );
32
+ if (!_wq(node).has('bindings')) {
33
+ const bindingsObj = Object.create(null);
34
+ _wq(node).set('bindings', bindingsObj);
35
+ Observer.observe(bindingsObj, mutations => {
36
+ if (isElement(node)) {
37
+ const bindingsParse = parseBindingsAttr(node.getAttribute(config.attr.bindingsreflection) || '');
37
38
  const bindingsParseBefore = new Map(bindingsParse);
38
- for ( const m of mutations ) {
39
- if ( m.detail?.publish !== false ) {
40
- if ( m.type === 'delete' ) bindingsParse.delete( m.key );
41
- else bindingsParse.set( m.key, undefined );
39
+ for (const m of mutations) {
40
+ if (m.detail?.publish !== false) {
41
+ if (m.type === 'delete') bindingsParse.delete(m.key);
42
+ else bindingsParse.set(m.key, undefined);
42
43
  }
43
44
  }
44
- if ( bindingsParse.size && bindingsParse.size !== bindingsParseBefore.size ) {
45
- node.setAttribute( config.attr.bindingsreflection, `{ ${ [ ...bindingsParse.entries() ].map(([ key, value ]) => value === undefined ? key : `${ key }: ${ value }` ).join( ', ' ) } }` );
46
- } else if ( !bindingsParse.size ) node.toggleAttribute( config.attr.bindingsreflection, false );
45
+ if (bindingsParse.size && bindingsParse.size !== bindingsParseBefore.size) {
46
+ node.setAttribute(config.attr.bindingsreflection, `{ ${[...bindingsParse.entries()].map(([key, value]) => value === undefined ? key : `${key}: ${value}`).join(', ')} }`);
47
+ } else if (!bindingsParse.size) node.toggleAttribute(config.attr.bindingsreflection, false);
47
48
  } else {
48
- const contextsApi = node[ ctxConfig.api.contexts ];
49
- for ( const m of mutations ) {
50
- if ( m.type === 'delete' ) {
51
- const ctx = contextsApi.find( DOMBindingsContext.kind, m.key );
52
- if ( ctx ) contextsApi.detach( ctx );
53
- } else if ( !contextsApi.find( DOMBindingsContext.kind, m.key ) ) {
54
- contextsApi.attach( new DOMBindingsContext( m.key ) );
49
+ const contextsApi = node[ctxConfig.api.contexts];
50
+ for (const m of mutations) {
51
+ if (m.type === 'delete') {
52
+ const ctx = contextsApi.find(DOMBindingsContext.kind, m.key);
53
+ if (ctx) contextsApi.detach(ctx);
54
+ } else if (!contextsApi.find(DOMBindingsContext.kind, m.key)) {
55
+ contextsApi.attach(new DOMBindingsContext(m.key));
55
56
  }
56
57
  }
57
58
  }
58
- } );
59
- }
60
- return _wq( node ).get( 'bindings' );
59
+ });
60
+ }
61
+ return _wq(node).get('bindings');
61
62
  }
62
63
 
63
64
  /**
@@ -67,22 +68,26 @@ function getBindings( config, node ) {
67
68
  *
68
69
  * @return Void
69
70
  */
70
- function exposeAPIs( config ) {
71
- const window = this, { webqit: { Observer } } = window;
71
+ function exposeAPIs(config) {
72
+ const window = this, { webqit: { Observer } } = window;
72
73
  // The Bindings APIs
73
- [ window.Document.prototype, window.Element.prototype, window.ShadowRoot.prototype ].forEach( prototype => {
74
+ [window.Document.prototype, window.Element.prototype, window.ShadowRoot.prototype].forEach(prototype => {
74
75
  // No-conflict assertions
75
- const type = prototype === window.Document.prototype ? 'Document' : ( prototype === window.ShadowRoot.prototype ? 'ShadowRoot' : 'Element' );
76
- if ( config.api.bind in prototype ) { throw new Error( `The ${ type } prototype already has a "${ config.api.bind }" API!` ); }
77
- if ( config.api.bindings in prototype ) { throw new Error( `The ${ type } prototype already has a "${ config.api.bindings }" API!` ); }
76
+ const type = prototype === window.Document.prototype ? 'Document' : (prototype === window.ShadowRoot.prototype ? 'ShadowRoot' : 'Element');
77
+ if (config.api.bind in prototype) { throw new Error(`The ${type} prototype already has a "${config.api.bind}" API!`); }
78
+ if (config.api.bindings in prototype) { throw new Error(`The ${type} prototype already has a "${config.api.bindings}" API!`); }
78
79
  // Definitions
79
- Object.defineProperty( prototype, config.api.bind, { value: function( bindings, options = {} ) {
80
- return applyBindings.call( window, config, this, bindings, options );
81
- } });
82
- Object.defineProperty( prototype, config.api.bindings, { get: function() {
83
- return Observer.proxy( getBindings.call( window, config, this ) );
84
- } } );
85
- } );
80
+ Object.defineProperty(prototype, config.api.bind, {
81
+ value: function (bindings, options = {}) {
82
+ return applyBindings.call(window, config, this, bindings, options);
83
+ }
84
+ });
85
+ Object.defineProperty(prototype, config.api.bindings, {
86
+ get: function () {
87
+ return Observer.proxy(getBindings.call(window, config, this));
88
+ }
89
+ });
90
+ });
86
91
  }
87
92
 
88
93
  /**
@@ -95,15 +100,15 @@ function exposeAPIs( config ) {
95
100
  *
96
101
  * @return Void
97
102
  */
98
- function applyBindings( config, target, bindings, { merge, diff, publish, namespace } = {} ) {
103
+ function applyBindings(config, target, bindings, { merge, diff, publish, namespace } = {}) {
99
104
  const window = this, { webqit: { Observer } } = window;
100
- const bindingsObj = getBindings.call( this, config, target );
105
+ const bindingsObj = getBindings.call(this, config, target);
101
106
  const $params = { diff, namespace, detail: { publish } };
102
- const exitingKeys = merge ? [] : Object.keys( bindingsObj ).filter( key => !( key in bindings ) );
103
- return Observer.batch( bindingsObj, () => {
104
- if ( exitingKeys.length ) { Observer.deleteProperties( bindingsObj, exitingKeys, $params ); }
105
- return Observer.set( bindingsObj, bindings, $params );
106
- }, $params );
107
+ const exitingKeys = merge ? [] : Object.keys(bindingsObj).filter(key => !(key in bindings));
108
+ return Observer.batch(bindingsObj, () => {
109
+ if (exitingKeys.length) { Observer.deleteProperties(bindingsObj, exitingKeys, $params); }
110
+ return Observer.set(bindingsObj, bindings, $params);
111
+ }, $params);
107
112
  }
108
113
 
109
114
  /**
@@ -119,47 +124,47 @@ function realtime(config) {
119
124
  // ------------
120
125
  const attachBindingsContext = (host, key) => {
121
126
  const contextsApi = host[configs.CONTEXT_API.api.contexts];
122
- if ( !contextsApi.find( DOMBindingsContext.kind, key ) ) {
123
- contextsApi.attach( new DOMBindingsContext( key ) );
127
+ if (!contextsApi.find(DOMBindingsContext.kind, key)) {
128
+ contextsApi.attach(new DOMBindingsContext(key));
124
129
  }
125
130
  };
126
131
  const detachBindingsContext = (host, key) => {
127
132
  let ctx, contextsApi = host[configs.CONTEXT_API.api.contexts];
128
- while( ctx = contextsApi.find( DOMBindingsContext.kind, key ) ) contextsApi.detach(ctx);
133
+ while (ctx = contextsApi.find(DOMBindingsContext.kind, key)) contextsApi.detach(ctx);
129
134
  };
130
135
  // ------------
131
- realdom.realtime(window.document).query( `[${window.CSS.escape(config.attr.bindingsreflection)}]`, record => {
132
- record.exits.forEach( entry => detachBindingsContext( entry ) );
136
+ realdom.realtime(window.document).query(`[${window.CSS.escape(config.attr.bindingsreflection)}]`, record => {
137
+ record.exits.forEach(entry => detachBindingsContext(entry));
133
138
  record.entrants.forEach(entry => {
134
- const bindingsParse = parseBindingsAttr( entry.getAttribute( config.attr.bindingsreflection ) || '' );
135
- const newData = [ ...bindingsParse.entries() ].filter(([ k, v ]) => v !== undefined );
136
- if ( newData.length ) entry[ config.api.bind ]( Object.fromEntries( newData ), { merge: true, publish: false } );
137
- for ( const [ key ] of bindingsParse ) {
138
- attachBindingsContext( entry, key );
139
+ const bindingsParse = parseBindingsAttr(entry.getAttribute(config.attr.bindingsreflection) || '');
140
+ const newData = [...bindingsParse.entries()].filter(([k, v]) => v !== undefined);
141
+ if (newData.length) entry[config.api.bind](Object.fromEntries(newData), { merge: true, publish: false });
142
+ for (const [key] of bindingsParse) {
143
+ attachBindingsContext(entry, key);
139
144
  }
140
- } );
145
+ });
141
146
  }, { id: 'bindings:dom', live: true, subtree: 'cross-roots', timing: 'sync', eventDetails: true });
142
- realdom.realtime( window.document, 'attr' ).observe( config.attr.bindingsreflection, record => {
143
- const bindingsObj = getBindings.call( window, config, record.target );
144
- const bindingsParse = parseBindingsAttr( record.value || '' );
145
- const oldBindings = parseBindingsAttr( record.oldValue || '' );
146
- for ( const key of new Set([ ...bindingsParse.keys(), ...oldBindings.keys() ]) ) {
147
- if ( !oldBindings.has( key ) ) {
148
- if ( bindingsParse.get( key ) !== undefined ) Observer.set( bindingsObj, key, bindingsParse.get( key ), { detail: { publish: false } } );
149
- attachBindingsContext( record.target, key );
150
- } else if ( !bindingsParse.has( key ) ) {
151
- if ( oldBindings.get( key ) !== undefined ) Observer.deleteProperty( bindingsObj, key, { detail: { publish: false } } );
152
- detachBindingsContext( record.target, key );
153
- } else if ( bindingsParse.get( key ) !== oldBindings.get( key ) ) {
154
- Observer.set( bindingsObj, key, bindingsParse.get( key ), { detail: { publish: false } } );
147
+ realdom.realtime(window.document, 'attr').observe(config.attr.bindingsreflection, record => {
148
+ const bindingsObj = getBindings.call(window, config, record.target);
149
+ const bindingsParse = parseBindingsAttr(record.value || '');
150
+ const oldBindings = parseBindingsAttr(record.oldValue || '');
151
+ for (const key of new Set([...bindingsParse.keys(), ...oldBindings.keys()])) {
152
+ if (!oldBindings.has(key)) {
153
+ if (bindingsParse.get(key) !== undefined) Observer.set(bindingsObj, key, bindingsParse.get(key), { detail: { publish: false } });
154
+ attachBindingsContext(record.target, key);
155
+ } else if (!bindingsParse.has(key)) {
156
+ if (oldBindings.get(key) !== undefined) Observer.deleteProperty(bindingsObj, key, { detail: { publish: false } });
157
+ detachBindingsContext(record.target, key);
158
+ } else if (bindingsParse.get(key) !== oldBindings.get(key)) {
159
+ Observer.set(bindingsObj, key, bindingsParse.get(key), { detail: { publish: false } });
155
160
  }
156
161
  }
157
- }, { id: 'bindings:attr', subtree: 'cross-roots', timing: 'sync', newValue: true, oldValue: true } );
162
+ }, { id: 'bindings:attr', subtree: 'cross-roots', timing: 'sync', newValue: true, oldValue: true });
158
163
  }
159
164
 
160
165
  const parseBindingsAttr = str => {
161
166
  str = str.trim();
162
- return new Map(_splitOuter( str.slice(1, -1), ',' ).filter( s => s.trim() ).map( _str => {
163
- return _splitOuter( _str, ':' ).map( s => s.trim() );
167
+ return new Map(_splitOuter(str.slice(1, -1), ',').filter(s => s.trim()).map(_str => {
168
+ return _splitOuter(_str, ':').map(s => s.trim());
164
169
  }));
165
170
  };
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * @imports
4
4
  */
5
+ import { isDocument, isShadowRoot } from '@webqit/realdom';
5
6
  import DOMContexts from './DOMContexts.js';
6
7
  import { env } from '../util.js';
7
8
 
@@ -36,7 +37,7 @@ export default class DOMContext {
36
37
  /**
37
38
  * @name
38
39
  */
39
- get name() { return [ env.window.Document, env.window.ShadowRoot ].some( x => this.host instanceof x ) ? Infinity : this.host.getAttribute( this.configs.CONTEXT_API.attr.contextname ); }
40
+ get name() { return isDocument(this.host) || isShadowRoot(this.host) ? Infinity : this.host.getAttribute( this.configs.CONTEXT_API.attr.contextname ); }
40
41
 
41
42
  /**
42
43
  * @subscribed()
@@ -225,7 +225,7 @@ function compileInlineBindings( config, str ) {
225
225
  $existing__.delete( $key___ );
226
226
  $exec__($itemNode__, '${ config.BINDINGS_API.api.bind }', $itemBinding__ );
227
227
  } else {
228
- $itemNode__ = ( Array.isArray( $import__.value ) ? $import__.value[ 0 ] : ( $import__.value instanceof window.HTMLTemplateElement ? $import__.value.content.firstElementChild : $import__.value ) ).cloneNode( true );
228
+ $itemNode__ = ( Array.isArray( $import__.value ) ? $import__.value[ 0 ] : ( $import__.value?.nodeName === 'TEMPLATE' ? $import__.value.content.firstElementChild : $import__.value ) ).cloneNode( true );
229
229
  $itemNode__.setAttribute( "${ config.attr.itemIndex }", $key___ );
230
230
  $exec__($itemNode__, '${ config.BINDINGS_API.api.bind }', $itemBinding__ );
231
231
  $exec__(this, 'appendChild', $itemNode__ );
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * @imports
4
4
  */
5
+ import { isNodeInterface } from '@webqit/realdom';
5
6
  import HTMLImportsContext from './HTMLImportsContext.js';
6
7
  import { _wq, env } from '../util.js';
7
8
 
@@ -26,7 +27,7 @@ export default function () {
26
27
  * @returns
27
28
  */
28
29
  static instance(node) {
29
- if (configs.HTML_IMPORTS.elements.import.includes('-') && (node instanceof this)) return node;
30
+ if (configs.HTML_IMPORTS.elements.import.includes('-') && (node.nodeName === this.nodeName)) return node;
30
31
  return _wq(node).get('import::instance') || new this(node);
31
32
  }
32
33
 
@@ -57,7 +58,7 @@ export default function () {
57
58
  const moduleRef = priv.moduleRef.includes('#') ? priv.moduleRef : `${priv.moduleRef}#`/* for live children */;
58
59
  const request = { ...HTMLImportsContext.createRequest(moduleRef), live: signal && true, signal, diff: !moduleRef.endsWith('#') };
59
60
  parentNode[configs.CONTEXT_API.api.contexts].request(request, response => {
60
- callback((response instanceof window.HTMLTemplateElement ? [...response.content.children] : (
61
+ callback((isNodeInterface(response, 'HTMLTemplateElement') ? [...response.content.children] : (
61
62
  Array.isArray(response) ? response : response && [response]
62
63
  )) || []);
63
64
  });
@@ -88,6 +89,7 @@ export default function () {
88
89
  priv.slottedElements.add(slottedElement);
89
90
  });
90
91
  priv.originalsRemapped = true;
92
+ priv.autoRestore();
91
93
  });
92
94
  };
93
95
 
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * @imports
4
4
  */
5
+ import { isDocument, isShadowRoot } from '@webqit/realdom';
5
6
  import DOMNamingContext from './DOMNamingContext.js';
6
7
  import { _wq, _init, _splitOuter, _fromHash, _toHash, getInternalAttrInteraction, internalAttrInteraction } from '../util.js';
7
8
 
@@ -122,7 +123,7 @@ export function getOwnNamespaceObject(node) {
122
123
  if (!_wq(node).has('namespace')) {
123
124
  const namespaceObj = Object.create(null);
124
125
  _wq(node).set('namespace', namespaceObj);
125
- const isDocumentRoot = [window.Document, window.ShadowRoot].some(x => node instanceof x);
126
+ const isDocumentRoot = isDocument(node) || isShadowRoot(node);
126
127
  Object.defineProperty(namespaceObj, Symbol.toStringTag, {
127
128
  get() {
128
129
  return isDocumentRoot ? 'RootNamespaceRegistry' : 'NamespaceRegistry';
@@ -140,7 +141,7 @@ export function getOwnNamespaceObject(node) {
140
141
  */
141
142
  export function getOwnerNamespaceObject(node, forID = false) {
142
143
  const window = this, { webqit: { oohtml: { configs: { NAMESPACED_HTML: config } } } } = window;
143
- const isDocumentRoot = [window.Document, window.ShadowRoot].some(x => node instanceof x);
144
+ const isDocumentRoot = isDocument(node) || isShadowRoot(node);
144
145
  return getOwnNamespaceObject.call(window, isDocumentRoot ? node : ((forID ? node.parentNode : node)?.closest/*can be documentFragment when Shadow DOM*/?.(config.namespaceSelector) || node.getRootNode()));
145
146
  }
146
147
 
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * @imports
4
4
  */
5
+ import { isElement } from '@webqit/realdom';
5
6
  import { rewriteSelector, getOwnerNamespaceObject, getNamespaceUUID } from '../namespaced-html/index.js';
6
7
  import { _init, _toHash } from '../util.js';
7
8
 
@@ -76,7 +77,7 @@ function realtime( config ) {
76
77
  const scopeSelector = style.scoped && ( supportsHAS ? `:has(> style[rand-${ sourceHash }])` : `[rand-${ sourceHash }]` );
77
78
  const supportsScope = style.scoped && window.CSSScopeRule && false/* Disabled for buggy behaviour: rewriting selectorText within an @scope block invalidates the scoping */;
78
79
  const scopeRoot = style.scoped && style.parentNode || style.getRootNode();
79
- if ( scopeRoot instanceof window.Element ) {
80
+ if ( isElement(scopeRoot) ) {
80
81
  scopeRoot[ config.api.styleSheets ].push( style );
81
82
  if ( !inBrowser ) return;
82
83
  ( supportsHAS ? style : scopeRoot ).toggleAttribute( `rand-${ sourceHash }`, true );
@@ -1,37 +1,40 @@
1
+ import { isElement } from '@webqit/realdom';
1
2
  import { resolveParams } from '@webqit/use-live/params';
2
3
  import { _wq, _init, _toHash, _fromHash } from '../util.js';
3
4
 
4
5
  export default function init({ advanced = {}, ...$config }) {
5
- const { config, window } = _init.call( this, 'scoped-js', $config, {
6
+ const { config, window } = _init.call(this, 'scoped-js', $config, {
6
7
  script: { retention: 'retain', mimeTypes: 'module|text/javascript|application/javascript', timing: 'auto' },
7
8
  api: { scripts: 'scripts' },
8
9
  advanced: resolveParams(advanced),
9
- } );
10
- const customTypes = Array.isArray( config.script.mimeTypes ) ? config.script.mimeTypes : config.script.mimeTypes.split( '|' ).filter( t => t );
11
- config.scriptSelector = customTypes.map( t => `script[type="${ window.CSS.escape( t ) }"]:not([oohtmlignore])` ).concat(`script:not([type])`).join( ',' );
10
+ });
11
+ const customTypes = Array.isArray(config.script.mimeTypes) ? config.script.mimeTypes : config.script.mimeTypes.split('|').filter(t => t);
12
+ config.scriptSelector = customTypes.map(t => `script[type="${window.CSS.escape(t)}"]:not([oohtmlignore])`).concat(`script:not([type])`).join(',');
12
13
  window.webqit.oohtml.Script = {
13
- compileCache: [ new Map, new Map, ],
14
- execute: execute.bind( window, config ),
14
+ compileCache: [new Map, new Map,],
15
+ execute: execute.bind(window, config),
15
16
  };
16
- exposeAPIs.call( window, config );
17
- realtime.call( window, config );
17
+ exposeAPIs.call(window, config);
18
+ realtime.call(window, config);
18
19
  }
19
20
 
20
- function exposeAPIs( config ) {
21
+ function exposeAPIs(config) {
21
22
  const window = this, { webqit: { nextKeyword, matchPrologDirective } } = window;
22
23
  const scriptsMap = new Map;
23
- if ( config.api.scripts in window.Element.prototype ) { throw new Error( `The "Element" class already has a "${ config.api.scripts }" property!` ); }
24
- [ window.ShadowRoot.prototype, window.Element.prototype ].forEach( proto => {
25
- Object.defineProperty( proto, config.api.scripts, { get: function() {
26
- if ( !scriptsMap.has( this ) ) { scriptsMap.set( this, [] ); }
27
- return scriptsMap.get( this );
28
- }, } );
29
- } );
30
- Object.defineProperties( window.HTMLScriptElement.prototype, {
24
+ if (config.api.scripts in window.Element.prototype) { throw new Error(`The "Element" class already has a "${config.api.scripts}" property!`); }
25
+ [window.ShadowRoot.prototype, window.Element.prototype].forEach(proto => {
26
+ Object.defineProperty(proto, config.api.scripts, {
27
+ get: function () {
28
+ if (!scriptsMap.has(this)) { scriptsMap.set(this, []); }
29
+ return scriptsMap.get(this);
30
+ },
31
+ });
32
+ });
33
+ Object.defineProperties(window.HTMLScriptElement.prototype, {
31
34
  scoped: {
32
35
  configurable: true,
33
- get() { return this.hasAttribute( 'scoped' ); },
34
- set( value ) { this.toggleAttribute( 'scoped', value ); },
36
+ get() { return this.hasAttribute('scoped'); },
37
+ set(value) { this.toggleAttribute('scoped', value); },
35
38
  },
36
39
  live: {
37
40
  configurable: true,
@@ -41,19 +44,19 @@ function exposeAPIs( config ) {
41
44
  return matchPrologDirective(scriptContents, true);
42
45
  },
43
46
  },
44
- } );
47
+ });
45
48
  }
46
49
 
47
50
  // Script runner
48
- async function execute( config, execHash ) {
51
+ async function execute(config, execHash) {
49
52
  const window = this, { realdom } = window.webqit;
50
- const exec = _fromHash( execHash );
51
- if ( !exec ) throw new Error( `Argument must be a valid exec hash.` );
53
+ const exec = _fromHash(execHash);
54
+ if (!exec) throw new Error(`Argument must be a valid exec hash.`);
52
55
  const { script, compiledScript, thisContext } = exec;
53
56
  // Honour retention flag
54
- if ( config.script.retention === 'dispose' ) {
57
+ if (config.script.retention === 'dispose') {
55
58
  script.remove();
56
- } else if ( config.script.retention === 'hidden' ) {
59
+ } else if (config.script.retention === 'hidden') {
57
60
  script.textContent = `"source hidden"`;
58
61
  } else {
59
62
  setTimeout(async () => {
@@ -62,73 +65,73 @@ async function execute( config, execHash ) {
62
65
  }
63
66
  // Execute and save state
64
67
  const varScope = script.scoped ? thisContext : script.getRootNode();
65
- if ( !_wq( varScope ).has( 'scriptEnv' ) ) {
66
- _wq( varScope ).set( 'scriptEnv', Object.create( null ) );
68
+ if (!_wq(varScope).has('scriptEnv')) {
69
+ _wq(varScope).set('scriptEnv', Object.create(null));
67
70
  }
68
- const liveProgramHandle = await ( await compiledScript.bind( thisContext, _wq( varScope ).get( 'scriptEnv' ) ) ).execute();
69
- if ( script.live ) { Object.defineProperty( script, 'liveProgramHandle', { value: liveProgramHandle } ); }
70
- realdom.realtime( window.document ).observe( script, () => {
71
- if ( script.live ) { liveProgramHandle.abort(); }
72
- if ( thisContext instanceof window.Element ) { thisContext[ config.api.scripts ]?.splice( thisContext[ config.api.scripts ].indexOf( script, 1 ) ); }
73
- }, { id: 'scoped-js:script-exits', subtree: 'cross-roots', timing: 'sync', generation: 'exits' } );
71
+ const liveProgramHandle = await (await compiledScript.bind(thisContext, _wq(varScope).get('scriptEnv'))).execute();
72
+ if (script.live) { Object.defineProperty(script, 'liveProgramHandle', { value: liveProgramHandle }); }
73
+ realdom.realtime(window.document).observe(script, () => {
74
+ if (script.live) { liveProgramHandle.abort(); }
75
+ if (isElement(thisContext)) { thisContext[config.api.scripts]?.splice(thisContext[config.api.scripts].indexOf(script, 1)); }
76
+ }, { id: 'scoped-js:script-exits', subtree: 'cross-roots', timing: 'sync', generation: 'exits' });
74
77
  }
75
78
 
76
- function realtime( config ) {
77
- const inBrowser = Object.getOwnPropertyDescriptor( globalThis, 'window' )?.get?.toString().includes( '[native code]' ) ?? false;
79
+ function realtime(config) {
80
+ const inBrowser = Object.getOwnPropertyDescriptor(globalThis, 'window')?.get?.toString().includes('[native code]') ?? false;
78
81
  const window = this, { webqit: { oohtml, realdom } } = window;
79
- if ( !window.HTMLScriptElement.supports ) { window.HTMLScriptElement.supports = type => [ 'text/javascript', 'application/javascript' ].includes( type ); }
82
+ if (!window.HTMLScriptElement.supports) { window.HTMLScriptElement.supports = type => ['text/javascript', 'application/javascript'].includes(type); }
80
83
  const handled = new WeakSet;
81
- realdom.realtime( window.document ).query( config.scriptSelector, record => {
82
- record.entrants.forEach( script => {
83
- if ( handled.has( script ) || script.hasAttribute('oohtmlno') || (!inBrowser && !script.hasAttribute('ssr')) ) return;
84
+ realdom.realtime(window.document).query(config.scriptSelector, record => {
85
+ record.entrants.forEach(script => {
86
+ if (handled.has(script) || script.hasAttribute('oohtmlno') || (!inBrowser && !script.hasAttribute('ssr'))) return;
84
87
  // Do compilation
85
- const compiledScript = compileScript.call( window, config, script );
86
- if ( !compiledScript ) return;
87
- handled.add( script );
88
+ const compiledScript = compileScript.call(window, config, script);
89
+ if (!compiledScript) return;
90
+ handled.add(script);
88
91
  // Run now!!!
89
- const thisContext = script.scoped ? script.parentNode || record.target : ( script.type === 'module' ? undefined : window );
90
- if ( script.scoped ) { thisContext[ config.api.scripts ].push( script ); }
91
- const execHash = _toHash( { script, compiledScript, thisContext } );
92
- const manualHandling = record.type === 'query' || ( script.type && !window.HTMLScriptElement.supports( script.type ) ) || script.getAttribute('data-handling') === 'manual';
93
- if ( manualHandling || config.script.timing === 'manual' ) { oohtml.Script.execute( execHash ); } else {
94
- script.textContent = `webqit.oohtml.Script.execute( '${ execHash }' );`;
92
+ const thisContext = script.scoped ? script.parentNode || record.target : (script.type === 'module' ? undefined : window);
93
+ if (script.scoped) { thisContext[config.api.scripts].push(script); }
94
+ const execHash = _toHash({ script, compiledScript, thisContext });
95
+ const manualHandling = record.type === 'query' || (script.type && !window.HTMLScriptElement.supports(script.type)) || script.getAttribute('data-handling') === 'manual';
96
+ if (manualHandling || config.script.timing === 'manual') { oohtml.Script.execute(execHash); } else {
97
+ script.textContent = `webqit.oohtml.Script.execute( '${execHash}' );`;
95
98
  }
96
- } );
97
- }, { id: 'scoped-js:script-entries', live: true, subtree: 'cross-roots', timing: 'intercept', generation: 'entrants', eventDetails: true } );
99
+ });
100
+ }, { id: 'scoped-js:script-entries', live: true, subtree: 'cross-roots', timing: 'intercept', generation: 'entrants', eventDetails: true });
98
101
  // ---
99
102
  }
100
103
 
101
- function compileScript( config, script ) {
104
+ function compileScript(config, script) {
102
105
  const window = this, { webqit: { oohtml, LiveScript, AsyncLiveScript, LiveModule } } = window;
103
-
106
+
104
107
  let textContent = script.textContent.trim();
105
- if ( textContent.startsWith( '/*@oohtml*/if(false){' ) && textContent.endsWith( '}/*@oohtml*/' ) ) {
106
- textContent = textContent.slice( 21, -12 );
107
- Object.defineProperty( script, 'oohtml__textContent', { value: textContent } );
108
+ if (textContent.startsWith('/*@oohtml*/if(false){') && textContent.endsWith('}/*@oohtml*/')) {
109
+ textContent = textContent.slice(21, -12);
110
+ Object.defineProperty(script, 'oohtml__textContent', { value: textContent });
108
111
  }
109
- if ( !textContent.trim().length ) return;
112
+ if (!textContent.trim().length) return;
110
113
 
111
- const sourceHash = _toHash( textContent );
112
- const compileCache = oohtml.Script.compileCache[ script.live ? 0 : 1 ];
114
+ const sourceHash = _toHash(textContent);
115
+ const compileCache = oohtml.Script.compileCache[script.live ? 0 : 1];
113
116
  let compiledScript;
114
- if ( !( compiledScript = compileCache.get( sourceHash ) ) ) {
117
+ if (!(compiledScript = compileCache.get(sourceHash))) {
115
118
  const { parserParams, compilerParams, runtimeParams } = config.advanced;
116
- compiledScript = new ( script.type === 'module' ? LiveModule : ( LiveScript || AsyncLiveScript ) )( textContent, {
119
+ compiledScript = new (script.type === 'module' ? LiveModule : (LiveScript || AsyncLiveScript))(textContent, {
117
120
  liveMode: script.live,
118
- exportNamespace: `#${ script.id }`,
119
- fileName: `${ window.document.url?.split( '#' )?.[ 0 ] || '' }#${ script.id }`,
121
+ exportNamespace: `#${script.id}`,
122
+ fileName: `${window.document.url?.split('#')?.[0] || ''}#${script.id}`,
120
123
  parserParams,
121
124
  compilerParams,
122
125
  runtimeParams,
123
- } );
124
- compileCache.set( sourceHash, compiledScript );
126
+ });
127
+ compileCache.set(sourceHash, compiledScript);
125
128
  }
126
129
  return compiledScript;
127
130
  }
128
131
 
129
- export function idleCompiler( node ) {
132
+ export function idleCompiler(node) {
130
133
  const window = this, { webqit: { oohtml: { configs: { SCOPED_JS: config } } } } = window;
131
- [ ...( node?.querySelectorAll( config.scriptSelector ) || [] ) ].forEach( script => {
132
- compileScript.call( window, config, script );
133
- } );
134
+ [...(node?.querySelectorAll(config.scriptSelector) || [])].forEach(script => {
135
+ compileScript.call(window, config, script);
136
+ });
134
137
  }
@@ -3,7 +3,7 @@
3
3
  * @imports
4
4
  */
5
5
  import { expect } from 'chai';
6
- import { createDocument } from './index.js';
6
+ import { createDocument, delay } from './index.js';
7
7
 
8
8
  describe(`Bindings API`, function() {
9
9
 
@@ -19,7 +19,9 @@ describe(`Bindings API`, function() {
19
19
  } );
20
20
 
21
21
  it ( `Bindings objects should be observable...`, async function() {
22
+ await delay( 200 );
22
23
  const { webqit: { Observer } } = window;
24
+
23
25
  let idReceived = null;
24
26
  Observer.observe( document.bindings, records => {
25
27
  idReceived = records[ 0 ].key;
@@ -31,6 +31,8 @@ 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
+
35
+ await delay( 0 );
34
36
  console.log('\n\n\n\n', document.body.outerHTML);
35
37
  expect( document.body.children ).to.have.length( 3 );
36
38
  } );
@@ -203,11 +205,17 @@ describe(`HTML Imports`, function() {
203
205
 
204
206
  const routingElement = document.body.firstElementChild;
205
207
  expect( routingElement.firstElementChild.nodeName ).to.eq( 'TEXTAREA' );
208
+
206
209
  routingElement.setAttribute( 'importscontext', 'temp0/temp1/temp2' );
210
+ await delay( 0 );
207
211
  expect( routingElement.firstElementChild.nodeName ).to.eq( 'SELECT' );
212
+
208
213
  routingElement.removeChild( routingElement.firstElementChild );
214
+ await delay( 0 );
209
215
  expect( routingElement.firstElementChild.nodeName ).to.eq( 'IMPORT' );
216
+
210
217
  routingElement.setAttribute( 'importscontext', 'temp0' );
218
+ await delay( 0 );
211
219
  expect( routingElement.firstElementChild.nodeName ).to.eq( 'INPUT' );
212
220
  } );
213
221