2
0
mirror of https://github.com/esiur/iui.git synced 2025-05-06 06:42:58 +00:00
This commit is contained in:
Ahmed Zamil 2021-11-15 00:11:12 +03:00
parent 589c4f3227
commit e52b89fb4d
13 changed files with 213 additions and 41 deletions

View File

@ -1,6 +1,7 @@
<html> <html>
<head> <head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
@ -15,7 +16,7 @@
<meta name="msapplication-TileColor" content="#ffc40d"> <meta name="msapplication-TileColor" content="#ffc40d">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>
<link rel="stylesheet" <link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css"> href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.3.1/styles/default.min.css">
@ -95,6 +96,14 @@
<i-route name="if-content" caption="Conditions and Filling" :content="load(this)"> <i-route name="if-content" caption="Conditions and Filling" :content="load(this)">
</i-route> </i-route>
<i-route name="scripts" caption="Scripts" :content="load(this)">
</i-route>
<i-route name="referencing" caption="Referencing" :content="load(this)">
</i-route>
</i-route> </i-route>

View File

@ -0,0 +1,5 @@
# I-Input
<i-codepreview>
</i-codepreview>

View File

@ -22,3 +22,8 @@ When the document is loaded or new HTML is inserted in the document, IUI process
Render invokes the data binding functions and sets the element text nodes and attributes to the value returned by the binding functions. The process is recursive to all children in the element tree (**IUIElement** and **HTMLElement**) Render invokes the data binding functions and sets the element text nodes and attributes to the value returned by the binding functions. The process is recursive to all children in the element tree (**IUIElement** and **HTMLElement**)
The rootElement is the first `<i-app>` in the document when its loaded. The rootElement is the first `<i-app>` in the document when its loaded.
# Extension to the Scope
IUI components might extend the scope with

View File

@ -1,7 +1,9 @@
# Data flow # Data flow
When the :data attribute is set to an element any other attribute and child will be able to access this field directly using the variable *data* or the shortended *d*. When the :data attribute is set to an element any other attribute and child will be able to access this field directly using the variable *data* or the shortended *d*.
It is a scope variable responsible for the rerendering process of the view when the value provided implements **Modifiable** interface, such as the Esiur distributed objects.
<i-codepreview> <i-codepreview>
<div :data="{name: 'Ahmed Zamil', job: 'Developer'} "> <div :data="{name: 'Ahmed Zamil', job: 'Developer'} ">

View File

@ -1,2 +1,20 @@
# Referencing # Referencing
To avoid duplication, IUI relies on referencing instead of element id.
Refs is a scope variable contains a collection of every element in the tree, unless overwritten by another component.
<i-codepreview>
<button @click="refs.modification.innerHTML = new Date()">Click me</button>
<div>Last modification : </div><div ref="modification"></div>
</i-codepreview>
When a duplicated referecing happens, the key in the refs collection will represent an array of the duplicated elements. *note: this always happends in I-Repeat components*
<i-codepreview>
<button @click="refs.name[0].innerHTML = 'Zak'; refs.name[1].innerHTML = 'Bilal'">
Click me
</button>
<div>First Name : </div><div ref="name"></div>
<div>Last Name : </div><div ref="name"></div>
</i-codepreview>

View File

@ -11,3 +11,14 @@ These variables propagate from an element to its children and could be altered o
<div>Time: ${now.toTimeString()}</div> <div>Time: ${now.toTimeString()}</div>
</div> </div>
</i-codepreview> </i-codepreview>
# Events
Events can access scope variables when declared with *@eventName*
<i-codepreview>
<div :scope="{now: new Date()}">
<button @click="this.innerHTML = now">Click me</button>
</div>
</i-codepreview>

View File

@ -0,0 +1,38 @@
#Scripts
Scripts within tags are executed as functions with the scope varaiables and *this* argument is set to the parent element
<i-codepreview>
<button>
<script>
this.onclick = () => { alert("Hello World !")};
this.innerHTML = "Click me";
</script>
</button>
</i-codepreview>
When the function returns an object it will be projected to the parent element.
<i-codepreview>
<button>
<script>
this.onclick = () => { alert("Hello World !")};
return {innerHTML: "Click Me"}
</script>
</button>
</i-codepreview>
Scope variables example
<i-codepreview>
<button>
<script>
this.onclick = () => {
refs.msg.innerHTML = new Date().toTimeString();
};
return {innerHTML: "Click Me"}
</script>
</button>
<div ref="msg"></div>
</i-codepreview>

View File

