Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
Abdussamed | 833c7182de | |
Abdussamed | fba60407e4 | |
Abdussamed | c71a180d8a |
|
@ -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)
|
||||||
|
};
|
|
@ -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>
|
|
@ -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
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@saqut/wirejs",
|
"name": "@saqut/wirejs",
|
||||||
"version": "0.2.0",
|
"version": "1.0.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
549
readme.md
|
@ -1,420 +1,241 @@
|
||||||
# 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éJS, dizinlenmiş akıllı değişkenler oluşturup kendi kendini yönetebilen veri değişimini kolayca yönetebilen sistemler oluşturabileceğiniz
|
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.
|
||||||
yapılar kurmanıza olanak sağlayan sistemdir.
|
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.
|
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
|
||||||
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
|
||||||
|
|
||||||
## Veri yazma
|
### Kablo okuma ve değiştirme
|
||||||
|
|
||||||
Tanım
|
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
|
||||||
|
|
||||||
```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
|
||||||
// İlk argümanı isim ve ikinci argümanı değer olarak alır ve depolar
|
// Yeni kablo oluşturun
|
||||||
é('name', 'John');
|
let currency = é(+0.00);
|
||||||
é('surname', 'Carter');
|
|
||||||
é('age', '34');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Gelişmiş
|
// Değerini okuyun
|
||||||
|
currency.get(); // --> 0
|
||||||
|
|
||||||
`name` alanına verdiğiniz değerler dosya sistemi benzeri dizinlenmiş olarak saklanır.
|
// Değerini değiştirin
|
||||||
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.set(5);
|
||||||
|
|
||||||
```javascript
|
currency.get(); // --> 5
|
||||||
é("userform/name/value", "John");
|
|
||||||
é("userform/name/required", true);
|
|
||||||
é("userform/surname/value", "carter");
|
|
||||||
é("userform/surname/required", true);
|
|
||||||
é("userform/age/value", "17");
|
|
||||||
é("userform/age/required", false);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Veri okuma
|
// Değerini fonksiyon ile değiştirin
|
||||||
|
currency.set(oldValue => {
|
||||||
Tanım
|
// Eski verinin üzerine 20 ekle
|
||||||
|
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;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
é("code", 117);
|
currency.get(); // --> 25
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
é("person", null);
|
é.watch(()=>{
|
||||||
é("person/name", "John");
|
sonuc.set( toplayan.get() + toplanan.get() )
|
||||||
é("person/surname", "Carter");
|
},[toplayan, toplanan])
|
||||||
|
|
||||||
é.read('person',function(){
|
toplayan.watch(()=>{
|
||||||
return é("person/name") + " " + é("person/surname");
|
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("Sonuç",sonuc.get())
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
```javascript
|
||||||
|
// Yeni kablo oluşturun
|
||||||
é("integer_value", 217.7525);
|
let user = é({
|
||||||
|
name: "John",
|
||||||
é.write("integer_value",usernameValue => {
|
surname: "Wattson"
|
||||||
return parseInt(usernameValue);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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
|
```javascript
|
||||||
|
// Yeni kablo oluşturun
|
||||||
|
let user = é([2,4,6,8,10]);
|
||||||
|
|
||||||
é.write("numberList",usernameValue => {
|
user.writeLayer((value, update)=>{
|
||||||
return usernameValue.filter(e => e != null)
|
// 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]);
|
// İsmini null iken John olarak değiştiği için değişecek
|
||||||
|
variable.set(oldValue => {
|
||||||
console.log(é("numberList")) // ---> [1, 7, 63, 74, 7, 15]
|
return {
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
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
|
|
||||||
|
|
||||||
```javascript
|
```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);
|
// 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 => {
|
||||||
é("number", 6); // ----> changed !
|
// sadece three değerini 3 ile çarpıyoruz
|
||||||
é("number", 0); // ----> changed !
|
return {
|
||||||
é("number", false); // ----> no change
|
...oldValue,
|
||||||
é("number", null); // ----> no change
|
three: oldValue.three * 3
|
||||||
é("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
|
|
||||||
});
|
});
|
||||||
|
```
|
||||||
é("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
662
wire.js
|
@ -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 É {
|
let pipeLine = function(){
|
||||||
map = new Map();
|
this.reads = [];
|
||||||
assertThrow(statement, message){
|
this.writes = [];
|
||||||
if(statement === false)
|
this.read = (fn,pr) => this.reads.push({fn,pr});
|
||||||
{
|
this.write = (fn,pr) => this.writes.push({fn,pr});
|
||||||
throw new Error(message);
|
this.get = (val) => {
|
||||||
}
|
let fns = this.reads.sort((a,b) => (a?.pr|0) - (b?.pr|0)),
|
||||||
}
|
real = val;
|
||||||
checkTyping(locks, value)
|
for (const { fn } of fns) {
|
||||||
{
|
fn( real, e => real = e );
|
||||||
if(locks)
|
};
|
||||||
{
|
return real;
|
||||||
if(locks.nullable === false)
|
};
|
||||||
{
|
this.set = (val) => {
|
||||||
this.assertThrow(typeof value != "undefined","value is not nullable")
|
let fns = this.writes.sort((a,b) => (a?.pr|0) - (b?.pr|0)),
|
||||||
}
|
real = val;
|
||||||
|
for (const { fn } of fns) {
|
||||||
if(locks.instance)
|
fn( real, e => real = e );
|
||||||
{
|
};
|
||||||
this.assertThrow(value instanceof (locks.instance),"value is not instanceof " + locks.instance.prototype.constructor.name)
|
return real;
|
||||||
}
|
};
|
||||||
|
|
||||||
if(locks.type)
|
|
||||||
{
|
|
||||||
this.assertThrow(locks.type == typeof value,"value is not typeof " + locks.type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setValue(path, value){
|
|
||||||
let isReadonly = this.getValue(path,'readonly') || false;
|
|
||||||
|
|
||||||
if(isReadonly) return;
|
|
||||||
|
|
||||||
let writers = this.getValue(path,'writer') || [];
|
|
||||||
|
|
||||||
if(writers.length){
|
|
||||||
let oldvalue = this.getValue(path);
|
|
||||||
for (const writer of writers)
|
|
||||||
{
|
|
||||||
value = writer(value,oldvalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let locks = this.getValue(path,'locks') || false;
|
|
||||||
|
|
||||||
locks && this.checkTyping(locks, value)
|
|
||||||
|
|
||||||
let {npath,process} = this.setAttribute(path, "value", value);
|
|
||||||
|
|
||||||
// optimization
|
|
||||||
if(!process){
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
let callers = [];
|
|
||||||
|
|
||||||
this.mapHierachy(npath, node => {
|
|
||||||
let localCallers = node.has("changelistener") ? node.get("changelistener") : [];
|
|
||||||
if(localCallers.length){
|
|
||||||
callers = callers.concat(localCallers);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(callers.length) {
|
|
||||||
return callers.filter(e=>typeof e == "function").map(callbacks => {
|
|
||||||
let result;
|
|
||||||
try{
|
|
||||||
result = callbacks(npath)
|
|
||||||
}catch(error){
|
|
||||||
result = error
|
|
||||||
}finally{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}).filter(e => e !== void 0);
|
|
||||||
}else{
|
|
||||||
return callers.filter(e=>typeof e == "function").map(callbacks => {
|
|
||||||
let result;
|
|
||||||
try{
|
|
||||||
result = callbacks(npath)
|
|
||||||
}catch(error){
|
|
||||||
result = error
|
|
||||||
}finally{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}).filter(e => e !== void 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
deleteAttribute(
|
|
||||||
path,
|
|
||||||
attributeName = "value"
|
|
||||||
){
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
this.map.get(npath).delete(attributeName)
|
|
||||||
}
|
|
||||||
removeNode(path){
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
this.map.delete(npath);
|
|
||||||
}
|
|
||||||
removeAttribute(
|
|
||||||
path,
|
|
||||||
attributeName
|
|
||||||
){
|
|
||||||
if(this.map.has(path))
|
|
||||||
{
|
|
||||||
this.map.delete(attributeName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setAttribute(
|
|
||||||
path,
|
|
||||||
attributeName = "value",
|
|
||||||
value,
|
|
||||||
callableValue = true
|
|
||||||
){
|
|
||||||
let process;
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
if(this.map.has(path))
|
|
||||||
{
|
|
||||||
let node = this.map.get(npath);
|
|
||||||
if(typeof value == "function" && callableValue)
|
|
||||||
{
|
|
||||||
let oldv = node.get(attributeName);
|
|
||||||
let newv = value(oldv);
|
|
||||||
let diffAlgorithm = node.has('diff') ? node.get('diff') : ediffEpsilon;
|
|
||||||
if(diffAlgorithm(oldv, newv))
|
|
||||||
{
|
|
||||||
node.set(attributeName, newv)
|
|
||||||
process = 'update';
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
let oldv = node.get(attributeName);
|
|
||||||
let diffAlgorithm = node.has('diff') ? node.get('diff') : ediffEpsilon;
|
|
||||||
if(diffAlgorithm(oldv, value))
|
|
||||||
{
|
|
||||||
this.map.get(npath).set(attributeName, value)
|
|
||||||
process = 'update';
|
|
||||||
}
|
|
||||||
this.map.get(npath).set(attributeName, value)
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
let valueMap = new Map();
|
|
||||||
valueMap.set(attributeName, typeof value == "function" ? value() : value);
|
|
||||||
this.map.set(npath, valueMap);
|
|
||||||
process = 'create';
|
|
||||||
}
|
|
||||||
return {npath,process};
|
|
||||||
}
|
|
||||||
mapHierachy(npath, mapCallback){
|
|
||||||
let npaths = npath.split('/');
|
|
||||||
let t = [], current = "";
|
|
||||||
for (const path of npaths)
|
|
||||||
{
|
|
||||||
t.push(path);
|
|
||||||
current = t.join('/');
|
|
||||||
if(this.map.has(current))
|
|
||||||
{
|
|
||||||
mapCallback(this.map.get(current))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createListenerForPath(path, callback){
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
this.setAttribute(
|
|
||||||
npath,
|
|
||||||
'changelistener',
|
|
||||||
callbacksOrNull => (callbacksOrNull ? callbacksOrNull.concat([callback]) : [callback])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
getValue(path, attribute = "value"){
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
let staticValue = this.map.get(npath)?.get(attribute);
|
|
||||||
if(attribute == 'value')
|
|
||||||
{
|
|
||||||
let readers = this.getValue(path,'reader') || [];
|
|
||||||
if(readers.length){
|
|
||||||
let oldvalue = staticValue, value;
|
|
||||||
for (const reader of readers)
|
|
||||||
{
|
|
||||||
value = reader(oldvalue, value);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}else{
|
|
||||||
return staticValue;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
return staticValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
normalizePath(str){
|
|
||||||
if(!str)
|
|
||||||
{
|
|
||||||
return ':moment:'
|
|
||||||
}else if(!(typeof str == "string")){
|
|
||||||
throw new Error("É is not valid path ")
|
|
||||||
}else{
|
|
||||||
if(str.indexOf('/') == -1)
|
|
||||||
{
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
let path = str.split('/').map(e => e.trim());
|
|
||||||
let first = path[0]
|
|
||||||
let last = path[path.length - 1];
|
|
||||||
if(first == '') path.shift();
|
|
||||||
if(last == '') path.pop();
|
|
||||||
let result = path.join('/');
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createListenerForWrite(path, callback){
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
this.setAttribute(
|
|
||||||
npath,
|
|
||||||
'writer',
|
|
||||||
value => (value || []).concat([callback]),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
createListenerForRead(path, callback){
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
this.setAttribute(
|
|
||||||
npath,
|
|
||||||
'reader',
|
|
||||||
value => (value || []).concat([callback]),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
changeDifferenceAlgorithm(path, callback){
|
|
||||||
this.setAttribute(
|
|
||||||
path,
|
|
||||||
'diff',
|
|
||||||
callback,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
lockValue(path){
|
|
||||||
é.current.setAttribute(path, 'readonly', true);
|
|
||||||
}
|
|
||||||
unlockValue(path){
|
|
||||||
é.current.removeAttribute(path, 'readonly');
|
|
||||||
}
|
|
||||||
setTypedLocks(path, options){
|
|
||||||
let npath = this.normalizePath(path);
|
|
||||||
if(options.instance !== void 0)
|
|
||||||
{
|
|
||||||
this.setAttribute(
|
|
||||||
npath,
|
|
||||||
'locks',
|
|
||||||
obj => ({
|
|
||||||
...(obj || {}),
|
|
||||||
instance: options.instance
|
|
||||||
}),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if(options.type !== void 0)
|
|
||||||
{
|
|
||||||
this.setAttribute(
|
|
||||||
npath,
|
|
||||||
'locks',
|
|
||||||
obj => ({
|
|
||||||
...(obj || {}),
|
|
||||||
type: options.type
|
|
||||||
}),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if(options.nullable !== void 0)
|
|
||||||
{
|
|
||||||
this.setAttribute(
|
|
||||||
npath,
|
|
||||||
'locks',
|
|
||||||
obj => ({
|
|
||||||
...(obj || {}),
|
|
||||||
nullable: options.nullable
|
|
||||||
}),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setTypedUnlocks(path, options){
|
|
||||||
this.setAttribute(
|
|
||||||
path,
|
|
||||||
'locks',
|
|
||||||
void 0,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function é(arg1, arg2)
|
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)
|
this.value = é.extract(defaultValue);
|
||||||
{
|
this.version = 0;
|
||||||
return é.current.getValue(arg1)
|
this.effects = [];
|
||||||
}
|
this.piping = new pipeLine();
|
||||||
|
this.flag = 8;
|
||||||
if(["bigint","boolean"/*,"function"*/,"number","object","symbol","string"].includes(typeof arg2) || arg2 === null)
|
}else{
|
||||||
{
|
return new é(defaultValue)
|
||||||
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 É();
|
é.fingerPrint = Symbol("wire");
|
||||||
é.write = (...args) => é.current.createListenerForWrite(...args);
|
é.isWire = n => n?.fp == é.fingerPrint;
|
||||||
é.read = (...args) => é.current.createListenerForRead(...args);
|
é.extract = (e,v) => {
|
||||||
é.diff = (...args) => é.current.changeDifferenceAlgorithm(...args);
|
if(typeof e=='function')
|
||||||
|
{
|
||||||
é.var = (path, value, write, read) => {
|
return é.freeze(e(v))
|
||||||
é.current.setValue(path, value);
|
}else{
|
||||||
é.current.createListenerForWrite(path, write);
|
return é.freeze(e);
|
||||||
é.current.createListenerForWrite(path, read);
|
|
||||||
return {
|
|
||||||
get: () => é.current.getValue(path),
|
|
||||||
set: (value) => é.current.setValue(path, value)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
é.freeze = a => {
|
||||||
é.typedLock = (path, options) => {
|
if(Object.isFrozen(a))
|
||||||
é.current.setTypedLocks(path,options)
|
{
|
||||||
|
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;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
é.isSame = (a,b) => !é.diff(a,b);
|
||||||
é.typedUnLock = path => {
|
é.watch = (fn, assoc) => {
|
||||||
é.current.setTypedUnlocks(path)
|
for (const wireVar of assoc)
|
||||||
};
|
{
|
||||||
|
if(é.isWire(wireVar))
|
||||||
é.delete = path => {
|
{
|
||||||
é.current.removeNode(path);
|
wireVar.watch(fn);
|
||||||
};
|
}
|
||||||
|
|
||||||
é.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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
é.diff = (a,b,c) => {
|
||||||
function objectDiff(a, b, c){
|
let cursoryDiffResult = cDiff(a,b);
|
||||||
if (c.includes(a) && c.includes(b)) {
|
if(cursoryDiffResult == cDiff.adiff)
|
||||||
console.error("Circular object detected !");
|
{
|
||||||
return objectKeyDiff(a, b);
|
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 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) {
|
{
|
||||||
return true;
|
if(a.length != b.length)
|
||||||
|
{
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
for (let k = 0; k < a.length; k++) {
|
for(let k = 0; k < a.length; k++)
|
||||||
if (ediffEpsilon(a[k], b[k])) {
|
{
|
||||||
return true;
|
if(é.diff(a[k], b[k]))
|
||||||
|
{
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
if (a instanceof Object) {
|
if(a instanceof Object)
|
||||||
if (Object.keys(a).length != Object.keys(b).length) {
|
{
|
||||||
return true;
|
if(Object.keys(a).length != Object.keys(b).length)
|
||||||
|
{
|
||||||
|
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 (ediffEpsilon(a[key], b[key], c)) {
|
if (Object.hasOwnProperty.call(a, key))
|
||||||
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) => {
|
||||||
function arrayLengthPointerDiff(a, b){
|
return cArrDiff(
|
||||||
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) => {
|
||||||
function typedDiff(a, b){
|
switch(typeof a)
|
||||||
if(typeof a == typeof b)
|
|
||||||
{
|
{
|
||||||
switch (typeof a) {
|
case "undefined":
|
||||||
case "undefined":
|
case "function":{
|
||||||
case "function": {
|
return typeof a == typeof b ? cDiff.some : cDiff.different
|
||||||
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 typedDiff.different;
|
return this.value
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
é.prototype.set = function(newValue){
|
||||||
typedDiff.arrayDiff = -1;
|
if(this.flag & 16)
|
||||||
typedDiff.some = 0;
|
{
|
||||||
typedDiff.different = 1;
|
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{
|
try{
|
||||||
module.exports = é;
|
module.exports = é;
|
||||||
}catch{
|
}catch{
|
||||||
window.é = é;
|
window.é = é;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
Loading…
Reference in New Issue