Compare commits

..

2 Commits
v1 ... v2

Author SHA1 Message Date
Abdussamed 7381d3ff2d v2 alpha released 2024-10-08 23:41:50 +03:00
Abdussamed 5ad3c17235 Add filter layer 2023-06-18 16:10:05 +03:00
6 changed files with 769 additions and 1339 deletions

525
Hemex.js
View File

@ -1,525 +0,0 @@
export default function Hemex()
{
};
Hemex.EOL = "\n";
/**
* Hemex variable white space chars
* @type {Number[]}
*/
Hemex.WhiteSpace = [
9,10,11,12,13,32,133
];
/**
* Current cursor position
* @type {Number}
*/
Hemex.prototype.offset = 0;
/**
* Mapping offset points
* @type {Number[]}
*/
Hemex.prototype.offsetMap = [];
Hemex.prototype.beginPosition = function(){
this.offsetMap.push(
this.getLastPosition()
)
}
/**
* Adding current position to offset map
*/
Hemex.prototype.acceptPosition = function(){
let t = this.offsetMap.pop();
this.setLastPosition(t)
}
/**
* Get text range current and parent offsets
* @returns {[Number,Number]}
*/
Hemex.prototype.positionRange = function(){
let len = this.offsetMap.length;
if(len == 0)
{
return [0,this.offset]
}else if(len == 1){
return [this.offset,this.offsetMap[len - 1]]
}else{
return [this.offsetMap[len - 2],this.offsetMap[len - 1]]
}
}
/**
* Get text range between current offset and parent offset
* @returns {String}
*/
Hemex.prototype.getPositionRange = function(){
let u = this.positionRange();
return this.text.slice(u[0],u[1])
}
/**
* Cancel current position and return to parent offset
*/
Hemex.prototype.rejectPosition = function(){
this.offsetMap.pop()
}
/**
* Get current layer of position from last offset of map
* @returns {Number}
*/
Hemex.prototype.getLastPosition = function(){
return this.offsetMap.length == 0 ? this.offset : this.offsetMap.slice(-1)[0]
}
/**
* Set last position offset from offset map last layer
* @param {Number} n
*/
Hemex.prototype.setLastPosition = function(n){
if(this.offsetMap.length == 0)
this.offset = n
else this.offsetMap[this.offsetMap.length - 1] = n
}
/**
* Get current layer of position from last offset of map
* Some as getLastPosition()
* @returns {Number}
*/
Hemex.prototype.getOffset = function(){
return this.getLastPosition()
}
/**
* Set last position offset from offset map last layer and return it value
* @param {Number} n
* @returns {Number}
*/
Hemex.prototype.setOffset = function(n){
this.setLastPosition(n);
return this.getLastPosition()
}
/**
* Get text length
* @type {Number}
*/
Hemex.prototype.length = 0;
/**
* Hemex lexing data
* @type {String}
*/
Hemex.prototype.text = "";
/**
* set lexing data
* @param {String} text
* @returns {void}
*/
Hemex.prototype.setText = function(text){
this.offset = 0;
this.length = text.length;
this.offsetMap = [];
this.text = text;
}
/**
* get lexing all data
* @returns {String}
*/
Hemex.prototype.getText = function(){
return this.text;
}
/**
* Get one character from cursor
* @param {Number} n
* @returns {String}
*/
Hemex.prototype.getChar = function(n){
return this.text.charAt(n?this.getOffset()+n:this.getOffset())
}
/**
* Boolean
* @param {Number} n
* @returns {String}
*/
Hemex.prototype.isChar = function(b){
return this.getChar() == b
}
/**
* Dump all data from cursor position to end of char
* @param {Number} n
*/
Hemex.prototype.dump = function(n){
return this.text.slice(this.getOffset(),this.getOffset()+n)
}
/**
* Control coming end of line
* @returns {Bollean}
*/
Hemex.prototype.isEnd = function(){
return this.length > this.getOffset()
}
/**
* Forward one char
*/
Hemex.prototype.nextChar = function(){
this.setOffset(this.getOffset() + 1);
}
/**
* Forward n char
*/
Hemex.prototype.toChar = function(n){
this.setOffset(this.getOffset() + n);
}
/**
* Reading while end of line
* @returns {String}
*/
Hemex.prototype.getLine = function(){
return this.of(function(){
switch(this.getChar())
{
case Hemex.EOL: return false;
default: return true;
}
}.bind(this))
}
/**
* Read all data until the function returns false
* @param {Boolean} p
* @param {(char:String)=>Boolean} e
* @returns {String}
*/
Hemex.prototype.of = function(e,p){
let k = [],count=0;
while(this.isEnd()){
if(e(p,count)) k.push(this.getChar());
else return k.join('');
count++;
this.nextChar();
};
return k.length == 0 ? false : k.join('')
}
Hemex.prototype.each = function(e,p){
let k = [];
while(this.isEnd())
if(!e(p)) return;
else this.nextChar();
}
Hemex.prototype.while = function(e,p){
let k = [];
while(this.isEnd())
if(!e(p)) return;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isNumber = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 48 && c <= 57;
return reverse ? !result : result;
}
/**
* Read all data until char type is not number
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readNumbers = function(reverse){
return this.of(this.isNumber.bind(this),reverse)
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isBigLetter = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 97 && c <= 122;
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isSmallLetter = function(reverse){
let c = this.getChar().charCodeAt(0);
let result = c >= 65 && c <= 90;
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isLetter = function(reverse){
let result = this.isSmallLetter() || this.isBigLetter()
return reverse ? !result : result;
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isLetterRegex = function(reverse){
let result = /^[\p{L}]$/u.test(this.getChar());
return reverse ? !result : result;
}
/**
* Read all data until char type is not letter
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readLetters = function(reverse){
return this.of(this.isLetter.bind(this),reverse)
}
/**
* Controlling for current char type
* @param {Boolean} reverse
* @returns {Boolean}
*/
Hemex.prototype.isWhiteSpace = function(reverse){
let c = this.getChar(),ct = c.charCodeAt(0);
let result = (
c == '\n' ||
c == '\r' ||
c == '\t' ||
c == ' ' ||
Hemex.WhiteSpace.includes(ct)
)
return reverse ? !result : result;
}
/**
* Read all data until char type is not white space
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.readWhiteSpace = function(reverse){
return this.of(this.isWhiteSpace.bind(this),reverse)
}
/**
* Controlling data
* @param {Boolean} reverse
* @returns {String}
*/
Hemex.prototype.include = function(words,next){
this.beginPosition();
for(let i = 0; i<words.length; i++)
{
if(words[i] != this.getChar())
{
this.rejectPosition();
return false;
};
this.nextChar();
};
if(next) this.acceptPosition();
else this.rejectPosition();
return true;
}
/**
* Controlling data
* @param {Boolean} reverse
* @returns {String|boolean}
*/
Hemex.prototype.includes = function(arrays,accept){
if(!this.isEnd()){
return false;
};
this.beginPosition();
let flags = Array.from(arrays).fill(true);
let index = 0;
this.each(function(){
let stopLoop = true;
for(let T in arrays)
{
if(!flags[T] || arrays[T].length <= index) continue;
stopLoop = false;
flags[T] &= arrays[T][index] == this.getChar()
};
index++;
return !stopLoop && flags.filter(function(val){return val}).length != 0;
}.bind(this));
let result = arrays.filter(function(_,index){return flags[index]});
if(accept) this.acceptPosition();
else this.rejectPosition();
return result.length == 0 ? false : result;
}
/**
* Parsing number formats like; 12 75.1 0xE7 0b10 +3.46
* @returns {[String,Number]}
*/
Hemex.prototype.readNumber = function(){
let data = [];
let base = 10;
let nextDot = false;
let c = this.getChar();
if(this.isChar('0'))
{
this.nextChar();
switch(this.getChar())
{
case 'x':{
base = 16;
this.nextChar();
data.push('0x')
break;
}
case 'b':{
base = 2;
this.nextChar();
data.push('0b')
break;
}
default:{
base = 8;
this.nextChar();
data.push('0')
}
}
}else base = 10;
this.each(()=>{
switch(c = this.getChar())
{
case '0':
case '1':{
data.push(c);
break;
}
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':{
if(base >= 8){
data.push(c);
break;
}else return false;
}
case '8':
case '9':{
if(base >= 10){
data.push(c);
break;
}else return false;
}
case 'A':
case 'a':
case 'B':
case 'b':
case 'C':
case 'c':
case 'D':
case 'd':
/* case 'E': case 'e': */
case 'F':
case 'f':{
if(base >= 16){
data.push(c);
break;
}else return false;
}
case '.':{
if(!nextDot){
if(data.length == 0){
data.push("0");
}else data.push(".");
nextDot = true;
isFloat = true;
}else{
throw new Error("Float number in Double dot");
};
break;
}
case 'E':
case 'e':{
if(this.getChar(1)!='+'){
if(base == 16){
data.push(c);
break;
}else return false;
};
if(data.length == 0){
this.rejectPosition();
return false;
};
data.push('e');
this.nextChar();
if(this.getChar()=='+' || this.getChar()=='-'){
data.push(char());
this.nextChar();
};
let result = null;
this.each(()=>{
switch(this.getChar()){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':{
data.push(this.getChar());
this.nextChar();
break;
}
default:{
return false;
}
}
})
}
default:{
return false;
}
};
return true
});
return data.length == 0 ? false : [data.join(''),base]
}
Hemex.prototype.syntaxs = new Map();
/**
*
* @param {string} name
* @param {(hmx:Hemex, result: (result:any) => any,...args:any[]) => any} callback
*/
Hemex.prototype.syntax = function(name, callback){
this.syntaxs.set(name, callback)
}
Hemex.prototype.give = function(name, ...args){
let sandbox = this.syntaxs.get(name);
if(sandbox)
{
let res = undefined;
this.beginPosition();
if(sandbox(
this,
arg => {
res = arg
},
...args
))
{
this.acceptPosition();
return res;
}else{
this.rejectPosition();
return res;
}
}
}
/**
* @param {Error} message
*/
Hemex.prototype.throw = function(message){
throw new Error(message)
};

