@webqit/oohtml 3.1.13 → 3.2.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/README.md +212 -217
- package/dist/bindings-api.js +1 -1
- package/dist/bindings-api.js.map +2 -2
- package/dist/context-api.js +1 -1
- package/dist/context-api.js.map +1 -1
- package/dist/data-binding.js +8 -8
- package/dist/data-binding.js.map +2 -2
- package/dist/html-imports.js +1 -1
- package/dist/html-imports.js.map +2 -2
- package/dist/main.js +25 -23
- package/dist/main.js.map +3 -3
- package/dist/main.lite.js +23 -21
- package/dist/main.lite.js.map +3 -3
- package/dist/namespaced-html.js +1 -1
- package/dist/namespaced-html.js.map +3 -3
- package/dist/scoped-css.js +5 -3
- package/dist/scoped-css.js.map +3 -3
- package/dist/scoped-js.js +1 -1
- package/dist/scoped-js.js.map +3 -3
- package/package.json +3 -3
- package/src/bindings-api/DOMBindingsContext.js +1 -1
- package/src/bindings-api/index.js +21 -21
- package/src/data-binding/index.js +4 -19
- package/src/html-imports/HTMLImportsContext.js +1 -3
- package/src/html-imports/index.js +7 -1
- package/src/index.js +3 -3
- package/src/namespaced-html/DOMNamingContext.js +54 -0
- package/src/namespaced-html/index.js +237 -60
- package/src/scoped-css/index.js +108 -16
- package/src/scoped-js/index.js +48 -25
- package/src/util.js +38 -0
- package/test/scoped-css.test.js +2 -2
- package/src/scoped-js/Hash.js +0 -26
package/src/scoped-css/index.js
CHANGED
|
@@ -2,24 +2,51 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import { rewriteSelector } from '../namespaced-html/index.js';
|
|
6
|
+
import { _init, _toHash, _splitOuter } from '../util.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @init
|
|
9
10
|
*
|
|
10
11
|
* @param Object $config
|
|
11
12
|
*/
|
|
12
|
-
export default function init(
|
|
13
|
+
export default function init({ advanced = {}, ...$config }) {
|
|
13
14
|
const { config, window } = _init.call( this, 'scoped-css', $config, {
|
|
15
|
+
api: { styleSheets: 'styleSheets' },
|
|
14
16
|
style: { retention: 'retain', mimeType: '', strategy: null },
|
|
15
17
|
} );
|
|
16
|
-
|
|
18
|
+
config.styleSelector = (Array.isArray( config.style.mimeType ) ? config.style.mimeType : [ config.style.mimeType ] ).reduce( ( selector, mm ) => {
|
|
17
19
|
const qualifier = mm ? `[type=${ window.CSS.escape( mm ) }]` : '';
|
|
18
20
|
return selector.concat( `style${ qualifier }[scoped]` );
|
|
19
21
|
}, [] ).join( ',' );
|
|
22
|
+
window.webqit.oohtml.Style = {
|
|
23
|
+
compileCache: new Map,
|
|
24
|
+
};
|
|
25
|
+
exposeAPIs.call( window, config );
|
|
20
26
|
realtime.call( window, config );
|
|
21
27
|
}
|
|
22
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Exposes Bindings with native APIs.
|
|
31
|
+
*
|
|
32
|
+
* @param Object config
|
|
33
|
+
*
|
|
34
|
+
* @return Void
|
|
35
|
+
*/
|
|
36
|
+
function exposeAPIs( config ) {
|
|
37
|
+
const window = this, styleSheetsMap = new Map;
|
|
38
|
+
if ( config.api.styleSheets in window.Element.prototype ) { throw new Error( `The "Element" class already has a "${ config.api.styleSheets }" property!` ); }
|
|
39
|
+
Object.defineProperty( window.HTMLElement.prototype, config.api.styleSheets, { get: function() {
|
|
40
|
+
if ( !styleSheetsMap.has( this ) ) { styleSheetsMap.set( this, [] ); }
|
|
41
|
+
return styleSheetsMap.get( this );
|
|
42
|
+
}, } );
|
|
43
|
+
Object.defineProperty( window.HTMLStyleElement.prototype, 'scoped', {
|
|
44
|
+
configurable: true,
|
|
45
|
+
get() { return this.hasAttribute( 'scoped' ); },
|
|
46
|
+
set( value ) { this.toggleAttribute( 'scoped', value ); },
|
|
47
|
+
} );
|
|
48
|
+
}
|
|
49
|
+
|
|
23
50
|
/**
|
|
24
51
|
* Performs realtime capture of elements and builds their relationships.
|
|
25
52
|
*
|
|
@@ -28,22 +55,87 @@ export default function init( { advanced = {}, ...$config } ) {
|
|
|
28
55
|
* @return Void
|
|
29
56
|
*/
|
|
30
57
|
function realtime( config ) {
|
|
31
|
-
|
|
32
|
-
if ( !window.
|
|
33
|
-
const handled =
|
|
34
|
-
|
|
58
|
+
const window = this, { webqit: { oohtml, realdom } } = window;
|
|
59
|
+
if ( !window.CSS.supports ) { window.CSS.supports = () => false; }
|
|
60
|
+
const handled = new WeakSet;
|
|
61
|
+
realdom.realtime( window.document ).subtree/*instead of observe(); reason: jsdom timing*/( config.styleSelector, record => {
|
|
35
62
|
record.entrants.forEach( style => {
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
if ( handled.has( style ) ) return;
|
|
64
|
+
handled.add( style );
|
|
65
|
+
if ( !style.scoped ) return;
|
|
66
|
+
// Do compilation
|
|
67
|
+
const sourceHash = _toHash( style.textContent );
|
|
68
|
+
let compiledSheet, supportsHAS = CSS.supports( 'selector(:has(a,b))' );
|
|
69
|
+
if ( !( compiledSheet = oohtml.Style.compileCache.get( sourceHash ) ) ) {
|
|
70
|
+
const scopeSelector = supportsHAS ? `:has(> style[rand-${ sourceHash }])` : `[rand-${ sourceHash }]`;
|
|
71
|
+
compiledSheet = createAdoptableStylesheet.call( window, style, scopeSelector );
|
|
72
|
+
//compiledSheet = style.sheet; upgradeSheet( style.sheet, /*!window.CSSScopeRule &&*/ scopeSelector );
|
|
73
|
+
oohtml.Style.compileCache.set( sourceHash, compiledSheet );
|
|
43
74
|
}
|
|
75
|
+
// Run now!!!
|
|
76
|
+
style.parentNode[ config.api.styleSheets ].push( style );
|
|
77
|
+
Object.defineProperty( style, 'sheet', { value: compiledSheet, configurable: true } );
|
|
78
|
+
( supportsHAS ? style : style.parentNode ).toggleAttribute( `rand-${ sourceHash }`, true );
|
|
79
|
+
style.textContent = '\n/*[ Shared style sheet ]*/\n';
|
|
44
80
|
} );
|
|
45
|
-
|
|
81
|
+
}, { live: true, timing: 'intercept', generation: 'entrants' } );
|
|
46
82
|
// ---
|
|
47
83
|
}
|
|
48
84
|
|
|
49
|
-
|
|
85
|
+
function createAdoptableStylesheet( style, scopeSelector ) {
|
|
86
|
+
const window = this, textContent = style.textContent, supportsScope = window.CSSScopeRule && false/* Disabled for buggy behaviour: rewriting selectorText within an @scope block invalidates the scoping */;
|
|
87
|
+
let styleSheet, cssText = supportsScope ? `@scope (${ scopeSelector }) {\n${ textContent.trim() }\n}` : textContent.trim();
|
|
88
|
+
try {
|
|
89
|
+
styleSheet = new window.CSSStyleSheet;
|
|
90
|
+
styleSheet.replaceSync( cssText );
|
|
91
|
+
upgradeSheet( styleSheet, !supportsScope && scopeSelector );
|
|
92
|
+
document.adoptedStyleSheets.push( styleSheet );
|
|
93
|
+
} catch( e ) {
|
|
94
|
+
const style = window.document.createElement( 'style' );
|
|
95
|
+
window.document.body.appendChild( style );
|
|
96
|
+
style.textContent = cssText;
|
|
97
|
+
styleSheet = style.sheet;
|
|
98
|
+
upgradeSheet( styleSheet, !supportsScope && scopeSelector );
|
|
99
|
+
}
|
|
100
|
+
return styleSheet;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function upgradeSheet( styleSheet, scopeSelector = null ) {
|
|
104
|
+
const l = styleSheet?.cssRules.length || -1;
|
|
105
|
+
for ( let i = 0; i < l; ++i ) {
|
|
106
|
+
const cssRule = styleSheet.cssRules[ i ];
|
|
107
|
+
if ( cssRule instanceof CSSImportRule ) {
|
|
108
|
+
// Handle imported stylesheets
|
|
109
|
+
//upgradeSheet( cssRule.styleSheet, scopeSelector );
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
upgradeRule( cssRule, scopeSelector );
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function upgradeRule( cssRule, scopeSelector = null ) {
|
|
117
|
+
if ( cssRule instanceof CSSStyleRule ) {
|
|
118
|
+
// Resolve relative IDs and scoping (for non-@scope browsers)
|
|
119
|
+
upgradeSelector( cssRule, scopeSelector );
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if ( [ window.CSSScopeRule, window.CSSMediaRule, window.CSSContainerRule, window.CSSSupportsRule, window.CSSLayerBlockRule ].some( type => type && cssRule instanceof type ) ) {
|
|
123
|
+
// Parse @rule blocks
|
|
124
|
+
const l = cssRule.cssRules.length;
|
|
125
|
+
for ( let i = 0; i < l; ++i ) {
|
|
126
|
+
upgradeRule( cssRule.cssRules[ i ], scopeSelector );
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function upgradeSelector( cssRule, scopeSelector = null ) {
|
|
132
|
+
const newSelectorText = rewriteSelector( cssRule.selectorText, scopeSelector, true );
|
|
133
|
+
cssRule.selectorText = newSelectorText;
|
|
134
|
+
// Parse nested blocks. (CSS nesting)
|
|
135
|
+
if ( cssRule.cssRules ) {
|
|
136
|
+
const l = cssRule.cssRules.length;
|
|
137
|
+
for ( let i = 0; i < l; ++i ) {
|
|
138
|
+
upgradeSelector( cssRule.cssRules[ i ], /* Nesting has nothing to do with scopeSelector */ );
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
package/src/scoped-js/index.js
CHANGED
|
@@ -3,34 +3,63 @@
|
|
|
3
3
|
* @imports
|
|
4
4
|
*/
|
|
5
5
|
import { resolveParams } from '@webqit/quantum-js/params';
|
|
6
|
-
import { _init } from '../util.js';
|
|
7
|
-
import Hash from './Hash.js';
|
|
6
|
+
import { _init, _toHash, _fromHash } from '../util.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* @init
|
|
11
10
|
*
|
|
12
11
|
* @param Object $config
|
|
13
12
|
*/
|
|
14
|
-
export default function init(
|
|
13
|
+
export default function init({ advanced = {}, ...$config }) {
|
|
15
14
|
const { config, window } = _init.call( this, 'scoped-js', $config, {
|
|
16
15
|
script: { retention: 'retain', mimeType: '' },
|
|
17
|
-
|
|
16
|
+
api: { scripts: 'scripts' },
|
|
17
|
+
advanced: resolveParams(advanced),
|
|
18
18
|
} );
|
|
19
|
-
|
|
20
|
-
const qualifier = mm ? `[type=${ window.CSS.escape( mm ) }]` : '';
|
|
19
|
+
config.scriptSelector = ( Array.isArray( config.script.mimeType ) ? config.script.mimeType : [ config.script.mimeType ] ).reduce( ( selector, mm ) => {
|
|
20
|
+
const qualifier = mm ? `[type=${ window.CSS.escape( mm ) } ]` : '';
|
|
21
21
|
return selector.concat( `script${ qualifier }[scoped],script${ qualifier }[quantum]` );
|
|
22
22
|
}, [] ).join( ',' );
|
|
23
23
|
window.webqit.oohtml.Script = {
|
|
24
24
|
compileCache: [ new Map, new Map, ],
|
|
25
25
|
execute: execute.bind( window, config ),
|
|
26
26
|
};
|
|
27
|
+
exposeAPIs.call( window, config );
|
|
27
28
|
realtime.call( window, config );
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Exposes Bindings with native APIs.
|
|
33
|
+
*
|
|
34
|
+
* @param Object config
|
|
35
|
+
*
|
|
36
|
+
* @return Void
|
|
37
|
+
*/
|
|
38
|
+
function exposeAPIs( config ) {
|
|
39
|
+
const window = this, scriptsMap = new Map;
|
|
40
|
+
if ( config.api.scripts in window.Element.prototype ) { throw new Error( `The "Element" class already has a "${ config.api.scripts }" property!` ); }
|
|
41
|
+
Object.defineProperty( window.HTMLElement.prototype, config.api.scripts, { get: function() {
|
|
42
|
+
if ( !scriptsMap.has( this ) ) { scriptsMap.set( this, [] ); }
|
|
43
|
+
return scriptsMap.get( this );
|
|
44
|
+
}, } );
|
|
45
|
+
Object.defineProperties( window.HTMLScriptElement.prototype, {
|
|
46
|
+
scoped: {
|
|
47
|
+
configurable: true,
|
|
48
|
+
get() { return this.hasAttribute( 'scoped' ); },
|
|
49
|
+
set( value ) { this.toggleAttribute( 'scoped', value ); },
|
|
50
|
+
},
|
|
51
|
+
quantum: {
|
|
52
|
+
configurable: true,
|
|
53
|
+
get() { return this.hasAttribute( 'quantum' ); },
|
|
54
|
+
set( value ) { this.toggleAttribute( 'quantum', value ); },
|
|
55
|
+
},
|
|
56
|
+
} );
|
|
57
|
+
}
|
|
58
|
+
|
|
30
59
|
// Script runner
|
|
31
60
|
async function execute( config, execHash ) {
|
|
32
61
|
const window = this, { realdom } = window.webqit;
|
|
33
|
-
const exec =
|
|
62
|
+
const exec = _fromHash( execHash );
|
|
34
63
|
if ( !exec ) throw new Error( `Argument must be a valid exec hash.` );
|
|
35
64
|
const { script, compiledScript, thisContext } = exec;
|
|
36
65
|
// Honour retention flag
|
|
@@ -46,7 +75,7 @@ async function execute( config, execHash ) {
|
|
|
46
75
|
if ( script.quantum ) { Object.defineProperty( script, 'state', { value: state } ); }
|
|
47
76
|
realdom.realtime( window.document ).observe( script, () => {
|
|
48
77
|
if ( script.quantum ) { state.dispose(); }
|
|
49
|
-
if ( script.scoped ) { thisContext.scripts.splice( thisContext.scripts.indexOf( script, 1 ) ); }
|
|
78
|
+
if ( script.scoped ) { thisContext[ config.api.scripts ].splice( thisContext[ config.api.scripts ].indexOf( script, 1 ) ); }
|
|
50
79
|
}, { subtree: true, timing: 'sync', generation: 'exits' } );
|
|
51
80
|
}
|
|
52
81
|
|
|
@@ -58,26 +87,23 @@ async function execute( config, execHash ) {
|
|
|
58
87
|
* @return Void
|
|
59
88
|
*/
|
|
60
89
|
function realtime( config ) {
|
|
61
|
-
|
|
90
|
+
const window = this, { webqit: { oohtml, realdom, QuantumScript, QuantumAsyncScript, QuantumModule } } = window;
|
|
62
91
|
if ( !window.HTMLScriptElement.supports ) { window.HTMLScriptElement.supports = () => false; }
|
|
63
|
-
const potentialManualTypes = [ 'module' ].concat( config.script.mimeType || [] );
|
|
64
|
-
|
|
92
|
+
const potentialManualTypes = [ 'module' ].concat( config.script.mimeType || [] ), handled = new WeakSet;
|
|
93
|
+
realdom.realtime( window.document ).subtree/*instead of observe(); reason: jsdom timing*/( config.scriptSelector, record => {
|
|
65
94
|
record.entrants.forEach( script => {
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
Object.defineProperty( script, 'quantum', { value: script.hasAttribute( 'quantum' ) } );
|
|
69
|
-
if ( 'scoped' in script ) return handled( script );
|
|
70
|
-
Object.defineProperty( script, 'scoped', { value: script.hasAttribute( 'scoped' ) } );
|
|
95
|
+
if ( handled.has( script ) ) return;
|
|
96
|
+
handled.add( script );
|
|
71
97
|
// Do compilation
|
|
72
98
|
const textContent = ( script._ = script.textContent.trim() ) && script._.startsWith( '/*@oohtml*/if(false){' ) && script._.endsWith( '}/*@oohtml*/' ) ? script._.slice( 21, -12 ) : script.textContent;
|
|
73
|
-
const sourceHash =
|
|
99
|
+
const sourceHash = _toHash( textContent );
|
|
74
100
|
const compileCache = oohtml.Script.compileCache[ script.quantum ? 0 : 1 ];
|
|
75
101
|
let compiledScript;
|
|
76
102
|
if ( !( compiledScript = compileCache.get( sourceHash ) ) ) {
|
|
77
103
|
const { parserParams, compilerParams, runtimeParams } = config.advanced;
|
|
78
|
-
compiledScript = new ( script.type === 'module' ? QuantumModule : (
|
|
104
|
+
compiledScript = new ( script.type === 'module' ? QuantumModule : (QuantumScript || QuantumAsyncScript) )( textContent, {
|
|
79
105
|
exportNamespace: `#${ script.id }`,
|
|
80
|
-
fileName
|
|
106
|
+
fileName: `${ window.document.url?.split( '#' )?.[ 0 ] || '' }#${ script.id }`,
|
|
81
107
|
parserParams,
|
|
82
108
|
compilerParams: { ...compilerParams, startStatic: !script.quantum },
|
|
83
109
|
runtimeParams,
|
|
@@ -86,16 +112,13 @@ function realtime( config ) {
|
|
|
86
112
|
}
|
|
87
113
|
// Run now!!!
|
|
88
114
|
const thisContext = script.scoped ? script.parentNode || record.target : ( script.type === 'module' ? undefined : window );
|
|
89
|
-
if ( script.scoped ) {
|
|
90
|
-
|
|
91
|
-
thisContext.scripts.push( script );
|
|
92
|
-
}
|
|
93
|
-
const execHash = Hash.toHash( { script, compiledScript, thisContext } );
|
|
115
|
+
if ( script.scoped ) { thisContext[ config.api.scripts ].push( script ); }
|
|
116
|
+
const execHash = _toHash( { script, compiledScript, thisContext } );
|
|
94
117
|
const manualHandling = record.type === 'query' || ( potentialManualTypes.includes( script.type ) && !window.HTMLScriptElement.supports( script.type ) );
|
|
95
118
|
if ( manualHandling ) { oohtml.Script.execute( execHash ); } else {
|
|
96
119
|
script.textContent = `webqit.oohtml.Script.execute( '${ execHash }' );`;
|
|
97
120
|
}
|
|
98
121
|
} );
|
|
99
|
-
|
|
122
|
+
}, { live: true, timing: 'intercept', generation: 'entrants', eventDetails: true } );
|
|
100
123
|
// ---
|
|
101
124
|
}
|
package/src/util.js
CHANGED
|
@@ -53,4 +53,42 @@ export function _compare( a, b, depth = 1, objectSizing = false ) {
|
|
|
53
53
|
return ( b = b.slice( 0 ).sort() ) && a.slice( 0 ).sort().every( ( valueA, i ) => valueA === b[ i ] );
|
|
54
54
|
}
|
|
55
55
|
return a === b;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function _splitOuter( str, delim ) {
|
|
59
|
+
return [ ...str ].reduce( ( [ quote, depth, splits ], x ) => {
|
|
60
|
+
if ( !quote && depth === 0 && ( Array.isArray( delim ) ? delim : [ delim ] ).includes( x ) ) {
|
|
61
|
+
return [ quote, depth, [ '' ].concat( splits ) ];
|
|
62
|
+
}
|
|
63
|
+
if ( !quote && [ '(', '[', '{' ].includes( x ) && !splits[ 0 ].endsWith( '\\' ) ) depth++;
|
|
64
|
+
if ( !quote && [ ')', ']', '}' ].includes( x ) && !splits[ 0 ].endsWith( '\\' ) ) depth--;
|
|
65
|
+
if ( [ '"', "'", '`' ].includes( x ) && !splits[ 0 ].endsWith( '\\' ) ) {
|
|
66
|
+
quote = quote === x ? null : ( quote || x );
|
|
67
|
+
}
|
|
68
|
+
splits[ 0 ] += x;
|
|
69
|
+
return [ quote, depth, splits ]
|
|
70
|
+
}, [ null, 0, [ '' ] ] )[ 2 ].reverse();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Unique ID generator
|
|
74
|
+
export const _uniqId = () => ( 0 | Math.random() * 9e6 ).toString( 36 );
|
|
75
|
+
|
|
76
|
+
// Hash of anything generator
|
|
77
|
+
const hashTable = new Map;
|
|
78
|
+
export function _toHash( val ) {
|
|
79
|
+
let hash;
|
|
80
|
+
if ( !( hash = hashTable.get( val ) ) ) {
|
|
81
|
+
hash = _uniqId();
|
|
82
|
+
hashTable.set( val, hash );
|
|
83
|
+
}
|
|
84
|
+
return hash;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Value of any hash
|
|
88
|
+
export function _fromHash( hash ) {
|
|
89
|
+
let val;
|
|
90
|
+
hashTable.forEach( ( _hash, _val ) => {
|
|
91
|
+
if ( _hash === hash ) val = _val;
|
|
92
|
+
} );
|
|
93
|
+
return val;
|
|
56
94
|
}
|
package/test/scoped-css.test.js
CHANGED
|
@@ -10,7 +10,7 @@ describe(`Test: Scoped CSS`, function() {
|
|
|
10
10
|
describe(`Styles`, function() {
|
|
11
11
|
|
|
12
12
|
it(`Should do basic rewrite`, async function() {
|
|
13
|
-
const head = '
|
|
13
|
+
const head = '', body = `
|
|
14
14
|
<div>
|
|
15
15
|
<h1>Hello World!</h1>
|
|
16
16
|
<style scoped>
|
|
@@ -24,7 +24,7 @@ describe(`Test: Scoped CSS`, function() {
|
|
|
24
24
|
await delay( 160 ); // Takes time to dynamically load Reflex compiler
|
|
25
25
|
const styleElement = window.document.querySelector( 'style' );
|
|
26
26
|
|
|
27
|
-
expect( styleElement.textContent.substring( 0, 13 ) ).to.eq( '@scope
|
|
27
|
+
//expect( styleElement.textContent.substring( 0, 13 ) ).to.eq( '@scope (' );
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
});
|
package/src/scoped-js/Hash.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
export default class Hash {
|
|
3
|
-
|
|
4
|
-
// Unique ID generator
|
|
5
|
-
static hashTable = new Map;
|
|
6
|
-
static uniqId = () => (0|Math.random()*9e6).toString(36);
|
|
7
|
-
|
|
8
|
-
// Hash of anything generator
|
|
9
|
-
static toHash( val ) {
|
|
10
|
-
let hash;
|
|
11
|
-
if ( !( hash = this.hashTable.get( val ) ) ) {
|
|
12
|
-
hash = this.uniqId();
|
|
13
|
-
this.hashTable.set( val, hash );
|
|
14
|
-
}
|
|
15
|
-
return hash;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Value of any hash
|
|
19
|
-
static fromHash( hash ) {
|
|
20
|
-
let val;
|
|
21
|
-
this.hashTable.forEach( ( _hash, _val ) => {
|
|
22
|
-
if ( _hash === hash ) val = _val;
|
|
23
|
-
} );
|
|
24
|
-
return val;
|
|
25
|
-
}
|
|
26
|
-
}
|