d3_rails 2.8.0 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.DS_Store +0 -0
  2. data/README.md +27 -5
  3. data/lib/d3_rails/version.rb +1 -1
  4. data/vendor/.DS_Store +0 -0
  5. data/vendor/assets/.DS_Store +0 -0
  6. data/vendor/assets/javascripts/.DS_Store +0 -0
  7. data/vendor/assets/javascripts/d3.v2.js +99 -80
  8. data/vendor/assets/javascripts/morris.js +1 -0
  9. data/vendor/assets/javascripts/morris/.DS_Store +0 -0
  10. data/vendor/assets/javascripts/morris/Makefile +10 -0
  11. data/vendor/assets/javascripts/morris/README.md +87 -0
  12. data/vendor/assets/javascripts/morris/examples/_template.html +18 -0
  13. data/vendor/assets/javascripts/morris/examples/days.html +36 -0
  14. data/vendor/assets/javascripts/morris/examples/decimal.html +31 -0
  15. data/vendor/assets/javascripts/morris/examples/lib/example.css +13 -0
  16. data/vendor/assets/javascripts/morris/examples/lib/example.js +4 -0
  17. data/vendor/assets/javascripts/morris/examples/lib/prettify.css +1 -0
  18. data/vendor/assets/javascripts/morris/examples/lib/prettify.js +28 -0
  19. data/vendor/assets/javascripts/morris/examples/months-no-smooth.html +37 -0
  20. data/vendor/assets/javascripts/morris/examples/negative.html +35 -0
  21. data/vendor/assets/javascripts/morris/examples/non-date.html +36 -0
  22. data/vendor/assets/javascripts/morris/examples/quarters.html +53 -0
  23. data/vendor/assets/javascripts/morris/examples/timestamps.html +37 -0
  24. data/vendor/assets/javascripts/morris/examples/weeks.html +52 -0
  25. data/vendor/assets/javascripts/morris/morris.coffee +444 -0
  26. data/vendor/assets/javascripts/morris/morris.js +493 -0
  27. data/vendor/assets/javascripts/morris/morris.min.js +1 -0
  28. data/vendor/assets/javascripts/tesseract.js +1 -0
  29. data/vendor/assets/javascripts/tesseract/.gitignore +2 -0
  30. data/vendor/assets/javascripts/tesseract/LICENSE +12 -0
  31. data/vendor/assets/javascripts/tesseract/Makefile +48 -0
  32. data/vendor/assets/javascripts/tesseract/README.md +11 -0
  33. data/vendor/assets/javascripts/tesseract/index.js +1 -0
  34. data/vendor/assets/javascripts/tesseract/lib/dart/AUTHORS +9 -0
  35. data/vendor/assets/javascripts/tesseract/lib/dart/LICENSE +25 -0
  36. data/vendor/assets/javascripts/tesseract/lib/dart/dual_pivot_quicksort.dart +342 -0
  37. data/vendor/assets/javascripts/tesseract/package.json +11 -0
  38. data/vendor/assets/javascripts/tesseract/src/array.js +32 -0
  39. data/vendor/assets/javascripts/tesseract/src/bisect.js +44 -0
  40. data/vendor/assets/javascripts/tesseract/src/filter.js +19 -0
  41. data/vendor/assets/javascripts/tesseract/src/heap.js +44 -0
  42. data/vendor/assets/javascripts/tesseract/src/heapselect.js +36 -0
  43. data/vendor/assets/javascripts/tesseract/src/identity.js +3 -0
  44. data/vendor/assets/javascripts/tesseract/src/insertionsort.js +18 -0
  45. data/vendor/assets/javascripts/tesseract/src/null.js +3 -0
  46. data/vendor/assets/javascripts/tesseract/src/package.js +14 -0
  47. data/vendor/assets/javascripts/tesseract/src/permute.js +8 -0
  48. data/vendor/assets/javascripts/tesseract/src/quicksort.js +282 -0
  49. data/vendor/assets/javascripts/tesseract/src/reduce.js +19 -0
  50. data/vendor/assets/javascripts/tesseract/src/tesseract.js +663 -0
  51. data/vendor/assets/javascripts/tesseract/src/version.js +1 -0
  52. data/vendor/assets/javascripts/tesseract/src/zero.js +3 -0
  53. data/vendor/assets/javascripts/tesseract/tesseract.js +1177 -0
  54. data/vendor/assets/javascripts/tesseract/tesseract.min.js +1 -0
  55. data/vendor/assets/javascripts/tesseract/test/benchmark.js +177 -0
  56. data/vendor/assets/javascripts/tesseract/test/bisect-test.js +206 -0
  57. data/vendor/assets/javascripts/tesseract/test/heap-test.js +44 -0
  58. data/vendor/assets/javascripts/tesseract/test/permute-test.js +51 -0
  59. data/vendor/assets/javascripts/tesseract/test/select-test.js +63 -0
  60. data/vendor/assets/javascripts/tesseract/test/sort-test.js +83 -0
  61. data/vendor/assets/javascripts/tesseract/test/tesseract-test.js +655 -0
  62. data/vendor/assets/javascripts/tesseract/test/version-test.js +16 -0
  63. metadata +63 -8
