| Index: charted/lib/selection/src/selection_impl.dart
|
| diff --git a/charted/lib/selection/src/selection_impl.dart b/charted/lib/selection/src/selection_impl.dart
|
| deleted file mode 100644
|
| index 8604da773ffc38df7dd8087671452742448b39bd..0000000000000000000000000000000000000000
|
| --- a/charted/lib/selection/src/selection_impl.dart
|
| +++ /dev/null
|
| @@ -1,542 +0,0 @@
|
| -/*
|
| - * Copyright 2014 Google Inc. All rights reserved.
|
| - *
|
| - * Use of this source code is governed by a BSD-style
|
| - * license that can be found in the LICENSE file or at
|
| - * https://developers.google.com/open-source/licenses/bsd
|
| - */
|
| -part of charted.selection;
|
| -
|
| -/**
|
| - * Implementation of [Selection].
|
| - * Selections cannot be created directly - they are only created using
|
| - * the select or selectAll methods on [SelectionScope] and [Selection].
|
| - */
|
| -class _SelectionImpl implements Selection {
|
| -
|
| - Iterable<SelectionGroup> groups;
|
| - SelectionScope scope;
|
| -
|
| - /**
|
| - * Creates a new selection.
|
| - *
|
| - * When [source] is not specified, the new selection would have exactly
|
| - * one group with [SelectionScope.root] as it's parent. Otherwise, one group
|
| - * per for each non-null element is created with element as it's parent.
|
| - *
|
| - * When [selector] is specified, each group contains all elements matching
|
| - * [selector] and under the group's parent element. Otherwise, [fn] is
|
| - * called once per group with parent element's "data", "index" and the
|
| - * "element" itself passed as parameters. [fn] must return an iterable of
|
| - * elements to be used in each group.
|
| - */
|
| - _SelectionImpl.all({String selector, SelectionCallback<Iterable<Element>> fn,
|
| - SelectionScope this.scope, Selection source}) {
|
| - assert(selector != null || fn != null);
|
| - assert(source != null || scope != null);
|
| -
|
| - if (selector != null) {
|
| - fn = (d, i, c) => c == null ?
|
| - scope.root.querySelectorAll(selector) :
|
| - c.querySelectorAll(selector);
|
| - }
|
| -
|
| - var tmpGroups = new List<SelectionGroup>();
|
| - if (source != null) {
|
| - scope = source.scope;
|
| - for (int gi = 0; gi < source.groups.length; ++gi) {
|
| - final g = source.groups.elementAt(gi);
|
| - for (int ei = 0; ei < g.elements.length; ++ei) {
|
| - final e = g.elements.elementAt(ei);
|
| - if (e != null) {
|
| - tmpGroups.add(
|
| - new _SelectionGroupImpl(
|
| - fn(scope.datum(e), gi, e), parent: e));
|
| - }
|
| - }
|
| - }
|
| - } else {
|
| - tmpGroups.add(
|
| - new _SelectionGroupImpl(fn(null, 0, null), parent: scope.root));
|
| - }
|
| - groups = tmpGroups;
|
| - }
|
| -
|
| - /**
|
| - * Same as [all] but only uses the first element matching [selector] when
|
| - * [selector] is specified. Otherwise, call [fn] which must return the
|
| - * element to be selected.
|
| - */
|
| - _SelectionImpl.single({String selector, SelectionCallback<Element> fn,
|
| - SelectionScope this.scope, Selection source}) {
|
| - assert(selector != null || fn != null);
|
| - assert(source != null || scope != null);
|
| -
|
| - if (selector != null) {
|
| - fn = (d, i, c) => c == null ?
|
| - scope.root.querySelector(selector) :
|
| - c.querySelector(selector);
|
| - }
|
| -
|
| - if (source != null) {
|
| - scope = source.scope;
|
| - groups = new List<SelectionGroup>.generate(source.groups.length, (gi) {
|
| - SelectionGroup g = source.groups.elementAt(gi);
|
| - return new _SelectionGroupImpl(
|
| - new List.generate(g.elements.length, (ei) {
|
| - var e = g.elements.elementAt(ei);
|
| - if (e != null) {
|
| - var datum = scope.datum(e);
|
| - var enterElement = fn(datum, ei, e);
|
| - if (datum != null) {
|
| - scope.associate(enterElement, datum);
|
| - }
|
| - return enterElement;
|
| - } else {
|
| - return null;
|
| - }
|
| - }), parent: g.parent);
|
| - });
|
| - } else {
|
| - groups = new List<SelectionGroup>.generate(1,
|
| - (_) => new _SelectionGroupImpl(new List.generate(1,
|
| - (_) => fn(null, 0, null), growable: false)), growable: false);
|
| - }
|
| - }
|
| -
|
| - /** Creates a selection using the pre-computed list of [SelectionGroup] */
|
| - _SelectionImpl.selectionGroups(
|
| - Iterable<SelectionGroup> this.groups, SelectionScope this.scope);
|
| -
|
| - /**
|
| - * Creates a selection using the list of elements. All elements will
|
| - * be part of the same group, with [SelectionScope.root] as the group's parent
|
| - */
|
| - _SelectionImpl.elements(Iterable elements, SelectionScope this.scope) {
|
| - groups = new List<SelectionGroup>()
|
| - ..add(new _SelectionGroupImpl(elements));
|
| - }
|
| -
|
| - /**
|
| - * Utility to evaluate value of parameters (uses value when given
|
| - * or invokes a callback to get the value) and calls [action] for
|
| - * each non-null element in this selection
|
| - */
|
| - void _do(SelectionCallback f, Function action) {
|
| - each((d, i, e) => action(e, f == null ? null : f(scope.datum(e), i, e)));
|
| - }
|
| -
|
| - /** Calls a function on each non-null element in the selection */
|
| - void each(SelectionCallback fn) {
|
| - if (fn == null) return;
|
| - for (int gi = 0, gLen = groups.length; gi < gLen; ++gi) {
|
| - final g = groups.elementAt(gi);
|
| - for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) {
|
| - final e = g.elements.elementAt(ei);
|
| - if (e != null) fn(scope.datum(e), ei, e);
|
| - }
|
| - }
|
| - }
|
| -
|
| - void on(String type, [SelectionCallback listener, bool capture]) {
|
| - Function getEventHandler(i, e) => (Event event) {
|
| - var previous = scope.event;
|
| - scope.event = event;
|
| - try {
|
| - listener(scope.datum(e), i, e);
|
| - } finally {
|
| - scope.event = previous;
|
| - }
|
| - };
|
| -
|
| - if (!type.startsWith('.')) {
|
| - if (listener != null) {
|
| - // Add a listener to each element.
|
| - each((d, i, Element e){
|
| - var handlers = scope._listeners[e];
|
| - if (handlers == null) scope._listeners[e] = handlers = {};
|
| - handlers[type] = new Pair(getEventHandler(i, e), capture);
|
| - e.addEventListener(type, handlers[type].first, capture);
|
| - });
|
| - } else {
|
| - // Remove the listener from each element.
|
| - each((d, i, Element e) {
|
| - var handlers = scope._listeners[e];
|
| - if (handlers != null && handlers[type] != null) {
|
| - e.removeEventListener(
|
| - type, handlers[type].first, handlers[type].last);
|
| - }
|
| - });
|
| - }
|
| - } else {
|
| - // Remove all listeners on the event type (ignoring the namespace)
|
| - each((d, i, Element e) {
|
| - var handlers = scope._listeners[e],
|
| - t = type.substring(1);
|
| - handlers.forEach((String s, Pair<Function, bool> value) {
|
| - if (s.split('.')[0] == t) {
|
| - e.removeEventListener(s, value.first, value.last);
|
| - }
|
| - });
|
| - });
|
| - }
|
| - }
|
| -
|
| - int get length {
|
| - int retval = 0;
|
| - each((d, i, e) => retval++);
|
| - return retval;
|
| - }
|
| -
|
| - bool get isEmpty => length == 0;
|
| -
|
| - /** First non-null element in this selection */
|
| - Element get first {
|
| - for (int gi = 0; gi < groups.length; gi++) {
|
| - SelectionGroup g = groups.elementAt(gi);
|
| - for (int ei = 0; ei < g.elements.length; ei++) {
|
| - if (g.elements.elementAt(ei) != null) {
|
| - return g.elements.elementAt(ei);
|
| - }
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - void attr(String name, val) {
|
| - assert(name != null && name.isNotEmpty);
|
| - attrWithCallback(name, toCallback(val));
|
| - }
|
| -
|
| - void attrWithCallback(String name, SelectionCallback fn) {
|
| - assert(fn != null);
|
| - _do(fn, (e, v) => v == null ?
|
| - e.attributes.remove(name) : e.attributes[name] = "$v");
|
| - }
|
| -
|
| - void classed(String name, [bool val = true]) {
|
| - assert(name != null && name.isNotEmpty);
|
| - classedWithCallback(name, toCallback(val));
|
| - }
|
| -
|
| - void classedWithCallback(String name, SelectionCallback<bool> fn) {
|
| - assert(fn != null);
|
| - _do(fn, (e, v) =>
|
| - v == false ? e.classes.remove(name) : e.classes.add(name));
|
| - }
|
| -
|
| - void style(String property, val, {String priority}) {
|
| - assert(property != null && property.isNotEmpty);
|
| - styleWithCallback(property,
|
| - toCallback(val as String), priority: priority);
|
| - }
|
| -
|
| - void styleWithCallback(String property,
|
| - SelectionCallback<String> fn, {String priority}) {
|
| - assert(fn != null);
|
| - _do(fn, (Element e, String v) =>
|
| - v == null || v.isEmpty ?
|
| - e.style.removeProperty(property) :
|
| - e.style.setProperty(property, v, priority));
|
| - }
|
| -
|
| - void text(String val) => textWithCallback(toCallback(val));
|
| -
|
| - void textWithCallback(SelectionCallback<String> fn) {
|
| - assert(fn != null);
|
| - _do(fn, (e, v) => e.text = v == null ? '' : v);
|
| - }
|
| -
|
| - void html(String val) => htmlWithCallback(toCallback(val));
|
| -
|
| - void htmlWithCallback(SelectionCallback<String> fn) {
|
| - assert(fn != null);
|
| - _do(fn, (e, v) => e.innerHtml = v == null ? '' : v);
|
| - }
|
| -
|
| - void remove() => _do(null, (e, _) => e.remove());
|
| -
|
| - Selection select(String selector) {
|
| - assert(selector != null && selector.isNotEmpty);
|
| - return new _SelectionImpl.single(selector: selector, source: this);
|
| - }
|
| -
|
| - Selection selectWithCallback(SelectionCallback<Element> fn) {
|
| - assert(fn != null);
|
| - return new _SelectionImpl.single(fn: fn, source:this);
|
| - }
|
| -
|
| - Selection append(String tag) {
|
| - assert(tag != null && tag.isNotEmpty);
|
| - return appendWithCallback(
|
| - (d, ei, e) => Namespace.createChildElement(tag, e));
|
| - }
|
| -
|
| - Selection appendWithCallback(SelectionCallback<Element> fn) {
|
| - assert(fn != null);
|
| - return new _SelectionImpl.single(fn: (datum, ei, e) {
|
| - Element child = fn(datum, ei, e);
|
| - return child == null ? null : e.append(child);
|
| - }, source: this);
|
| - }
|
| -
|
| - Selection insert(String tag,
|
| - {String before, SelectionCallback<Element> beforeFn}) {
|
| - assert(tag != null && tag.isNotEmpty);
|
| - return insertWithCallback(
|
| - (d, ei, e) => Namespace.createChildElement(tag, e),
|
| - before: before, beforeFn: beforeFn);
|
| - }
|
| -
|
| - Selection insertWithCallback(SelectionCallback<Element> fn,
|
| - {String before, SelectionCallback<Element> beforeFn}) {
|
| - assert(fn != null);
|
| - beforeFn =
|
| - before == null ? beforeFn : (d, ei, e) => e.querySelector(before);
|
| - return new _SelectionImpl.single(
|
| - fn: (datum, ei, e) {
|
| - Element child = fn(datum, ei, e);
|
| - Element before = beforeFn(datum, ei, e);
|
| - return child == null ? null : e.insertBefore(child, before);
|
| - },
|
| - source: this);
|
| - }
|
| -
|
| - Selection selectAll(String selector) {
|
| - assert(selector != null && selector.isNotEmpty);
|
| - return new _SelectionImpl.all(selector: selector, source: this);
|
| - }
|
| -
|
| - Selection selectAllWithCallback(SelectionCallback<Iterable<Element>> fn) {
|
| - assert(fn != null);
|
| - return new _SelectionImpl.all(fn: fn, source:this);
|
| - }
|
| -
|
| - DataSelection data(Iterable vals, [SelectionKeyFunction keyFn]) {
|
| - assert(vals != null);
|
| - return dataWithCallback(toCallback(vals), keyFn);
|
| - }
|
| -
|
| - DataSelection dataWithCallback(
|
| - SelectionCallback<Iterable> fn, [SelectionKeyFunction keyFn]) {
|
| - assert(fn != null);
|
| -
|
| - var enterGroups = [],
|
| - updateGroups = [],
|
| - exitGroups = [];
|
| -
|
| - // Create a dummy node to be used with enter() selection.
|
| - Object dummy(val) {
|
| - var element = new Object();
|
| - scope.associate(element, val);
|
| - return element;
|
| - };
|
| -
|
| - // Joins data to all elements in the group.
|
| - void join(SelectionGroup g, Iterable vals) {
|
| - final int valuesLength = vals.length;
|
| - final int elementsLength = g.elements.length;
|
| -
|
| - // Nodes exiting, entering and updating in this group.
|
| - // We maintain the nodes at the same index as they currently
|
| - // are (for exiting) or where they should be (for entering and updating)
|
| - var update = new List(valuesLength),
|
| - enter = new List(valuesLength),
|
| - exit = new List(elementsLength);
|
| -
|
| - // Use key function to determine DOMElement to data associations.
|
| - if (keyFn != null) {
|
| - var keysOnDOM = [],
|
| - elementsByKey = {},
|
| - valuesByKey = {};
|
| -
|
| - // Create a key to DOM element map.
|
| - // Used later to see if an element already exists for a key.
|
| - for (int ei = 0, len = elementsLength; ei < len; ++ei) {
|
| - final e = g.elements.elementAt(ei);
|
| - var keyValue = keyFn(scope.datum(e));
|
| - if (elementsByKey.containsKey(keyValue)) {
|
| - exit[ei] = e;
|
| - } else {
|
| - elementsByKey[keyValue] = e;
|
| - }
|
| - keysOnDOM.add(keyValue);
|
| - }
|
| -
|
| - // Iterate through the values and find values that don't have
|
| - // corresponding elements in the DOM, collect the entering elements.
|
| - for (int vi = 0, len = valuesLength; vi < len; ++vi) {
|
| - final v = vals.elementAt(vi);
|
| - var keyValue = keyFn(v);
|
| - Element e = elementsByKey[keyValue];
|
| - if (e != null) {
|
| - update[vi] = e;
|
| - scope.associate(e, v);
|
| - } else if (!valuesByKey.containsKey(keyValue)) {
|
| - enter[vi] = dummy(v);
|
| - }
|
| - valuesByKey[keyValue] = v;
|
| - elementsByKey.remove(keyValue);
|
| - }
|
| -
|
| - // Iterate through the previously saved keys to
|
| - // find a list of elements that don't have data anymore.
|
| - // We don't use elementsByKey.keys() because that does not
|
| - // guarantee the order of returned keys.
|
| - for (int i = 0, len = elementsLength; i < len; ++i) {
|
| - if (elementsByKey.containsKey(keysOnDOM[i])) {
|
| - exit[i] = g.elements.elementAt(i);
|
| - }
|
| - }
|
| - } else {
|
| - // When we don't have the key function, just use list index as the key
|
| - int updateElementsCount = math.min(elementsLength, valuesLength);
|
| - int i = 0;
|
| -
|
| - // Collect a list of elements getting updated in this group
|
| - for (int len = updateElementsCount; i < len; ++i) {
|
| - var e = g.elements.elementAt(i);
|
| - if (e != null) {
|
| - scope.associate(e, vals.elementAt(i));
|
| - update[i] = e;
|
| - } else {
|
| - enter[i] = dummy(vals.elementAt(i));
|
| - }
|
| - }
|
| -
|
| - // List of elements newly getting added
|
| - for (int len = valuesLength; i < len; ++i) {
|
| - enter[i] = dummy(vals.elementAt(i));
|
| - }
|
| -
|
| - // List of elements exiting this group
|
| - for (int len = elementsLength; i < len; ++i) {
|
| - exit[i] = g.elements.elementAt(i);
|
| - }
|
| - }
|
| -
|
| - // Create the element groups and set parents from the current group.
|
| - enterGroups.add(new _SelectionGroupImpl(enter, parent: g.parent));
|
| - updateGroups.add(new _SelectionGroupImpl(update, parent: g.parent));
|
| - exitGroups.add(new _SelectionGroupImpl(exit, parent: g.parent));
|
| - };
|
| -
|
| - for (int gi = 0; gi < groups.length; ++gi) {
|
| - final g = groups.elementAt(gi);
|
| - join(g, fn(scope.datum(g.parent), gi, g.parent));
|
| - }
|
| -
|
| - return new _DataSelectionImpl(
|
| - updateGroups, enterGroups, exitGroups, scope);
|
| - }
|
| -
|
| - void datum(Iterable vals) {
|
| - throw new UnimplementedError();
|
| - }
|
| -
|
| - void datumWithCallback(SelectionCallback<Iterable> fn) {
|
| - throw new UnimplementedError();
|
| - }
|
| -
|
| - Transition transition() => new Transition(this);
|
| -}
|
| -
|
| -/* Implementation of [DataSelection] */
|
| -class _DataSelectionImpl extends _SelectionImpl implements DataSelection {
|
| - EnterSelection enter;
|
| - ExitSelection exit;
|
| -
|
| - _DataSelectionImpl(Iterable updated, Iterable entering, Iterable exiting,
|
| - SelectionScope scope) : super.selectionGroups(updated, scope) {
|
| - enter = new _EnterSelectionImpl(entering, this);
|
| - exit = new _ExitSelectionImpl(exiting, this);
|
| - }
|
| -}
|
| -
|
| -/* Implementation of [EnterSelection] */
|
| -class _EnterSelectionImpl implements EnterSelection {
|
| - final DataSelection update;
|
| -
|
| - SelectionScope scope;
|
| - Iterable<SelectionGroup> groups;
|
| -
|
| - _EnterSelectionImpl(Iterable this.groups, DataSelection this.update) {
|
| - scope = update.scope;
|
| - }
|
| -
|
| - bool get isEmpty => false;
|
| -
|
| - Selection insert(String tag,
|
| - {String before, SelectionCallback<Element> beforeFn}) {
|
| - assert(tag != null && tag.isNotEmpty);
|
| - return insertWithCallback(
|
| - (d, ei, e) => Namespace.createChildElement(tag, e),
|
| - before: before, beforeFn: beforeFn);
|
| - }
|
| -
|
| - Selection insertWithCallback(SelectionCallback<Element> fn,
|
| - {String before, SelectionCallback<Element> beforeFn}) {
|
| - assert(fn != null);
|
| - return selectWithCallback((d, ei, e) {
|
| - Element child = fn(d, ei, e);
|
| - e.insertBefore(child, e.querySelector(before));
|
| - return child;
|
| - });
|
| - }
|
| -
|
| - Selection append(String tag) {
|
| - assert(tag != null && tag.isNotEmpty);
|
| - return appendWithCallback(
|
| - (d, ei, e) => Namespace.createChildElement(tag, e));
|
| - }
|
| -
|
| - Selection appendWithCallback(SelectionCallback<Element> fn) {
|
| - assert(fn != null);
|
| - return selectWithCallback((datum, ei, e) {
|
| - Element child = fn(datum, ei, e);
|
| - e.append(child);
|
| - return child;
|
| - });
|
| - }
|
| -
|
| - Selection select(String selector) {
|
| - assert(selector == null && selector.isNotEmpty);
|
| - return selectWithCallback((d, ei, e) => e.querySelector(selector));
|
| - }
|
| -
|
| - Selection selectWithCallback(SelectionCallback<Element> fn) {
|
| - var subgroups = [];
|
| - for (int gi = 0, len = groups.length; gi < len; ++gi) {
|
| - final g = groups.elementAt(gi);
|
| - final u = update.groups.elementAt(gi);
|
| - final subgroup = [];
|
| - for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) {
|
| - final e = g.elements.elementAt(ei);
|
| - if (e != null) {
|
| - var datum = scope.datum(e),
|
| - selected = fn(datum, ei, g.parent);
|
| - scope.associate(selected, datum);
|
| - u.elements[ei] = selected;
|
| - subgroup.add(selected);
|
| - } else {
|
| - subgroup.add(null);
|
| - }
|
| - }
|
| - subgroups.add(new _SelectionGroupImpl(subgroup, parent: g.parent));
|
| - }
|
| - return new _SelectionImpl.selectionGroups(subgroups, scope);
|
| - }
|
| -}
|
| -
|
| -/* Implementation of [ExitSelection] */
|
| -class _ExitSelectionImpl extends _SelectionImpl implements ExitSelection {
|
| - final DataSelection update;
|
| - _ExitSelectionImpl(Iterable groups, DataSelection update)
|
| - : update = update, super.selectionGroups(groups, update.scope);
|
| -}
|
| -
|
| -class _SelectionGroupImpl implements SelectionGroup {
|
| - Iterable<Element> elements;
|
| - Element parent;
|
| - _SelectionGroupImpl(this.elements, {this.parent});
|
| -}
|
|
|