@webqit/oohtml 1.10.3 → 2.0.0

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.
Files changed (53) hide show
  1. package/.gitignore +3 -3
  2. package/LICENSE +20 -20
  3. package/README.md +399 -396
  4. package/dist/context-api.js +2 -0
  5. package/dist/context-api.js.map +7 -0
  6. package/dist/html-imports.js +1 -2
  7. package/dist/html-imports.js.map +3 -3
  8. package/dist/html-modules.js +1 -2
  9. package/dist/html-modules.js.map +3 -3
  10. package/dist/main.js +26 -14
  11. package/dist/main.js.map +3 -3
  12. package/dist/namespaced-html.js +1 -2
  13. package/dist/namespaced-html.js.map +3 -3
  14. package/dist/scoped-js.js +27 -0
  15. package/dist/scoped-js.js.map +7 -0
  16. package/dist/state-api.js +1 -2
  17. package/dist/state-api.js.map +3 -3
  18. package/package.json +76 -76
  19. package/src/context-api/HTMLContext.js +158 -0
  20. package/src/context-api/HTMLContextManager.js +77 -0
  21. package/src/context-api/_ContextRequestEvent.js +26 -0
  22. package/src/context-api/index.js +53 -0
  23. package/src/{namespaced-html/browser-entry.js → context-api/targets.browser.js} +9 -9
  24. package/src/html-imports/_HTMLImportElement.js +216 -0
  25. package/src/html-imports/index.js +92 -557
  26. package/src/{browser-entry.js → html-imports/targets.browser.js} +10 -10
  27. package/src/html-modules/HTMLExportsManager.js +191 -0
  28. package/src/html-modules/_HTMLImportsContext.js +114 -0
  29. package/src/html-modules/index.js +133 -384
  30. package/src/{html-imports/browser-entry.js → html-modules/targets.browser.js} +9 -9
  31. package/src/index.js +34 -39
  32. package/src/namespaced-html/index.js +130 -144
  33. package/src/namespaced-html/targets.browser.js +10 -0
  34. package/src/scoped-js/index.js +382 -0
  35. package/src/scoped-js/targets.browser.js +10 -0
  36. package/src/state-api/index.js +55 -142
  37. package/src/state-api/targets.browser.js +10 -0
  38. package/src/{html-modules/browser-entry.js → targets.browser.js} +10 -10
  39. package/src/util.js +20 -180
  40. package/test/imports.test.js +194 -0
  41. package/test/index.js +119 -0
  42. package/test/modules.test.js +198 -0
  43. package/test/namespaced-html.test.js +50 -0
  44. package/test/scoped-js.js +57 -0
  45. package/test/state-api.test.js +34 -0
  46. package/test/test.html +69 -0
  47. package/dist/subscript.js +0 -15
  48. package/dist/subscript.js.map +0 -7
  49. package/src/state-api/browser-entry.js +0 -10
  50. package/src/subscript/Element.js +0 -103
  51. package/src/subscript/browser-entry.js +0 -10
  52. package/src/subscript/index.js +0 -70
  53. package/test/all.test.js +0 -0
