@webqit/oohtml 4.3.4 → 4.3.6

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": "4.3.4",
17
+ "version": "4.3.6",
18
18
  "license": "MIT",
19
19
  "repository": {
20
20
  "type": "git",
@@ -39,7 +39,7 @@
39
39
  "postpublish": "git push && git push --tags"
40
40
  },
41
41
  "dependencies": {
42
- "@webqit/quantum-js": "^4.5.11",
42
+ "@webqit/quantum-js": "^4.5.25",
43
43
  "@webqit/realdom": "^2.1.32",
44
44
  "@webqit/util": "^0.8.11"
45
45
  },
@@ -3,7 +3,7 @@
3
3
  * @imports
4
4
  */
5
5
  import DOMBindingsContext from './DOMBindingsContext.js';
6
- import { _, _init } from '../util.js';
6
+ import { _, _init, _splitOuter } from '../util.js';
7
7
 
8
8
  /**
9
9
  * @init
@@ -17,6 +17,7 @@ export default function init( $config = {} ) {
17
17
  } );
18
18
  window.webqit.DOMBindingsContext = DOMBindingsContext;
19
19
  exposeAPIs.call( window, config );
20
+ realtime.call(window, config);
20
21
  }
21
22
 
22
23
  /**
@@ -31,23 +32,27 @@ function getBindings( config, node ) {
31
32
  const bindingsObj = Object.create( null );
32
33
  _( node ).set( 'bindings', bindingsObj );
33
34
  Observer.observe( bindingsObj, mutations => {
34
- // Reflection
35
- const props = Object.keys( bindingsObj );
36
- const reflectionTargetNode = node instanceof window.Document ? node.documentElement : node;
37
- const bindingsReflection = config.attr.bindingsreflection;
38
- if ( props.length && bindingsReflection && reflectionTargetNode.setAttribute ) {
39
- reflectionTargetNode.setAttribute( config.attr.bindingsreflection, props.join( ' ') );
40
- } else if ( bindingsReflection && reflectionTargetNode.setAttribute ) {
41
- reflectionTargetNode.toggleAttribute( config.attr.bindingsreflection, false );
42
- }
43
- // Re: DOMBindingsContext
44
- const contextsApi = node[ ctxConfig.api.contexts ];
45
- for ( const mutation of mutations ) {
46
- if ( mutation.type === 'delete' ) {
47
- const ctx = contextsApi.find( DOMBindingsContext.kind, mutation.key );
48
- if ( ctx ) contextsApi.detach( ctx );
49
- } else if ( !contextsApi.find( DOMBindingsContext.kind, mutation.key ) ) {
50
- contextsApi.attach( new DOMBindingsContext( mutation.key ) );
35
+ if ( node instanceof window.Element ) {
36
+ const bindingsParse = parseBindingsAttr( node.getAttribute( config.attr.bindingsreflection ) || '' );
37
+ 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 );
42
+ }
43
+ }
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 );
47
+ } 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 ) );
55
+ }
51
56
  }
52
57
  }
53
58
  } );
@@ -90,13 +95,71 @@ function exposeAPIs( config ) {
90
95
  *
91
96
  * @return Void
92
97
  */
93
- function applyBindings( config, target, bindings, { merge, diff, namespace } = {} ) {
98
+ function applyBindings( config, target, bindings, { merge, diff, publish, namespace } = {} ) {
94
99
  const window = this, { webqit: { Observer } } = window;
95
100
  const bindingsObj = getBindings.call( this, config, target );
96
- const $params = { diff, namespace };
101
+ const $params = { diff, namespace, detail: { publish } };
97
102
  const exitingKeys = merge ? [] : Observer.ownKeys( bindingsObj, $params ).filter( key => !( key in bindings ) );
98
103
  return Observer.batch( bindingsObj, () => {
99
104
  if ( exitingKeys.length ) { Observer.deleteProperties( bindingsObj, exitingKeys, $params ); }
100
105
  return Observer.set( bindingsObj, bindings, $params );
101
106
  }, $params );
102
107
  }
108
+
109
+ /**
110
+ * Performs realtime capture of elements and their attributes
111
+ * and their module query results; then resolves the respective import elements.
112
+ *
113
+ * @param Object config
114
+ *
115
+ * @return Void
116
+ */
117
+ function realtime(config) {
118
+ const window = this, { webqit: { realdom, Observer, oohtml: { configs } } } = window;
119
+ // ------------
120
+ const attachBindingsContext = (host, key) => {
121
+ const contextsApi = host[configs.CONTEXT_API.api.contexts];
122
+ if ( !contextsApi.find( DOMBindingsContext.kind, key ) ) {
123
+ contextsApi.attach( new DOMBindingsContext( key ) );
124
+ }
125
+ };
126
+ const detachBindingsContext = (host, key) => {
127
+ let ctx, contextsApi = host[configs.CONTEXT_API.api.contexts];
128
+ while( ctx = contextsApi.find( DOMBindingsContext.kind, key ) ) contextsApi.detach(ctx);
129
+ };
130
+ // ------------
131
+ realdom.realtime(window.document).query( `[${window.CSS.escape(config.attr.bindingsreflection)}]`, record => {
132
+ record.exits.forEach( entry => detachBindingsContext( entry ) );
133
+ 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
+ }
140
+ } );
141
+ }, { 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 } } );
155
+ }
156
+ }
157
+ }, { id: 'bindings:attr', subtree: 'cross-roots', timing: 'sync', newValue: true, oldValue: true } );
158
+ }
159
+
160
+ const parseBindingsAttr = str => {
161
+ 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() );
164
+ }));
165
+ };
@@ -120,6 +120,7 @@ export default class DOMContext {
120
120
  this.subscriptions.delete( subscriptionEvent );
121
121
  this.unsubscribed( subscriptionEvent );
122
122
  const { target } = subscriptionEvent;
123
+ subscriptionEvent.meta.answered = false;
123
124
  target.dispatchEvent( subscriptionEvent );
124
125
  } );
