(()=>{ class É { map = new Map(); assertThrow(statement, message){ if(statement === false) { throw new Error(message); } } checkTyping(locks, value) { if(locks) { if(locks.nullable === false) { this.assertThrow(typeof value != "undefined","value is not nullable") } if(locks.instance) { this.assertThrow(value instanceof (locks.instance),"value is not instanceof " + locks.instance.prototype.constructor.name) } if(locks.type) { this.assertThrow(locks.type == typeof value,"value is not typeof " + locks.type) } } } setValue(path, value){ let isReadonly = this.getValue(path,'readonly') || false; if(isReadonly) return; let writers = this.getValue(path,'writer') || []; if(writers.length){ let oldvalue = this.getValue(path); for (const writer of writers) { value = writer(value,oldvalue); } } let locks = this.getValue(path,'locks') || false; locks && this.checkTyping(locks, value) let {npath,process} = this.setAttribute(path, "value", value); // optimization if(!process){ return [] } let callers = []; this.mapHierachy(npath, node => { let localCallers = node.has("changelistener") ? node.get("changelistener") : []; if(localCallers.length){ callers = callers.concat(localCallers); } }); if(callers.length) { return callers.filter(e=>typeof e == "function").map(callbacks => { let result; try{ result = callbacks(npath) }catch(error){ result = error }finally{ return result; } }).filter(e => e !== void 0); }else{ return callers.filter(e=>typeof e == "function").map(callbacks => { let result; try{ result = callbacks(npath) }catch(error){ result = error }finally{ return result; } }).filter(e => e !== void 0); } } deleteAttribute( path, attributeName = "value" ){ let npath = this.normalizePath(path); this.map.get(npath).delete(attributeName) } removeNode(path){ let npath = this.normalizePath(path); this.map.delete(npath); } removeAttribute( path, attributeName ){ if(this.map.has(path)) { this.map.delete(attributeName) } } setAttribute( path, attributeName = "value", value, callableValue = true ){ let process; let npath = this.normalizePath(path); if(this.map.has(path)) { let node = this.map.get(npath); if(typeof value == "function" && callableValue) { let oldv = node.get(attributeName); let newv = value(oldv); let diffAlgorithm = node.has('diff') ? node.get('diff') : ediffEpsilon; if(diffAlgorithm(oldv, newv)) { node.set(attributeName, newv) process = 'update'; } }else{ let oldv = node.get(attributeName); let diffAlgorithm = node.has('diff') ? node.get('diff') : ediffEpsilon; if(diffAlgorithm(oldv, value)) { this.map.get(npath).set(attributeName, value) process = 'update'; } this.map.get(npath).set(attributeName, value) } }else{ let valueMap = new Map(); valueMap.set(attributeName, typeof value == "function" ? value() : value); this.map.set(npath, valueMap); process = 'create'; } return {npath,process}; } mapHierachy(npath, mapCallback){ let npaths = npath.split('/'); let t = [], current = ""; for (const path of npaths) { t.push(path); current = t.join('/'); if(this.map.has(current)) { mapCallback(this.map.get(current)) }; } } createListenerForPath(path, callback){ let npath = this.normalizePath(path); this.setAttribute( npath, 'changelistener', callbacksOrNull => (callbacksOrNull ? callbacksOrNull.concat([callback]) : [callback]) ); } getValue(path, attribute = "value"){ let npath = this.normalizePath(path); let staticValue = this.map.get(npath)?.get(attribute); if(attribute == 'value') { let readers = this.getValue(path,'reader') || []; if(readers.length){ let oldvalue = staticValue, value; for (const reader of readers) { value = reader(oldvalue, value); } return value; }else{ return staticValue; } }else{ return staticValue; } } normalizePath(str){ if(!str) { return ':moment:' }else if(!(typeof str == "string")){ throw new Error("É is not valid path ") }else{ if(str.indexOf('/') == -1) { return str; } let path = str.split('/').map(e => e.trim()); let first = path[0] let last = path[path.length - 1]; if(first == '') path.shift(); if(last == '') path.pop(); let result = path.join('/'); return result; } } createListenerForWrite(path, callback){ let npath = this.normalizePath(path); this.setAttribute( npath, 'writer', value => (value || []).concat([callback]), true ) } createListenerForRead(path, callback){ let npath = this.normalizePath(path); this.setAttribute( npath, 'reader', value => (value || []).concat([callback]), true ) } changeDifferenceAlgorithm(path, callback){ this.setAttribute( path, 'diff', callback, false ) } lockValue(path){ é.current.setAttribute(path, 'readonly', true); } unlockValue(path){ é.current.removeAttribute(path, 'readonly'); } setTypedLocks(path, options){ let npath = this.normalizePath(path); if(options.instance !== void 0) { this.setAttribute( npath, 'locks', obj => ({ ...(obj || {}), instance: options.instance }), true ); } if(options.type !== void 0) { this.setAttribute( npath, 'locks', obj => ({ ...(obj || {}), type: options.type }), true ); } if(options.nullable !== void 0) { this.setAttribute( npath, 'locks', obj => ({ ...(obj || {}), nullable: options.nullable }), true ); } } setTypedUnlocks(path, options){ this.setAttribute( path, 'locks', void 0, false ); } } function é(arg1, arg2) { if("string" == typeof arg1) { if(arg2 === void 0) { return é.current.getValue(arg1) } if(["bigint","boolean"/*,"function"*/,"number","object","symbol","string"].includes(typeof arg2) || arg2 === null) { return é.current.setValue(arg1, arg2) } /* if(["function"].includes(typeof arg2) || arg2 === null) { return é.current.setFunctionValue(arg1, arg2) } */ } if( "function" == typeof arg1 && arg2 instanceof Array && arg2.every(e => typeof e == "string") ){ for (const path of arg2) { é.current.createListenerForPath(path, arg1); } } } é.current = new É(); é.write = (...args) => é.current.createListenerForWrite(...args); é.read = (...args) => é.current.createListenerForRead(...args); é.diff = (...args) => é.current.changeDifferenceAlgorithm(...args); é.var = (path, value, write, read) => { é.current.setValue(path, value); é.current.createListenerForWrite(path, write); é.current.createListenerForWrite(path, read); return { get: () => é.current.getValue(path), set: (value) => é.current.setValue(path, value) } }; é.typedLock = (path, options) => { é.current.setTypedLocks(path,options) }; é.typedUnLock = path => { é.current.setTypedUnlocks(path) }; é.delete = path => { é.current.removeNode(path); }; é.lockValue = path => { é.current.setAttribute(path, 'readonly', true); }; é.unlockValue = path => { é.current.removeNode(path, 'readonly', true); }; // Differance algorithm function ediffEpsilon(a, b, c){ let cursoryDiffResult = typedDiff(a, b); if (cursoryDiffResult == typedDiff.arrayDiff) { return objectDiff(a, b, c || []); } else { return cursoryDiffResult == typedDiff.some ? false : true; } }; function objectDiff(a, b, c){ if (c.includes(a) && c.includes(b)) { console.error("Circular object detected !"); return objectKeyDiff(a, b); } let typea = a instanceof Array ? 0 : a.byteLength ? 1 : 2; let typeb = b instanceof Array ? 0 : b.byteLength ? 1 : 2; if (typea != typeb) { return true; } if (typea == 0) { if (a.length != b.length) { return true; } for (let k = 0; k < a.length; k++) { if (ediffEpsilon(a[k], b[k])) { return true; } } return false; } if (a instanceof Object) { if (Object.keys(a).length != Object.keys(b).length) { return true; } c.push(a); c.push(b); for (const key in a) { if (Object.hasOwnProperty.call(a, key)) { if (ediffEpsilon(a[key], b[key], c)) { return true; } } } return false; } }; function arrayLengthPointerDiff(a, b){ return a.length != b.length || !a.every((v, i) => a[i] === v); }; function objectKeyDiff(a, b){ return arrayLengthPointerDiff( Object.keys(a), Object.keys(b) ); }; function typedDiff(a, b){ if(typeof a == typeof b) { switch (typeof a) { case "undefined": case "function": { return typeof a == typeof b ? typedDiff.some : typedDiff.different; } case "symbol": case "bigint": case "boolean": case "number": case "string": { return a === b ? typedDiff.some : typedDiff.different; } case "object": { return typedDiff.arrayDiff; } } }else{ return typedDiff.different; } }; typedDiff.arrayDiff = -1; typedDiff.some = 0; typedDiff.different = 1; try{ module.exports = é; }catch{ window.é = é; } })();