View File

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="expol.js" type="module"></script>
</body>
</html>

359
expol.js
View File

@ -1,359 +0,0 @@
import Hemex from "./Hemex.js"
const style = document.createElement("style")
document.head.append(style)
style.sheet.insertRule(`
.expol-container{
box-sizing: border-box;
padding: 5px;
border: solid 1px black;
overflow: hidden;
border-radius: 5px;
}
`)
style.sheet.insertRule(`
.expol-container .expol{
box-sizing: border-box;
position: relative;
min-height: 1em;
font-family: inherit;3em
font-size: inherit;
font-weight: inherit;
white-space: nowrap;
}
`)
/**
* @type {CSSRule}
*/
let fontRule = Array.from(style.sheet.cssRules).filter(e => e.selectorText == '.expol-container .expol')[0]
function updateFont(family,size,weight)
{
fontRule.style.fontFamily = family
fontRule.style.fontSize = size
fontRule.style.fontWeight = weight
}
style.sheet.insertRule(`
.expol .viewport{
position: absolute;
top: 0px;
left:0;
right:0;
height:0;
width: 100%;
height: 100%;
padding: 0
margin: 0;
color: black;
z-index: 0;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
}
`)
style.sheet.insertRule(`
.expol .textinput{
position: absolute;
top: 0px;
left:0;
right:0;
height:0;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
color: transparent;
z-index: 1;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
outline: none;
caret-color: black;
}
`)
style.sheet.insertRule(`
.expol pre{
margin: 0;
font-family: inherit;
display: inline;
}
`)
style.sheet.insertRule(`
.expol .col1{
color: #00a902;
}
`)
style.sheet.insertRule(`
.expol .col2{
color: #ff0000;
}
`)
style.sheet.insertRule(`
.expol .col3{
color: black;
}
`)
style.sheet.insertRule(`
.expol .col4{
color: black;
opacity: 0.5;
}
`)
style.sheet.insertRule(`
.expol .col5{
text-decoration: underline;
color: #0016ff;
}
`)
updateFont("monospace","2em","500")
let container = document.createElement("div")
container.classList.add("expol-container")
document.body.appendChild(container)
let expol = document.createElement("div")
expol.classList.add("expol")
container.appendChild(expol)
let viewport = document.createElement("pre")
viewport.classList.add("viewport")
expol.appendChild(viewport)
let textinput = document.createElement("pre")
textinput.classList.add("textinput")
textinput.contentEditable = true
expol.appendChild(textinput)
textinput.addEventListener("keyup",synctoViewPort)
textinput.addEventListener("keypress",synctoViewPort)
textinput.addEventListener("keydown",synctoViewPort)
textinput.addEventListener("paste",(pasteevent)=>{
let text = pasteevent.clipboardData.getData('text/plain')
textinput.innerText = text
synctoViewPort()
pasteevent.preventDefault()
})
function synctoViewPort()
{
let fragment = renderColorizer(textinput.innerText)
for (const child of Array.from(viewport.childNodes))
{
child.remove()
}
viewport.append(fragment)
}
function renderColorizer(text)
{
let fragment = document.createDocumentFragment()
let hmx = new Hemex()
hmx.setText(text)
let blank = true
let query = []
let term = []
hmx.syntax("term",function(hmx,result){
if(hmx.isChar('-'))
{
hmx.nextChar()
let name = hmx.of(() => hmx.isLetterRegex() || hmx.isNumber() || hmx.includes(['-','_']))
let operator = 'has'
let value = ""
if(name)
{
let pre = document.createElement("pre")
pre.classList.add("col1")
pre.setAttribute("role","name")
let textDoc = document.createTextNode('-' + name)
pre.appendChild(textDoc);
fragment.appendChild(pre)
}else{
let pre = document.createElement("pre")
pre.classList.add("col1")
pre.setAttribute("role","name")
let textDoc = document.createTextNode('-');
pre.appendChild(textDoc);
fragment.appendChild(pre)
}
let k
if(k = hmx.includes([':','>','<','>=','<=','!=']))
{
operator = k[0]
let pre = document.createElement("pre")
pre.classList.add("col2")
pre.setAttribute("role","operator")
let textDoc = document.createTextNode(k[0])
pre.appendChild(textDoc)
fragment.appendChild(pre)
hmx.nextChar()
value = hmx.give('strorword')
}
query.push({
query:[name,operator,value]
})
return true
}
})
hmx.syntax("mention",function(hmx,result){
if(hmx.isChar('@'))
{
hmx.nextChar()
let name = hmx.of(() => hmx.isLetterRegex() || hmx.isNumber() || hmx.includes(['-','_']))
if(name)
{
let pre = document.createElement("pre")
pre.classList.add("col5")
pre.setAttribute("role","mention")
let textDoc = document.createTextNode('@' + name)
pre.appendChild(textDoc);
fragment.appendChild(pre)
}else{
let pre = document.createElement("pre")
pre.classList.add("col5")
pre.setAttribute("role","mention")
let textDoc = document.createTextNode('@');
pre.appendChild(textDoc);
fragment.appendChild(pre)
}
query.push({
mention: name
})
return true
}
})
// cute --username:"absorbe"
hmx.syntax("strorword",function(hmx,result){
let k = hmx.includes(['\'','"','`'])
if(k.length == 0 || k == false){
let name = hmx.of(() => !hmx.isWhiteSpace())
if(name)
{
let pre = document.createElement("pre")
pre.classList.add("col3")
pre.setAttribute("role","value")
let textDoc = document.createTextNode(name||"");
pre.appendChild(textDoc)
fragment.appendChild(pre)
result(name||false)
}
return true
}else{
let char = hmx.getChar()
hmx.nextChar()
let name = hmx.of(() => !hmx.isChar(char))
let kt = "";
if(name)
{
result(name||false)
kt = char + name;
if(hmx.isChar(char)){
hmx.nextChar()
kt += char;
}
}else{
kt += char;
hmx.nextChar()
}
let pre = document.createElement("pre")
pre.classList.add("col3")
pre.setAttribute("role","value")
let textDoc = document.createTextNode(kt)
pre.appendChild(textDoc)
fragment.appendChild(pre)
return true
}
})
hmx.syntax("scope",function(hmx,result){
let description = []
hmx.while(()=>{
switch(hmx.getChar())
{
case "-":{
if(description.length)
{
let pre = document.createElement("pre")
pre.classList.add("col4")
pre.setAttribute("role","description")
let textDoc = document.createTextNode(description.join(''))
pre.appendChild(textDoc)
fragment.appendChild(pre)
if(description.join('').trim().length) term.push(description.join(''))
description = []
}
if(blank)
{
hmx.give("term")
return true
}
}
case "@":{
if(description.length)
{
let pre = document.createElement("pre")
pre.classList.add("col4")
pre.setAttribute("role","description")
let textDoc = document.createTextNode(description.join(''))
pre.appendChild(textDoc)
fragment.appendChild(pre)
if(description.join('').trim().length) term.push(description.join(''))
description = []
}
if(blank)
{
hmx.give("mention")
return true
}
}
default:{
blank = hmx.isWhiteSpace()
description.push(hmx.getChar())
hmx.nextChar()
return true
}
}
})
if(description.length)
{
if(description.join('').trim().length) term.push(description.join(''))
let pre = document.createElement("pre")
pre.classList.add("col4")
pre.setAttribute("role","description")
let textDoc = document.createTextNode(description.join(''))
pre.appendChild(textDoc)
fragment.appendChild(pre)
}
query.push({
term
})
})
hmx.give('scope')
console.log("Query:",query)
return fragment
}

