| // 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 extends SetBase<String> implements CssClassSet { |
| static final RegExp _validTokenRE = new RegExp(r'^\S+$'); |
| |
| String _validateToken(String value) { |
| if (_validTokenRE.hasMatch(value)) return value; |
| throw new ArgumentError.value(value, 'value', 'Not a valid class token'); |
| } |
| |
| 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]) { |
| _validateToken(value); |
| 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<T> map<T>(T f(String e)) => readClasses().map<T>(f); |
| |
| Iterable<String> where(bool f(String element)) => readClasses().where(f); |
| |
| Iterable<T> expand<T>(Iterable<T> f(String element)) => |
| readClasses().expand<T>(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); |
| } |
| |
| T fold<T>(T initialValue, T combine(T previousValue, String element)) { |
| return readClasses().fold<T>(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(Object? value) { |
| if (value is! String) return false; |
| _validateToken(value); |
| return readClasses().contains(value); |
| } |
| |
| /** Lookup from the Set interface. Not interesting for a String set. */ |
| String? lookup(Object? value) => contains(value) ? value as String : 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) { |
| _validateToken(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)) ?? false; |
| } |
| |
| /** |
| * 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; |
| _validateToken(value); |
| 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.map(_validateToken))); |
| } |
| |
| /** |
| * 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<Object?> 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<Object?> 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<Object?> collection) => |
| readClasses().containsAll(collection); |
| |
| Set<String> intersection(Set<Object?> other) => |
| readClasses().intersection(other); |
| |
| Set<String> union(Set<String> other) => readClasses().union(other); |
| |
| Set<String> difference(Set<Object?> 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); |
| String firstWhere(bool test(String value), {String orElse()?}) => |
| readClasses().firstWhere(test, orElse: orElse); |
| String lastWhere(bool test(String value), {String orElse()?}) => |
| readClasses().lastWhere(test, orElse: orElse); |
| String singleWhere(bool test(String value), {String orElse()?}) => |
| readClasses().singleWhere(test, orElse: orElse); |
| String elementAt(int index) => readClasses().elementAt(index); |
| |
| void clear() { |
| // TODO(sra): Do this without reading the classes. |
| 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); |
| } |