import {OpaqueToken, Inject, Injectable} from 'angular2/di'; import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; import {DOM} from 'angular2/src/dom/dom_adapter'; import {Content} from '../shadow_dom/content_tag'; import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy'; import {EventManager} from 'angular2/src/render/dom/events/event_manager'; import * as vcModule from './view_container'; import * as pvModule from './proto_view'; import * as viewModule from './view'; import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from '../util'; // TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this! export const VIEW_POOL_CAPACITY = 'render.ViewFactory.viewPoolCapacity'; @Injectable() export class ViewFactory { _poolCapacityPerProtoView:number; _pooledViewsPerProtoView:Map>; _eventManager:EventManager; _shadowDomStrategy:ShadowDomStrategy; constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, eventManager:EventManager, shadowDomStrategy:ShadowDomStrategy) { this._poolCapacityPerProtoView = poolCapacityPerProtoView; this._pooledViewsPerProtoView = MapWrapper.create(); this._eventManager = eventManager; this._shadowDomStrategy = shadowDomStrategy; } getView(protoView:pvModule.RenderProtoView):viewModule.RenderView { var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView); if (isPresent(pooledViews)) { var result = ListWrapper.removeLast(pooledViews); if (pooledViews.length === 0) { MapWrapper.delete(this._pooledViewsPerProtoView, protoView); } return result; } return this._createView(protoView); } returnView(view:viewModule.RenderView) { if (view.hydrated()) { view.dehydrate(); } var protoView = view.proto; var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView); if (isBlank(pooledViews)) { pooledViews = []; MapWrapper.set(this._pooledViewsPerProtoView, protoView, pooledViews); } if (pooledViews.length < this._poolCapacityPerProtoView) { ListWrapper.push(pooledViews, view); } } _createView(protoView:pvModule.RenderProtoView): viewModule.RenderView { var rootElementClone = protoView.isRootView ? protoView.element : DOM.importIntoDoc(protoView.element); var elementsWithBindingsDynamic; if (protoView.isTemplateElement) { elementsWithBindingsDynamic = DOM.querySelectorAll(DOM.content(rootElementClone), NG_BINDING_CLASS_SELECTOR); } else { elementsWithBindingsDynamic = DOM.getElementsByClassName(rootElementClone, NG_BINDING_CLASS); } var elementsWithBindings = ListWrapper.createFixedSize(elementsWithBindingsDynamic.length); for (var binderIdx = 0; binderIdx < elementsWithBindingsDynamic.length; ++binderIdx) { elementsWithBindings[binderIdx] = elementsWithBindingsDynamic[binderIdx]; } var viewRootNodes; if (protoView.isTemplateElement) { var childNode = DOM.firstChild(DOM.content(rootElementClone)); viewRootNodes = []; // TODO(perf): Should be fixed size, since we could pre-compute in in pvModule.RenderProtoView // Note: An explicit loop is the fastest way to convert a DOM array into a JS array! while(childNode != null) { ListWrapper.push(viewRootNodes, childNode); childNode = DOM.nextSibling(childNode); } } else { viewRootNodes = [rootElementClone]; } var binders = protoView.elementBinders; var boundTextNodes = []; var boundElements = ListWrapper.createFixedSize(binders.length); var viewContainers = ListWrapper.createFixedSize(binders.length); var contentTags = ListWrapper.createFixedSize(binders.length); for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { var binder = binders[binderIdx]; var element; if (binderIdx === 0 && protoView.rootBindingOffset === 1) { element = rootElementClone; } else { element = elementsWithBindings[binderIdx - protoView.rootBindingOffset]; } boundElements[binderIdx] = element; // boundTextNodes var childNodes = DOM.childNodes(DOM.templateAwareRoot(element)); var textNodeIndices = binder.textNodeIndices; for (var i = 0; i { view.dispatchEvent(elementIndex, eventName, event); }); } }