chore(packaging): move files to match target file structure
This commit is contained in:
59
modules/angular2/src/core/compiler/pipeline/compile_control.js
vendored
Normal file
59
modules/angular2/src/core/compiler/pipeline/compile_control.js
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
import {isBlank} from 'facade/src/lang';
|
||||
import {List, ListWrapper} from 'facade/src/collection';
|
||||
import {DOM, Element} from 'facade/src/dom';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileStep} from './compile_step';
|
||||
|
||||
/**
|
||||
* Controls the processing order of elements.
|
||||
* Right now it only allows to add a parent element.
|
||||
*/
|
||||
export class CompileControl {
|
||||
_steps:List<CompileStep>;
|
||||
_currentStepIndex:number;
|
||||
_parent:CompileElement;
|
||||
_results;
|
||||
_additionalChildren;
|
||||
constructor(steps) {
|
||||
this._steps = steps;
|
||||
this._currentStepIndex = 0;
|
||||
this._parent = null;
|
||||
this._results = null;
|
||||
this._additionalChildren = null;
|
||||
}
|
||||
|
||||
// only public so that it can be used by compile_pipeline
|
||||
internalProcess(results, startStepIndex, parent:CompileElement, current:CompileElement) {
|
||||
this._results = results;
|
||||
var previousStepIndex = this._currentStepIndex;
|
||||
var previousParent = this._parent;
|
||||
|
||||
for (var i=startStepIndex; i<this._steps.length; i++) {
|
||||
var step = this._steps[i];
|
||||
this._parent = parent;
|
||||
this._currentStepIndex = i;
|
||||
step.process(parent, current, this);
|
||||
parent = this._parent;
|
||||
}
|
||||
ListWrapper.push(results, current);
|
||||
|
||||
this._currentStepIndex = previousStepIndex;
|
||||
this._parent = previousParent;
|
||||
|
||||
var localAdditionalChildren = this._additionalChildren;
|
||||
this._additionalChildren = null;
|
||||
return localAdditionalChildren;
|
||||
}
|
||||
|
||||
addParent(newElement:CompileElement) {
|
||||
this.internalProcess(this._results, this._currentStepIndex+1, this._parent, newElement);
|
||||
this._parent = newElement;
|
||||
}
|
||||
|
||||
addChild(element:CompileElement) {
|
||||
if (isBlank(this._additionalChildren)) {
|
||||
this._additionalChildren = ListWrapper.create();
|
||||
}
|
||||
ListWrapper.push(this._additionalChildren, element);
|
||||
}
|
||||
}
|
159
modules/angular2/src/core/compiler/pipeline/compile_element.js
vendored
Normal file
159
modules/angular2/src/core/compiler/pipeline/compile_element.js
vendored
Normal file
@ -0,0 +1,159 @@
|
||||
import {List, Map, ListWrapper, MapWrapper} from 'facade/src/collection';
|
||||
import {Element, DOM} from 'facade/src/dom';
|
||||
import {int, isBlank, isPresent, Type} from 'facade/src/lang';
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
import {Decorator} from '../../annotations/annotations';
|
||||
import {Component} from '../../annotations/annotations';
|
||||
import {Template} from '../../annotations/annotations';
|
||||
import {ElementBinder} from '../element_binder';
|
||||
import {ProtoElementInjector} from '../element_injector';
|
||||
import {ProtoView} from '../view';
|
||||
|
||||
import {AST} from 'change_detection/change_detection';
|
||||
|
||||
/**
|
||||
* 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:Element;
|
||||
_attrs:Map;
|
||||
_classList:List;
|
||||
textNodeBindings:Map;
|
||||
propertyBindings:Map;
|
||||
eventBindings:Map;
|
||||
|
||||
/// Store directive name to template name mapping.
|
||||
/// Directive name is what the directive exports the variable as
|
||||
/// Template name is how it is reffered to it in template
|
||||
variableBindings:Map;
|
||||
decoratorDirectives:List<DirectiveMetadata>;
|
||||
templateDirective:DirectiveMetadata;
|
||||
componentDirective:DirectiveMetadata;
|
||||
_allDirectives:List<DirectiveMetadata>;
|
||||
isViewRoot:boolean;
|
||||
hasBindings:boolean;
|
||||
inheritedProtoView:ProtoView;
|
||||
inheritedProtoElementInjector:ProtoElementInjector;
|
||||
inheritedElementBinder:ElementBinder;
|
||||
distanceToParentInjector:number;
|
||||
compileChildren: boolean;
|
||||
constructor(element:Element) {
|
||||
this.element = element;
|
||||
this._attrs = null;
|
||||
this._classList = null;
|
||||
this.textNodeBindings = null;
|
||||
this.propertyBindings = null;
|
||||
this.eventBindings = null;
|
||||
this.variableBindings = null;
|
||||
this.decoratorDirectives = null;
|
||||
this.templateDirective = null;
|
||||
this.componentDirective = null;
|
||||
this._allDirectives = null;
|
||||
this.isViewRoot = false;
|
||||
this.hasBindings = 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 protoElementInjector
|
||||
this.inheritedProtoElementInjector = null;
|
||||
// inherited down to children if they don't have
|
||||
// an own elementBinder
|
||||
this.inheritedElementBinder = null;
|
||||
this.distanceToParentInjector = 0;
|
||||
this.compileChildren = true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
addTextNodeBinding(indexInParent:int, expression:AST) {
|
||||
if (isBlank(this.textNodeBindings)) {
|
||||
this.textNodeBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.textNodeBindings, indexInParent, expression);
|
||||
}
|
||||
|
||||
addPropertyBinding(property:string, expression:AST) {
|
||||
if (isBlank(this.propertyBindings)) {
|
||||
this.propertyBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.propertyBindings, property, expression);
|
||||
}
|
||||
|
||||
addVariableBinding(directiveName:string, templateName:string) {
|
||||
if (isBlank(this.variableBindings)) {
|
||||
this.variableBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.variableBindings, templateName, directiveName);
|
||||
}
|
||||
|
||||
addEventBinding(eventName:string, expression:AST) {
|
||||
if (isBlank(this.eventBindings)) {
|
||||
this.eventBindings = MapWrapper.create();
|
||||
}
|
||||
MapWrapper.set(this.eventBindings, eventName, expression);
|
||||
}
|
||||
|
||||
addDirective(directive:DirectiveMetadata) {
|
||||
var annotation = directive.annotation;
|
||||
this._allDirectives = null;
|
||||
if (annotation instanceof Decorator) {
|
||||
if (isBlank(this.decoratorDirectives)) {
|
||||
this.decoratorDirectives = ListWrapper.create();
|
||||
}
|
||||
ListWrapper.push(this.decoratorDirectives, directive);
|
||||
if (!annotation.compileChildren) {
|
||||
this.compileChildren = false;
|
||||
}
|
||||
} else if (annotation instanceof Template) {
|
||||
this.templateDirective = directive;
|
||||
} else if (annotation instanceof Component) {
|
||||
this.componentDirective = directive;
|
||||
}
|
||||
}
|
||||
|
||||
getAllDirectives(): List<DirectiveMetadata> {
|
||||
if (this._allDirectives === null) {
|
||||
// Collect all the directives
|
||||
// When present the component directive must be first
|
||||
var directives = ListWrapper.create();
|
||||
if (isPresent(this.componentDirective)) {
|
||||
ListWrapper.push(directives, this.componentDirective);
|
||||
}
|
||||
if (isPresent(this.templateDirective)) {
|
||||
ListWrapper.push(directives, this.templateDirective);
|
||||
}
|
||||
if (isPresent(this.decoratorDirectives)) {
|
||||
directives = ListWrapper.concat(directives, this.decoratorDirectives);
|
||||
}
|
||||
this._allDirectives = directives;
|
||||
}
|
||||
return this._allDirectives;
|
||||
}
|
||||
}
|
47
modules/angular2/src/core/compiler/pipeline/compile_pipeline.js
vendored
Normal file
47
modules/angular2/src/core/compiler/pipeline/compile_pipeline.js
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
import {isPresent} from 'facade/src/lang';
|
||||
import {List, ListWrapper} from 'facade/src/collection';
|
||||
import {Element, Node, DOM} from 'facade/src/dom';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {CompileStep} from './compile_step';
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
|
||||
/**
|
||||
* CompilePipeline for executing CompileSteps recursively for
|
||||
* all elements in a template.
|
||||
*/
|
||||
export class CompilePipeline {
|
||||
_control:CompileControl;
|
||||
constructor(steps:List<CompileStep>) {
|
||||
this._control = new CompileControl(steps);
|
||||
}
|
||||
|
||||
process(rootElement:Element):List {
|
||||
var results = ListWrapper.create();
|
||||
this._process(results, null, new CompileElement(rootElement));
|
||||
return results;
|
||||
}
|
||||
|
||||
_process(results, parent:CompileElement, current:CompileElement) {
|
||||
var additionalChildren = this._control.internalProcess(results, 0, parent, current);
|
||||
|
||||
if (current.compileChildren) {
|
||||
var node = DOM.templateAwareRoot(current.element).firstChild;
|
||||
while (isPresent(node)) {
|
||||
// compiliation can potentially move the node, so we need to store the
|
||||
// next sibling before recursing.
|
||||
var nextNode = DOM.nextSibling(node);
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
this._process(results, current, new CompileElement(node));
|
||||
}
|
||||
node = nextNode;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(additionalChildren)) {
|
||||
for (var i=0; i<additionalChildren.length; i++) {
|
||||
this._process(results, current, additionalChildren[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
modules/angular2/src/core/compiler/pipeline/compile_step.js
vendored
Normal file
11
modules/angular2/src/core/compiler/pipeline/compile_step.js
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
|
||||
/**
|
||||
* One part of the compile process.
|
||||
* Is guaranteed to be called in depth first order
|
||||
*/
|
||||
export class CompileStep {
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {}
|
||||
}
|
38
modules/angular2/src/core/compiler/pipeline/default_steps.js
vendored
Normal file
38
modules/angular2/src/core/compiler/pipeline/default_steps.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
import {ChangeDetection, Parser} from 'change_detection/change_detection';
|
||||
import {List} from 'facade/src/collection';
|
||||
|
||||
import {PropertyBindingParser} from './property_binding_parser';
|
||||
import {TextInterpolationParser} from './text_interpolation_parser';
|
||||
import {DirectiveParser} from './directive_parser';
|
||||
import {ViewSplitter} from './view_splitter';
|
||||
import {ElementBindingMarker} from './element_binding_marker';
|
||||
import {ProtoViewBuilder} from './proto_view_builder';
|
||||
import {ProtoElementInjectorBuilder} from './proto_element_injector_builder';
|
||||
import {ElementBinderBuilder} from './element_binder_builder';
|
||||
import {DirectiveMetadata} from 'core/src/compiler/directive_metadata';
|
||||
import {stringify} from 'facade/src/lang';
|
||||
|
||||
/**
|
||||
* Default steps used for compiling a template.
|
||||
* Takes in an HTMLElement and produces the ProtoViews,
|
||||
* ProtoElementInjectors and ElementBinders in the end.
|
||||
*/
|
||||
export function createDefaultSteps(
|
||||
changeDetection:ChangeDetection,
|
||||
parser:Parser,
|
||||
compiledComponent: DirectiveMetadata,
|
||||
directives: List<DirectiveMetadata>) {
|
||||
|
||||
var compilationUnit = stringify(compiledComponent.type);
|
||||
|
||||
return [
|
||||
new ViewSplitter(parser, compilationUnit),
|
||||
new PropertyBindingParser(parser, compilationUnit),
|
||||
new DirectiveParser(directives),
|
||||
new TextInterpolationParser(parser, compilationUnit),
|
||||
new ElementBindingMarker(),
|
||||
new ProtoViewBuilder(changeDetection),
|
||||
new ProtoElementInjectorBuilder(),
|
||||
new ElementBinderBuilder()
|
||||
];
|
||||
}
|
82
modules/angular2/src/core/compiler/pipeline/directive_parser.js
vendored
Normal file
82
modules/angular2/src/core/compiler/pipeline/directive_parser.js
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
import {isPresent, BaseException} from 'facade/src/lang';
|
||||
import {List, MapWrapper} from 'facade/src/collection';
|
||||
import {TemplateElement} from 'facade/src/dom';
|
||||
import {SelectorMatcher} from '../selector';
|
||||
import {CssSelector} from '../selector';
|
||||
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
import {Template} from '../../annotations/annotations';
|
||||
import {Component} from '../../annotations/annotations';
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
/**
|
||||
* Parses the directives on a single element. Assumes ViewSplitter has already created
|
||||
* <template> elements for template directives.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#templateDirecitve
|
||||
* - CompileElement#componentDirective.
|
||||
*
|
||||
* Reads:
|
||||
* - CompileElement#propertyBindings (to find directives contained
|
||||
* in the property bindings)
|
||||
* - CompileElement#variableBindings (to find directives contained
|
||||
* in the variable bindings)
|
||||
*/
|
||||
export class DirectiveParser extends CompileStep {
|
||||
_selectorMatcher:SelectorMatcher;
|
||||
constructor(directives:List<DirectiveMetadata>) {
|
||||
this._selectorMatcher = new SelectorMatcher();
|
||||
for (var i=0; i<directives.length; i++) {
|
||||
var directiveMetadata = directives[i];
|
||||
this._selectorMatcher.addSelectable(
|
||||
CssSelector.parse(directiveMetadata.annotation.selector),
|
||||
directiveMetadata
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var classList = current.classList();
|
||||
|
||||
var cssSelector = new CssSelector();
|
||||
cssSelector.setElement(current.element.nodeName);
|
||||
for (var i=0; i < classList.length; i++) {
|
||||
cssSelector.addClassName(classList[i]);
|
||||
}
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
cssSelector.addAttribute(attrName, attrValue);
|
||||
});
|
||||
if (isPresent(current.propertyBindings)) {
|
||||
MapWrapper.forEach(current.propertyBindings, (expression, prop) => {
|
||||
cssSelector.addAttribute(prop, expression.source);
|
||||
});
|
||||
}
|
||||
if (isPresent(current.variableBindings)) {
|
||||
MapWrapper.forEach(current.variableBindings, (value, name) => {
|
||||
cssSelector.addAttribute(name, value);
|
||||
});
|
||||
}
|
||||
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
|
||||
// only be present on <template> elements any more!
|
||||
var isTemplateElement = current.element instanceof TemplateElement;
|
||||
this._selectorMatcher.match(cssSelector, (directive) => {
|
||||
if (directive.annotation instanceof Template) {
|
||||
if (!isTemplateElement) {
|
||||
throw new BaseException('Template directives need to be placed on <template> elements or elements with template attribute!');
|
||||
} else if (isPresent(current.templateDirective)) {
|
||||
throw new BaseException('Only one template directive per element is allowed!');
|
||||
}
|
||||
} else if (isTemplateElement) {
|
||||
throw new BaseException('Only template directives are allowed on <template> elements!');
|
||||
} else if ((directive.annotation instanceof Component) && isPresent(current.componentDirective)) {
|
||||
throw new BaseException('Only one component directive per element is allowed!');
|
||||
}
|
||||
current.addDirective(directive);
|
||||
});
|
||||
}
|
||||
}
|
145
modules/angular2/src/core/compiler/pipeline/element_binder_builder.js
vendored
Normal file
145
modules/angular2/src/core/compiler/pipeline/element_binder_builder.js
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
import {int, isPresent, isBlank, Type, BaseException, StringWrapper, stringify} from 'facade/src/lang';
|
||||
import {Element, DOM} from 'facade/src/dom';
|
||||
import {ListWrapper, List, MapWrapper, StringMapWrapper} from 'facade/src/collection';
|
||||
|
||||
import {reflector} from 'reflection/src/reflection';
|
||||
|
||||
import {Parser, ProtoChangeDetector} from 'change_detection/change_detection';
|
||||
|
||||
import {Component, Directive} from '../../annotations/annotations';
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from '../view';
|
||||
import {ProtoElementInjector} from '../element_injector';
|
||||
import {ElementBinder} from '../element_binder';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
const CLASS_PREFIX = 'class.';
|
||||
var classSettersCache = StringMapWrapper.create();
|
||||
|
||||
function classSetterFactory(className:string) {
|
||||
var setterFn = StringMapWrapper.get(classSettersCache, className);
|
||||
|
||||
if (isBlank(setterFn)) {
|
||||
setterFn = function(element:Element, value) {
|
||||
if (value) {
|
||||
DOM.addClass(element, className);
|
||||
} else {
|
||||
DOM.removeClass(element, className);
|
||||
}
|
||||
};
|
||||
StringMapWrapper.set(classSettersCache, className, setterFn);
|
||||
}
|
||||
|
||||
return setterFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the ElementBinders and adds watches to the
|
||||
* ProtoChangeDetector.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#inheritedElementBinder
|
||||
*
|
||||
* Reads:
|
||||
* - (in parent) CompileElement#inheritedElementBinder
|
||||
* - CompileElement#hasBindings
|
||||
* - CompileElement#inheritedProtoView
|
||||
* - CompileElement#inheritedProtoElementInjector
|
||||
* - CompileElement#textNodeBindings
|
||||
* - CompileElement#propertyBindings
|
||||
* - CompileElement#eventBindings
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#componentDirective
|
||||
* - CompileElement#templateDirective
|
||||
*
|
||||
* Note: This actually only needs the CompileElements with the flags
|
||||
* `hasBindings` and `isViewRoot`,
|
||||
* and only needs the actual HTMLElement for the ones
|
||||
* with the flag `isViewRoot`.
|
||||
*/
|
||||
export class ElementBinderBuilder extends CompileStep {
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var elementBinder = null;
|
||||
if (current.hasBindings) {
|
||||
var protoView = current.inheritedProtoView;
|
||||
elementBinder = protoView.bindElement(current.inheritedProtoElementInjector,
|
||||
current.componentDirective, current.templateDirective);
|
||||
|
||||
if (isPresent(current.textNodeBindings)) {
|
||||
this._bindTextNodes(protoView, current);
|
||||
}
|
||||
if (isPresent(current.propertyBindings)) {
|
||||
this._bindElementProperties(protoView, current);
|
||||
}
|
||||
if (isPresent(current.eventBindings)) {
|
||||
this._bindEvents(protoView, current);
|
||||
}
|
||||
this._bindDirectiveProperties(current.getAllDirectives(), current);
|
||||
} else if (isPresent(parent)) {
|
||||
elementBinder = parent.inheritedElementBinder;
|
||||
}
|
||||
current.inheritedElementBinder = elementBinder;
|
||||
}
|
||||
|
||||
_bindTextNodes(protoView, compileElement) {
|
||||
MapWrapper.forEach(compileElement.textNodeBindings, (expression, indexInParent) => {
|
||||
protoView.bindTextNode(indexInParent, expression);
|
||||
});
|
||||
}
|
||||
|
||||
_bindElementProperties(protoView, compileElement) {
|
||||
MapWrapper.forEach(compileElement.propertyBindings, (expression, property) => {
|
||||
var setterFn;
|
||||
|
||||
if (StringWrapper.startsWith(property, CLASS_PREFIX)) {
|
||||
setterFn = classSetterFactory(StringWrapper.substring(property, CLASS_PREFIX.length));
|
||||
} else if (DOM.hasProperty(compileElement.element, property)) {
|
||||
setterFn = reflector.setter(property);
|
||||
}
|
||||
|
||||
if (isPresent(setterFn)) {
|
||||
protoView.bindElementProperty(expression.ast, property, setterFn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_bindEvents(protoView, compileElement) {
|
||||
MapWrapper.forEach(compileElement.eventBindings, (expression, eventName) => {
|
||||
protoView.bindEvent(eventName, expression);
|
||||
});
|
||||
}
|
||||
|
||||
_bindDirectiveProperties(directives: List<DirectiveMetadata>,
|
||||
compileElement: CompileElement) {
|
||||
var protoView = compileElement.inheritedProtoView;
|
||||
|
||||
for (var directiveIndex = 0; directiveIndex < directives.length; directiveIndex++) {
|
||||
var directive = ListWrapper.get(directives, directiveIndex);
|
||||
var annotation = directive.annotation;
|
||||
if (isBlank(annotation.bind)) continue;
|
||||
StringMapWrapper.forEach(annotation.bind, function (dirProp, elProp) {
|
||||
var expression = isPresent(compileElement.propertyBindings) ?
|
||||
MapWrapper.get(compileElement.propertyBindings, elProp) :
|
||||
null;
|
||||
if (isBlank(expression)) {
|
||||
throw new BaseException("No element binding found for property '" + elProp
|
||||
+ "' which is required by directive '" + stringify(directive.type) + "'");
|
||||
}
|
||||
var len = dirProp.length;
|
||||
var dirBindingName = dirProp;
|
||||
var isContentWatch = dirProp[len - 2] === '[' && dirProp[len - 1] === ']';
|
||||
if (isContentWatch) dirBindingName = dirProp.substring(0, len - 2);
|
||||
protoView.bindDirectiveProperty(
|
||||
directiveIndex,
|
||||
expression,
|
||||
dirBindingName,
|
||||
reflector.setter(dirBindingName),
|
||||
isContentWatch
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
44
modules/angular2/src/core/compiler/pipeline/element_binding_marker.js
vendored
Normal file
44
modules/angular2/src/core/compiler/pipeline/element_binding_marker.js
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
import {isPresent} from 'facade/src/lang';
|
||||
import {MapWrapper} from 'facade/src/collection';
|
||||
import {DOM} from 'facade/src/dom';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
const NG_BINDING_CLASS = 'ng-binding';
|
||||
|
||||
/**
|
||||
* Marks elements that have bindings with a css class
|
||||
* and sets the CompileElement.hasBindings flag.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#hasBindings
|
||||
*
|
||||
* Reads:
|
||||
* - CompileElement#textNodeBindings
|
||||
* - CompileElement#propertyBindings
|
||||
* - CompileElement#variableBindings
|
||||
* - CompileElement#eventBindings
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#componentDirective
|
||||
* - CompileElement#templateDirective
|
||||
*/
|
||||
export class ElementBindingMarker extends CompileStep {
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var hasBindings =
|
||||
(isPresent(current.textNodeBindings) && MapWrapper.size(current.textNodeBindings)>0) ||
|
||||
(isPresent(current.propertyBindings) && MapWrapper.size(current.propertyBindings)>0) ||
|
||||
(isPresent(current.variableBindings) && MapWrapper.size(current.variableBindings)>0) ||
|
||||
(isPresent(current.eventBindings) && MapWrapper.size(current.eventBindings)>0) ||
|
||||
(isPresent(current.decoratorDirectives) && current.decoratorDirectives.length > 0) ||
|
||||
isPresent(current.templateDirective) ||
|
||||
isPresent(current.componentDirective);
|
||||
|
||||
if (hasBindings) {
|
||||
var element = current.element;
|
||||
DOM.addClass(element, NG_BINDING_CLASS);
|
||||
current.hasBindings = true;
|
||||
}
|
||||
}
|
||||
}
|
76
modules/angular2/src/core/compiler/pipeline/property_binding_parser.js
vendored
Normal file
76
modules/angular2/src/core/compiler/pipeline/property_binding_parser.js
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
import {isPresent, isBlank, RegExpWrapper, BaseException} from 'facade/src/lang';
|
||||
import {MapWrapper} from 'facade/src/collection';
|
||||
import {TemplateElement} from 'facade/src/dom';
|
||||
|
||||
import {Parser, AST, ExpressionWithSource} from 'change_detection/change_detection';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
// TODO(tbosch): Cannot make this const/final right now because of the transpiler...
|
||||
var BIND_NAME_REGEXP = RegExpWrapper.create('^(?:(?:(bind)|(var)|(on))-(.+))|\\[([^\\]]+)\\]|\\(([^\\)]+)\\)');
|
||||
|
||||
/**
|
||||
* Parses the property bindings on a single element.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#propertyBindings
|
||||
* - CompileElement#eventBindings
|
||||
* - CompileElement#variableBindings
|
||||
*/
|
||||
export class PropertyBindingParser extends CompileStep {
|
||||
_parser:Parser;
|
||||
_compilationUnit:any;
|
||||
constructor(parser:Parser, compilationUnit:any) {
|
||||
this._parser = parser;
|
||||
this._compilationUnit = compilationUnit;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
|
||||
if (isPresent(bindParts)) {
|
||||
if (isPresent(bindParts[1])) {
|
||||
// match: bind-prop
|
||||
current.addPropertyBinding(bindParts[4], this._parseBinding(attrValue));
|
||||
} else if (isPresent(bindParts[2])) {
|
||||
// match: let-prop
|
||||
// Note: We assume that the ViewSplitter already did its work, i.e. template directive should
|
||||
// only be present on <template> elements any more!
|
||||
if (!(current.element instanceof TemplateElement)) {
|
||||
throw new BaseException('var-* is only allowed on <template> elements!');
|
||||
}
|
||||
current.addVariableBinding(bindParts[4], attrValue);
|
||||
} else if (isPresent(bindParts[3])) {
|
||||
// match: on-prop
|
||||
current.addEventBinding(bindParts[4], this._parseAction(attrValue));
|
||||
} else if (isPresent(bindParts[5])) {
|
||||
// match: [prop]
|
||||
current.addPropertyBinding(bindParts[5], this._parseBinding(attrValue));
|
||||
} else if (isPresent(bindParts[6])) {
|
||||
// match: (prop)
|
||||
current.addEventBinding(bindParts[6], this._parseBinding(attrValue));
|
||||
}
|
||||
} else {
|
||||
var ast = this._parseInterpolation(attrValue);
|
||||
if (isPresent(ast)) {
|
||||
current.addPropertyBinding(attrName, ast);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_parseInterpolation(input:string):AST {
|
||||
return this._parser.parseInterpolation(input, this._compilationUnit);
|
||||
}
|
||||
|
||||
_parseBinding(input:string):AST {
|
||||
return this._parser.parseBinding(input, this._compilationUnit);
|
||||
}
|
||||
|
||||
_parseAction(input:string):AST {
|
||||
return this._parser.parseAction(input, this._compilationUnit);
|
||||
}
|
||||
}
|
72
modules/angular2/src/core/compiler/pipeline/proto_element_injector_builder.js
vendored
Normal file
72
modules/angular2/src/core/compiler/pipeline/proto_element_injector_builder.js
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
import {isPresent, isBlank} from 'facade/src/lang';
|
||||
import {ListWrapper} from 'facade/src/collection';
|
||||
|
||||
import {Key} from 'di/di';
|
||||
import {ProtoElementInjector, ComponentKeyMetaData, DirectiveBinding} from '../element_injector';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {DirectiveMetadata} from '../directive_metadata';
|
||||
|
||||
/**
|
||||
* Creates the ProtoElementInjectors.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#inheritedProtoElementInjector
|
||||
* - CompileElement#distanceToParentInjector
|
||||
*
|
||||
* Reads:
|
||||
* - (in parent) CompileElement#inheritedProtoElementInjector
|
||||
* - (in parent) CompileElement#distanceToParentInjector
|
||||
* - CompileElement#isViewRoot
|
||||
* - CompileElement#inheritedProtoView
|
||||
* - CompileElement#decoratorDirectives
|
||||
* - CompileElement#componentDirective
|
||||
* - CompileElement#templateDirective
|
||||
*/
|
||||
export class ProtoElementInjectorBuilder extends CompileStep {
|
||||
// public so that we can overwrite it in tests
|
||||
internalCreateProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance) {
|
||||
return new ProtoElementInjector(parent, index, directives, firstBindingIsComponent, distance);
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var distanceToParentInjector = this._getDistanceToParentInjector(parent, current);
|
||||
var parentProtoElementInjector = this._getParentProtoElementInjector(parent, current);
|
||||
var injectorBindings = ListWrapper.map(current.getAllDirectives(), this._createBinding);
|
||||
// TODO: add lightDomServices as well,
|
||||
// but after the directives as we rely on that order
|
||||
// in the element_binder_builder.
|
||||
|
||||
if (injectorBindings.length > 0) {
|
||||
var protoView = current.inheritedProtoView;
|
||||
var hasComponent = isPresent(current.componentDirective);
|
||||
|
||||
current.inheritedProtoElementInjector = this.internalCreateProtoElementInjector(
|
||||
parentProtoElementInjector, protoView.elementBinders.length, injectorBindings,
|
||||
hasComponent, distanceToParentInjector
|
||||
);
|
||||
current.distanceToParentInjector = 0;
|
||||
|
||||
} else {
|
||||
current.inheritedProtoElementInjector = parentProtoElementInjector;
|
||||
current.distanceToParentInjector = distanceToParentInjector;
|
||||
}
|
||||
}
|
||||
|
||||
_getDistanceToParentInjector(parent, current) {
|
||||
return isPresent(parent) ? parent.distanceToParentInjector + 1 : 0;
|
||||
}
|
||||
|
||||
_getParentProtoElementInjector(parent, current) {
|
||||
if (isPresent(parent) && !current.isViewRoot) {
|
||||
return parent.inheritedProtoElementInjector;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_createBinding(d:DirectiveMetadata): DirectiveBinding {
|
||||
return DirectiveBinding.createFromType(d.type, d.annotation);
|
||||
}
|
||||
}
|
50
modules/angular2/src/core/compiler/pipeline/proto_view_builder.js
vendored
Normal file
50
modules/angular2/src/core/compiler/pipeline/proto_view_builder.js
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
import {isPresent, BaseException} from 'facade/src/lang';
|
||||
import {ListWrapper, MapWrapper} from 'facade/src/collection';
|
||||
|
||||
import {ProtoView} from '../view';
|
||||
import {ChangeDetection} from 'change_detection/change_detection';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
/**
|
||||
* Creates ProtoViews and forwards variable bindings from parent to children.
|
||||
*
|
||||
* Fills:
|
||||
* - (in parent): CompileElement#inheritedElementBinder.nestedProtoView
|
||||
* - CompileElement#inheritedProtoView
|
||||
*
|
||||
* Reads:
|
||||
* - (in parent): CompileElement#inheritedProtoView
|
||||
* - (in parent): CompileElement#variableBindings
|
||||
* - CompileElement#isViewRoot
|
||||
*/
|
||||
export class ProtoViewBuilder extends CompileStep {
|
||||
changeDetection:ChangeDetection;
|
||||
constructor(changeDetection:ChangeDetection) {
|
||||
this.changeDetection = changeDetection;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
var inheritedProtoView = null;
|
||||
if (current.isViewRoot) {
|
||||
var protoChangeDetector = this.changeDetection.createProtoChangeDetector('dummy');
|
||||
inheritedProtoView = new ProtoView(current.element, protoChangeDetector);
|
||||
if (isPresent(parent)) {
|
||||
if (isPresent(parent.inheritedElementBinder.nestedProtoView)) {
|
||||
throw new BaseException('Only one nested view per element is allowed');
|
||||
}
|
||||
parent.inheritedElementBinder.nestedProtoView = inheritedProtoView;
|
||||
if (isPresent(parent.variableBindings)) {
|
||||
MapWrapper.forEach(parent.variableBindings, (mappedName, varName) => {
|
||||
inheritedProtoView.bindVariable(varName, mappedName);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (isPresent(parent)) {
|
||||
inheritedProtoView = parent.inheritedProtoView;
|
||||
}
|
||||
current.inheritedProtoView = inheritedProtoView;
|
||||
}
|
||||
}
|
45
modules/angular2/src/core/compiler/pipeline/text_interpolation_parser.js
vendored
Normal file
45
modules/angular2/src/core/compiler/pipeline/text_interpolation_parser.js
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
import {RegExpWrapper, StringWrapper, isPresent} from 'facade/src/lang';
|
||||
import {Node, DOM} from 'facade/src/dom';
|
||||
|
||||
import {Parser} from 'change_detection/change_detection';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
|
||||
/**
|
||||
* Parses interpolations in direct text child nodes of the current element.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#textNodeBindings
|
||||
*/
|
||||
export class TextInterpolationParser extends CompileStep {
|
||||
_parser:Parser;
|
||||
_compilationUnit:any;
|
||||
constructor(parser:Parser, compilationUnit:any) {
|
||||
this._parser = parser;
|
||||
this._compilationUnit = compilationUnit;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
if (!current.compileChildren) {
|
||||
return;
|
||||
}
|
||||
var element = current.element;
|
||||
var childNodes = DOM.templateAwareRoot(element).childNodes;
|
||||
for (var i=0; i<childNodes.length; i++) {
|
||||
var node = childNodes[i];
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
this._parseTextNode(current, node, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_parseTextNode(pipelineElement, node, nodeIndex) {
|
||||
var ast = this._parser.parseInterpolation(node.nodeValue, this._compilationUnit);
|
||||
if (isPresent(ast)) {
|
||||
DOM.setText(node, ' ');
|
||||
pipelineElement.addTextNodeBinding(nodeIndex, ast);
|
||||
}
|
||||
}
|
||||
}
|
113
modules/angular2/src/core/compiler/pipeline/view_splitter.js
vendored
Normal file
113
modules/angular2/src/core/compiler/pipeline/view_splitter.js
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
import {isBlank, isPresent, BaseException} from 'facade/src/lang';
|
||||
import {DOM, TemplateElement} from 'facade/src/dom';
|
||||
import {MapWrapper, ListWrapper} from 'facade/src/collection';
|
||||
|
||||
import {Parser} from 'change_detection/change_detection';
|
||||
|
||||
import {CompileStep} from './compile_step';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileControl} from './compile_control';
|
||||
import {StringWrapper} from 'facade/src/lang';
|
||||
|
||||
import {$BANG} from 'change_detection/src/parser/lexer';
|
||||
|
||||
/**
|
||||
* Splits views at `<template>` elements or elements with `template` attribute:
|
||||
* For `<template>` elements:
|
||||
* - moves the content into a new and disconnected `<template>` element
|
||||
* that is marked as view root.
|
||||
*
|
||||
* For elements with a `template` attribute:
|
||||
* - replaces the element with an empty `<template>` element,
|
||||
* parses the content of the `template` attribute and adds the information to that
|
||||
* `<template>` element. Marks the elements as view root.
|
||||
*
|
||||
* Note: In both cases the root of the nested view is disconnected from its parent element.
|
||||
* This is needed for browsers that don't support the `<template>` element
|
||||
* as we want to do locate elements with bindings using `getElementsByClassName` later on,
|
||||
* which should not descend into the nested view.
|
||||
*
|
||||
* Fills:
|
||||
* - CompileElement#isViewRoot
|
||||
* - CompileElement#variableBindings
|
||||
* - CompileElement#propertyBindings
|
||||
*/
|
||||
export class ViewSplitter extends CompileStep {
|
||||
_parser:Parser;
|
||||
_compilationUnit:any;
|
||||
constructor(parser:Parser, compilationUnit:any) {
|
||||
this._parser = parser;
|
||||
this._compilationUnit = compilationUnit;
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
if (isBlank(parent)) {
|
||||
current.isViewRoot = true;
|
||||
} else {
|
||||
if (current.element instanceof TemplateElement) {
|
||||
if (!current.isViewRoot) {
|
||||
var viewRoot = new CompileElement(DOM.createTemplate(''));
|
||||
var currentElement:TemplateElement = current.element;
|
||||
var viewRootElement:TemplateElement = viewRoot.element;
|
||||
this._moveChildNodes(currentElement.content, viewRootElement.content);
|
||||
viewRoot.isViewRoot = true;
|
||||
control.addChild(viewRoot);
|
||||
}
|
||||
} else {
|
||||
var attrs = current.attrs();
|
||||
var templateBindings = MapWrapper.get(attrs, 'template');
|
||||
var hasTemplateBinding = isPresent(templateBindings);
|
||||
|
||||
// look for template shortcuts such as !if="condition" and treat them as template="if condition"
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
if (StringWrapper.charCodeAt(attrName, 0) == $BANG) {
|
||||
var key = StringWrapper.substring(attrName, 1); // remove the bang
|
||||
if (hasTemplateBinding) {
|
||||
// 2nd template binding detected
|
||||
throw new BaseException(`Only one template directive per element is allowed: ` +
|
||||
`${templateBindings} and ${key} cannot be used simultaneously!`);
|
||||
} else {
|
||||
templateBindings = (attrValue.length == 0) ? key : key + ' ' + attrValue;
|
||||
hasTemplateBinding = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (hasTemplateBinding) {
|
||||
var newParent = new CompileElement(DOM.createTemplate(''));
|
||||
current.isViewRoot = true;
|
||||
this._parseTemplateBindings(templateBindings, newParent);
|
||||
this._addParentElement(current.element, newParent.element);
|
||||
|
||||
control.addParent(newParent);
|
||||
current.element.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_moveChildNodes(source, target) {
|
||||
while (isPresent(source.firstChild)) {
|
||||
DOM.appendChild(target, source.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
_addParentElement(currentElement, newParentElement) {
|
||||
DOM.insertBefore(currentElement, newParentElement);
|
||||
DOM.appendChild(newParentElement, currentElement);
|
||||
}
|
||||
|
||||
_parseTemplateBindings(templateBindings:string, compileElement:CompileElement) {
|
||||
var bindings = this._parser.parseTemplateBindings(templateBindings, this._compilationUnit);
|
||||
for (var i=0; i<bindings.length; i++) {
|
||||
var binding = bindings[i];
|
||||
if (binding.keyIsVar) {
|
||||
compileElement.addVariableBinding(binding.key, binding.name);
|
||||
} else if (isPresent(binding.expression)) {
|
||||
compileElement.addPropertyBinding(binding.key, binding.expression);
|
||||
} else {
|
||||
compileElement.element.setAttribute(binding.key, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user