From bfef54e0666c3ac7c6e3fa1bd1f1ae55b9f3f9b0 Mon Sep 17 00:00:00 2001 From: Reece Date: Thu, 11 Dec 2014 21:50:07 -0500 Subject: [PATCH] Remove crossfilter example since it's the same thing as lineWithFocusChart and just uses the unnecessary 3rd party cross filter library? --- examples/crossfilter.html | 130 ---- examples/lib/crossfilter.js | 1180 ------------------------------- examples/lib/crossfilter.min.js | 1 - 3 files changed, 1311 deletions(-) delete mode 100644 examples/crossfilter.html delete mode 100644 examples/lib/crossfilter.js delete mode 100644 examples/lib/crossfilter.min.js diff --git a/examples/crossfilter.html b/examples/crossfilter.html deleted file mode 100644 index 4322ee159..000000000 --- a/examples/crossfilter.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - -
- -
- - - - diff --git a/examples/lib/crossfilter.js b/examples/lib/crossfilter.js deleted file mode 100644 index 1aaabca28..000000000 --- a/examples/lib/crossfilter.js +++ /dev/null @@ -1,1180 +0,0 @@ -(function(exports){ -crossfilter.version = "1.0.3"; -function crossfilter_identity(d) { - return d; -} -crossfilter.permute = permute; - -function permute(array, index) { - for (var i = 0, n = index.length, copy = new Array(n); i < n; ++i) { - copy[i] = array[index[i]]; - } - return copy; -} -var bisect = crossfilter.bisect = bisect_by(crossfilter_identity); - -bisect.by = bisect_by; - -function bisect_by(f) { - - // Locate the insertion point for x in a to maintain sorted order. The - // arguments lo and hi may be used to specify a subset of the array which - // should be considered; by default the entire array is used. If x is already - // present in a, the insertion point will be before (to the left of) any - // existing entries. The return value is suitable for use as the first - // argument to `array.splice` assuming that a is already sorted. - // - // The returned insertion point i partitions the array a into two halves so - // that all v < x for v in a[lo:i] for the left side and all v >= x for v in - // a[i:hi] for the right side. - function bisectLeft(a, x, lo, hi) { - while (lo < hi) { - var mid = lo + hi >> 1; - if (f(a[mid]) < x) lo = mid + 1; - else hi = mid; - } - return lo; - } - - // Similar to bisectLeft, but returns an insertion point which comes after (to - // the right of) any existing entries of x in a. - // - // The returned insertion point i partitions the array into two halves so that - // all v <= x for v in a[lo:i] for the left side and all v > x for v in - // a[i:hi] for the right side. - function bisectRight(a, x, lo, hi) { - while (lo < hi) { - var mid = lo + hi >> 1; - if (x < f(a[mid])) hi = mid; - else lo = mid + 1; - } - return lo; - } - - bisectRight.right = bisectRight; - bisectRight.left = bisectLeft; - return bisectRight; -} -var heap = crossfilter.heap = heap_by(crossfilter_identity); - -heap.by = heap_by; - -function heap_by(f) { - - // Builds a binary heap within the specified array a[lo:hi]. The heap has the - // property such that the parent a[lo+i] is always less than or equal to its - // two children: a[lo+2*i+1] and a[lo+2*i+2]. - function heap(a, lo, hi) { - var n = hi - lo, - i = (n >>> 1) + 1; - while (--i > 0) sift(a, i, n, lo); - return a; - } - - // Sorts the specified array a[lo:hi] in descending order, assuming it is - // already a heap. - function sort(a, lo, hi) { - var n = hi - lo, - t; - while (--n > 0) t = a[lo], a[lo] = a[lo + n], a[lo + n] = t, sift(a, 1, n, lo); - return a; - } - - // Sifts the element a[lo+i-1] down the heap, where the heap is the contiguous - // slice of array a[lo:lo+n]. This method can also be used to update the heap - // incrementally, without incurring the full cost of reconstructing the heap. - function sift(a, i, n, lo) { - var d = a[--lo + i], - x = f(d), - child; - while ((child = i << 1) <= n) { - if (child < n && f(a[lo + child]) > f(a[lo + child + 1])) child++; - if (x <= f(a[lo + child])) break; - a[lo + i] = a[lo + child]; - i = child; - } - a[lo + i] = d; - } - - heap.sort = sort; - return heap; -} -var heapselect = crossfilter.heapselect = heapselect_by(crossfilter_identity); - -heapselect.by = heapselect_by; - -function heapselect_by(f) { - var heap = heap_by(f); - - // Returns a new array containing the top k elements in the array a[lo:hi]. - // The returned array is not sorted, but maintains the heap property. If k is - // greater than hi - lo, then fewer than k elements will be returned. The - // order of elements in a is unchanged by this operation. - function heapselect(a, lo, hi, k) { - var queue = new Array(k = Math.min(hi - lo, k)), - min, - i, - x, - d; - - for (i = 0; i < k; ++i) queue[i] = a[lo++]; - heap(queue, 0, k); - - if (lo < hi) { - min = f(queue[0]); - do { - if (x = f(d = a[lo]) > min) { - queue[0] = d; - min = f(heap(queue, 0, k)[0]); - } - } while (++lo < hi); - } - - return queue; - } - - return heapselect; -} -var insertionsort = crossfilter.insertionsort = insertionsort_by(crossfilter_identity); - -insertionsort.by = insertionsort_by; - -function insertionsort_by(f) { - - function insertionsort(a, lo, hi) { - for (var i = lo + 1; i < hi; ++i) { - for (var j = i, t = a[i], x = f(t); j > lo && f(a[j - 1]) > x; --j) { - a[j] = a[j - 1]; - } - a[j] = t; - } - return a; - } - - return insertionsort; -} -// Algorithm designed by Vladimir Yaroslavskiy. -// Implementation based on the Dart project; see lib/dart/LICENSE for details. - -var quicksort = crossfilter.quicksort = quicksort_by(crossfilter_identity); - -quicksort.by = quicksort_by; - -function quicksort_by(f) { - var insertionsort = insertionsort_by(f); - - function sort(a, lo, hi) { - return (hi - lo < quicksort_sizeThreshold - ? insertionsort - : quicksort)(a, lo, hi); - } - - function quicksort(a, lo, hi) { - - // Compute the two pivots by looking at 5 elements. - var sixth = (hi - lo) / 6 | 0, - i1 = lo + sixth, - i5 = hi - 1 - sixth, - i3 = lo + hi - 1 >> 1, // The midpoint. - i2 = i3 - sixth, - i4 = i3 + sixth; - - var e1 = a[i1], x1 = f(e1), - e2 = a[i2], x2 = f(e2), - e3 = a[i3], x3 = f(e3), - e4 = a[i4], x4 = f(e4), - e5 = a[i5], x5 = f(e5); - - var t; - - // Sort the selected 5 elements using a sorting network. - if (x1 > x2) t = e1, e1 = e2, e2 = t, t = x1, x1 = x2, x2 = t; - if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; - if (x1 > x3) t = e1, e1 = e3, e3 = t, t = x1, x1 = x3, x3 = t; - if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; - if (x1 > x4) t = e1, e1 = e4, e4 = t, t = x1, x1 = x4, x4 = t; - if (x3 > x4) t = e3, e3 = e4, e4 = t, t = x3, x3 = x4, x4 = t; - if (x2 > x5) t = e2, e2 = e5, e5 = t, t = x2, x2 = x5, x5 = t; - if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; - if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; - - var pivot1 = e2, pivotValue1 = x2, - pivot2 = e4, pivotValue2 = x4; - - // e2 and e4 have been saved in the pivot variables. They will be written - // back, once the partitioning is finished. - a[i1] = e1; - a[i2] = a[lo]; - a[i3] = e3; - a[i4] = a[hi - 1]; - a[i5] = e5; - - var less = lo + 1, // First element in the middle partition. - great = hi - 2; // Last element in the middle partition. - - // Note that for value comparison, <, <=, >= and > coerce to a primitive via - // Object.prototype.valueOf; == and === do not, so in order to be consistent - // with natural order (such as for Date objects), we must do two compares. - var pivotsEqual = pivotValue1 <= pivotValue2 && pivotValue1 >= pivotValue2; - if (pivotsEqual) { - - // Degenerated case where the partitioning becomes a dutch national flag - // problem. - // - // [ | < pivot | == pivot | unpartitioned | > pivot | ] - // ^ ^ ^ ^ ^ - // left less k great right - // - // a[left] and a[right] are undefined and are filled after the - // partitioning. - // - // Invariants: - // 1) for x in ]left, less[ : x < pivot. - // 2) for x in [less, k[ : x == pivot. - // 3) for x in ]great, right[ : x > pivot. - for (var k = less; k <= great; ++k) { - var ek = a[k], xk = f(ek); - if (xk < pivotValue1) { - if (k !== less) { - a[k] = a[less]; - a[less] = ek; - } - ++less; - } else if (xk > pivotValue1) { - - // Find the first element <= pivot in the range [k - 1, great] and - // put [:ek:] there. We know that such an element must exist: - // When k == less, then el3 (which is equal to pivot) lies in the - // interval. Otherwise a[k - 1] == pivot and the search stops at k-1. - // Note that in the latter case invariant 2 will be violated for a - // short amount of time. The invariant will be restored when the - // pivots are put into their final positions. - while (true) { - var greatValue = f(a[great]); - if (greatValue > pivotValue1) { - great--; - // This is the only location in the while-loop where a new - // iteration is started. - continue; - } else if (greatValue < pivotValue1) { - // Triple exchange. - a[k] = a[less]; - a[less++] = a[great]; - a[great--] = ek; - break; - } else { - a[k] = a[great]; - a[great--] = ek; - // Note: if great < k then we will exit the outer loop and fix - // invariant 2 (which we just violated). - break; - } - } - } - } - } else { - - // We partition the list into three parts: - // 1. < pivot1 - // 2. >= pivot1 && <= pivot2 - // 3. > pivot2 - // - // During the loop we have: - // [ | < pivot1 | >= pivot1 && <= pivot2 | unpartitioned | > pivot2 | ] - // ^ ^ ^ ^ ^ - // left less k great right - // - // a[left] and a[right] are undefined and are filled after the - // partitioning. - // - // Invariants: - // 1. for x in ]left, less[ : x < pivot1 - // 2. for x in [less, k[ : pivot1 <= x && x <= pivot2 - // 3. for x in ]great, right[ : x > pivot2 - for (var k = less; k <= great; k++) { - var ek = a[k], xk = f(ek); - if (xk < pivotValue1) { - if (k !== less) { - a[k] = a[less]; - a[less] = ek; - } - ++less; - } else { - if (xk > pivotValue2) { - while (true) { - var greatValue = f(a[great]); - if (greatValue > pivotValue2) { - great--; - if (great < k) break; - // This is the only location inside the loop where a new - // iteration is started. - continue; - } else { - // a[great] <= pivot2. - if (greatValue < pivotValue1) { - // Triple exchange. - a[k] = a[less]; - a[less++] = a[great]; - a[great--] = ek; - } else { - // a[great] >= pivot1. - a[k] = a[great]; - a[great--] = ek; - } - break; - } - } - } - } - } - } - - // Move pivots into their final positions. - // We shrunk the list from both sides (a[left] and a[right] have - // meaningless values in them) and now we move elements from the first - // and third partition into these locations so that we can store the - // pivots. - a[lo] = a[less - 1]; - a[less - 1] = pivot1; - a[hi - 1] = a[great + 1]; - a[great + 1] = pivot2; - - // The list is now partitioned into three partitions: - // [ < pivot1 | >= pivot1 && <= pivot2 | > pivot2 ] - // ^ ^ ^ ^ - // left less great right - - // Recursive descent. (Don't include the pivot values.) - sort(a, lo, less - 1); - sort(a, great + 2, hi); - - if (pivotsEqual) { - // All elements in the second partition are equal to the pivot. No - // need to sort them. - return a; - } - - // In theory it should be enough to call _doSort recursively on the second - // partition. - // The Android source however removes the pivot elements from the recursive - // call if the second partition is too large (more than 2/3 of the list). - if (less < i1 && great > i5) { - var lessValue, greatValue; - while ((lessValue = f(a[less])) <= pivotValue1 && lessValue >= pivotValue1) ++less; - while ((greatValue = f(a[great])) <= pivotValue2 && greatValue >= pivotValue2) --great; - - // Copy paste of the previous 3-way partitioning with adaptions. - // - // We partition the list into three parts: - // 1. == pivot1 - // 2. > pivot1 && < pivot2 - // 3. == pivot2 - // - // During the loop we have: - // [ == pivot1 | > pivot1 && < pivot2 | unpartitioned | == pivot2 ] - // ^ ^ ^ - // less k great - // - // Invariants: - // 1. for x in [ *, less[ : x == pivot1 - // 2. for x in [less, k[ : pivot1 < x && x < pivot2 - // 3. for x in ]great, * ] : x == pivot2 - for (var k = less; k <= great; k++) { - var ek = a[k], xk = f(ek); - if (xk <= pivotValue1 && xk >= pivotValue1) { - if (k !== less) { - a[k] = a[less]; - a[less] = ek; - } - less++; - } else { - if (xk <= pivotValue2 && xk >= pivotValue2) { - while (true) { - var greatValue = f(a[great]); - if (greatValue <= pivotValue2 && greatValue >= pivotValue2) { - great--; - if (great < k) break; - // This is the only location inside the loop where a new - // iteration is started. - continue; - } else { - // a[great] < pivot2. - if (greatValue < pivotValue1) { - // Triple exchange. - a[k] = a[less]; - a[less++] = a[great]; - a[great--] = ek; - } else { - // a[great] == pivot1. - a[k] = a[great]; - a[great--] = ek; - } - break; - } - } - } - } - } - } - - // The second partition has now been cleared of pivot elements and looks - // as follows: - // [ * | > pivot1 && < pivot2 | * ] - // ^ ^ - // less great - // Sort the second partition using recursive descent. - - // The second partition looks as follows: - // [ * | >= pivot1 && <= pivot2 | * ] - // ^ ^ - // less great - // Simply sort it by recursive descent. - - return sort(a, less, great + 1); - } - - return sort; -} - -var quicksort_sizeThreshold = 32; -var crossfilter_array8 = crossfilter_arrayUntyped, - crossfilter_array16 = crossfilter_arrayUntyped, - crossfilter_array32 = crossfilter_arrayUntyped, - crossfilter_arrayLengthen = crossfilter_identity, - crossfilter_arrayWiden = crossfilter_identity; - -if (typeof Uint8Array !== "undefined") { - crossfilter_array8 = function(n) { return new Uint8Array(n); }; - crossfilter_array16 = function(n) { return new Uint16Array(n); }; - crossfilter_array32 = function(n) { return new Uint32Array(n); }; - - crossfilter_arrayLengthen = function(array, length) { - var copy = new array.constructor(length); - copy.set(array); - return copy; - }; - - crossfilter_arrayWiden = function(array, width) { - var copy; - switch (width) { - case 16: copy = crossfilter_array16(array.length); break; - case 32: copy = crossfilter_array32(array.length); break; - default: throw new Error("invalid array width!"); - } - copy.set(array); - return copy; - }; -} - -function crossfilter_arrayUntyped(n) { - return new Array(n); -} -function crossfilter_filterExact(bisect, value) { - return function(values) { - var n = values.length; - return [bisect.left(values, value, 0, n), bisect.right(values, value, 0, n)]; - }; -} - -function crossfilter_filterRange(bisect, range) { - var min = range[0], - max = range[1]; - return function(values) { - var n = values.length; - return [bisect.left(values, min, 0, n), bisect.left(values, max, 0, n)]; - }; -} - -function crossfilter_filterAll(values) { - return [0, values.length]; -} -function crossfilter_null() { - return null; -} -function crossfilter_zero() { - return 0; -} -function crossfilter_reduceIncrement(p) { - return p + 1; -} - -function crossfilter_reduceDecrement(p) { - return p - 1; -} - -function crossfilter_reduceAdd(f) { - return function(p, v) { - return p + +f(v); - }; -} - -function crossfilter_reduceSubtract(f) { - return function(p, v) { - return p - f(v); - }; -} -exports.crossfilter = crossfilter; - -function crossfilter() { - var crossfilter = { - add: add, - dimension: dimension, - groupAll: groupAll, - size: size - }; - - var data = [], // the records - n = 0, // the number of records; data.length - m = 0, // number of dimensions in use - M = 8, // number of dimensions that can fit in `filters` - filters = crossfilter_array8(0), // M bits per record; 1 is filtered out - filterListeners = [], // when the filters change - dataListeners = []; // when data is added - - // Adds the specified new records to this crossfilter. - function add(newData) { - var n0 = n, - n1 = newData.length; - - // If there's actually new data to add… - // Merge the new data into the existing data. - // Lengthen the filter bitset to handle the new records. - // Notify listeners (dimensions and groups) that new data is available. - if (n1) { - data = data.concat(newData); - filters = crossfilter_arrayLengthen(filters, n += n1); - dataListeners.forEach(function(l) { l(newData, n0, n1); }); - } - - return crossfilter; - } - - // Adds a new dimension with the specified value accessor function. - function dimension(value) { - var dimension = { - filter: filter, - filterExact: filterExact, - filterRange: filterRange, - filterAll: filterAll, - top: top, - group: group, - groupAll: groupAll - }; - - var one = 1 << m++, // bit mask, e.g., 00001000 - zero = ~one, // inverted one, e.g., 11110111 - values, // sorted, cached array - index, // value rank ↦ object id - newValues, // temporary array storing newly-added values - newIndex, // temporary array storing newly-added index - sort = quicksort_by(function(i) { return newValues[i]; }), - refilter = crossfilter_filterAll, // for recomputing filter - indexListeners = [], // when data is added - lo0 = 0, - hi0 = 0; - - // Updating a dimension is a two-stage process. First, we must update the - // associated filters for the newly-added records. Once all dimensions have - // updated their filters, the groups are notified to update. - dataListeners.unshift(preAdd); - dataListeners.push(postAdd); - - // Incorporate any existing data into this dimension, and make sure that the - // filter bitset is wide enough to handle the new dimension. - if (m > M) filters = crossfilter_arrayWiden(filters, M <<= 1); - preAdd(data, 0, n); - postAdd(data, 0, n); - - // Incorporates the specified new records into this dimension. - // This function is responsible for updating filters, values, and index. - function preAdd(newData, n0, n1) { - - // Permute new values into natural order using a sorted index. - newValues = newData.map(value); - newIndex = sort(crossfilter_range(n1), 0, n1); - newValues = permute(newValues, newIndex); - - // Bisect newValues to determine which new records are selected. - var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1], i; - for (i = 0; i < lo1; ++i) filters[newIndex[i] + n0] |= one; - for (i = hi1; i < n1; ++i) filters[newIndex[i] + n0] |= one; - - // If this dimension previously had no data, then we don't need to do the - // more expensive merge operation; use the new values and index as-is. - if (!n0) { - values = newValues; - index = newIndex; - lo0 = lo1; - hi0 = hi1; - return; - } - - var oldValues = values, - oldIndex = index, - i0 = 0, - i1 = 0; - - // Otherwise, create new arrays into which to merge new and old. - values = new Array(n); - index = crossfilter_index(n, n); - - // Merge the old and new sorted values, and old and new index. - for (i = 0; i0 < n0 && i1 < n1; ++i) { - if (oldValues[i0] < newValues[i1]) { - values[i] = oldValues[i0]; - index[i] = oldIndex[i0++]; - } else { - values[i] = newValues[i1]; - index[i] = newIndex[i1++] + n0; - } - } - - // Add any remaining old values. - for (; i0 < n0; ++i0, ++i) { - values[i] = oldValues[i0]; - index[i] = oldIndex[i0]; - } - - // Add any remaining new values. - for (; i1 < n1; ++i1, ++i) { - values[i] = newValues[i1]; - index[i] = newIndex[i1] + n0; - } - - // Bisect again to recompute lo0 and hi0. - bounds = refilter(values), lo0 = bounds[0], hi0 = bounds[1]; - } - - // When all filters have updated, notify index listeners of the new values. - function postAdd(newData, n0, n1) { - indexListeners.forEach(function(l) { l(newValues, newIndex, n0, n1); }); - newValues = newIndex = null; - } - - // Updates the selected values based on the specified bounds [lo, hi]. - // This implementation is used by all the public filter methods. - function filterIndex(bounds) { - var i, - j, - k, - lo1 = bounds[0], - hi1 = bounds[1], - added = [], - removed = []; - - // Fast incremental update based on previous lo index. - if (lo1 < lo0) { - for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) { - filters[k = index[i]] ^= one; - added.push(k); - } - } else if (lo1 > lo0) { - for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) { - filters[k = index[i]] ^= one; - removed.push(k); - } - } - - // Fast incremental update based on previous hi index. - if (hi1 > hi0) { - for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) { - filters[k = index[i]] ^= one; - added.push(k); - } - } else if (hi1 < hi0) { - for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) { - filters[k = index[i]] ^= one; - removed.push(k); - } - } - - lo0 = lo1; - hi0 = hi1; - filterListeners.forEach(function(l) { l(one, added, removed); }); - return dimension; - } - - // Filters this dimension using the specified range, value, or null. - // If the range is null, this is equivalent to filterAll. - // If the range is an array, this is equivalent to filterRange. - // Otherwise, this is equivalent to filterExact. - function filter(range) { - return range == null - ? filterAll() : Array.isArray(range) - ? filterRange(range) - : filterExact(range); - } - - // Filters this dimension to select the exact value. - function filterExact(value) { - return filterIndex((refilter = crossfilter_filterExact(bisect, value))(values)); - } - - // Filters this dimension to select the specified range [lo, hi]. - // The lower bound is inclusive, and the upper bound is exclusive. - function filterRange(range) { - return filterIndex((refilter = crossfilter_filterRange(bisect, range))(values)); - } - - // Clears any filters on this dimension. - function filterAll() { - return filterIndex((refilter = crossfilter_filterAll)(values)); - } - - // Returns the top K selected records, based on this dimension's order. - // Note: observes this dimension's filter, unlike group and groupAll. - function top(k) { - var array = [], - i = hi0, - j; - - while (--i >= lo0 && k > 0) { - if (!filters[j = index[i]]) { - array.push(data[j]); - --k; - } - } - - return array; - } - - // Adds a new group to this dimension, using the specified key function. - function group(key) { - var group = { - top: top, - all: all, - reduce: reduce, - reduceCount: reduceCount, - reduceSum: reduceSum, - order: order, - orderNatural: orderNatural, - size: size - }; - - var groups, // array of {key, value} - groupIndex, // object id ↦ group id - groupWidth = 8, - groupCapacity = crossfilter_capacity(groupWidth), - k = 0, // cardinality - select, - heap, - reduceAdd, - reduceRemove, - reduceInitial, - update = crossfilter_null, - reset = crossfilter_null, - resetNeeded = true; - - if (arguments.length < 1) key = crossfilter_identity; - - // The group listens to the crossfilter for when any dimension changes, so - // that it can update the associated reduce values. It must also listen to - // the parent dimension for when data is added, and compute new keys. - filterListeners.push(update); - indexListeners.push(add); - - // Incorporate any existing data into the grouping. - add(values, index, 0, n); - - // Incorporates the specified new values into this group. - // This function is responsible for updating groups and groupIndex. - function add(newValues, newIndex, n0, n1) { - var oldGroups = groups, - reIndex = crossfilter_index(k, groupCapacity), - add = reduceAdd, - initial = reduceInitial, - k0 = k, // old cardinality - i0 = 0, // index of old group - i1 = 0, // index of new record - j, // object id - g0, // old group - x0, // old key - x1, // new key - g, // group to add - x; // key of group to add - - // If a reset is needed, we don't need to update the reduce values. - if (resetNeeded) add = initial = crossfilter_null; - - // Reset the new groups (k is a lower bound). - // Also, make sure that groupIndex exists and is long enough. - groups = new Array(k), k = 0; - groupIndex = k0 > 1 ? crossfilter_arrayLengthen(groupIndex, n) : crossfilter_index(n, groupCapacity); - - // Get the first old key (x0 of g0), if it exists. - if (k0) x0 = (g0 = oldGroups[0]).key; - - // Find the first new key (x1), skipping NaN keys. - while (i1 < n1 && !((x1 = key(newValues[i1])) >= x1)) ++i1; - - // While new keys remain… - while (i1 < n1) { - - // Determine the lesser of the two current keys; new and old. - // If there are no old keys remaining, then always add the new key. - if (g0 && x0 <= x1) { - g = g0, x = x0; - - // Record the new index of the old group. - reIndex[i0] = k; - - // Retrieve the next old key. - if (g0 = oldGroups[++i0]) x0 = g0.key; - } else { - g = {key: x1, value: initial()}, x = x1; - } - - // Add the lesser group. - groups[k] = g; - - // Add any selected records belonging to the added group, while - // advancing the new key and populating the associated group index. - while (!(x1 > x)) { - groupIndex[j = newIndex[i1] + n0] = k; - if (!(filters[j] & zero)) g.value = add(g.value, data[j]); - if (++i1 >= n1) break; - x1 = key(newValues[i1]); - } - - groupIncrement(); - } - - // Add any remaining old groups that were greater than all new keys. - // No incremental reduce is needed; these groups have no new records. - // Also record the new index of the old group. - while (i0 < k0) { - groups[reIndex[i0] = k] = oldGroups[i0++]; - groupIncrement(); - } - - // If we added any new groups before any old groups, - // update the group index of all the old records. - if (k > i0) for (i0 = 0; i0 < n0; ++i0) { - groupIndex[i0] = reIndex[groupIndex[i0]]; - } - - // Modify the update and reset behavior based on the cardinality. - // If the cardinality is less than or equal to one, then the groupIndex - // is not needed. If the cardinality is zero, then there are no records - // and therefore no groups to update or reset. Note that we also must - // change the registered listener to point to the new method. - j = filterListeners.indexOf(update); - if (k > 1) { - update = updateMany; - reset = resetMany; - } else { - if (k === 1) { - update = updateOne; - reset = resetOne; - } else { - update = crossfilter_null; - reset = crossfilter_null; - } - groupIndex = null; - } - filterListeners[j] = update; - - // Count the number of added groups, - // and widen the group index as needed. - function groupIncrement() { - if (++k === groupCapacity) { - reIndex = crossfilter_arrayWiden(reIndex, groupWidth <<= 1); - groupIndex = crossfilter_arrayWiden(groupIndex, groupWidth); - groupCapacity = crossfilter_capacity(groupWidth); - } - } - } - - // Reduces the specified selected or deselected records. - // This function is only used when the cardinality is greater than 1. - function updateMany(filterOne, added, removed) { - if (filterOne === one || resetNeeded) return; - - var i, - k, - n, - g; - - // Add the added values. - for (i = 0, n = added.length; i < n; ++i) { - if (!(filters[k = added[i]] & zero)) { - g = groups[groupIndex[k]]; - g.value = reduceAdd(g.value, data[k]); - } - } - - // Remove the removed values. - for (i = 0, n = removed.length; i < n; ++i) { - if ((filters[k = removed[i]] & zero) === filterOne) { - g = groups[groupIndex[k]]; - g.value = reduceRemove(g.value, data[k]); - } - } - } - - // Reduces the specified selected or deselected records. - // This function is only used when the cardinality is 1. - function updateOne(filterOne, added, removed) { - if (filterOne === one || resetNeeded) return; - - var i, - k, - n, - g = groups[0]; - - // Add the added values. - for (i = 0, n = added.length; i < n; ++i) { - if (!(filters[k = added[i]] & zero)) { - g.value = reduceAdd(g.value, data[k]); - } - } - - // Remove the removed values. - for (i = 0, n = removed.length; i < n; ++i) { - if ((filters[k = removed[i]] & zero) === filterOne) { - g.value = reduceRemove(g.value, data[k]); - } - } - } - - // Recomputes the group reduce values from scratch. - // This function is only used when the cardinality is greater than 1. - function resetMany() { - var i, - g; - - // Reset all group values. - for (i = 0; i < k; ++i) { - groups[i].value = reduceInitial(); - } - - // Add any selected records. - for (i = 0; i < n; ++i) { - if (!(filters[i] & zero)) { - g = groups[groupIndex[i]]; - g.value = reduceAdd(g.value, data[i]); - } - } - } - - // Recomputes the group reduce values from scratch. - // This function is only used when the cardinality is 1. - function resetOne() { - var i, - g = groups[0]; - - // Reset the singleton group values. - g.value = reduceInitial(); - - // Add any selected records. - for (i = 0; i < n; ++i) { - if (!(filters[i] & zero)) { - g.value = reduceAdd(g.value, data[i]); - } - } - } - - // Returns the array of group values, in the dimension's natural order. - function all() { - if (resetNeeded) reset(), resetNeeded = false; - return groups; - } - - // Returns a new array containing the top K group values, in reduce order. - function top(k) { - var top = select(all(), 0, groups.length, k); - return heap.sort(top, 0, top.length); - } - - // Sets the reduce behavior for this group to use the specified functions. - // This method lazily recomputes the reduce values, waiting until needed. - function reduce(add, remove, initial) { - reduceAdd = add; - reduceRemove = remove; - reduceInitial = initial; - resetNeeded = true; - return group; - } - - // A convenience method for reducing by count. - function reduceCount() { - return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero); - } - - // A convenience method for reducing by sum(value). - function reduceSum(value) { - return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero); - } - - // Sets the reduce order, using the specified accessor. - function order(value) { - select = heapselect_by(valueOf); - heap = heap_by(valueOf); - function valueOf(d) { return value(d.value); } - return group; - } - - // A convenience method for natural ordering by reduce value. - function orderNatural() { - return order(crossfilter_identity); - } - - // Returns the cardinality of this group, irrespective of any filters. - function size() { - return k; - } - - return reduceCount().orderNatural(); - } - - // A convenience function for generating a singleton group. - function groupAll() { - var g = group(crossfilter_null), all = g.all; - delete g.all; - delete g.top; - delete g.order; - delete g.orderNatural; - delete g.size; - g.value = function() { return all()[0].value; }; - return g; - } - - return dimension; - } - - // A convenience method for groupAll on a dummy dimension. - // This implementation can be optimized since it is always cardinality 1. - function groupAll() { - var group = { - reduce: reduce, - reduceCount: reduceCount, - reduceSum: reduceSum, - value: value - }; - - var reduceValue, - reduceAdd, - reduceRemove, - reduceInitial, - resetNeeded = true; - - // The group listens to the crossfilter for when any dimension changes, so - // that it can update the reduce value. It must also listen to the parent - // dimension for when data is added. - filterListeners.push(update); - dataListeners.push(add); - - // For consistency; actually a no-op since resetNeeded is true. - add(data, 0, n); - - // Incorporates the specified new values into this group. - function add(newData, n0, n1) { - var i; - - if (resetNeeded) return; - - // Add the added values. - for (i = n0; i < n; ++i) { - if (!filters[i]) { - reduceValue = reduceAdd(reduceValue, data[i]); - } - } - } - - // Reduces the specified selected or deselected records. - function update(filterOne, added, removed) { - var i, - k, - n; - - if (resetNeeded) return; - - // Add the added values. - for (i = 0, n = added.length; i < n; ++i) { - if (!filters[k = added[i]]) { - reduceValue = reduceAdd(reduceValue, data[k]); - } - } - - // Remove the removed values. - for (i = 0, n = removed.length; i < n; ++i) { - if (filters[k = removed[i]] === filterOne) { - reduceValue = reduceRemove(reduceValue, data[k]); - } - } - } - - // Recomputes the group reduce value from scratch. - function reset() { - var i; - - reduceValue = reduceInitial(); - - for (i = 0; i < n; ++i) { - if (!filters[i]) { - reduceValue = reduceAdd(reduceValue, data[i]); - } - } - } - - // Sets the reduce behavior for this group to use the specified functions. - // This method lazily recomputes the reduce value, waiting until needed. - function reduce(add, remove, initial) { - reduceAdd = add; - reduceRemove = remove; - reduceInitial = initial; - resetNeeded = true; - return group; - } - - // A convenience method for reducing by count. - function reduceCount() { - return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero); - } - - // A convenience method for reducing by sum(value). - function reduceSum(value) { - return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero); - } - - // Returns the computed reduce value. - function value() { - if (resetNeeded) reset(), resetNeeded = false; - return reduceValue; - } - - return reduceCount(); - } - - // Returns the number of records in this crossfilter, irrespective of any filters. - function size() { - return n; - } - - return arguments.length - ? add(arguments[0]) - : crossfilter; -} - -// Returns an array of size n, big enough to store ids up to m. -function crossfilter_index(n, m) { - return (m < 0x101 - ? crossfilter_array8 : m < 0x10001 - ? crossfilter_array16 - : crossfilter_array32)(n); -} - -// Constructs a new array of size n, with sequential values from 0 to n - 1. -function crossfilter_range(n) { - var range = crossfilter_index(n, n); - for (var i = -1; ++i < n;) range[i] = i; - return range; -} - -function crossfilter_capacity(w) { - return w === 8 - ? 0x100 : w === 16 - ? 0x10000 - : 0x100000000; -} -})(this); diff --git a/examples/lib/crossfilter.min.js b/examples/lib/crossfilter.min.js deleted file mode 100644 index 981f0d64e..000000000 --- a/examples/lib/crossfilter.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(a){function b(a){return a}function c(a,b){for(var c=0,d=b.length,e=new Array(d);c>1;a(b[f])>1;c>>1)+1;while(--f>0)d(a,f,e,b);return a}function c(a,b,c){var e=c-b,f;while(--e>0)f=a[b],a[b]=a[b+e],a[b+e]=f,d(a,1,e,b);return a}function d(b,c,d,e){var f=b[--e+c],g=a(f),h;while((h=c<<1)<=d){ha(b[e+h+1])&&h++;if(g<=a(b[e+h]))break;b[e+c]=b[e+h],c=h}b[e+c]=f}return b.sort=c,b}function i(a){function c(c,d,e,f){var g=new Array(f=Math.min(e-d,f)),h,i,j,k;for(i=0;ih)g[0]=k,h=a(b(g,0,f)[0]);while(++dc&&a(b[f-1])>h;--f)b[f]=b[f-1];b[f]=g}return b}return b}function m(a){function c(a,c,e){return(e-c>1,j=i-f,k=i+f,l=b[g],m=a(l),n=b[j],o=a(n),p=b[i],q=a(p),r=b[k],s=a(r),t=b[h],u=a(t),v;m>o&&(v=l,l=n,n=v,v=m,m=o,o=v),s>u&&(v=r,r=t,t=v,v=s,s=u,u=v),m>q&&(v=l,l=p,p=v,v=m,m=q,q=v),o>q&&(v=n,n=p,p=v,v=o,o=q,q=v),m>s&&(v=l,l=r,r=v,v=m,m=s,s=v),q>s&&(v=p,p=r,r=v,v=q,q=s,s=v),o>u&&(v=n,n=t,t=v,v=o,o=u,u=v),o>q&&(v=n,n=p,p=v,v=o,o=q,q=v),s>u&&(v=r,r=t,t=v,v=s,s=u,u=v);var w=n,x=o,y=r,z=s;b[g]=l,b[j]=b[d],b[i]=p,b[k]=b[e-1],b[h]=t;var A=d+1,B=e-2,C=x<=z&&x>=z;if(C)for(var D=A;D<=B;++D){var E=b[D],F=a(E);if(Fx)for(;;){var G=a(b[B]);if(G>x){B--;continue}if(Gz)for(;;){var G=a(b[B]);if(G>z){B--;if(Bh){var H,G;while((H=a(b[A]))<=x&&H>=x)++A;while((G=a(b[B]))<=z&&G>=z)--B;for(var D=A;D<=B;D++){var E=b[D],F=a(E);if(F<=x&&F>=x)D!==A&&(b[D]=b[A],b[A]=E),A++;else if(F<=z&&F>=z)for(;;){var G=a(b[B]);if(G<=z&&G>=z){B--;if(BN)for(b=N,c=Math.min(e,O);bO)for(b=Math.max(e,O),c=f;b=N&&a>0)k[d=D[c]]||(b.push(e[d]),--a);return b}function X(a){function K(b,c,g,i){function Q(){++n===m&&(p=s(p,j<<=1),h=s(h,j),m=G(j))}var o=d,p=E(n,m),t=v,u=F,w=n,y=0,z=0,A,B,C,D,K,L;J&&(t=u=x),d=new Array(n),n=0,h=w>1?r(h,f):E(f,m),w&&(C=(B=o[0]).key);while(z=D))++z;while(zL)){h[A=c[z]+g]=n,k[A]&q||(K.value=t(K.value,e[A]));if(++z>=i)break;D=a(b[z])}Q()}while(yy)for(y=0;y1?(H=M,I=O):(n===1?(H=N,I=P):(H=x,I=x),h=null),l[A]=H}function M(a,b,c){if(a===p||J)return;var f,g,i,j;for(f=0,i=b.length;fj&&(k=s(k,j<<=1)),P(e,0,f),Q(e,0,f),o}function t(){function i(a,d,g){var i;if(h)return;for(i=d;i