@ -59,11 +59,19 @@ export class Binding {
isAsync = true; isAsync = true;
attrType = AttributeBindingDestination.Field; attrType = AttributeBindingDestination.Field;
attrKey = nodeOrAttributeOrIUIElement.name.substr(6); attrKey = nodeOrAttributeOrIUIElement.name.substr(6);
// skip scope
// if (attrKey == "scope")
// return null;
} }
else if (nodeOrAttributeOrIUIElement.name.startsWith(":")) { else if (nodeOrAttributeOrIUIElement.name.startsWith(":")) {
isAsync = false; isAsync = false;
attrType = AttributeBindingDestination.Field; attrType = AttributeBindingDestination.Field;
attrKey = nodeOrAttributeOrIUIElement.name.substr(1); attrKey = nodeOrAttributeOrIUIElement.name.substr(1);
// skip scope
// if (attrKey == "scope")
// return null;
} }
else { else {
return null; return null;
@ -151,7 +159,7 @@ export class Binding {
try { try {
let d = this.func.apply(thisArg, [proxy, proxy, {}, true let d = this.func.apply(thisArg, [proxy, proxy, {}, true
, ...this.scopeKeys]); , ...this.scopeValues]);
this.map = map; this.map = map;
return d; return d;

View File

@ -5,6 +5,22 @@ export default class BindingList extends Array {
super(); super();
this.target = target; this.target = target;
this.scope = scope; this.scope = scope;
this.events = [];
}
destroy(){
for(var i = 0; i < this.length; i++)
this[i].unbind();
this.scope = {};
this.target = null;
for(var i = 0; i < this.events.length; i++)
this.target.removeEventListener(this.events[i].name, this.events[i].handle);
}
addEvent(name, handle)
{
this.target.addEventListener(name, handle);
this.events.push({name, handle})
} }
getArgumentsNames(){ getArgumentsNames(){

View File

@ -169,6 +169,25 @@ export class IUI {
// some element extended or overwritten the binding arguments // some element extended or overwritten the binding arguments
if (element.scope != null) if (element.scope != null)
IUI.extend(scope, element.scope, true); IUI.extend(scope, element.scope, true);
else if (element.hasAttribute(":scope"))
{
let script = element.getAttribute(":scope");
let code = `try {\r\n context.value = ${script}; \r\n}\r\n catch(ex) { context.error = ex; }`
let func = new Function("context", code);
let context = {};
func.call(element, context);
if (context.error != undefined)
console.log("Scope binding failed", context.error.name + ": " + context.error.message, this.script, this.target);
else if (context.value != undefined
&& context.value instanceof Object)
IUI.extend(scope, context.value, true);
}
let scopeArgs = Object.keys(scope);
let scopeValues = Object.values(scope);
bindings = new BindingList(element, scope); bindings = new BindingList(element, scope);
@ -182,10 +201,29 @@ export class IUI {
} }
else else
{ {
element.__i_bindings?.destroy();
// compile attributes // compile attributes
for (var i = 0; i < element.attributes.length; i++) { for (var i = 0; i < element.attributes.length; i++) {
// skip scope
if (element.attributes[i].name == ":scope")
continue;
if (element.attributes[i].name.startsWith("@")){
// make events
let code = element.attributes[i].value;
//let code = `try {\r\n context.value = ${script}; \r\n}\r\n catch(ex) { context.error = ex; }`
let func = new Function("event", ...scopeArgs, code);
let handler = (event) => {
func.call(element, event, ...scopeValues);
}
bindings.addEvent(element.attributes[i].name.substr(1), handler);
}
else
{
let b = Binding.create(element.attributes[i], let b = Binding.create(element.attributes[i],
bindings.scope); bindings.scope);
@ -199,6 +237,7 @@ export class IUI {
bindings.push(b); bindings.push(b);
} }
} }
}
// add reference // add reference
@ -218,8 +257,6 @@ export class IUI {
// } // }
} }
// get new refs (scope might been overwritten) // get new refs (scope might been overwritten)
//refs = scope?.refs; //refs = scope?.refs;
@ -230,6 +267,31 @@ export class IUI {
// @TODO: check if the IUI element handles the binding // @TODO: check if the IUI element handles the binding
IUI.bind(el, false, sourcePath, scope); IUI.bind(el, false, sourcePath, scope);
} }
else if (el instanceof HTMLScriptElement)
{
try
{
// this because HTML parser don't evaluate script tag
/// let func = new Function("//# sourceURL=iui://" + sourcePath + "-" + Math.round(Math.random() * 10000) + "\r\n return " + el.text.trim());
let func = new Function(...scopeArgs,
"//# sourceURL=iui://" + sourcePath + "-"
+ Math.round(Math.random() * 10000)
+ "\r\n" + el.text.trim());
let rt = func.apply(el.parentElement, scopeValues);
console.log("rt", rt);
if (typeof (rt) === "object") {
for (var k in rt)
el.parentElement[k] = rt[k];
}
}
catch (ex) {
console.log(ex);
}
}
else if (el instanceof HTMLElement) { else if (el instanceof HTMLElement) {
IUI.bind(el, false, sourcePath, scope); IUI.bind(el, false, sourcePath, scope);
} }
@ -238,19 +300,6 @@ export class IUI {
if (b != null) if (b != null)
bindings.push(b); bindings.push(b);
} }
else if (el instanceof HTMLScriptElement)
{
// this because HTML parser don't evaluate script tag
/// let func = new Function("//# sourceURL=iui://" + sourcePath + "-" + Math.round(Math.random() * 10000) + "\r\n return " + el.text.trim());
let func = new Function("//# sourceURL=iui://" + sourcePath + "-" + Math.round(Math.random() * 10000) + "\r\n" + el.text.trim());
let rt = func.call(el.parentElement);
if (typeof (rt) === "object") {
for (var k in rt)
el.parentElement[k] = rt[k];
}
}
} }
element.__i_bindings = bindings; element.__i_bindings = bindings;

View File

@ -172,6 +172,7 @@ export default class IUIElement extends HTMLElement {
_register(event) { _register(event) {
this._events.push(event); this._events.push(event);
/*
if (this.hasAttribute("@" + event)) { if (this.hasAttribute("@" + event)) {
let handler = this.getAttribute("@" + event); let handler = this.getAttribute("@" + event);
if (handler.match(/^[A-Za-z\$_]+(?:[\$_][A-Za-z0-9]+)*$/g) === null) { if (handler.match(/^[A-Za-z\$_]+(?:[\$_][A-Za-z0-9]+)*$/g) === null) {
@ -196,6 +197,7 @@ export default class IUIElement extends HTMLElement {
} }
} }
} }
*/
} }
off(event, func) { off(event, func) {

View File

@ -6,11 +6,12 @@ export default class RefsCollection
this._rootElement = rootElement; this._rootElement = rootElement;
} }
_build(element) { _build(element, append) {
if (element == undefined) if (element == undefined)
element = this._rootElement; element = this._rootElement;
if (!append)
for(var i in this) for(var i in this)
if (i != "_rootElement" && i != "_build") if (i != "_rootElement" && i != "_build")
delete this[i]; delete this[i];
@ -33,14 +34,13 @@ export default class RefsCollection
var firstRef = this[ref]; var firstRef = this[ref];
this[ref] =[firstRef, child]; this[ref] =[firstRef, child];
} }
} }
if (child.refs != undefined) if (child.refs != undefined)
// opt out if the element handles referencing // opt out if the element handles referencing
break; break;
else else
this._build(child); this._build(child, true);
} }
} }
} }

