@webqit/oohtml 1.10.3 → 2.0.1

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 (55) 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/{html-modules/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/{namespaced-html/browser-entry.js → html-imports/targets.browser.js} +9 -9
  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 +135 -384
  30. package/src/html-modules/targets.browser.js +13 -0
  31. package/src/index.js +34 -39
  32. package/src/namespaced-html/index.js +132 -144
  33. package/src/namespaced-html/targets.browser.js +13 -0
  34. package/src/scoped-js/index.js +389 -0
  35. package/src/scoped-js/targets.browser.js +13 -0
  36. package/src/state-api/index.js +57 -142
  37. package/src/state-api/targets.browser.js +13 -0
  38. package/src/targets.browser.js +13 -0
  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.test2.js +58 -0
  45. package/test/state-api.test.js +34 -0
  46. package/test/test.html +71 -0
  47. package/dist/subscript.js +0 -15
  48. package/dist/subscript.js.map +0 -7
  49. package/src/browser-entry.js +0 -10
  50. package/src/html-imports/browser-entry.js +0 -10
  51. package/src/state-api/browser-entry.js +0 -10
  52. package/src/subscript/Element.js +0 -103
  53. package/src/subscript/browser-entry.js +0 -10
  54. package/src/subscript/index.js +0 -70
  55. package/test/all.test.js +0 -0
@@ -0,0 +1,389 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import { parserParams, compilerParams, runtimeParams } from '@webqit/subscript/src/params.js';
6
+ import SubscriptFunction from '@webqit/subscript/src/SubscriptFunctionLite.js';
7
+ import Observer from '@webqit/observer';
8
+ import wqDom from '@webqit/dom';
9
+
10
+ /**
11
+ * @init
12
+ *
13
+ * @param Object $params
14
+ */
15
+ export default function init( $params = {} ) {
16
+ const window = this, dom = wqDom.call( window );
17
+ // -------
18
+ const params = dom.meta( 'oohtml' ).copyWithDefaults( $params, {
19
+ script: { retention: 'retain', mimeType: '' },
20
+ parserParams: { ...parserParams, allowReturnOutsideFunction: false, allowSuperOutsideMethod: false, },
21
+ compilerParams: { ...compilerParams, globalsNoObserve: [ ...compilerParams.globalsNoObserve, 'alert' ] },
22
+ runtimeParams: { ...runtimeParams, apiVersion: 2, },
23
+ } );
24
+ params.scriptSelector = ( Array.isArray( params.script.mimeType ) ? params.script.mimeType : [ params.script.mimeType ] ).reduce( ( selector, mm ) => {
25
+ const qualifier = mm ? `[type=${ window.CSS.escape( mm ) }]` : '';
26
+ return selector.concat( `script${ qualifier }[scoped],script${ qualifier }[contract]` );
27
+ }, [] ).join( ',' );
28
+ // -------
29
+ realtime.call( this, params );
30
+ }
31
+
32
+ export { Observer }
33
+
34
+ /**
35
+ * Performs realtime capture of elements and builds their relationships.
36
+ *
37
+ * @param Object params
38
+ *
39
+ * @return Void
40
+ */
41
+ function realtime( params ) {
42
+ const window = this, { dom } = window.wq;
43
+ // ---
44
+ window.wq.transformCache = new Map;
45
+ window.wq.compileCache = [ new Map, new Map, ];
46
+ // ---
47
+ const handled = () => {};
48
+ dom.realtime( window.document ).observe( params.scriptSelector, record => {
49
+ record.entrants.forEach( script => {
50
+ if ( 'contract' in script ) return handled( script );
51
+ Object.defineProperty( script, 'contract', { value: script.hasAttribute( 'contract' ) } );
52
+ if ( 'scoped' in script ) return handled( script );
53
+ Object.defineProperty( script, 'scoped', { value: script.hasAttribute( 'scoped' ) } );
54
+ // ---
55
+ const thisContext = script.scoped ? record.target : ( script.type === 'module' ? undefined : window );
56
+ compile.call( this, script, thisContext, params );
57
+ } );
58
+ }, { subtree: true, timing: 'intercept', generation: 'entrants' } );
59
+ // ---
60
+ window.wq.exec = ( execHash ) => {
61
+ const exec = fromHash( execHash );
62
+ if ( !exec ) throw new Error( `Argument must be a valid exec hash.` );
63
+ const { script, compiledScript, thisContext } = exec;
64
+ if ( thisContext instanceof window.Element && script.scoped ) {
65
+ if ( !thisContext.scripts ) { Object.defineProperty( thisContext, 'scripts', { value: new Set } ); }
66
+ thisContext.scripts.add( script );
67
+ }
68
+ switch ( params.script.retention ) {
69
+ case 'dispose':
70
+ script.remove();
71
+ break;
72
+ case 'hidden':
73
+ script.textContent = `"hidden"`;
74
+ break;
75
+ default:
76
+ script.textContent = compiledScript.function.originalSource;
77
+ }
78
+ return execute.call( this, compiledScript, thisContext, script );
79
+ };
80
+ }
81
+
82
+ // ------------------
83
+ // Script runner
84
+ export function execute( compiledScript, thisContext, script ) {
85
+ if ( !compiledScript.function ) throw new Error( `Input script must already be compiled!` );
86
+ // Execute...
87
+ const returnValue = compiledScript.function.call( thisContext );
88
+ if ( script.contract ) {
89
+ // Rerending processes,,,
90
+ Object.defineProperty( script, 'rerender', { value: ( ...args ) => _await( returnValue, ( [ , rerender ] ) => rerender( ...args ) ) } );
91
+ _await( script.properties, properties => {
92
+ const getPaths = ( base, record_s ) => ( Array.isArray( record_s ) ? record_s : [ record_s ] ).map( record => [ ...base, ...( record.path || [ record.key ] ) ] );
93
+ properties.processes = properties.dependencies.map( path => {
94
+ if ( path[ 0 ] === 'this' ) {
95
+ return Observer.deep( thisContext, path.slice( 1 ), Observer.observe, record_s => {
96
+ script.rerender( ...getPaths( [ 'this' ], record_s ) );
97
+ } );
98
+ }
99
+ return Observer.deep( globalThis, path, Observer.observe, record_s => {
100
+ script.rerender( ...getPaths( [], record_s ) );
101
+ } );
102
+ } );
103
+ } );
104
+ }
105
+ const window = this, { dom } = window.wq;
106
+ dom.realtime( window.document ).observe( thisContext, () => {
107
+ if ( script.contract ) {
108
+ // Rerending processes,,,
109
+ _await( script.properties, properties => {
110
+ properties.processes.forEach( process => process.abort() );
111
+ } );
112
+ }
113
+ thisContext.dispatchEvent( new window.CustomEvent( 'remove' ) );
114
+ thisContext.scripts.delete( script );
115
+ }, { subtree: true, timing: 'sync', generation: 'exits' } );
116
+ return script;
117
+ }
118
+
119
+ // ------------------
120
+ // JAVASCRIPT::[SCOPED|CONTRACT]
121
+ export function compile( script, thisContext, params = {} ) {
122
+ const wq = this.wq, cache = wq.compileCache[ script.contract ? 0 : 1 ];
123
+ const sourceHash = toHash( script.textContent );
124
+ // Script instances are parsed only once
125
+ let source = script.textContent, compiledScript;
126
+ if ( !( compiledScript = cache.get( sourceHash ) ) ) {
127
+ // Are there "import" (and "await") statements? Then, we need to rewrite that
128
+ let imports = [], meta = {};
129
+ let targetKeywords = [];
130
+ if ( script.type === 'module' ) targetKeywords.push( 'import ' );
131
+ if ( script.type === 'module' && !script.contract ) targetKeywords.push( 'await ' );
132
+ if ( targetKeywords.length && ( new RegExp( targetKeywords.join( '|' ) ) ).test( source ) ) {
133
+ [ imports, source, meta ] = parse( source );
134
+ if ( imports.length ) {
135
+ source = `\n\t${ rewriteImportStmts( imports ).join( `\n\t` ) }\n\t${ source }\n`;
136
+ }
137
+ }
138
+ // Let's obtain a material functions
139
+ let _Function;
140
+ if ( script.contract ) {
141
+ let { parserParams, compilerParams, runtimeParams } = params;
142
+ parserParams = { ...parserParams, allowAwaitOutsideFunction: script.type === 'module' };
143
+ runtimeParams = { ...runtimeParams, async: script.type === 'module' };
144
+ _Function = SubscriptFunction( source, { compilerParams, parserParams, runtimeParams, } );
145
+ Object.defineProperty( script, 'properties', { configurable: true, value: SubscriptFunction.inspect( _Function, 'properties' ) } );
146
+ } else {
147
+ const isAsync = script.type === 'module'//meta.topLevelAwait || imports.length;
148
+ const _FunctionConstructor = isAsync ? Object.getPrototypeOf( async function() {} ).constructor : Function;
149
+ _Function = params.runtimeParams?.compileFunction
150
+ ? params.runtimeParams.compileFunction( source )
151
+ : new _FunctionConstructor( source );
152
+ Object.defineProperty( _Function, 'originalSource', { configurable: true, value: script.textContent } );
153
+ }
154
+ // Save material function to compile cache
155
+ compiledScript = Object.defineProperty( script.cloneNode(), 'function', { value: _Function } );
156
+ script.scoped && Object.defineProperty( compiledScript, 'scoped', Object.getOwnPropertyDescriptor( script, 'scoped') );
157
+ script.contract && Object.defineProperty( compiledScript, 'contract', Object.getOwnPropertyDescriptor( script, 'contract') );
158
+ cache.set( sourceHash, compiledScript );
159
+ }
160
+ const execHash = toHash( { script, compiledScript, thisContext } );
161
+ script.textContent = `wq.exec('${ execHash }');`;
162
+ }
163
+
164
+ // ------------------
165
+ // Match import statements
166
+ // and detect top-level await
167
+ export function parse( source ) {
168
+ const [ tokens, meta ] = tokenize( source, ( $tokens, event, char, meta, i, isLastChar ) => {
169
+
170
+ if ( event === 'start-enclosure' && char === '{' && !meta.openAsync?.type && meta.openEnclosures.length === meta.openAsync?.scopeId ) {
171
+ meta.openAsync.type = 'block';
172
+ } else if ( event === 'end-enclosure' && char === '}' && meta.openAsync?.type === 'block' && meta.openEnclosures.length === meta.openAsync.scopeId ) {
173
+ meta.openAsync = null;
174
+ } else if ( event === 'start-quote' && !meta.openEnclosures.length && [ 'starting', 'from' ].includes( meta.openImport ) ) {
175
+ meta.openImport = 'url';
176
+ } else if ( event === 'end-quote' && meta.openImport === 'url' ) {
177
+ meta.openImport = 'closing';
178
+ } else if ( event === 'char' ) {
179
+
180
+ if ( meta.openImport === 'closing' && (
181
+ char === ';'/* explicit */ || ![ ' ', `\n` ].includes( char )/* implicit */ || isLastChar
182
+ ) ) {
183
+ if ( char === ';' || isLastChar ) {
184
+ $tokens[ 0 ] += char;
185
+ $tokens.unshift( '' );
186
+ } else { $tokens.unshift( char ); }
187
+ meta.openImport = null;
188
+ return false;
189
+ }
190
+
191
+ let remainder = source.substring( i - 1 );
192
+
193
+ if ( !meta.openImport && /^[\W]?import[ ]*[^\(]/.test( remainder ) ) {
194
+ meta.openImport = 'starting';
195
+ $tokens.unshift( '' );
196
+ return 6;
197
+ }
198
+ if ( meta.openImport === 'starting' && /^[\W]?from /.test( remainder ) ) {
199
+ meta.openImport = 'from';
200
+ return 4;
201
+ }
202
+ if ( !meta.openAsync ) {
203
+ if ( /^[\W]?async /.test( remainder ) ) {
204
+ meta.openAsync = { scopeId: meta.openEnclosures.length };
205
+ return 5;
206
+ }
207
+ if ( /^[\W]?await /.test( remainder ) ) {
208
+ meta.topLevelAwait = true;
209
+ return 5;
210
+ }
211
+ }
212
+ if ( meta.openAsync ) {
213
+ if ( !meta.openAsync.type && /.?\=\>[ ]*?\{/.test( remainder ) ) {
214
+ meta.openAsync.type = 'inline-arrow';
215
+ } else if ( meta.openAsync.type === 'inline-arrow' && [ `\n`, ';' ].includes( char ) && meta.openEnclosures.length === meta.openAsync.scopeId ) {
216
+ meta.openAsync = null;
217
+ }
218
+ }
219
+
220
+ }
221
+
222
+ } );
223
+ // Hoist all import statements
224
+ let imports = [], body = '', _str;
225
+ for ( const str of tokens.reverse() ) {
226
+ if ( ( _str = str.trim() ).startsWith( 'import ' ) ) {
227
+ imports.push( str );
228
+ } else if ( _str ) { body += str; }
229
+ }
230
+
231
+ return [ imports, body, meta ];
232
+ }
233
+
234
+ // ------------------
235
+ // Rewrite import statements
236
+ export function rewriteImportStmts( imports ) {
237
+ const importSpecs = [], importPromises = [];
238
+ imports.forEach( ( $import, i ) => {
239
+ $import = parseImportStmt( $import );
240
+ // Identify whole imports and individual imports
241
+ const [ wholeImport, individualImports ] = $import.items.reduce( ( [ whole, parts ], item ) => {
242
+ return item.id === '*' ? [ item.alias, parts ] : [ whole, parts.concat( item ) ];
243
+ }, [ null, [] ] );
244
+ if ( wholeImport ) {
245
+ // const main = await import("url");
246
+ importSpecs.push( `const ${ wholeImport } = __$imports$__[${ i }];` );
247
+ }
248
+ if ( individualImports.length ) {
249
+ // const { aa: bb, cc } = await import("url");
250
+ const individualImportsSpec = individualImports.map( item => `${ item.id }${ item.id !== item.alias ? `: ${ item.alias }` : '' }` ).join( ', ' );
251
+ importSpecs.push( `const { ${ individualImportsSpec } } = __$imports$__[${ i }];` );
252
+ }
253
+ importPromises.push( `import("${ $import.url }")` );
254
+ } );
255
+ return [
256
+ `\n\tconst __$imports$__ = await Promise.all([\n\t\t${ importPromises.join( `,\n\t\t` ) }\n\t]);`,
257
+ importSpecs.join( `\n\t` ),
258
+ ];
259
+ }
260
+
261
+ // ------------------
262
+ // Parse import statements
263
+ export function parseImportStmt( str ) {
264
+ const getUrl = str => {
265
+ let quo = /^[`'"]/.exec( str );
266
+ return quo && str.substring( 1, str.lastIndexOf( quo[ 0 ] ) );
267
+ }
268
+ let $import = { items: [ { id: '' } ] }, _str = str.replace( 'import', '' ).trim();
269
+ if ( !( $import.url = getUrl( _str ) ) ) {
270
+ tokenize( _str, ( $tokens, event, char, meta, i, isLastChar ) => {
271
+ if ( [ 'start-quote', 'ongoing-quote', 'end-quote', 'char' ].includes( event ) ) {
272
+ if ( $import.url ) return;
273
+ if ( !meta.openQuote ) {
274
+ let remainder = _str.substring( i );
275
+ if ( char === ',' ) {
276
+ $import.items.unshift( { id: '' } );
277
+ return;
278
+ }
279
+ if ( remainder.startsWith( ' as ' ) ) {
280
+ $import.items[ 0 ].alias = '';
281
+ return 3;
282
+ }
283
+ if ( remainder.startsWith( ' from ' ) ) {
284
+ $import.url = getUrl( remainder.replace( 'from', '' ).trim() );
285
+ return remainder.length;
286
+ }
287
+ }
288
+ if ( 'alias' in $import.items[ 0 ] ) {
289
+ $import.items[ 0 ].alias += char;
290
+ } else {
291
+ $import.items[ 0 ].id += char;
292
+ if ( meta.openEnclosures.length ) {
293
+ $import.items[ 0 ].enclosed = true;
294
+ }
295
+ }
296
+ }
297
+ } );
298
+ }
299
+ $import.items = $import.items
300
+ .map( item => ( {
301
+ id: item.id && !item.alias && !item.enclosed ? 'default' : item.id.trim(),
302
+ alias: item.alias ? item.alias.trim() : item.id.trim(),
303
+ } ) )
304
+ .filter( item => item.id )
305
+ .reverse();
306
+ return $import;
307
+ }
308
+
309
+ // ------------------
310
+ // Token JavaScript source
311
+ export function tokenize( source, _callback ) {
312
+ const lastI = source.length - 1;
313
+ return [ ...source ].reduce( ( [ $tokens, meta, skip ], char, i ) => {
314
+
315
+ if ( skip ) {
316
+ $tokens[ 0 ] += char;
317
+ return [ $tokens, meta, --skip ];
318
+ }
319
+ let callbackReturn;
320
+
321
+ if ( meta.openQuote || meta.openComment ) {
322
+ if ( char === meta.openQuote ) {
323
+ meta.openQuote = null;
324
+ callbackReturn = _callback( $tokens, 'end-quote', char, meta, i, i === lastI );
325
+ } else if ( meta.openQuote ) {
326
+ callbackReturn = _callback( $tokens, 'ongoing-quote', char, meta, i, i === lastI );
327
+ } else if ( meta.openComment ) {
328
+ if ( ( meta.openComment === '//' && char === `\n` ) || ( meta.openComment === '/*' && $tokens[ 0 ].substr( -1 ) === '*' && char === '/' ) ) {
329
+ meta.openComment = null;
330
+ callbackReturn = _callback( $tokens, 'end-comment', char, meta, i, i === lastI );
331
+ }
332
+ }
333
+ if ( callbackReturn !== false ) {
334
+ $tokens[ 0 ] += char;
335
+ }
336
+ return [ $tokens, meta, typeof callbackReturn === 'number' ? callbackReturn : skip ];
337
+ }
338
+
339
+ let enclosure;
340
+ if ( enclosure = [ '()', '{}', '[]' ].filter( pair => char === pair[ 0 ] )[ 0 ] ) {
341
+ callbackReturn = _callback( $tokens, 'start-enclosure', char, meta, i, i === lastI );
342
+ meta.openEnclosures.unshift( enclosure );
343
+ } else if ( meta.openEnclosures.length && char === meta.openEnclosures[ 0 ][ 1 ] ) {
344
+ meta.openEnclosures.shift();
345
+ callbackReturn = _callback( $tokens, 'end-enclosure', char, meta, i, i === lastI );
346
+ } else if ( [ '"', "'", "`" ].includes( char ) ) {
347
+ callbackReturn = _callback( $tokens, 'start-quote', char, meta, i, i === lastI );
348
+ meta.openQuote = char;
349
+ } else if ( !meta.openComment && [ '/*', '//' ].includes( source.substr( i, 2 ) ) ) {
350
+ callbackReturn = _callback( $tokens, 'start-comment', char, meta, i, i === lastI );
351
+ meta.openComment = source.substr( i, 2 );
352
+ } else {
353
+ callbackReturn = _callback( $tokens, 'char', char, meta, i, i === lastI );
354
+ }
355
+
356
+ if ( callbackReturn !== false ) {
357
+ $tokens[ 0 ] += char;
358
+ }
359
+ return [ $tokens, meta, typeof callbackReturn === 'number' ? callbackReturn : skip ];
360
+
361
+ }, [ [ '' ], { openEnclosures: [], }, 0 ] );
362
+ }
363
+
364
+ // ------------------
365
+ // Unique ID generator
366
+ const hashTable = new Map;
367
+ const uniqId = () => (0|Math.random()*9e6).toString(36);
368
+ const _await = ( value, callback ) => value instanceof Promise ? value.then( callback ) : callback( value );
369
+
370
+ // ------------------
371
+ // Hash of anything generator
372
+ export function toHash( val ) {
373
+ let hash;
374
+ if ( !( hash = hashTable.get( val ) ) ) {
375
+ hash = uniqId();
376
+ hashTable.set( val, hash );
377
+ }
378
+ return hash;
379
+ }
380
+
381
+ // ------------------
382
+ // Value of any hash
383
+ export function fromHash( hash ) {
384
+ let val;
385
+ hashTable.forEach( ( _hash, _val ) => {
386
+ if ( _hash === hash ) val = _val;
387
+ } );
388
+ return val;
389
+ }
@@ -0,0 +1,13 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import init, { Observer } from './index.js';
6
+
7
+ /**
8
+ * @init
9
+ */
10
+ init.call( window );
11
+ // As globals
12
+ if ( !window.wq ) { window.wq = {}; }
13
+ window.wq.Observer = Observer;
@@ -1,142 +1,57 @@
1
-
2
- /**
3
- * @imports
4
- */
5
- import Observer from '@webqit/observer';
6
- import { _difference } from '@webqit/util/arr/index.js';
7
- import { _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
- * The State API
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
-
35
- const _meta = config.call(this, {
36
- api: {state: 'state', setState: 'setState', clearState: 'clearState',},
37
- }, _config.params );
38
-
39
- const getOrCreateState = function(subject, newStateObject = null) {
40
- if (!_internals(subject, 'oohtml').has('state') || newStateObject) {
41
- const stateObject = newStateObject || Object.create(null);
42
- const prevStateObject = _internals(subject, 'oohtml').get('state');
43
- _internals(subject, 'oohtml').set('state', stateObject);
44
- if (prevStateObject && Observer.unlink) {
45
- Observer.unlink(subject, _meta.get('api.state'), prevStateObject);
46
- }
47
- if (Observer.link) {
48
- let event = newStateObject ? {isUpdate: prevStateObject ? true : false, oldValue: prevStateObject} : null;
49
- Observer.link(subject, _meta.get('api.state'), stateObject, event);
50
- }
51
- }
52
- return _internals(subject, 'oohtml').get('state');
53
- };
54
-
55
- // ----------------------
56
- // Define the "local" state property on Element.prototype
57
- // ----------------------
58
-
59
- if (_meta.get('api.state') in window.Element.prototype) {
60
- throw new Error('The "Element" class already has a "' + _meta.get('api.state') + '" property!');
61
- }
62
- Object.defineProperty(window.Element.prototype, _meta.get('api.state'), {
63
- get: function() {
64
- return Observer.proxy(getOrCreateState(this));
65
- }
66
- });
67
-
68
- // ----------------------
69
-
70
- if (_meta.get('api.setState') in window.Element.prototype) {
71
- throw new Error('The "Element" class already has a "' + _meta.get('api.setState') + '" property!');
72
- }
73
- Object.defineProperty(window.Element.prototype, _meta.get('api.setState'), {
74
- value: function(stateObject, params = {}) {
75
- if (!params.update) {
76
- getOrCreateState(this, stateObject);
77
- } else {
78
- var currentStateObject = getOrCreateState(this);
79
- if (params.update !== 'merge') {
80
- var outgoingKeys = _difference(Object.keys(currentStateObject), Object.keys(stateObject));
81
- Observer.deleteProperty(currentStateObject, outgoingKeys);
82
- }
83
- Observer.set(currentStateObject, stateObject);
84
- }
85
- }
86
- });
87
-
88
- // ----------------------
89
-
90
- if (_meta.get('api.clearState') in window.Element.prototype) {
91
- throw new Error('The "Element" class already has a "' + _meta.get('api.clearState') + '" property!');
92
- }
93
- Object.defineProperty(window.Element.prototype, _meta.get('api.clearState'), {
94
- value: function() {
95
- getOrCreateState(this, {});
96
- }
97
- });
98
-
99
- // ----------------------
100
- // Define the global "state" object
101
- // ----------------------
102
-
103
- if (_meta.get('api.state') in document) {
104
- throw new Error('The "document" object already has a "' + _meta.get('api.state') + '" property!');
105
- }
106
- Object.defineProperty(document, _meta.get('api.state'), {
107
- get: function() {
108
- return Observer.proxy(getOrCreateState(document));
109
- }
110
- });
111
-
112
- // ----------------------
113
-
114
- if (_meta.get('api.setState') in document) {
115
- throw new Error('The "document" object already has a "' + _meta.get('api.setState') + '" property!');
116
- }
117
- Object.defineProperty(document, _meta.get('api.setState'), {
118
- value: function(stateObject, params = {}) {
119
- if (!params.update) {
120
- getOrCreateState(document, stateObject);
121
- } else {
122
- var currentStateObject = getOrCreateState(document);
123
- if (params.update !== 'merge') {
124
- var outgoingKeys = _difference(Object.keys(currentStateObject), Object.keys(stateObject));
125
- Observer.deleteProperty(currentStateObject, outgoingKeys);
126
- }
127
- Observer.set(currentStateObject, stateObject);
128
- }
129
- }
130
- });
131
-
132
- // ----------------------
133
-
134
- if (_meta.get('api.clearState') in document) {
135
- throw new Error('The "document" object already has a "' + _meta.get('api.clearState') + '" property!');
136
- }
137
- Object.defineProperty(document, _meta.get('api.clearState'), {
138
- value: function() {
139
- getOrCreateState(document, {});
140
- }
141
- });
142
- };
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import wqDom from '@webqit/dom';
6
+ import Observer from '@webqit/observer';
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
+ const params = dom.meta( 'oohtml' ).copyWithDefaults( $params, {
17
+ api: { state: 'state', },
18
+ } );
19
+ exposeAPIs.call( this, params );
20
+ }
21
+
22
+ export { Observer }
23
+
24
+ /**
25
+ * @Exports
26
+ *
27
+ * The internal state object
28
+ * within elements and the document object.
29
+ */
30
+ function getStateObject( node ) {
31
+ if ( !_( node ).has( 'state' ) ) {
32
+ const stateObj = Object.create( null );
33
+ _( node ).set( 'state', stateObj );
34
+ }
35
+ return _( node ).get( 'state' );
36
+ }
37
+
38
+ /**
39
+ * Exposes State with native APIs.
40
+ *
41
+ * @param Object params
42
+ *
43
+ * @return Void
44
+ */
45
+ function exposeAPIs( params ) {
46
+ const window = this;
47
+ // Assertions
48
+ if ( params.api.state in window.document ) { throw new Error( `document already has a "${ params.api.state }" property!` ); }
49
+ if ( params.api.state in window.Element.prototype ) { throw new Error( `The "Element" class already has a "${ params.api.state }" property!` ); }
50
+ // Definitions
51
+ Object.defineProperty( window.document, params.api.state, { get: function() {
52
+ return Observer.proxy( getStateObject( window.document ) );
53
+ } });
54
+ Object.defineProperty( window.Element.prototype, params.api.state, { get: function() {
55
+ return Observer.proxy( getStateObject( this ) );
56
+ } } );
57
+ }
@@ -0,0 +1,13 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import init, { Observer } from './index.js';
6
+
7
+ /**
8
+ * @init
9
+ */
10
+ init.call( window );
11
+ // As globals
12
+ if ( !window.wq ) { window.wq = {}; }
13
+ window.wq.Observer = Observer;
@@ -0,0 +1,13 @@
1
+
2
+ /**
3
+ * @imports
4
+ */
5
+ import init, { Observer } from './index.js';
6
+
7
+ /**
8
+ * @init
9
+ */
10
+ init.call( window );
11
+ // As globals
12
+ if ( !window.wq ) { window.wq = {}; }
13
+ window.wq.Observer = Observer;