@webqit/oohtml 2.1.53 → 2.1.54

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": "2.1.53",
17
+ "version": "2.1.54",
18
18
  "license": "MIT",
19
19
  "repository": {
20
20
  "type": "git",
@@ -25,9 +25,6 @@
25
25
  },
26
26
  "type": "module",
27
27
  "sideEffects": false,
28
- "browser": {
29
- "fs": false
30
- },
31
28
  "main": "./src/index.js",
32
29
  "scripts": {
33
30
  "test": "mocha --extension .test.js --exit",
@@ -39,8 +36,8 @@
39
36
  },
40
37
  "dependencies": {
41
38
  "@webqit/observer": "^2.2.9",
42
- "@webqit/realdom": "^2.1.17-0",
43
- "@webqit/stateful-js": "^3.0.14-0",
39
+ "@webqit/realdom": "^2.1.17",
40
+ "@webqit/stateful-js": "^3.0.15",
44
41
  "@webqit/util": "^0.8.11"
45
42
  },
46
43
  "devDependencies": {
@@ -6,8 +6,8 @@ import Observer from "@webqit/observer";
6
6
 
7
7
  export default class ContextReturnValue {
8
8
  constructor( request, hostElement ) {
9
- this.request = request;
10
- this.hostElement = hostElement;
9
+ Object.defineProperty( this, 'request', { value: request } );
10
+ Object.defineProperty( this, 'hostElement', { value: hostElement } );
11
11
  if ( request.live && !request.signal ) {
12
12
  Object.defineProperty( this, 'abortController', { value: new AbortController } );
13
13
  request.signal = this.abortController.signal;
@@ -7,12 +7,12 @@ import Bracelet from './Bracelet.js';
7
7
  import { _ } from '../util.js';
8
8
 
9
9
  export default class AttrBracelet extends Bracelet {
10
- static get query() { return `*[@*[${ this.tokens.contains }]]`; }
10
+ static get query() { return `@*[${ this.tokens.contains }]`; }
11
11
 
12
12
  static parse( ...attrs ) {
13
13
  return attrs.reduce( ( attrs, attr ) => {
14
14
  return attrs.concat( [ ...attr.nodeValue.matchAll( new RegExp( this.tokens.regex, 'g' ) ) ].reduce( ( bracelets, match ) => {
15
- const bracelet = new this( attr, match[ 0 ], match.index, match[ 1 ].trim() );
15
+ const bracelet = new this( attr, match[ 0 ], match.index, match[ 1 ].trim(), attr.nodeName === 'class' || match[ 0 ] === attr.nodeValue.trim() );
16
16
  const prev = bracelets.slice( -1 )[ 0 ];
17
17
  if ( prev ) { prev._nextSibling = bracelet; }
18
18
  return bracelets.concat( bracelet );
@@ -55,25 +55,31 @@ export default class AttrBracelet extends Bracelet {
55
55
  }
56
56
  }
57
57
 
58
- constructor( attr, _value, startIndex, expr ) {
58
+ constructor( attr, _value, startIndex, expr, booleanAble ) {
59
59
  super();
60
60
  const $refs = [], $expr = this.parseExpr( expr, $refs );
61
+ const ownerElement = attr.ownerElement; // Hard save. Deleted attributes don't retain .ownerElement
61
62
  Object.defineProperties( this, {
62
63
  type: { get: () => 'attr' },
63
64
  expr: { get: () => $expr },
64
65
  refs: { get: () => $refs },
65
66
  attr: { get: () => attr },
66
- ownerElement: { get: () => attr.ownerElement },
67
+ ownerElement: { get: () => ownerElement },
67
68
  originalValue: { value: _value },
68
69
  _value: { value: _value, writable: true },
69
70
  _dirty: { value: false, writable: true },
70
71
  _startIndex: { value: undefined, writable: true },
71
72
  _endIndex: { value: undefined, writable: true },
72
73
  _nextSibling: { value: undefined, writable: true },
74
+ _booleanAble: { value: booleanAble, writable: true },
73
75
  _nested: { value: new Set },
74
76
  } );
77
+ if ( this.attr.nodeName === 'class' && ( this.expr.length !== 1 || this.expr[ 0 ].type !== 'ref' ) ) {
78
+ throw new Error( `Invalid bracelet for the class attribute: "${ this.originalValue }"` );
79
+ }
75
80
  this.startIndex = startIndex;
76
81
  }
82
+ get isBoolean() { return this._booleanAble && typeof this._value === 'boolean'; }
77
83
 
78
84
  get startIndex() { return this._startIndex; }
79
85
  set startIndex( value ) {
@@ -97,7 +103,14 @@ export default class AttrBracelet extends Bracelet {
97
103
  this._nested.forEach( p => p.disconnect() );
98
104
  const attrBraceletsRegistry = _( this.ownerElement ).get( 'attr-bracelets' );
99
105
  attrBraceletsRegistry.active.unshift( this );
100
- this.ownerElement.setAttribute( this.attr.nodeName, this.attr.nodeValue.substring( 0, this.startIndex ) + value + this.attr.nodeValue.substring( this.endIndex ) );
106
+ if ( this.isBoolean && this.attr.nodeName !== 'class' ) {
107
+ this.ownerElement.toggleAttribute( this.attr.nodeName, value );
108
+ } else {
109
+ if ( this.isBoolean && this.attr.nodeName === 'class' ) {
110
+ value = value ? this.expr[ 0 ].value.join( '' ) : '';
111
+ }
112
+ this.ownerElement.setAttribute( this.attr.nodeName, this.attr.nodeValue.substring( 0, this.startIndex ) + value + this.attr.nodeValue.substring( this.endIndex ) );
113
+ }
101
114
  attrBraceletsRegistry.active.shift();
102
115
  // Reindex
103
116
  const newEndIndex = this.startIndex + value.length;
@@ -65,13 +65,17 @@ export default class Bracelet {
65
65
  }
66
66
 
67
67
  render( bindings ) {
68
- const value = this.renderExpr( this.expr, bindings );
68
+ let value = this.renderExpr( this.expr, bindings );
69
69
  if ( typeof value === 'undefined' ) {
70
- if ( !this.dirty ) return;
71
- this.value = this.originalValue;
70
+ value = this.originalValue;
71
+ if ( !this.dirty ) {
72
+ if ( this._booleanAble ) { value = false; }
73
+ else return;
74
+ }
75
+ this.value = value;
72
76
  return;
73
77
  }
74
- this.value = value + '';
78
+ this.value = value;
75
79
  }
76
80
 
77
81
  disconnect() { this.disconnected = true; }
@@ -31,17 +31,17 @@ export default class HTMLBracelets extends Set {
31
31
  if ( !( bracelet instanceof Bracelet ) ) throw new Error( `Argument must be instance of Bracelet.` );
32
32
  const returnValue = super.add( bracelet );
33
33
  const bindings = this[ '#' ].bindings;
34
- bracelet.refs.forEach( ref => {
35
- const $ref = ref.join( '.' );
36
- if ( !( $ref in bindings ) ) {
37
- bindings[ $ref ] = { subs: new Set, controller: new AbortController };
38
- const request = _HTMLBindingsProvider.createRequest( { detail: ref, live: true, signal: bindings[ $ref ].signal } );
34
+ bracelet.refs.forEach( path => {
35
+ const $path = path.join( '.' );
36
+ if ( !( $path in bindings ) ) {
37
+ bindings[ $path ] = { subs: new Set, controller: new AbortController };
38
+ const request = _HTMLBindingsProvider.createRequest( { detail: path, live: true, signal: bindings[ $path ].signal } );
39
39
  HTMLContext.instance( this[ '#' ].host ).request( request, value => {
40
- bindings[ $ref ].value = value;
41
- bindings[ $ref ].subs.forEach( bracelet => bracelet.render( bindings ) );
40
+ bindings[ $path ].value = value;
41
+ bindings[ $path ].subs.forEach( bracelet => bracelet.render( bindings ) );
42
42
  } );
43
43
  }
44
- bindings[ $ref ].subs.add( bracelet );
44
+ bindings[ $path ].subs.add( bracelet );
45
45
  } );
46
46
  bracelet.render( bindings );
47
47
  return returnValue;
@@ -51,11 +51,12 @@ export default class HTMLBracelets extends Set {
51
51
  if ( !( bracelet instanceof Bracelet ) ) throw new Error( `Argument must be instance of Bracelet.` );
52
52
  const returnValue = super.delete( bracelet );
53
53
  const bindings = this[ '#' ].bindings;
54
- bracelet.refs.forEach( ref => {
55
- bindings[ ref ].subs.delete( bracelet );
56
- if ( !bindings[ ref ].subs.size ) {
57
- bindings[ ref ].controller.abort();
58
- delete bindings[ ref ];
54
+ bracelet.refs.forEach( path => {
55
+ const $path = path.join( '.' );
56
+ bindings[ $path ].subs.delete( bracelet );
57
+ if ( !bindings[ $path ].subs.size ) {
58
+ bindings[ $path ].controller.abort();
59
+ delete bindings[ $path ];
59
60
  }
60
61
  } );
61
62
  return returnValue;
@@ -7,7 +7,7 @@ import Bracelet from './Bracelet.js';
7
7
  import { _ } from '../util.js';
8
8
 
9
9
  export default class TextBracelet extends Bracelet {
10
- static get query() { return `text()[not(ancestor::script) and ${ this.tokens.contains }]`; }
10
+ static get query() { return `text()[not(ancestor::script) and not(ancestor::style) and ${ this.tokens.contains }]`; }
11
11
 
12
12
  static parse( ...nodes ) {
13
13
  return nodes.reduce( ( nodes, node ) => {
@@ -53,19 +53,20 @@ function exposeAPIs( config ) {
53
53
  */
54
54
  function realtime( config ) {
55
55
  const window = this, { realdom } = window.webqit;
56
- realdom.realtime( window.document ).observe( `(${ TextBracelet.query })`, record => {
56
+ realdom.realtime( window.document ).subtree( `(${ TextBracelet.query })`, record => {
57
57
  TextBracelet.cleanup( ...record.exits );
58
58
  TextBracelet.mount( ...TextBracelet.parse( ...record.entrants.filter( node => !_( node ).has( 'text-bracelet' )/** generated text nodes during parse() */ ) ) );
59
- }, { subtree: true } );
59
+ }, { live: true } );
60
+ realdom.realtime( window.document ).subtree( `(${ AttrBracelet.query })`, record => {
61
+ AttrBracelet.cleanup( ...record.exits );
62
+ AttrBracelet.mount( ...AttrBracelet.parse( ...record.entrants ) );
63
+ }, { live: true } );
60
64
  realdom.realtime( window.document, 'attr' ).observe( records => {
61
65
  for ( const record of records ) {
62
66
  if ( _( record.target ).get( 'attr-bracelets' )?.active.some( p => p.attr.nodeName === record.name ) ) continue;
67
+ if ( [ ...( _( record.target ).get( 'attr-bracelets' )?.get( record.name ) || [] ) ].some( p => p.isBoolean ) ) continue;
63
68
  if ( record.oldValue ) { AttrBracelet.cleanup( record.value ? record.target.attributes[ record.name ] : { ownerElement: record.target, nodeName: record.name } ); }
64
69
  if ( record.value ) { AttrBracelet.mount( ...AttrBracelet.parse( record.target.attributes[ record.name ] ) ); }
65
70
  }
66
71
  }, { subtree: true, newValue: true, oldValue: true, timing: 'sync' } );
67
- realdom.realtime( window.document ).observe( `(${ AttrBracelet.query })`, record => {
68
- AttrBracelet.cleanup( ...record.exits.reduce( ( attrs, node ) => [ ...attrs, ...node.attributes ], [] ) );
69
- AttrBracelet.mount( ...AttrBracelet.parse( ...record.entrants.reduce( ( attrs, node ) => [ ...attrs, ...node.attributes ], [] ) ) );
70
- }, { subtree: true } );
71
72
  }
@@ -76,14 +76,18 @@ function exposeAPIs( config ) {
76
76
  Object.defineProperty( window.HTMLTemplateElement.prototype, config.template.api.moduledef, { get: function() {
77
77
  return this.getAttribute( config.template.attr.moduledef );
78
78
  } } );
79
- Object.defineProperty( window.document, config.context.api.import, { value: function( ref, callback = null ) {
79
+ Object.defineProperty( window.document, config.context.api.import, { value: function( ref, live = false, callback = null ) {
80
80
  return importRequest( window.document, ...arguments );
81
81
  } } );
82
- Object.defineProperty( window.HTMLElement.prototype, config.context.api.import, { value: function( ref, callback = null ) {
82
+ Object.defineProperty( window.HTMLElement.prototype, config.context.api.import, { value: function( ref, live = false, callback = null ) {
83
83
  return importRequest( this, ...arguments );
84
84
  } } );
85
- function importRequest( context, ref, callback = null ) {
86
- const request = _HTMLImportsProvider.createRequest( { detail: ref } );
85
+ function importRequest( context, ref, live = false, callback = null ) {
86
+ if ( typeof live === 'function' ) {
87
+ callback = live;
88
+ live = false;
89
+ }
90
+ const request = _HTMLImportsProvider.createRequest( { detail: ref, live } );
87
91
  return HTMLContext.instance( context ).request( request, callback );
88
92
  }
89
93
  }
@@ -2,8 +2,8 @@
2
2
  /**
3
3
  * @imports
4
4
  */
5
- import { resolveParams } from '@webqit/stateful-js/src/params.js';
6
- import { StatefulAsyncFunction, StatefulAsyncScript, StatefulModule, State } from '@webqit/stateful-js/src/index.async.js';
5
+ import { resolveParams } from '@webqit/stateful-js/params';
6
+ import { StatefulAsyncFunction, StatefulAsyncScript, StatefulModule, State } from '@webqit/stateful-js/async';
7
7
  import Observer from '@webqit/observer';
8
8
  import Hash from './Hash.js';
9
9
  import { _init } from '../util.js';
@@ -54,16 +54,10 @@ async function execute( config, execHash ) {
54
54
  }
55
55
  // Execute and save state
56
56
  const state = ( await compiledScript.bind( thisContext ) ).execute();
57
- if ( thisContext instanceof window.Element && script.scoped ) {
58
- if ( !thisContext.scripts ) { Object.defineProperty( thisContext, 'scripts', { value: [] } ); }
59
- thisContext.scripts.push( state );
60
- }
61
- // Observe DOM removal
62
- if ( !( thisContext instanceof window.Node ) ) return script;
63
- realdom.realtime( window.document ).observe( thisContext, () => {
64
- thisContext.dispatchEvent( new window.CustomEvent( 'remove' ) );
65
- state.dispose();
66
- thisContext.scripts.splice( thisContext.scripts.indexOf( state, 1 ) );
57
+ if ( script.stateful ) { Object.defineProperty( script, 'state', { value: state } ); }
58
+ realdom.realtime( window.document ).observe( script, () => {
59
+ if ( script.stateful ) { state.dispose(); }
60
+ if ( script.scoped ) { thisContext.scripts.splice( thisContext.scripts.indexOf( script, 1 ) ); }
67
61
  }, { subtree: true, timing: 'sync', generation: 'exits' } );
68
62
  }
69
63
 
@@ -102,6 +96,10 @@ function realtime( config ) {
102
96
  }
103
97
  // Run now!!!
104
98
  const thisContext = script.scoped ? script.parentNode || record.target : ( script.type === 'module' ? undefined : window );
99
+ if ( script.scoped ) {
100
+ if ( !thisContext.scripts ) { Object.defineProperty( thisContext, 'scripts', { value: [] } ); }
101
+ thisContext.scripts.push( script );
102
+ }
105
103
  const execHash = Hash.toHash( { script, compiledScript, thisContext } );
106
104
  const manualHandling = record.type === 'query' || ( potentialManualTypes.includes( script.type ) && !window.HTMLScriptElement.supports( script.type ) );
107
105
  if ( manualHandling ) { oohtml.Script.execute( execHash ); } else {