View File

@ -1,6 +1,6 @@
{ {
"name": "@saqut/wirejs", "name": "@saqut/wirejs",
"version": "1.0.0", "version": "0.2.0",
"type": "commonjs", "type": "commonjs",
"private": false, "private": false,
"description": "Dont use variable, use smart variable wires !", "description": "Dont use variable, use smart variable wires !",

549
readme.md
View File

@ -1,241 +1,420 @@
# WiréJS | Değişken değil, kablo kullanın ! # WiréJS | Değişken değil, kablo kullanın !
## Açıklama # Açıklama
WiréS, ReactJS kütüphanesinin useState ve watch kancalarından ilham alınarak browser ortamı için tekrar yazılmış bir kütüphanedir. WiréJS, dizinlenmiş akıllı değişkenler oluşturup kendi kendini yönetebilen veri değişimini kolayca yönetebilen sistemler oluşturabileceğiniz
Verileri getter/setter fonksiyonlarıyla sararak gerçek değişikleri algılayan özelliği sayesinde gereksiz güncellemeleri göz ardı eder. yapılar kurmanıza olanak sağlayan sistemdir.
Veri ilişkilerini önceden belirleyerek değişiklik olduğunda okunacak ve yazılacak verileri koşullara bağlayabilir, okuma ve yazma öncesi ara katmanlar ekleyebilirsiniz Verileri wriré içerisinden oluşturarak değişimleri ile etkileyecek diğer değişkenleri bağımsız yapılar içinde yazabilirsiniz.
Ayrıca verilerinizi sistematik olarak depolar bu sayede veri türünü okuma ve yazma olaylarını denetleyebilirsiniz (getter/setter)
## Kullanım örneği # Kullanım örneği
### Kablo okuma ve değiştirme ## Veri yazma
Bir değişkeni `get` ile okuyabilir, `set` ile değiştirebilirsiniz. `get` fonksiyonu veriyi olduğu gibi döner. `set` fonksiyonuna bir fonksiyon verirseniz hemen çalıştıracak ve return edilen veriyi kabul edecektir Tanım
```typescript
declare function é(name: string, value: any): void;
```
### Basit
`name` ve `value` değerlerini `é` fonksiyonuna aktardığınızda bu ismi ve değeri global olarak depolar. Bu değeri istediğiniz herhangi bir noktada kullanabilirsiniz
```javascript ```javascript
// Yeni kablo oluşturun // İlk argümanı isim ve ikinci argümanı değer olarak alır ve depolar
let currency = é(+0.00); é('name', 'John');
é('surname', 'Carter');
é('age', '34');
```
// Değerini okuyun ### Gelişmiş
currency.get(); // --> 0
// Değerini değiştirin `name` alanına verdiğiniz değerler dosya sistemi benzeri dizinlenmiş olarak saklanır.
currency.set(5); Teknik olarak `/` ile ayırdığınız her alan için sistem o alan klasör gibi davranır ve son eğik çizgiden sonraki isme o değeri kaydeder. Teknik olarak `name`, `surname` ve `age` değerleri `userform` adı altında tutulur ve izlenir
currency.get(); // --> 5 ```javascript
é("userform/name/value", "John");
é("userform/name/required", true);
é("userform/surname/value", "carter");
é("userform/surname/required", true);
é("userform/age/value", "17");
é("userform/age/required", false);
```
// Değerini fonksiyon ile değiştirin ## Veri okuma
currency.set(oldValue => {
// Eski verinin üzerine 20 ekle Tanım
return oldValue + 20;
```typescript
declare function é(name: string): any;
```
### Basit
Bir değeri okumak için `é` fonksiyonune değerin ismini yazmanız yeterli
```javascript
let name = é("name");
console.log(name) // --> john
```
### Gelişmiş
Sistem yanlızca değerleri döner, dizinleri vermez. Bu yüzden değişkenin tam ismini kullanmanız gerekir
```javascript
é("userform/name/value", "John");
é("userform") // ----> undefined
é("userform/name") // ----> undefined
é("userform/name/value") // ----> "John" !!
é("userform/name/value/other") // ----> undefined
```
## Veriyi izleme
Tanım
```typescript
declare function é(callback: function, variables:string[]): any;
```
`variables` ismindeki veriler değiştiğinde `callback` fonksiyonunu tetikler.
### Basit
```javascript
é("number1", 20);
é("number2", 40);
é("number3", 60);
é("average", 0);
é(() => {
let total = é("number1") + é("number2") + é("number3");
é("average", total / 3);
},["number1","number2","number3"]);
```
Yukarıdaki kodda `number` isimli değişkenlerin herhangi birinin değişmesi durumunda en alttaki fonksiyonumuz çalışarak `average` değerini güncelleyecektir.
Değişiklik izleme tiplemeleride dikkate alan karmaşık bir karşılaştırma algoritması üzerinden yapılır böylecek number, string, boolean değişimleri dışında array ve object tipli değişkenlerin değerleri izlene rekürsif bir şekilde izlenir
```javascript
é("number1", 20);
é("number1", 20); // does nothing, no write, no access
é("number1", 21); // change detected, writed
é("number1", 21); // does nothing, no write, no access
é("object_variable", {var1: true,arr2: [1,2,3,[2]],callback: () => {}});
é("object_variable", {var1: true,arr2: [1,2,3,[2]],callback: () => {}}); // does nothing, no write, no access
é("object_variable", {var1: true,arr2: [1,2,3,[3]],callback: () => {}}); // does nothing, no write, no access
```
### Gelişmiş
Ayrıca dizinlenmiş veriler için iç değerlerin değişimi, üst dizinlerden de izlenebilir
```javascript
é("userform/register/name/value", "John");
é("userform/register/name/required", true);
é("userform/register/surname/value", "carter");
é("userform/register/surname/required", true);
é("userform/register/age/value", "17");
é("userform/register/age/required", false);
é(()=>{
console.log("changed userform -> register")
},["userform/register"]);
```
`userform/register` dizinin altındaki herhangi bir değerin değişmesi durumunda callback çağırılacaktır
## Okuma arabirimi (Middleware)
```typescript
declare namespace é {
function read(name: string, callback: (value:any) => any): void;
}
```
Okuma arabirimi bir değerin saklanmasından sonra `é` fonksiyonu üzerinden geri çağırılması durumunda durumunda devreye girer. Bazı verileri saklandığı gibi değilde, farklı bir formatta almak istediğinizde kullanışlıdır. Nesneye yönelik dillerde getter gibidir **ancak burda değişkenin bulunması zorunlu değildir**, önce getter daha sonra değer belirlenebilir
```javascript
é.read("code",usernameValue => {
return `CODE#` + usernameValue;
}); });
currency.get(); // --> 25 é("code", 117);
console.log(é("code")) // ---> CODE#117
``` ```
## Değişimler
Bir kablonun değerinin değiştiğini `watch` özelliğiyle dinleyebilirsiniz veya birden fazla kablonun değişimini aynı anda dinlemek için `é.watch` fonksiyonunu kullanabilirsiniz
```javascript ```javascript
let a = é(0);
let b = é(0);
a.watch(()=>{
// a kablosu değiştiğinde burası çalışacak
})
b.watch(()=>{
// b kablosu değiştiğinde burası çalışacak
})
é.watch(()=>{
// a veya b değiştiğinde burası çalışacak
},[a, b])
```
Örnek
```javascript
let toplayan = é(0);
let toplanan = é(0);
let sonuc = é(0);
é.watch(()=>{ é("person", null);
sonuc.set( toplayan.get() + toplanan.get() ) é("person/name", "John");
},[toplayan, toplanan]) é("person/surname", "Carter");
toplayan.watch(()=>{ é.read('person',function(){
console.log("Toplayan: ",toplayan.get()) return é("person/name") + " " + é("person/surname");
})
toplanan.watch(()=>{
console.log("Toplanan: ",toplanan.get())
})
sonuc.watch(()=>{
console.log("Değişen Sonuç: ",sonuc.get())
}) })
toplayan.set(7); console.log(é("person")) // ---> "John Carter"
toplanan.set(2);
console.log("Sonuç",sonuc.get()) é("person/name", "Bell");
console.log(é("person")) // ---> "Bell Carter"
``` ```
Konsol çıktısı aşağıdaki gibi olacaktır
``` ## Yazma arabirimi (Middleware)
Toplayan: 0
Toplanan: 0
Değişen Sonuç: 0
Toplayan: 7
Değişen Sonuç: 7
Toplanan: 2
Değişen Sonuç: 9
Sonuç: 9
```typescript
declare namespace é {
function write(name: string, callback: (value:any) => any): void;
}
``` ```
## Ara katmanlar Yazma arabirimi bir verinin kaydedilmesi sırasında devreye girer ve verilen verinin depolanmadan önce ek kontroller yapılmasına değiştirilmesini sağlamak için kullanılabilir. Nesneye yönelik dillerde setter gibidir **ancak burda değişkenin bulunması zorunlu değildir**, önce setter daha sonra değer belirlenebilir
### Okuma ara katmanı
Bazen orjinal değişkeni değiştirmeden her seferinde daha gelişmiş bir veri tipiyle okumak istersiniz Bir değerin tipinin değiştirilmesi
```javascript ```javascript
// Yeni kablo oluşturun
let user = é({ é("integer_value", 217.7525);
name: "John",
surname: "Wattson" é.write("integer_value",usernameValue => {
return parseInt(usernameValue);
}); });
// Normalde okuma katmanı eklemezseniz veri düz bir şekilde verecektir console.log(é("integer_value")) // ---> 217
user.get(); // --> {"name":"John",surname:"Wattson"}
// Verinin nasıl okunacağını belirleyin
user.readLayer((value, update)=>{
// value: orjinal veridir
// değiştirmek istediğinizde update fonksiyonunda verirsiniz
update(
`İsim: ${value.name}, soyisim: ${value.surname}`
)
})
// `get` fonksiyonu veriyi nasıl okuyacağını readLayer fonksiyonunda öğrenecektir
user.get(); // --> "İsim: John, soyisim: Wattson"
``` ```
### Yazma ara katmanı Bir listenin içerisindeki null verilerinin çıkarılarak saklanması
Bazen orjinal değişkeni değiştirmeden her seferinde daha gelişmiş bir veri tipiyle okumak istersiniz
```javascript ```javascript
// Yeni kablo oluşturun
let user = é([2,4,6,8,10]);
user.writeLayer((value, update)=>{ é.write("numberList",usernameValue => {
// Sadece çift sayıları alıyoruz return usernameValue.filter(e => e != null)
update(
value.filter(e => e % 2 == 0)
)
})
user.writeLayer((value, update)=>{
// Verinin sadece ilk 5 öğesini alıyoruz
update(
value.slice(0, 5)
)
})
// Orjinal veri
user.get(); // --> [2,4,6,8,10]
// set fonksiyonumuz gerçek veriyi değiştirmeden önce yukarıdaki iki yazma katmanını çalıştırıyorlar
// set fonksiyonumuz artık sadece ilk 5 çift sayıyı kaydediyor
user.set([10,11,12,13,14,15,16,17,18,19,20,21,22,23])
user.get(); // --> [10,12,14,16,18]
```
## Fark bulma algoritması
WiréS eşitlenmek istenen değeri zaten var olan değer ile karşılaştırarak farklı olup olmadığını algılayan özel bir algoritmaya sahiptir. Bu şekilde tekrarlı eşitlemelerde veya bellekte aynı şekilde tutulan veriler için yazma işlemi gerçekleştirmez
### Basit değişkenler
```javascript
let variable = é(0);
variable.set(false); // --> Değişecek
variable.set(7); // --> Değişecek
variable.set(7); // --> Değişmeyecek
variable.set(7 + 0); // --> Değişmeyecek
variable.set(-7); // --> Değişecek
```
### Objeler ve listeler
```javascript
let variable = é([1,2,3]);
variable.set([1,2,3,4]) // --> Değişecek
variable.set([1,2,3,4]) // --> Değişmeyecek
variable.set([1,0,3,4]) // --> Değişecek
variable.set() // --> Değişecek (undefined olarak)
variable.set(null) // --> Değişmeyecek
let variable = é({
name: null,
surname: "Wattson",
numbers: [1,2,3,4]
}); });
// İsmini null iken John olarak değiştiği için değişecek é("numberList", [1, null, 7, 63, 74, null, 7, 15]);
variable.set(oldValue => {
return { console.log(é("numberList")) // ---> [1, 7, 63, 74, 7, 15]
name: "John",
surname: "Wattson", ```
numbers: [1,2,3,4]
> [!WARNING] Dikkat
> getter ve setter metotları belirlendiği andan itibaren devreye girer, kendinden önce ve sonraki depolama veya okuma operasyonlarına karışmaz
## Veriyi kısıtlama
```typescript
declare namespace é {
function typedLock(name: string, {instance?:Function, type?:string, nullable?:boolean}): void;
}
```
### Veri tipini kısıtlama
`typedLock` fonksiyonu belirlenen `name` değeri için tipleme ve değersizlik (nullable) kısıtlamalarını uygular. Bu tipleme isteğe bağlı olarak değiştirilebilir veya tekrar belirlenebilir
```javascript
é("person/name", "John");
é.typedLock("person/name",{
type: "string",
nullable: false
});
é("person/name", null); // ----> Uncaught Error: value is not typeof number
é("person/name", 27); /// -----> Uncaught Error: value is not typeof string
```
### Veri tipi kısıtını kaldırma
`typedLock` fonksiyonu belirlenen `name` değeri için tipleme ve değersizlik (nullable) kısıtlama kurallarını siler
```javascript
é("person/name", "John");
é.typedLock("person/name",{
type: "string",
nullable: false
});
é("person/name", null); // ----> Uncaught Error: value is not typeof number
é("person/name", 27); /// -----> Uncaught Error: value is not typeof string
é.typedUnLock("person/name");
é("person/name", null); // ----> success, allowed!
é("person/name", 27); /// -----> success, allowed !
```
## Seti temizleme
WiréJS'de veriler ile arabirim ve veri tipi kısıtlayıcıları vs. ayrı ayrı tutulur. Veri bellek içerisinden silinse veya null değeri verilse dahil dinleyiciler aktif olarak çalışır.
```typescript
declare namespace é {
function delete(name: string): void;
}
```
Verilen veri isminden tüm verileri, kısıtlayıcıları, okuma ve yazma arabirimlerini temizler.
```javascript
é("name", "john");
// use updated callback
é(()=> console.log("name changed"),['name']);
// use setter callback
é.read('name',value => `--${value}--`);
é('name'); // ---> "--john---"
é('name','Bell'); // ---> name changed
// Delete name value, remove updated callbacks and setter callbacks
é.delete("name");
é('name'); // ---> undefined
é('name','Kyra'); // ---> do nothing....
é('name','Oliver'); // ---> do nothing....
é('name'); // ---> "Oliver"
```
## Detaylar
### DOM ile birlikte kullanma
```javascript
let nameInput = document.createElement("input");
// input değiştiğinde bildir
nameInput.oninput = () => é('name', nameInput.value);
é.read('name/valid',value => value.trim() != "");
let surnameInput = document.createElement("input");
// input değiştiğinde bildir
surnameInput.oninput = () => é('surname', surnameInput.value);
é.read('surname/valid',value => value.trim() != "");
let errorSpan = document.createElement("span");
é(() => surnameInput.value = é('message'),['message']);
let submitButton = document.createElement("button");
submitButton.disabled = true;
// submit_isActive değeri değiştiğinde disabled özelliğini değiştir
é(() => {
submitButton.disabled = !é("submit_isActive")
},["submit_isActive"]);
é(()=>{
if(é("name/valid") && é("surname/valid")){
é('message', "Form's input is valid")
é("submit_isActive", true)
}else{
é('message', "Please fill all inputs")
é("submit_isActive", false)
} }
})
// isim ve soyisim bir öncekiyle aynı olduğu için hiç bir şey yapılmaz },["name","surname"]);
variable.set({ ```
name: "John",
surname: "Wattson",
numbers: [1,2,3,4]
})
// Ayrıca fark bulmak algoritması sonsuz derinlikte tarama yapıp farkı anlayabilir ### Fark algoritmasını değiştirme
variable.set({
name: "John",
surname: "Wattson", Bazen verilerin değişimini algılamak için tipleri veya verinin içeriği kontrol etmek gereksiz olabilir. Senaryonuzu karmaşık veriler dışında basit primitive veriler üzerine kurguluyorsanız fark algoritmasını basitleştirerek performansı yarı yarıya artırabilirsiniz
numbers: [1,2,3,4,"Hello !"]
}) ```typescript
declare namespace é {
function diff(callback: (oldValue?:any, newValue?:any) => boolean): void;
}
```
Belirli bir verinin farklı bir algoritma kullanılarak karşılaştırılmasını sağlar
Örneğin sizin için basit bir (tipleme korumasız) bir karşılaştırma yetiyor olabilir
```javascript
é("number", 4);
é("number", 5); // ----> changed
é.diff("number", (oldValue, newValue) => oldValue != newValue);
é("number", 6); // ----> changed !
é("number", 0); // ----> changed !
é("number", false); // ----> no change
é("number", null); // ----> no change
é("number", undefined); // ----> no change
é("number", []); // ----> no change
é("number", 3); // ----> changed !
é("number", 1); // ----> changed !
é("number", true); // ----> no change
``` ```
### Sonsuz derinlikteki objeler Veya ortalaması aynı olan bir listenin eşitlenmesi gereksiz bir durum olabilir
Javascriptte bir objenin içindeki bir değer yine kendisini işaret ediyor olabilir, böyle bir durumda objenin içini tarayan algoritma aynı objeyi defalarca taramaması için (Maximum call stack hatası) önlem koyulmuştur
```javascript ```javascript
let infinite = {
one: 1,
two: 2,
three: 3,
onetwothree: undefined
};
// Objenin içine yine kendisini yerleştiriyoruz
infinite.onetwothree = infinite;
let variable = é(infinite); é("numberList", [ 0, 1, 2, 3]);
é("numberList", [-1, 1, 2, 3, 4]); // ----> changed !
/** é.diff("numberList", (oldValue, newValue) => {
* Aşağıdaki kodda WiréJS neyin değiştiğini anlamak için infinite let oldValueSum = oldValue.reduce((sum, num) => sum + num, 0) / oldValue.length;
* objesinin içine girdiğinde, içine girdiği objeleri bellekte let newValue = newValue.reduce((sum, num) => sum + num, 0) / newValue.length;
* tutarak aynı objeyi tekrar taramasını engelleyen yapıya return oldValueSum != newValue
* sahip olur
**/
// Sonsuz döngüde dönerek [Maximum call stack] hatası vereceğine three değerini başarılı bir şekilde değiştirir
variable.set(oldValue => {
// sadece three değerini 3 ile çarpıyoruz
return {
...oldValue,
three: oldValue.three * 3
}
}); });
```
é("numberList", [2]);
é("numberList", [ 1, 2, 3]); // ----> no change, no write
é("numberList", [ 0, 1, 2, 3, 4]); // ----> no change, no write
é("numberList", [-2, 0, 2, 4, 6]); // ----> no change, no write
é("numberList", [+1, 2, 3, 4, 5]) // ----> changed !
```
Bu karşılaştırmanın sonucunu depolamadığı gibi aynı zamanda dinleyicileride çalıştırmayacaktır

662
wire.js
View File

@ -1,300 +1,446 @@
/*
Flags
0x01 01 watch enabled
0x02 02 read layer enabled
0x04 04 write layer enable
0x08 08 initial state
0x10 16 changing state
0x20 32 immediate change
*/
(()=>{ (()=>{
let pipeLine = function(){ class É {
this.reads = []; map = new Map();
this.writes = []; assertThrow(statement, message){
this.read = (fn,pr) => this.reads.push({fn,pr}); if(statement === false)
this.write = (fn,pr) => this.writes.push({fn,pr}); {
this.get = (val) => { throw new Error(message);
let fns = this.reads.sort((a,b) => (a?.pr|0) - (b?.pr|0)), }
real = val;
for (const { fn } of fns) {
fn( real, e => real = e );
};
return real;
};
this.set = (val) => {
let fns = this.writes.sort((a,b) => (a?.pr|0) - (b?.pr|0)),
real = val;
for (const { fn } of fns) {
fn( real, e => real = e );
};
return real;
};
}
pipeLine.prototype.read = function(fn,pr){
this.reads.push({fn,pr})
}
pipeLine.prototype.write = function(fn,pr){
this.writes.writes({fn,pr})
}
pipeLine.prototype.get = function(val){
let fns = this.reads.sort((a,b) => (a?.pr|0) - (b?.pr|0)),
real = val;
for (const { fn } of fns) {
fn( real, e => real = e );
};
return real;
}
pipeLine.prototype.set = function(val){
let fns = this.writes.sort((a,b) => (a?.pr|0) - (b?.pr|0)),
real = val;
for (const { fn } of fns) {
fn( real, e => real = e );
};
return real;
}
function é(defaultValue)
{
if(this instanceof é)
{
this.value = é.extract(defaultValue);
this.version = 0;
this.effects = [];
this.piping = new pipeLine();
this.flag = 8;
}else{
return new é(defaultValue)
} }
} checkTyping(locks, value)
é.fingerPrint = Symbol("wire");
é.isWire = n => n?.fp == é.fingerPrint;
é.extract = (e,v) => {
if(typeof e=='function')
{ {
return é.freeze(e(v)) if(locks)
}else{ {
return é.freeze(e); if(locks.nullable === false)
}
};
é.freeze = a => {
if(Object.isFrozen(a))
{
return a;
}
switch(typeof a)
{
case "object":{
if(Array.isArray(a))
{ {
let k = [...a]; this.assertThrow(typeof value != "undefined","value is not nullable")
for (let name = 0; name < k.length; name++) { }
let value = k[name];
if(typeof value == "object") if(locks.instance)
{ {
k[name]=é.freeze(value); this.assertThrow(value instanceof (locks.instance),"value is not instanceof " + locks.instance.prototype.constructor.name)
} }
};
Object.freeze(k); if(locks.type)
return k; {
}else{ this.assertThrow(locks.type == typeof value,"value is not typeof " + locks.type)
let k = Object.assign({}, a);
for (const [name, value] of Object.entries(k)) {
if(typeof value == "object")
{
k[name]=é.freeze(value);
}
};
Object.freeze(k);
return k;
} }
} }
default: return a; }
}; setValue(path, value){
}; let isReadonly = this.getValue(path,'readonly') || false;
é.isSame = (a,b) => !é.diff(a,b);
é.watch = (fn, assoc) => { if(isReadonly) return;
for (const wireVar of assoc)
{ let writers = this.getValue(path,'writer') || [];
if(é.isWire(wireVar))
{ if(writers.length){
wireVar.watch(fn); 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(
é.diff = (a,b,c) => { path,
let cursoryDiffResult = cDiff(a,b); attributeName = "value"
if(cursoryDiffResult == cDiff.adiff) ){
{ let npath = this.normalizePath(path);
return adiff(a,b,c||[]) this.map.get(npath).delete(attributeName)
}else{ }
return cursoryDiffResult == cDiff.some ? false : true; 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
);
} }
} }
const adiff = (a,b,c) => { function é(arg1, arg2)
if(c.includes(a) && c.includes(b)) {
if("string" == typeof arg1)
{ {
console.error("Circular object detected !") if(arg2 === void 0)
return cObjectDiff(a,b); {
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 typea = a instanceof Array ? 0 : a.byteLength ? 1 : 2;
let typeb = b instanceof Array ? 0 : b.byteLength ? 1 : 2; let typeb = b instanceof Array ? 0 : b.byteLength ? 1 : 2;
if(typea != typeb) if (typea != typeb) {
{
return true; return true;
} }
if(typea==0) if (typea == 0) {
{ if (a.length != b.length) {
if(a.length != b.length) return true;
{
return true
} }
for(let k = 0; k < a.length; k++) for (let k = 0; k < a.length; k++) {
{ if (ediffEpsilon(a[k], b[k])) {
if(é.diff(a[k], b[k])) return true;
{
return true
} }
} }
return false return false;
} }
if(a instanceof Object) if (a instanceof Object) {
{ if (Object.keys(a).length != Object.keys(b).length) {
if(Object.keys(a).length != Object.keys(b).length) return true;
{
return true
} }
c.push(a); c.push(a);
c.push(b); c.push(b);
for (const key in a) for (const key in a) {
{ if (Object.hasOwnProperty.call(a, key)) {
if (Object.hasOwnProperty.call(a, key)) if (ediffEpsilon(a[key], b[key], c)) {
{ return true;
if(é.diff(a[key], b[key], c))
{
return true
} }
} }
} }
return false return false;
} }
}
const cArrDiff = (a,b) => {
return a.length != b.length || !a.every((v,i) => a[i] === v)
}; };
const cObjectDiff = (a,b) => {
return cArrDiff( 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(a),
Object.keys(b) Object.keys(b)
); );
}; };
const cDiff = (a,b) => {
switch(typeof a) function typedDiff(a, b){
if(typeof a == typeof b)
{ {
case "undefined": switch (typeof a) {
case "function":{ case "undefined":
return typeof a == typeof b ? cDiff.some : cDiff.different 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;
}
} }
case "symbol":
case "bigint":
case "boolean":
case "number":
case "string":{
return a === b ? cDiff.some : cDiff.different;
}
case "object":{
return cDiff.adiff;
}
};
}
cDiff.adiff = -1;
cDiff.some = 0;
cDiff.different = 1;
é.prototype.fp = é.fingerPrint;
é.prototype.diff = é.diff;
é.prototype.get = function(){
if(this.flag & 2)
{
return this.piping.get(this.value)
}else{ }else{
return this.value return typedDiff.different;
} }
}; };
é.prototype.set = function(newValue){
if(this.flag & 16) typedDiff.arrayDiff = -1;
{ typedDiff.some = 0;
return; typedDiff.different = 1;
}
newValue = é.extract(newValue, this.value);
if(this.flag & 4)
{
newValue = this.piping.set(newValue)
};
if(this.diff(newValue,this.value))
{
this.flag = this.flag | 16;
if(this.flag & 1)
{
schedule((()=>{
this.effects.filter(e => e.o).forEach(k => k())
}));
}
this.value = newValue;
this.version++;
if(this.flag & 1)
{
schedule((()=>{
this.effects.forEach(e => e.f());
}));
}
this.flag = this.flag ^ 16;
}
};
const schedule = function(fn){
schedule.jobs.push(fn)
if(!schedule.executing)
{
requestAnimationFrame(()=>{
for (const fn of schedule.jobs) {
try{
fn()
}catch(e){
console.error(e)
}
};
schedule.jobs=[];
})
}
};
schedule.executing = false;
schedule.jobs = [];
é.prototype.watch = function(fn){
this.flag = this.flag | 1;
let k = {
f:fn,
o:undefined
};
requestAnimationFrame(()=> {
k.o = fn();
})
this.effects.push(k);
};
é.prototype.getVersion = function(){
return this.version;
}
é.prototype.equalTo = function(value){
return é.isSame(value, this.value)
}
é.prototype.readLayer = function(fn,priority){
this.flag = this.flag | 2;
this.piping.read(fn,priority)
}
é.prototype.writeLayer = function(fn,priority){
this.flag = this.flag | 4;
this.piping.write(fn,priority)
}
try{ try{
module.exports = é; module.exports = é;
}catch{ }catch{
window.é = é; window.é = é;
} }
})(); })();