import {ComponentFactory, ComponentRef, ElementRef, Injector, TemplateRef, ViewContainerRef, ViewRef} from '@angular/core'; export function unimplemented(): any { throw new Error('unimplemented'); } export interface FtlView { context: C; detectChangesInternal(throwOnChange: boolean): void; createEmbeddedView?(context: NC, nodeIndex: number): FtlEmbeddedView; destroyInternal(): void; } export interface FtlEmbeddedView extends FtlView { // Note: if the view has a view container as first node, // create a comment node before it. This makes // inserting a view before this view simpler! _node0: any; prev: FtlEmbeddedView; next: FtlEmbeddedView; // Purpose of the `ctx` argument: // Allows to use top level functions, i.e. no need to create closures! visitRootNodes(callback: (node: any, ctx: CTX) => void, ctx: CTX): void; } // TODO(tbosch): FTL EmbeddedViewRefs should have no // methods / properties at all. Because of this we can just use the FtlView for it as well! // TODO(tbosch): We can't cast to EmbededViewRef as that also has the `rootNodes` filled // -> would need to generate code for that as well. // Rather: change API for FTL to allow to call View.attachBefore / View.detach // -> faster and we don't need to flatten the root nodes! export type FtlEmbeddedViewRef = FtlView; export class FtlTemplateRef implements TemplateRef { constructor(private _index: number, private _view: FtlView) {} get elementRef(): ElementRef { return unimplemented(); } createEmbeddedView(context: C): any { return this._view.createEmbeddedView(context, this._index); } } export class FtlViewContainerRef implements ViewContainerRef { private _firstView: FtlEmbeddedView = null; private _lastView: FtlEmbeddedView = null; private _length = 0; constructor(private _anchor: any) {} detectChangesInternal(throwOnChange: boolean) { let view = this._firstView; while (view) { view.detectChangesInternal(throwOnChange); view = view.next; } } // TODO(tbosch): don't allow this API in FTL mode! get element(): ElementRef { return unimplemented(); } // TODO(tbosch): don't allow this API in FTL mode! get injector(): Injector { return unimplemented(); } // TODO(tbosch): don't allow this API in FTL mode! get parentInjector(): Injector { return unimplemented(); } destroyInternal() { let view = this._firstView; while (view) { view.destroyInternal(); view = view.next; } } clear(): void { let view = this._firstView; while (view) { detachView(view); view.destroyInternal(); view = view.next; } this._firstView = null; this._lastView = null; this._length = 0; } get(index: number): any { var result = this._firstView; while (index > 0 && result) { result = result.next; index--; } return result; } get length(): number { return this._length; }; createEmbeddedView(templateRef: TemplateRef, context?: C, index?: number): any { const view = templateRef.createEmbeddedView(context); return this.insert(view, index); } createComponent( componentFactory: ComponentFactory, index?: number, injector?: Injector, projectableNodes?: any[][]): ComponentRef { // TODO(tbosch): implement this! return unimplemented(); } insert(viewRef: ViewRef, index?: number): any { const view: FtlEmbeddedView = viewRef; let insertBeforeNode: any; if (this._length === 0) { this._firstView = this._lastView = view; insertBeforeNode = this._anchor; view.prev = null; view.next = null; } else if (index >= this._length) { view.prev = this._lastView; view.next = null; this._lastView.next = view; this._lastView = view; insertBeforeNode = this._anchor; } else { // TODO(tbosch): implement this! unimplemented(); } attachViewBefore(view, insertBeforeNode); this._length++; } detach(index?: number): any { let view: FtlEmbeddedView; if (this._length === 1) { view = this._firstView; this._firstView = this._lastView = null; } else if (index >= this._length) { view = this._lastView; this._lastView = view.prev; view.prev = null; this._lastView.next = null; } else { // TODO(tbosch): implement this! unimplemented(); } this._length--; detachView(view); return view; } move(viewRef: ViewRef, currentIndex: number): ViewRef { // TODO(tbosch): implement this! return unimplemented(); } indexOf(viewRef: ViewRef): number { // TODO(tbosch): implement this! return unimplemented(); } remove(index?: number): void { var view: FtlView = this.detach(index); view.destroyInternal(); } } function attachViewBefore(view: FtlEmbeddedView, node: any) { const parent = node.parentNode; view.visitRootNodes(insertBefore, {parent: parent, refNode: node}); } function insertBefore(node: any, ctx: {parent: any, refNode: any}) { ctx.parent.insertBefore(node, ctx.refNode); } function detachView(view: FtlEmbeddedView) { view.visitRootNodes(remove, null); } function remove(node: any, ctx: any) { node.remove(); } export function createElementAndAppend(parent: any, name: string) { const el = document.createElement(name); parent.appendChild(el); return el; } export function createTextAndAppend(parent: any) { const txt = document.createTextNode(''); parent.appendChild(txt); return txt; } export function createAnchorAndAppend(parent: any) { const txt = document.createComment(''); parent.appendChild(txt); return txt; }