Compare commits

..

3 Commits
v2 ... v1

Author SHA1 Message Date
Abdussamed 833c7182de Expol 2024-04-03 04:50:41 +03:00
Abdussamed fba60407e4 Arguments error fix 2023-11-10 21:37:49 +03:00
Abdussamed c71a180d8a package json added new version 2023-06-18 16:12:11 +03:00
6 changed files with 1339 additions and 769 deletions

525
Hemex.js Normal file
View File

@ -0,0 +1,525 @@
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)
};

11
document.html Normal file
View File

@ -0,0 +1,11 @@
<!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 Normal file
View File

@ -0,0 +1,359 @@
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",
"version": "0.2.0",
"version": "1.0.0",
"type": "commonjs",
"private": false,
"description": "Dont use variable, use smart variable wires !",

549
readme.md
View File

@ -1,420 +1,241 @@
# WiréJS | Değişken değil, kablo kullanın !
# Açıklama
## Açıklama
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
yapılar kurmanıza olanak sağlayan sistemdir.
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.
Verileri getter/setter fonksiyonlarıyla sararak gerçek değişikleri algılayan özelliği sayesinde gereksiz güncellemeleri göz ardı eder.
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)
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
# Kullanım örneği
## Kullanım örneği
## Veri yazma
### Kablo okuma ve değiştirme
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
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
```javascript
// İlk argümanı isim ve ikinci argümanı değer olarak alır ve depolar
é('name', 'John');
é('surname', 'Carter');
é('age', '34');
```
// Yeni kablo oluşturun
let currency = é(+0.00);
### Gelişmiş
// Değerini okuyun
currency.get(); // --> 0
`name` alanına verdiğiniz değerler dosya sistemi benzeri dizinlenmiş olarak saklanır.
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
// Değerini değiştirin
currency.set(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);
```
currency.get(); // --> 5
## Veri okuma
Tanım
```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;
// Değerini fonksiyon ile değiştirin
currency.set(oldValue => {
// Eski verinin üzerine 20 ekle
return oldValue + 20;
});
é("code", 117);
console.log(é("code")) // ---> CODE#117
currency.get(); // --> 25
```
## 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
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);
é("person", null);
é("person/name", "John");
é("person/surname", "Carter");
é.watch(()=>{
sonuc.set( toplayan.get() + toplanan.get() )
},[toplayan, toplanan])
é.read('person',function(){
return é("person/name") + " " + é("person/surname");
toplayan.watch(()=>{
console.log("Toplayan: ",toplayan.get())
})
toplanan.watch(()=>{
console.log("Toplanan: ",toplanan.get())
})
sonuc.watch(()=>{
console.log("Değişen Sonuç: ",sonuc.get())
})
console.log(é("person")) // ---> "John Carter"
toplayan.set(7);
toplanan.set(2);
é("person/name", "Bell");
console.log(é("person")) // ---> "Bell Carter"
console.log("Sonuç",sonuc.get())
```
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;
}
```
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
## Ara katmanlar
### Okuma ara katmanı
Bir değerin tipinin değiştirilmesi
Bazen orjinal değişkeni değiştirmeden her seferinde daha gelişmiş bir veri tipiyle okumak istersiniz
```javascript
é("integer_value", 217.7525);
é.write("integer_value",usernameValue => {
return parseInt(usernameValue);
// Yeni kablo oluşturun
let user = é({
name: "John",
surname: "Wattson"
});
console.log(é("integer_value")) // ---> 217
// Normalde okuma katmanı eklemezseniz veri düz bir şekilde verecektir
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"
```
Bir listenin içerisindeki null verilerinin çıkarılarak saklanması
### Yazma ara katmanı
Bazen orjinal değişkeni değiştirmeden her seferinde daha gelişmiş bir veri tipiyle okumak istersiniz
```javascript
// Yeni kablo oluşturun
let user = é([2,4,6,8,10]);
é.write("numberList",usernameValue => {
return usernameValue.filter(e => e != null)
user.writeLayer((value, update)=>{
// Sadece çift sayıları alıyoruz
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]
});
é("numberList", [1, null, 7, 63, 74, null, 7, 15]);
console.log(é("numberList")) // ---> [1, 7, 63, 74, 7, 15]
```
> [!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)
// İsmini null iken John olarak değiştiği için değişecek
variable.set(oldValue => {
return {
name: "John",
surname: "Wattson",
numbers: [1,2,3,4]
}
})
// isim ve soyisim bir öncekiyle aynı olduğu için hiç bir şey yapılmaz
variable.set({
name: "John",
surname: "Wattson",
numbers: [1,2,3,4]
})
// Ayrıca fark bulmak algoritması sonsuz derinlikte tarama yapıp farkı anlayabilir
variable.set({
name: "John",
surname: "Wattson",
numbers: [1,2,3,4,"Hello !"]
})
},["name","surname"]);
```
### Fark algoritmasını değiştirme
### Sonsuz derinlikteki objeler
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
```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
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
let infinite = {
one: 1,
two: 2,
three: 3,
onetwothree: undefined
};
// Objenin içine yine kendisini yerleştiriyoruz
infinite.onetwothree = infinite;
é("number", 4);
let variable = é(infinite);
é("number", 5); // ----> changed
/**
* Aşağıdaki kodda WiréJS neyin değiştiğini anlamak için infinite
* objesinin içine girdiğinde, içine girdiği objeleri bellekte
* tutarak aynı objeyi tekrar taramasını engelleyen yapıya
* sahip olur
**/
é.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
```
Veya ortalaması aynı olan bir listenin eşitlenmesi gereksiz bir durum olabilir
```javascript
é("numberList", [ 0, 1, 2, 3]);
é("numberList", [-1, 1, 2, 3, 4]); // ----> changed !
é.diff("numberList", (oldValue, newValue) => {
let oldValueSum = oldValue.reduce((sum, num) => sum + num, 0) / oldValue.length;
let newValue = newValue.reduce((sum, num) => sum + num, 0) / newValue.length;
return oldValueSum != newValue
// 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,446 +1,300 @@
/*
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
*/
(()=>{
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
);
}
let pipeLine = function(){
this.reads = [];
this.writes = [];
this.read = (fn,pr) => this.reads.push({fn,pr});
this.write = (fn,pr) => this.writes.push({fn,pr});
this.get = (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;
};
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;
};
}
function é(arg1, arg2)
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("string" == typeof arg1)
if(this instanceof é)
{
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);
}
this.value = é.extract(defaultValue);
this.version = 0;
this.effects = [];
this.piping = new pipeLine();
this.flag = 8;
}else{
return new é(defaultValue)
}
}
é.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)
é.fingerPrint = Symbol("wire");
é.isWire = n => n?.fp == é.fingerPrint;
é.extract = (e,v) => {
if(typeof e=='function')
{
return é.freeze(e(v))
}else{
return é.freeze(e);
}
};
é.typedLock = (path, options) => {
é.current.setTypedLocks(path,options)
é.freeze = a => {
if(Object.isFrozen(a))
{
return a;
}
switch(typeof a)
{
case "object":{
if(Array.isArray(a))
{
let k = [...a];
for (let name = 0; name < k.length; name++) {
let value = k[name];
if(typeof value == "object")
{
k[name]=é.freeze(value);
}
};
Object.freeze(k);
return k;
}else{
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;
};
};
é.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;
é.isSame = (a,b) => !é.diff(a,b);
é.watch = (fn, assoc) => {
for (const wireVar of assoc)
{
if(é.isWire(wireVar))
{
wireVar.watch(fn);
}
}
};
function objectDiff(a, b, c){
if (c.includes(a) && c.includes(b)) {
console.error("Circular object detected !");
return objectKeyDiff(a, b);
é.diff = (a,b,c) => {
let cursoryDiffResult = cDiff(a,b);
if(cursoryDiffResult == cDiff.adiff)
{
return adiff(a,b,c||[])
}else{
return cursoryDiffResult == cDiff.some ? false : true;
}
}
const adiff = (a,b,c) => {
if(c.includes(a) && c.includes(b))
{
console.error("Circular object detected !")
return cObjectDiff(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) {
if(typea != typeb)
{
return true;
}
if (typea == 0) {
if (a.length != b.length) {
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;
for(let k = 0; k < a.length; k++)
{
if(é.diff(a[k], b[k]))
{
return true
}
}
return false;
return false
}
if (a instanceof Object) {
if (Object.keys(a).length != Object.keys(b).length) {
return true;
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;
for (const key in a)
{
if (Object.hasOwnProperty.call(a, key))
{
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)
};
function arrayLengthPointerDiff(a, b){
return a.length != b.length || !a.every((v, i) => a[i] === v);
};
function objectKeyDiff(a, b){
return arrayLengthPointerDiff(
const cObjectDiff = (a,b) => {
return cArrDiff(
Object.keys(a),
Object.keys(b)
);
};
function typedDiff(a, b){
if(typeof a == typeof b)
const cDiff = (a,b) => {
switch(typeof a)
{
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;
}
case "undefined":
case "function":{
return typeof a == typeof b ? cDiff.some : cDiff.different
}
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{
return typedDiff.different;
return this.value
}
};
typedDiff.arrayDiff = -1;
typedDiff.some = 0;
typedDiff.different = 1;
é.prototype.set = function(newValue){
if(this.flag & 16)
{
return;
}
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{
module.exports = é;
}catch{
window.é = é;
}
})();
})();