import {OpaqueToken} 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 {ViewContainer} from './view_container'; import {ProtoView} from './proto_view'; import {View} from './view'; import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from '../util'; export var VIEW_POOL_CAPACITY = new OpaqueToken('ViewFactory.viewPoolCapacity'); export class ViewFactory { _poolCapacity:number; _pooledViews:List; _eventManager:EventManager; _shadowDomStrategy:ShadowDomStrategy; constructor(capacity, eventManager, shadowDomStrategy) { this._poolCapacity = capacity; this._pooledViews = ListWrapper.create(); this._eventManager = eventManager; this._shadowDomStrategy = shadowDomStrategy; } getView(protoView:ProtoView):View { // TODO(tbosch): benchmark this scanning of views and maybe // replace it with a fancy LRU Map/List combination... var view; for (var i=0; i this._poolCapacity) { ListWrapper.removeAt(this._pooledViews, 0); } } _createView(protoView:ProtoView): View { 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 ProtoView // 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 { this._createEventListener(view, element, binderIdx, eventName, binder.eventLocals); }); } } if (protoView.isRootView) { view.hydrate(null); } return view; } _createEventListener(view, element, elementIndex, eventName, eventLocals) { this._eventManager.addEventListener(element, eventName, (event) => { view.dispatchEvent(elementIndex, eventName, event); }); } }