2014-10-10 20:44:55 -07:00
|
|
|
import {DOM, Element, Node, Text, DocumentFragment, TemplateElement} from 'facade/dom';
|
2014-12-02 17:09:46 -08:00
|
|
|
import {ListWrapper, MapWrapper, StringMapWrapper, List} from 'facade/collection';
|
2014-11-19 15:52:01 -08:00
|
|
|
import {ProtoRecordRange, RecordRange, WatchGroupDispatcher} from 'change_detection/record_range';
|
2014-09-28 16:29:11 -07:00
|
|
|
import {Record} from 'change_detection/record';
|
2014-11-11 17:33:47 -08:00
|
|
|
import {AST} from 'change_detection/parser/ast';
|
2014-11-13 15:15:55 -08:00
|
|
|
|
2014-11-14 10:58:58 -08:00
|
|
|
import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
|
2014-11-11 17:33:47 -08:00
|
|
|
import {ElementBinder} from './element_binder';
|
2014-11-13 15:15:55 -08:00
|
|
|
import {AnnotatedType} from './annotated_type';
|
2014-11-20 12:07:48 -08:00
|
|
|
import {SetterFn} from 'reflection/types';
|
2014-10-27 11:47:13 -04:00
|
|
|
import {FIELD, IMPLEMENTS, int, isPresent, isBlank} from 'facade/lang';
|
2014-10-29 15:41:50 -07:00
|
|
|
import {Injector} from 'di/di';
|
2014-11-14 10:58:58 -08:00
|
|
|
import {NgElement} from 'core/dom/element';
|
2014-11-21 15:13:01 -08:00
|
|
|
import {ViewPort} from './viewport';
|
2014-12-02 17:09:46 -08:00
|
|
|
import {OnChange} from './interfaces';
|
2014-09-28 16:29:11 -07:00
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
const NG_BINDING_CLASS = 'ng-binding';
|
|
|
|
|
2014-11-21 15:13:01 -08:00
|
|
|
/**
|
2014-10-10 20:44:55 -07:00
|
|
|
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
|
|
|
*/
|
2014-09-28 16:29:11 -07:00
|
|
|
@IMPLEMENTS(WatchGroupDispatcher)
|
|
|
|
export class View {
|
|
|
|
/// This list matches the _nodes list. It is sparse, since only Elements have ElementInjector
|
2014-11-21 21:19:23 -08:00
|
|
|
rootElementInjectors:List<ElementInjector>;
|
|
|
|
elementInjectors:List<ElementInjector>;
|
|
|
|
bindElements:List<Element>;
|
|
|
|
textNodes:List<Text>;
|
|
|
|
recordRange:RecordRange;
|
2014-09-28 16:29:11 -07:00
|
|
|
/// When the view is part of render tree, the DocumentFragment is empty, which is why we need
|
|
|
|
/// to keep track of the nodes.
|
2014-11-21 21:19:23 -08:00
|
|
|
nodes:List<Node>;
|
|
|
|
onChangeDispatcher:OnChangeDispatcher;
|
2014-11-21 15:13:01 -08:00
|
|
|
componentChildViews: List<View>;
|
|
|
|
viewPorts: List<ViewPort>;
|
2014-11-11 17:33:47 -08:00
|
|
|
constructor(nodes:List<Node>, elementInjectors:List,
|
2014-10-29 15:41:50 -07:00
|
|
|
rootElementInjectors:List, textNodes:List, bindElements:List,
|
2014-11-19 15:52:01 -08:00
|
|
|
protoRecordRange:ProtoRecordRange, context) {
|
2014-11-11 17:33:47 -08:00
|
|
|
this.nodes = nodes;
|
|
|
|
this.elementInjectors = elementInjectors;
|
2014-10-27 11:47:13 -04:00
|
|
|
this.rootElementInjectors = rootElementInjectors;
|
2014-10-10 20:44:55 -07:00
|
|
|
this.onChangeDispatcher = null;
|
2014-10-27 11:47:13 -04:00
|
|
|
this.textNodes = textNodes;
|
2014-10-27 23:16:31 -07:00
|
|
|
this.bindElements = bindElements;
|
2014-11-19 15:52:01 -08:00
|
|
|
this.recordRange = protoRecordRange.instantiate(this, MapWrapper.create());
|
|
|
|
this.recordRange.setContext(context);
|
2014-11-21 15:13:01 -08:00
|
|
|
this.componentChildViews = null;
|
|
|
|
this.viewPorts = null;
|
2014-09-28 16:29:11 -07:00
|
|
|
}
|
|
|
|
|
2014-12-02 17:09:46 -08:00
|
|
|
onRecordChange(groupMemento, records:List<Record>) {
|
|
|
|
this._invokeMementoForRecords(records);
|
|
|
|
if (groupMemento instanceof DirectivePropertyGroupMemento) {
|
|
|
|
this._notifyDirectiveAboutChanges(groupMemento, records);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_invokeMementoForRecords(records:List<Record>) {
|
|
|
|
for(var i = 0; i < records.length; ++i) {
|
|
|
|
this._invokeMementoFor(records[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_notifyDirectiveAboutChanges(groupMemento, records:List<Record>) {
|
|
|
|
var dir = groupMemento.directive(this.elementInjectors);
|
|
|
|
if (dir instanceof OnChange) {
|
|
|
|
dir.onChange(this._collectChanges(records));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-28 16:29:11 -07:00
|
|
|
// dispatch to element injector or text nodes based on context
|
2014-12-02 17:09:46 -08:00
|
|
|
_invokeMementoFor(record:Record) {
|
|
|
|
var memento = record.expressionMemento();
|
|
|
|
if (memento instanceof DirectivePropertyMemento) {
|
2014-10-10 20:44:55 -07:00
|
|
|
// we know that it is DirectivePropertyMemento
|
2014-12-02 17:09:46 -08:00
|
|
|
var directiveMemento:DirectivePropertyMemento = memento;
|
2014-10-10 20:44:55 -07:00
|
|
|
directiveMemento.invoke(record, this.elementInjectors);
|
2014-12-02 17:09:46 -08:00
|
|
|
|
|
|
|
} else if (memento instanceof ElementPropertyMemento) {
|
|
|
|
var elementMemento:ElementPropertyMemento = memento;
|
2014-10-10 20:44:55 -07:00
|
|
|
elementMemento.invoke(record, this.bindElements);
|
2014-12-02 17:09:46 -08:00
|
|
|
|
2014-09-28 16:29:11 -07:00
|
|
|
} else {
|
2014-10-10 20:44:55 -07:00
|
|
|
// we know it refers to _textNodes.
|
2014-12-02 17:09:46 -08:00
|
|
|
var textNodeIndex:number = memento;
|
2014-10-10 20:44:55 -07:00
|
|
|
DOM.setText(this.textNodes[textNodeIndex], record.currentValue);
|
2014-09-28 16:29:11 -07:00
|
|
|
}
|
|
|
|
}
|
2014-11-12 11:40:36 -08:00
|
|
|
|
2014-12-02 17:09:46 -08:00
|
|
|
_collectChanges(records:List) {
|
|
|
|
var changes = StringMapWrapper.create();
|
|
|
|
for(var i = 0; i < records.length; ++i) {
|
|
|
|
var record = records[i];
|
|
|
|
var propertyUpdate = new PropertyUpdate(record.currentValue, record.previousValue);
|
|
|
|
StringMapWrapper.set(changes, record.expressionMemento()._setterName, propertyUpdate);
|
|
|
|
}
|
|
|
|
return changes;
|
|
|
|
}
|
|
|
|
|
2014-11-21 15:13:01 -08:00
|
|
|
addViewPort(viewPort: ViewPort) {
|
|
|
|
if (isBlank(this.viewPorts)) this.viewPorts = [];
|
|
|
|
ListWrapper.push(this.viewPorts, viewPort);
|
|
|
|
}
|
|
|
|
|
|
|
|
addComponentChildView(childView: View) {
|
|
|
|
if (isBlank(this.componentChildViews)) this.componentChildViews = [];
|
|
|
|
ListWrapper.push(this.componentChildViews, childView);
|
|
|
|
this.recordRange.addRange(childView.recordRange);
|
|
|
|
}
|
|
|
|
|
|
|
|
addViewPortChildView(childView: View) {
|
2014-11-19 15:52:01 -08:00
|
|
|
this.recordRange.addRange(childView.recordRange);
|
2014-11-12 11:40:36 -08:00
|
|
|
}
|
2014-11-21 15:13:01 -08:00
|
|
|
|
|
|
|
removeViewPortChildView(childView: View) {
|
|
|
|
childView.recordRange.remove();
|
|
|
|
}
|
2014-09-28 16:29:11 -07:00
|
|
|
}
|
|
|
|
|
2014-09-28 20:02:32 -07:00
|
|
|
export class ProtoView {
|
2014-11-21 21:19:23 -08:00
|
|
|
element:Element;
|
|
|
|
elementBinders:List<ElementBinder>;
|
|
|
|
protoRecordRange:ProtoRecordRange;
|
|
|
|
variableBindings: Map;
|
|
|
|
textNodesWithBindingCount:int;
|
|
|
|
elementsWithBindingCount:int;
|
2014-09-28 20:02:32 -07:00
|
|
|
constructor(
|
2014-11-11 17:33:47 -08:00
|
|
|
template:Element,
|
2014-11-19 15:52:01 -08:00
|
|
|
protoRecordRange:ProtoRecordRange) {
|
2014-11-11 17:33:47 -08:00
|
|
|
this.element = template;
|
|
|
|
this.elementBinders = [];
|
2014-11-18 16:38:36 -08:00
|
|
|
this.variableBindings = MapWrapper.create();
|
2014-11-19 15:52:01 -08:00
|
|
|
this.protoRecordRange = protoRecordRange;
|
2014-11-11 17:33:47 -08:00
|
|
|
this.textNodesWithBindingCount = 0;
|
|
|
|
this.elementsWithBindingCount = 0;
|
2014-09-28 20:02:32 -07:00
|
|
|
}
|
|
|
|
|
2014-11-07 14:30:04 -08:00
|
|
|
instantiate(context, lightDomAppInjector:Injector,
|
|
|
|
hostElementInjector: ElementInjector, inPlace:boolean = false):View {
|
|
|
|
var clone = inPlace ? this.element : DOM.clone(this.element);
|
2014-11-14 10:37:42 -08:00
|
|
|
var elements;
|
|
|
|
if (clone instanceof TemplateElement) {
|
|
|
|
elements = ListWrapper.clone(DOM.querySelectorAll(clone.content, `.${NG_BINDING_CLASS}`));
|
|
|
|
} else {
|
|
|
|
elements = ListWrapper.clone(DOM.getElementsByClassName(clone, NG_BINDING_CLASS));
|
|
|
|
}
|
2014-11-11 17:33:47 -08:00
|
|
|
if (DOM.hasClass(clone, NG_BINDING_CLASS)) {
|
|
|
|
ListWrapper.insert(elements, 0, clone);
|
|
|
|
}
|
|
|
|
var binders = this.elementBinders;
|
2014-10-27 11:47:13 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO: vsavkin: benchmark
|
2014-11-12 11:40:36 -08:00
|
|
|
* If this performs poorly, the seven loops can be collapsed into one.
|
2014-10-27 11:47:13 -04:00
|
|
|
*/
|
2014-11-12 11:40:36 -08:00
|
|
|
var elementInjectors = ProtoView._createElementInjectors(elements, binders, hostElementInjector);
|
2014-10-27 11:47:13 -04:00
|
|
|
var rootElementInjectors = ProtoView._rootElementInjectors(elementInjectors);
|
2014-10-30 14:41:19 -07:00
|
|
|
var textNodes = ProtoView._textNodes(elements, binders);
|
|
|
|
var bindElements = ProtoView._bindElements(elements, binders);
|
2014-11-12 11:40:36 -08:00
|
|
|
var shadowAppInjectors = ProtoView._createShadowAppInjectors(binders, lightDomAppInjector);
|
2014-11-11 17:33:47 -08:00
|
|
|
var viewNodes;
|
2014-11-12 11:40:36 -08:00
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
if (clone instanceof TemplateElement) {
|
|
|
|
viewNodes = ListWrapper.clone(clone.content.childNodes);
|
|
|
|
} else {
|
|
|
|
viewNodes = [clone];
|
|
|
|
}
|
2014-11-14 10:58:58 -08:00
|
|
|
var view = new View(viewNodes, elementInjectors, rootElementInjectors, textNodes,
|
2014-11-19 15:52:01 -08:00
|
|
|
bindElements, this.protoRecordRange, context);
|
2014-11-14 10:58:58 -08:00
|
|
|
|
2014-11-12 11:40:36 -08:00
|
|
|
ProtoView._instantiateDirectives(
|
2014-11-21 15:13:01 -08:00
|
|
|
view, elements, binders, elementInjectors, lightDomAppInjector,
|
|
|
|
shadowAppInjectors, hostElementInjector);
|
|
|
|
ProtoView._instantiateChildComponentViews(view, elements, binders,
|
|
|
|
elementInjectors, shadowAppInjectors);
|
2014-11-14 10:58:58 -08:00
|
|
|
|
|
|
|
return view;
|
2014-11-11 17:33:47 -08:00
|
|
|
}
|
|
|
|
|
2014-11-18 16:38:36 -08:00
|
|
|
bindVariable(contextName:string, templateName:string) {
|
|
|
|
MapWrapper.set(this.variableBindings, contextName, templateName);
|
|
|
|
}
|
|
|
|
|
2014-11-13 15:15:55 -08:00
|
|
|
bindElement(protoElementInjector:ProtoElementInjector,
|
|
|
|
componentDirective:AnnotatedType = null, templateDirective:AnnotatedType = null):ElementBinder {
|
|
|
|
var elBinder = new ElementBinder(protoElementInjector, componentDirective, templateDirective);
|
2014-11-11 17:33:47 -08:00
|
|
|
ListWrapper.push(this.elementBinders, elBinder);
|
|
|
|
return elBinder;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a text node binding for the last created ElementBinder via bindElement
|
|
|
|
*/
|
|
|
|
bindTextNode(indexInParent:int, expression:AST) {
|
|
|
|
var elBinder = this.elementBinders[this.elementBinders.length-1];
|
2014-11-19 14:54:07 -08:00
|
|
|
if (isBlank(elBinder.textNodeIndices)) {
|
|
|
|
elBinder.textNodeIndices = ListWrapper.create();
|
|
|
|
}
|
2014-11-11 17:33:47 -08:00
|
|
|
ListWrapper.push(elBinder.textNodeIndices, indexInParent);
|
2014-12-02 17:09:46 -08:00
|
|
|
var memento = this.textNodesWithBindingCount++;
|
|
|
|
this.protoRecordRange.addRecordsFromAST(expression, memento, memento);
|
2014-11-11 17:33:47 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds an element property binding for the last created ElementBinder via bindElement
|
|
|
|
*/
|
|
|
|
bindElementProperty(propertyName:string, expression:AST) {
|
|
|
|
var elBinder = this.elementBinders[this.elementBinders.length-1];
|
|
|
|
if (!elBinder.hasElementPropertyBindings) {
|
|
|
|
elBinder.hasElementPropertyBindings = true;
|
|
|
|
this.elementsWithBindingCount++;
|
|
|
|
}
|
2014-12-02 17:09:46 -08:00
|
|
|
var memento = new ElementPropertyMemento(this.elementsWithBindingCount-1, propertyName);
|
|
|
|
this.protoRecordRange.addRecordsFromAST(expression, memento, memento);
|
2014-11-11 17:33:47 -08:00
|
|
|
}
|
|
|
|
|
2014-11-19 14:54:07 -08:00
|
|
|
/**
|
|
|
|
* Adds an event binding for the last created ElementBinder via bindElement
|
|
|
|
*/
|
|
|
|
bindEvent(eventName:string, expression:AST) {
|
|
|
|
var elBinder = this.elementBinders[this.elementBinders.length-1];
|
|
|
|
if (isBlank(elBinder.events)) {
|
|
|
|
elBinder.events = MapWrapper.create();
|
|
|
|
}
|
|
|
|
MapWrapper.set(elBinder.events, eventName, expression);
|
|
|
|
}
|
|
|
|
|
2014-11-11 17:33:47 -08:00
|
|
|
/**
|
|
|
|
* Adds a directive property binding for the last created ElementBinder via bindElement
|
|
|
|
*/
|
|
|
|
bindDirectiveProperty(
|
|
|
|
directiveIndex:number,
|
|
|
|
expression:AST,
|
|
|
|
setterName:string,
|
|
|
|
setter:SetterFn) {
|
2014-12-02 17:09:46 -08:00
|
|
|
|
|
|
|
var expMemento = new DirectivePropertyMemento(
|
|
|
|
this.elementBinders.length-1,
|
|
|
|
directiveIndex,
|
|
|
|
setterName,
|
|
|
|
setter
|
2014-11-11 17:33:47 -08:00
|
|
|
);
|
2014-12-02 17:09:46 -08:00
|
|
|
var groupMemento = DirectivePropertyGroupMemento.get(expMemento);
|
|
|
|
this.protoRecordRange.addRecordsFromAST(expression, expMemento, groupMemento, false);
|
2014-10-27 11:47:13 -04:00
|
|
|
}
|
|
|
|
|
2014-11-12 11:40:36 -08:00
|
|
|
static _createElementInjectors(elements, binders, hostElementInjector) {
|
2014-10-30 14:41:19 -07:00
|
|
|
var injectors = ListWrapper.createFixedSize(binders.length);
|
|
|
|
for (var i = 0; i < binders.length; ++i) {
|
2014-11-04 17:03:35 -08:00
|
|
|
var proto = binders[i].protoElementInjector;
|
2014-11-11 17:33:47 -08:00
|
|
|
if (isPresent(proto)) {
|
|
|
|
var parentElementInjector = isPresent(proto.parent) ? injectors[proto.parent.index] : null;
|
2014-11-12 11:40:36 -08:00
|
|
|
injectors[i] = proto.instantiate(parentElementInjector, hostElementInjector);
|
2014-11-11 17:33:47 -08:00
|
|
|
} else {
|
|
|
|
injectors[i] = null;
|
|
|
|
}
|
2014-10-27 23:16:31 -07:00
|
|
|
}
|
2014-10-27 11:47:13 -04:00
|
|
|
return injectors;
|
|
|
|
}
|
|
|
|
|
2014-10-29 15:41:50 -07:00
|
|
|
static _instantiateDirectives(
|
2014-11-21 15:13:01 -08:00
|
|
|
view, elements:List, binders: List<ElementBinder>, injectors:List<ElementInjectors>,
|
|
|
|
lightDomAppInjector: Injector, shadowDomAppInjectors:List<Injectors>,
|
|
|
|
hostElementInjector: ElementInjector) {
|
2014-10-29 15:41:50 -07:00
|
|
|
for (var i = 0; i < injectors.length; ++i) {
|
2014-11-21 15:13:01 -08:00
|
|
|
var injector = injectors[i];
|
|
|
|
if (injector != null) {
|
|
|
|
var binder = binders[i];
|
|
|
|
var element = elements[i];
|
|
|
|
var ngElement = new NgElement(element);
|
|
|
|
var viewPort = null;
|
|
|
|
if (isPresent(binder.templateDirective)) {
|
|
|
|
viewPort = new ViewPort(view, element, binder.nestedProtoView, injector);
|
|
|
|
viewPort.attach(lightDomAppInjector, hostElementInjector);
|
|
|
|
view.addViewPort(viewPort);
|
|
|
|
}
|
|
|
|
var preBuiltObjs = new PreBuiltObjects(view, ngElement, viewPort);
|
|
|
|
injector.instantiateDirectives(
|
2014-11-12 11:40:36 -08:00
|
|
|
lightDomAppInjector, shadowDomAppInjectors[i], preBuiltObjs);
|
2014-11-21 15:13:01 -08:00
|
|
|
}
|
2014-10-29 15:41:50 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-27 11:47:13 -04:00
|
|
|
static _rootElementInjectors(injectors) {
|
|
|
|
return ListWrapper.filter(injectors, inj => isPresent(inj) && isBlank(inj.parent));
|
|
|
|
}
|
|
|
|
|
2014-10-30 14:41:19 -07:00
|
|
|
static _textNodes(elements, binders) {
|
2014-10-27 11:47:13 -04:00
|
|
|
var textNodes = [];
|
2014-10-30 14:41:19 -07:00
|
|
|
for (var i = 0; i < binders.length; ++i) {
|
2014-10-27 23:16:31 -07:00
|
|
|
ProtoView._collectTextNodes(textNodes, elements[i],
|
2014-10-30 14:41:19 -07:00
|
|
|
binders[i].textNodeIndices);
|
2014-10-27 11:47:13 -04:00
|
|
|
}
|
|
|
|
return textNodes;
|
|
|
|
}
|
|
|
|
|
2014-10-30 14:41:19 -07:00
|
|
|
static _bindElements(elements, binders):List<Element> {
|
2014-10-27 23:16:31 -07:00
|
|
|
var bindElements = [];
|
2014-10-30 14:41:19 -07:00
|
|
|
for (var i = 0; i < binders.length; ++i) {
|
|
|
|
if (binders[i].hasElementPropertyBindings) ListWrapper.push(
|
2014-10-27 23:16:31 -07:00
|
|
|
bindElements, elements[i]);
|
|
|
|
}
|
|
|
|
return bindElements;
|
|
|
|
}
|
|
|
|
|
|
|
|
static _collectTextNodes(allTextNodes, element, indices) {
|
2014-11-19 14:54:07 -08:00
|
|
|
if (isPresent(indices)) {
|
|
|
|
var childNodes = DOM.templateAwareRoot(element).childNodes;
|
|
|
|
for (var i = 0; i < indices.length; ++i) {
|
|
|
|
ListWrapper.push(allTextNodes, childNodes[indices[i]]);
|
|
|
|
}
|
2014-10-27 23:16:31 -07:00
|
|
|
}
|
2014-09-28 20:02:32 -07:00
|
|
|
}
|
2014-11-12 11:40:36 -08:00
|
|
|
|
2014-11-21 15:13:01 -08:00
|
|
|
static _instantiateChildComponentViews(view: View, elements, binders,
|
|
|
|
injectors, shadowDomAppInjectors: List<Injector>) {
|
2014-11-12 11:40:36 -08:00
|
|
|
for (var i = 0; i < binders.length; ++i) {
|
|
|
|
var binder = binders[i];
|
|
|
|
if (isPresent(binder.componentDirective)) {
|
|
|
|
var injector = injectors[i];
|
|
|
|
var childView = binder.nestedProtoView.instantiate(
|
|
|
|
injector.getComponent(), shadowDomAppInjectors[i], injector);
|
2014-11-21 15:13:01 -08:00
|
|
|
view.addComponentChildView(childView);
|
2014-11-12 11:40:36 -08:00
|
|
|
var shadowRoot = elements[i].createShadowRoot();
|
2014-11-21 15:13:01 -08:00
|
|
|
ViewPort.moveViewNodesIntoParent(shadowRoot, childView);
|
2014-11-12 11:40:36 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static _createShadowAppInjectors(binders: List<ElementBinders>, lightDomAppInjector: Injector): List<Injectors> {
|
|
|
|
var injectors = ListWrapper.createFixedSize(binders.length);
|
|
|
|
for (var i = 0; i < binders.length; ++i) {
|
|
|
|
var componentDirective = binders[i].componentDirective;
|
|
|
|
if (isPresent(componentDirective)) {
|
|
|
|
var services = componentDirective.annotation.componentServices;
|
|
|
|
injectors[i] = isPresent(services) ?
|
|
|
|
lightDomAppInjector.createChild(services) : lightDomAppInjector;
|
|
|
|
} else {
|
|
|
|
injectors[i] = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return injectors;
|
|
|
|
}
|
2014-11-07 14:30:04 -08:00
|
|
|
|
|
|
|
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
|
|
|
|
// and the component template is already compiled into protoView.
|
|
|
|
// Used for bootstrapping.
|
|
|
|
static createRootProtoView(protoView: ProtoView,
|
|
|
|
insertionElement, rootComponentAnnotatedType: AnnotatedType): ProtoView {
|
2014-11-19 15:52:01 -08:00
|
|
|
var rootProtoView = new ProtoView(insertionElement, new ProtoRecordRange());
|
2014-11-07 14:30:04 -08:00
|
|
|
var binder = rootProtoView.bindElement(
|
|
|
|
new ProtoElementInjector(null, 0, [rootComponentAnnotatedType.type], true));
|
|
|
|
binder.componentDirective = rootComponentAnnotatedType;
|
|
|
|
binder.nestedProtoView = protoView;
|
|
|
|
DOM.addClass(insertionElement, 'ng-binding');
|
|
|
|
return rootProtoView;
|
|
|
|
}
|
2014-09-28 20:02:32 -07:00
|
|
|
}
|
|
|
|
|
2014-10-10 20:44:55 -07:00
|
|
|
export class ElementPropertyMemento {
|
2014-11-21 21:19:23 -08:00
|
|
|
_elementIndex:int;
|
|
|
|
_propertyName:string;
|
2014-10-10 20:44:55 -07:00
|
|
|
constructor(elementIndex:int, propertyName:string) {
|
|
|
|
this._elementIndex = elementIndex;
|
|
|
|
this._propertyName = propertyName;
|
|
|
|
}
|
|
|
|
|
2014-10-27 23:16:31 -07:00
|
|
|
invoke(record:Record, bindElements:List<Element>) {
|
|
|
|
var element:Element = bindElements[this._elementIndex];
|
2014-10-10 20:44:55 -07:00
|
|
|
DOM.setProperty(element, this._propertyName, record.currentValue);
|
|
|
|
}
|
|
|
|
}
|
2014-09-28 16:29:11 -07:00
|
|
|
|
2014-10-10 20:44:55 -07:00
|
|
|
export class DirectivePropertyMemento {
|
2014-11-21 21:19:23 -08:00
|
|
|
_elementInjectorIndex:int;
|
|
|
|
_directiveIndex:int;
|
|
|
|
_setterName:string;
|
|
|
|
_setter:SetterFn;
|
2014-09-28 16:29:11 -07:00
|
|
|
constructor(
|
|
|
|
elementInjectorIndex:number,
|
|
|
|
directiveIndex:number,
|
2014-10-27 23:16:31 -07:00
|
|
|
setterName:string,
|
|
|
|
setter:SetterFn) {
|
2014-09-28 16:29:11 -07:00
|
|
|
this._elementInjectorIndex = elementInjectorIndex;
|
|
|
|
this._directiveIndex = directiveIndex;
|
|
|
|
this._setterName = setterName;
|
|
|
|
this._setter = setter;
|
|
|
|
}
|
|
|
|
|
|
|
|
invoke(record:Record, elementInjectors:List<ElementInjector>) {
|
|
|
|
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
|
2014-10-27 23:16:31 -07:00
|
|
|
var directive = elementInjector.getAtIndex(this._directiveIndex);
|
2014-09-28 16:29:11 -07:00
|
|
|
this._setter(directive, record.currentValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-02 17:09:46 -08:00
|
|
|
var _groups = MapWrapper.create();
|
|
|
|
|
|
|
|
class DirectivePropertyGroupMemento {
|
|
|
|
_elementInjectorIndex:number;
|
|
|
|
_directiveIndex:number;
|
|
|
|
|
|
|
|
constructor(elementInjectorIndex:number, directiveIndex:number) {
|
|
|
|
this._elementInjectorIndex = elementInjectorIndex;
|
|
|
|
this._directiveIndex = directiveIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
static get(memento:DirectivePropertyMemento) {
|
|
|
|
var elementInjectorIndex = memento._elementInjectorIndex;
|
|
|
|
var directiveIndex = memento._directiveIndex;
|
|
|
|
var id = elementInjectorIndex * 100 + directiveIndex;
|
|
|
|
|
|
|
|
if (! MapWrapper.contains(_groups, id)) {
|
|
|
|
return MapWrapper.set(_groups, id, new DirectivePropertyGroupMemento(elementInjectorIndex, directiveIndex));
|
|
|
|
}
|
|
|
|
return MapWrapper.get(_groups, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
directive(elementInjectors:List<ElementInjector>) {
|
|
|
|
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
|
|
|
|
return elementInjector.getAtIndex(this._directiveIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class PropertyUpdate {
|
|
|
|
currentValue;
|
|
|
|
previousValue;
|
|
|
|
|
|
|
|
constructor(currentValue, previousValue) {
|
|
|
|
this.currentValue = currentValue;
|
|
|
|
this.previousValue = previousValue;
|
|
|
|
}
|
|
|
|
}
|
2014-09-28 16:29:11 -07:00
|
|
|
|
|
|
|
|
|
|
|
//TODO(tbosch): I don't like to have done be called from a different place than notify
|
|
|
|
// notify is called by change detection, but done is called by our wrapper on detect changes.
|
|
|
|
export class OnChangeDispatcher {
|
|
|
|
|
2014-11-21 21:19:23 -08:00
|
|
|
_lastView:View;
|
|
|
|
_lastTarget:DirectivePropertyMemento;
|
2014-09-28 16:29:11 -07:00
|
|
|
constructor() {
|
|
|
|
this._lastView = null;
|
|
|
|
this._lastTarget = null;
|
|
|
|
}
|
|
|
|
|
2014-10-10 20:44:55 -07:00
|
|
|
notify(view:View, eTarget:DirectivePropertyMemento) {
|
2014-09-28 16:29:11 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
done() {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|