125
126
  return this;
@@ -44,7 +44,7 @@ export default class DOMContexts {
44
44
  find( ...args ) {
45
45
  return [ ...this[ '#' ].contexts ].find( ctx => {
46
46
  if ( typeof args[ 0 ] === 'function' ) return args[ 0 ]( ctx );
47
- return ctx.constructor.kind === args[ 0 ] && ( args.length === 1 || ctx.detail === args[ 1 ] );
47
+ return ctx.constructor.kind === args[ 0 ] && ( !args[ 1 ] || ctx.detail === args[ 1 ] );
48
48
  } );
49
49
  }
50
50
 
@@ -84,32 +84,7 @@ export default class DOMContexts {
84
84
  args[ args.indexOf( options ) ] = { ...options, signal: responseInstance.signal };
85
85
  }
86
86
  const event = new ( _DOMContextRequestEvent() )( ...args );
87
- // Initally
88
- event.meta.target = this[ '#' ].host;
89
-
90
- const rootNode = this[ '#' ].host.getRootNode();
91
- const temp = event => {
92
- event.stopImmediatePropagation();
93
- // Always set thus whether answered or not
94
- event.meta.target = event.target;
95
- if ( event.answered ) return;
96
- if ( !waitListMappings.get( rootNode ) ) { waitListMappings.set( rootNode, new Set ); }
97
- if ( event.type === 'contextrequest' && event.live ) {
98
- waitListMappings.get( rootNode ).add( event );
99
- } else if ( event.type === 'contextclaim' ) {
100
- const claims = new Set;
101
- waitListMappings.get( rootNode ).forEach( subscriptionEvent => {
102
- if ( !this[ '#' ].host.contains( subscriptionEvent.target ) || !event.detail.matchEvent( subscriptionEvent ) ) return;
103
- waitListMappings.get( rootNode ).delete( subscriptionEvent );
104
- claims.add( subscriptionEvent );
105
- } );
106
- return event.respondWith( claims );
107
- }
108
- };
109
-
110
- rootNode.addEventListener( event.type, temp );
111
- this[ '#' ].host.dispatchEvent( event );
112
- rootNode.removeEventListener( event.type, temp );
87
+ this[ '#' ].host.dispatchEvent( event );
113
88
  } );
114
89
  }
115
90
 
@@ -21,6 +21,37 @@ export default function init( $config = {} ) {
21
21
  attr: { contextname: 'contextname', },
22
22
  api: { contexts: 'contexts', },
23
23
  } );
24
+ const waitListMappings = new Map, dispatchEvent = window.EventTarget.prototype.dispatchEvent;
25
+ Object.defineProperty( window.EventTarget.prototype, 'dispatchEvent', { value: function( ...args ) {
26
+ const event = args[0], rootNode = this.getRootNode?.();
27
+ if ( [ 'contextclaim', 'contextrequest' ].includes( event.type ) && rootNode ) {
28
+ if ( event.meta ) event.meta.target = this;
29
+ const temp = event => {
30
+ event.stopImmediatePropagation();
31
+ // Always set this whether answered or not
32
+ if ( event.meta ) event.meta.target = event.target;
33
+ if ( event.answered ) return;
34
+ if ( !waitListMappings.get( rootNode ) ) waitListMappings.set( rootNode, new Set );
35
+ if ( event.type === 'contextrequest' && event.live ) {
36
+ waitListMappings.get( rootNode ).add( event );
37
+ } else if ( event.type === 'contextclaim' ) {
38
+ const claims = new Set;
39
+ waitListMappings.get( rootNode ).forEach( subscriptionEvent => {
40
+ if ( !event.target.contains( subscriptionEvent.target ) || !event.detail?.matchEvent?.( subscriptionEvent ) ) return;
41
+ waitListMappings.get( rootNode ).delete( subscriptionEvent );
42
+ claims.add( subscriptionEvent );
43
+ } );
44
+ if ( !waitListMappings.get( rootNode ).size ) waitListMappings.delete( rootNode );
45
+ return event.respondWith?.( claims );
46
+ }
47
+ };
48
+ rootNode.addEventListener( event.type, temp );
49
+ const returnValue = dispatchEvent.call( this, ...args );
50
+ rootNode.removeEventListener( event.type, temp );
51
+ return returnValue;
52
+ }
53
+ return dispatchEvent.call( this, ...args );
54
+ } } );
24
55
  window.webqit.DOMContexts = DOMContexts;
25
56
  window.webqit.DOMContext = DOMContext;
26
57
  window.webqit.DOMContextRequestEvent = _DOMContextRequestEvent();
@@ -219,7 +219,7 @@ function compileInlineBindings( config, str ) {
219
219
 
220
220
  if ( $import__.value && $iteratee__ ) {
221
221
  let $existing__ = new Map;
222
- this.querySelectorAll( '[${ config.attr.itemIndex }]' ).forEach( x => {
222
+ [ ...this.children ].filter( el => el.matches( '[${ config.attr.itemIndex }]' ) ).forEach( x => {
223
223
  $existing__.set( x.getAttribute( '${ config.attr.itemIndex }' ), x );
224
224
  } );
225
225
  ${ indices ? `let ${ indices } = -1;` : '' }