View File

@ -6,6 +6,9 @@ export default IUI.module(class CodePreview extends IUIElement {
constructor() { constructor() {
super(); super();
this.refs = new RefsCollection(this); this.refs = new RefsCollection(this);
this._code = this.innerHTML.trim();
this.textContent = '';
} }
async create() { async create() {
@ -13,8 +16,8 @@ export default IUI.module(class CodePreview extends IUIElement {
if (this.hasAttribute("debug")) if (this.hasAttribute("debug"))
debugger; debugger;
this._code = this.innerHTML.trim(); //this._code = this.innerHTML.trim();
this.textContent = ''; //this.textContent = '';
// create elements // create elements
this.bar = document.createElement("div"); this.bar = document.createElement("div");
@ -31,7 +34,7 @@ export default IUI.module(class CodePreview extends IUIElement {
let self = this; let self = this;
this.editor.addEventListener("input", function() { this.editor.addEventListener("input", function() {
self._code = self.editor.innerText.trim(); self._code = self.editor.textContent.trim();
self.updatePreview(); self.updatePreview();
}, false); }, false);
@ -59,12 +62,18 @@ export default IUI.module(class CodePreview extends IUIElement {
async updatePreview() { async updatePreview() {
if (this._updating) if (this._updating)
return; return;
this._updating = true; this._updating = true;
this.preview.innerHTML = this._code;
this.preview.innerHTML = this._code;
//this.editor.innerHTML = hljs.highlightAuto(this._code).value;
// this.editor.innerHTML = hljs.highlight(this._code, {language: 'html'}).value
// this.editor.innerHTML = hljs.highlightElement(this.editor, {language: 'html'}).value;
if (window.app?.loaded) if (window.app?.loaded)
{ {