anchor.diff:  
     toString() => annotation.selector;
   }
  -
   
   class _ContainsSelector {
     final Directive annotation;
  
positive.code:   }
    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);
    }
  }
  
negative.code:  *
   * 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].
   */
  @Injectable()
  class DirectiveSelectorFactory {
    ElementBinderFactory _binderFactory;
  
    DirectiveSelectorFactory(this._binderFactory);
  
    DirectiveSelector selector(DirectiveMap directives) =>
        new DirectiveSelector(directives, _binderFactory);
  }
  
  class _Directive {
    final Type type;
    final Directive annotation;
  