@webqit/oohtml 5.0.0 → 5.0.2

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.
@@ -10,18 +10,18 @@ import { _wq, _init, _splitOuter, _fromHash, _toHash, getInternalAttrInteraction
10
10
  *
11
11
  * @param Object $config
12
12
  */
13
- export default function init( $config = {} ) {
14
- const { config, window } = _init.call( this, 'namespaced-html', $config, {
13
+ export default function init($config = {}) {
14
+ const { config, window } = _init.call(this, 'namespaced-html', $config, {
15
15
  attr: { namespace: 'namespace', lid: 'id', },
16
16
  api: { namespace: 'namespace', },
17
17
  tokens: { lidrefPrefix: '~', lidrefSeparator: ':' },
18
18
  target: { className: ':target', eventName: ':target', scrolling: true },
19
- } );
20
- config.lidSelector = `[${ window.CSS.escape( config.attr.lid ) }]`;
21
- config.namespaceSelector = `[${ window.CSS.escape( config.attr.namespace ) }]`;
22
- window.webqit.DOMNamingContext = DOMNamingContext;
23
- exposeAPIs.call( window, config );
24
- realtime.call( window, config );
19
+ });
20
+ config.lidSelector = `[${window.CSS.escape(config.attr.lid)}]`;
21
+ config.namespaceSelector = `[${window.CSS.escape(config.attr.namespace)}]`;
22
+ window.webqit.DOMNamingContext = DOMNamingContext;
23
+ exposeAPIs.call(window, config);
24
+ realtime.call(window, config);
25
25
  }
26
26
 
27
27
  /**
@@ -31,17 +31,17 @@ export default function init( $config = {} ) {
31
31
  *
32
32
  * @return String
33
33
  */
34
- function lidUtil( config ) {
34
+ function lidUtil(config) {
35
35
  const { lidrefPrefix, lidrefSeparator, } = config.tokens;
36
36
  return {
37
- escape( str, mode = 1 ) { return [ ...str ].map( x => !/\w/.test( x ) ? ( mode === 2 ? `\\\\${ x }` : `\\${ x }` ) : x ).join( '' ); },
38
- lidrefPrefix( escapeMode = 0 ) { return escapeMode ? this.escape( lidrefPrefix, escapeMode ) : lidrefPrefix; },
39
- lidrefSeparator( escapeMode = 0 ) { return escapeMode ? this.escape( lidrefSeparator, escapeMode ) : lidrefSeparator; },
40
- isUuid( str, escapeMode = 0 ) { return str.startsWith( this.lidrefPrefix( escapeMode ) ) && str.includes( this.lidrefSeparator( escapeMode ) ); },
37
+ escape(str, mode = 1) { return [...str].map(x => !/\w/.test(x) ? (mode === 2 ? `\\\\${x}` : `\\${x}`) : x).join(''); },
38
+ lidrefPrefix(escapeMode = 0) { return escapeMode ? this.escape(lidrefPrefix, escapeMode) : lidrefPrefix; },
39
+ lidrefSeparator(escapeMode = 0) { return escapeMode ? this.escape(lidrefSeparator, escapeMode) : lidrefSeparator; },
40
+ isUuid(str, escapeMode = 0) { return str.startsWith(this.lidrefPrefix(escapeMode)) && str.includes(this.lidrefSeparator(escapeMode)); },
41
41
  //isLidref( str, escapeMode = 0 ) { return str.startsWith( this.lidrefPrefix( escapeMode ) ) && !str.includes( this.lidrefSeparator( escapeMode ) ); },
42
- toUuid( hash, lid, escapeMode = 0 ) { return hash.endsWith( '-root' ) ? lid : `${ this.lidrefPrefix( escapeMode ) }${ hash }${ this.lidrefSeparator( escapeMode ) }${ lid }`; },
43
- uuidToId( str, escapeMode = 0 ) { return this.isUuid( str ) ? str.split( this.lidrefSeparator( escapeMode ) )[ 1 ] : str; },
44
- uuidToLidref( str, escapeMode = 0 ) { return this.isUuid( str ) ? `${ this.lidrefPrefix( escapeMode ) }${ str.split( this.lidrefSeparator( escapeMode ) )[ 1 ] }` : str; },
42
+ toUuid(hash, lid, escapeMode = 0) { return hash.endsWith('-root') ? lid : `${this.lidrefPrefix(escapeMode)}${hash}${this.lidrefSeparator(escapeMode)}${lid}`; },
43
+ uuidToId(str, escapeMode = 0) { return this.isUuid(str) ? str.split(this.lidrefSeparator(escapeMode))[1] : str; },
44
+ uuidToLidref(str, escapeMode = 0) { return this.isUuid(str) ? `${this.lidrefPrefix(escapeMode)}${str.split(this.lidrefSeparator(escapeMode))[1]}` : str; },
45
45
  }
46
46
  }
47
47
 
@@ -55,59 +55,59 @@ function lidUtil( config ) {
55
55
  *
56
56
  * @return String
57
57
  */
58
- export function rewriteSelector( selectorText, namespaceUUID, scopeSelector = null, escapeMode = 0 ) {
58
+ export function rewriteSelector(selectorText, namespaceUUID, scopeSelector = null, escapeMode = 0) {
59
59
  const window = this, { webqit: { oohtml: { configs: { NAMESPACED_HTML: config } } } } = window;
60
- const $lidUtil = lidUtil( config );
60
+ const $lidUtil = lidUtil(config);
61
61
  // Match :scope and relative ID selector
62
- const regex = new RegExp( `${ scopeSelector ? `:scope|` : '' }#(${ $lidUtil.lidrefPrefix( escapeMode + 1 ) })?([\\w]+${ $lidUtil.lidrefSeparator( escapeMode + 1 ) })?((?:[\\w-]|\\\\.)+)`, 'g' );
62
+ const regex = new RegExp(`${scopeSelector ? `:scope|` : ''}#(${$lidUtil.lidrefPrefix(escapeMode + 1)})?([\\w]+${$lidUtil.lidrefSeparator(escapeMode + 1)})?((?:[\\w-]|\\\\.)+)`, 'g');
63
63
  // Parse potentially combined selectors individually and categorise into categories per whether they have :scope or not
64
- const [ cat1, cat2 ] = _splitOuter( selectorText, ',' ).reduce( ( [ cat1, cat2 ], selector ) => {
64
+ const [cat1, cat2] = _splitOuter(selectorText, ',').reduce(([cat1, cat2], selector) => {
65
65
  // The deal: match and replace
66
66
  let quotesMatch, hadScopeSelector;
67
- selector = selector.replace( regex, ( match, lidrefPrefixMatch, lidrefSeparatorMatch, id, index ) => {
68
- if ( !quotesMatch ) { // Lazy: stuff
67
+ selector = selector.replace(regex, (match, lidrefPrefixMatch, lidrefSeparatorMatch, id, index) => {
68
+ if (!quotesMatch) { // Lazy: stuff
69
69
  // Match strings between quotes (single or double) and use that qualify matches above
70
70
  // The string: String.raw`She said, "Hello, John. I\"m your friend." or "you're he're" 'f\'"j\'"f'jfjf`;
71
71
  // Should yield: `"Hello, John. I\\"m your friend."`, `"you're he're"`, `'f\\'"j\\'"f'`
72
- quotesMatch = [ ...selector.matchAll( /(["'])(?:(?=(\\?))\2.)*?\1/g ) ];
72
+ quotesMatch = [...selector.matchAll(/(["'])(?:(?=(\\?))\2.)*?\1/g)];
73
73
  }
74
74
  // Qualify match
75
- if ( quotesMatch.some( q => index > q.index && index + match.length < q.index + q[ 0 ].length ) ) return match;
75
+ if (quotesMatch.some(q => index > q.index && index + match.length < q.index + q[0].length)) return match;
76
76
  // Replace :scope
77
- if ( match === ':scope' ) {
77
+ if (match === ':scope') {
78
78
  hadScopeSelector = true;
79
79
  return scopeSelector;
80
80
  }
81
81
  const isLidref = lidrefPrefixMatch && !lidrefSeparatorMatch;
82
- const isUuid = lidrefPrefixMatch && lidrefSeparatorMatch;
83
- if ( isUuid ) {
84
- return `#${ $lidUtil.escape( match.replace( '#', '' ), 1 ) }`;
82
+ const isUuid = lidrefPrefixMatch && lidrefSeparatorMatch;
83
+ if (isUuid) {
84
+ return `#${$lidUtil.escape(match.replace('#', ''), 1)}`;
85
85
  }
86
86
  // Rewrite relative ID selector
87
- if ( isLidref ) {
88
- if ( config.attr.lid === 'id' && namespaceUUID && !namespaceUUID.endsWith( '-root' ) ) {
89
- return `#${ $lidUtil.toUuid( namespaceUUID, id, 1 ) }`;
87
+ if (isLidref) {
88
+ if (config.attr.lid === 'id' && namespaceUUID && !namespaceUUID.endsWith('-root')) {
89
+ return `#${$lidUtil.toUuid(namespaceUUID, id, 1)}`;
90
90
  }
91
91
  // Fallback to attr-based
92
92
  }
93
93
  // Rewrite absolute ID selector
94
94
  let rewrite;
95
- if ( config.attr.lid === 'id' ) {
96
- rewrite = `:is(#${ id },[id^="${ $lidUtil.lidrefPrefix( escapeMode ) }"][id$="${ $lidUtil.lidrefSeparator( escapeMode ) }${ id }"])`;
95
+ if (config.attr.lid === 'id') {
96
+ rewrite = `:is(#${id},[id^="${$lidUtil.lidrefPrefix(escapeMode)}"][id$="${$lidUtil.lidrefSeparator(escapeMode)}${id}"])`;
97
97
  } else {
98
- rewrite = `:is(#${ id },[${ window.CSS.escape( config.attr.lid ) }="${ id }"])`;
98
+ rewrite = `:is(#${id},[${window.CSS.escape(config.attr.lid)}="${id}"])`;
99
99
  }
100
- return isLidref ? `:is(${ rewrite }):not(${ scopeSelector ? scopeSelector + ' ' : '' }${ config.namespaceSelector } *)` : rewrite;
101
- } );
100
+ return isLidref ? `:is(${rewrite}):not(${scopeSelector ? scopeSelector + ' ' : ''}${config.namespaceSelector} *)` : rewrite;
101
+ });
102
102
  // Category 2 has :scope and category 1 does not
103
- return hadScopeSelector ? [ cat1, cat2.concat( selector ) ] : [ cat1.concat( selector ), cat2 ];
104
- }, [ [], [] ] );
103
+ return hadScopeSelector ? [cat1, cat2.concat(selector)] : [cat1.concat(selector), cat2];
104
+ }, [[], []]);
105
105
  // Do the upgrade
106
106
  let newSelectorText;
107
- if ( scopeSelector && cat1.length ) {
108
- newSelectorText = [ cat1.length > 1 ? `${ scopeSelector } :is(${ cat1.join( ', ' ) })` : `${ scopeSelector } ${ cat1[ 0 ] }`, cat2.join( ', ' ) ].filter( x => x ).join( ', ' );
107
+ if (scopeSelector && cat1.length) {
108
+ newSelectorText = [cat1.length > 1 ? `${scopeSelector} :is(${cat1.join(', ')})` : `${scopeSelector} ${cat1[0]}`, cat2.join(', ')].filter(x => x).join(', ');
109
109
  } else {
110
- newSelectorText = [ ...cat1, ...cat2 ].join( ', ' );
110
+ newSelectorText = [...cat1, ...cat2].join(', ');
111
111
  }
112
112
  return newSelectorText;
113
113
  }
@@ -117,17 +117,19 @@ export function rewriteSelector( selectorText, namespaceUUID, scopeSelector = nu
117
117
  *
118
118
  * @return Object
119
119
  */
120
- export function getOwnNamespaceObject( node ) {
120
+ export function getOwnNamespaceObject(node) {
121
121
  const window = this;
122
- if ( !_wq( node ).has( 'namespace' ) ) {
123
- const namespaceObj = Object.create( null );
124
- _wq( node ).set( 'namespace', namespaceObj );
125
- const isDocumentRoot = [ window.Document, window.ShadowRoot ].some( x => node instanceof x );
126
- Object.defineProperty( namespaceObj, Symbol.toStringTag, { get() {
127
- return isDocumentRoot ? 'RootNamespaceRegistry' : 'NamespaceRegistry';
128
- } } );
122
+ if (!_wq(node).has('namespace')) {
123
+ const namespaceObj = Object.create(null);
124
+ _wq(node).set('namespace', namespaceObj);
125
+ const isDocumentRoot = [window.Document, window.ShadowRoot].some(x => node instanceof x);
126
+ Object.defineProperty(namespaceObj, Symbol.toStringTag, {
127
+ get() {
128
+ return isDocumentRoot ? 'RootNamespaceRegistry' : 'NamespaceRegistry';
129
+ }
130
+ });
129
131
  }
130
- return _wq( node ).get( 'namespace' );
132
+ return _wq(node).get('namespace');
131
133
  }
132
134
 
133
135
  /**
@@ -136,10 +138,10 @@ export function getOwnNamespaceObject( node ) {
136
138
  *
137
139
  * @return Object
138
140
  */
139
- export function getOwnerNamespaceObject( node, forID = false ) {
141
+ export function getOwnerNamespaceObject(node, forID = false) {
140
142
  const window = this, { webqit: { oohtml: { configs: { NAMESPACED_HTML: config } } } } = window;
141
- const isDocumentRoot = [ window.Document, window.ShadowRoot ].some( x => node instanceof x );
142
- return getOwnNamespaceObject.call( window, isDocumentRoot ? node : ( ( forID ? node.parentNode : node )?.closest/*can be documentFragment when Shadow DOM*/?.( config.namespaceSelector ) || node.getRootNode() ) );
143
+ const isDocumentRoot = [window.Document, window.ShadowRoot].some(x => node instanceof x);
144
+ return getOwnNamespaceObject.call(window, isDocumentRoot ? node : ((forID ? node.parentNode : node)?.closest/*can be documentFragment when Shadow DOM*/?.(config.namespaceSelector) || node.getRootNode()));
143
145
  }
144
146
 
145
147
  /**
@@ -147,9 +149,9 @@ export function getOwnerNamespaceObject( node, forID = false ) {
147
149
  *
148
150
  * @return String
149
151
  */
150
- export function getNamespaceUUID( namespaceObj ) {
151
- const isDocumentRoot = Object.prototype.toString.call( namespaceObj ) === '[object RootNamespaceRegistry]';
152
- return ( _fromHash( namespaceObj ) || _toHash( namespaceObj ) ) + ( isDocumentRoot ? '-root' : '' );
152
+ export function getNamespaceUUID(namespaceObj) {
153
+ const isDocumentRoot = Object.prototype.toString.call(namespaceObj) === '[object RootNamespaceRegistry]';
154
+ return (_fromHash(namespaceObj) || _toHash(namespaceObj)) + (isDocumentRoot ? '-root' : '');
153
155
  }
154
156
 
155
157
  /**
@@ -159,18 +161,20 @@ export function getNamespaceUUID( namespaceObj ) {
159
161
  *
160
162
  * @return Void
161
163
  */
162
- function exposeAPIs( config ) {
163
- const window = this, { webqit: { Observer } } = window;
164
- // The Namespace API
165
- [ window.Document.prototype, window.Element.prototype, window.ShadowRoot.prototype ].forEach( prototype => {
166
- // No-conflict assertions
167
- const type = prototype === window.Document.prototype ? 'Document' : ( prototype === window.ShadowRoot.prototype ? 'ShadowRoot' : 'Element' );
168
- if ( config.api.namespace in prototype ) { throw new Error( `The ${ type } prototype already has a "${ config.api.namespace }" API!` ); }
169
- // Definitions
170
- Object.defineProperty( prototype, config.api.namespace, { get: function() {
171
- return Observer.proxy( getOwnNamespaceObject.call( window, this ) );
172
- } } );
173
- } );
164
+ function exposeAPIs(config) {
165
+ const window = this, { webqit: { Observer } } = window;
166
+ // The Namespace API
167
+ [window.Document.prototype, window.Element.prototype, window.ShadowRoot.prototype].forEach(prototype => {
168
+ // No-conflict assertions
169
+ const type = prototype === window.Document.prototype ? 'Document' : (prototype === window.ShadowRoot.prototype ? 'ShadowRoot' : 'Element');
170
+ if (config.api.namespace in prototype) { throw new Error(`The ${type} prototype already has a "${config.api.namespace}" API!`); }
171
+ // Definitions
172
+ Object.defineProperty(prototype, config.api.namespace, {
173
+ get: function () {
174
+ return Observer.proxy(getOwnNamespaceObject.call(window, this));
175
+ }
176
+ });
177
+ });
174
178
  }
175
179
 
176
180
  /**
@@ -180,222 +184,237 @@ function exposeAPIs( config ) {
180
184
  *
181
185
  * @return Void
182
186
  */
183
- function realtime( config ) {
184
- const window = this, { webqit: { Observer, realdom, oohtml: { configs }, DOMNamingContext } } = window;
185
-
187
+ function realtime(config) {
188
+ const window = this, { webqit: { Observer, realdom, oohtml: { configs }, DOMNamingContext } } = window;
189
+
186
190
  // ------------
187
191
  // APIS
188
- // ------------
192
+ // ------------
189
193
  // See https://wicg.github.io/aom/aria-reflection-explainer.html & https://github.com/whatwg/html/issues/3515 for the ARIA refelction properties idea
190
194
  // See https://www.w3.org/TR/wai-aria-1.1/#attrs_relationships for the relational ARIA attributes
191
- const idRefsAttrs = [ 'aria-owns', 'aria-controls', 'aria-labelledby', 'aria-describedby', 'aria-flowto', ];
192
- const idRefAttrs = [ 'for', 'list', 'form', 'aria-activedescendant', 'aria-details', 'aria-errormessage', ];
193
- const attrList = [ config.attr.lid, ...idRefsAttrs, ...idRefAttrs ];
194
- const relMap = { id: 'id'/* just in case it's in attrList */, for: 'htmlFor', 'aria-owns': 'ariaOwns', 'aria-controls': 'ariaControls', 'aria-labelledby': 'ariaLabelledBy', 'aria-describedby': 'ariaDescribedBy', 'aria-flowto': 'ariaFlowto', 'aria-activedescendant': 'ariaActiveDescendant', 'aria-details': 'ariaDetails', 'aria-errormessage': 'ariaErrorMessage' };
195
- const $lidUtil = lidUtil( config );
196
- const uuidsToLidrefs = ( node, attrName, getter ) => {
197
- if ( !getInternalAttrInteraction( node, attrName ) && _wq( node, 'attrOriginals' ).has( attrName ) ) {
198
- return _wq( node, 'attrOriginals' ).get( attrName );
195
+ const idRefsAttrs = ['aria-owns', 'aria-controls', 'aria-labelledby', 'aria-describedby', 'aria-flowto',];
196
+ const idRefAttrs = ['for', 'list', 'form', 'aria-activedescendant', 'aria-details', 'aria-errormessage', 'popovertarget'];
197
+ const attrList = [config.attr.lid, ...idRefsAttrs, ...idRefAttrs];
198
+ const relMap = { id: 'id'/* just in case it's in attrList */, for: 'htmlFor', 'aria-owns': 'ariaOwns', 'aria-controls': 'ariaControls', 'aria-labelledby': 'ariaLabelledBy', 'aria-describedby': 'ariaDescribedBy', 'aria-flowto': 'ariaFlowto', 'aria-activedescendant': 'ariaActiveDescendant', 'aria-details': 'ariaDetails', 'aria-errormessage': 'ariaErrorMessage', 'popovertarget': 'popoverTargetElement' };
199
+ const $lidUtil = lidUtil(config);
200
+ const uuidsToLidrefs = (node, attrName, getter) => {
201
+ if (!getInternalAttrInteraction(node, attrName) && _wq(node, 'attrOriginals').has(attrName)) {
202
+ return _wq(node, 'attrOriginals').get(attrName);
199
203
  }
200
204
  const value = getter();
201
- if ( getInternalAttrInteraction( node, attrName ) ) return value;
202
- return value && value.split( ' ' ).map( x => ( x = x.trim() ) && ( attrName === config.attr.lid ? $lidUtil.uuidToId : $lidUtil.uuidToLidref ).call( $lidUtil, x ) ).join( ' ' );
205
+ if (getInternalAttrInteraction(node, attrName)) return value;
206
+ return value && value.split(' ').map(x => (x = x.trim()) && (attrName === config.attr.lid ? $lidUtil.uuidToId : $lidUtil.uuidToLidref).call($lidUtil, x)).join(' ');
203
207
  };
204
208
 
205
209
  // Intercept getElementById()
206
- const getElementByIdDescr = Object.getOwnPropertyDescriptor( window.Document.prototype, 'getElementById' );
207
- Object.defineProperty( window.Document.prototype, 'getElementById', { ...getElementByIdDescr, value( id ) {
208
- return this.querySelector( `#${ id }` ); // To be rewritten at querySelector()
209
- } } );
210
+ const getElementByIdDescr = Object.getOwnPropertyDescriptor(window.Document.prototype, 'getElementById');
211
+ Object.defineProperty(window.Document.prototype, 'getElementById', {
212
+ ...getElementByIdDescr, value(id) {
213
+ return this.querySelector(`#${id}`); // To be rewritten at querySelector()
214
+ }
215
+ });
210
216
  // Intercept querySelector() and querySelectorAll()
211
- for ( const queryApi of [ 'querySelector', 'querySelectorAll' ] ) {
212
- for ( const nodeApi of [ window.Document, window.Element ] ) {
213
- const querySelectorDescr = Object.getOwnPropertyDescriptor( nodeApi.prototype, queryApi );
214
- Object.defineProperty( nodeApi.prototype, queryApi, { ...querySelectorDescr, value( selector ) {
215
- return querySelectorDescr.value.call( this, rewriteSelector.call( window, selector, getNamespaceUUID( getOwnNamespaceObject.call( window, this ) ) ) );
216
- } } );
217
+ for (const queryApi of ['querySelector', 'querySelectorAll']) {
218
+ for (const nodeApi of [window.Document, window.Element]) {
219
+ const querySelectorDescr = Object.getOwnPropertyDescriptor(nodeApi.prototype, queryApi);
220
+ Object.defineProperty(nodeApi.prototype, queryApi, {
221
+ ...querySelectorDescr, value(selector) {
222
+ return querySelectorDescr.value.call(this, rewriteSelector.call(window, selector, getNamespaceUUID(getOwnNamespaceObject.call(window, this))));
223
+ }
224
+ });
217
225
  }
218
226
  }
219
227
  // Intercept getAttribute()
220
- const getAttributeDescr = Object.getOwnPropertyDescriptor( window.Element.prototype, 'getAttribute' );
221
- Object.defineProperty( window.Element.prototype, 'getAttribute', { ...getAttributeDescr, value( attrName ) {
222
- const getter = () => getAttributeDescr.value.call( this, attrName );
223
- return attrList.includes( attrName ) && !_wq( this, 'lock' ).get( attrName ) ? uuidsToLidrefs( this, attrName, getter ) : getter();
224
- } } );
228
+ const getAttributeDescr = Object.getOwnPropertyDescriptor(window.Element.prototype, 'getAttribute');
229
+ Object.defineProperty(window.Element.prototype, 'getAttribute', {
230
+ ...getAttributeDescr, value(attrName) {
231
+ const getter = () => getAttributeDescr.value.call(this, attrName);
232
+ return attrList.includes(attrName) && !_wq(this, 'lock').get(attrName) ? uuidsToLidrefs(this, attrName, getter) : getter();
233
+ }
234
+ });
225
235
  // Hide implementation details on the Attr node too.
226
- const propertyDescr = Object.getOwnPropertyDescriptor( window.Attr.prototype, 'value' );
227
- Object.defineProperty( window.Attr.prototype, 'value', { ...propertyDescr, get() {
228
- const getter = () => propertyDescr.get.call( this );
229
- return attrList.includes( this.name ) ? uuidsToLidrefs( this.ownerElement, this.name, getter ) : getter();
230
- } } );
231
- const propertyDescr2 = Object.getOwnPropertyDescriptor( window.Node.prototype, 'nodeValue' );
232
- Object.defineProperty( window.Node.prototype, 'nodeValue', { ...propertyDescr2, get() {
233
- const getter = () => propertyDescr2.get.call( this );
234
- return this instanceof window.Attr && attrList.includes( this.name ) ? uuidsToLidrefs( this.ownerElement, this.name, getter ) : getter();
235
- } } );
236
+ const propertyDescr = Object.getOwnPropertyDescriptor(window.Attr.prototype, 'value');
237
+ Object.defineProperty(window.Attr.prototype, 'value', {
238
+ ...propertyDescr, get() {
239
+ const getter = () => propertyDescr.get.call(this);
240
+ return attrList.includes(this.name) ? uuidsToLidrefs(this.ownerElement, this.name, getter) : getter();
241
+ }
242
+ });
243
+ const propertyDescr2 = Object.getOwnPropertyDescriptor(window.Node.prototype, 'nodeValue');
244
+ Object.defineProperty(window.Node.prototype, 'nodeValue', {
245
+ ...propertyDescr2, get() {
246
+ const getter = () => propertyDescr2.get.call(this);
247
+ return this instanceof window.Attr && attrList.includes(this.name) ? uuidsToLidrefs(this.ownerElement, this.name, getter) : getter();
248
+ }
249
+ });
236
250
  // These APIs should return LIDREFS minus the hash part
237
- for ( const attrName of attrList ) {
238
- if ( !( attrName in relMap ) ) continue;
239
- const domApis = attrName === 'for' ? [ window.HTMLLabelElement, window.HTMLOutputElement ] : [ window.Element ];
240
- for ( const domApi of domApis ) {
241
- const propertyDescr = Object.getOwnPropertyDescriptor( domApi.prototype, relMap[ attrName ] );
242
- if ( !propertyDescr ) continue;
243
- Object.defineProperty( domApi.prototype, relMap[ attrName ], { ...propertyDescr, get() {
244
- const getter = () => propertyDescr.get.call( this, attrName );
245
- return uuidsToLidrefs( this, attrName, getter );
246
- } } );
251
+ for (const attrName of attrList) {
252
+ if (!(attrName in relMap)) continue;
253
+ const domApis = attrName === 'for' ? [window.HTMLLabelElement, window.HTMLOutputElement]
254
+ : (attrName === 'popovertarget' ? [window.HTMLButtonElement, window.HTMLInputElement] : [window.Element]);
255
+ for (const domApi of domApis) {
256
+ const propertyDescr = Object.getOwnPropertyDescriptor(domApi.prototype, relMap[attrName]);
257
+ if (!propertyDescr) continue;
258
+ Object.defineProperty(domApi.prototype, relMap[attrName], {
259
+ ...propertyDescr, get() {
260
+ const getter = () => propertyDescr.get.call(this, attrName);
261
+ return uuidsToLidrefs(this, attrName, getter);
262
+ }
263
+ });
247
264
  }
248
265
  }
249
- if ( config.attr.lid !== 'id' ) {
266
+ if (config.attr.lid !== 'id') {
250
267
  // Reflect the custom [config.attr.lid] attribute
251
- Object.defineProperty( window.Element.prototype, config.attr.lid, { configurable: true, enumerable: true, get() {
252
- return this.getAttribute( config.attr.lid );
253
- }, set( value ) {
254
- return this.setAttribute( config.attr.lid, value );
255
- } } );
268
+ Object.defineProperty(window.Element.prototype, config.attr.lid, {
269
+ configurable: true, enumerable: true, get() {
270
+ return this.getAttribute(config.attr.lid);
271
+ }, set(value) {
272
+ return this.setAttribute(config.attr.lid, value);
273
+ }
274
+ });
256
275
  }
257
276
 
258
277
  // ------------
259
- // LOCAL IDS & IDREFS
260
- // ------------
261
- const attrChange = ( entry, attrName, value, callback ) => {
262
- return internalAttrInteraction( entry, attrName, () => {
263
- if ( typeof value === 'function' ) value = value();
264
- return callback( value );
265
- } );
278
+ // LOCAL IDS & IDREFS
279
+ // ------------
280
+ const attrChange = (entry, attrName, value, callback) => {
281
+ return internalAttrInteraction(entry, attrName, () => {
282
+ if (typeof value === 'function') value = value();
283
+ return callback(value);
284
+ });
266
285
  };
267
- const setupBinding = ( entry, attrName, value, newNamespaceObj = null ) => {
268
- attrChange( entry, attrName, value, value => {
286
+ const setupBinding = (entry, attrName, value, newNamespaceObj = null) => {
287
+ attrChange(entry, attrName, value, value => {
269
288
  const isLidAttr = attrName === config.attr.lid;
270
- const namespaceObj = newNamespaceObj || getOwnerNamespaceObject.call( window, entry, isLidAttr );
271
- const namespaceUUID = getNamespaceUUID( namespaceObj );
272
- if ( isLidAttr ) {
273
- const id = $lidUtil.uuidToId( value );
274
- if ( Observer.get( namespaceObj, id ) !== entry ) {
275
- const uuid = $lidUtil.toUuid( namespaceUUID, id );
276
- if ( uuid !== value ) { entry.setAttribute( 'id', uuid ); }
277
- Observer.set( namespaceObj, id, entry );
289
+ const namespaceObj = newNamespaceObj || getOwnerNamespaceObject.call(window, entry, isLidAttr);
290
+ const namespaceUUID = getNamespaceUUID(namespaceObj);
291
+ if (isLidAttr) {
292
+ const id = $lidUtil.uuidToId(value);
293
+ if (Observer.get(namespaceObj, id) !== entry) {
294
+ const uuid = $lidUtil.toUuid(namespaceUUID, id);
295
+ if (uuid !== value) { entry.setAttribute('id', uuid); }
296
+ Observer.set(namespaceObj, id, entry);
278
297
  }
279
298
  } else {
280
- _wq( entry, 'attrOriginals' ).set( attrName, value ); // Save original before rewrite
281
- const newAttrValue = value.split( ' ' ).map( idref => ( idref = idref.trim() ) && $lidUtil.isUuid( idref ) ? idref : $lidUtil.toUuid( namespaceUUID, idref ) ).join( ' ' );
282
- entry.setAttribute( attrName, newAttrValue );
283
- _wq( namespaceObj ).set( 'idrefs', _wq( namespaceObj ).get( 'idrefs' ) || new Set );
284
- _wq( namespaceObj ).get( 'idrefs' ).add( entry );
299
+ _wq(entry, 'attrOriginals').set(attrName, value); // Save original before rewrite
300
+ const newAttrValue = value.split(' ').map(idref => (idref = idref.trim()) && $lidUtil.isUuid(idref) ? idref : $lidUtil.toUuid(namespaceUUID, idref)).join(' ');
301
+ entry.setAttribute(attrName, newAttrValue);
302
+ _wq(namespaceObj).set('idrefs', _wq(namespaceObj).get('idrefs') || new Set);
303
+ _wq(namespaceObj).get('idrefs').add(entry);
285
304
  }
286
- } );
305
+ });
287
306
  };
288
- const cleanupBinding = ( entry, attrName, oldValue, prevNamespaceObj = null ) => {
289
- attrChange( entry, attrName, oldValue, oldValue => {
307
+ const cleanupBinding = (entry, attrName, oldValue, prevNamespaceObj = null) => {
308
+ attrChange(entry, attrName, oldValue, oldValue => {
290
309
  const isLidAttr = attrName === config.attr.lid;
291
- const namespaceObj = prevNamespaceObj || getOwnerNamespaceObject.call( window, entry, isLidAttr );
292
- if ( isLidAttr ) {
293
- const id = $lidUtil.uuidToId( oldValue );
294
- if ( Observer.get( namespaceObj, id ) === entry ) {
295
- Observer.deleteProperty( namespaceObj, id );
310
+ const namespaceObj = prevNamespaceObj || getOwnerNamespaceObject.call(window, entry, isLidAttr);
311
+ if (isLidAttr) {
312
+ const id = $lidUtil.uuidToId(oldValue);
313
+ if (Observer.get(namespaceObj, id) === entry) {
314
+ Observer.deleteProperty(namespaceObj, id);
296
315
  }
297
316
  } else {
298
- const newAttrValue = _wq( entry, 'attrOriginals' ).get( attrName );// oldValue.split( ' ' ).map( lid => ( lid = lid.trim() ) && $lidUtil.uuidToLidref( lid ) ).join( ' ' );
299
- if ( entry.hasAttribute( attrName ) ) entry.setAttribute( attrName, newAttrValue );
300
- _wq( namespaceObj ).get( 'idrefs' )?.delete( entry );
317
+ const newAttrValue = _wq(entry, 'attrOriginals').get(attrName);// oldValue.split( ' ' ).map( lid => ( lid = lid.trim() ) && $lidUtil.uuidToLidref( lid ) ).join( ' ' );
318
+ if (entry.hasAttribute(attrName)) entry.setAttribute(attrName, newAttrValue);
319
+ _wq(namespaceObj).get('idrefs')?.delete(entry);
301
320
  }
302
- } );
321
+ });
303
322
  };
304
323
 
305
- // ------------
306
- // NAMESPACE
307
- // ------------
308
- realdom.realtime( window.document ).query( config.namespaceSelector, record => {
309
- const reAssociate = ( entry, attrName, oldNamespaceObj, newNamespaceObj ) => {
310
- if ( !entry.hasAttribute( attrName ) ) return;
311
- const attrValue = () => entry.getAttribute( attrName );
312
- cleanupBinding( entry, attrName, attrValue/* Current resolved value as-is */, oldNamespaceObj );
313
- if ( entry.isConnected ) { setupBinding( entry, attrName, _wq( entry, 'attrOriginals' ).get( attrName )/* Saved original value */ || attrValue/* Lest it's ID */, newNamespaceObj ); }
324
+ // ------------
325
+ // NAMESPACE
326
+ // ------------
327
+ realdom.realtime(window.document).query(config.namespaceSelector, record => {
328
+ const reAssociate = (entry, attrName, oldNamespaceObj, newNamespaceObj) => {
329
+ if (!entry.hasAttribute(attrName)) return;
330
+ const attrValue = () => entry.getAttribute(attrName);
331
+ cleanupBinding(entry, attrName, attrValue/* Current resolved value as-is */, oldNamespaceObj);
332
+ if (entry.isConnected) { setupBinding(entry, attrName, _wq(entry, 'attrOriginals').get(attrName)/* Saved original value */ || attrValue/* Lest it's ID */, newNamespaceObj); }
314
333
  };
315
- record.exits.forEach( entry => {
316
- if ( entry.isConnected ) {
317
- const namespaceObj = getOwnNamespaceObject.call( window, entry );
334
+ record.exits.forEach(entry => {
335
+ if (entry.isConnected) {
336
+ const namespaceObj = getOwnNamespaceObject.call(window, entry);
318
337
  // Detach ID and IDREF associations
319
- for ( const node of new Set( [ ...Object.values( namespaceObj ), ...( _wq( namespaceObj ).get( 'idrefs' ) || [] ) ] ) ) {
320
- for ( const attrName of attrList ) { reAssociate( node, attrName, namespaceObj ); }
338
+ for (const node of new Set([...Object.values(namespaceObj), ...(_wq(namespaceObj).get('idrefs') || [])])) {
339
+ for (const attrName of attrList) { reAssociate(node, attrName, namespaceObj); }
321
340
  }
322
341
  }
323
342
  // Detach ID associations
324
- const contextsApi = entry[ configs.CONTEXT_API.api.contexts ];
325
- const ctx = contextsApi.find( DOMNamingContext.kind );
343
+ const contextsApi = entry[configs.CONTEXT_API.api.contexts];
344
+ const ctx = contextsApi.find(DOMNamingContext.kind);
326
345
  // Detach namespace instance
327
- if ( ctx ) { contextsApi.detach( ctx ); }
328
- } );
329
- record.entrants.forEach( entry => {
346
+ if (ctx) { contextsApi.detach(ctx); }
347
+ });
348
+ record.entrants.forEach(entry => {
330
349
  // Claim ID and IDREF associations
331
350
  let newSuperNamespaceObj;
332
- const superNamespaceObj = getOwnerNamespaceObject.call( window, entry, true );
333
- for ( const node of new Set( [ ...Object.values( superNamespaceObj ), ...( _wq( superNamespaceObj ).get( 'idrefs' ) || [] ) ] ) ) {
334
- if ( ( newSuperNamespaceObj = getOwnerNamespaceObject.call( window, node, true ) ) === superNamespaceObj ) continue;
335
- for ( const attrName of attrList ) { reAssociate( node, attrName, superNamespaceObj, newSuperNamespaceObj ); }
351
+ const superNamespaceObj = getOwnerNamespaceObject.call(window, entry, true);
352
+ for (const node of new Set([...Object.values(superNamespaceObj), ...(_wq(superNamespaceObj).get('idrefs') || [])])) {
353
+ if ((newSuperNamespaceObj = getOwnerNamespaceObject.call(window, node, true)) === superNamespaceObj) continue;
354
+ for (const attrName of attrList) { reAssociate(node, attrName, superNamespaceObj, newSuperNamespaceObj); }
336
355
  }
337
356
  // Attach namespace instance
338
- const contextsApi = entry[ configs.CONTEXT_API.api.contexts ];
339
- if ( !contextsApi.find( DOMNamingContext.kind ) ) { contextsApi.attach( new DOMNamingContext ); }
340
- } );
341
- }, { id: 'namespace-html:namespace', live: true, subtree: 'cross-roots', timing: 'sync', staticSensitivity: true, eventDetails: true } );
357
+ const contextsApi = entry[configs.CONTEXT_API.api.contexts];
358
+ if (!contextsApi.find(DOMNamingContext.kind)) { contextsApi.attach(new DOMNamingContext); }
359
+ });
360
+ }, { id: 'namespace-html:namespace', live: true, subtree: 'cross-roots', timing: 'sync', staticSensitivity: true, eventDetails: true });
342
361
 
343
362
  // DOM realtime
344
- realdom.realtime( window.document ).query( `[${ attrList.map( attrName => window.CSS.escape( attrName ) ).join( '],[' ) }]`, record => {
363
+ realdom.realtime(window.document).query(`[${attrList.map(attrName => window.CSS.escape(attrName)).join('],[')}]`, record => {
345
364
  // We do some caching to prevent redundanct lookups
346
365
  const namespaceNodesToTest = { forID: new Map, forOther: new Map, };
347
- for ( const attrName of attrList ) {
366
+ for (const attrName of attrList) {
348
367
  // Point to the right cache
349
368
  const _namespaceNodesToTest = attrName === config.attr.lid ? namespaceNodesToTest.forID : namespaceNodesToTest.forOther;
350
- record.exits.forEach( entry => {
351
- if ( !entry.hasAttribute( attrName ) ) return;
369
+ record.exits.forEach(entry => {
370
+ if (!entry.hasAttribute(attrName)) return;
352
371
  // Point to the right namespace node
353
- let namespaceNodeToTest = _namespaceNodesToTest.get( entry );
354
- if ( typeof namespaceNodeToTest === 'undefined' ) {
355
- namespaceNodeToTest = ( attrName === config.attr.lid ? entry.parentNode : entry )?.closest/*can be documentFragment when Shadow DOM*/?.( config.namespaceSelector ) || entry.getRootNode().host;
356
- _namespaceNodesToTest.set( entry, namespaceNodeToTest );
372
+ let namespaceNodeToTest = _namespaceNodesToTest.get(entry);
373
+ if (typeof namespaceNodeToTest === 'undefined') {
374
+ namespaceNodeToTest = (attrName === config.attr.lid ? entry.parentNode : entry)?.closest/*can be documentFragment when Shadow DOM*/?.(config.namespaceSelector) || entry.getRootNode().host;
375
+ _namespaceNodesToTest.set(entry, namespaceNodeToTest);
357
376
  }
358
- if ( namespaceNodeToTest && !namespaceNodeToTest.isConnected ) return;
359
- cleanupBinding( entry, attrName, () => entry.getAttribute( attrName )/* Current resolved value as-is */ );
360
- } );
361
- record.entrants.forEach( entry => {
362
- if ( !entry.hasAttribute( attrName ) ) return;
363
- setupBinding( entry, attrName, () => entry.getAttribute( attrName )/* Raw value (as-is) that will be saved as original */ );
364
- } );
377
+ if (namespaceNodeToTest && !namespaceNodeToTest.isConnected) return;
378
+ cleanupBinding(entry, attrName, () => entry.getAttribute(attrName)/* Current resolved value as-is */);
379
+ });
380
+ record.entrants.forEach(entry => {
381
+ if (!entry.hasAttribute(attrName)) return;
382
+ setupBinding(entry, attrName, () => entry.getAttribute(attrName)/* Raw value (as-is) that will be saved as original */);
383
+ });
365
384
  }
366
385
  namespaceNodesToTest.forID.clear();
367
386
  namespaceNodesToTest.forOther.clear();
368
- }, { id: 'namespace-html:attrs', live: true, subtree: 'cross-roots', timing: 'sync' } );
387
+ }, { id: 'namespace-html:attrs', live: true, subtree: 'cross-roots', timing: 'sync' });
369
388
  // Attr realtime
370
- realdom.realtime( window.document, 'attr' ).observe( attrList, records => {
371
- for ( const record of records ) {
372
- if ( record.oldValue && record.value !== record.oldValue ) {
373
- cleanupBinding( record.target, record.name, record.oldValue/* Current resolved value as-is */ );
389
+ realdom.realtime(window.document, 'attr').observe(attrList, records => {
390
+ for (const record of records) {
391
+ if (record.oldValue && record.value !== record.oldValue) {
392
+ cleanupBinding(record.target, record.name, record.oldValue/* Current resolved value as-is */);
374
393
  }
375
- if ( record.value && record.value !== record.oldValue ) {
376
- setupBinding( record.target, record.name, record.value/* Raw value (as-is) that will be saved as original */ );
394
+ if (record.value && record.value !== record.oldValue) {
395
+ setupBinding(record.target, record.name, record.value/* Raw value (as-is) that will be saved as original */);
377
396
  }
378
397
  }
379
- }, { id: 'namespace-html:attr(attrs)', subtree: 'cross-roots', timing: 'sync', newValue: true, oldValue: true } );
398
+ }, { id: 'namespace-html:attr(attrs)', subtree: 'cross-roots', timing: 'sync', newValue: true, oldValue: true });
380
399
 
381
- // ------------
400
+ // ------------
382
401
  // TARGETS
383
- // ------------
402
+ // ------------
384
403
  let prevTarget;
385
404
  const activateTarget = () => {
386
- if ( !window.location.hash?.startsWith( `#${ $lidUtil.lidrefPrefix() }` ) ) return;
387
- const path = window.location.hash?.substring( `#${ $lidUtil.lidrefPrefix() }`.length ).split( '/' ).map( s => s.trim() ).filter( s => s ) || [];
388
- const currTarget = path.reduce( ( prev, segment ) => prev && prev[ config.api.namespace ][ segment ], window.document );
389
- if ( prevTarget && config.target.className ) { prevTarget.classList.toggle( config.target.className, false ); }
390
- if ( currTarget && currTarget !== window.document ) {
391
- if ( config.target.className ) { currTarget.classList.toggle( config.target.className, true ); }
392
- if ( config.target.eventName ) { currTarget.dispatchEvent( new window.CustomEvent( config.target.eventName ) ); }
393
- if ( config.target.scrolling && path.length > 1 ) { currTarget.scrollIntoView(); }
405
+ if (!window.location.hash?.startsWith(`#${$lidUtil.lidrefPrefix()}`)) return;
406
+ const path = window.location.hash?.substring(`#${$lidUtil.lidrefPrefix()}`.length).split('/').map(s => s.trim()).filter(s => s) || [];
407
+ const currTarget = path.reduce((prev, segment) => prev && prev[config.api.namespace][segment], window.document);
408
+ if (prevTarget && config.target.className) { prevTarget.classList.toggle(config.target.className, false); }
409
+ if (currTarget && currTarget !== window.document) {
410
+ if (config.target.className) { currTarget.classList.toggle(config.target.className, true); }
411
+ if (config.target.eventName) { currTarget.dispatchEvent(new window.CustomEvent(config.target.eventName)); }
412
+ if (config.target.scrolling && path.length > 1) { currTarget.scrollIntoView(); }
394
413
  prevTarget = currTarget;
395
414
  }
396
415
  };
397
416
  // "hash" realtime
398
- window.addEventListener( 'hashchange', activateTarget );
399
- realdom.ready( activateTarget );
417
+ window.addEventListener('hashchange', activateTarget);
418
+ realdom.ready(activateTarget);
400
419
  // ----------------
401
420
  }