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