OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. All rights reserved. | 2 * Copyright 2014 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style | 4 * Use of this source code is governed by a BSD-style |
5 * license that can be found in the LICENSE file or at | 5 * license that can be found in the LICENSE file or at |
6 * https://developers.google.com/open-source/licenses/bsd | 6 * https://developers.google.com/open-source/licenses/bsd |
7 */ | 7 */ |
8 part of charted.selection; | 8 part of charted.selection; |
9 | 9 |
10 /** | 10 /** |
11 * Implementation of [Selection]. | 11 * Implementation of [Selection]. |
12 * Selections cannot be created directly - they are only created using | 12 * Selections cannot be created directly - they are only created using |
13 * the select or selectAll methods on [SelectionScope] and [Selection]. | 13 * the select or selectAll methods on [SelectionScope] and [Selection]. |
14 */ | 14 */ |
15 class _SelectionImpl implements Selection { | 15 class _SelectionImpl implements Selection { |
16 | |
17 Iterable<SelectionGroup> groups; | 16 Iterable<SelectionGroup> groups; |
18 SelectionScope scope; | 17 SelectionScope scope; |
19 | 18 |
20 /** | 19 /** |
21 * Creates a new selection. | 20 * Creates a new selection. |
22 * | 21 * |
23 * When [source] is not specified, the new selection would have exactly | 22 * When [source] is not specified, the new selection would have exactly |
24 * one group with [SelectionScope.root] as it's parent. Otherwise, one group | 23 * one group with [SelectionScope.root] as it's parent. Otherwise, one group |
25 * per for each non-null element is created with element as it's parent. | 24 * per for each non-null element is created with element as it's parent. |
26 * | 25 * |
27 * When [selector] is specified, each group contains all elements matching | 26 * When [selector] is specified, each group contains all elements matching |
28 * [selector] and under the group's parent element. Otherwise, [fn] is | 27 * [selector] and under the group's parent element. Otherwise, [fn] is |
29 * called once per group with parent element's "data", "index" and the | 28 * called once per group with parent element's "data", "index" and the |
30 * "element" itself passed as parameters. [fn] must return an iterable of | 29 * "element" itself passed as parameters. [fn] must return an iterable of |
31 * elements to be used in each group. | 30 * elements to be used in each group. |
32 */ | 31 */ |
33 _SelectionImpl.all({String selector, SelectionCallback<Iterable<Element>> fn, | 32 _SelectionImpl.all( |
34 SelectionScope this.scope, Selection source}) { | 33 {String selector, |
| 34 SelectionCallback<Iterable<Element>> fn, |
| 35 SelectionScope this.scope, |
| 36 Selection source}) { |
35 assert(selector != null || fn != null); | 37 assert(selector != null || fn != null); |
36 assert(source != null || scope != null); | 38 assert(source != null || scope != null); |
37 | 39 |
38 if (selector != null) { | 40 if (selector != null) { |
39 fn = (d, i, c) => c == null ? | 41 fn = (d, i, c) => c == null |
40 scope.root.querySelectorAll(selector) : | 42 ? scope.root.querySelectorAll(selector) |
41 c.querySelectorAll(selector); | 43 : c.querySelectorAll(selector); |
42 } | 44 } |
43 | 45 |
44 var tmpGroups = new List<SelectionGroup>(); | 46 var tmpGroups = new List<SelectionGroup>(); |
45 if (source != null) { | 47 if (source != null) { |
46 scope = source.scope; | 48 scope = source.scope; |
47 for (int gi = 0; gi < source.groups.length; ++gi) { | 49 for (int gi = 0; gi < source.groups.length; ++gi) { |
48 final g = source.groups.elementAt(gi); | 50 final g = source.groups.elementAt(gi); |
49 for (int ei = 0; ei < g.elements.length; ++ei) { | 51 for (int ei = 0; ei < g.elements.length; ++ei) { |
50 final e = g.elements.elementAt(ei); | 52 final e = g.elements.elementAt(ei); |
51 if (e != null) { | 53 if (e != null) { |
52 tmpGroups.add( | 54 tmpGroups.add( |
53 new _SelectionGroupImpl( | 55 new _SelectionGroupImpl(fn(scope.datum(e), gi, e), parent: e)); |
54 fn(scope.datum(e), gi, e), parent: e)); | |
55 } | 56 } |
56 } | 57 } |
57 } | 58 } |
58 } else { | 59 } else { |
59 tmpGroups.add( | 60 tmpGroups |
60 new _SelectionGroupImpl(fn(null, 0, null), parent: scope.root)); | 61 .add(new _SelectionGroupImpl(fn(null, 0, null), parent: scope.root)); |
61 } | 62 } |
62 groups = tmpGroups; | 63 groups = tmpGroups; |
63 } | 64 } |
64 | 65 |
65 /** | 66 /** |
66 * Same as [all] but only uses the first element matching [selector] when | 67 * Same as [all] but only uses the first element matching [selector] when |
67 * [selector] is specified. Otherwise, call [fn] which must return the | 68 * [selector] is specified. Otherwise, call [fn] which must return the |
68 * element to be selected. | 69 * element to be selected. |
69 */ | 70 */ |
70 _SelectionImpl.single({String selector, SelectionCallback<Element> fn, | 71 _SelectionImpl.single( |
71 SelectionScope this.scope, Selection source}) { | 72 {String selector, |
| 73 SelectionCallback<Element> fn, |
| 74 SelectionScope this.scope, |
| 75 Selection source}) { |
72 assert(selector != null || fn != null); | 76 assert(selector != null || fn != null); |
73 assert(source != null || scope != null); | 77 assert(source != null || scope != null); |
74 | 78 |
75 if (selector != null) { | 79 if (selector != null) { |
76 fn = (d, i, c) => c == null ? | 80 fn = (d, i, c) => c == null |
77 scope.root.querySelector(selector) : | 81 ? scope.root.querySelector(selector) |
78 c.querySelector(selector); | 82 : c.querySelector(selector); |
79 } | 83 } |
80 | 84 |
81 if (source != null) { | 85 if (source != null) { |
82 scope = source.scope; | 86 scope = source.scope; |
83 groups = new List<SelectionGroup>.generate(source.groups.length, (gi) { | 87 groups = new List<SelectionGroup>.generate(source.groups.length, (gi) { |
84 SelectionGroup g = source.groups.elementAt(gi); | 88 SelectionGroup g = source.groups.elementAt(gi); |
85 return new _SelectionGroupImpl( | 89 return new _SelectionGroupImpl( |
86 new List.generate(g.elements.length, (ei) { | 90 new List.generate(g.elements.length, (ei) { |
87 var e = g.elements.elementAt(ei); | 91 var e = g.elements.elementAt(ei); |
88 if (e != null) { | 92 if (e != null) { |
89 var datum = scope.datum(e); | 93 var datum = scope.datum(e); |
90 var enterElement = fn(datum, ei, e); | 94 var enterElement = fn(datum, ei, e); |
91 if (datum != null) { | 95 if (datum != null) { |
92 scope.associate(enterElement, datum); | 96 scope.associate(enterElement, datum); |
93 } | 97 } |
94 return enterElement; | 98 return enterElement; |
95 } else { | 99 } else { |
96 return null; | 100 return null; |
97 } | 101 } |
98 }), parent: g.parent); | 102 }), |
| 103 parent: g.parent); |
99 }); | 104 }); |
100 } else { | 105 } else { |
101 groups = new List<SelectionGroup>.generate(1, | 106 groups = new List<SelectionGroup>.generate( |
102 (_) => new _SelectionGroupImpl(new List.generate(1, | 107 1, |
103 (_) => fn(null, 0, null), growable: false)), growable: false); | 108 (_) => new _SelectionGroupImpl( |
| 109 new List.generate(1, (_) => fn(null, 0, null), growable: false)), |
| 110 growable: false); |
104 } | 111 } |
105 } | 112 } |
106 | 113 |
107 /** Creates a selection using the pre-computed list of [SelectionGroup] */ | 114 /** Creates a selection using the pre-computed list of [SelectionGroup] */ |
108 _SelectionImpl.selectionGroups( | 115 _SelectionImpl.selectionGroups( |
109 Iterable<SelectionGroup> this.groups, SelectionScope this.scope); | 116 Iterable<SelectionGroup> this.groups, SelectionScope this.scope); |
110 | 117 |
111 /** | 118 /** |
112 * Creates a selection using the list of elements. All elements will | 119 * Creates a selection using the list of elements. All elements will |
113 * be part of the same group, with [SelectionScope.root] as the group's parent | 120 * be part of the same group, with [SelectionScope.root] as the group's parent |
114 */ | 121 */ |
115 _SelectionImpl.elements(Iterable elements, SelectionScope this.scope) { | 122 _SelectionImpl.elements(Iterable elements, SelectionScope this.scope) { |
116 groups = new List<SelectionGroup>() | 123 groups = new List<SelectionGroup>()..add(new _SelectionGroupImpl(elements)); |
117 ..add(new _SelectionGroupImpl(elements)); | |
118 } | 124 } |
119 | 125 |
120 /** | 126 /** |
121 * Utility to evaluate value of parameters (uses value when given | 127 * Utility to evaluate value of parameters (uses value when given |
122 * or invokes a callback to get the value) and calls [action] for | 128 * or invokes a callback to get the value) and calls [action] for |
123 * each non-null element in this selection | 129 * each non-null element in this selection |
124 */ | 130 */ |
125 void _do(SelectionCallback f, Function action) { | 131 void _do(SelectionCallback f, Function action) { |
126 each((d, i, e) => action(e, f == null ? null : f(scope.datum(e), i, e))); | 132 each((d, i, e) => action(e, f == null ? null : f(scope.datum(e), i, e))); |
127 } | 133 } |
128 | 134 |
129 /** Calls a function on each non-null element in the selection */ | 135 /** Calls a function on each non-null element in the selection */ |
130 void each(SelectionCallback fn) { | 136 void each(SelectionCallback fn) { |
131 if (fn == null) return; | 137 if (fn == null) return; |
132 for (int gi = 0, gLen = groups.length; gi < gLen; ++gi) { | 138 for (int gi = 0, gLen = groups.length; gi < gLen; ++gi) { |
133 final g = groups.elementAt(gi); | 139 final g = groups.elementAt(gi); |
134 for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) { | 140 for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) { |
135 final e = g.elements.elementAt(ei); | 141 final e = g.elements.elementAt(ei); |
136 if (e != null) fn(scope.datum(e), ei, e); | 142 if (e != null) fn(scope.datum(e), ei, e); |
137 } | 143 } |
138 } | 144 } |
139 } | 145 } |
140 | 146 |
141 void on(String type, [SelectionCallback listener, bool capture]) { | 147 void on(String type, [SelectionCallback listener, bool capture]) { |
142 Function getEventHandler(i, e) => (Event event) { | 148 Function getEventHandler(i, e) => (Event event) { |
143 var previous = scope.event; | 149 var previous = scope.event; |
144 scope.event = event; | 150 scope.event = event; |
145 try { | 151 try { |
146 listener(scope.datum(e), i, e); | 152 listener(scope.datum(e), i, e); |
147 } finally { | 153 } finally { |
148 scope.event = previous; | 154 scope.event = previous; |
149 } | 155 } |
150 }; | 156 }; |
151 | 157 |
152 if (!type.startsWith('.')) { | 158 if (!type.startsWith('.')) { |
153 if (listener != null) { | 159 if (listener != null) { |
154 // Add a listener to each element. | 160 // Add a listener to each element. |
155 each((d, i, Element e){ | 161 each((d, i, Element e) { |
156 var handlers = scope._listeners[e]; | 162 var handlers = scope._listeners[e]; |
157 if (handlers == null) scope._listeners[e] = handlers = {}; | 163 if (handlers == null) scope._listeners[e] = handlers = {}; |
158 handlers[type] = new Pair(getEventHandler(i, e), capture); | 164 handlers[type] = new Pair(getEventHandler(i, e), capture); |
159 e.addEventListener(type, handlers[type].first, capture); | 165 e.addEventListener(type, handlers[type].first, capture); |
160 }); | 166 }); |
161 } else { | 167 } else { |
162 // Remove the listener from each element. | 168 // Remove the listener from each element. |
163 each((d, i, Element e) { | 169 each((d, i, Element e) { |
164 var handlers = scope._listeners[e]; | 170 var handlers = scope._listeners[e]; |
165 if (handlers != null && handlers[type] != null) { | 171 if (handlers != null && handlers[type] != null) { |
166 e.removeEventListener( | 172 e.removeEventListener( |
167 type, handlers[type].first, handlers[type].last); | 173 type, handlers[type].first, handlers[type].last); |
168 } | 174 } |
169 }); | 175 }); |
170 } | 176 } |
171 } else { | 177 } else { |
172 // Remove all listeners on the event type (ignoring the namespace) | 178 // Remove all listeners on the event type (ignoring the namespace) |
173 each((d, i, Element e) { | 179 each((d, i, Element e) { |
174 var handlers = scope._listeners[e], | 180 var handlers = scope._listeners[e], t = type.substring(1); |
175 t = type.substring(1); | |
176 handlers.forEach((String s, Pair<Function, bool> value) { | 181 handlers.forEach((String s, Pair<Function, bool> value) { |
177 if (s.split('.')[0] == t) { | 182 if (s.split('.')[0] == t) { |
178 e.removeEventListener(s, value.first, value.last); | 183 e.removeEventListener(s, value.first, value.last); |
179 } | 184 } |
180 }); | 185 }); |
181 }); | 186 }); |
182 } | 187 } |
183 } | 188 } |
184 | 189 |
185 int get length { | 190 int get length { |
(...skipping 17 matching lines...) Expand all Loading... |
203 return null; | 208 return null; |
204 } | 209 } |
205 | 210 |
206 void attr(String name, val) { | 211 void attr(String name, val) { |
207 assert(name != null && name.isNotEmpty); | 212 assert(name != null && name.isNotEmpty); |
208 attrWithCallback(name, toCallback(val)); | 213 attrWithCallback(name, toCallback(val)); |
209 } | 214 } |
210 | 215 |
211 void attrWithCallback(String name, SelectionCallback fn) { | 216 void attrWithCallback(String name, SelectionCallback fn) { |
212 assert(fn != null); | 217 assert(fn != null); |
213 _do(fn, (e, v) => v == null ? | 218 _do( |
214 e.attributes.remove(name) : e.attributes[name] = "$v"); | 219 fn, |
| 220 (e, v) => |
| 221 v == null ? e.attributes.remove(name) : e.attributes[name] = "$v"); |
215 } | 222 } |
216 | 223 |
217 void classed(String name, [bool val = true]) { | 224 void classed(String name, [bool val = true]) { |
218 assert(name != null && name.isNotEmpty); | 225 assert(name != null && name.isNotEmpty); |
219 classedWithCallback(name, toCallback(val)); | 226 classedWithCallback(name, toCallback(val)); |
220 } | 227 } |
221 | 228 |
222 void classedWithCallback(String name, SelectionCallback<bool> fn) { | 229 void classedWithCallback(String name, SelectionCallback<bool> fn) { |
223 assert(fn != null); | 230 assert(fn != null); |
224 _do(fn, (e, v) => | 231 _do(fn, |
225 v == false ? e.classes.remove(name) : e.classes.add(name)); | 232 (e, v) => v == false ? e.classes.remove(name) : e.classes.add(name)); |
226 } | 233 } |
227 | 234 |
228 void style(String property, val, {String priority}) { | 235 void style(String property, val, {String priority}) { |
229 assert(property != null && property.isNotEmpty); | 236 assert(property != null && property.isNotEmpty); |
230 styleWithCallback(property, | 237 styleWithCallback(property, toCallback(val as String), priority: priority); |
231 toCallback(val as String), priority: priority); | |
232 } | 238 } |
233 | 239 |
234 void styleWithCallback(String property, | 240 void styleWithCallback(String property, SelectionCallback<String> fn, |
235 SelectionCallback<String> fn, {String priority}) { | 241 {String priority}) { |
236 assert(fn != null); | 242 assert(fn != null); |
237 _do(fn, (Element e, String v) => | 243 _do( |
238 v == null || v.isEmpty ? | 244 fn, |
239 e.style.removeProperty(property) : | 245 (Element e, String v) => v == null || v.isEmpty |
240 e.style.setProperty(property, v, priority)); | 246 ? e.style.removeProperty(property) |
| 247 : e.style.setProperty(property, v, priority)); |
241 } | 248 } |
242 | 249 |
243 void text(String val) => textWithCallback(toCallback(val)); | 250 void text(String val) => textWithCallback(toCallback(val)); |
244 | 251 |
245 void textWithCallback(SelectionCallback<String> fn) { | 252 void textWithCallback(SelectionCallback<String> fn) { |
246 assert(fn != null); | 253 assert(fn != null); |
247 _do(fn, (e, v) => e.text = v == null ? '' : v); | 254 _do(fn, (e, v) => e.text = v == null ? '' : v); |
248 } | 255 } |
249 | 256 |
250 void html(String val) => htmlWithCallback(toCallback(val)); | 257 void html(String val) => htmlWithCallback(toCallback(val)); |
251 | 258 |
252 void htmlWithCallback(SelectionCallback<String> fn) { | 259 void htmlWithCallback(SelectionCallback<String> fn) { |
253 assert(fn != null); | 260 assert(fn != null); |
254 _do(fn, (e, v) => e.innerHtml = v == null ? '' : v); | 261 _do(fn, (e, v) => e.innerHtml = v == null ? '' : v); |
255 } | 262 } |
256 | 263 |
257 void remove() => _do(null, (e, _) => e.remove()); | 264 void remove() => _do(null, (e, _) => e.remove()); |
258 | 265 |
259 Selection select(String selector) { | 266 Selection select(String selector) { |
260 assert(selector != null && selector.isNotEmpty); | 267 assert(selector != null && selector.isNotEmpty); |
261 return new _SelectionImpl.single(selector: selector, source: this); | 268 return new _SelectionImpl.single(selector: selector, source: this); |
262 } | 269 } |
263 | 270 |
264 Selection selectWithCallback(SelectionCallback<Element> fn) { | 271 Selection selectWithCallback(SelectionCallback<Element> fn) { |
265 assert(fn != null); | 272 assert(fn != null); |
266 return new _SelectionImpl.single(fn: fn, source:this); | 273 return new _SelectionImpl.single(fn: fn, source: this); |
267 } | 274 } |
268 | 275 |
269 Selection append(String tag) { | 276 Selection append(String tag) { |
270 assert(tag != null && tag.isNotEmpty); | 277 assert(tag != null && tag.isNotEmpty); |
271 return appendWithCallback( | 278 return appendWithCallback( |
272 (d, ei, e) => Namespace.createChildElement(tag, e)); | 279 (d, ei, e) => Namespace.createChildElement(tag, e)); |
273 } | 280 } |
274 | 281 |
275 Selection appendWithCallback(SelectionCallback<Element> fn) { | 282 Selection appendWithCallback(SelectionCallback<Element> fn) { |
276 assert(fn != null); | 283 assert(fn != null); |
277 return new _SelectionImpl.single(fn: (datum, ei, e) { | 284 return new _SelectionImpl.single(fn: (datum, ei, e) { |
278 Element child = fn(datum, ei, e); | 285 Element child = fn(datum, ei, e); |
279 return child == null ? null : e.append(child); | 286 return child == null ? null : e.append(child); |
280 }, source: this); | 287 }, source: this); |
281 } | 288 } |
282 | 289 |
283 Selection insert(String tag, | 290 Selection insert(String tag, |
284 {String before, SelectionCallback<Element> beforeFn}) { | 291 {String before, SelectionCallback<Element> beforeFn}) { |
285 assert(tag != null && tag.isNotEmpty); | 292 assert(tag != null && tag.isNotEmpty); |
286 return insertWithCallback( | 293 return insertWithCallback( |
287 (d, ei, e) => Namespace.createChildElement(tag, e), | 294 (d, ei, e) => Namespace.createChildElement(tag, e), |
288 before: before, beforeFn: beforeFn); | 295 before: before, |
| 296 beforeFn: beforeFn); |
289 } | 297 } |
290 | 298 |
291 Selection insertWithCallback(SelectionCallback<Element> fn, | 299 Selection insertWithCallback(SelectionCallback<Element> fn, |
292 {String before, SelectionCallback<Element> beforeFn}) { | 300 {String before, SelectionCallback<Element> beforeFn}) { |
293 assert(fn != null); | 301 assert(fn != null); |
294 beforeFn = | 302 beforeFn = |
295 before == null ? beforeFn : (d, ei, e) => e.querySelector(before); | 303 before == null ? beforeFn : (d, ei, e) => e.querySelector(before); |
296 return new _SelectionImpl.single( | 304 return new _SelectionImpl.single(fn: (datum, ei, e) { |
297 fn: (datum, ei, e) { | 305 Element child = fn(datum, ei, e); |
298 Element child = fn(datum, ei, e); | 306 Element before = beforeFn(datum, ei, e); |
299 Element before = beforeFn(datum, ei, e); | 307 return child == null ? null : e.insertBefore(child, before); |
300 return child == null ? null : e.insertBefore(child, before); | 308 }, source: this); |
301 }, | |
302 source: this); | |
303 } | 309 } |
304 | 310 |
305 Selection selectAll(String selector) { | 311 Selection selectAll(String selector) { |
306 assert(selector != null && selector.isNotEmpty); | 312 assert(selector != null && selector.isNotEmpty); |
307 return new _SelectionImpl.all(selector: selector, source: this); | 313 return new _SelectionImpl.all(selector: selector, source: this); |
308 } | 314 } |
309 | 315 |
310 Selection selectAllWithCallback(SelectionCallback<Iterable<Element>> fn) { | 316 Selection selectAllWithCallback(SelectionCallback<Iterable<Element>> fn) { |
311 assert(fn != null); | 317 assert(fn != null); |
312 return new _SelectionImpl.all(fn: fn, source:this); | 318 return new _SelectionImpl.all(fn: fn, source: this); |
313 } | 319 } |
314 | 320 |
315 DataSelection data(Iterable vals, [SelectionKeyFunction keyFn]) { | 321 DataSelection data(Iterable vals, [SelectionKeyFunction keyFn]) { |
316 assert(vals != null); | 322 assert(vals != null); |
317 return dataWithCallback(toCallback(vals), keyFn); | 323 return dataWithCallback(toCallback(vals), keyFn); |
318 } | 324 } |
319 | 325 |
320 DataSelection dataWithCallback( | 326 DataSelection dataWithCallback(SelectionCallback<Iterable> fn, |
321 SelectionCallback<Iterable> fn, [SelectionKeyFunction keyFn]) { | 327 [SelectionKeyFunction keyFn]) { |
322 assert(fn != null); | 328 assert(fn != null); |
323 | 329 |
324 var enterGroups = [], | 330 var enterGroups = [], updateGroups = [], exitGroups = []; |
325 updateGroups = [], | |
326 exitGroups = []; | |
327 | 331 |
328 // Create a dummy node to be used with enter() selection. | 332 // Create a dummy node to be used with enter() selection. |
329 Object dummy(val) { | 333 Object dummy(val) { |
330 var element = new Object(); | 334 var element = new Object(); |
331 scope.associate(element, val); | 335 scope.associate(element, val); |
332 return element; | 336 return element; |
333 }; | 337 } |
| 338 ; |
334 | 339 |
335 // Joins data to all elements in the group. | 340 // Joins data to all elements in the group. |
336 void join(SelectionGroup g, Iterable vals) { | 341 void join(SelectionGroup g, Iterable vals) { |
337 final int valuesLength = vals.length; | 342 final int valuesLength = vals.length; |
338 final int elementsLength = g.elements.length; | 343 final int elementsLength = g.elements.length; |
339 | 344 |
340 // Nodes exiting, entering and updating in this group. | 345 // Nodes exiting, entering and updating in this group. |
341 // We maintain the nodes at the same index as they currently | 346 // We maintain the nodes at the same index as they currently |
342 // are (for exiting) or where they should be (for entering and updating) | 347 // are (for exiting) or where they should be (for entering and updating) |
343 var update = new List(valuesLength), | 348 var update = new List(valuesLength), |
344 enter = new List(valuesLength), | 349 enter = new List(valuesLength), |
345 exit = new List(elementsLength); | 350 exit = new List(elementsLength); |
346 | 351 |
347 // Use key function to determine DOMElement to data associations. | 352 // Use key function to determine DOMElement to data associations. |
348 if (keyFn != null) { | 353 if (keyFn != null) { |
349 var keysOnDOM = [], | 354 var keysOnDOM = [], elementsByKey = {}, valuesByKey = {}; |
350 elementsByKey = {}, | |
351 valuesByKey = {}; | |
352 | 355 |
353 // Create a key to DOM element map. | 356 // Create a key to DOM element map. |
354 // Used later to see if an element already exists for a key. | 357 // Used later to see if an element already exists for a key. |
355 for (int ei = 0, len = elementsLength; ei < len; ++ei) { | 358 for (int ei = 0, len = elementsLength; ei < len; ++ei) { |
356 final e = g.elements.elementAt(ei); | 359 final e = g.elements.elementAt(ei); |
357 var keyValue = keyFn(scope.datum(e)); | 360 var keyValue = keyFn(scope.datum(e)); |
358 if (elementsByKey.containsKey(keyValue)) { | 361 if (elementsByKey.containsKey(keyValue)) { |
359 exit[ei] = e; | 362 exit[ei] = e; |
360 } else { | 363 } else { |
361 elementsByKey[keyValue] = e; | 364 elementsByKey[keyValue] = e; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 // List of elements exiting this group | 415 // List of elements exiting this group |
413 for (int len = elementsLength; i < len; ++i) { | 416 for (int len = elementsLength; i < len; ++i) { |
414 exit[i] = g.elements.elementAt(i); | 417 exit[i] = g.elements.elementAt(i); |
415 } | 418 } |
416 } | 419 } |
417 | 420 |
418 // Create the element groups and set parents from the current group. | 421 // Create the element groups and set parents from the current group. |
419 enterGroups.add(new _SelectionGroupImpl(enter, parent: g.parent)); | 422 enterGroups.add(new _SelectionGroupImpl(enter, parent: g.parent)); |
420 updateGroups.add(new _SelectionGroupImpl(update, parent: g.parent)); | 423 updateGroups.add(new _SelectionGroupImpl(update, parent: g.parent)); |
421 exitGroups.add(new _SelectionGroupImpl(exit, parent: g.parent)); | 424 exitGroups.add(new _SelectionGroupImpl(exit, parent: g.parent)); |
422 }; | 425 } |
| 426 ; |
423 | 427 |
424 for (int gi = 0; gi < groups.length; ++gi) { | 428 for (int gi = 0; gi < groups.length; ++gi) { |
425 final g = groups.elementAt(gi); | 429 final g = groups.elementAt(gi); |
426 join(g, fn(scope.datum(g.parent), gi, g.parent)); | 430 join(g, fn(scope.datum(g.parent), gi, g.parent)); |
427 } | 431 } |
428 | 432 |
429 return new _DataSelectionImpl( | 433 return new _DataSelectionImpl(updateGroups, enterGroups, exitGroups, scope); |
430 updateGroups, enterGroups, exitGroups, scope); | |
431 } | 434 } |
432 | 435 |
433 void datum(Iterable vals) { | 436 void datum(Iterable vals) { |
434 throw new UnimplementedError(); | 437 throw new UnimplementedError(); |
435 } | 438 } |
436 | 439 |
437 void datumWithCallback(SelectionCallback<Iterable> fn) { | 440 void datumWithCallback(SelectionCallback<Iterable> fn) { |
438 throw new UnimplementedError(); | 441 throw new UnimplementedError(); |
439 } | 442 } |
440 | 443 |
441 Transition transition() => new Transition(this); | 444 Transition transition() => new Transition(this); |
442 } | 445 } |
443 | 446 |
444 /* Implementation of [DataSelection] */ | 447 /* Implementation of [DataSelection] */ |
445 class _DataSelectionImpl extends _SelectionImpl implements DataSelection { | 448 class _DataSelectionImpl extends _SelectionImpl implements DataSelection { |
446 EnterSelection enter; | 449 EnterSelection enter; |
447 ExitSelection exit; | 450 ExitSelection exit; |
448 | 451 |
449 _DataSelectionImpl(Iterable updated, Iterable entering, Iterable exiting, | 452 _DataSelectionImpl(Iterable updated, Iterable entering, Iterable exiting, |
450 SelectionScope scope) : super.selectionGroups(updated, scope) { | 453 SelectionScope scope) |
| 454 : super.selectionGroups(updated, scope) { |
451 enter = new _EnterSelectionImpl(entering, this); | 455 enter = new _EnterSelectionImpl(entering, this); |
452 exit = new _ExitSelectionImpl(exiting, this); | 456 exit = new _ExitSelectionImpl(exiting, this); |
453 } | 457 } |
454 } | 458 } |
455 | 459 |
456 /* Implementation of [EnterSelection] */ | 460 /* Implementation of [EnterSelection] */ |
457 class _EnterSelectionImpl implements EnterSelection { | 461 class _EnterSelectionImpl implements EnterSelection { |
458 final DataSelection update; | 462 final DataSelection update; |
459 | 463 |
460 SelectionScope scope; | 464 SelectionScope scope; |
461 Iterable<SelectionGroup> groups; | 465 Iterable<SelectionGroup> groups; |
462 | 466 |
463 _EnterSelectionImpl(Iterable this.groups, DataSelection this.update) { | 467 _EnterSelectionImpl(Iterable this.groups, DataSelection this.update) { |
464 scope = update.scope; | 468 scope = update.scope; |
465 } | 469 } |
466 | 470 |
467 bool get isEmpty => false; | 471 bool get isEmpty => false; |
468 | 472 |
469 Selection insert(String tag, | 473 Selection insert(String tag, |
470 {String before, SelectionCallback<Element> beforeFn}) { | 474 {String before, SelectionCallback<Element> beforeFn}) { |
471 assert(tag != null && tag.isNotEmpty); | 475 assert(tag != null && tag.isNotEmpty); |
472 return insertWithCallback( | 476 return insertWithCallback( |
473 (d, ei, e) => Namespace.createChildElement(tag, e), | 477 (d, ei, e) => Namespace.createChildElement(tag, e), |
474 before: before, beforeFn: beforeFn); | 478 before: before, |
| 479 beforeFn: beforeFn); |
475 } | 480 } |
476 | 481 |
477 Selection insertWithCallback(SelectionCallback<Element> fn, | 482 Selection insertWithCallback(SelectionCallback<Element> fn, |
478 {String before, SelectionCallback<Element> beforeFn}) { | 483 {String before, SelectionCallback<Element> beforeFn}) { |
479 assert(fn != null); | 484 assert(fn != null); |
480 return selectWithCallback((d, ei, e) { | 485 return selectWithCallback((d, ei, e) { |
481 Element child = fn(d, ei, e); | 486 Element child = fn(d, ei, e); |
482 e.insertBefore(child, e.querySelector(before)); | 487 e.insertBefore(child, e.querySelector(before)); |
483 return child; | 488 return child; |
484 }); | 489 }); |
485 } | 490 } |
486 | 491 |
487 Selection append(String tag) { | 492 Selection append(String tag) { |
488 assert(tag != null && tag.isNotEmpty); | 493 assert(tag != null && tag.isNotEmpty); |
489 return appendWithCallback( | 494 return appendWithCallback( |
490 (d, ei, e) => Namespace.createChildElement(tag, e)); | 495 (d, ei, e) => Namespace.createChildElement(tag, e)); |
491 } | 496 } |
492 | 497 |
493 Selection appendWithCallback(SelectionCallback<Element> fn) { | 498 Selection appendWithCallback(SelectionCallback<Element> fn) { |
494 assert(fn != null); | 499 assert(fn != null); |
495 return selectWithCallback((datum, ei, e) { | 500 return selectWithCallback((datum, ei, e) { |
496 Element child = fn(datum, ei, e); | 501 Element child = fn(datum, ei, e); |
497 e.append(child); | 502 e.append(child); |
498 return child; | 503 return child; |
499 }); | 504 }); |
500 } | 505 } |
501 | 506 |
502 Selection select(String selector) { | 507 Selection select(String selector) { |
503 assert(selector == null && selector.isNotEmpty); | 508 assert(selector == null && selector.isNotEmpty); |
504 return selectWithCallback((d, ei, e) => e.querySelector(selector)); | 509 return selectWithCallback((d, ei, e) => e.querySelector(selector)); |
505 } | 510 } |
506 | 511 |
507 Selection selectWithCallback(SelectionCallback<Element> fn) { | 512 Selection selectWithCallback(SelectionCallback<Element> fn) { |
508 var subgroups = []; | 513 var subgroups = []; |
509 for (int gi = 0, len = groups.length; gi < len; ++gi) { | 514 for (int gi = 0, len = groups.length; gi < len; ++gi) { |
510 final g = groups.elementAt(gi); | 515 final g = groups.elementAt(gi); |
511 final u = update.groups.elementAt(gi); | 516 final u = update.groups.elementAt(gi); |
512 final subgroup = []; | 517 final subgroup = []; |
513 for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) { | 518 for (int ei = 0, eLen = g.elements.length; ei < eLen; ++ei) { |
514 final e = g.elements.elementAt(ei); | 519 final e = g.elements.elementAt(ei); |
515 if (e != null) { | 520 if (e != null) { |
516 var datum = scope.datum(e), | 521 var datum = scope.datum(e), selected = fn(datum, ei, g.parent); |
517 selected = fn(datum, ei, g.parent); | |
518 scope.associate(selected, datum); | 522 scope.associate(selected, datum); |
519 u.elements[ei] = selected; | 523 u.elements[ei] = selected; |
520 subgroup.add(selected); | 524 subgroup.add(selected); |
521 } else { | 525 } else { |
522 subgroup.add(null); | 526 subgroup.add(null); |
523 } | 527 } |
524 } | 528 } |
525 subgroups.add(new _SelectionGroupImpl(subgroup, parent: g.parent)); | 529 subgroups.add(new _SelectionGroupImpl(subgroup, parent: g.parent)); |
526 } | 530 } |
527 return new _SelectionImpl.selectionGroups(subgroups, scope); | 531 return new _SelectionImpl.selectionGroups(subgroups, scope); |
528 } | 532 } |
529 } | 533 } |
530 | 534 |
531 /* Implementation of [ExitSelection] */ | 535 /* Implementation of [ExitSelection] */ |
532 class _ExitSelectionImpl extends _SelectionImpl implements ExitSelection { | 536 class _ExitSelectionImpl extends _SelectionImpl implements ExitSelection { |
533 final DataSelection update; | 537 final DataSelection update; |
534 _ExitSelectionImpl(Iterable groups, DataSelection update) | 538 _ExitSelectionImpl(Iterable groups, DataSelection update) |
535 : update = update, super.selectionGroups(groups, update.scope); | 539 : update = update, |
| 540 super.selectionGroups(groups, update.scope); |
536 } | 541 } |
537 | 542 |
538 class _SelectionGroupImpl implements SelectionGroup { | 543 class _SelectionGroupImpl implements SelectionGroup { |
539 Iterable<Element> elements; | 544 Iterable<Element> elements; |
540 Element parent; | 545 Element parent; |
541 _SelectionGroupImpl(this.elements, {this.parent}); | 546 _SelectionGroupImpl(this.elements, {this.parent}); |
542 } | 547 } |
OLD | NEW |