Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

style(Selector): Move public API on top of the file #939

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
300 changes: 145 additions & 155 deletions lib/core_dom/selector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,159 @@ 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]
* method.
* DirectiveSelector can be created using the [DirectiveSelectorFactory].
*
* The DirectiveSelector supports CSS selectors which do not cross
* element boundaries only. The selectors can have any mix of element-name,
* class-names and attribute-names.
*
* Examples:
*
* <pre>
* element
* .class
* [attribute]
* [attribute=value]
* element[attribute1][attribute2=value]
* :contains(/abc/)
* </pre>
*
*
* * 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].
*/
@Injectable()
class DirectiveSelectorFactory {
ElementBinderFactory _binderFactory;

DirectiveSelectorFactory(this._binderFactory);

DirectiveSelector selector(DirectiveMap directives) =>
new DirectiveSelector(directives, _binderFactory);
}

class _Directive {
final Type type;
final Directive annotation;
Expand All @@ -36,7 +169,6 @@ class _Directive {
toString() => annotation.selector;
}


class _ContainsSelector {
final Directive annotation;
final RegExp regexp;
Expand Down Expand Up @@ -239,145 +371,3 @@ List<_SelectorPart> _splitCss(String selector, Type type) {
}
return parts;
}


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;
}

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) {
return _binderFactory.builder().binder;
}
}
/**
* Factory for creating a [DirectiveSelector].
*/
@Injectable()
class DirectiveSelectorFactory {
ElementBinderFactory _binderFactory;

DirectiveSelectorFactory(this._binderFactory);

DirectiveSelector selector(DirectiveMap directives) {
return new DirectiveSelector(directives, _binderFactory);
}
}