import {isPresent} from 'angular2/src/facade/lang'; import {Promise} from 'angular2/src/facade/async'; import {List, Map} from 'angular2/src/facade/collection'; import {ASTWithSource} from 'angular2/change_detection'; /** * General notes: * * The methods for creating / destroying views in this API are used in the AppViewHydrator * and RenderViewHydrator as well. * * We are already parsing expressions on the render side: * - this makes the ElementBinders more compact * (e.g. no need to distinguish interpolations from regular expressions from literals) * - allows to retrieve which properties should be accessed from the event * by looking at the expression * - we need the parse at least for the `template` attribute to match * directives in it * - render compiler is not on the critical path as * its output will be stored in precompiled templates. */ export class EventBinding { fullName: string; // name/target:name, e.g "click", "window:resize" source: ASTWithSource; constructor(fullName: string, source: ASTWithSource) { this.fullName = fullName; this.source = source; } } export class ElementBinder { index: number; parentIndex: number; distanceToParent: number; directives: List<DirectiveBinder>; nestedProtoView: ProtoViewDto; propertyBindings: Map<string, ASTWithSource>; variableBindings: Map<string, ASTWithSource>; // Note: this contains a preprocessed AST // that replaced the values that should be extracted from the element // with a local name eventBindings: List<EventBinding>; textBindings: List<ASTWithSource>; readAttributes: Map<string, string>; constructor({ index, parentIndex, distanceToParent, directives, nestedProtoView, propertyBindings, variableBindings, eventBindings, textBindings, readAttributes }:{index?:number, parentIndex?:number, distanceToParent?:number, directives?:List<DirectiveBinder>, nestedProtoView?:ProtoViewDto, propertyBindings?:Map<string, ASTWithSource>, variableBindings?:Map<string, ASTWithSource>, eventBindings?:List<EventBinding>, textBindings?:List<ASTWithSource>, readAttributes?:Map<string, string>}={}) { this.index = index; this.parentIndex = parentIndex; this.distanceToParent = distanceToParent; this.directives = directives; this.nestedProtoView = nestedProtoView; this.propertyBindings = propertyBindings; this.variableBindings = variableBindings; this.eventBindings = eventBindings; this.textBindings = textBindings; this.readAttributes = readAttributes; } } export class DirectiveBinder { // Index into the array of directives in the View instance directiveIndex: number; propertyBindings: Map<string, ASTWithSource>; // Note: this contains a preprocessed AST // that replaced the values that should be extracted from the element // with a local name eventBindings: List<EventBinding>; hostPropertyBindings: Map<string, ASTWithSource>; constructor({ directiveIndex, propertyBindings, eventBindings, hostPropertyBindings }:{ directiveIndex?:number, propertyBindings?:Map<string, ASTWithSource>, eventBindings?:List<EventBinding>, hostPropertyBindings?:Map<string, ASTWithSource> }) { this.directiveIndex = directiveIndex; this.propertyBindings = propertyBindings; this.eventBindings = eventBindings; this.hostPropertyBindings = hostPropertyBindings; } } export class ProtoViewDto { // A view that contains the host element with bound // component directive. // Contains a view of type #COMPONENT_VIEW_TYPE. static get HOST_VIEW_TYPE() { return 0; } // The view of the component // Can contain 0 to n views of type #EMBEDDED_VIEW_TYPE static get COMPONENT_VIEW_TYPE() { return 1; } // A view that is embedded into another View via a <template> element // inside of a component view static get EMBEDDED_VIEW_TYPE() { return 2; } render: RenderProtoViewRef; elementBinders: List<ElementBinder>; variableBindings: Map<string, string>; type: number; constructor({ render, elementBinders, variableBindings, type }:{ render?:RenderProtoViewRef, elementBinders?:List<ElementBinder>, variableBindings?:Map<string, string>, type?:number }) { this.render = render; this.elementBinders = elementBinders; this.variableBindings = variableBindings; this.type = type; } } export class DirectiveMetadata { static get DIRECTIVE_TYPE() { return 0; } static get COMPONENT_TYPE() { return 1; } id: any; selector: string; compileChildren: boolean; events: List<string>; hostListeners: Map<string, string>; hostProperties: Map<string, string>; hostAttributes: Map<string, string>; hostActions: Map<string, string>; properties: Map<string, string>; readAttributes: List<string>; type: number; callOnDestroy: boolean; callOnChange: boolean; callOnAllChangesDone: boolean; changeDetection: string; constructor({ id, selector, compileChildren, events, hostListeners, hostProperties, hostAttributes, hostActions, properties, readAttributes, type, callOnDestroy, callOnChange, callOnAllChangesDone, changeDetection }:{ id?:string, selector?:string, compileChildren?:boolean, events?:List<string>, hostListeners?:Map<string, string>, hostProperties?:Map<string, string>, hostAttributes?:Map<string, string>, hostActions?:Map<string, string>, properties?:Map<string, string>, readAttributes?:List<string>, type?:number, callOnDestroy?:boolean, callOnChange?:boolean, callOnAllChangesDone?:boolean, changeDetection?:string }) { this.id = id; this.selector = selector; this.compileChildren = isPresent(compileChildren) ? compileChildren : true; this.events = events; this.hostListeners = hostListeners; this.hostProperties = hostProperties; this.hostAttributes = hostAttributes; this.hostActions = hostActions; this.properties = properties; this.readAttributes = readAttributes; this.type = type; this.callOnDestroy = callOnDestroy; this.callOnChange = callOnChange; this.callOnAllChangesDone = callOnAllChangesDone; this.changeDetection = changeDetection; } } // An opaque reference to a DomProtoView export class RenderProtoViewRef {} // An opaque reference to a DomView export class RenderViewRef {} export class ViewDefinition { componentId: string; absUrl: string; template: string; directives: List<DirectiveMetadata>; constructor({ componentId, absUrl, template, directives }:{ componentId?:string, absUrl?:string, template?:string, directives?:List<DirectiveMetadata> }) { this.componentId = componentId; this.absUrl = absUrl; this.template = template; this.directives = directives; } } export class RenderCompiler { /** * Creats a ProtoViewDto that contains a single nested component with the given componentId. */ compileHost(directiveMetadata: DirectiveMetadata): Promise<ProtoViewDto> { return null; } /** * Compiles a single DomProtoView. Non recursive so that * we don't need to serialize all possible components over the wire, * but only the needed ones based on previous calls. */ compile(template: ViewDefinition): Promise<ProtoViewDto> { return null; } } export class Renderer { /** * Creates a root host view that includes the given element. * @param {RenderProtoViewRef} hostProtoViewRef a RenderProtoViewRef of type * ProtoViewDto.HOST_VIEW_TYPE * @param {any} hostElementSelector css selector for the host element (will be queried against the * main document) * @return {RenderViewRef} the created view */ createRootHostView(hostProtoViewRef: RenderProtoViewRef, hostElementSelector: string): RenderViewRef { return null; } /** * Detaches a free host view's element from the DOM. */ detachFreeHostView(parentHostViewRef: RenderViewRef, hostViewRef: RenderViewRef) {} /** * Creates a regular view out of the given ProtoView */ createView(protoViewRef: RenderProtoViewRef): RenderViewRef { return null; } /** * Destroys the given view after it has been dehydrated and detached */ destroyView(viewRef: RenderViewRef) {} /** * Attaches a componentView into the given hostView at the given element */ attachComponentView(hostViewRef: RenderViewRef, elementIndex: number, componentViewRef: RenderViewRef) {} /** * Detaches a componentView into the given hostView at the given element */ detachComponentView(hostViewRef: RenderViewRef, boundElementIndex: number, componentViewRef: RenderViewRef) {} /** * Attaches a view into a ViewContainer (in the given parentView at the given element) at the * given index. */ attachViewInContainer(parentViewRef: RenderViewRef, boundElementIndex: number, atIndex: number, viewRef: RenderViewRef) {} /** * Detaches a view into a ViewContainer (in the given parentView at the given element) at the * given index. */ // TODO(tbosch): this should return a promise as it can be animated! detachViewInContainer(parentViewRef: RenderViewRef, boundElementIndex: number, atIndex: number, viewRef: RenderViewRef) {} /** * Hydrates a view after it has been attached. Hydration/dehydration is used for reusing views * inside of the view pool. */ hydrateView(viewRef: RenderViewRef) {} /** * Dehydrates a view after it has been attached. Hydration/dehydration is used for reusing views * inside of the view pool. */ dehydrateView(viewRef: RenderViewRef) {} /** * Sets a property on an element. * Note: This will fail if the property was not mentioned previously as a host property * in the ProtoView */ setElementProperty(viewRef: RenderViewRef, elementIndex: number, propertyName: string, propertyValue: any) {} /** * Calls an action. * Note: This will fail if the action was not mentioned previously as a host action * in the ProtoView */ callAction(viewRef: RenderViewRef, elementIndex: number, actionExpression: string, actionArgs: any) {} /** * Sets the value of a text node. */ setText(viewRef: RenderViewRef, textNodeIndex: number, text: string) {} /** * Sets the dispatcher for all events of the given view */ setEventDispatcher(viewRef: RenderViewRef, dispatcher: EventDispatcher) {} } /** * A dispatcher for all events happening in a view. */ export interface EventDispatcher { /** * Called when an event was triggered for a on-* attribute on an element. * @param {Map<string, any>} locals Locals to be used to evaluate the * event expressions */ dispatchEvent(elementIndex: number, eventName: string, locals: Map<string, any>); }