content: // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
  // for details. All rights reserved. Use of this source code is governed by a
  // BSD-style license that can be found in the LICENSE file.
  
  part of html_common;
  
  abstract class CssClassSetImpl implements CssClassSet {
  
    String toString() {
      return readClasses().join(' ');
    }
  
    /**
     * Adds the class [value] to the element if it is not on it, removes it if it
     * is.
     *
     * If [shouldAdd] is true, then we always add that [value] to the element. If
     * [shouldAdd] is false then we always remove [value] from the element.
     */
    bool toggle(String value, [bool shouldAdd]) {
      Set<String> s = readClasses();
      bool result = false;
      if (shouldAdd == null) shouldAdd = !s.contains(value);
      if (shouldAdd) {
        s.add(value);
        result = true;
      } else {
        s.remove(value);
      }
      writeClasses(s);
      return result;
    }
  
    /**
     * Returns [:true:] if classes cannot be added or removed from this
     * [:CssClassSet:].
     */
    bool get frozen => false;
  
    // interface Iterable - BEGIN
    Iterator<String> get iterator => readClasses().iterator;
    // interface Iterable - END
  
    // interface Collection - BEGIN
    void forEach(void f(String element)) {
      readClasses().forEach(f);
    }
  
    String join([String separator = ""]) => readClasses().join(separator);
  
    Iterable map(f(String element)) => readClasses().map(f);
  
    Iterable<String> where(bool f(String element)) => readClasses().where(f);
  
    Iterable expand(Iterable f(String element)) => readClasses().expand(f);
  
    bool every(bool f(String element)) => readClasses().every(f);
  
    bool any(bool f(String element)) => readClasses().any(f);
  
    bool get isEmpty => readClasses().isEmpty;
  
    bool get isNotEmpty => readClasses().isNotEmpty;
  
    int get length => readClasses().length;
  
    String reduce(String combine(String value, String element)) {
      return readClasses().reduce(combine);
    }
  
    dynamic fold(dynamic initialValue,
        dynamic combine(dynamic previousValue, String element)) {
      return readClasses().fold(initialValue, combine);
    }
    // interface Collection - END
  
    // interface Set - BEGIN
    /**
     * Determine if this element contains the class [value].
     *
     * This is the Dart equivalent of jQuery's
     * [hasClass](http://api.jquery.com/hasClass/).
     */
    bool contains(String value) => readClasses().contains(value);
  
    /** Lookup from the Set interface. Not interesting for a String set. */
    String lookup(String value) => contains(value) ? value : null;
  
    /**
     * Add the class [value] to element.
     *
     * This is the Dart equivalent of jQuery's
     * [addClass](http://api.jquery.com/addClass/).
     */
    bool add(String value) {
      // TODO - figure out if we need to do any validation here
      // or if the browser natively does enough.
      return modify((s) => s.add(value));
    }
  
    /**
     * Remove the class [value] from element, and return true on successful
     * removal.
     *
     * This is the Dart equivalent of jQuery's
     * [removeClass](http://api.jquery.com/removeClass/).
     */
    bool remove(Object value) {
      if (value is! String) return false;
      Set<String> s = readClasses();
      bool result = s.remove(value);
      writeClasses(s);
      return result;
    }
  
    /**
     * Add all classes specified in [iterable] to element.
     *
     * This is the Dart equivalent of jQuery's
     * [addClass](http://api.jquery.com/addClass/).
     */
    void addAll(Iterable<String> iterable) {
      // TODO - see comment above about validation.
      modify((s) => s.addAll(iterable));
    }
  
    /**
     * Remove all classes specified in [iterable] from element.
     *
     * This is the Dart equivalent of jQuery's
     * [removeClass](http://api.jquery.com/removeClass/).
     */
    void removeAll(Iterable<String> iterable) {
      modify((s) => s.removeAll(iterable));
    }
  
    /**
     * Toggles all classes specified in [iterable] on element.
     *
     * Iterate through [iterable]'s items, and add it if it is not on it, or
     * remove it if it is. This is the Dart equivalent of jQuery's
     * [toggleClass](http://api.jquery.com/toggleClass/).
     * If [shouldAdd] is true, then we always add all the classes in [iterable]
     * element. If [shouldAdd] is false then we always remove all the classes in
     * [iterable] from the element.
     */
    void toggleAll(Iterable<String> iterable, [bool shouldAdd]) {
      iterable.forEach((e) => toggle(e, shouldAdd));
    }
  
    void retainAll(Iterable<String> iterable) {
      modify((s) => s.retainAll(iterable));
    }
  
    void removeWhere(bool test(String name)) {
      modify((s) => s.removeWhere(test));
    }
  
    void retainWhere(bool test(String name)) {
      modify((s) => s.retainWhere(test));
    }
  
    bool containsAll(Iterable<String> collection) =>
      readClasses().containsAll(collection);
  
    Set<String> intersection(Set<String> other) =>
      readClasses().intersection(other);
  
    Set<String> union(Set<String> other) =>
      readClasses().union(other);
  
    Set<String> difference(Set<String> other) =>
      readClasses().difference(other);
  
    String get first => readClasses().first;
    String get last => readClasses().last;
    String get single => readClasses().single;
    List<String> toList({ bool growable: true }) =>
        readClasses().toList(growable: growable);
    Set<String> toSet() => readClasses().toSet();
    Iterable<String> take(int n) => readClasses().take(n);
    Iterable<String> takeWhile(bool test(String value)) =>
        readClasses().takeWhile(test);
    Iterable<String> skip(int n) => readClasses().skip(n);
    Iterable<String> skipWhile(bool test(String value)) =>
        readClasses().skipWhile(test);
    dynamic firstWhere(bool test(String value), { Object orElse() }) =>
        readClasses().firstWhere(test, orElse: orElse);
    dynamic lastWhere(bool test(String value), { Object orElse()}) =>
        readClasses().lastWhere(test, orElse: orElse);
    String singleWhere(bool test(String value)) =>
        readClasses().singleWhere(test);
    String elementAt(int index) => readClasses().elementAt(index);
  
    void clear() {
      modify((s) => s.clear());
    }
    // interface Set - END
  
    /**
     * Helper method used to modify the set of css classes on this element.
     *
     *   f - callback with:
     *   s - a Set of all the css class name currently on this element.
     *
     *   After f returns, the modified set is written to the
     *       className property of this element.
     */
    modify( f(Set<String> s)) {
      Set<String> s = readClasses();
      var ret = f(s);
      writeClasses(s);
      return ret;
    }
  
    /**
     * Read the class names from the Element class property,
     * and put them into a set (duplicates are discarded).
     * This is intended to be overridden by specific implementations.
     */
    Set<String> readClasses();
  
    /**
     * Join all the elements of a set into one string and write
     * back to the element.
     * This is intended to be overridden by specific implementations.
     */
    void writeClasses(Set<String> s);
  }
  