|
|
|
@ -13,58 +13,20 @@ import {DefaultRenderView, DefaultRenderFragmentRef} from './view';
|
|
|
|
|
|
|
|
|
|
export function createRenderView(fragmentCmds: RenderTemplateCmd[], inplaceElement: any,
|
|
|
|
|
nodeFactory: NodeFactory<any>): DefaultRenderView<any> {
|
|
|
|
|
var builders: RenderViewBuilder<any>[] = [];
|
|
|
|
|
visitAll(new RenderViewBuilder<any>(null, null, inplaceElement, builders, nodeFactory),
|
|
|
|
|
fragmentCmds);
|
|
|
|
|
var boundElements: any[] = [];
|
|
|
|
|
var boundTextNodes: any[] = [];
|
|
|
|
|
var nativeShadowRoots: any[] = [];
|
|
|
|
|
var fragments: DefaultRenderFragmentRef<any>[] = [];
|
|
|
|
|
var viewElementOffset = 0;
|
|
|
|
|
var view: DefaultRenderView<any>;
|
|
|
|
|
var eventDispatcher = (boundElementIndex: number, eventName: string, event: any) =>
|
|
|
|
|
view.dispatchRenderEvent(boundElementIndex, eventName, event);
|
|
|
|
|
var globalEventAdders: Function[] = [];
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < builders.length; i++) {
|
|
|
|
|
var builder = builders[i];
|
|
|
|
|
addAll(builder.boundElements, boundElements);
|
|
|
|
|
addAll(builder.boundTextNodes, boundTextNodes);
|
|
|
|
|
addAll(builder.nativeShadowRoots, nativeShadowRoots);
|
|
|
|
|
if (isBlank(builder.rootNodesParent)) {
|
|
|
|
|
fragments.push(new DefaultRenderFragmentRef<any>(builder.fragmentRootNodes));
|
|
|
|
|
}
|
|
|
|
|
for (var j = 0; j < builder.eventData.length; j++) {
|
|
|
|
|
var eventData = builder.eventData[j];
|
|
|
|
|
var boundElementIndex = eventData[0] + viewElementOffset;
|
|
|
|
|
var target = eventData[1];
|
|
|
|
|
var eventName = eventData[2];
|
|
|
|
|
if (isPresent(target)) {
|
|
|
|
|
var handler =
|
|
|
|
|
createEventHandler(boundElementIndex, `${target}:${eventName}`, eventDispatcher);
|
|
|
|
|
globalEventAdders.push(createGlobalEventAdder(target, eventName, handler, nodeFactory));
|
|
|
|
|
} else {
|
|
|
|
|
var handler = createEventHandler(boundElementIndex, eventName, eventDispatcher);
|
|
|
|
|
nodeFactory.on(boundElements[boundElementIndex], eventName, handler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
viewElementOffset += builder.boundElements.length;
|
|
|
|
|
var context = new BuildContext(eventDispatcher, nodeFactory, inplaceElement);
|
|
|
|
|
context.build(fragmentCmds);
|
|
|
|
|
var fragments: DefaultRenderFragmentRef<any>[] = [];
|
|
|
|
|
for (var i = 0; i < context.fragments.length; i++) {
|
|
|
|
|
fragments.push(new DefaultRenderFragmentRef(context.fragments[i]));
|
|
|
|
|
}
|
|
|
|
|
view = new DefaultRenderView<any>(fragments, boundTextNodes, boundElements, nativeShadowRoots,
|
|
|
|
|
globalEventAdders);
|
|
|
|
|
view = new DefaultRenderView<any>(fragments, context.boundTextNodes, context.boundElements,
|
|
|
|
|
context.nativeShadowRoots, context.globalEventAdders);
|
|
|
|
|
return view;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createEventHandler(boundElementIndex: number, eventName: string,
|
|
|
|
|
eventDispatcher: Function): Function {
|
|
|
|
|
return ($event) => eventDispatcher(boundElementIndex, eventName, $event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createGlobalEventAdder(target: string, eventName: string, eventHandler: Function,
|
|
|
|
|
nodeFactory: NodeFactory<any>): Function {
|
|
|
|
|
return () => nodeFactory.globalOn(target, eventName, eventHandler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface NodeFactory<N> {
|
|
|
|
|
resolveComponentTemplate(templateId: number): RenderTemplateCmd[];
|
|
|
|
|
createTemplateAnchor(attrNameAndValues: string[]): N;
|
|
|
|
@ -77,96 +39,156 @@ export interface NodeFactory<N> {
|
|
|
|
|
globalOn(target: string, eventName: string, callback: Function): Function;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class BuildContext<N> {
|
|
|
|
|
constructor(private _eventDispatcher: Function, public factory: NodeFactory<N>,
|
|
|
|
|
private _inplaceElement: N) {}
|
|
|
|
|
private _builders: RenderViewBuilder<N>[] = [];
|
|
|
|
|
|
|
|
|
|
globalEventAdders: Function[] = [];
|
|
|
|
|
boundElements: N[] = [];
|
|
|
|
|
boundTextNodes: N[] = [];
|
|
|
|
|
nativeShadowRoots: N[] = [];
|
|
|
|
|
fragments: N[][] = [];
|
|
|
|
|
|
|
|
|
|
build(fragmentCmds: RenderTemplateCmd[]) {
|
|
|
|
|
this.enqueueFragmentBuilder(null, fragmentCmds);
|
|
|
|
|
this._build(this._builders[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _build(builder: RenderViewBuilder<N>) {
|
|
|
|
|
this._builders = [];
|
|
|
|
|
builder.build(this);
|
|
|
|
|
var enqueuedBuilders = this._builders;
|
|
|
|
|
for (var i = 0; i < enqueuedBuilders.length; i++) {
|
|
|
|
|
this._build(enqueuedBuilders[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enqueueComponentBuilder(component: Component<N>) {
|
|
|
|
|
this._builders.push(new RenderViewBuilder<N>(
|
|
|
|
|
component, null, this.factory.resolveComponentTemplate(component.cmd.templateId)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enqueueFragmentBuilder(parentComponent: Component<N>, commands: RenderTemplateCmd[]) {
|
|
|
|
|
var rootNodes = [];
|
|
|
|
|
this.fragments.push(rootNodes);
|
|
|
|
|
this._builders.push(new RenderViewBuilder<N>(parentComponent, rootNodes, commands));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consumeInplaceElement(): N {
|
|
|
|
|
var result = this._inplaceElement;
|
|
|
|
|
this._inplaceElement = null;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addEventListener(boundElementIndex: number, target: string, eventName: string) {
|
|
|
|
|
if (isPresent(target)) {
|
|
|
|
|
var handler =
|
|
|
|
|
createEventHandler(boundElementIndex, `${target}:${eventName}`, this._eventDispatcher);
|
|
|
|
|
this.globalEventAdders.push(createGlobalEventAdder(target, eventName, handler, this.factory));
|
|
|
|
|
} else {
|
|
|
|
|
var handler = createEventHandler(boundElementIndex, eventName, this._eventDispatcher);
|
|
|
|
|
this.factory.on(this.boundElements[boundElementIndex], eventName, handler);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function createEventHandler(boundElementIndex: number, eventName: string,
|
|
|
|
|
eventDispatcher: Function): Function {
|
|
|
|
|
return ($event) => eventDispatcher(boundElementIndex, eventName, $event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createGlobalEventAdder(target: string, eventName: string, eventHandler: Function,
|
|
|
|
|
nodeFactory: NodeFactory<any>): Function {
|
|
|
|
|
return () => nodeFactory.globalOn(target, eventName, eventHandler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class RenderViewBuilder<N> implements RenderCommandVisitor {
|
|
|
|
|
parentStack: Array<N | Component<N>>;
|
|
|
|
|
boundTextNodes: N[] = [];
|
|
|
|
|
boundElements: N[] = [];
|
|
|
|
|
eventData: any[][] = [];
|
|
|
|
|
|
|
|
|
|
fragmentRootNodes: N[] = [];
|
|
|
|
|
nativeShadowRoots: N[] = [];
|
|
|
|
|
|
|
|
|
|
constructor(public parentComponent: Component<N>, public rootNodesParent: N,
|
|
|
|
|
public inplaceElement: N, public allBuilders: RenderViewBuilder<N>[],
|
|
|
|
|
public factory: NodeFactory<N>) {
|
|
|
|
|
constructor(public parentComponent: Component<N>, public fragmentRootNodes: N[],
|
|
|
|
|
public commands: RenderTemplateCmd[]) {
|
|
|
|
|
var rootNodesParent = isPresent(fragmentRootNodes) ? null : parentComponent.shadowRoot;
|
|
|
|
|
this.parentStack = [rootNodesParent];
|
|
|
|
|
allBuilders.push(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
build(context: BuildContext<N>) {
|
|
|
|
|
for (var i = 0; i < this.commands.length; i++) {
|
|
|
|
|
this.commands[i].visit(this, context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get parent(): N | Component<N> { return this.parentStack[this.parentStack.length - 1]; }
|
|
|
|
|
|
|
|
|
|
visitText(cmd: RenderTextCmd, context: any): any {
|
|
|
|
|
var text = this.factory.createText(cmd.value);
|
|
|
|
|
this._addChild(text, cmd.ngContentIndex);
|
|
|
|
|
visitText(cmd: RenderTextCmd, context: BuildContext<N>): any {
|
|
|
|
|
var text = context.factory.createText(cmd.value);
|
|
|
|
|
this._addChild(text, cmd.ngContentIndex, context);
|
|
|
|
|
if (cmd.isBound) {
|
|
|
|
|
this.boundTextNodes.push(text);
|
|
|
|
|
context.boundTextNodes.push(text);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
visitNgContent(cmd: RenderNgContentCmd, context: any): any {
|
|
|
|
|
visitNgContent(cmd: RenderNgContentCmd, context: BuildContext<N>): any {
|
|
|
|
|
if (isPresent(this.parentComponent)) {
|
|
|
|
|
var projectedNodes = this.parentComponent.project();
|
|
|
|
|
var projectedNodes = this.parentComponent.project(cmd.index);
|
|
|
|
|
for (var i = 0; i < projectedNodes.length; i++) {
|
|
|
|
|
var node = projectedNodes[i];
|
|
|
|
|
this._addChild(node, cmd.ngContentIndex);
|
|
|
|
|
this._addChild(node, cmd.ngContentIndex, context);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
visitBeginElement(cmd: RenderBeginElementCmd, context: any): any {
|
|
|
|
|
this.parentStack.push(this._beginElement(cmd));
|
|
|
|
|
visitBeginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): any {
|
|
|
|
|
this.parentStack.push(this._beginElement(cmd, context));
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
visitEndElement(context: any): any {
|
|
|
|
|
visitEndElement(context: BuildContext<N>): any {
|
|
|
|
|
this._endElement();
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
visitBeginComponent(cmd: RenderBeginComponentCmd, context: any): any {
|
|
|
|
|
var el = this._beginElement(cmd);
|
|
|
|
|
visitBeginComponent(cmd: RenderBeginComponentCmd, context: BuildContext<N>): any {
|
|
|
|
|
var el = this._beginElement(cmd, context);
|
|
|
|
|
var root = el;
|
|
|
|
|
if (cmd.nativeShadow) {
|
|
|
|
|
root = this.factory.createShadowRoot(el, cmd.templateId);
|
|
|
|
|
this.nativeShadowRoots.push(root);
|
|
|
|
|
root = context.factory.createShadowRoot(el, cmd.templateId);
|
|
|
|
|
context.nativeShadowRoots.push(root);
|
|
|
|
|
}
|
|
|
|
|
var component = new Component(el, root, cmd, this.factory, this.allBuilders);
|
|
|
|
|
var component = new Component(el, root, cmd);
|
|
|
|
|
context.enqueueComponentBuilder(component);
|
|
|
|
|
this.parentStack.push(component);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
visitEndComponent(context: any): any {
|
|
|
|
|
var c = <Component<N>>this.parent;
|
|
|
|
|
c.build();
|
|
|
|
|
visitEndComponent(context: BuildContext<N>): any {
|
|
|
|
|
this._endElement();
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: any): any {
|
|
|
|
|
var el = this.factory.createTemplateAnchor(cmd.attrNameAndValues);
|
|
|
|
|
this._addChild(el, cmd.ngContentIndex);
|
|
|
|
|
this.boundElements.push(el);
|
|
|
|
|
visitEmbeddedTemplate(cmd: RenderEmbeddedTemplateCmd, context: BuildContext<N>): any {
|
|
|
|
|
var el = context.factory.createTemplateAnchor(cmd.attrNameAndValues);
|
|
|
|
|
this._addChild(el, cmd.ngContentIndex, context);
|
|
|
|
|
context.boundElements.push(el);
|
|
|
|
|
if (cmd.isMerged) {
|
|
|
|
|
visitAll(
|
|
|
|
|
new RenderViewBuilder(this.parentComponent, null, null, this.allBuilders, this.factory),
|
|
|
|
|
cmd.children);
|
|
|
|
|
context.enqueueFragmentBuilder(this.parentComponent, cmd.children);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private _beginElement(cmd: RenderBeginElementCmd): N {
|
|
|
|
|
var el: N;
|
|
|
|
|
if (isPresent(this.inplaceElement)) {
|
|
|
|
|
el = this.inplaceElement;
|
|
|
|
|
this.inplaceElement = null;
|
|
|
|
|
this.factory.mergeElement(el, cmd.attrNameAndValues);
|
|
|
|
|
private _beginElement(cmd: RenderBeginElementCmd, context: BuildContext<N>): N {
|
|
|
|
|
var el: N = context.consumeInplaceElement();
|
|
|
|
|
if (isPresent(el)) {
|
|
|
|
|
context.factory.mergeElement(el, cmd.attrNameAndValues);
|
|
|
|
|
this.fragmentRootNodes.push(el);
|
|
|
|
|
} else {
|
|
|
|
|
el = this.factory.createElement(cmd.name, cmd.attrNameAndValues);
|
|
|
|
|
this._addChild(el, cmd.ngContentIndex);
|
|
|
|
|
el = context.factory.createElement(cmd.name, cmd.attrNameAndValues);
|
|
|
|
|
this._addChild(el, cmd.ngContentIndex, context);
|
|
|
|
|
}
|
|
|
|
|
if (cmd.isBound) {
|
|
|
|
|
this.boundElements.push(el);
|
|
|
|
|
var boundElementIndex = context.boundElements.length;
|
|
|
|
|
context.boundElements.push(el);
|
|
|
|
|
for (var i = 0; i < cmd.eventTargetAndNames.length; i += 2) {
|
|
|
|
|
var target = cmd.eventTargetAndNames[i];
|
|
|
|
|
var eventName = cmd.eventTargetAndNames[i + 1];
|
|
|
|
|
this.eventData.push([this.boundElements.length - 1, target, eventName]);
|
|
|
|
|
context.addEventListener(boundElementIndex, target, eventName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return el;
|
|
|
|
@ -174,13 +196,13 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
|
|
|
|
|
|
|
|
|
private _endElement() { this.parentStack.pop(); }
|
|
|
|
|
|
|
|
|
|
private _addChild(node: N, ngContentIndex: number) {
|
|
|
|
|
private _addChild(node: N, ngContentIndex: number, context: BuildContext<N>) {
|
|
|
|
|
var parent = this.parent;
|
|
|
|
|
if (isPresent(parent)) {
|
|
|
|
|
if (parent instanceof Component) {
|
|
|
|
|
parent.addContentNode(ngContentIndex, node);
|
|
|
|
|
parent.addContentNode(ngContentIndex, node, context);
|
|
|
|
|
} else {
|
|
|
|
|
this.factory.appendChild(<N>parent, node);
|
|
|
|
|
context.factory.appendChild(<N>parent, node);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.fragmentRootNodes.push(node);
|
|
|
|
@ -190,17 +212,12 @@ class RenderViewBuilder<N> implements RenderCommandVisitor {
|
|
|
|
|
|
|
|
|
|
class Component<N> {
|
|
|
|
|
private contentNodesByNgContentIndex: N[][] = [];
|
|
|
|
|
private projectingNgContentIndex: number = 0;
|
|
|
|
|
private viewBuilder: RenderViewBuilder<N>;
|
|
|
|
|
|
|
|
|
|
constructor(public hostElement: N, shadowRoot: N, public cmd: RenderBeginComponentCmd,
|
|
|
|
|
public factory: NodeFactory<N>, allBuilders: RenderViewBuilder<N>[]) {
|
|
|
|
|
this.viewBuilder = new RenderViewBuilder(this, shadowRoot, null, allBuilders, factory);
|
|
|
|
|
}
|
|
|
|
|
addContentNode(ngContentIndex: number, node: N) {
|
|
|
|
|
constructor(public hostElement: N, public shadowRoot: N, public cmd: RenderBeginComponentCmd) {}
|
|
|
|
|
addContentNode(ngContentIndex: number, node: N, context: BuildContext<N>) {
|
|
|
|
|
if (isBlank(ngContentIndex)) {
|
|
|
|
|
if (this.cmd.nativeShadow) {
|
|
|
|
|
this.factory.appendChild(this.hostElement, node);
|
|
|
|
|
context.factory.appendChild(this.hostElement, node);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
while (this.contentNodesByNgContentIndex.length <= ngContentIndex) {
|
|
|
|
@ -209,15 +226,11 @@ class Component<N> {
|
|
|
|
|
this.contentNodesByNgContentIndex[ngContentIndex].push(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
project(): N[] {
|
|
|
|
|
var ngContentIndex = this.projectingNgContentIndex++;
|
|
|
|
|
project(ngContentIndex: number): N[] {
|
|
|
|
|
return ngContentIndex < this.contentNodesByNgContentIndex.length ?
|
|
|
|
|
this.contentNodesByNgContentIndex[ngContentIndex] :
|
|
|
|
|
[];
|
|
|
|
|
}
|
|
|
|
|
build() {
|
|
|
|
|
visitAll(this.viewBuilder, this.factory.resolveComponentTemplate(this.cmd.templateId));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addAll(source: any[], target: any[]) {
|
|
|
|
@ -225,9 +238,3 @@ function addAll(source: any[], target: any[]) {
|
|
|
|
|
target.push(source[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function visitAll(visitor: RenderCommandVisitor, fragmentCmds: RenderTemplateCmd[]) {
|
|
|
|
|
for (var i = 0; i < fragmentCmds.length; i++) {
|
|
|
|
|
fragmentCmds[i].visit(visitor, null);
|
|
|
|
|
}
|
|
|
|
|
}
|