wirejs/wire.js

446 lines
14 KiB
JavaScript
Executable File

(()=>{
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.é = é;
}
})();