125 lines
3.9 KiB
JavaScript

import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
import {DOM} from 'angular2/src/dom/dom_adapter';
import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'angular2/src/facade/lang';
import {ProtoViewBuilder, ElementBinderBuilder} from '../view/proto_view_builder';
/**
* Collects all data that is needed to process an element
* in the compile process. Fields are filled
* by the CompileSteps starting out with the pure HTMLElement.
*/
export class CompileElement {
element;
_attrs:Map;
_classList:List;
isViewRoot:boolean;
inheritedProtoView:ProtoViewBuilder;
distanceToInheritedBinder:number;
inheritedElementBinder:ElementBinderBuilder;
compileChildren: boolean;
ignoreBindings: boolean;
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
constructor(element, compilationUnit = '') {
this.element = element;
this._attrs = null;
this._classList = null;
this.isViewRoot = false;
// inherited down to children if they don't have
// an own protoView
this.inheritedProtoView = null;
// inherited down to children if they don't have
// an own elementBinder
this.inheritedElementBinder = null;
this.distanceToInheritedBinder = 0;
this.compileChildren = true;
// set to true to ignore all the bindings on the element
this.ignoreBindings = false;
// description is calculated here as compilation steps may change the element
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
if (compilationUnit !== '') {
this.elementDescription = compilationUnit;
if (isPresent(tplDesc)) this.elementDescription += ": " + tplDesc;
} else {
this.elementDescription = tplDesc;
}
}
isBound() {
return isPresent(this.inheritedElementBinder) && this.distanceToInheritedBinder === 0;
}
bindElement() {
if (!this.isBound()) {
var parentBinder = this.inheritedElementBinder;
this.inheritedElementBinder = this.inheritedProtoView.bindElement(this.element, this.elementDescription);
if (isPresent(parentBinder)) {
this.inheritedElementBinder.setParent(parentBinder, this.distanceToInheritedBinder);
}
this.distanceToInheritedBinder = 0;
}
return this.inheritedElementBinder;
}
refreshAttrs() {
this._attrs = null;
}
attrs():Map<string,string> {
if (isBlank(this._attrs)) {
this._attrs = DOM.attributeMap(this.element);
}
return this._attrs;
}
refreshClassList() {
this._classList = null;
}
classList():List<string> {
if (isBlank(this._classList)) {
this._classList = ListWrapper.create();
var elClassList = DOM.classList(this.element);
for (var i = 0; i < elClassList.length; i++) {
ListWrapper.push(this._classList, elClassList[i]);
}
}
return this._classList;
}
}
// return an HTML representation of an element start tag - without its content
// this is used to give contextual information in case of errors
function getElementDescription(domElement):string {
var buf = new StringJoiner();
var atts = DOM.attributeMap(domElement);
buf.add("<");
buf.add(DOM.tagName(domElement).toLowerCase());
// show id and class first to ease element identification
addDescriptionAttribute(buf, "id", MapWrapper.get(atts, "id"));
addDescriptionAttribute(buf, "class", MapWrapper.get(atts, "class"));
MapWrapper.forEach(atts, (attValue, attName) => {
if (attName !== "id" && attName !== "class") {
addDescriptionAttribute(buf, attName, attValue);
}
});
buf.add(">");
return buf.toString();
}
function addDescriptionAttribute(buffer:StringJoiner, attName:string, attValue) {
if (isPresent(attValue)) {
if (attValue.length === 0) {
buffer.add(' ' + attName);
} else {
buffer.add(' ' + attName + '="' + attValue + '"');
}
}
}