import { isNull, mixin } from "@implab/core-amd/safe"; import { isPlainObject, DojoNodePosition, Rendition, isDocumentFragmentNode, placeAt, collectNodes, autostartWidgets } from "./traits"; import { IScope } from "./Scope"; import { getScope } from "./render"; export abstract class RenditionBase implements Rendition { private _attrs = {}; private _children = new Array(); private _created: boolean = false; visitNext(v: any) { if (this._created) throw new Error("The Element is already created"); if (isNull(v) || typeof v === "boolean") // skip null, undefined, booleans ( this will work: {value && {value}} ) return; if (isPlainObject(v)) { mixin(this._attrs, v); } else if (v instanceof Array) { v.forEach(x => this.visitNext(x)); } else { this._children.push(v); } } ensureCreated(scope: IScope) { if (!this._created) { this._create(this._attrs, this._children, scope); this._children = []; this._attrs = {}; this._created = true; } } /** Is rendition was instantiated to the DOM node */ isCreated() { return this._created; } /** Creates DOM node if not created. No additional actions are taken. */ getDomNode(scope?: IScope) { this.ensureCreated(scope || getScope()); return this._getDomNode(); } /** Creates DOM node if not created, places it to the specified position * and calls startup() method for all widgets contained by this node. * * @param {string | Node} refNode The reference node where the created * DOM should be placed. * @param {DojoNodePosition} position Optional parameter, specifies the * position relative to refNode. Default is "last" (i.e. last child). */ placeAt(refNode: string | Node, position: DojoNodePosition = "last") { const domNode = this.getDomNode(); const startupPending = isDocumentFragmentNode(domNode) ? collectNodes(domNode.children) : [domNode]; placeAt(domNode, refNode, position); startupPending.forEach(autostartWidgets); } protected abstract _create(attrs: object, children: unknown[], scope: IScope): void; protected abstract _getDomNode(): TNode; }