@@ -0,0 +1,19 @@
1
+ function tesseract_reduceIncrement(p) {
2
+ return p + 1;
3
+ }
4
+
5
+ function tesseract_reduceDecrement(p) {
6
+ return p - 1;
7
+ }
8
+
9
+ function tesseract_reduceAdd(f) {
10
+ return function(p, v) {
11
+ return p + +f(v);
12
+ };
13
+ }
14
+
15
+ function tesseract_reduceSubtract(f) {
16
+ return function(p, v) {
17
+ return p - f(v);
18
+ };
19
+ }
@@ -0,0 +1,663 @@
1
+ exports.tesseract = tesseract;
2
+
3
+ function tesseract() {
4
+ var tesseract = {
5
+ add: add,
6
+ dimension: dimension,
7
+ groupAll: groupAll,
8
+ size: size
9
+ };
10
+
11
+ var data = [], // the records
12
+ n = 0, // the number of records; data.length
13
+ m = 0, // number of dimensions in use
14
+ M = 8, // number of dimensions that can fit in `filters`
15
+ filters = tesseract_array8(0), // M bits per record; 1 is filtered out
16
+ filterListeners = [], // when the filters change
17
+ dataListeners = []; // when data is added
18
+
19
+ // Adds the specified new records to this tesseract.
20
+ function add(newData) {
21
+ var n0 = n,
22
+ n1 = newData.length;
23
+
24
+ // If there's actually new data to add…
25
+ // Merge the new data into the existing data.
26
+ // Lengthen the filter bitset to handle the new records.
27
+ // Notify listeners (dimensions and groups) that new data is available.
28
+ if (n1) {
29
+ data = data.concat(newData);
30
+ filters = tesseract_arrayLengthen(filters, n += n1);
31
+ dataListeners.forEach(function(l) { l(newData, n0, n1); });
32
+ }
33
+
34
+ return tesseract;
35
+ }
36
+
37
+ // Adds a new dimension with the specified value accessor function.
38
+ function dimension(value) {
39
+ var dimension = {
40
+ filter: filter,
41
+ filterExact: filterExact,
42
+ filterRange: filterRange,
43
+ filterAll: filterAll,
44
+ top: top,
45
+ group: group,
46
+ groupAll: groupAll
47
+ };
48
+
49
+ var one = 1 << m++, // bit mask, e.g., 00001000
50
+ zero = ~one, // inverted one, e.g., 11110111
51
+ values, // sorted, cached array
52
+ index, // value rank ↦ object id
53
+ newValues, // temporary array storing newly-added values
54
+ newIndex, // temporary array storing newly-added index
55
+ sort = quicksort_by(function(i) { return newValues[i]; }),
56
+ refilter = tesseract_filterAll, // for recomputing filter
57
+ indexListeners = [], // when data is added
58
+ lo0 = 0,
59
+ hi0 = 0;
60
+
61
+ // Updating a dimension is a two-stage process. First, we must update the
62
+ // associated filters for the newly-added records. Once all dimensions have
63
+ // updated their filters, the groups are notified to update.
64
+ dataListeners.unshift(preAdd);
65
+ dataListeners.push(postAdd);
66
+
67
+ // Incorporate any existing data into this dimension, and make sure that the
68
+ // filter bitset is wide enough to handle the new dimension.
69
+ if (m > M) filters = tesseract_arrayWiden(filters, M <<= 1);
70
+ preAdd(data, 0, n);
71
+ postAdd(data, 0, n);
72
+
73
+ // Incorporates the specified new records into this dimension.
74
+ // This function is responsible for updating filters, values, and index.
75
+ function preAdd(newData, n0, n1) {
76
+
77
+ // Permute new values into natural order using a sorted index.
78
+ newValues = newData.map(value);
79
+ newIndex = sort(tesseract_range(n1), 0, n1);
80
+ newValues = permute(newValues, newIndex);
81
+
82
+ // Bisect newValues to determine which new records are selected.
83
+ var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1], i;
84
+ for (i = 0; i < lo1; ++i) filters[newIndex[i] + n0] |= one;
85
+ for (i = hi1; i < n1; ++i) filters[newIndex[i] + n0] |= one;
86
+
87
+ // If this dimension previously had no data, then we don't need to do the
88
+ // more expensive merge operation; use the new values and index as-is.
89
+ if (!n0) {
90
+ values = newValues;
91
+ index = newIndex;
92
+ lo0 = lo1;
93
+ hi0 = hi1;
94
+ return;
95
+ }
96
+
97
+ var oldValues = values,
98
+ oldIndex = index,
99
+ i0 = 0,
100
+ i1 = 0;
101
+
102
+ // Otherwise, create new arrays into which to merge new and old.
103
+ values = new Array(n);
104
+ index = tesseract_index(n, n);
105
+
106
+ // Merge the old and new sorted values, and old and new index.
107
+ for (i = 0; i0 < n0 && i1 < n1; ++i) {
108
+ if (oldValues[i0] < newValues[i1]) {
109
+ values[i] = oldValues[i0];
110
+ index[i] = oldIndex[i0++];
111
+ } else {
112
+ values[i] = newValues[i1];
113
+ index[i] = newIndex[i1++] + n0;
114
+ }
115
+ }
116
+
117
+ // Add any remaining old values.
118
+ for (; i0 < n0; ++i0, ++i) {
119
+ values[i] = oldValues[i0];
120
+ index[i] = oldIndex[i0];
121
+ }
122
+
123
+ // Add any remaining new values.
124
+ for (; i1 < n1; ++i1, ++i) {
125
+ values[i] = newValues[i1];
126
+ index[i] = newIndex[i1] + n0;
127
+ }
128
+
129
+ // Bisect again to recompute lo0 and hi0.
130
+ bounds = refilter(values), lo0 = bounds[0], hi0 = bounds[1];
131
+ }
132
+
133
+ // When all filters have updated, notify index listeners of the new values.
134
+ function postAdd(newData, n0, n1) {
135
+ indexListeners.forEach(function(l) { l(newValues, newIndex, n0, n1); });
136
+ newValues = newIndex = null;
137
+ }
138
+
139
+ // Updates the selected values based on the specified bounds [lo, hi].
140
+ // This implementation is used by all the public filter methods.
141
+ function filterIndex(bounds) {
142
+ var i,
143
+ j,
144
+ k,
145
+ lo1 = bounds[0],
146
+ hi1 = bounds[1],
147
+ added = [],
148
+ removed = [];
149
+
150
+ // Fast incremental update based on previous lo index.
151
+ if (lo1 < lo0) {
152
+ for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) {
153
+ filters[k = index[i]] ^= one;
154
+ added.push(k);
155
+ }
156
+ } else if (lo1 > lo0) {
157
+ for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) {
158
+ filters[k = index[i]] ^= one;
159
+ removed.push(k);
160
+ }
161
+ }
162
+
163
+ // Fast incremental update based on previous hi index.
164
+ if (hi1 > hi0) {
165
+ for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) {
166
+ filters[k = index[i]] ^= one;
167
+ added.push(k);
168
+ }
169
+ } else if (hi1 < hi0) {
170
+ for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) {
171
+ filters[k = index[i]] ^= one;
172
+ removed.push(k);
173
+ }
174
+ }
175
+
176
+ lo0 = lo1;
177
+ hi0 = hi1;
178
+ filterListeners.forEach(function(l) { l(one, added, removed); });
179
+ return dimension;
180
+ }
181
+
182
+ // Filters this dimension using the specified range, value, or null.
183
+ // If the range is null, this is equivalent to filterAll.
184
+ // If the range is an array, this is equivalent to filterRange.
185
+ // Otherwise, this is equivalent to filterExact.
186
+ function filter(range) {
187
+ return range == null
188
+ ? filterAll() : Array.isArray(range)
189
+ ? filterRange(range)
190
+ : filterExact(range);
191
+ }
192
+
193
+ // Filters this dimension to select the exact value.
194
+ function filterExact(value) {
195
+ return filterIndex((refilter = tesseract_filterExact(bisect, value))(values));
196
+ }
197
+
198
+ // Filters this dimension to select the specified range [lo, hi].
199
+ // The lower bound is inclusive, and the upper bound is exclusive.
200
+ function filterRange(range) {
201
+ return filterIndex((refilter = tesseract_filterRange(bisect, range))(values));
202
+ }
203
+
204
+ // Clears any filters on this dimension.
205
+ function filterAll() {
206
+ return filterIndex((refilter = tesseract_filterAll)(values));
207
+ }
208
+
209
+ // Returns the top K selected records, based on this dimension's order.
210
+ // Note: observes this dimension's filter, unlike group and groupAll.
211
+ function top(k) {
212
+ var array = [],
213
+ i = hi0,
214
+ j;
215
+
216
+ while (--i >= lo0 && k > 0) {
217
+ if (!filters[j = index[i]]) {
218
+ array.push(data[j]);
219
+ --k;
220
+ }
221
+ }
222
+
223
+ return array;
224
+ }
225
+
226
+ // Adds a new group to this dimension, using the specified key function.
227
+ function group(key) {
228
+ var group = {
229
+ top: top,
230
+ all: all,
231
+ reduce: reduce,
232
+ reduceCount: reduceCount,
233
+ reduceSum: reduceSum,
234
+ order: order,
235
+ orderNatural: orderNatural,
236
+ size: size
237
+ };
238
+
239
+ var groups, // array of {key, value}
240
+ groupIndex, // object id ↦ group id
241
+ groupWidth = 8,
242
+ groupCapacity = tesseract_capacity(groupWidth),
243
+ k = 0, // cardinality
244
+ select,
245
+ heap,
246
+ reduceAdd,
247
+ reduceRemove,
248
+ reduceInitial,
249
+ update = tesseract_null,
250
+ reset = tesseract_null,
251
+ resetNeeded = true;
252
+
253
+ if (arguments.length < 1) key = tesseract_identity;
254
+
255
+ // The group listens to the tesseract for when any dimension changes, so
256
+ // that it can update the associated reduce values. It must also listen to
257
+ // the parent dimension for when data is added, and compute new keys.
258
+ filterListeners.push(update);
259
+ indexListeners.push(add);
260
+
261
+ // Incorporate any existing data into the grouping.
262
+ add(values, index, 0, n);
263
+
264
+ // Incorporates the specified new values into this group.
265
+ // This function is responsible for updating groups and groupIndex.
266
+ function add(newValues, newIndex, n0, n1) {
267
+ var oldGroups = groups,
268
+ reIndex = tesseract_index(k, groupCapacity),
269
+ add = reduceAdd,
270
+ initial = reduceInitial,
271
+ k0 = k, // old cardinality
272
+ i0 = 0, // index of old group
273
+ i1 = 0, // index of new record
274
+ j, // object id
275
+ g0, // old group
276
+ x0, // old key
277
+ x1, // new key
278
+ g, // group to add
279
+ x; // key of group to add
280
+
281
+ // If a reset is needed, we don't need to update the reduce values.
282
+ if (resetNeeded) add = initial = tesseract_null;
283
+
284
+ // Reset the new groups (k is a lower bound).
285
+ // Also, make sure that groupIndex exists and is long enough.
286
+ groups = new Array(k), k = 0;
287
+ groupIndex = k0 > 1 ? tesseract_arrayLengthen(groupIndex, n) : tesseract_index(n, groupCapacity);
288
+
289
+ // Get the first old key (x0 of g0), if it exists.
290
+ if (k0) x0 = (g0 = oldGroups[0]).key;
291
+
292
+ // Find the first new key (x1), skipping NaN keys.
293
+ while (i1 < n1 && !((x1 = key(newValues[i1])) >= x1)) ++i1;
294
+
295
+ // While new keys remain…
296
+ while (i1 < n1) {
297
+
298
+ // Determine the lesser of the two current keys; new and old.
299
+ // If there are no old keys remaining, then always add the new key.
300
+ if (g0 && x0 <= x1) {
301
+ g = g0, x = x0;
302
+
303
+ // Record the new index of the old group.
304
+ reIndex[i0] = k;
305
+
306
+ // Retrieve the next old key.
307
+ if (g0 = oldGroups[++i0]) x0 = g0.key;
308
+ } else {
309
+ g = {key: x1, value: initial()}, x = x1;
310
+ }
311
+
312
+ // Add the lesser group.
313
+ groups[k] = g;
314
+
315
+ // Add any selected records belonging to the added group, while
316
+ // advancing the new key and populating the associated group index.
317
+ while (!(x1 > x)) {
318
+ groupIndex[j = newIndex[i1] + n0] = k;
319
+ if (!(filters[j] & zero)) g.value = add(g.value, data[j]);
320
+ if (++i1 >= n1) break;
321
+ x1 = key(newValues[i1]);
322
+ }
323
+
324
+ groupIncrement();
325
+ }
326
+
327
+ // Add any remaining old groups that were greater than all new keys.
328
+ // No incremental reduce is needed; these groups have no new records.
329
+ // Also record the new index of the old group.
330
+ while (i0 < k0) {
331
+ groups[reIndex[i0] = k] = oldGroups[i0++];
332
+ groupIncrement();
333
+ }
334
+
335
+ // If we added any new groups before any old groups,
336
+ // update the group index of all the old records.
337
+ if (k > i0) for (i0 = 0; i0 < n0; ++i0) {
338
+ groupIndex[i0] = reIndex[groupIndex[i0]];
339
+ }
340
+
341
+ // Modify the update and reset behavior based on the cardinality.
342
+ // If the cardinality is less than or equal to one, then the groupIndex
343
+ // is not needed. If the cardinality is zero, then there are no records
344
+ // and therefore no groups to update or reset. Note that we also must
345
+ // change the registered listener to point to the new method.
346
+ j = filterListeners.indexOf(update);
347
+ if (k > 1) {
348
+ update = updateMany;
349
+ reset = resetMany;
350
+ } else {
351
+ if (k === 1) {
352
+ update = updateOne;
353
+ reset = resetOne;
354
+ } else {
355
+ update = tesseract_null;
356
+ reset = tesseract_null;
357
+ }
358
+ groupIndex = null;
359
+ }
360
+ filterListeners[j] = update;
361
+
362
+ // Count the number of added groups,
363
+ // and widen the group index as needed.
364
+ function groupIncrement() {
365
+ if (++k === groupCapacity) {
366
+ reIndex = tesseract_arrayWiden(reIndex, groupWidth <<= 1);
367
+ groupIndex = tesseract_arrayWiden(groupIndex, groupWidth);
368
+ groupCapacity = tesseract_capacity(groupWidth);
369
+ }
370
+ }
371
+ }
372
+
373
+ // Reduces the specified selected or deselected records.
374
+ // This function is only used when the cardinality is greater than 1.
375
+ function updateMany(filterOne, added, removed) {
376
+ if (filterOne === one || resetNeeded) return;
377
+
378
+ var i,
379
+ k,
380
+ n;
381
+
382
+ // Add the added values.
383
+ for (i = 0, n = added.length; i < n; ++i) {
384
+ if (!(filters[k = added[i]] & zero)) {
385
+ g = groups[groupIndex[k]];
386
+ g.value = reduceAdd(g.value, data[k]);
387
+ }
388
+ }
389
+
390
+ // Remove the removed values.
391
+ for (i = 0, n = removed.length; i < n; ++i) {
392
+ if ((filters[k = removed[i]] & zero) === filterOne) {
393
+ g = groups[groupIndex[k]];
394
+ g.value = reduceRemove(g.value, data[k]);
395
+ }
396
+ }
397
+ }
398
+
399
+ // Reduces the specified selected or deselected records.
400
+ // This function is only used when the cardinality is 1.
401
+ function updateOne(filterOne, added, removed) {
402
+ if (filterOne === one || resetNeeded) return;
403
+
404
+ var i,
405
+ k,
406
+ n,
407
+ g = groups[0];
408
+
409
+ // Add the added values.
410
+ for (i = 0, n = added.length; i < n; ++i) {
411
+ if (!(filters[k = added[i]] & zero)) {
412
+ g.value = reduceAdd(g.value, data[k]);
413
+ }
414
+ }
415
+
416
+ // Remove the removed values.
417
+ for (i = 0, n = removed.length; i < n; ++i) {
418
+ if ((filters[k = removed[i]] & zero) === filterOne) {
419
+ g.value = reduceRemove(g.value, data[k]);
420
+ }
421
+ }
422
+ }
423
+
424
+ // Recomputes the group reduce values from scratch.
425
+ // This function is only used when the cardinality is greater than 1.
426
+ function resetMany() {
427
+ var i,
428
+ g;
429
+
430
+ // Reset all group values.
431
+ for (i = 0; i < k; ++i) {
432
+ groups[i].value = reduceInitial();
433
+ }
434
+
435
+ // Add any selected records.
436
+ for (i = 0; i < n; ++i) {
437
+ if (!(filters[i] & zero)) {
438
+ g = groups[groupIndex[i]];
439
+ g.value = reduceAdd(g.value, data[i]);
440
+ }
441
+ }
442
+ }
443
+
444
+ // Recomputes the group reduce values from scratch.
445
+ // This function is only used when the cardinality is 1.
446
+ function resetOne() {
447
+ var i,
448
+ g = groups[0];
449
+
450
+ // Reset the singleton group values.
451
+ g.value = reduceInitial();
452
+
453
+ // Add any selected records.
454
+ for (i = 0; i < n; ++i) {
455
+ if (!(filters[i] & zero)) {
456
+ g.value = reduceAdd(g.value, data[i]);
457
+ }
458
+ }
459
+ }
460
+
461
+ // Returns the array of group values, in the dimension's natural order.
462
+ function all() {
463
+ if (resetNeeded) reset(), resetNeeded = false;
464
+ return groups;
465
+ }
466
+
467
+ // Returns a new array containing the top K group values, in reduce order.
468
+ function top(k) {
469
+ var top = select(all(), 0, groups.length, k);
470
+ return heap.sort(top, 0, top.length);
471
+ }
472
+
473
+ // Sets the reduce behavior for this group to use the specified functions.
474
+ // This method lazily recomputes the reduce values, waiting until needed.
475
+ function reduce(add, remove, initial) {
476
+ reduceAdd = add;
477
+ reduceRemove = remove;
478
+ reduceInitial = initial;
479
+ resetNeeded = true;
480
+ return group;
481
+ }
482
+
483
+ // A convenience method for reducing by count.
484
+ function reduceCount() {
485
+ return reduce(tesseract_reduceIncrement, tesseract_reduceDecrement, tesseract_zero);
486
+ }
487
+
488
+ // A convenience method for reducing by sum(value).
489
+ function reduceSum(value) {
490
+ return reduce(tesseract_reduceAdd(value), tesseract_reduceSubtract(value), tesseract_zero);
491
+ }
492
+
493
+ // Sets the reduce order, using the specified accessor.
494
+ function order(value) {
495
+ select = heapselect_by(valueOf);
496
+ heap = heap_by(valueOf);
497
+ function valueOf(d) { return value(d.value); }
498
+ return group;
499
+ }
500
+
501
+ // A convenience method for natural ordering by reduce value.
502
+ function orderNatural() {
503
+ return order(tesseract_identity);
504
+ }
505
+
506
+ // Returns the cardinality of this group, irrespective of any filters.
507
+ function size() {
508
+ return k;
509
+ }
510
+
511
+ return reduceCount().orderNatural();
512
+ }
513
+
514
+ // A convenience function for generating a singleton group.
515
+ function groupAll() {
516
+ var g = group(tesseract_null), all = g.all;
517
+ delete g.all;
518
+ delete g.top;
519
+ delete g.order;
520
+ delete g.orderNatural;
521
+ delete g.size;
522
+ g.value = function() { return all()[0].value; };
523
+ return g;
524
+ }
525
+
526
+ return dimension;
527
+ }
528
+
529
+ // A convenience method for groupAll on a dummy dimension.
530
+ // This implementation can be optimized since it is always cardinality 1.
531
+ function groupAll() {
532
+ var group = {
533
+ reduce: reduce,
534
+ reduceCount: reduceCount,
535
+ reduceSum: reduceSum,
536
+ value: value
537
+ };
538
+
539
+ var reduceValue,
540
+ reduceAdd,
541
+ reduceRemove,
542
+ reduceInitial,
543
+ resetNeeded = true;
544
+
545
+ // The group listens to the tesseract for when any dimension changes, so
546
+ // that it can update the reduce value. It must also listen to the parent
547
+ // dimension for when data is added.
548
+ filterListeners.push(update);
549
+ dataListeners.push(add);
550
+
551
+ // For consistency; actually a no-op since resetNeeded is true.
552
+ add(data, 0, n);
553
+
554
+ // Incorporates the specified new values into this group.
555
+ function add(newData, n0, n1) {
556
+ var i;
557
+
558
+ if (resetNeeded) return;
559
+
560
+ // Add the added values.
561
+ for (i = n0; i < n; ++i) {
562
+ if (!filters[i]) {
563
+ reduceValue = reduceAdd(reduceValue, data[i]);
564
+ }
565
+ }
566
+ }
567
+
568
+ // Reduces the specified selected or deselected records.
569
+ function update(filterOne, added, removed) {
570
+ var i,
571
+ k,
572
+ n;
573
+
574
+ if (resetNeeded) return;
575
+
576
+ // Add the added values.
577
+ for (i = 0, n = added.length; i < n; ++i) {
578
+ if (!filters[k = added[i]]) {
579
+ reduceValue = reduceAdd(reduceValue, data[k]);
580
+ }
581
+ }
582
+
583
+ // Remove the removed values.
584
+ for (i = 0, n = removed.length; i < n; ++i) {
585
+ if (filters[k = removed[i]] === filterOne) {
586
+ reduceValue = reduceRemove(reduceValue, data[k]);
587
+ }
588
+ }
589
+ }
590
+
591
+ // Recomputes the group reduce value from scratch.
592
+ function reset() {
593
+ var i;
594
+
595
+ reduceValue = reduceInitial();
596
+
597
+ for (i = 0; i < n; ++i) {
598
+ if (!filters[i]) {
599
+ reduceValue = reduceAdd(reduceValue, data[i]);
600
+ }
601
+ }
602
+ }
603
+
604
+ // Sets the reduce behavior for this group to use the specified functions.
605
+ // This method lazily recomputes the reduce value, waiting until needed.
606
+ function reduce(add, remove, initial) {
607
+ reduceAdd = add;
608
+ reduceRemove = remove;
609
+ reduceInitial = initial;
610
+ resetNeeded = true;
611
+ return group;
612
+ }
613
+
614
+ // A convenience method for reducing by count.
615
+ function reduceCount() {
616
+ return reduce(tesseract_reduceIncrement, tesseract_reduceDecrement, tesseract_zero);
617
+ }
618
+
619
+ // A convenience method for reducing by sum(value).
620
+ function reduceSum(value) {
621
+ return reduce(tesseract_reduceAdd(value), tesseract_reduceSubtract(value), tesseract_zero);
622
+ }
623
+
624
+ // Returns the computed reduce value.
625
+ function value() {
626
+ if (resetNeeded) reset(), resetNeeded = false;
627
+ return reduceValue;
628
+ }
629
+
630
+ return reduceCount();
631
+ }
632
+
633
+ // Returns the number of records in this tesseract, irrespective of any filters.
634
+ function size() {
635
+ return n;
636
+ }
637
+
638
+ return arguments.length
639
+ ? add(arguments[0])
640
+ : tesseract;
641
+ }
642
+
643
+ // Returns an array of size n, big enough to store ids up to m.
644
+ function tesseract_index(n, m) {
645
+ return (m < 0x101
646
+ ? tesseract_array8 : m < 0x10001
647
+ ? tesseract_array16
648
+ : tesseract_array32)(n);
649
+ }
650
+
651
+ // Constructs a new array of size n, with sequential values from 0 to n - 1.
652
+ function tesseract_range(n) {
653
+ var range = tesseract_index(n, n);
654
+ for (var i = -1; ++i < n;) range[i] = i;
655
+ return range;
656
+ }
657
+
658
+ function tesseract_capacity(w) {
659
+ return w === 8
660
+ ? 0x100 : w === 16
661
+ ? 0x10000
662
+ : 0x100000000;
663
+ }