refactor(render): ts’ify render api
This commit is contained in:
@ -8,9 +8,9 @@ import {CompileStep} from './compile_step';
|
||||
* Right now it only allows to add a parent element.
|
||||
*/
|
||||
export class CompileControl {
|
||||
_steps:List<CompileStep>;
|
||||
_currentStepIndex:number;
|
||||
_parent:CompileElement;
|
||||
_steps: List<CompileStep>;
|
||||
_currentStepIndex: number;
|
||||
_parent: CompileElement;
|
||||
_results;
|
||||
_additionalChildren;
|
||||
_ignoreCurrentElement: boolean;
|
||||
@ -24,16 +24,14 @@ export class CompileControl {
|
||||
}
|
||||
|
||||
// only public so that it can be used by compile_pipeline
|
||||
internalProcess(results, startStepIndex, parent:CompileElement, current:CompileElement) {
|
||||
internalProcess(results, startStepIndex, parent: CompileElement, current: CompileElement) {
|
||||
this._results = results;
|
||||
var previousStepIndex = this._currentStepIndex;
|
||||
var previousParent = this._parent;
|
||||
|
||||
this._ignoreCurrentElement = false;
|
||||
|
||||
for (var i = startStepIndex;
|
||||
i < this._steps.length && !this._ignoreCurrentElement;
|
||||
i++) {
|
||||
for (var i = startStepIndex; i < this._steps.length && !this._ignoreCurrentElement; i++) {
|
||||
var step = this._steps[i];
|
||||
this._parent = parent;
|
||||
this._currentStepIndex = i;
|
||||
@ -53,12 +51,12 @@ export class CompileControl {
|
||||
return localAdditionalChildren;
|
||||
}
|
||||
|
||||
addParent(newElement:CompileElement) {
|
||||
this.internalProcess(this._results, this._currentStepIndex+1, this._parent, newElement);
|
||||
addParent(newElement: CompileElement) {
|
||||
this.internalProcess(this._results, this._currentStepIndex + 1, this._parent, newElement);
|
||||
this._parent = newElement;
|
||||
}
|
||||
|
||||
addChild(element:CompileElement) {
|
||||
addChild(element: CompileElement) {
|
||||
if (isBlank(this._additionalChildren)) {
|
||||
this._additionalChildren = ListWrapper.create();
|
||||
}
|
||||
@ -71,7 +69,5 @@ export class CompileControl {
|
||||
* When a step calls `ignoreCurrentElement`, no further steps are executed on the current
|
||||
* element and no `CompileElement` is added to the result list.
|
||||
*/
|
||||
ignoreCurrentElement() {
|
||||
this._ignoreCurrentElement = true;
|
||||
}
|
||||
ignoreCurrentElement() { this._ignoreCurrentElement = true; }
|
||||
}
|
@ -1,6 +1,13 @@
|
||||
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 {
|
||||
int,
|
||||
isBlank,
|
||||
isPresent,
|
||||
Type,
|
||||
StringJoiner,
|
||||
assertionsEnabled
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
import {ProtoViewBuilder, ElementBinderBuilder} from '../view/proto_view_builder';
|
||||
|
||||
@ -11,14 +18,15 @@ import {ProtoViewBuilder, ElementBinderBuilder} from '../view/proto_view_builder
|
||||
*/
|
||||
export class CompileElement {
|
||||
element;
|
||||
_attrs:Map;
|
||||
_classList:List;
|
||||
isViewRoot:boolean;
|
||||
inheritedProtoView:ProtoViewBuilder;
|
||||
distanceToInheritedBinder:number;
|
||||
inheritedElementBinder:ElementBinderBuilder;
|
||||
_attrs: Map<string, string>;
|
||||
_classList: List<string>;
|
||||
isViewRoot: boolean;
|
||||
inheritedProtoView: ProtoViewBuilder;
|
||||
distanceToInheritedBinder: number;
|
||||
inheritedElementBinder: ElementBinderBuilder;
|
||||
compileChildren: boolean;
|
||||
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
|
||||
elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of
|
||||
// error
|
||||
|
||||
constructor(element, compilationUnit = '') {
|
||||
this.element = element;
|
||||
@ -34,7 +42,7 @@ export class CompileElement {
|
||||
this.distanceToInheritedBinder = 0;
|
||||
this.compileChildren = true;
|
||||
// description is calculated here as compilation steps may change the element
|
||||
var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
|
||||
var tplDesc = assertionsEnabled() ? getElementDescription(element) : null;
|
||||
if (compilationUnit !== '') {
|
||||
this.elementDescription = compilationUnit;
|
||||
if (isPresent(tplDesc)) this.elementDescription += ": " + tplDesc;
|
||||
@ -50,7 +58,8 @@ export class CompileElement {
|
||||
bindElement() {
|
||||
if (!this.isBound()) {
|
||||
var parentBinder = this.inheritedElementBinder;
|
||||
this.inheritedElementBinder = this.inheritedProtoView.bindElement(this.element, this.elementDescription);
|
||||
this.inheritedElementBinder =
|
||||
this.inheritedProtoView.bindElement(this.element, this.elementDescription);
|
||||
if (isPresent(parentBinder)) {
|
||||
this.inheritedElementBinder.setParent(parentBinder, this.distanceToInheritedBinder);
|
||||
}
|
||||
@ -59,22 +68,18 @@ export class CompileElement {
|
||||
return this.inheritedElementBinder;
|
||||
}
|
||||
|
||||
refreshAttrs() {
|
||||
this._attrs = null;
|
||||
}
|
||||
refreshAttrs() { this._attrs = null; }
|
||||
|
||||
attrs():Map<string,string> {
|
||||
attrs(): Map<string, string> {
|
||||
if (isBlank(this._attrs)) {
|
||||
this._attrs = DOM.attributeMap(this.element);
|
||||
}
|
||||
return this._attrs;
|
||||
}
|
||||
|
||||
refreshClassList() {
|
||||
this._classList = null;
|
||||
}
|
||||
refreshClassList() { this._classList = null; }
|
||||
|
||||
classList():List<string> {
|
||||
classList(): List<string> {
|
||||
if (isBlank(this._classList)) {
|
||||
this._classList = ListWrapper.create();
|
||||
var elClassList = DOM.classList(this.element);
|
||||
@ -84,12 +89,11 @@ export class CompileElement {
|
||||
}
|
||||
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 {
|
||||
function getElementDescription(domElement): string {
|
||||
var buf = new StringJoiner();
|
||||
var atts = DOM.attributeMap(domElement);
|
||||
|
||||
@ -100,9 +104,9 @@ function getElementDescription(domElement):string {
|
||||
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);
|
||||
}
|
||||
if (attName !== "id" && attName !== "class") {
|
||||
addDescriptionAttribute(buf, attName, attValue);
|
||||
}
|
||||
});
|
||||
|
||||
buf.add(">");
|
||||
@ -110,12 +114,12 @@ function getElementDescription(domElement):string {
|
||||
}
|
||||
|
||||
|
||||
function addDescriptionAttribute(buffer:StringJoiner, attName:string, attValue) {
|
||||
function addDescriptionAttribute(buffer: StringJoiner, attName: string, attValue) {
|
||||
if (isPresent(attValue)) {
|
||||
if (attValue.length === 0) {
|
||||
buffer.add(' ' + attName);
|
||||
} else {
|
||||
buffer.add(' ' + attName + '="' + attValue + '"');
|
||||
}
|
||||
if (attValue.length === 0) {
|
||||
buffer.add(' ' + attName);
|
||||
} else {
|
||||
buffer.add(' ' + attName + '="' + attValue + '"');
|
||||
}
|
||||
}
|
||||
}
|
@ -12,12 +12,11 @@ import {ProtoViewDto} from '../../api';
|
||||
* all elements in a template.
|
||||
*/
|
||||
export class CompilePipeline {
|
||||
_control:CompileControl;
|
||||
constructor(steps:List<CompileStep>) {
|
||||
this._control = new CompileControl(steps);
|
||||
}
|
||||
_control: CompileControl;
|
||||
constructor(steps: List<CompileStep>) { this._control = new CompileControl(steps); }
|
||||
|
||||
process(rootElement, protoViewType:number = null, compilationCtxtDescription:string = ''):List {
|
||||
process(rootElement, protoViewType: number = null,
|
||||
compilationCtxtDescription: string = ''): List<CompileElement> {
|
||||
if (isBlank(protoViewType)) {
|
||||
protoViewType = ProtoViewDto.COMPONENT_VIEW_TYPE;
|
||||
}
|
||||
@ -25,13 +24,12 @@ export class CompilePipeline {
|
||||
var rootCompileElement = new CompileElement(rootElement, compilationCtxtDescription);
|
||||
rootCompileElement.inheritedProtoView = new ProtoViewBuilder(rootElement, protoViewType);
|
||||
rootCompileElement.isViewRoot = true;
|
||||
this._process(results, null, rootCompileElement,
|
||||
compilationCtxtDescription
|
||||
);
|
||||
this._process(results, null, rootCompileElement, compilationCtxtDescription);
|
||||
return results;
|
||||
}
|
||||
|
||||
_process(results, parent:CompileElement, current:CompileElement, compilationCtxtDescription:string = '') {
|
||||
_process(results, parent: CompileElement, current: CompileElement,
|
||||
compilationCtxtDescription: string = '') {
|
||||
var additionalChildren = this._control.internalProcess(results, 0, parent, current);
|
||||
|
||||
if (current.compileChildren) {
|
||||
@ -44,7 +42,7 @@ export class CompilePipeline {
|
||||
var childCompileElement = new CompileElement(node, compilationCtxtDescription);
|
||||
childCompileElement.inheritedProtoView = current.inheritedProtoView;
|
||||
childCompileElement.inheritedElementBinder = current.inheritedElementBinder;
|
||||
childCompileElement.distanceToInheritedBinder = current.distanceToInheritedBinder+1;
|
||||
childCompileElement.distanceToInheritedBinder = current.distanceToInheritedBinder + 1;
|
||||
this._process(results, current, childCompileElement);
|
||||
}
|
||||
node = nextNode;
|
||||
@ -52,7 +50,7 @@ export class CompilePipeline {
|
||||
}
|
||||
|
||||
if (isPresent(additionalChildren)) {
|
||||
for (var i=0; i<additionalChildren.length; i++) {
|
||||
for (var i = 0; i < additionalChildren.length; i++) {
|
||||
this._process(results, current, additionalChildren[i]);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import * as compileControlModule from './compile_control';
|
||||
* One part of the compile process.
|
||||
* Is guaranteed to be called in depth first order
|
||||
*/
|
||||
export class CompileStep {
|
||||
process(parent:CompileElement, current:CompileElement, control:compileControlModule.CompileControl) {}
|
||||
export interface CompileStep {
|
||||
process(parent: CompileElement, current: CompileElement,
|
||||
control: compileControlModule.CompileControl): void;
|
||||
}
|
@ -12,7 +12,7 @@ import {ShadowDomCompileStep} from '../shadow_dom/shadow_dom_compile_step';
|
||||
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||
|
||||
export class CompileStepFactory {
|
||||
createSteps(template: ViewDefinition, subTaskPromises: List<Promise>):List<CompileStep> {
|
||||
createSteps(template: ViewDefinition, subTaskPromises: List<Promise<any>>): List<CompileStep> {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,7 @@ export class DefaultStepFactory extends CompileStepFactory {
|
||||
this._shadowDomStrategy = shadowDomStrategy;
|
||||
}
|
||||
|
||||
createSteps(template: ViewDefinition, subTaskPromises: List<Promise>) {
|
||||
createSteps(template: ViewDefinition, subTaskPromises: List<Promise<any>>) {
|
||||
return [
|
||||
new ViewSplitter(this._parser),
|
||||
new PropertyBindingParser(this._parser),
|
@ -1,10 +1,16 @@
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {Injectable} from 'angular2/di';
|
||||
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {BaseException, isPresent} from 'angular2/src/facade/lang';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
|
||||
import {ViewDefinition, ProtoViewDto, DirectiveMetadata, RenderCompiler, RenderProtoViewRef} from '../../api';
|
||||
import {
|
||||
ViewDefinition,
|
||||
ProtoViewDto,
|
||||
DirectiveMetadata,
|
||||
RenderCompiler,
|
||||
RenderProtoViewRef
|
||||
} from '../../api';
|
||||
import {CompilePipeline} from './compile_pipeline';
|
||||
import {TemplateLoader} from 'angular2/src/render/dom/compiler/template_loader';
|
||||
import {CompileStepFactory, DefaultStepFactory} from './compile_step_factory';
|
||||
@ -26,26 +32,27 @@ export class DomCompiler extends RenderCompiler {
|
||||
this._stepFactory = stepFactory;
|
||||
}
|
||||
|
||||
compile(template: ViewDefinition):Promise<ProtoViewDto> {
|
||||
compile(template: ViewDefinition): Promise<ProtoViewDto> {
|
||||
var tplPromise = this._templateLoader.load(template);
|
||||
return PromiseWrapper.then(tplPromise,
|
||||
(el) => this._compileTemplate(template, el, ProtoViewDto.COMPONENT_VIEW_TYPE),
|
||||
(_) => { throw new BaseException(`Failed to load the template "${template.componentId}"`); }
|
||||
);
|
||||
return PromiseWrapper.then(
|
||||
tplPromise, (el) => this._compileTemplate(template, el, ProtoViewDto.COMPONENT_VIEW_TYPE),
|
||||
(_) => {
|
||||
throw new BaseException(`Failed to load the template "${template.componentId}"`);
|
||||
});
|
||||
}
|
||||
|
||||
compileHost(directiveMetadata: DirectiveMetadata):Promise<ProtoViewDto> {
|
||||
compileHost(directiveMetadata: DirectiveMetadata): Promise<ProtoViewDto> {
|
||||
var hostViewDef = new ViewDefinition({
|
||||
componentId: directiveMetadata.id,
|
||||
absUrl: null,
|
||||
template: null,
|
||||
absUrl: null, template: null,
|
||||
directives: [directiveMetadata]
|
||||
});
|
||||
var element = DOM.createElement(directiveMetadata.selector);
|
||||
return this._compileTemplate(hostViewDef, element, ProtoViewDto.HOST_VIEW_TYPE);
|
||||
}
|
||||
|
||||
_compileTemplate(viewDef: ViewDefinition, tplElement, protoViewType:number):Promise<ProtoViewDto> {
|
||||
_compileTemplate(viewDef: ViewDefinition, tplElement,
|
||||
protoViewType: number): Promise<ProtoViewDto> {
|
||||
var subTaskPromises = [];
|
||||
var pipeline = new CompilePipeline(this._stepFactory.createSteps(viewDef, subTaskPromises));
|
||||
var compileElements = pipeline.process(tplElement, protoViewType, viewDef.componentId);
|
||||
@ -62,7 +69,8 @@ export class DomCompiler extends RenderCompiler {
|
||||
|
||||
@Injectable()
|
||||
export class DefaultDomCompiler extends DomCompiler {
|
||||
constructor(parser:Parser, shadowDomStrategy:ShadowDomStrategy, templateLoader: TemplateLoader) {
|
||||
constructor(parser: Parser, shadowDomStrategy: ShadowDomStrategy,
|
||||
templateLoader: TemplateLoader) {
|
||||
super(new DefaultStepFactory(parser, shadowDomStrategy), templateLoader);
|
||||
}
|
||||
}
|
@ -1,4 +1,11 @@
|
||||
import {isPresent, isBlank, BaseException, assertionsEnabled, RegExpWrapper, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
BaseException,
|
||||
assertionsEnabled,
|
||||
RegExpWrapper,
|
||||
StringWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {List, MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {Parser} from 'angular2/change_detection';
|
||||
@ -16,17 +23,16 @@ import {dashCaseToCamelCase, camelCaseToDashCase, EVENT_TARGET_SEPARATOR} from '
|
||||
* Parses the directives on a single element. Assumes ViewSplitter has already created
|
||||
* <template> elements for template directives.
|
||||
*/
|
||||
export class DirectiveParser extends CompileStep {
|
||||
_selectorMatcher:SelectorMatcher;
|
||||
_directives:List<DirectiveMetadata>;
|
||||
_parser:Parser;
|
||||
export class DirectiveParser implements CompileStep {
|
||||
_selectorMatcher: SelectorMatcher;
|
||||
_directives: List<DirectiveMetadata>;
|
||||
_parser: Parser;
|
||||
|
||||
constructor(parser: Parser, directives:List<DirectiveMetadata>) {
|
||||
super();
|
||||
constructor(parser: Parser, directives: List<DirectiveMetadata>) {
|
||||
this._parser = parser;
|
||||
this._selectorMatcher = new SelectorMatcher();
|
||||
this._directives = directives;
|
||||
for (var i=0; i<directives.length; i++) {
|
||||
for (var i = 0; i < directives.length; i++) {
|
||||
var directive = directives[i];
|
||||
var selector = CssSelector.parse(directive.selector);
|
||||
this._ensureComponentOnlyHasElementSelector(selector, directive);
|
||||
@ -36,25 +42,25 @@ export class DirectiveParser extends CompileStep {
|
||||
|
||||
_ensureComponentOnlyHasElementSelector(selector, directive) {
|
||||
var isElementSelector = selector.length === 1 && selector[0].isElementSelector();
|
||||
if (! isElementSelector && directive.type === DirectiveMetadata.COMPONENT_TYPE) {
|
||||
throw new BaseException(`Component '${directive.id}' can only have an element selector, but had '${directive.selector}'`);
|
||||
if (!isElementSelector && directive.type === DirectiveMetadata.COMPONENT_TYPE) {
|
||||
throw new BaseException(
|
||||
`Component '${directive.id}' can only have an element selector, but had '${directive.selector}'`);
|
||||
}
|
||||
}
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var classList = current.classList();
|
||||
|
||||
var cssSelector = new CssSelector();
|
||||
var nodeName = DOM.nodeName(current.element);
|
||||
cssSelector.setElement(nodeName);
|
||||
for (var i=0; i < classList.length; i++) {
|
||||
for (var i = 0; i < classList.length; i++) {
|
||||
cssSelector.addClassName(classList[i]);
|
||||
}
|
||||
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
cssSelector.addAttribute(attrName, attrValue);
|
||||
});
|
||||
MapWrapper.forEach(attrs,
|
||||
(attrValue, attrName) => { cssSelector.addAttribute(attrName, attrValue); });
|
||||
|
||||
var componentDirective;
|
||||
var foundDirectiveIndices = [];
|
||||
@ -66,7 +72,8 @@ export class DirectiveParser extends CompileStep {
|
||||
// components need to go first, so it is easier to locate them in the result.
|
||||
ListWrapper.insert(foundDirectiveIndices, 0, directiveIndex);
|
||||
if (isPresent(componentDirective)) {
|
||||
throw new BaseException(`Only one component directive is allowed per element - check ${current.elementDescription}`);
|
||||
throw new BaseException(
|
||||
`Only one component directive is allowed per element - check ${current.elementDescription}`);
|
||||
}
|
||||
componentDirective = directive;
|
||||
elementBinder.setComponentId(directive.id);
|
||||
@ -95,7 +102,8 @@ export class DirectiveParser extends CompileStep {
|
||||
}
|
||||
if (isPresent(directive.hostProperties)) {
|
||||
MapWrapper.forEach(directive.hostProperties, (hostPropertyName, directivePropertyName) => {
|
||||
this._bindHostProperty(hostPropertyName, directivePropertyName, current, directiveBinderBuilder);
|
||||
this._bindHostProperty(hostPropertyName, directivePropertyName, current,
|
||||
directiveBinderBuilder);
|
||||
});
|
||||
}
|
||||
if (isPresent(directive.hostAttributes)) {
|
||||
@ -104,9 +112,8 @@ export class DirectiveParser extends CompileStep {
|
||||
});
|
||||
}
|
||||
if (isPresent(directive.readAttributes)) {
|
||||
ListWrapper.forEach(directive.readAttributes, (attrName) => {
|
||||
elementBinder.readAttribute(attrName);
|
||||
});
|
||||
ListWrapper.forEach(directive.readAttributes,
|
||||
(attrName) => { elementBinder.readAttribute(attrName); });
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -115,27 +122,21 @@ export class DirectiveParser extends CompileStep {
|
||||
var pipes = this._splitBindConfig(bindConfig);
|
||||
var elProp = ListWrapper.removeAt(pipes, 0);
|
||||
|
||||
var bindingAst = MapWrapper.get(
|
||||
compileElement.bindElement().propertyBindings,
|
||||
dashCaseToCamelCase(elProp)
|
||||
);
|
||||
var bindingAst =
|
||||
MapWrapper.get(compileElement.bindElement().propertyBindings, dashCaseToCamelCase(elProp));
|
||||
|
||||
if (isBlank(bindingAst)) {
|
||||
var attributeValue = MapWrapper.get(compileElement.attrs(), camelCaseToDashCase(elProp));
|
||||
if (isPresent(attributeValue)) {
|
||||
bindingAst = this._parser.wrapLiteralPrimitive(
|
||||
attributeValue,
|
||||
compileElement.elementDescription
|
||||
);
|
||||
bindingAst =
|
||||
this._parser.wrapLiteralPrimitive(attributeValue, compileElement.elementDescription);
|
||||
}
|
||||
}
|
||||
|
||||
// Bindings are optional, so this binding only needs to be set up if an expression is given.
|
||||
if (isPresent(bindingAst)) {
|
||||
var fullExpAstWithBindPipes = this._parser.addPipes(bindingAst, pipes);
|
||||
directiveBinderBuilder.bindProperty(
|
||||
dirProperty, fullExpAstWithBindPipes
|
||||
);
|
||||
directiveBinderBuilder.bindProperty(dirProperty, fullExpAstWithBindPipes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,24 +155,23 @@ export class DirectiveParser extends CompileStep {
|
||||
directiveBinderBuilder.bindHostAction(actionName, actionExpression, ast);
|
||||
}
|
||||
|
||||
_bindHostProperty(hostPropertyName, directivePropertyName, compileElement, directiveBinderBuilder) {
|
||||
_bindHostProperty(hostPropertyName, directivePropertyName, compileElement,
|
||||
directiveBinderBuilder) {
|
||||
var ast = this._parser.parseBinding(directivePropertyName,
|
||||
`hostProperties of ${compileElement.elementDescription}`);
|
||||
`hostProperties of ${compileElement.elementDescription}`);
|
||||
directiveBinderBuilder.bindHostProperty(hostPropertyName, ast);
|
||||
}
|
||||
|
||||
_addHostAttribute(attrName, attrValue, compileElement) {
|
||||
if (StringWrapper.equals(attrName, 'class')) {
|
||||
ListWrapper.forEach(attrValue.split(' '), (className) => {
|
||||
DOM.addClass(compileElement.element, className);
|
||||
});
|
||||
ListWrapper.forEach(attrValue.split(' '),
|
||||
(className) => { DOM.addClass(compileElement.element, className); });
|
||||
} else if (!DOM.hasAttribute(compileElement.element, attrName)) {
|
||||
DOM.setAttribute(compileElement.element, attrName, attrValue);
|
||||
}
|
||||
}
|
||||
|
||||
_splitBindConfig(bindConfig:string) {
|
||||
_splitBindConfig(bindConfig: string) {
|
||||
return ListWrapper.map(bindConfig.split('|'), (s) => s.trim());
|
||||
}
|
||||
}
|
||||
|
@ -22,92 +22,82 @@ var BIND_NAME_REGEXP = RegExpWrapper.create(
|
||||
/**
|
||||
* Parses the property bindings on a single element.
|
||||
*/
|
||||
export class PropertyBindingParser extends CompileStep {
|
||||
_parser:Parser;
|
||||
export class PropertyBindingParser implements CompileStep {
|
||||
_parser: Parser;
|
||||
|
||||
constructor(parser:Parser) {
|
||||
super();
|
||||
this._parser = parser;
|
||||
}
|
||||
constructor(parser: Parser) { this._parser = parser; }
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var newAttrs = MapWrapper.create();
|
||||
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
var bindParts = RegExpWrapper.firstMatch(BIND_NAME_REGEXP, attrName);
|
||||
if (isPresent(bindParts)) {
|
||||
if (isPresent(bindParts[1])) { // match: bind-prop
|
||||
if (isPresent(bindParts[1])) { // match: bind-prop
|
||||
this._bindProperty(bindParts[5], attrValue, current, newAttrs);
|
||||
|
||||
} else if (isPresent(bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
|
||||
} else if (isPresent(
|
||||
bindParts[2])) { // match: var-name / var-name="iden" / #name / #name="iden"
|
||||
var identifier = bindParts[5];
|
||||
var value = attrValue == '' ? '\$implicit' : attrValue;
|
||||
this._bindVariable(identifier, value, current, newAttrs);
|
||||
|
||||
} else if (isPresent(bindParts[3])) { // match: on-event
|
||||
} else if (isPresent(bindParts[3])) { // match: on-event
|
||||
this._bindEvent(bindParts[5], attrValue, current, newAttrs);
|
||||
|
||||
} else if (isPresent(bindParts[4])) { // match: bindon-prop
|
||||
} else if (isPresent(bindParts[4])) { // match: bindon-prop
|
||||
this._bindProperty(bindParts[5], attrValue, current, newAttrs);
|
||||
this._bindAssignmentEvent(bindParts[5], attrValue, current, newAttrs);
|
||||
|
||||
} else if (isPresent(bindParts[6])) { // match: [(expr)]
|
||||
} else if (isPresent(bindParts[6])) { // match: [(expr)]
|
||||
this._bindProperty(bindParts[6], attrValue, current, newAttrs);
|
||||
this._bindAssignmentEvent(bindParts[6], attrValue, current, newAttrs);
|
||||
|
||||
} else if (isPresent(bindParts[7])) { // match: [expr]
|
||||
} else if (isPresent(bindParts[7])) { // match: [expr]
|
||||
this._bindProperty(bindParts[7], attrValue, current, newAttrs);
|
||||
|
||||
} else if (isPresent(bindParts[8])) { // match: (event)
|
||||
} else if (isPresent(bindParts[8])) { // match: (event)
|
||||
this._bindEvent(bindParts[8], attrValue, current, newAttrs);
|
||||
}
|
||||
} else {
|
||||
var expr = this._parser.parseInterpolation(
|
||||
attrValue, current.elementDescription
|
||||
);
|
||||
var expr = this._parser.parseInterpolation(attrValue, current.elementDescription);
|
||||
if (isPresent(expr)) {
|
||||
this._bindPropertyAst(attrName, expr, current, newAttrs);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MapWrapper.forEach(newAttrs, (attrValue, attrName) => {
|
||||
MapWrapper.set(attrs, attrName, attrValue);
|
||||
});
|
||||
MapWrapper.forEach(newAttrs,
|
||||
(attrValue, attrName) => { MapWrapper.set(attrs, attrName, attrValue); });
|
||||
}
|
||||
|
||||
_bindVariable(identifier, value, current:CompileElement, newAttrs) {
|
||||
_bindVariable(identifier, value, current: CompileElement, newAttrs) {
|
||||
current.bindElement().bindVariable(dashCaseToCamelCase(identifier), value);
|
||||
MapWrapper.set(newAttrs, identifier, value);
|
||||
}
|
||||
|
||||
_bindProperty(name, expression, current:CompileElement, newAttrs) {
|
||||
this._bindPropertyAst(
|
||||
name,
|
||||
this._parser.parseBinding(expression, current.elementDescription),
|
||||
current,
|
||||
newAttrs
|
||||
);
|
||||
_bindProperty(name, expression, current: CompileElement, newAttrs) {
|
||||
this._bindPropertyAst(name, this._parser.parseBinding(expression, current.elementDescription),
|
||||
current, newAttrs);
|
||||
}
|
||||
|
||||
_bindPropertyAst(name, ast, current:CompileElement, newAttrs) {
|
||||
_bindPropertyAst(name, ast, current: CompileElement, newAttrs) {
|
||||
var binder = current.bindElement();
|
||||
var camelCaseName = dashCaseToCamelCase(name);
|
||||
binder.bindProperty(camelCaseName, ast);
|
||||
MapWrapper.set(newAttrs, name, ast.source);
|
||||
}
|
||||
|
||||
_bindAssignmentEvent(name, expression, current:CompileElement, newAttrs) {
|
||||
_bindAssignmentEvent(name, expression, current: CompileElement, newAttrs) {
|
||||
this._bindEvent(name, `${expression}=$event`, current, newAttrs);
|
||||
}
|
||||
|
||||
_bindEvent(name, expression, current:CompileElement, newAttrs) {
|
||||
_bindEvent(name, expression, current: CompileElement, newAttrs) {
|
||||
current.bindElement().bindEvent(
|
||||
dashCaseToCamelCase(name), this._parser.parseAction(expression, current.elementDescription)
|
||||
);
|
||||
dashCaseToCamelCase(name),
|
||||
this._parser.parseAction(expression, current.elementDescription));
|
||||
// Don't detect directives for event names for now,
|
||||
// so don't add the event name to the CompileElement.attrs
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +1,24 @@
|
||||
import {List, Map, ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, BaseException} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
RegExpWrapper,
|
||||
RegExpMatcherWrapper,
|
||||
StringWrapper,
|
||||
BaseException
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
const _EMPTY_ATTR_VALUE = '';
|
||||
|
||||
// TODO: Can't use `const` here as
|
||||
// in Dart this is not transpiled into `final` yet...
|
||||
var _SELECTOR_REGEXP =
|
||||
RegExpWrapper.create('(\\:not\\()|' + //":not("
|
||||
'([-\\w]+)|' + // "tag"
|
||||
'(?:\\.([-\\w]+))|' + // ".class"
|
||||
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]" or "[name*=value]"
|
||||
'(?:\\))|' + // ")"
|
||||
'(\\s*,\\s*)'); // ","
|
||||
var _SELECTOR_REGEXP = RegExpWrapper.create(
|
||||
'(\\:not\\()|' + //":not("
|
||||
'([-\\w]+)|' + // "tag"
|
||||
'(?:\\.([-\\w]+))|' + // ".class"
|
||||
'(?:\\[([-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]" or "[name*=value]"
|
||||
'(?:\\))|' + // ")"
|
||||
'(\\s*,\\s*)'); // ","
|
||||
|
||||
/**
|
||||
* A css selector contains an element name,
|
||||
@ -19,110 +26,107 @@ var _SELECTOR_REGEXP =
|
||||
* of selecting subsets out of them.
|
||||
*/
|
||||
export class CssSelector {
|
||||
element:string;
|
||||
classNames:List;
|
||||
attrs:List;
|
||||
element: string;
|
||||
classNames: List<string>;
|
||||
attrs: List<string>;
|
||||
notSelector: CssSelector;
|
||||
static parse(selector:string): List<CssSelector> {
|
||||
static parse(selector: string): List<CssSelector> {
|
||||
var results = ListWrapper.create();
|
||||
var _addResult = (res, cssSel) => {
|
||||
if (isPresent(cssSel.notSelector) && isBlank(cssSel.element)
|
||||
&& ListWrapper.isEmpty(cssSel.classNames) && ListWrapper.isEmpty(cssSel.attrs)) {
|
||||
if (isPresent(cssSel.notSelector) && isBlank(cssSel.element) &&
|
||||
ListWrapper.isEmpty(cssSel.classNames) && ListWrapper.isEmpty(cssSel.attrs)) {
|
||||
cssSel.element = "*";
|
||||
}
|
||||
ListWrapper.push(res, cssSel);
|
||||
}
|
||||
var cssSelector = new CssSelector();
|
||||
var matcher = RegExpWrapper.matcher(_SELECTOR_REGEXP, selector);
|
||||
var match;
|
||||
var current = cssSelector;
|
||||
while (isPresent(match = RegExpMatcherWrapper.next(matcher))) {
|
||||
if (isPresent(match[1])) {
|
||||
if (isPresent(cssSelector.notSelector)) {
|
||||
throw new BaseException('Nesting :not is not allowed in a selector');
|
||||
}
|
||||
current.notSelector = new CssSelector();
|
||||
current = current.notSelector;
|
||||
}
|
||||
if (isPresent(match[2])) {
|
||||
current.setElement(match[2]);
|
||||
}
|
||||
if (isPresent(match[3])) {
|
||||
current.addClassName(match[3]);
|
||||
}
|
||||
if (isPresent(match[4])) {
|
||||
current.addAttribute(match[4], match[5]);
|
||||
}
|
||||
if (isPresent(match[6])) {
|
||||
_addResult(results, cssSelector);
|
||||
cssSelector = current = new CssSelector();
|
||||
}
|
||||
}
|
||||
_addResult(results, cssSelector);
|
||||
return results;
|
||||
} ListWrapper.push(res, cssSel);
|
||||
}
|
||||
var cssSelector = new CssSelector();
|
||||
var matcher = RegExpWrapper.matcher(_SELECTOR_REGEXP, selector);
|
||||
var match;
|
||||
var current = cssSelector;
|
||||
while (isPresent(match = RegExpMatcherWrapper.next(matcher))) {
|
||||
if (isPresent(match[1])) {
|
||||
if (isPresent(cssSelector.notSelector)) {
|
||||
throw new BaseException('Nesting :not is not allowed in a selector');
|
||||
}
|
||||
current.notSelector = new CssSelector();
|
||||
current = current.notSelector;
|
||||
}
|
||||
if (isPresent(match[2])) {
|
||||
current.setElement(match[2]);
|
||||
}
|
||||
if (isPresent(match[3])) {
|
||||
current.addClassName(match[3]);
|
||||
}
|
||||
if (isPresent(match[4])) {
|
||||
current.addAttribute(match[4], match[5]);
|
||||
}
|
||||
if (isPresent(match[6])) {
|
||||
_addResult(results, cssSelector);
|
||||
cssSelector = current = new CssSelector();
|
||||
}
|
||||
}
|
||||
_addResult(results, cssSelector);
|
||||
return results;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.element = null;
|
||||
this.classNames = ListWrapper.create();
|
||||
this.attrs = ListWrapper.create();
|
||||
this.notSelector = null;
|
||||
}
|
||||
constructor() {
|
||||
this.element = null;
|
||||
this.classNames = ListWrapper.create();
|
||||
this.attrs = ListWrapper.create();
|
||||
this.notSelector = null;
|
||||
}
|
||||
|
||||
isElementSelector():boolean {
|
||||
return isPresent(this.element) &&
|
||||
ListWrapper.isEmpty(this.classNames) &&
|
||||
ListWrapper.isEmpty(this.attrs) &&
|
||||
isBlank(this.notSelector);
|
||||
}
|
||||
isElementSelector(): boolean {
|
||||
return isPresent(this.element) && ListWrapper.isEmpty(this.classNames) &&
|
||||
ListWrapper.isEmpty(this.attrs) && isBlank(this.notSelector);
|
||||
}
|
||||
|
||||
setElement(element:string = null) {
|
||||
if (isPresent(element)) {
|
||||
element = element.toLowerCase();
|
||||
setElement(element: string = null) {
|
||||
if (isPresent(element)) {
|
||||
element = element.toLowerCase();
|
||||
}
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
addAttribute(name: string, value: string = _EMPTY_ATTR_VALUE) {
|
||||
ListWrapper.push(this.attrs, name.toLowerCase());
|
||||
if (isPresent(value)) {
|
||||
value = value.toLowerCase();
|
||||
} else {
|
||||
value = _EMPTY_ATTR_VALUE;
|
||||
}
|
||||
ListWrapper.push(this.attrs, value);
|
||||
}
|
||||
|
||||
addClassName(name: string) {
|
||||
ListWrapper.push(this.classNames, name.toLowerCase());
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
var res = '';
|
||||
if (isPresent(this.element)) {
|
||||
res += this.element;
|
||||
}
|
||||
if (isPresent(this.classNames)) {
|
||||
for (var i = 0; i < this.classNames.length; i++) {
|
||||
res += '.' + this.classNames[i];
|
||||
}
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
addAttribute(name:string, value:string = _EMPTY_ATTR_VALUE) {
|
||||
ListWrapper.push(this.attrs, name.toLowerCase());
|
||||
if (isPresent(value)) {
|
||||
value = value.toLowerCase();
|
||||
} else {
|
||||
value = _EMPTY_ATTR_VALUE;
|
||||
}
|
||||
ListWrapper.push(this.attrs, value);
|
||||
}
|
||||
|
||||
addClassName(name:string) {
|
||||
ListWrapper.push(this.classNames, name.toLowerCase());
|
||||
}
|
||||
|
||||
toString():string {
|
||||
var res = '';
|
||||
if (isPresent(this.element)) {
|
||||
res += this.element;
|
||||
}
|
||||
if (isPresent(this.classNames)) {
|
||||
for (var i=0; i<this.classNames.length; i++) {
|
||||
res += '.' + this.classNames[i];
|
||||
if (isPresent(this.attrs)) {
|
||||
for (var i = 0; i < this.attrs.length;) {
|
||||
var attrName = this.attrs[i++];
|
||||
var attrValue = this.attrs[i++];
|
||||
res += '[' + attrName;
|
||||
if (attrValue.length > 0) {
|
||||
res += '=' + attrValue;
|
||||
}
|
||||
res += ']';
|
||||
}
|
||||
if (isPresent(this.attrs)) {
|
||||
for (var i=0; i<this.attrs.length;) {
|
||||
var attrName = this.attrs[i++];
|
||||
var attrValue = this.attrs[i++]
|
||||
res += '[' + attrName;
|
||||
if (attrValue.length > 0) {
|
||||
res += '=' + attrValue;
|
||||
}
|
||||
res += ']';
|
||||
}
|
||||
}
|
||||
if (isPresent(this.notSelector)) {
|
||||
res += ":not(" + this.notSelector.toString() + ")";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (isPresent(this.notSelector)) {
|
||||
res += ":not(" + this.notSelector.toString() + ")";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,13 +134,20 @@ export class CssSelector {
|
||||
* are contained in a given CssSelector.
|
||||
*/
|
||||
export class SelectorMatcher {
|
||||
_elementMap:Map;
|
||||
_elementPartialMap:Map;
|
||||
_classMap:Map;
|
||||
_classPartialMap:Map;
|
||||
_attrValueMap:Map;
|
||||
_attrValuePartialMap:Map;
|
||||
_listContexts:List;
|
||||
static createNotMatcher(notSelector:CssSelector) {
|
||||
var notMatcher = new SelectorMatcher();
|
||||
notMatcher._addSelectable(notSelector, null, null);
|
||||
return notMatcher;
|
||||
}
|
||||
|
||||
private _elementMap: Map<string, string>;
|
||||
private _elementPartialMap: Map<string, string>;
|
||||
private _classMap: Map<string, string>;
|
||||
private _classPartialMap: Map<string, string>;
|
||||
private _attrValueMap: Map<string, string>;
|
||||
private _attrValuePartialMap: Map<string, string>;
|
||||
private _listContexts: List<SelectorListContext>;
|
||||
|
||||
constructor() {
|
||||
this._elementMap = MapWrapper.create();
|
||||
this._elementPartialMap = MapWrapper.create();
|
||||
@ -150,14 +161,14 @@ export class SelectorMatcher {
|
||||
this._listContexts = ListWrapper.create();
|
||||
}
|
||||
|
||||
addSelectables(cssSelectors:List<CssSelector>, callbackCtxt) {
|
||||
addSelectables(cssSelectors: List<CssSelector>, callbackCtxt: any) {
|
||||
var listContext = null;
|
||||
if (cssSelectors.length > 1) {
|
||||
listContext= new SelectorListContext(cssSelectors);
|
||||
listContext = new SelectorListContext(cssSelectors);
|
||||
ListWrapper.push(this._listContexts, listContext);
|
||||
}
|
||||
for (var i = 0; i < cssSelectors.length; i++) {
|
||||
this.addSelectable(cssSelectors[i], callbackCtxt, listContext);
|
||||
this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,7 +177,7 @@ export class SelectorMatcher {
|
||||
* @param cssSelector A css selector
|
||||
* @param callbackCtxt An opaque object that will be given to the callback of the `match` function
|
||||
*/
|
||||
addSelectable(cssSelector, callbackCtxt, listContext: SelectorListContext) {
|
||||
private _addSelectable(cssSelector: CssSelector, callbackCtxt: any, listContext: SelectorListContext) {
|
||||
var matcher = this;
|
||||
var element = cssSelector.element;
|
||||
var classNames = cssSelector.classNames;
|
||||
@ -184,7 +195,7 @@ export class SelectorMatcher {
|
||||
}
|
||||
|
||||
if (isPresent(classNames)) {
|
||||
for (var index = 0; index<classNames.length; index++) {
|
||||
for (var index = 0; index < classNames.length; index++) {
|
||||
var isTerminal = attrs.length === 0 && index === classNames.length - 1;
|
||||
var className = classNames[index];
|
||||
if (isTerminal) {
|
||||
@ -196,12 +207,12 @@ export class SelectorMatcher {
|
||||
}
|
||||
|
||||
if (isPresent(attrs)) {
|
||||
for (var index = 0; index<attrs.length; ) {
|
||||
for (var index = 0; index < attrs.length;) {
|
||||
var isTerminal = index === attrs.length - 2;
|
||||
var attrName = attrs[index++];
|
||||
var attrValue = attrs[index++];
|
||||
var map = isTerminal ? matcher._attrValueMap : matcher._attrValuePartialMap;
|
||||
var valuesMap = MapWrapper.get(map, attrName)
|
||||
var valuesMap = MapWrapper.get(map, attrName);
|
||||
if (isBlank(valuesMap)) {
|
||||
valuesMap = MapWrapper.create();
|
||||
MapWrapper.set(map, attrName, valuesMap);
|
||||
@ -215,8 +226,8 @@ export class SelectorMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
_addTerminal(map:Map<string,string>, name:string, selectable) {
|
||||
var terminalList = MapWrapper.get(map, name)
|
||||
private _addTerminal(map: Map<string, string>, name: string, selectable: SelectorContext) {
|
||||
var terminalList = MapWrapper.get(map, name);
|
||||
if (isBlank(terminalList)) {
|
||||
terminalList = ListWrapper.create();
|
||||
MapWrapper.set(map, name, terminalList);
|
||||
@ -224,8 +235,8 @@ export class SelectorMatcher {
|
||||
ListWrapper.push(terminalList, selectable);
|
||||
}
|
||||
|
||||
_addPartial(map:Map<string,string>, name:string) {
|
||||
var matcher = MapWrapper.get(map, name)
|
||||
private _addPartial(map: Map<string, string>, name: string) {
|
||||
var matcher = MapWrapper.get(map, name);
|
||||
if (isBlank(matcher)) {
|
||||
matcher = new SelectorMatcher();
|
||||
MapWrapper.set(map, name, matcher);
|
||||
@ -240,7 +251,7 @@ export class SelectorMatcher {
|
||||
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
|
||||
* @return boolean true if a match was found
|
||||
*/
|
||||
match(cssSelector:CssSelector, matchedCallback:Function):boolean {
|
||||
match(cssSelector: CssSelector, matchedCallback /*: (CssSelector, any) => void*/): boolean {
|
||||
var result = false;
|
||||
var element = cssSelector.element;
|
||||
var classNames = cssSelector.classNames;
|
||||
@ -251,35 +262,42 @@ export class SelectorMatcher {
|
||||
}
|
||||
|
||||
result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
|
||||
result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) || result;
|
||||
result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
|
||||
result;
|
||||
|
||||
if (isPresent(classNames)) {
|
||||
for (var index = 0; index<classNames.length; index++) {
|
||||
for (var index = 0; index < classNames.length; index++) {
|
||||
var className = classNames[index];
|
||||
result = this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
||||
result = this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) || result;
|
||||
result =
|
||||
this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
||||
result =
|
||||
this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
|
||||
result;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPresent(attrs)) {
|
||||
for (var index = 0; index<attrs.length;) {
|
||||
for (var index = 0; index < attrs.length;) {
|
||||
var attrName = attrs[index++];
|
||||
var attrValue = attrs[index++];
|
||||
|
||||
var valuesMap = MapWrapper.get(this._attrValueMap, attrName);
|
||||
if (!StringWrapper.equals(attrValue, _EMPTY_ATTR_VALUE)) {
|
||||
result = this._matchTerminal(valuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) || result;
|
||||
result =
|
||||
this._matchTerminal(valuesMap, _EMPTY_ATTR_VALUE, cssSelector, matchedCallback) ||
|
||||
result;
|
||||
}
|
||||
result = this._matchTerminal(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||
|
||||
valuesMap = MapWrapper.get(this._attrValuePartialMap, attrName)
|
||||
valuesMap = MapWrapper.get(this._attrValuePartialMap, attrName);
|
||||
result = this._matchPartial(valuesMap, attrValue, cssSelector, matchedCallback) || result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
_matchTerminal(map:Map<string,string> = null, name, cssSelector, matchedCallback):boolean {
|
||||
_matchTerminal(map: Map<string, string>, name, cssSelector: CssSelector,
|
||||
matchedCallback /*: (CssSelector, any) => void*/): boolean {
|
||||
if (isBlank(map) || isBlank(name)) {
|
||||
return false;
|
||||
}
|
||||
@ -294,18 +312,19 @@ export class SelectorMatcher {
|
||||
}
|
||||
var selectable;
|
||||
var result = false;
|
||||
for (var index=0; index<selectables.length; index++) {
|
||||
for (var index = 0; index < selectables.length; index++) {
|
||||
selectable = selectables[index];
|
||||
result = selectable.finalize(cssSelector, matchedCallback) || result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
_matchPartial(map:Map<string,string> = null, name, cssSelector, matchedCallback):boolean {
|
||||
_matchPartial(map: Map<string, string>, name, cssSelector: CssSelector,
|
||||
matchedCallback /*: (CssSelector, any) => void*/): boolean {
|
||||
if (isBlank(map) || isBlank(name)) {
|
||||
return false;
|
||||
}
|
||||
var nestedSelector = MapWrapper.get(map, name)
|
||||
var nestedSelector = MapWrapper.get(map, name);
|
||||
if (isBlank(nestedSelector)) {
|
||||
return false;
|
||||
}
|
||||
@ -321,7 +340,7 @@ class SelectorListContext {
|
||||
selectors: List<CssSelector>;
|
||||
alreadyMatched: boolean;
|
||||
|
||||
constructor(selectors:List<CssSelector>) {
|
||||
constructor(selectors: List<CssSelector>) {
|
||||
this.selectors = selectors;
|
||||
this.alreadyMatched = false;
|
||||
}
|
||||
@ -329,26 +348,27 @@ class SelectorListContext {
|
||||
|
||||
// Store context to pass back selector and context when a selector is matched
|
||||
class SelectorContext {
|
||||
selector:CssSelector;
|
||||
notSelector:CssSelector;
|
||||
cbContext; // callback context
|
||||
selector: CssSelector;
|
||||
notSelector: CssSelector;
|
||||
cbContext; // callback context
|
||||
listContext: SelectorListContext;
|
||||
|
||||
constructor(selector:CssSelector, cbContext, listContext: SelectorListContext) {
|
||||
constructor(selector: CssSelector, cbContext: any, listContext: SelectorListContext) {
|
||||
this.selector = selector;
|
||||
this.notSelector = selector.notSelector;
|
||||
this.cbContext = cbContext;
|
||||
this.listContext = listContext;
|
||||
}
|
||||
|
||||
finalize(cssSelector: CssSelector, callback) {
|
||||
finalize(cssSelector: CssSelector, callback /*: (CssSelector, any) => void*/) {
|
||||
var result = true;
|
||||
if (isPresent(this.notSelector) && (isBlank(this.listContext) || !this.listContext.alreadyMatched)) {
|
||||
var notMatcher = new SelectorMatcher();
|
||||
notMatcher.addSelectable(this.notSelector, null, null);
|
||||
if (isPresent(this.notSelector) &&
|
||||
(isBlank(this.listContext) || !this.listContext.alreadyMatched)) {
|
||||
var notMatcher = SelectorMatcher.createNotMatcher(this.notSelector);
|
||||
result = !notMatcher.match(cssSelector, null);
|
||||
}
|
||||
if (result && isPresent(callback) && (isBlank(this.listContext) || !this.listContext.alreadyMatched)) {
|
||||
if (result && isPresent(callback) &&
|
||||
(isBlank(this.listContext) || !this.listContext.alreadyMatched)) {
|
||||
if (isPresent(this.listContext)) {
|
||||
this.listContext.alreadyMatched = true;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import {Injectable} from 'angular2/src/di/annotations_impl';
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {isBlank, isPresent, BaseException, stringify} from 'angular2/src/facade/lang';
|
||||
import {Map, MapWrapper, StringMapWrapper, StringMap} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
@ -16,14 +16,14 @@ import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||
@Injectable()
|
||||
export class TemplateLoader {
|
||||
_xhr: XHR;
|
||||
_htmlCache: StringMap;
|
||||
_htmlCache: StringMap<string, /*element*/ any>;
|
||||
|
||||
constructor(xhr: XHR, urlResolver: UrlResolver) {
|
||||
this._xhr = xhr;
|
||||
this._htmlCache = StringMapWrapper.create();
|
||||
}
|
||||
|
||||
load(template: ViewDefinition):Promise {
|
||||
load(template: ViewDefinition): Promise</*element*/ any> {
|
||||
if (isPresent(template.template)) {
|
||||
return PromiseWrapper.resolve(DOM.createTemplate(template.template));
|
||||
}
|
||||
@ -32,7 +32,7 @@ export class TemplateLoader {
|
||||
var promise = StringMapWrapper.get(this._htmlCache, url);
|
||||
|
||||
if (isBlank(promise)) {
|
||||
promise = this._xhr.get(url).then(function (html) {
|
||||
promise = this._xhr.get(url).then(function(html) {
|
||||
var template = DOM.createTemplate(html);
|
||||
return template;
|
||||
});
|
||||
@ -41,7 +41,7 @@ export class TemplateLoader {
|
||||
|
||||
// We need to clone the result as others might change it
|
||||
// (e.g. the compiler).
|
||||
return promise.then( (tplElement) => DOM.clone(tplElement) );
|
||||
return promise.then((tplElement) => DOM.clone(tplElement));
|
||||
}
|
||||
|
||||
throw new BaseException('View should have either the url or template property set');
|
@ -10,21 +10,18 @@ import {CompileControl} from './compile_control';
|
||||
/**
|
||||
* Parses interpolations in direct text child nodes of the current element.
|
||||
*/
|
||||
export class TextInterpolationParser extends CompileStep {
|
||||
_parser:Parser;
|
||||
export class TextInterpolationParser implements CompileStep {
|
||||
_parser: Parser;
|
||||
|
||||
constructor(parser:Parser) {
|
||||
super();
|
||||
this._parser = parser;
|
||||
}
|
||||
constructor(parser: Parser) { this._parser = parser; }
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
if (!current.compileChildren) {
|
||||
return;
|
||||
}
|
||||
var element = current.element;
|
||||
var childNodes = DOM.childNodes(DOM.templateAwareRoot(element));
|
||||
for (var i=0; i<childNodes.length; i++) {
|
||||
for (var i = 0; i < childNodes.length; i++) {
|
||||
var node = childNodes[i];
|
||||
if (DOM.isTextNode(node)) {
|
||||
var text = DOM.nodeValue(node);
|
@ -25,28 +25,26 @@ import {dashCaseToCamelCase} from '../util';
|
||||
* as we want to do locate elements with bindings using `getElementsByClassName` later on,
|
||||
* which should not descend into the nested view.
|
||||
*/
|
||||
export class ViewSplitter extends CompileStep {
|
||||
_parser:Parser;
|
||||
export class ViewSplitter implements CompileStep {
|
||||
_parser: Parser;
|
||||
|
||||
constructor(parser:Parser) {
|
||||
super();
|
||||
this._parser = parser;
|
||||
}
|
||||
constructor(parser: Parser) { this._parser = parser; }
|
||||
|
||||
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
|
||||
process(parent: CompileElement, current: CompileElement, control: CompileControl) {
|
||||
var attrs = current.attrs();
|
||||
var templateBindings = MapWrapper.get(attrs, 'template');
|
||||
var hasTemplateBinding = isPresent(templateBindings);
|
||||
|
||||
// look for template shortcuts such as *ng-if="condition" and treat them as template="if condition"
|
||||
// look for template shortcuts such as *ng-if="condition" and treat them as template="if
|
||||
// condition"
|
||||
MapWrapper.forEach(attrs, (attrValue, attrName) => {
|
||||
if (StringWrapper.startsWith(attrName, '*')) {
|
||||
var key = StringWrapper.substring(attrName, 1); // remove the star
|
||||
if (hasTemplateBinding) {
|
||||
// 2nd template binding detected
|
||||
throw new BaseException(`Only one template directive per element is allowed: ` +
|
||||
`${templateBindings} and ${key} cannot be used simultaneously ` +
|
||||
`in ${current.elementDescription}`);
|
||||
`${templateBindings} and ${key} cannot be used simultaneously ` +
|
||||
`in ${current.elementDescription}`);
|
||||
} else {
|
||||
templateBindings = (attrValue.length == 0) ? key : key + ' ' + attrValue;
|
||||
hasTemplateBinding = true;
|
||||
@ -67,7 +65,8 @@ export class ViewSplitter extends CompileStep {
|
||||
this._moveChildNodes(DOM.content(current.element), DOM.content(viewRoot.element));
|
||||
control.addChild(viewRoot);
|
||||
}
|
||||
} if (hasTemplateBinding) {
|
||||
}
|
||||
if (hasTemplateBinding) {
|
||||
var newParent = new CompileElement(DOM.createTemplate(''));
|
||||
newParent.inheritedProtoView = current.inheritedProtoView;
|
||||
newParent.inheritedElementBinder = current.inheritedElementBinder;
|
||||
@ -102,19 +101,17 @@ export class ViewSplitter extends CompileStep {
|
||||
DOM.appendChild(newParentElement, currentElement);
|
||||
}
|
||||
|
||||
_parseTemplateBindings(templateBindings:string, compileElement:CompileElement) {
|
||||
var bindings = this._parser.parseTemplateBindings(templateBindings, compileElement.elementDescription);
|
||||
for (var i=0; i<bindings.length; i++) {
|
||||
_parseTemplateBindings(templateBindings: string, compileElement: CompileElement) {
|
||||
var bindings =
|
||||
this._parser.parseTemplateBindings(templateBindings, compileElement.elementDescription);
|
||||
for (var i = 0; i < bindings.length; i++) {
|
||||
var binding = bindings[i];
|
||||
if (binding.keyIsVar) {
|
||||
compileElement.bindElement().bindVariable(
|
||||
dashCaseToCamelCase(binding.key), binding.name
|
||||
);
|
||||
compileElement.bindElement().bindVariable(dashCaseToCamelCase(binding.key), binding.name);
|
||||
MapWrapper.set(compileElement.attrs(), binding.key, binding.name);
|
||||
} else if (isPresent(binding.expression)) {
|
||||
compileElement.bindElement().bindProperty(
|
||||
dashCaseToCamelCase(binding.key), binding.expression
|
||||
);
|
||||
compileElement.bindElement().bindProperty(dashCaseToCamelCase(binding.key),
|
||||
binding.expression);
|
||||
MapWrapper.set(compileElement.attrs(), binding.key, binding.expression.source);
|
||||
} else {
|
||||
DOM.setAttribute(compileElement.element, binding.key, '');
|
Reference in New Issue
Block a user