|
|
import { isNull, mixin } from "@implab/core-amd/safe";
|
|
|
import { isPlainObject, isNode, isRendition, DojoNodePosition, Rendition, isInPage, isWidget, isDocumentFragmentNode, startupWidgets } from "./traits";
|
|
|
|
|
|
import dom = require("dojo/dom-construct");
|
|
|
import registry = require("dijit/registry");
|
|
|
|
|
|
|
|
|
export abstract class RenditionBase<TNode extends Node> implements Rendition<TNode> {
|
|
|
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 && <span>{value}</span>} )
|
|
|
return;
|
|
|
|
|
|
if (isPlainObject(v)) {
|
|
|
mixin(this._attrs, v);
|
|
|
} else if (v instanceof Array) {
|
|
|
v.forEach(x => this.visitNext(x));
|
|
|
} else {
|
|
|
this._children.push(v);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
protected getItemDom(v: any) {
|
|
|
const tv = typeof v;
|
|
|
|
|
|
if (tv === "string" || tv === "number" || v instanceof RegExp || v instanceof Date) {
|
|
|
// primitive types converted to the text nodes
|
|
|
return document.createTextNode(v.toString());
|
|
|
} else if (isNode(v)) {
|
|
|
// nodes are kept as is
|
|
|
return v;
|
|
|
} else if (isRendition(v)) {
|
|
|
// renditions as instantinated
|
|
|
return v.getDomNode();
|
|
|
} else if (isWidget(v)) {
|
|
|
// widgets are converted to it's markup
|
|
|
return v.domNode;
|
|
|
} else if (tv === "boolean" || v === null || v === undefined) {
|
|
|
// null | undefined | boolean are removed, converted to comments
|
|
|
return document.createComment(`[${tv} ${String(v)}]`);
|
|
|
} else {
|
|
|
// bug: explicit error otherwise
|
|
|
throw new Error("Invalid parameter: " + v);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
ensureCreated() {
|
|
|
if (!this._created) {
|
|
|
this._create(this._attrs, this._children);
|
|
|
this._children = [];
|
|
|
this._attrs = {};
|
|
|
this._created = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/** @deprecated will be removed in 1.0.0, use getDomNode() */
|
|
|
getDomElement() {
|
|
|
return this.getDomNode();
|
|
|
}
|
|
|
|
|
|
/** Creates DOM node if not created. No additional actions are taken. */
|
|
|
getDomNode() {
|
|
|
this.ensureCreated();
|
|
|
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) {
|
|
|
const domNode = this.getDomNode();
|
|
|
|
|
|
const collect = (collection: HTMLCollection) => {
|
|
|
const items = [];
|
|
|
for (let i = 0, n = collection.length; i < n; i++) {
|
|
|
items.push(collection[i]);
|
|
|
}
|
|
|
return items;
|
|
|
};
|
|
|
|
|
|
const startup = (node: Node) => {
|
|
|
if (node.parentNode) {
|
|
|
const parentWidget = registry.getEnclosingWidget(node.parentNode);
|
|
|
if (parentWidget && parentWidget._started)
|
|
|
return startupWidgets(node);
|
|
|
}
|
|
|
if (isInPage(node))
|
|
|
startupWidgets(node);
|
|
|
};
|
|
|
|
|
|
const startupPending = isDocumentFragmentNode(domNode) ? collect(domNode.children) : [domNode];
|
|
|
|
|
|
dom.place(domNode, refNode, position);
|
|
|
|
|
|
startupPending.forEach(startup);
|
|
|
|
|
|
}
|
|
|
|
|
|
protected abstract _create(attrs: object, children: any[]): void;
|
|
|
|
|
|
protected abstract _getDomNode(): TNode;
|
|
|
}
|
|
|
|