@@ -1,144 +1,130 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import Observer from '@webqit/observer';
6
- import { _any, _from as _arrFrom } from '@webqit/util/arr/index.js';
7
- import { _isString, _internals } from '@webqit/util/js/index.js';
8
- import domInit from '@webqit/browser-pie/src/dom/index.js';
9
- import { config } from '../util.js';
10
-
11
- /**
12
- * ---------------------------
13
- * Namespaced HTML
14
- * ---------------------------
15
- */
16
-
17
- /**
18
- * @init
19
- *
20
- * @param Object config
21
- */
22
- export default function init( _config = {} ) {
23
-
24
- const WebQit = domInit.call( this );
25
- if ( _config.onDomReady ) {
26
- WebQit.DOM.ready( () => {
27
- init.call( this, { ..._config, onDomReady: false } );
28
- } );
29
- return;
30
- }
31
-
32
- const window = WebQit.window;
33
- const document = WebQit.window.document;
34
- const mutations = WebQit.DOM.mutations;
35
-
36
- const scopedIdInertContexts = [];
37
- const _meta = config.call(this, {
38
- attr: {
39
- namespace: 'namespace',
40
- id: 'id',
41
- },
42
- api: {
43
- namespace: 'namespace',
44
- },
45
- eagermode: true,
46
- }, _config.params );
47
-
48
- const getNamespaceObject = function(subject) {
49
- if (!_internals(subject, 'oohtml').has('namespace')) {
50
- const namespaceObject = Object.create(null);
51
- _internals(subject, 'oohtml').set('namespace', namespaceObject);
52
- if (Observer.link) {
53
- Observer.link(subject, _meta.get('api.namespace'), namespaceObject);
54
- }
55
- }
56
- return _internals(subject, 'oohtml').get('namespace');
57
- };
58
-
59
- const getPublicNamespaceObject = function(subject) {
60
- if (!_internals(subject, 'oohtml').has('publicNamespace')) {
61
- const namespaceObject = getNamespaceObject(subject);
62
- _internals(subject, 'oohtml').set('publicNamespace', !_meta.get('eagermode') ? namespaceObject : new Proxy(namespaceObject, {
63
- get(target, name) {
64
- if (_isString(name) && !namespaceObject[name]) {
65
- var node = _arrFrom(subject.querySelectorAll('[' + window.CSS.escape(_meta.get('attr.id')) + '="' + name + '"]')).filter(node => {
66
- var ownerRoot = node.parentNode.closest('[' + window.CSS.escape(_meta.get('attr.namespace')) + ']');
67
- if (subject === document) {
68
- // Only IDs without a scope actually belong to the document scope
69
- return !ownerRoot;
70
- }
71
- return ownerRoot === subject;
72
- })[0];
73
- if (node) {
74
- Observer.set(namespaceObject, name, node);
75
- }
76
- }
77
- return namespaceObject[name];
78
- }
79
- }));
80
- }
81
- return _internals(subject, 'oohtml').get('publicNamespace');
82
- };
83
-
84
- // ----------------------
85
- // Define the local "namespace" object
86
- // ----------------------
87
-
88
- if (_meta.get('api.namespace') in window.Element.prototype) {
89
- throw new Error('The "Element" class already has a "' + _meta.get('api.namespace') + '" property!');
90
- }
91
- Object.defineProperty(window.Element.prototype, _meta.get('api.namespace'), {
92
- get: function() {
93
- return getPublicNamespaceObject(this);
94
- }
95
- });
96
-
97
- // ----------------------
98
- // Define the global "namespace" object
99
- // ----------------------
100
-
101
- if (_meta.get('api.namespace') in document) {
102
- throw new Error('The "document" object already has a "' + _meta.get('api.namespace') + '" property!');
103
- }
104
- Object.defineProperty(document, _meta.get('api.namespace'), {
105
- get: function() {
106
- return getPublicNamespaceObject(document);
107
- }
108
- });
109
-
110
- // ----------------------
111
- // Capture scoped elements
112
- // ----------------------
113
-
114
- mutations.onPresent('[' + window.CSS.escape(_meta.get('attr.id')) + ']', el => {
115
- var elOohtmlObj = _internals(el, 'oohtml');
116
- if (elOohtmlObj.get('idAlreadyBeingWatched') || _any(scopedIdInertContexts, inertContext => el.closest(inertContext))) {
117
- return;
118
- }
119
- var scopedId = el.getAttribute(_meta.get('attr.id')),
120
- ownerRoot = el.parentNode.closest('[' + window.CSS.escape(_meta.get('attr.namespace')) + ']');
121
- if (!ownerRoot) {
122
- ownerRoot = document;
123
- }
124
- var namespaceObject = getNamespaceObject(ownerRoot);
125
- if (namespaceObject[scopedId] !== el) {
126
- Observer.set(namespaceObject, scopedId, el);
127
- }
128
- // new permanent watch
129
- elOohtmlObj.set('idAlreadyBeingWatched', true);
130
- mutations.onPresenceChange(el, (els, presence) => {
131
- if (presence) {
132
- // ONLY if I am not currently the one in place
133
- if (namespaceObject[scopedId] !== el) {
134
- Observer.set(namespaceObject, scopedId, el);
135
- }
136
- } else {
137
- // ONLY if I am still the one in place
138
- if (namespaceObject[scopedId] === el) {
139
- Observer.deleteProperty(namespaceObject, scopedId);
140
- }
141
- }
142
- });
143
- });
144
- };
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import Observer from '@webqit/observer';
6
+ import wqDom from '@webqit/dom';
7
+ import { _ } from '../util.js';
8
+
9
+ /**
10
+ * @init
11
+ *
12
+ * @param Object $params
13
+ */
14
+ export default function init( $params = {} ) {
15
+ const window = this, dom = wqDom.call( window );
16
+ // -------
17
+ const params = dom.meta( 'oohtml' ).copyWithDefaults( $params, {
18
+ attr: { namespace: 'namespace', id: 'data-id', },
19
+ api: { namespace: 'namespace', },
20
+ staticsensitivity: true,
21
+ eagermode: true,
22
+ } );
23
+ params.idSelector = `[${ window.CSS.escape( params.attr.id ) }]`;
24
+ params.namespaceSelector = `[${ window.CSS.escape( params.attr.namespace ) }]`;
25
+ // -------
26
+ exposeNamespaceObjects.call( this, params );
27
+ realtime.call( this, params );
28
+ }
29
+
30
+ /**
31
+ * Exposes Namespaced HTML with native APIs.
32
+ *
33
+ * @param Object params
34
+ *
35
+ * @return Void
36
+ */
37
+ function exposeNamespaceObjects( params ) {
38
+ const window = this;
39
+ // Assertions
40
+ if ( params.api.namespace in window.document ) { throw new Error( `document already has a "${ params.api.namespace }" property!` ); }
41
+ if ( params.api.namespace in window.Element.prototype ) { throw new Error( `The "Element" class already has a "${ params.api.namespace }" property!` ); }
42
+ // Definitions
43
+ Object.defineProperty( window.document, params.api.namespace, { get: function() {
44
+ return Observer.proxy( getNamespaceObject.call( window, window.document, params ) );
45
+ } });
46
+ Object.defineProperty( window.Element.prototype, params.api.namespace, { get: function() {
47
+ return Observer.proxy( getNamespaceObject.call( window, this, params ) );
48
+ } } );
49
+ }
50
+
51
+ /**
52
+ * Returns the "namespace" object associated with the given node.
53
+ *
54
+ * @param Element node
55
+ *
56
+ * @return Object
57
+ */
58
+ function getNamespaceObject( node, params ) {
59
+ const window = this;
60
+ if ( !_( node ).has( 'namespace' ) ) {
61
+ const namespaceObj = Object.create( null );
62
+ Observer.intercept( namespaceObj, 'get', ( event, receiver, next ) => {
63
+ if ( Observer.has( namespaceObj, event.key ) || !params.eagermode ) return next();
64
+ const selector = `[${ window.CSS.escape( params.attr.id ) }="${ event.key }"]`;
65
+ const resultNode = Array.from( node.querySelectorAll( selector ) ).filter( idNode => {
66
+ const ownerRoot = idNode.parentNode.closest( params.namespaceSelector );
67
+ if ( node === window.document ) {
68
+ // Only IDs without a scope actually belong to the document scope
69
+ return !ownerRoot;
70
+ }
71
+ return ownerRoot === node;
72
+ } )[ 0 ];
73
+ if ( resultNode ) Observer.set( namespaceObj, event.key, resultNode );
74
+ return next();
75
+ } );
76
+ _( node ).set( 'namespace', namespaceObj );
77
+ }
78
+ return _( node ).get( 'namespace' );
79
+ }
80
+
81
+ /**
82
+ * Performs realtime capture of elements and builds their relationships.
83
+ *
84
+ * @param Object params
85
+ *
86
+ * @return Void
87
+ */
88
+ function realtime( params ) {
89
+ const window = this, { dom } = window.wq;
90
+ // ----------------
91
+ const handle = ( target, entry, incoming ) => {
92
+ const identifier = entry.getAttribute( params.attr.id );
93
+ const ownerRoot = target.closest( params.namespaceSelector ) || _( entry ).get( 'ownerNamespace' ) || window.document;
94
+ const namespaceObj = getNamespaceObject.call( window, ownerRoot, params );
95
+ if ( incoming ) {
96
+ if ( Observer.get( namespaceObj, identifier ) !== entry ) {
97
+ _( entry ).set( 'ownerNamespace', ownerRoot );
98
+ Observer.set( namespaceObj, identifier, entry );
99
+ }
100
+ } else if ( Observer.get( namespaceObj, identifier ) === entry ) {
101
+ _( entry ).delete( 'ownerNamespace' );
102
+ Observer.deleteProperty( namespaceObj, identifier );
103
+ }
104
+ };
105
+ dom.realtime( window.document ).observe( params.idSelector, record => {
106
+ record.entrants.forEach( entry => handle( record.target, entry, true ) );
107
+ record.exits.forEach( entry => handle( record.target, entry, false ) );
108
+ }, { subtree: true, timing: 'sync', staticSensitivity: params.staticsensitivity } );
109
+ // ----------------
110
+ if ( params.staticsensitivity ) {
111
+ dom.realtime( window.document, 'attr' ).observe( params.namespaceSelector, record => {
112
+ const ownerRoot = record.target.parentNode?.closest( params.namespaceSelector ) || _( record.target ).get( 'ownerNamespace' ) || window.document;
113
+ const ownerRootNamespaceObj = getNamespaceObject.call( window, ownerRoot, params );
114
+ const namespaceObj = getNamespaceObject.call( window, record.target, params );
115
+ if ( record.target.matches( params.namespaceSelector ) ) {
116
+ for ( const [ key, entry ] of Object.entries( ownerRootNamespaceObj ) ) {
117
+ if ( !record.target.contains( entry.parentNode ) ) continue;
118
+ Observer.deleteProperty( ownerRootNamespaceObj, key );
119
+ Observer.set( namespaceObj, key, entry );
120
+ }
121
+ } else {
122
+ for ( const [ key, entry ] of Object.entries( namespaceObj ) ) {
123
+ Observer.deleteProperty( namespaceObj, key );
124
+ Observer.set( ownerRootNamespaceObj, key, entry );
125
+ }
126
+ }
127
+ }, { subtree: true, timing: 'sync' } );
128
+ }
129
+ // ----------------
130
+ }
@@ -0,0 +1,10 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import init from './index.js';
6
+
7
+ /**
8
+ * @init
9
+ */
10
+ init.call( window );