UNPKG

web-atoms-core

Version:
431 lines (354 loc) • 14.5 kB
import { AjaxOptions } from "../services/http/AjaxOptions"; import { AtomUI, ChildEnumerator } from "../web/core/AtomUI"; import { AtomBinder } from "./AtomBinder"; import { IAtomElement, IDisposable, INameValuePairs, INativeComponent, IUIAtomControl } from "./types"; import XNode from "./XNode"; export abstract class BaseElementBridge<T extends IAtomElement> { public controlFactory: any; public createBusyIndicator: () => IDisposable; public ajax: ( url: string, options: AjaxOptions, success: (r) => void, failed: (r) => void, progress: (p) => void) => void; public setTemplate: (element: any, name: string, templateFactory: () => any) => void; public setImport: (element: any, name: string, templateFactory: () => any) => void; public reset: () => void; public abstract create( type: string | ((n: any, ... nodes: XNode[]) => XNode) | (new (... a: any[]) => any), node?: any, app?: any): T; public abstract attachControl(element: T, control: IUIAtomControl): void; public abstract addEventHandler( element: T, name: string, handler: EventListenerOrEventListenerObject, capture?: boolean): IDisposable; public abstract atomParent(element: T, climbUp?: boolean): IUIAtomControl; public abstract elementParent(element: T): T; public abstract templateParent(element: T): IUIAtomControl; public abstract visitDescendents(element: T, action: (e: T, ac: IUIAtomControl) => boolean): void; public abstract dispose(element: T): void; public abstract appendChild(parent: T, child: T): void; public abstract getValue(element: HTMLElement, name: string): any; public abstract setValue(element: T, name: string, value: any): void; public abstract watchProperty(element: T, name: string, events: string[], f: (v: any) => void): IDisposable; public abstract loadContent(element: T, text: string): void; public abstract findChild(element: T, name: string): T; public abstract close(element: T, success: () => void, error: (e) => void): void; public refreshInherited(target: { element: T }, name: string, fieldName?: string): void { AtomBinder.refreshValue(target, name); if (!fieldName) { fieldName = "m" + name[0].toUpperCase() + name.substr(1); } this.visitDescendents(target.element, (e, ac) => { if (ac) { if (ac[fieldName] === undefined) { this.refreshInherited(ac as any, name, fieldName); } return false; } return true; }); } public createNode( target: any, node: XNode, // tslint:disable-next-line: ban-types binder: Function, // tslint:disable-next-line: ban-types xNodeClass: Function, // tslint:disable-next-line: ban-types creator: Function): any { throw new Error("Method not implemented."); } } export class AtomElementBridge extends BaseElementBridge<HTMLElement> { public addEventHandler( element: HTMLElement, name: string, handler: EventListenerOrEventListenerObject, capture?: boolean): IDisposable { element.addEventListener(name, handler, capture); return { dispose: () => { element.removeEventListener(name, handler, capture); } }; } public atomParent(element: HTMLElement, climbUp: boolean = true): IUIAtomControl { const eAny: INameValuePairs = element as INameValuePairs; if (eAny.atomControl) { return eAny.atomControl; } if (!climbUp) { return null; } if (!element.parentNode) { return null; } return this.atomParent(this.elementParent(element)); } public elementParent(element: HTMLElement): HTMLElement { const eAny = element as any; const lp = eAny._logicalParent; if (lp) { return lp; } return element.parentElement; } public templateParent(element: HTMLElement): IUIAtomControl { if (!element) { return null; } const eAny = element as any; if (eAny._templateParent) { return this.atomParent(element); } const parent = this.elementParent(element); if (!parent) { return null; } return this.templateParent(parent); } public visitDescendents(element: HTMLElement, action: (e: HTMLElement, ac: IUIAtomControl) => boolean): void { const en = new ChildEnumerator(element); while (en.next()) { const iterator = en.current; const eAny = iterator as any; const ac = eAny ? eAny.atomControl : undefined; if (!action(iterator, ac)) { continue; } this.visitDescendents(iterator, action); } } public dispose(element: HTMLElement): void { const eAny = element as any; eAny.atomControl = undefined; eAny.innerHTML = ""; delete eAny.atomControl; } public appendChild(parent: HTMLElement, child: HTMLElement): void { parent.appendChild(child); } public setValue(element: HTMLElement, name: string, value: any): void { element[name] = value; } public getValue(element: HTMLElement, name: string): any { return element[name]; } public watchProperty(element: HTMLElement, name: string, events: string[], f: (v: any) => void): IDisposable { if (events.indexOf("change") === -1) { events.push("change"); } const l = (e) => { const e1 = element as HTMLInputElement; const v = e1.type === "checkbox" ? e1.checked : e1.value; f(v); }; for (const iterator of events) { element.addEventListener(iterator, l , false); } return { dispose: () => { for (const iterator of events) { element.removeEventListener(iterator, l, false); } } }; } public attachControl(element: HTMLElement, control: IUIAtomControl): void { (element as any).atomControl = control; } public create(type: string): HTMLElement { return document.createElement(type); } public loadContent(element: HTMLElement, text: string): void { throw new Error("Not supported"); } public findChild(element: HTMLElement, name: string): HTMLElement { throw new Error("Not supported"); } public close(element: HTMLElement, success: () => void, error: (e) => void): void { throw new Error("Not supported"); } public toTemplate(element, creator) { const templateNode = element as any; const name = templateNode.name; if (typeof name === "string") { element = ((bx, n) => class extends bx { public create(): void { this.render(n); } })(creator as any, templateNode.children[0]); } else { element = ((base, n) => class extends base { public create(): void { this.render(n); } })(name, templateNode.children[0]); } return element; } public createNode( target: any, node: XNode, // tslint:disable-next-line: ban-types binder: Function, // tslint:disable-next-line: ban-types xNodeClass: Function, // tslint:disable-next-line: ban-types creator: Function): any { let parent = null; const app = target.app; let e: HTMLElement = null; const nn = node.attributes ? node.attributes.for : undefined; if (typeof node.name === "string") { // it is simple node.. e = document.createElement(node.name); parent = e; if (nn) { delete node.attributes.for; } } else { if (nn) { target = new (node.name as any)(app, document.createElement(nn)); delete node.attributes.for; } target = new (node.name as any)(app); e = target.element; parent = target; // target.append(child); // const firstChild = node.children ? node.children[0] : null; // if (firstChild) { // const n = this.createNode(child, firstChild, binder, xNodeClass, creator); // child.append(n.atomControl || n); // } // return child.element; } const a = node.attributes; if (a) { for (const key in a) { if (a.hasOwnProperty(key)) { let element = a[key] as any; if (element instanceof binder) { if (/^event/.test(key)) { let ev = key.substr(5); if (ev.startsWith("-")) { ev = ev.split("-").map((s) => s[0].toLowerCase() + s.substr(1)).join(""); } else { ev = ev[0].toLowerCase() + ev.substr(1); } (element as any).setupFunction(ev, element, target, e); } else { (element as any).setupFunction(key, element, target, e); } } else { // this is template... if (element instanceof xNodeClass) { element = this.toTemplate(element, creator); } target.setLocalValue(e, key, element); } } } } const children = node.children; if (children) { for (const iterator of children) { if (typeof iterator === "string") { e.appendChild(document.createTextNode(iterator)); continue; } const t = iterator.attributes ? iterator.attributes.template : null; if (t) { const tx = this.toTemplate(iterator, creator); target[t] = tx; continue; } if (typeof iterator.name === "string") { e.appendChild(this.createNode(target, iterator, binder, xNodeClass, creator)); continue; } const child = this.createNode(target, iterator, binder, xNodeClass, creator); if (parent.element && parent.element.atomControl === parent) { parent.append(child.atomControl || child); } else { parent.appendChild(child); } } } return e; } } export class AtomBridge { public static platform: "web" | "xf"; public static instance: BaseElementBridge<IAtomElement>; public static createNode(iterator: XNode, app: any): { element?: any, control?: any } { if (typeof iterator.name !== "function") { return { element: AtomBridge.instance.create(iterator.name.toString(), iterator, app) }; } const fx = iterator.attributes ? iterator.attributes.for : undefined; const c = new (iterator.name as any)(app, fx ? AtomBridge.instance.create(fx, iterator, app) : undefined) as any; return { element: c.element, control: c }; } public static toTemplate(app: any, n: XNode, creator: any) { if (n.isTemplate) { const t = AtomBridge.toTemplate(app, n.children[0], creator); return AtomBridge.instance.create(n.name.toString(), t, app); } const bridge = AtomBridge.instance; let fx; let en; if (typeof n.name === "function") { fx = n.name; en = (n.attributes && n.attributes.for) ? n.attributes.for : undefined; } else { fx = bridge.controlFactory; en = n.name; } return class Template extends (fx as any) { // tslint:disable-next-line: variable-name public _creator = fx; constructor(a, e1) { super(a || app, e1 || (en ? bridge.create(en, null, app) : undefined)); } public create() { super.create(); this.render(n, null, creator); } }; } public static refreshInherited(target: { element: any }, name: string, fieldName?: string): void { if (AtomBridge.instance.refreshInherited) { AtomBridge.instance.refreshInherited(target, name, fieldName); return; } AtomBinder.refreshValue(target, name); if (!fieldName) { fieldName = "m" + name[0].toUpperCase() + name.substr(1); } AtomBridge.instance.visitDescendents(target.element, (e, ac) => { if (ac) { if (ac[fieldName] === undefined) { AtomBridge.refreshInherited(ac as any, name, fieldName); } return false; } return true; }); } } // tslint:disable-next-line: one-variable-per-declaration declare var window, global; const globalNS = (typeof window !== "undefined" ? window : (global as any)) as any; globalNS.AtomBridge = AtomBridge; if (typeof window !== "undefined") { AtomBridge.instance = new AtomElementBridge(); AtomBridge.platform = "web"; }