mirror of
https://github.com/esiur/iui.git
synced 2025-06-27 17:33:12 +00:00
scope
This commit is contained in:
@ -1,9 +1,11 @@
|
||||
import IUIElement from "../Core/IUIElement.js";
|
||||
import { IUI } from "../Core/IUI.js";
|
||||
import RefsCollection from "./RefsCollection.js";
|
||||
|
||||
export default IUI.module(class App extends IUIElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.refs = new RefsCollection(this);
|
||||
}
|
||||
|
||||
create() {
|
||||
@ -11,8 +13,14 @@ export default IUI.module(class App extends IUIElement {
|
||||
window.app = this;
|
||||
}
|
||||
|
||||
|
||||
created() {
|
||||
IUI.bind(this, this, "/");
|
||||
|
||||
IUI.bind(this, this, "/", {app: this, refs: this.refs});
|
||||
|
||||
// update referencing
|
||||
this.refs._build();
|
||||
|
||||
//IUIElement._make_bindings(this);
|
||||
this.render();
|
||||
this._emit("load", { app: this });
|
||||
|
@ -20,7 +20,7 @@ export const AttributeBindingDestination = {
|
||||
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
|
||||
|
||||
export class Binding {
|
||||
static create(nodeOrAttributeOrIUIElement) {
|
||||
static create(nodeOrAttributeOrIUIElement, scope) {
|
||||
var code, isAsync, type, attrType, attrKey, func, script;
|
||||
|
||||
//if (nodeOrAttributeOrIUIElement.created)
|
||||
@ -100,11 +100,17 @@ export class Binding {
|
||||
|
||||
// test the function
|
||||
|
||||
let scopeKeys = Object.keys(scope);
|
||||
let scopeValues = Object.values(scope);
|
||||
|
||||
try {
|
||||
let args = ["data", "d", "context", "_test",
|
||||
...scopeKeys]
|
||||
|
||||
if (isAsync)
|
||||
func = new AsyncFunction("data", "d", "context", "_test", code);
|
||||
func = new AsyncFunction(...args, code);
|
||||
else
|
||||
func = new Function("data", "d", "context", "_test", code);
|
||||
func = new Function(...args, code);
|
||||
}
|
||||
catch (ex) {
|
||||
console.log("Test failed: " + ex, code);
|
||||
@ -113,7 +119,7 @@ export class Binding {
|
||||
|
||||
|
||||
let rt = new Binding();
|
||||
Object.assign(rt, { isAsync, type, attrType, attrKey, func, target: nodeOrAttributeOrIUIElement, checked: false, script });
|
||||
Object.assign(rt, { isAsync, type, attrType, attrKey, func, target: nodeOrAttributeOrIUIElement, checked: false, script, scopeKeys, scopeValues });
|
||||
return rt;
|
||||
}
|
||||
|
||||
@ -144,7 +150,9 @@ export class Binding {
|
||||
let proxy = new Proxy(map, detector);
|
||||
|
||||
try {
|
||||
let d = this.func.apply(thisArg, [proxy, proxy, {}, true]);
|
||||
let d = this.func.apply(thisArg, [proxy, proxy, {}, true
|
||||
, ...this.scopeKeys]);
|
||||
|
||||
this.map = map;
|
||||
return d;
|
||||
}
|
||||
@ -158,9 +166,9 @@ export class Binding {
|
||||
if (!this.checked)
|
||||
this._findMap(thisArg);
|
||||
|
||||
|
||||
let context = {};
|
||||
var rt = this.func.apply(thisArg, [data, data, context, false]);
|
||||
var rt = this.func.apply(thisArg, [data, data, context, false,
|
||||
...this.scopeValues]);
|
||||
|
||||
//console.log(rt);
|
||||
if (rt instanceof Promise)
|
||||
@ -231,9 +239,12 @@ export class Binding {
|
||||
|
||||
try {
|
||||
if (this.type === BindingType.IUIElement) {
|
||||
let d = this.func.apply(this.target, [data, data]);
|
||||
if (d instanceof Promise)
|
||||
d = await d;
|
||||
//let d = this.func.apply(this.target, [data, data]);
|
||||
//if (d instanceof Promise)
|
||||
// d = await d;
|
||||
|
||||
let d = await this._execute(this.target, data);
|
||||
|
||||
await this.target.setData(d);
|
||||
}
|
||||
else if (this.type === BindingType.TextNode) {
|
||||
@ -277,8 +288,10 @@ export class Binding {
|
||||
if (window?.app?.loaded)
|
||||
{
|
||||
await IUI.create(targetElement);
|
||||
IUI.bind(targetElement, true, "content", targetElement.__i_bindings?.scope);
|
||||
// update references
|
||||
targetElement.__i_bindings?.scope?.refs?._build();
|
||||
await IUI.created(targetElement);
|
||||
IUI.bind(targetElement, targetElement, "content");
|
||||
await IUI.render(targetElement, targetElement._data, true);
|
||||
}
|
||||
//await IUI.updateTree(targetElement);
|
||||
|
21
src/Core/BindingList.js
Normal file
21
src/Core/BindingList.js
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
export default class BindingList extends Array {
|
||||
|
||||
constructor(target, scope) {
|
||||
super();
|
||||
this.target = target;
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
getArgumentsNames(){
|
||||
if (this.scope == null)
|
||||
return [];
|
||||
|
||||
let rt;
|
||||
for (var i in this.scope.length)
|
||||
rt.push(i);
|
||||
return rt;
|
||||
}
|
||||
|
||||
|
||||
}
|
102
src/Core/IUI.js
102
src/Core/IUI.js
@ -1,6 +1,7 @@
|
||||
import IUIElement from "./IUIElement.js";
|
||||
import { Binding, BindingType } from "./Binding.js";
|
||||
//import Route from '../Router/Route.js';
|
||||
import BindingList from "./BindingList.js";
|
||||
|
||||
|
||||
export class IUI {
|
||||
@ -124,13 +125,13 @@ export class IUI {
|
||||
return objectClass;
|
||||
}
|
||||
|
||||
static extend(properties, defaults, force)
|
||||
static extend(properties, defaults, overwrite)
|
||||
{
|
||||
if (properties == null)
|
||||
properties = defaults;
|
||||
else
|
||||
for(var i in defaults)
|
||||
if (force)
|
||||
if (overwrite)
|
||||
properties[i] = defaults[i];
|
||||
else if (properties[i] === undefined)
|
||||
properties[i] = defaults[i];
|
||||
@ -138,38 +139,55 @@ export class IUI {
|
||||
}
|
||||
|
||||
|
||||
static bind(element, rootElement, sourcePath){
|
||||
static bind(element, skipAttributes, sourcePath, scope) {
|
||||
|
||||
// ::Attribute
|
||||
// : Field
|
||||
// : Field
|
||||
// async:: Async Attribute
|
||||
// async: Async Field
|
||||
// @ Event
|
||||
|
||||
// skip element ?
|
||||
if (element.hasAttribute("skip")
|
||||
|| element.hasAttribute("i-skip")
|
||||
|| element instanceof HTMLTemplateElement)
|
||||
return;
|
||||
|
||||
// tags to skip
|
||||
//if (element instanceof HTMLScriptElement )
|
||||
//return;
|
||||
|
||||
if (rootElement == null)
|
||||
rootElement = element;
|
||||
|
||||
let bindings;
|
||||
|
||||
if (element != rootElement)
|
||||
{
|
||||
element.view = rootElement.view;
|
||||
element.route = rootElement.route;
|
||||
|
||||
|
||||
if (scope == null)
|
||||
scope = {};
|
||||
|
||||
// get refs before they get overwritten
|
||||
//let refs = scope?.refs;
|
||||
|
||||
// some element extended or overwritten the binding arguments
|
||||
if (element.scope != null)
|
||||
IUI.extend(scope, element.scope, true);
|
||||
|
||||
bindings = new BindingList(element, scope);
|
||||
|
||||
if (skipAttributes)
|
||||
{
|
||||
// copy attributes bindings
|
||||
if (element.__i_bindings != null)
|
||||
for(var i = 0; i < element.__i_bindings.length; i++)
|
||||
if (element.__i_bindings[i].type != BindingType.TextNode)
|
||||
bindings.push(element.__i_bindings[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
bindings = [];
|
||||
// compile attributes
|
||||
for (var i = 0; i < element.attributes.length; i++) {
|
||||
|
||||
let b = Binding.create(element.attributes[i]);
|
||||
let b = Binding.create(element.attributes[i],
|
||||
bindings.scope);
|
||||
|
||||
if (b != null) {
|
||||
if (b.type == BindingType.HTMLElementDataAttribute
|
||||
@ -182,37 +200,50 @@ export class IUI {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// add reference
|
||||
if (element.hasAttribute("ref")) {
|
||||
rootElement.refs[el.getAttribute("ref")] = element;
|
||||
}
|
||||
// if (element.hasAttribute("ref")) {
|
||||
// let ref = element.getAttribute("ref");
|
||||
// if (refs[ref] == null)
|
||||
// refs[ref] = element;
|
||||
// else if (refs[ref] == element){
|
||||
// // do nothing
|
||||
// }
|
||||
// else if (refs[ref] instanceof Array){
|
||||
// refs[ref].push(element);
|
||||
// } else {
|
||||
// var firstRef = refs[ref];
|
||||
// refs[ref] =[firstRef, element];
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove previous text node bindings
|
||||
bindings = element.bindings == null ? [] : element.bindings.filter(x=> x.type != BindingType.TextNode);
|
||||
element.refs = {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
// get new refs (scope might been overwritten)
|
||||
//refs = scope?.refs;
|
||||
|
||||
// compile nodes
|
||||
for (var i = 0; i < element.childNodes.length; i++) {
|
||||
let el = element.childNodes[i];
|
||||
if (el instanceof IUIElement) {
|
||||
// @TODO: check if the IUI element handles the binding
|
||||
IUI.bind(el, rootElement, sourcePath);
|
||||
IUI.bind(el, false, sourcePath, scope);
|
||||
}
|
||||
else if (el instanceof HTMLElement) {
|
||||
IUI.bind(el, rootElement, sourcePath);
|
||||
IUI.bind(el, false, sourcePath, scope);
|
||||
}
|
||||
else if (el instanceof Text) {
|
||||
let b = Binding.create(el);
|
||||
let b = Binding.create(el, bindings.scope);
|
||||
if (b != null)
|
||||
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 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") {
|
||||
@ -222,24 +253,25 @@ export class IUI {
|
||||
}
|
||||
}
|
||||
|
||||
element.bindings = bindings;
|
||||
|
||||
element.__i_bindings = bindings;
|
||||
}
|
||||
|
||||
static async render(element, data, textNodesOnly = false) {
|
||||
|
||||
if (!element.bindings) {
|
||||
if (!element.__i_bindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
let bindings = element.__i_bindings;
|
||||
|
||||
if (textNodesOnly) {
|
||||
for (var i = 0; i < element.bindings.length; i++)
|
||||
if (element.bindings[i].type == BindingType.TextNode)
|
||||
await element.bindings[i].render(data);
|
||||
for (var i = 0; i < bindings.length; i++)
|
||||
if (bindings[i].type == BindingType.TextNode)
|
||||
await bindings[i].render(data);
|
||||
} else {
|
||||
// render attributes & text nodes
|
||||
for (var i = 0; i < element.bindings.length; i++)
|
||||
await element.bindings[i].render(data);
|
||||
for (var i = 0; i < bindings.length; i++)
|
||||
await bindings[i].render(data);
|
||||
}
|
||||
|
||||
// render children
|
||||
|
@ -41,7 +41,6 @@ export default class IUIElement extends HTMLElement {
|
||||
|
||||
async render() {
|
||||
await IUI.render(this, this._data);
|
||||
//await IUIElement._renderElement(this, this._data);
|
||||
}
|
||||
|
||||
_getParentData() {
|
||||
@ -57,22 +56,15 @@ export default class IUIElement extends HTMLElement {
|
||||
async setData(value) {
|
||||
this._data = value;
|
||||
this._emit("data", {data: value});
|
||||
await IUIElement._renderElement(this, value);
|
||||
await IUI.render(this, value);
|
||||
}
|
||||
|
||||
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
async revert(){
|
||||
//if (this.revertMap != null)
|
||||
//{
|
||||
//if (data == undefined)
|
||||
// await this.revertMap.render(this._getParentData());
|
||||
//else
|
||||
// await this.revertMap.render(data);
|
||||
// revert parents
|
||||
|
||||
let e = this;
|
||||
|
||||
do {
|
||||
@ -81,7 +73,6 @@ export default class IUIElement extends HTMLElement {
|
||||
if (e.revertMap != null)
|
||||
await e.revertMap.render(p?.data);
|
||||
} while (e = p);
|
||||
//}
|
||||
}
|
||||
|
||||
async update(data) {
|
||||
@ -101,51 +92,17 @@ export default class IUIElement extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
static async _renderElement(element, data) {
|
||||
if (!element.bindings) {
|
||||
return;
|
||||
}
|
||||
|
||||
// render attributes & text nodes
|
||||
for (var i = 0; i < element.bindings.length; i++)
|
||||
await element.bindings[i].render(data);
|
||||
|
||||
// render children
|
||||
for (var i = 0; i < element.children.length; i++) {
|
||||
let e = element.children[i];
|
||||
if (e instanceof IUIElement)
|
||||
// @TODO should check if the element depends on parent or not
|
||||
if (e.dataMap != null) {
|
||||
// if map function failed to call setData, we will render without it
|
||||
if (!(await e.dataMap.render(data)))
|
||||
await e.render();
|
||||
}
|
||||
else
|
||||
await e.setData(data);
|
||||
else {
|
||||
if (e.dataMap != null)
|
||||
await e.dataMap.render(data);
|
||||
else
|
||||
e.data = data;
|
||||
|
||||
//let data = e.mapData(data);
|
||||
await IUIElement._renderElement(e, e.data);
|
||||
}
|
||||
}
|
||||
// bindings arguments
|
||||
get scope(){
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// this meant to be inherited
|
||||
modified() {
|
||||
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
|
||||
if (this.hasAttribute("css-class"))
|
||||
{
|
||||
this.classList.add(this.getAttribute("css-class"));
|
||||
@ -182,8 +139,6 @@ export default class IUIElement extends HTMLElement {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
||||
console.log("Destroy", this);
|
||||
IUI.registry.splice(IUI.registry.indexOf(this), 1);
|
||||
if (this.parentNode)
|
||||
this.parentNode.removeChild(this);
|
||||
@ -191,67 +146,6 @@ export default class IUIElement extends HTMLElement {
|
||||
|
||||
|
||||
|
||||
static _make_bindings(element, isRoot = false) {
|
||||
|
||||
// ::Attribute
|
||||
// : Field
|
||||
// async:: Async Attribute
|
||||
// async: Async Field
|
||||
// @ Event
|
||||
|
||||
|
||||
// skip element ?
|
||||
if (element.hasAttribute("skip"))
|
||||
return;
|
||||
|
||||
// tags to skip
|
||||
if (element instanceof HTMLScriptElement
|
||||
|| element instanceof HTMLTemplateElement)
|
||||
return;
|
||||
|
||||
let bindings = [];
|
||||
|
||||
|
||||
if (!isRoot)
|
||||
{
|
||||
// compile attributes
|
||||
for (var i = 0; i < element.attributes.length; i++) {
|
||||
|
||||
let b = Binding.create(element.attributes[i]);
|
||||
|
||||
if (b != null) {
|
||||
if (b.type == BindingType.HTMLElementDataAttribute
|
||||
|| b.type == BindingType.IUIElementDataAttribute)
|
||||
element.dataMap = b;
|
||||
else if (b.type == BindingType.RevertAttribute)
|
||||
element.revertMap = b;
|
||||
else
|
||||
bindings.push(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compile nodes
|
||||
for (var i = 0; i < element.childNodes.length; i++) {
|
||||
let e = element.childNodes[i];
|
||||
if (e instanceof IUIElement) {
|
||||
// @TODO: check if the IUI element handles the binding
|
||||
IUIElement._make_bindings(e);
|
||||
}
|
||||
else if (e instanceof HTMLElement) {
|
||||
IUIElement._make_bindings(e);
|
||||
}
|
||||
else if (e instanceof Text) {
|
||||
let b = Binding.create(e);
|
||||
if (b != null)
|
||||
bindings.push(b);
|
||||
}
|
||||
}
|
||||
|
||||
element.bindings = bindings;
|
||||
}
|
||||
|
||||
|
||||
_emit(event, values) {
|
||||
//var args = Array.prototype.slice.call(arguments, 1);
|
||||
var e = new CustomEvent(event, values);
|
||||
@ -304,13 +198,13 @@ export default class IUIElement extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
off(event, fn) {
|
||||
this.removeEventListener(event, fn);
|
||||
off(event, func) {
|
||||
this.removeEventListener(event, func);
|
||||
return this;
|
||||
}
|
||||
|
||||
on(event, fn) {
|
||||
this.addEventListener(event, fn, false);
|
||||
on(event, func) {
|
||||
this.addEventListener(event, func, false);
|
||||
return this;
|
||||
}
|
||||
}
|
46
src/Core/RefsCollection.js
Normal file
46
src/Core/RefsCollection.js
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
export default class RefsCollection
|
||||
{
|
||||
|
||||
constructor(rootElement){
|
||||
this._rootElement = rootElement;
|
||||
}
|
||||
|
||||
_build(element) {
|
||||
|
||||
if (element == undefined)
|
||||
element = this._rootElement;
|
||||
|
||||
for(var i in this)
|
||||
if (i != "_rootElement" && i != "_build")
|
||||
delete this[i];
|
||||
|
||||
for(var i = 0; i < element.children.length; i++)
|
||||
{
|
||||
let child = element.children[i];
|
||||
|
||||
if (child.hasAttribute("ref"))
|
||||
{
|
||||
let ref = child.getAttribute("ref");
|
||||
if (this[ref] == null)
|
||||
this[ref] = child;
|
||||
else if (this[ref] == child){
|
||||
// do nothing
|
||||
}
|
||||
else if (this[ref] instanceof Array){
|
||||
this[ref].push(child);
|
||||
} else {
|
||||
var firstRef = this[ref];
|
||||
this[ref] =[firstRef, child];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (child.refs != undefined)
|
||||
// opt out if the element handles referencing
|
||||
break;
|
||||
else
|
||||
this._build(child);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user