| Index: third_party/pkg/angular/lib/core_dom/selector.dart
|
| diff --git a/third_party/pkg/angular/lib/core_dom/selector.dart b/third_party/pkg/angular/lib/core_dom/selector.dart
|
| index fec6946e86c683a223c3acbae18ceed9489b51fa..2492d0608208a7831af9a8dc56412db804a71fe3 100644
|
| --- a/third_party/pkg/angular/lib/core_dom/selector.dart
|
| +++ b/third_party/pkg/angular/lib/core_dom/selector.dart
|
| @@ -1,4 +1,4 @@
|
| -part of angular.core.dom_internal;
|
| +part of angular.core.dom;
|
|
|
| /**
|
| * DirectiveSelector is a function which given a node it will return a
|
| @@ -7,7 +7,8 @@ part of angular.core.dom_internal;
|
| * DirectiveSelector is used by the [Compiler] during the template walking
|
| * to extract the [DirectiveRef]s.
|
| *
|
| - * DirectiveSelector can be created using the [DirectiveSelectorFactory].
|
| + * DirectiveSelector can be created using the [directiveSelectorFactory]
|
| + * method.
|
| *
|
| * The DirectiveSelector supports CSS selectors which do not cross
|
| * element boundaries only. The selectors can have any mix of element-name,
|
| @@ -15,171 +16,39 @@ part of angular.core.dom_internal;
|
| *
|
| * Examples:
|
| *
|
| - * * element
|
| - * * .class
|
| - * * [attribute]
|
| - * * [attribute=value]
|
| - * * element[attribute1][attribute2=value]
|
| - * * :contains(/abc/)
|
| - */
|
| -class DirectiveSelector {
|
| - ElementBinderFactory _binderFactory;
|
| - DirectiveMap _directives;
|
| - var elementSelector;
|
| - var attrSelector;
|
| - var textSelector;
|
| -
|
| - DirectiveSelector(this._directives, this._binderFactory) {
|
| - elementSelector = new _ElementSelector('');
|
| - attrSelector = <_ContainsSelector>[];
|
| - textSelector = <_ContainsSelector>[];
|
| - _directives.forEach((Directive annotation, Type type) {
|
| - var match;
|
| - var selector = annotation.selector;
|
| - List<_SelectorPart> selectorParts;
|
| - if (selector == null) {
|
| - throw new ArgumentError('Missing selector annotation for $type');
|
| - }
|
| -
|
| - if ((match = _CONTAINS_REGEXP.firstMatch(selector)) != null) {
|
| - textSelector.add(new _ContainsSelector(annotation, match.group(1)));
|
| - } else if ((match = _ATTR_CONTAINS_REGEXP.firstMatch(selector)) != null) {
|
| - attrSelector.add(new _ContainsSelector(annotation, match[1]));
|
| - } else if ((selectorParts = _splitCss(selector, type)) != null){
|
| - elementSelector.addDirective(selectorParts,
|
| - new _Directive(type, annotation));
|
| - } else {
|
| - throw new ArgumentError('Unsupported Selector: $selector');
|
| - }
|
| - });
|
| - }
|
| -
|
| - ElementBinder matchElement(dom.Node node) {
|
| - assert(node is dom.Element);
|
| -
|
| - ElementBinderBuilder builder = _binderFactory.builder();
|
| - List<_ElementSelector> partialSelection;
|
| - var classes = <String, bool>{};
|
| - Map<String, String> attrs = {};
|
| -
|
| - dom.Element element = node;
|
| - String nodeName = element.tagName.toLowerCase();
|
| -
|
| - // Set default attribute
|
| - if (nodeName == 'input' && !element.attributes.containsKey('type')) {
|
| - element.attributes['type'] = 'text';
|
| - }
|
| -
|
| - // Select node
|
| - partialSelection = elementSelector.selectNode(builder,
|
| - partialSelection, element, nodeName);
|
| -
|
| - // Select .name
|
| - if ((element.classes) != null) {
|
| - for (var name in element.classes) {
|
| - classes[name] = true;
|
| - partialSelection = elementSelector.selectClass(builder,
|
| - partialSelection, element, name);
|
| - }
|
| - }
|
| -
|
| - // Select [attributes]
|
| - element.attributes.forEach((attrName, value) {
|
| -
|
| - if (attrName.startsWith("on-")) {
|
| - builder.onEvents[attrName] = value;
|
| - } else if (attrName.startsWith("bind-")) {
|
| - builder.bindAttrs[attrName] = value;
|
| - }
|
| -
|
| - attrs[attrName] = value;
|
| - for (var k = 0; k < attrSelector.length; k++) {
|
| - _ContainsSelector selectorRegExp = attrSelector[k];
|
| - if (selectorRegExp.regexp.hasMatch(value)) {
|
| - // this directive is matched on any attribute name, and so
|
| - // we need to pass the name to the directive by prefixing it to
|
| - // the value. Yes it is a bit of a hack.
|
| - _directives[selectorRegExp.annotation].forEach((type) {
|
| - builder.addDirective(new DirectiveRef(
|
| - node, type, selectorRegExp.annotation, '$attrName=$value'));
|
| - });
|
| - }
|
| - }
|
| -
|
| - partialSelection = elementSelector.selectAttr(builder,
|
| - partialSelection, node, attrName, value);
|
| - });
|
| -
|
| - while (partialSelection != null) {
|
| - List<_ElementSelector> elementSelectors = partialSelection;
|
| - partialSelection = null;
|
| - elementSelectors.forEach((_ElementSelector elementSelector) {
|
| - classes.forEach((className, _) {
|
| - partialSelection = elementSelector.selectClass(builder,
|
| - partialSelection, node, className);
|
| - });
|
| - attrs.forEach((attrName, value) {
|
| - partialSelection = elementSelector.selectAttr(builder,
|
| - partialSelection, node, attrName, value);
|
| - });
|
| - });
|
| - }
|
| - return builder.binder;
|
| - }
|
| -
|
| - ElementBinder matchText(dom.Node node) {
|
| - ElementBinderBuilder builder = _binderFactory.builder();
|
| -
|
| - var value = node.nodeValue;
|
| - for (var k = 0; k < textSelector.length; k++) {
|
| - var selectorRegExp = textSelector[k];
|
| - if (selectorRegExp.regexp.hasMatch(value)) {
|
| - _directives[selectorRegExp.annotation].forEach((type) {
|
| - builder.addDirective(new DirectiveRef(node, type,
|
| - selectorRegExp.annotation, value));
|
| - });
|
| - }
|
| - }
|
| - return builder.binder;
|
| - }
|
| -
|
| - ElementBinder matchComment(dom.Node node) =>
|
| - _binderFactory.builder().binder;
|
| -}
|
| -
|
| -/**
|
| - * Factory for creating a [DirectiveSelector].
|
| + * <pre>
|
| + * element
|
| + * .class
|
| + * [attribute]
|
| + * [attribute=value]
|
| + * element[attribute1][attribute2=value]
|
| + * </pre>
|
| + *
|
| + *
|
| */
|
| -@Injectable()
|
| -class DirectiveSelectorFactory {
|
| - ElementBinderFactory _binderFactory;
|
| -
|
| - DirectiveSelectorFactory(this._binderFactory);
|
| -
|
| - DirectiveSelector selector(DirectiveMap directives) =>
|
| - new DirectiveSelector(directives, _binderFactory);
|
| -}
|
| +typedef List<DirectiveRef> DirectiveSelector(dom.Node node);
|
|
|
| class _Directive {
|
| final Type type;
|
| - final Directive annotation;
|
| + final NgAnnotation annotation;
|
|
|
| _Directive(this.type, this.annotation);
|
|
|
| toString() => annotation.selector;
|
| }
|
|
|
| +
|
| class _ContainsSelector {
|
| - final Directive annotation;
|
| + final NgAnnotation annotation;
|
| final RegExp regexp;
|
|
|
| _ContainsSelector(this.annotation, String regexp)
|
| : regexp = new RegExp(regexp);
|
| }
|
|
|
| -var _SELECTOR_REGEXP = new RegExp(r'^(?:([-\w]+)|(?:\.([-\w]+))|'
|
| - r'(?:\[([-\w*]+)(?:=([^\]]*))?\]))');
|
| -var _COMMENT_COMPONENT_REGEXP = new RegExp(r'^\[([-\w]+)(?:\=(.*))?\]$');
|
| +var _SELECTOR_REGEXP = new RegExp(r'^(?:([\w\-]+)|(?:\.([\w\-]+))|'
|
| + r'(?:\[([\w\-\*]+)(?:=([^\]]*))?\]))');
|
| +var _COMMENT_COMPONENT_REGEXP = new RegExp(r'^\[([\w\-]+)(?:\=(.*))?\]$');
|
| var _CONTAINS_REGEXP = new RegExp(r'^:contains\(\/(.+)\/\)$'); //
|
| var _ATTR_CONTAINS_REGEXP = new RegExp(r'^\[\*=\/(.+)\/\]$'); //
|
|
|
| @@ -195,6 +64,7 @@ class _SelectorPart {
|
| const _SelectorPart.fromClass(this.className)
|
| : element = null, attrName = null, attrValue = null;
|
|
|
| +
|
| const _SelectorPart.fromAttribute(this.attrName, this.attrValue)
|
| : element = null, className = null;
|
|
|
| @@ -206,12 +76,6 @@ class _SelectorPart {
|
| : element;
|
| }
|
|
|
| -_addRefs(ElementBinderBuilder builder, List<_Directive> directives, dom.Node node,
|
| - [String attrValue]) {
|
| - directives.forEach((directive) {
|
| - builder.addDirective(new DirectiveRef(node, directive.type, directive.annotation, attrValue));
|
| - });
|
| -}
|
|
|
| class _ElementSelector {
|
| final String name;
|
| @@ -233,7 +97,9 @@ class _ElementSelector {
|
| var name;
|
| if ((name = selectorPart.element) != null) {
|
| if (terminal) {
|
| - elementMap.putIfAbsent(name, () => []).add(directive);
|
| + elementMap
|
| + .putIfAbsent(name, () => [])
|
| + .add(directive);
|
| } else {
|
| elementPartialMap
|
| .putIfAbsent(name, () => new _ElementSelector(name))
|
| @@ -267,13 +133,18 @@ class _ElementSelector {
|
| }
|
| }
|
|
|
| + _addRefs(List<DirectiveRef> refs, List<_Directive> directives, dom.Node node,
|
| + [String attrValue]) {
|
| + directives.forEach((directive) =>
|
| + refs.add(new DirectiveRef(node, directive.type, directive.annotation,
|
| + attrValue)));
|
| + }
|
|
|
| -
|
| - List<_ElementSelector> selectNode(ElementBinderBuilder builder,
|
| + List<_ElementSelector> selectNode(List<DirectiveRef> refs,
|
| List<_ElementSelector> partialSelection,
|
| dom.Node node, String nodeName) {
|
| if (elementMap.containsKey(nodeName)) {
|
| - _addRefs(builder, elementMap[nodeName], node);
|
| + _addRefs(refs, elementMap[nodeName], node);
|
| }
|
| if (elementPartialMap.containsKey(nodeName)) {
|
| if (partialSelection == null) {
|
| @@ -284,11 +155,11 @@ class _ElementSelector {
|
| return partialSelection;
|
| }
|
|
|
| - List<_ElementSelector> selectClass(ElementBinderBuilder builder,
|
| + List<_ElementSelector> selectClass(List<DirectiveRef> refs,
|
| List<_ElementSelector> partialSelection,
|
| dom.Node node, String className) {
|
| if (classMap.containsKey(className)) {
|
| - _addRefs(builder, classMap[className], node);
|
| + _addRefs(refs, classMap[className], node);
|
| }
|
| if (classPartialMap.containsKey(className)) {
|
| if (partialSelection == null) {
|
| @@ -299,7 +170,7 @@ class _ElementSelector {
|
| return partialSelection;
|
| }
|
|
|
| - List<_ElementSelector> selectAttr(ElementBinderBuilder builder,
|
| + List<_ElementSelector> selectAttr(List<DirectiveRef> refs,
|
| List<_ElementSelector> partialSelection,
|
| dom.Node node, String attrName,
|
| String attrValue) {
|
| @@ -309,10 +180,10 @@ class _ElementSelector {
|
| if (matchingKey != null) {
|
| Map<String, List<_Directive>> valuesMap = attrValueMap[matchingKey];
|
| if (valuesMap.containsKey('')) {
|
| - _addRefs(builder, valuesMap[''], node, attrValue);
|
| + _addRefs(refs, valuesMap[''], node, attrValue);
|
| }
|
| if (attrValue != '' && valuesMap.containsKey(attrValue)) {
|
| - _addRefs(builder, valuesMap[attrValue], node, attrValue);
|
| + _addRefs(refs, valuesMap[attrValue], node, attrValue);
|
| }
|
| }
|
| if (attrValuePartialMap.containsKey(attrName)) {
|
| @@ -334,14 +205,9 @@ class _ElementSelector {
|
| return partialSelection;
|
| }
|
|
|
| - // A global cache for the _matchingKey RegExps. The size is bounded by
|
| - // the number of attribute directive selectors used in the application.
|
| - static var _matchingKeyCache = <String, RegExp>{};
|
| -
|
| String _matchingKey(Iterable<String> keys, String attrName) =>
|
| keys.firstWhere((key) =>
|
| - _matchingKeyCache.putIfAbsent(key,
|
| - () => new RegExp('^${key.replaceAll('*', r'[\w\-]+')}\$'))
|
| + new RegExp('^${key.replaceAll('*', r'[\w\-]+')}\$')
|
| .hasMatch(attrName), orElse: () => null);
|
|
|
| toString() => 'ElementSelector($name)';
|
| @@ -371,3 +237,128 @@ List<_SelectorPart> _splitCss(String selector, Type type) {
|
| }
|
| return parts;
|
| }
|
| +
|
| +/**
|
| + * Factory method for creating a [DirectiveSelector].
|
| + */
|
| +DirectiveSelector directiveSelectorFactory(DirectiveMap directives) {
|
| +
|
| + var elementSelector = new _ElementSelector('');
|
| + var attrSelector = <_ContainsSelector>[];
|
| + var textSelector = <_ContainsSelector>[];
|
| + directives.forEach((NgAnnotation annotation, Type type) {
|
| + var match;
|
| + var selector = annotation.selector;
|
| + List<_SelectorPart> selectorParts;
|
| + if (selector == null) {
|
| + throw new ArgumentError('Missing selector annotation for $type');
|
| + }
|
| +
|
| + if ((match = _CONTAINS_REGEXP.firstMatch(selector)) != null) {
|
| + textSelector.add(new _ContainsSelector(annotation, match.group(1)));
|
| + } else if ((match = _ATTR_CONTAINS_REGEXP.firstMatch(selector)) != null) {
|
| + attrSelector.add(new _ContainsSelector(annotation, match[1]));
|
| + } else if ((selectorParts = _splitCss(selector, type)) != null){
|
| + elementSelector.addDirective(selectorParts,
|
| + new _Directive(type, annotation));
|
| + } else {
|
| + throw new ArgumentError('Unsupported Selector: $selector');
|
| + }
|
| + });
|
| +
|
| + return (dom.Node node) {
|
| + var directiveRefs = <DirectiveRef>[];
|
| + List<_ElementSelector> partialSelection;
|
| + var classes = <String, bool>{};
|
| + var attrs = <String, String>{};
|
| +
|
| + switch(node.nodeType) {
|
| + case 1: // Element
|
| + dom.Element element = node;
|
| + String nodeName = element.tagName.toLowerCase();
|
| + Map<String, String> attrs = {};
|
| +
|
| + // Set default attribute
|
| + if (nodeName == 'input' && !element.attributes.containsKey('type')) {
|
| + element.attributes['type'] = 'text';
|
| + }
|
| +
|
| + // Select node
|
| + partialSelection = elementSelector.selectNode(directiveRefs,
|
| + partialSelection, element, nodeName);
|
| +
|
| + // Select .name
|
| + if ((element.classes) != null) {
|
| + for (var name in element.classes) {
|
| + classes[name] = true;
|
| + partialSelection = elementSelector.selectClass(directiveRefs,
|
| + partialSelection, element, name);
|
| + }
|
| + }
|
| +
|
| + // Select [attributes]
|
| + element.attributes.forEach((attrName, value) {
|
| + attrs[attrName] = value;
|
| + for (var k = 0; k < attrSelector.length; k++) {
|
| + _ContainsSelector selectorRegExp = attrSelector[k];
|
| + if (selectorRegExp.regexp.hasMatch(value)) {
|
| + // this directive is matched on any attribute name, and so
|
| + // we need to pass the name to the directive by prefixing it to
|
| + // the value. Yes it is a bit of a hack.
|
| + directives[selectorRegExp.annotation].forEach((type) {
|
| + directiveRefs.add(new DirectiveRef(
|
| + node, type, selectorRegExp.annotation, '$attrName=$value'));
|
| + });
|
| + }
|
| + }
|
| +
|
| + partialSelection = elementSelector.selectAttr(directiveRefs,
|
| + partialSelection, node, attrName, value);
|
| + });
|
| +
|
| + while(partialSelection != null) {
|
| + List<_ElementSelector> elementSelectors = partialSelection;
|
| + partialSelection = null;
|
| + elementSelectors.forEach((_ElementSelector elementSelector) {
|
| + classes.forEach((className, _) {
|
| + partialSelection = elementSelector.selectClass(directiveRefs,
|
| + partialSelection, node, className);
|
| + });
|
| + attrs.forEach((attrName, value) {
|
| + partialSelection = elementSelector.selectAttr(directiveRefs,
|
| + partialSelection, node, attrName, value);
|
| + });
|
| + });
|
| + }
|
| + break;
|
| + case 3: // Text Node
|
| + var value = node.nodeValue;
|
| + for (var k = 0; k < textSelector.length; k++) {
|
| + var selectorRegExp = textSelector[k];
|
| +
|
| + if (selectorRegExp.regexp.hasMatch(value)) {
|
| + directives[selectorRegExp.annotation].forEach((type) {
|
| + directiveRefs.add(new DirectiveRef(node, type,
|
| + selectorRegExp.annotation, value));
|
| + });
|
| + }
|
| + }
|
| + break;
|
| + }
|
| +
|
| + directiveRefs.sort(_priorityComparator);
|
| + return directiveRefs;
|
| + };
|
| +}
|
| +
|
| +int _directivePriority(NgAnnotation annotation) {
|
| + if (annotation is NgDirective) {
|
| + return (annotation.children == NgAnnotation.TRANSCLUDE_CHILDREN) ? 2 : 1;
|
| + } else if (annotation is NgComponent) {
|
| + return 0;
|
| + }
|
| + throw "Unexpected Type: ${annotation}.";
|
| +}
|
| +
|
| +int _priorityComparator(DirectiveRef a, DirectiveRef b) =>
|
| + _directivePriority(b.annotation) - _directivePriority(a.annotation);
|
|
|