import { Container } from 'aurelia-dependency-injection'; import { DOM } from 'aurelia-pal'; import { Animator } from './animator'; import { CompositionContext, CompositionEngine } from './composition-engine'; import { Controller } from './controller'; import { BehaviorInstruction } from './instructions'; import { ModuleAnalyzer } from './module-analyzer'; import { View } from './view'; import { ViewCompiler } from './view-compiler'; import { ViewFactory } from './view-factory'; import { ViewResources } from './view-resources'; /** * Instructs the framework in how to enhance an existing DOM structure. */ export interface EnhanceInstruction { /** * The DI container to use as the root for UI enhancement. */ container?: Container; /** * The element to enhance. */ element: Element; /** * The resources available for enhancement. */ resources?: ViewResources; /** * A binding context for the enhancement. */ bindingContext?: Object; /** * A secondary binding context that can override the standard context. */ overrideContext?: any; } /** * A facade of the templating engine capabilties which provides a more user friendly API for common use cases. */ export class TemplatingEngine { /** @internal */ static inject = [Container, ModuleAnalyzer, ViewCompiler, CompositionEngine]; /** @internal */ private _container: Container; /** @internal */ private _moduleAnalyzer: ModuleAnalyzer; /** @internal */ private _viewCompiler: ViewCompiler; /** @internal */ private _compositionEngine: CompositionEngine; /** * Creates an instance of TemplatingEngine. * @param container The root DI container. * @param moduleAnalyzer The module analyzer for discovering view resources. * @param viewCompiler The view compiler for compiling views. * @param compositionEngine The composition engine used during dynamic component composition. */ constructor(container: Container, moduleAnalyzer: ModuleAnalyzer, viewCompiler: ViewCompiler, compositionEngine: CompositionEngine) { this._container = container; this._moduleAnalyzer = moduleAnalyzer; this._viewCompiler = viewCompiler; this._compositionEngine = compositionEngine; container.registerInstance(Animator, Animator.instance = new Animator()); } /** * Configures the default animator. * @param animator The animator instance. */ configureAnimator(animator: Animator): void { this._container.unregister(Animator); this._container.registerInstance(Animator, Animator.instance = animator); } /** * Dynamically composes components and views. * @param context The composition context to use. * @return A promise for the resulting Controller or View. Consumers of this API * are responsible for enforcing the Controller/View lifecycle. */ compose(context: CompositionContext): Promise { return this._compositionEngine.compose(context); } /** * Enhances existing DOM with behaviors and bindings. * @param instruction The element to enhance or a set of instructions for the enhancement process. * @return A View representing the enhanced UI. Consumers of this API * are responsible for enforcing the View lifecycle. */ enhance(instruction: Element | EnhanceInstruction): View { if (instruction instanceof DOM.Element) { instruction = { element: instruction }; } let compilerInstructions = { letExpressions: [] }; let resources = instruction.resources || this._container.get(ViewResources); this._viewCompiler._compileNode(instruction.element, resources, compilerInstructions, instruction.element.parentNode, 'root', true); // todo(typings): view factory should accept Element beside Document fragment let factory = new ViewFactory(instruction.element as unknown as DocumentFragment, compilerInstructions, resources); let container = instruction.container || this._container.createChild(); let view = factory.create(container, BehaviorInstruction.enhance()); view.bind(instruction.bindingContext || {}, instruction.overrideContext); view.firstChild = view.lastChild = view.fragment as ChildNode; view.fragment = DOM.createDocumentFragment(); view.attached(); return view; } }