├── .gitignore ├── Procfile ├── logotype.png ├── package.json ├── README.md ├── web.js ├── www ├── index.html └── d3.v2.min.js ├── crossfilter.v1.js └── crossfilter.v1.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node web.js -------------------------------------------------------------------------------- /logotype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZJONSSON/crossfilter/HEAD/logotype.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crossfilter-example", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "express": "3.0.x" 6 | }, 7 | "engines": { 8 | "node": "0.8.x", 9 | "npm": "1.1.x" 10 | } 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Forked example demonstrating Ajax updating 2 | 3 | Simple example using Crossfilter on node.js and serving the results through AJAX eliminating the need to run Crossfilter in the browser. -------------------------------------------------------------------------------- /web.js: -------------------------------------------------------------------------------- 1 | // Requires Express.js 2 | 3 | var fs = require("fs"), 4 | express=require("express"), 5 | crossfilter = require("./crossfilter.v1.min.js").crossfilter, 6 | app=express(); 7 | 8 | // Like d3.time.format, but faster. 9 | function parseDate(d) { 10 | return new Date(2001, 11 | d.substring(0, 2) - 1, 12 | d.substring(2, 4), 13 | d.substring(4, 6), 14 | d.substring(6, 8)); 15 | } 16 | 17 | // Read the CSV file into flights 18 | var flights = fs.readFileSync("flights-3m.json").toString().replace(/\r/g,"").split("\n"), 19 | header = flights[0].split(","); 20 | 21 | flights = flights.slice(1).map(function(d) { 22 | var line = {}; 23 | d.split(",").forEach(function(d,i) { 24 | line[header[i]] = d; 25 | }) 26 | return line; 27 | }) 28 | 29 | // A little coercion, since the CSV is untyped. 30 | flights.forEach(function(d, i) { 31 | d.index = i; 32 | d.date = parseDate(d.date); 33 | d.delay = +d.delay; 34 | d.distance = +d.distance; 35 | }); 36 | 37 | // Create the crossfilter for the relevant dimensions and groups. 38 | var flight = crossfilter(flights), 39 | dimensions = { 40 | date: flight.dimension(function(d) { var d = new Date(d.date); d.setHours(0,0,0,0); return d; }), 41 | hour: flight.dimension(function(d) { return d.date.getHours() + d.date.getMinutes() / 60; }), 42 | delay: flight.dimension(function(d) { return Math.max(-60, Math.min(149, d.delay)); }), 43 | distance: flight.dimension(function(d) { return Math.min(1999, d.distance); }) 44 | }, 45 | 46 | groups = { 47 | date : dimensions.date.group(), 48 | hour : dimensions.hour.group(Math.floor), 49 | delay : dimensions.delay.group(function(d) { return Math.floor(d / 10) * 10; }), 50 | distance : dimensions.distance.group(function(d) { return Math.floor(d / 50) * 50; }) 51 | }, 52 | 53 | size = flight.size(), 54 | all = flight.groupAll(); 55 | 56 | 57 | // Handle the AJAX requests 58 | app.use("/flights-3m",function(req,res,next) { 59 | filter = req.param("filter") ? JSON.parse(req.param("filter")) : {} 60 | // Loop through each dimension and check if user requested a filter 61 | Object.keys(dimensions).forEach(function(dim) { 62 | if (filter[dim]) { 63 | // In this example the only string variables in the filter are dates 64 | if (typeof filter[dim][0] === 'string') filter[dim]=[new Date(filter[dim][0]),new Date(filter[dim][1])] 65 | dimensions[dim].filterRange(filter[dim]) 66 | } else { 67 | dimensions[dim].filterAll() 68 | } 69 | }) 70 | 71 | // Assemble group results and and the maximum value for each group 72 | var results = {} 73 | Object.keys(groups).forEach(function(key) { 74 | results[key] = {values:groups[key].all(),top:groups[key].top(1)[0].value} 75 | }) 76 | 77 | // Send back as json 78 | res.writeHead(200, { 'content-type': 'application/json' }); 79 | res.end((JSON.stringify(results))) 80 | }) 81 | 82 | // Change this to the static directory of the index.html file 83 | app.use("/",express.static("./www/", {maxAge: 0})) 84 | 85 | var port = process.env.PORT || 5000; 86 | app.listen(port,function() { 87 | console.log("listening to port "+port) 88 | }) -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Crossfilter 4 | 154 | 155 |
156 | 157 |

Crossfilter

158 | 159 |

Forked example demonstrating Ajax updating

160 | 161 | Simple example using Crossfilter on node.js and serving the results through AJAX eliminating the need to run Crossfilter in the browser. 162 | 163 | 164 |
165 |
166 |
Time of Day
167 |
168 |
169 |
Arrival Delay (min.)
170 |
171 |
172 |
Distance (mi.)
173 |
174 |
175 |
Date
176 |
177 |
178 | 179 | 180 | 181 | 187 | 188 |
189 | 190 | Fork me on GitHub 191 | 192 | 193 | 194 | 486 | -------------------------------------------------------------------------------- /crossfilter.v1.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | crossfilter.version = "1.1.2"; 3 | function crossfilter_identity(d) { 4 | return d; 5 | } 6 | crossfilter.permute = permute; 7 | 8 | function permute(array, index) { 9 | for (var i = 0, n = index.length, copy = new Array(n); i < n; ++i) { 10 | copy[i] = array[index[i]]; 11 | } 12 | return copy; 13 | } 14 | var bisect = crossfilter.bisect = bisect_by(crossfilter_identity); 15 | 16 | bisect.by = bisect_by; 17 | 18 | function bisect_by(f) { 19 | 20 | // Locate the insertion point for x in a to maintain sorted order. The 21 | // arguments lo and hi may be used to specify a subset of the array which 22 | // should be considered; by default the entire array is used. If x is already 23 | // present in a, the insertion point will be before (to the left of) any 24 | // existing entries. The return value is suitable for use as the first 25 | // argument to `array.splice` assuming that a is already sorted. 26 | // Incomparable values such as NaN and undefined are assumed to be at the end 27 | // of the array. 28 | // 29 | // The returned insertion point i partitions the array a into two halves so 30 | // that all v < x for v in a[lo:i] for the left side and all v >= x for v in 31 | // a[i:hi] for the right side. 32 | function bisectLeft(a, x, lo, hi) { 33 | while (lo < hi) { 34 | var mid = lo + hi >>> 1, 35 | y = f(a[mid]); 36 | if (x <= y || !(y <= y)) hi = mid; 37 | else lo = mid + 1; 38 | } 39 | return lo; 40 | } 41 | 42 | // Similar to bisectLeft, but returns an insertion point which comes after (to 43 | // the right of) any existing entries of x in a. 44 | // 45 | // The returned insertion point i partitions the array into two halves so that 46 | // all v <= x for v in a[lo:i] for the left side and all v > x for v in 47 | // a[i:hi] for the right side. 48 | function bisectRight(a, x, lo, hi) { 49 | while (lo < hi) { 50 | var mid = lo + hi >>> 1, 51 | y = f(a[mid]); 52 | if (x < y || !(y <= y)) hi = mid; 53 | else lo = mid + 1; 54 | } 55 | return lo; 56 | } 57 | 58 | bisectRight.right = bisectRight; 59 | bisectRight.left = bisectLeft; 60 | return bisectRight; 61 | } 62 | var heap = crossfilter.heap = heap_by(crossfilter_identity); 63 | 64 | heap.by = heap_by; 65 | 66 | function heap_by(f) { 67 | 68 | // Builds a binary heap within the specified array a[lo:hi]. The heap has the 69 | // property such that the parent a[lo+i] is always less than or equal to its 70 | // two children: a[lo+2*i+1] and a[lo+2*i+2]. 71 | function heap(a, lo, hi) { 72 | var n = hi - lo, 73 | i = (n >>> 1) + 1; 74 | while (--i > 0) sift(a, i, n, lo); 75 | return a; 76 | } 77 | 78 | // Sorts the specified array a[lo:hi] in descending order, assuming it is 79 | // already a heap. 80 | function sort(a, lo, hi) { 81 | var n = hi - lo, 82 | t; 83 | while (--n > 0) t = a[lo], a[lo] = a[lo + n], a[lo + n] = t, sift(a, 1, n, lo); 84 | return a; 85 | } 86 | 87 | // Sifts the element a[lo+i-1] down the heap, where the heap is the contiguous 88 | // slice of array a[lo:lo+n]. This method can also be used to update the heap 89 | // incrementally, without incurring the full cost of reconstructing the heap. 90 | function sift(a, i, n, lo) { 91 | var d = a[--lo + i], 92 | x = f(d), 93 | child; 94 | while ((child = i << 1) <= n) { 95 | if (child < n && f(a[lo + child]) > f(a[lo + child + 1])) child++; 96 | if (x <= f(a[lo + child])) break; 97 | a[lo + i] = a[lo + child]; 98 | i = child; 99 | } 100 | a[lo + i] = d; 101 | } 102 | 103 | heap.sort = sort; 104 | return heap; 105 | } 106 | var heapselect = crossfilter.heapselect = heapselect_by(crossfilter_identity); 107 | 108 | heapselect.by = heapselect_by; 109 | 110 | function heapselect_by(f) { 111 | var heap = heap_by(f); 112 | 113 | // Returns a new array containing the top k elements in the array a[lo:hi]. 114 | // The returned array is not sorted, but maintains the heap property. If k is 115 | // greater than hi - lo, then fewer than k elements will be returned. The 116 | // order of elements in a is unchanged by this operation. 117 | function heapselect(a, lo, hi, k) { 118 | var queue = new Array(k = Math.min(hi - lo, k)), 119 | min, 120 | i, 121 | x, 122 | d; 123 | 124 | for (i = 0; i < k; ++i) queue[i] = a[lo++]; 125 | heap(queue, 0, k); 126 | 127 | if (lo < hi) { 128 | min = f(queue[0]); 129 | do { 130 | if (x = f(d = a[lo]) > min) { 131 | queue[0] = d; 132 | min = f(heap(queue, 0, k)[0]); 133 | } 134 | } while (++lo < hi); 135 | } 136 | 137 | return queue; 138 | } 139 | 140 | return heapselect; 141 | } 142 | var insertionsort = crossfilter.insertionsort = insertionsort_by(crossfilter_identity); 143 | 144 | insertionsort.by = insertionsort_by; 145 | 146 | function insertionsort_by(f) { 147 | 148 | function insertionsort(a, lo, hi) { 149 | for (var i = lo + 1; i < hi; ++i) { 150 | for (var j = i, t = a[i], x = f(t), y; j > lo && ((y = f(a[j - 1])) > x || !(y <= y)); --j) { 151 | a[j] = a[j - 1]; 152 | } 153 | a[j] = t; 154 | } 155 | return a; 156 | } 157 | 158 | return insertionsort; 159 | } 160 | // Algorithm designed by Vladimir Yaroslavskiy. 161 | // Implementation based on the Dart project; see lib/dart/LICENSE for details. 162 | 163 | var quicksort = crossfilter.quicksort = quicksort_by(crossfilter_identity); 164 | 165 | quicksort.by = quicksort_by; 166 | 167 | function quicksort_by(f) { 168 | var insertionsort = insertionsort_by(f); 169 | 170 | function sort(a, lo, hi) { 171 | return (hi - lo < quicksort_sizeThreshold 172 | ? insertionsort 173 | : quicksort)(a, lo, hi); 174 | } 175 | 176 | function quicksort(a, lo, hi) { 177 | // First move NaN and undefined to the end. 178 | var x, y; 179 | while (lo < hi && !(x = f(a[hi - 1]), x <= x)) hi--; 180 | for (var i = hi; --i >= lo; ) { 181 | x = f(y = a[i]); 182 | if (!(x <= x)) { 183 | a[i] = a[--hi]; 184 | a[hi] = y; 185 | } 186 | } 187 | 188 | // Compute the two pivots by looking at 5 elements. 189 | var sixth = (hi - lo) / 6 | 0, 190 | i1 = lo + sixth, 191 | i5 = hi - 1 - sixth, 192 | i3 = lo + hi - 1 >> 1, // The midpoint. 193 | i2 = i3 - sixth, 194 | i4 = i3 + sixth; 195 | 196 | var e1 = a[i1], x1 = f(e1), 197 | e2 = a[i2], x2 = f(e2), 198 | e3 = a[i3], x3 = f(e3), 199 | e4 = a[i4], x4 = f(e4), 200 | e5 = a[i5], x5 = f(e5); 201 | 202 | var t; 203 | 204 | // Sort the selected 5 elements using a sorting network. 205 | if (x1 > x2) t = e1, e1 = e2, e2 = t, t = x1, x1 = x2, x2 = t; 206 | if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; 207 | if (x1 > x3) t = e1, e1 = e3, e3 = t, t = x1, x1 = x3, x3 = t; 208 | if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; 209 | if (x1 > x4) t = e1, e1 = e4, e4 = t, t = x1, x1 = x4, x4 = t; 210 | if (x3 > x4) t = e3, e3 = e4, e4 = t, t = x3, x3 = x4, x4 = t; 211 | if (x2 > x5) t = e2, e2 = e5, e5 = t, t = x2, x2 = x5, x5 = t; 212 | if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; 213 | if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; 214 | 215 | var pivot1 = e2, pivotValue1 = x2, 216 | pivot2 = e4, pivotValue2 = x4; 217 | 218 | // e2 and e4 have been saved in the pivot variables. They will be written 219 | // back, once the partitioning is finished. 220 | a[i1] = e1; 221 | a[i2] = a[lo]; 222 | a[i3] = e3; 223 | a[i4] = a[hi - 1]; 224 | a[i5] = e5; 225 | 226 | var less = lo + 1, // First element in the middle partition. 227 | great = hi - 2; // Last element in the middle partition. 228 | 229 | // Note that for value comparison, <, <=, >= and > coerce to a primitive via 230 | // Object.prototype.valueOf; == and === do not, so in order to be consistent 231 | // with natural order (such as for Date objects), we must do two compares. 232 | var pivotsEqual = pivotValue1 <= pivotValue2 && pivotValue1 >= pivotValue2; 233 | if (pivotsEqual) { 234 | 235 | // Degenerated case where the partitioning becomes a dutch national flag 236 | // problem. 237 | // 238 | // [ | < pivot | == pivot | unpartitioned | > pivot | ] 239 | // ^ ^ ^ ^ ^ 240 | // left less k great right 241 | // 242 | // a[left] and a[right] are undefined and are filled after the 243 | // partitioning. 244 | // 245 | // Invariants: 246 | // 1) for x in ]left, less[ : x < pivot. 247 | // 2) for x in [less, k[ : x == pivot. 248 | // 3) for x in ]great, right[ : x > pivot. 249 | for (var k = less; k <= great; ++k) { 250 | var ek = a[k], xk = f(ek); 251 | if (xk < pivotValue1) { 252 | if (k !== less) { 253 | a[k] = a[less]; 254 | a[less] = ek; 255 | } 256 | ++less; 257 | } else if (xk > pivotValue1) { 258 | 259 | // Find the first element <= pivot in the range [k - 1, great] and 260 | // put [:ek:] there. We know that such an element must exist: 261 | // When k == less, then el3 (which is equal to pivot) lies in the 262 | // interval. Otherwise a[k - 1] == pivot and the search stops at k-1. 263 | // Note that in the latter case invariant 2 will be violated for a 264 | // short amount of time. The invariant will be restored when the 265 | // pivots are put into their final positions. 266 | while (true) { 267 | var greatValue = f(a[great]); 268 | if (greatValue > pivotValue1) { 269 | great--; 270 | // This is the only location in the while-loop where a new 271 | // iteration is started. 272 | continue; 273 | } else if (greatValue < pivotValue1) { 274 | // Triple exchange. 275 | a[k] = a[less]; 276 | a[less++] = a[great]; 277 | a[great--] = ek; 278 | break; 279 | } else { 280 | a[k] = a[great]; 281 | a[great--] = ek; 282 | // Note: if great < k then we will exit the outer loop and fix 283 | // invariant 2 (which we just violated). 284 | break; 285 | } 286 | } 287 | } 288 | } 289 | } else { 290 | 291 | // We partition the list into three parts: 292 | // 1. < pivot1 293 | // 2. >= pivot1 && <= pivot2 294 | // 3. > pivot2 295 | // 296 | // During the loop we have: 297 | // [ | < pivot1 | >= pivot1 && <= pivot2 | unpartitioned | > pivot2 | ] 298 | // ^ ^ ^ ^ ^ 299 | // left less k great right 300 | // 301 | // a[left] and a[right] are undefined and are filled after the 302 | // partitioning. 303 | // 304 | // Invariants: 305 | // 1. for x in ]left, less[ : x < pivot1 306 | // 2. for x in [less, k[ : pivot1 <= x && x <= pivot2 307 | // 3. for x in ]great, right[ : x > pivot2 308 | for (var k = less; k <= great; k++) { 309 | var ek = a[k], xk = f(ek); 310 | if (xk < pivotValue1) { 311 | if (k !== less) { 312 | a[k] = a[less]; 313 | a[less] = ek; 314 | } 315 | ++less; 316 | } else { 317 | if (xk > pivotValue2) { 318 | while (true) { 319 | var greatValue = f(a[great]); 320 | if (greatValue > pivotValue2) { 321 | great--; 322 | if (great < k) break; 323 | // This is the only location inside the loop where a new 324 | // iteration is started. 325 | continue; 326 | } else { 327 | // a[great] <= pivot2. 328 | if (greatValue < pivotValue1) { 329 | // Triple exchange. 330 | a[k] = a[less]; 331 | a[less++] = a[great]; 332 | a[great--] = ek; 333 | } else { 334 | // a[great] >= pivot1. 335 | a[k] = a[great]; 336 | a[great--] = ek; 337 | } 338 | break; 339 | } 340 | } 341 | } 342 | } 343 | } 344 | } 345 | 346 | // Move pivots into their final positions. 347 | // We shrunk the list from both sides (a[left] and a[right] have 348 | // meaningless values in them) and now we move elements from the first 349 | // and third partition into these locations so that we can store the 350 | // pivots. 351 | a[lo] = a[less - 1]; 352 | a[less - 1] = pivot1; 353 | a[hi - 1] = a[great + 1]; 354 | a[great + 1] = pivot2; 355 | 356 | // The list is now partitioned into three partitions: 357 | // [ < pivot1 | >= pivot1 && <= pivot2 | > pivot2 ] 358 | // ^ ^ ^ ^ 359 | // left less great right 360 | 361 | // Recursive descent. (Don't include the pivot values.) 362 | sort(a, lo, less - 1); 363 | sort(a, great + 2, hi); 364 | 365 | if (pivotsEqual) { 366 | // All elements in the second partition are equal to the pivot. No 367 | // need to sort them. 368 | return a; 369 | } 370 | 371 | // In theory it should be enough to call _doSort recursively on the second 372 | // partition. 373 | // The Android source however removes the pivot elements from the recursive 374 | // call if the second partition is too large (more than 2/3 of the list). 375 | if (less < i1 && great > i5) { 376 | var lessValue, greatValue; 377 | while ((lessValue = f(a[less])) <= pivotValue1 && lessValue >= pivotValue1) ++less; 378 | while ((greatValue = f(a[great])) <= pivotValue2 && greatValue >= pivotValue2) --great; 379 | 380 | // Copy paste of the previous 3-way partitioning with adaptions. 381 | // 382 | // We partition the list into three parts: 383 | // 1. == pivot1 384 | // 2. > pivot1 && < pivot2 385 | // 3. == pivot2 386 | // 387 | // During the loop we have: 388 | // [ == pivot1 | > pivot1 && < pivot2 | unpartitioned | == pivot2 ] 389 | // ^ ^ ^ 390 | // less k great 391 | // 392 | // Invariants: 393 | // 1. for x in [ *, less[ : x == pivot1 394 | // 2. for x in [less, k[ : pivot1 < x && x < pivot2 395 | // 3. for x in ]great, * ] : x == pivot2 396 | for (var k = less; k <= great; k++) { 397 | var ek = a[k], xk = f(ek); 398 | if (xk <= pivotValue1 && xk >= pivotValue1) { 399 | if (k !== less) { 400 | a[k] = a[less]; 401 | a[less] = ek; 402 | } 403 | less++; 404 | } else { 405 | if (xk <= pivotValue2 && xk >= pivotValue2) { 406 | while (true) { 407 | var greatValue = f(a[great]); 408 | if (greatValue <= pivotValue2 && greatValue >= pivotValue2) { 409 | great--; 410 | if (great < k) break; 411 | // This is the only location inside the loop where a new 412 | // iteration is started. 413 | continue; 414 | } else { 415 | // a[great] < pivot2. 416 | if (greatValue < pivotValue1) { 417 | // Triple exchange. 418 | a[k] = a[less]; 419 | a[less++] = a[great]; 420 | a[great--] = ek; 421 | } else { 422 | // a[great] == pivot1. 423 | a[k] = a[great]; 424 | a[great--] = ek; 425 | } 426 | break; 427 | } 428 | } 429 | } 430 | } 431 | } 432 | } 433 | 434 | // The second partition has now been cleared of pivot elements and looks 435 | // as follows: 436 | // [ * | > pivot1 && < pivot2 | * ] 437 | // ^ ^ 438 | // less great 439 | // Sort the second partition using recursive descent. 440 | 441 | // The second partition looks as follows: 442 | // [ * | >= pivot1 && <= pivot2 | * ] 443 | // ^ ^ 444 | // less great 445 | // Simply sort it by recursive descent. 446 | 447 | return sort(a, less, great + 1); 448 | } 449 | 450 | return sort; 451 | } 452 | 453 | var quicksort_sizeThreshold = 32; 454 | var crossfilter_array8 = crossfilter_arrayUntyped, 455 | crossfilter_array16 = crossfilter_arrayUntyped, 456 | crossfilter_array32 = crossfilter_arrayUntyped, 457 | crossfilter_arrayLengthen = crossfilter_identity, 458 | crossfilter_arrayWiden = crossfilter_identity; 459 | 460 | if (typeof Uint8Array !== "undefined") { 461 | crossfilter_array8 = function(n) { return new Uint8Array(n); }; 462 | crossfilter_array16 = function(n) { return new Uint16Array(n); }; 463 | crossfilter_array32 = function(n) { return new Uint32Array(n); }; 464 | 465 | crossfilter_arrayLengthen = function(array, length) { 466 | var copy = new array.constructor(length); 467 | copy.set(array); 468 | return copy; 469 | }; 470 | 471 | crossfilter_arrayWiden = function(array, width) { 472 | var copy; 473 | switch (width) { 474 | case 16: copy = crossfilter_array16(array.length); break; 475 | case 32: copy = crossfilter_array32(array.length); break; 476 | default: throw new Error("invalid array width!"); 477 | } 478 | copy.set(array); 479 | return copy; 480 | }; 481 | } 482 | 483 | function crossfilter_arrayUntyped(n) { 484 | return new Array(n); 485 | } 486 | function crossfilter_filterExact(bisect, value) { 487 | return function(values) { 488 | var n = values.length; 489 | return [bisect.left(values, value, 0, n), bisect.right(values, value, 0, n)]; 490 | }; 491 | } 492 | 493 | function crossfilter_filterRange(bisect, range) { 494 | var min = range[0], 495 | max = range[1]; 496 | return function(values) { 497 | var n = values.length; 498 | return [bisect.left(values, min, 0, n), bisect.left(values, max, 0, n)]; 499 | }; 500 | } 501 | 502 | function crossfilter_filterAll(values) { 503 | return [0, values.length]; 504 | } 505 | function crossfilter_null() { 506 | return null; 507 | } 508 | function crossfilter_zero() { 509 | return 0; 510 | } 511 | function crossfilter_reduceIncrement(p) { 512 | return p + 1; 513 | } 514 | 515 | function crossfilter_reduceDecrement(p) { 516 | return p - 1; 517 | } 518 | 519 | function crossfilter_reduceAdd(f) { 520 | return function(p, v) { 521 | return p + +f(v); 522 | }; 523 | } 524 | 525 | function crossfilter_reduceSubtract(f) { 526 | return function(p, v) { 527 | return p - f(v); 528 | }; 529 | } 530 | exports.crossfilter = crossfilter; 531 | 532 | function crossfilter() { 533 | var crossfilter = { 534 | add: add, 535 | dimension: dimension, 536 | groupAll: groupAll, 537 | size: size 538 | }; 539 | 540 | var data = [], // the records 541 | n = 0, // the number of records; data.length 542 | m = 0, // number of dimensions in use 543 | M = 8, // number of dimensions that can fit in `filters` 544 | filters = crossfilter_array8(0), // M bits per record; 1 is filtered out 545 | filterListeners = [], // when the filters change 546 | dataListeners = []; // when data is added 547 | 548 | // Adds the specified new records to this crossfilter. 549 | function add(newData) { 550 | var n0 = n, 551 | n1 = newData.length; 552 | 553 | // If there's actually new data to add… 554 | // Merge the new data into the existing data. 555 | // Lengthen the filter bitset to handle the new records. 556 | // Notify listeners (dimensions and groups) that new data is available. 557 | if (n1) { 558 | data = data.concat(newData); 559 | filters = crossfilter_arrayLengthen(filters, n += n1); 560 | dataListeners.forEach(function(l) { l(newData, n0, n1); }); 561 | } 562 | 563 | return crossfilter; 564 | } 565 | 566 | // Adds a new dimension with the specified value accessor function. 567 | function dimension(value) { 568 | var dimension = { 569 | filter: filter, 570 | filterExact: filterExact, 571 | filterRange: filterRange, 572 | filterAll: filterAll, 573 | top: top, 574 | bottom: bottom, 575 | group: group, 576 | groupAll: groupAll 577 | }; 578 | 579 | var one = 1 << m++, // bit mask, e.g., 00001000 580 | zero = ~one, // inverted one, e.g., 11110111 581 | values, // sorted, cached array 582 | index, // value rank ↦ object id 583 | newValues, // temporary array storing newly-added values 584 | newIndex, // temporary array storing newly-added index 585 | sort = quicksort_by(function(i) { return newValues[i]; }), 586 | refilter = crossfilter_filterAll, // for recomputing filter 587 | indexListeners = [], // when data is added 588 | lo0 = 0, 589 | hi0 = 0; 590 | 591 | // Updating a dimension is a two-stage process. First, we must update the 592 | // associated filters for the newly-added records. Once all dimensions have 593 | // updated their filters, the groups are notified to update. 594 | dataListeners.unshift(preAdd); 595 | dataListeners.push(postAdd); 596 | 597 | // Incorporate any existing data into this dimension, and make sure that the 598 | // filter bitset is wide enough to handle the new dimension. 599 | if (m > M) filters = crossfilter_arrayWiden(filters, M <<= 1); 600 | preAdd(data, 0, n); 601 | postAdd(data, 0, n); 602 | 603 | // Incorporates the specified new records into this dimension. 604 | // This function is responsible for updating filters, values, and index. 605 | function preAdd(newData, n0, n1) { 606 | 607 | // Permute new values into natural order using a sorted index. 608 | newValues = newData.map(value); 609 | newIndex = sort(crossfilter_range(n1), 0, n1); 610 | newValues = permute(newValues, newIndex); 611 | 612 | // Bisect newValues to determine which new records are selected. 613 | var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1], i; 614 | for (i = 0; i < lo1; ++i) filters[newIndex[i] + n0] |= one; 615 | for (i = hi1; i < n1; ++i) filters[newIndex[i] + n0] |= one; 616 | 617 | // If this dimension previously had no data, then we don't need to do the 618 | // more expensive merge operation; use the new values and index as-is. 619 | if (!n0) { 620 | values = newValues; 621 | index = newIndex; 622 | lo0 = lo1; 623 | hi0 = hi1; 624 | return; 625 | } 626 | 627 | var oldValues = values, 628 | oldIndex = index, 629 | i0 = 0, 630 | i1 = 0; 631 | 632 | // Otherwise, create new arrays into which to merge new and old. 633 | values = new Array(n); 634 | index = crossfilter_index(n, n); 635 | 636 | // Merge the old and new sorted values, and old and new index. 637 | for (i = 0; i0 < n0 && i1 < n1; ++i) { 638 | if (oldValues[i0] < newValues[i1]) { 639 | values[i] = oldValues[i0]; 640 | index[i] = oldIndex[i0++]; 641 | } else { 642 | values[i] = newValues[i1]; 643 | index[i] = newIndex[i1++] + n0; 644 | } 645 | } 646 | 647 | // Add any remaining old values. 648 | for (; i0 < n0; ++i0, ++i) { 649 | values[i] = oldValues[i0]; 650 | index[i] = oldIndex[i0]; 651 | } 652 | 653 | // Add any remaining new values. 654 | for (; i1 < n1; ++i1, ++i) { 655 | values[i] = newValues[i1]; 656 | index[i] = newIndex[i1] + n0; 657 | } 658 | 659 | // Bisect again to recompute lo0 and hi0. 660 | bounds = refilter(values), lo0 = bounds[0], hi0 = bounds[1]; 661 | } 662 | 663 | // When all filters have updated, notify index listeners of the new values. 664 | function postAdd(newData, n0, n1) { 665 | indexListeners.forEach(function(l) { l(newValues, newIndex, n0, n1); }); 666 | newValues = newIndex = null; 667 | } 668 | 669 | // Updates the selected values based on the specified bounds [lo, hi]. 670 | // This implementation is used by all the public filter methods. 671 | function filterIndex(bounds) { 672 | var i, 673 | j, 674 | k, 675 | lo1 = bounds[0], 676 | hi1 = bounds[1], 677 | added = [], 678 | removed = []; 679 | 680 | // Fast incremental update based on previous lo index. 681 | if (lo1 < lo0) { 682 | for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) { 683 | filters[k = index[i]] ^= one; 684 | added.push(k); 685 | } 686 | } else if (lo1 > lo0) { 687 | for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) { 688 | filters[k = index[i]] ^= one; 689 | removed.push(k); 690 | } 691 | } 692 | 693 | // Fast incremental update based on previous hi index. 694 | if (hi1 > hi0) { 695 | for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) { 696 | filters[k = index[i]] ^= one; 697 | added.push(k); 698 | } 699 | } else if (hi1 < hi0) { 700 | for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) { 701 | filters[k = index[i]] ^= one; 702 | removed.push(k); 703 | } 704 | } 705 | 706 | lo0 = lo1; 707 | hi0 = hi1; 708 | filterListeners.forEach(function(l) { l(one, added, removed); }); 709 | return dimension; 710 | } 711 | 712 | // Filters this dimension using the specified range, value, or null. 713 | // If the range is null, this is equivalent to filterAll. 714 | // If the range is an array, this is equivalent to filterRange. 715 | // Otherwise, this is equivalent to filterExact. 716 | function filter(range) { 717 | return range == null 718 | ? filterAll() : Array.isArray(range) 719 | ? filterRange(range) 720 | : filterExact(range); 721 | } 722 | 723 | // Filters this dimension to select the exact value. 724 | function filterExact(value) { 725 | return filterIndex((refilter = crossfilter_filterExact(bisect, value))(values)); 726 | } 727 | 728 | // Filters this dimension to select the specified range [lo, hi]. 729 | // The lower bound is inclusive, and the upper bound is exclusive. 730 | function filterRange(range) { 731 | return filterIndex((refilter = crossfilter_filterRange(bisect, range))(values)); 732 | } 733 | 734 | // Clears any filters on this dimension. 735 | function filterAll() { 736 | return filterIndex((refilter = crossfilter_filterAll)(values)); 737 | } 738 | 739 | // Returns the top K selected records based on this dimension's order. 740 | // Note: observes this dimension's filter, unlike group and groupAll. 741 | function top(k) { 742 | var array = [], 743 | i = hi0, 744 | j; 745 | 746 | while (--i >= lo0 && k > 0) { 747 | if (!filters[j = index[i]]) { 748 | array.push(data[j]); 749 | --k; 750 | } 751 | } 752 | 753 | return array; 754 | } 755 | 756 | // Returns the bottom K selected records based on this dimension's order. 757 | // Note: observes this dimension's filter, unlike group and groupAll. 758 | function bottom(k) { 759 | var array = [], 760 | i = lo0, 761 | j; 762 | 763 | while (i < hi0 && k > 0) { 764 | if (!filters[j = index[i]]) { 765 | array.push(data[j]); 766 | --k; 767 | } 768 | i++; 769 | } 770 | 771 | return array; 772 | } 773 | 774 | // Adds a new group to this dimension, using the specified key function. 775 | function group(key) { 776 | var group = { 777 | top: top, 778 | all: all, 779 | reduce: reduce, 780 | reduceCount: reduceCount, 781 | reduceSum: reduceSum, 782 | order: order, 783 | orderNatural: orderNatural, 784 | size: size 785 | }; 786 | 787 | var groups, // array of {key, value} 788 | groupIndex, // object id ↦ group id 789 | groupWidth = 8, 790 | groupCapacity = crossfilter_capacity(groupWidth), 791 | k = 0, // cardinality 792 | select, 793 | heap, 794 | reduceAdd, 795 | reduceRemove, 796 | reduceInitial, 797 | update = crossfilter_null, 798 | reset = crossfilter_null, 799 | resetNeeded = true; 800 | 801 | if (arguments.length < 1) key = crossfilter_identity; 802 | 803 | // The group listens to the crossfilter for when any dimension changes, so 804 | // that it can update the associated reduce values. It must also listen to 805 | // the parent dimension for when data is added, and compute new keys. 806 | filterListeners.push(update); 807 | indexListeners.push(add); 808 | 809 | // Incorporate any existing data into the grouping. 810 | add(values, index, 0, n); 811 | 812 | // Incorporates the specified new values into this group. 813 | // This function is responsible for updating groups and groupIndex. 814 | function add(newValues, newIndex, n0, n1) { 815 | var oldGroups = groups, 816 | reIndex = crossfilter_index(k, groupCapacity), 817 | add = reduceAdd, 818 | initial = reduceInitial, 819 | k0 = k, // old cardinality 820 | i0 = 0, // index of old group 821 | i1 = 0, // index of new record 822 | j, // object id 823 | g0, // old group 824 | x0, // old key 825 | x1, // new key 826 | g, // group to add 827 | x; // key of group to add 828 | 829 | // If a reset is needed, we don't need to update the reduce values. 830 | if (resetNeeded) add = initial = crossfilter_null; 831 | 832 | // Reset the new groups (k is a lower bound). 833 | // Also, make sure that groupIndex exists and is long enough. 834 | groups = new Array(k), k = 0; 835 | groupIndex = k0 > 1 ? crossfilter_arrayLengthen(groupIndex, n) : crossfilter_index(n, groupCapacity); 836 | 837 | // Get the first old key (x0 of g0), if it exists. 838 | if (k0) x0 = (g0 = oldGroups[0]).key; 839 | 840 | // Find the first new key (x1). 841 | x1 = key(newValues[i1]); 842 | 843 | // While new keys remain… 844 | while (i1 < n1) { 845 | 846 | // Determine the lesser of the two current keys; new and old. 847 | // If there are no old keys remaining, then always add the new key. 848 | if (g0 && x0 <= x1) { 849 | g = g0, x = x0; 850 | 851 | // Record the new index of the old group. 852 | reIndex[i0] = k; 853 | 854 | // Retrieve the next old key. 855 | if (g0 = oldGroups[++i0]) x0 = g0.key; 856 | } else { 857 | g = {key: x1, value: initial()}, x = x1; 858 | } 859 | 860 | // Add the lesser group. 861 | groups[k] = g; 862 | 863 | // Add any selected records belonging to the added group, while 864 | // advancing the new key and populating the associated group index. 865 | while (x1 <= x || !(x1 <= x1) && !(x <= x)) { 866 | groupIndex[j = newIndex[i1] + n0] = k; 867 | if (!(filters[j] & zero)) g.value = add(g.value, data[j]); 868 | if (++i1 >= n1) break; 869 | x1 = key(newValues[i1]); 870 | } 871 | 872 | groupIncrement(); 873 | } 874 | 875 | // Add any remaining old groups that were greater than all new keys. 876 | // No incremental reduce is needed; these groups have no new records. 877 | // Also record the new index of the old group. 878 | while (i0 < k0) { 879 | groups[reIndex[i0] = k] = oldGroups[i0++]; 880 | groupIncrement(); 881 | } 882 | 883 | // If we added any new groups before any old groups, 884 | // update the group index of all the old records. 885 | if (k > i0) for (i0 = 0; i0 < n0; ++i0) { 886 | groupIndex[i0] = reIndex[groupIndex[i0]]; 887 | } 888 | 889 | // Modify the update and reset behavior based on the cardinality. 890 | // If the cardinality is less than or equal to one, then the groupIndex 891 | // is not needed. If the cardinality is zero, then there are no records 892 | // and therefore no groups to update or reset. Note that we also must 893 | // change the registered listener to point to the new method. 894 | j = filterListeners.indexOf(update); 895 | if (k > 1) { 896 | update = updateMany; 897 | reset = resetMany; 898 | } else { 899 | if (k === 1) { 900 | update = updateOne; 901 | reset = resetOne; 902 | } else { 903 | update = crossfilter_null; 904 | reset = crossfilter_null; 905 | } 906 | groupIndex = null; 907 | } 908 | filterListeners[j] = update; 909 | 910 | // Count the number of added groups, 911 | // and widen the group index as needed. 912 | function groupIncrement() { 913 | if (++k === groupCapacity) { 914 | reIndex = crossfilter_arrayWiden(reIndex, groupWidth <<= 1); 915 | groupIndex = crossfilter_arrayWiden(groupIndex, groupWidth); 916 | groupCapacity = crossfilter_capacity(groupWidth); 917 | } 918 | } 919 | } 920 | 921 | // Reduces the specified selected or deselected records. 922 | // This function is only used when the cardinality is greater than 1. 923 | function updateMany(filterOne, added, removed) { 924 | if (filterOne === one || resetNeeded) return; 925 | 926 | var i, 927 | k, 928 | n, 929 | g; 930 | 931 | // Add the added values. 932 | for (i = 0, n = added.length; i < n; ++i) { 933 | if (!(filters[k = added[i]] & zero)) { 934 | g = groups[groupIndex[k]]; 935 | g.value = reduceAdd(g.value, data[k]); 936 | } 937 | } 938 | 939 | // Remove the removed values. 940 | for (i = 0, n = removed.length; i < n; ++i) { 941 | if ((filters[k = removed[i]] & zero) === filterOne) { 942 | g = groups[groupIndex[k]]; 943 | g.value = reduceRemove(g.value, data[k]); 944 | } 945 | } 946 | } 947 | 948 | // Reduces the specified selected or deselected records. 949 | // This function is only used when the cardinality is 1. 950 | function updateOne(filterOne, added, removed) { 951 | if (filterOne === one || resetNeeded) return; 952 | 953 | var i, 954 | k, 955 | n, 956 | g = groups[0]; 957 | 958 | // Add the added values. 959 | for (i = 0, n = added.length; i < n; ++i) { 960 | if (!(filters[k = added[i]] & zero)) { 961 | g.value = reduceAdd(g.value, data[k]); 962 | } 963 | } 964 | 965 | // Remove the removed values. 966 | for (i = 0, n = removed.length; i < n; ++i) { 967 | if ((filters[k = removed[i]] & zero) === filterOne) { 968 | g.value = reduceRemove(g.value, data[k]); 969 | } 970 | } 971 | } 972 | 973 | // Recomputes the group reduce values from scratch. 974 | // This function is only used when the cardinality is greater than 1. 975 | function resetMany() { 976 | var i, 977 | g; 978 | 979 | // Reset all group values. 980 | for (i = 0; i < k; ++i) { 981 | groups[i].value = reduceInitial(); 982 | } 983 | 984 | // Add any selected records. 985 | for (i = 0; i < n; ++i) { 986 | if (!(filters[i] & zero)) { 987 | g = groups[groupIndex[i]]; 988 | g.value = reduceAdd(g.value, data[i]); 989 | } 990 | } 991 | } 992 | 993 | // Recomputes the group reduce values from scratch. 994 | // This function is only used when the cardinality is 1. 995 | function resetOne() { 996 | var i, 997 | g = groups[0]; 998 | 999 | // Reset the singleton group values. 1000 | g.value = reduceInitial(); 1001 | 1002 | // Add any selected records. 1003 | for (i = 0; i < n; ++i) { 1004 | if (!(filters[i] & zero)) { 1005 | g.value = reduceAdd(g.value, data[i]); 1006 | } 1007 | } 1008 | } 1009 | 1010 | // Returns the array of group values, in the dimension's natural order. 1011 | function all() { 1012 | if (resetNeeded) reset(), resetNeeded = false; 1013 | return groups; 1014 | } 1015 | 1016 | // Returns a new array containing the top K group values, in reduce order. 1017 | function top(k) { 1018 | var top = select(all(), 0, groups.length, k); 1019 | return heap.sort(top, 0, top.length); 1020 | } 1021 | 1022 | // Sets the reduce behavior for this group to use the specified functions. 1023 | // This method lazily recomputes the reduce values, waiting until needed. 1024 | function reduce(add, remove, initial) { 1025 | reduceAdd = add; 1026 | reduceRemove = remove; 1027 | reduceInitial = initial; 1028 | resetNeeded = true; 1029 | return group; 1030 | } 1031 | 1032 | // A convenience method for reducing by count. 1033 | function reduceCount() { 1034 | return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero); 1035 | } 1036 | 1037 | // A convenience method for reducing by sum(value). 1038 | function reduceSum(value) { 1039 | return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero); 1040 | } 1041 | 1042 | // Sets the reduce order, using the specified accessor. 1043 | function order(value) { 1044 | select = heapselect_by(valueOf); 1045 | heap = heap_by(valueOf); 1046 | function valueOf(d) { return value(d.value); } 1047 | return group; 1048 | } 1049 | 1050 | // A convenience method for natural ordering by reduce value. 1051 | function orderNatural() { 1052 | return order(crossfilter_identity); 1053 | } 1054 | 1055 | // Returns the cardinality of this group, irrespective of any filters. 1056 | function size() { 1057 | return k; 1058 | } 1059 | 1060 | return reduceCount().orderNatural(); 1061 | } 1062 | 1063 | // A convenience function for generating a singleton group. 1064 | function groupAll() { 1065 | var g = group(crossfilter_null), all = g.all; 1066 | delete g.all; 1067 | delete g.top; 1068 | delete g.order; 1069 | delete g.orderNatural; 1070 | delete g.size; 1071 | g.value = function() { return all()[0].value; }; 1072 | return g; 1073 | } 1074 | 1075 | return dimension; 1076 | } 1077 | 1078 | // A convenience method for groupAll on a dummy dimension. 1079 | // This implementation can be optimized since it is always cardinality 1. 1080 | function groupAll() { 1081 | var group = { 1082 | reduce: reduce, 1083 | reduceCount: reduceCount, 1084 | reduceSum: reduceSum, 1085 | value: value 1086 | }; 1087 | 1088 | var reduceValue, 1089 | reduceAdd, 1090 | reduceRemove, 1091 | reduceInitial, 1092 | resetNeeded = true; 1093 | 1094 | // The group listens to the crossfilter for when any dimension changes, so 1095 | // that it can update the reduce value. It must also listen to the parent 1096 | // dimension for when data is added. 1097 | filterListeners.push(update); 1098 | dataListeners.push(add); 1099 | 1100 | // For consistency; actually a no-op since resetNeeded is true. 1101 | add(data, 0, n); 1102 | 1103 | // Incorporates the specified new values into this group. 1104 | function add(newData, n0) { 1105 | var i; 1106 | 1107 | if (resetNeeded) return; 1108 | 1109 | // Add the added values. 1110 | for (i = n0; i < n; ++i) { 1111 | if (!filters[i]) { 1112 | reduceValue = reduceAdd(reduceValue, data[i]); 1113 | } 1114 | } 1115 | } 1116 | 1117 | // Reduces the specified selected or deselected records. 1118 | function update(filterOne, added, removed) { 1119 | var i, 1120 | k, 1121 | n; 1122 | 1123 | if (resetNeeded) return; 1124 | 1125 | // Add the added values. 1126 | for (i = 0, n = added.length; i < n; ++i) { 1127 | if (!filters[k = added[i]]) { 1128 | reduceValue = reduceAdd(reduceValue, data[k]); 1129 | } 1130 | } 1131 | 1132 | // Remove the removed values. 1133 | for (i = 0, n = removed.length; i < n; ++i) { 1134 | if (filters[k = removed[i]] === filterOne) { 1135 | reduceValue = reduceRemove(reduceValue, data[k]); 1136 | } 1137 | } 1138 | } 1139 | 1140 | // Recomputes the group reduce value from scratch. 1141 | function reset() { 1142 | var i; 1143 | 1144 | reduceValue = reduceInitial(); 1145 | 1146 | for (i = 0; i < n; ++i) { 1147 | if (!filters[i]) { 1148 | reduceValue = reduceAdd(reduceValue, data[i]); 1149 | } 1150 | } 1151 | } 1152 | 1153 | // Sets the reduce behavior for this group to use the specified functions. 1154 | // This method lazily recomputes the reduce value, waiting until needed. 1155 | function reduce(add, remove, initial) { 1156 | reduceAdd = add; 1157 | reduceRemove = remove; 1158 | reduceInitial = initial; 1159 | resetNeeded = true; 1160 | return group; 1161 | } 1162 | 1163 | // A convenience method for reducing by count. 1164 | function reduceCount() { 1165 | return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero); 1166 | } 1167 | 1168 | // A convenience method for reducing by sum(value). 1169 | function reduceSum(value) { 1170 | return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero); 1171 | } 1172 | 1173 | // Returns the computed reduce value. 1174 | function value() { 1175 | if (resetNeeded) reset(), resetNeeded = false; 1176 | return reduceValue; 1177 | } 1178 | 1179 | return reduceCount(); 1180 | } 1181 | 1182 | // Returns the number of records in this crossfilter, irrespective of any filters. 1183 | function size() { 1184 | return n; 1185 | } 1186 | 1187 | return arguments.length 1188 | ? add(arguments[0]) 1189 | : crossfilter; 1190 | } 1191 | 1192 | // Returns an array of size n, big enough to store ids up to m. 1193 | function crossfilter_index(n, m) { 1194 | return (m < 0x101 1195 | ? crossfilter_array8 : m < 0x10001 1196 | ? crossfilter_array16 1197 | : crossfilter_array32)(n); 1198 | } 1199 | 1200 | // Constructs a new array of size n, with sequential values from 0 to n - 1. 1201 | function crossfilter_range(n) { 1202 | var range = crossfilter_index(n, n); 1203 | for (var i = -1; ++i < n;) range[i] = i; 1204 | return range; 1205 | } 1206 | 1207 | function crossfilter_capacity(w) { 1208 | return w === 8 1209 | ? 0x100 : w === 16 1210 | ? 0x10000 1211 | : 0x100000000; 1212 | } 1213 | })(this); 1214 | -------------------------------------------------------------------------------- /crossfilter.v1.min.js: -------------------------------------------------------------------------------- 1 | (function(exports){ 2 | crossfilter.version = "1.1.2"; 3 | function crossfilter_identity(d) { 4 | return d; 5 | } 6 | crossfilter.permute = permute; 7 | 8 | function permute(array, index) { 9 | for (var i = 0, n = index.length, copy = new Array(n); i < n; ++i) { 10 | copy[i] = array[index[i]]; 11 | } 12 | return copy; 13 | } 14 | var bisect = crossfilter.bisect = bisect_by(crossfilter_identity); 15 | 16 | bisect.by = bisect_by; 17 | 18 | function bisect_by(f) { 19 | 20 | // Locate the insertion point for x in a to maintain sorted order. The 21 | // arguments lo and hi may be used to specify a subset of the array which 22 | // should be considered; by default the entire array is used. If x is already 23 | // present in a, the insertion point will be before (to the left of) any 24 | // existing entries. The return value is suitable for use as the first 25 | // argument to `array.splice` assuming that a is already sorted. 26 | // Incomparable values such as NaN and undefined are assumed to be at the end 27 | // of the array. 28 | // 29 | // The returned insertion point i partitions the array a into two halves so 30 | // that all v < x for v in a[lo:i] for the left side and all v >= x for v in 31 | // a[i:hi] for the right side. 32 | function bisectLeft(a, x, lo, hi) { 33 | while (lo < hi) { 34 | var mid = lo + hi >>> 1, 35 | y = f(a[mid]); 36 | if (x <= y || !(y <= y)) hi = mid; 37 | else lo = mid + 1; 38 | } 39 | return lo; 40 | } 41 | 42 | // Similar to bisectLeft, but returns an insertion point which comes after (to 43 | // the right of) any existing entries of x in a. 44 | // 45 | // The returned insertion point i partitions the array into two halves so that 46 | // all v <= x for v in a[lo:i] for the left side and all v > x for v in 47 | // a[i:hi] for the right side. 48 | function bisectRight(a, x, lo, hi) { 49 | while (lo < hi) { 50 | var mid = lo + hi >>> 1, 51 | y = f(a[mid]); 52 | if (x < y || !(y <= y)) hi = mid; 53 | else lo = mid + 1; 54 | } 55 | return lo; 56 | } 57 | 58 | bisectRight.right = bisectRight; 59 | bisectRight.left = bisectLeft; 60 | return bisectRight; 61 | } 62 | var heap = crossfilter.heap = heap_by(crossfilter_identity); 63 | 64 | heap.by = heap_by; 65 | 66 | function heap_by(f) { 67 | 68 | // Builds a binary heap within the specified array a[lo:hi]. The heap has the 69 | // property such that the parent a[lo+i] is always less than or equal to its 70 | // two children: a[lo+2*i+1] and a[lo+2*i+2]. 71 | function heap(a, lo, hi) { 72 | var n = hi - lo, 73 | i = (n >>> 1) + 1; 74 | while (--i > 0) sift(a, i, n, lo); 75 | return a; 76 | } 77 | 78 | // Sorts the specified array a[lo:hi] in descending order, assuming it is 79 | // already a heap. 80 | function sort(a, lo, hi) { 81 | var n = hi - lo, 82 | t; 83 | while (--n > 0) t = a[lo], a[lo] = a[lo + n], a[lo + n] = t, sift(a, 1, n, lo); 84 | return a; 85 | } 86 | 87 | // Sifts the element a[lo+i-1] down the heap, where the heap is the contiguous 88 | // slice of array a[lo:lo+n]. This method can also be used to update the heap 89 | // incrementally, without incurring the full cost of reconstructing the heap. 90 | function sift(a, i, n, lo) { 91 | var d = a[--lo + i], 92 | x = f(d), 93 | child; 94 | while ((child = i << 1) <= n) { 95 | if (child < n && f(a[lo + child]) > f(a[lo + child + 1])) child++; 96 | if (x <= f(a[lo + child])) break; 97 | a[lo + i] = a[lo + child]; 98 | i = child; 99 | } 100 | a[lo + i] = d; 101 | } 102 | 103 | heap.sort = sort; 104 | return heap; 105 | } 106 | var heapselect = crossfilter.heapselect = heapselect_by(crossfilter_identity); 107 | 108 | heapselect.by = heapselect_by; 109 | 110 | function heapselect_by(f) { 111 | var heap = heap_by(f); 112 | 113 | // Returns a new array containing the top k elements in the array a[lo:hi]. 114 | // The returned array is not sorted, but maintains the heap property. If k is 115 | // greater than hi - lo, then fewer than k elements will be returned. The 116 | // order of elements in a is unchanged by this operation. 117 | function heapselect(a, lo, hi, k) { 118 | var queue = new Array(k = Math.min(hi - lo, k)), 119 | min, 120 | i, 121 | x, 122 | d; 123 | 124 | for (i = 0; i < k; ++i) queue[i] = a[lo++]; 125 | heap(queue, 0, k); 126 | 127 | if (lo < hi) { 128 | min = f(queue[0]); 129 | do { 130 | if (x = f(d = a[lo]) > min) { 131 | queue[0] = d; 132 | min = f(heap(queue, 0, k)[0]); 133 | } 134 | } while (++lo < hi); 135 | } 136 | 137 | return queue; 138 | } 139 | 140 | return heapselect; 141 | } 142 | var insertionsort = crossfilter.insertionsort = insertionsort_by(crossfilter_identity); 143 | 144 | insertionsort.by = insertionsort_by; 145 | 146 | function insertionsort_by(f) { 147 | 148 | function insertionsort(a, lo, hi) { 149 | for (var i = lo + 1; i < hi; ++i) { 150 | for (var j = i, t = a[i], x = f(t), y; j > lo && ((y = f(a[j - 1])) > x || !(y <= y)); --j) { 151 | a[j] = a[j - 1]; 152 | } 153 | a[j] = t; 154 | } 155 | return a; 156 | } 157 | 158 | return insertionsort; 159 | } 160 | // Algorithm designed by Vladimir Yaroslavskiy. 161 | // Implementation based on the Dart project; see lib/dart/LICENSE for details. 162 | 163 | var quicksort = crossfilter.quicksort = quicksort_by(crossfilter_identity); 164 | 165 | quicksort.by = quicksort_by; 166 | 167 | function quicksort_by(f) { 168 | var insertionsort = insertionsort_by(f); 169 | 170 | function sort(a, lo, hi) { 171 | return (hi - lo < quicksort_sizeThreshold 172 | ? insertionsort 173 | : quicksort)(a, lo, hi); 174 | } 175 | 176 | function quicksort(a, lo, hi) { 177 | // First move NaN and undefined to the end. 178 | var x, y; 179 | while (lo < hi && !(x = f(a[hi - 1]), x <= x)) hi--; 180 | for (var i = hi; --i >= lo; ) { 181 | x = f(y = a[i]); 182 | if (!(x <= x)) { 183 | a[i] = a[--hi]; 184 | a[hi] = y; 185 | } 186 | } 187 | 188 | // Compute the two pivots by looking at 5 elements. 189 | var sixth = (hi - lo) / 6 | 0, 190 | i1 = lo + sixth, 191 | i5 = hi - 1 - sixth, 192 | i3 = lo + hi - 1 >> 1, // The midpoint. 193 | i2 = i3 - sixth, 194 | i4 = i3 + sixth; 195 | 196 | var e1 = a[i1], x1 = f(e1), 197 | e2 = a[i2], x2 = f(e2), 198 | e3 = a[i3], x3 = f(e3), 199 | e4 = a[i4], x4 = f(e4), 200 | e5 = a[i5], x5 = f(e5); 201 | 202 | var t; 203 | 204 | // Sort the selected 5 elements using a sorting network. 205 | if (x1 > x2) t = e1, e1 = e2, e2 = t, t = x1, x1 = x2, x2 = t; 206 | if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; 207 | if (x1 > x3) t = e1, e1 = e3, e3 = t, t = x1, x1 = x3, x3 = t; 208 | if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; 209 | if (x1 > x4) t = e1, e1 = e4, e4 = t, t = x1, x1 = x4, x4 = t; 210 | if (x3 > x4) t = e3, e3 = e4, e4 = t, t = x3, x3 = x4, x4 = t; 211 | if (x2 > x5) t = e2, e2 = e5, e5 = t, t = x2, x2 = x5, x5 = t; 212 | if (x2 > x3) t = e2, e2 = e3, e3 = t, t = x2, x2 = x3, x3 = t; 213 | if (x4 > x5) t = e4, e4 = e5, e5 = t, t = x4, x4 = x5, x5 = t; 214 | 215 | var pivot1 = e2, pivotValue1 = x2, 216 | pivot2 = e4, pivotValue2 = x4; 217 | 218 | // e2 and e4 have been saved in the pivot variables. They will be written 219 | // back, once the partitioning is finished. 220 | a[i1] = e1; 221 | a[i2] = a[lo]; 222 | a[i3] = e3; 223 | a[i4] = a[hi - 1]; 224 | a[i5] = e5; 225 | 226 | var less = lo + 1, // First element in the middle partition. 227 | great = hi - 2; // Last element in the middle partition. 228 | 229 | // Note that for value comparison, <, <=, >= and > coerce to a primitive via 230 | // Object.prototype.valueOf; == and === do not, so in order to be consistent 231 | // with natural order (such as for Date objects), we must do two compares. 232 | var pivotsEqual = pivotValue1 <= pivotValue2 && pivotValue1 >= pivotValue2; 233 | if (pivotsEqual) { 234 | 235 | // Degenerated case where the partitioning becomes a dutch national flag 236 | // problem. 237 | // 238 | // [ | < pivot | == pivot | unpartitioned | > pivot | ] 239 | // ^ ^ ^ ^ ^ 240 | // left less k great right 241 | // 242 | // a[left] and a[right] are undefined and are filled after the 243 | // partitioning. 244 | // 245 | // Invariants: 246 | // 1) for x in ]left, less[ : x < pivot. 247 | // 2) for x in [less, k[ : x == pivot. 248 | // 3) for x in ]great, right[ : x > pivot. 249 | for (var k = less; k <= great; ++k) { 250 | var ek = a[k], xk = f(ek); 251 | if (xk < pivotValue1) { 252 | if (k !== less) { 253 | a[k] = a[less]; 254 | a[less] = ek; 255 | } 256 | ++less; 257 | } else if (xk > pivotValue1) { 258 | 259 | // Find the first element <= pivot in the range [k - 1, great] and 260 | // put [:ek:] there. We know that such an element must exist: 261 | // When k == less, then el3 (which is equal to pivot) lies in the 262 | // interval. Otherwise a[k - 1] == pivot and the search stops at k-1. 263 | // Note that in the latter case invariant 2 will be violated for a 264 | // short amount of time. The invariant will be restored when the 265 | // pivots are put into their final positions. 266 | while (true) { 267 | var greatValue = f(a[great]); 268 | if (greatValue > pivotValue1) { 269 | great--; 270 | // This is the only location in the while-loop where a new 271 | // iteration is started. 272 | continue; 273 | } else if (greatValue < pivotValue1) { 274 | // Triple exchange. 275 | a[k] = a[less]; 276 | a[less++] = a[great]; 277 | a[great--] = ek; 278 | break; 279 | } else { 280 | a[k] = a[great]; 281 | a[great--] = ek; 282 | // Note: if great < k then we will exit the outer loop and fix 283 | // invariant 2 (which we just violated). 284 | break; 285 | } 286 | } 287 | } 288 | } 289 | } else { 290 | 291 | // We partition the list into three parts: 292 | // 1. < pivot1 293 | // 2. >= pivot1 && <= pivot2 294 | // 3. > pivot2 295 | // 296 | // During the loop we have: 297 | // [ | < pivot1 | >= pivot1 && <= pivot2 | unpartitioned | > pivot2 | ] 298 | // ^ ^ ^ ^ ^ 299 | // left less k great right 300 | // 301 | // a[left] and a[right] are undefined and are filled after the 302 | // partitioning. 303 | // 304 | // Invariants: 305 | // 1. for x in ]left, less[ : x < pivot1 306 | // 2. for x in [less, k[ : pivot1 <= x && x <= pivot2 307 | // 3. for x in ]great, right[ : x > pivot2 308 | for (var k = less; k <= great; k++) { 309 | var ek = a[k], xk = f(ek); 310 | if (xk < pivotValue1) { 311 | if (k !== less) { 312 | a[k] = a[less]; 313 | a[less] = ek; 314 | } 315 | ++less; 316 | } else { 317 | if (xk > pivotValue2) { 318 | while (true) { 319 | var greatValue = f(a[great]); 320 | if (greatValue > pivotValue2) { 321 | great--; 322 | if (great < k) break; 323 | // This is the only location inside the loop where a new 324 | // iteration is started. 325 | continue; 326 | } else { 327 | // a[great] <= pivot2. 328 | if (greatValue < pivotValue1) { 329 | // Triple exchange. 330 | a[k] = a[less]; 331 | a[less++] = a[great]; 332 | a[great--] = ek; 333 | } else { 334 | // a[great] >= pivot1. 335 | a[k] = a[great]; 336 | a[great--] = ek; 337 | } 338 | break; 339 | } 340 | } 341 | } 342 | } 343 | } 344 | } 345 | 346 | // Move pivots into their final positions. 347 | // We shrunk the list from both sides (a[left] and a[right] have 348 | // meaningless values in them) and now we move elements from the first 349 | // and third partition into these locations so that we can store the 350 | // pivots. 351 | a[lo] = a[less - 1]; 352 | a[less - 1] = pivot1; 353 | a[hi - 1] = a[great + 1]; 354 | a[great + 1] = pivot2; 355 | 356 | // The list is now partitioned into three partitions: 357 | // [ < pivot1 | >= pivot1 && <= pivot2 | > pivot2 ] 358 | // ^ ^ ^ ^ 359 | // left less great right 360 | 361 | // Recursive descent. (Don't include the pivot values.) 362 | sort(a, lo, less - 1); 363 | sort(a, great + 2, hi); 364 | 365 | if (pivotsEqual) { 366 | // All elements in the second partition are equal to the pivot. No 367 | // need to sort them. 368 | return a; 369 | } 370 | 371 | // In theory it should be enough to call _doSort recursively on the second 372 | // partition. 373 | // The Android source however removes the pivot elements from the recursive 374 | // call if the second partition is too large (more than 2/3 of the list). 375 | if (less < i1 && great > i5) { 376 | var lessValue, greatValue; 377 | while ((lessValue = f(a[less])) <= pivotValue1 && lessValue >= pivotValue1) ++less; 378 | while ((greatValue = f(a[great])) <= pivotValue2 && greatValue >= pivotValue2) --great; 379 | 380 | // Copy paste of the previous 3-way partitioning with adaptions. 381 | // 382 | // We partition the list into three parts: 383 | // 1. == pivot1 384 | // 2. > pivot1 && < pivot2 385 | // 3. == pivot2 386 | // 387 | // During the loop we have: 388 | // [ == pivot1 | > pivot1 && < pivot2 | unpartitioned | == pivot2 ] 389 | // ^ ^ ^ 390 | // less k great 391 | // 392 | // Invariants: 393 | // 1. for x in [ *, less[ : x == pivot1 394 | // 2. for x in [less, k[ : pivot1 < x && x < pivot2 395 | // 3. for x in ]great, * ] : x == pivot2 396 | for (var k = less; k <= great; k++) { 397 | var ek = a[k], xk = f(ek); 398 | if (xk <= pivotValue1 && xk >= pivotValue1) { 399 | if (k !== less) { 400 | a[k] = a[less]; 401 | a[less] = ek; 402 | } 403 | less++; 404 | } else { 405 | if (xk <= pivotValue2 && xk >= pivotValue2) { 406 | while (true) { 407 | var greatValue = f(a[great]); 408 | if (greatValue <= pivotValue2 && greatValue >= pivotValue2) { 409 | great--; 410 | if (great < k) break; 411 | // This is the only location inside the loop where a new 412 | // iteration is started. 413 | continue; 414 | } else { 415 | // a[great] < pivot2. 416 | if (greatValue < pivotValue1) { 417 | // Triple exchange. 418 | a[k] = a[less]; 419 | a[less++] = a[great]; 420 | a[great--] = ek; 421 | } else { 422 | // a[great] == pivot1. 423 | a[k] = a[great]; 424 | a[great--] = ek; 425 | } 426 | break; 427 | } 428 | } 429 | } 430 | } 431 | } 432 | } 433 | 434 | // The second partition has now been cleared of pivot elements and looks 435 | // as follows: 436 | // [ * | > pivot1 && < pivot2 | * ] 437 | // ^ ^ 438 | // less great 439 | // Sort the second partition using recursive descent. 440 | 441 | // The second partition looks as follows: 442 | // [ * | >= pivot1 && <= pivot2 | * ] 443 | // ^ ^ 444 | // less great 445 | // Simply sort it by recursive descent. 446 | 447 | return sort(a, less, great + 1); 448 | } 449 | 450 | return sort; 451 | } 452 | 453 | var quicksort_sizeThreshold = 32; 454 | var crossfilter_array8 = crossfilter_arrayUntyped, 455 | crossfilter_array16 = crossfilter_arrayUntyped, 456 | crossfilter_array32 = crossfilter_arrayUntyped, 457 | crossfilter_arrayLengthen = crossfilter_identity, 458 | crossfilter_arrayWiden = crossfilter_identity; 459 | 460 | if (typeof Uint8Array !== "undefined") { 461 | crossfilter_array8 = function(n) { return new Uint8Array(n); }; 462 | crossfilter_array16 = function(n) { return new Uint16Array(n); }; 463 | crossfilter_array32 = function(n) { return new Uint32Array(n); }; 464 | 465 | crossfilter_arrayLengthen = function(array, length) { 466 | var copy = new array.constructor(length); 467 | copy.set(array); 468 | return copy; 469 | }; 470 | 471 | crossfilter_arrayWiden = function(array, width) { 472 | var copy; 473 | switch (width) { 474 | case 16: copy = crossfilter_array16(array.length); break; 475 | case 32: copy = crossfilter_array32(array.length); break; 476 | default: throw new Error("invalid array width!"); 477 | } 478 | copy.set(array); 479 | return copy; 480 | }; 481 | } 482 | 483 | function crossfilter_arrayUntyped(n) { 484 | return new Array(n); 485 | } 486 | function crossfilter_filterExact(bisect, value) { 487 | return function(values) { 488 | var n = values.length; 489 | return [bisect.left(values, value, 0, n), bisect.right(values, value, 0, n)]; 490 | }; 491 | } 492 | 493 | function crossfilter_filterRange(bisect, range) { 494 | var min = range[0], 495 | max = range[1]; 496 | return function(values) { 497 | var n = values.length; 498 | return [bisect.left(values, min, 0, n), bisect.left(values, max, 0, n)]; 499 | }; 500 | } 501 | 502 | function crossfilter_filterAll(values) { 503 | return [0, values.length]; 504 | } 505 | function crossfilter_null() { 506 | return null; 507 | } 508 | function crossfilter_zero() { 509 | return 0; 510 | } 511 | function crossfilter_reduceIncrement(p) { 512 | return p + 1; 513 | } 514 | 515 | function crossfilter_reduceDecrement(p) { 516 | return p - 1; 517 | } 518 | 519 | function crossfilter_reduceAdd(f) { 520 | return function(p, v) { 521 | return p + +f(v); 522 | }; 523 | } 524 | 525 | function crossfilter_reduceSubtract(f) { 526 | return function(p, v) { 527 | return p - f(v); 528 | }; 529 | } 530 | exports.crossfilter = crossfilter; 531 | 532 | function crossfilter() { 533 | var crossfilter = { 534 | add: add, 535 | dimension: dimension, 536 | groupAll: groupAll, 537 | size: size 538 | }; 539 | 540 | var data = [], // the records 541 | n = 0, // the number of records; data.length 542 | m = 0, // number of dimensions in use 543 | M = 8, // number of dimensions that can fit in `filters` 544 | filters = crossfilter_array8(0), // M bits per record; 1 is filtered out 545 | filterListeners = [], // when the filters change 546 | dataListeners = []; // when data is added 547 | 548 | // Adds the specified new records to this crossfilter. 549 | function add(newData) { 550 | var n0 = n, 551 | n1 = newData.length; 552 | 553 | // If there's actually new data to add… 554 | // Merge the new data into the existing data. 555 | // Lengthen the filter bitset to handle the new records. 556 | // Notify listeners (dimensions and groups) that new data is available. 557 | if (n1) { 558 | data = data.concat(newData); 559 | filters = crossfilter_arrayLengthen(filters, n += n1); 560 | dataListeners.forEach(function(l) { l(newData, n0, n1); }); 561 | } 562 | 563 | return crossfilter; 564 | } 565 | 566 | // Adds a new dimension with the specified value accessor function. 567 | function dimension(value) { 568 | var dimension = { 569 | filter: filter, 570 | filterExact: filterExact, 571 | filterRange: filterRange, 572 | filterAll: filterAll, 573 | top: top, 574 | bottom: bottom, 575 | group: group, 576 | groupAll: groupAll 577 | }; 578 | 579 | var one = 1 << m++, // bit mask, e.g., 00001000 580 | zero = ~one, // inverted one, e.g., 11110111 581 | values, // sorted, cached array 582 | index, // value rank ↦ object id 583 | newValues, // temporary array storing newly-added values 584 | newIndex, // temporary array storing newly-added index 585 | sort = quicksort_by(function(i) { return newValues[i]; }), 586 | refilter = crossfilter_filterAll, // for recomputing filter 587 | indexListeners = [], // when data is added 588 | lo0 = 0, 589 | hi0 = 0; 590 | 591 | // Updating a dimension is a two-stage process. First, we must update the 592 | // associated filters for the newly-added records. Once all dimensions have 593 | // updated their filters, the groups are notified to update. 594 | dataListeners.unshift(preAdd); 595 | dataListeners.push(postAdd); 596 | 597 | // Incorporate any existing data into this dimension, and make sure that the 598 | // filter bitset is wide enough to handle the new dimension. 599 | if (m > M) filters = crossfilter_arrayWiden(filters, M <<= 1); 600 | preAdd(data, 0, n); 601 | postAdd(data, 0, n); 602 | 603 | // Incorporates the specified new records into this dimension. 604 | // This function is responsible for updating filters, values, and index. 605 | function preAdd(newData, n0, n1) { 606 | 607 | // Permute new values into natural order using a sorted index. 608 | newValues = newData.map(value); 609 | newIndex = sort(crossfilter_range(n1), 0, n1); 610 | newValues = permute(newValues, newIndex); 611 | 612 | // Bisect newValues to determine which new records are selected. 613 | var bounds = refilter(newValues), lo1 = bounds[0], hi1 = bounds[1], i; 614 | for (i = 0; i < lo1; ++i) filters[newIndex[i] + n0] |= one; 615 | for (i = hi1; i < n1; ++i) filters[newIndex[i] + n0] |= one; 616 | 617 | // If this dimension previously had no data, then we don't need to do the 618 | // more expensive merge operation; use the new values and index as-is. 619 | if (!n0) { 620 | values = newValues; 621 | index = newIndex; 622 | lo0 = lo1; 623 | hi0 = hi1; 624 | return; 625 | } 626 | 627 | var oldValues = values, 628 | oldIndex = index, 629 | i0 = 0, 630 | i1 = 0; 631 | 632 | // Otherwise, create new arrays into which to merge new and old. 633 | values = new Array(n); 634 | index = crossfilter_index(n, n); 635 | 636 | // Merge the old and new sorted values, and old and new index. 637 | for (i = 0; i0 < n0 && i1 < n1; ++i) { 638 | if (oldValues[i0] < newValues[i1]) { 639 | values[i] = oldValues[i0]; 640 | index[i] = oldIndex[i0++]; 641 | } else { 642 | values[i] = newValues[i1]; 643 | index[i] = newIndex[i1++] + n0; 644 | } 645 | } 646 | 647 | // Add any remaining old values. 648 | for (; i0 < n0; ++i0, ++i) { 649 | values[i] = oldValues[i0]; 650 | index[i] = oldIndex[i0]; 651 | } 652 | 653 | // Add any remaining new values. 654 | for (; i1 < n1; ++i1, ++i) { 655 | values[i] = newValues[i1]; 656 | index[i] = newIndex[i1] + n0; 657 | } 658 | 659 | // Bisect again to recompute lo0 and hi0. 660 | bounds = refilter(values), lo0 = bounds[0], hi0 = bounds[1]; 661 | } 662 | 663 | // When all filters have updated, notify index listeners of the new values. 664 | function postAdd(newData, n0, n1) { 665 | indexListeners.forEach(function(l) { l(newValues, newIndex, n0, n1); }); 666 | newValues = newIndex = null; 667 | } 668 | 669 | // Updates the selected values based on the specified bounds [lo, hi]. 670 | // This implementation is used by all the public filter methods. 671 | function filterIndex(bounds) { 672 | var i, 673 | j, 674 | k, 675 | lo1 = bounds[0], 676 | hi1 = bounds[1], 677 | added = [], 678 | removed = []; 679 | 680 | // Fast incremental update based on previous lo index. 681 | if (lo1 < lo0) { 682 | for (i = lo1, j = Math.min(lo0, hi1); i < j; ++i) { 683 | filters[k = index[i]] ^= one; 684 | added.push(k); 685 | } 686 | } else if (lo1 > lo0) { 687 | for (i = lo0, j = Math.min(lo1, hi0); i < j; ++i) { 688 | filters[k = index[i]] ^= one; 689 | removed.push(k); 690 | } 691 | } 692 | 693 | // Fast incremental update based on previous hi index. 694 | if (hi1 > hi0) { 695 | for (i = Math.max(lo1, hi0), j = hi1; i < j; ++i) { 696 | filters[k = index[i]] ^= one; 697 | added.push(k); 698 | } 699 | } else if (hi1 < hi0) { 700 | for (i = Math.max(lo0, hi1), j = hi0; i < j; ++i) { 701 | filters[k = index[i]] ^= one; 702 | removed.push(k); 703 | } 704 | } 705 | 706 | lo0 = lo1; 707 | hi0 = hi1; 708 | filterListeners.forEach(function(l) { l(one, added, removed); }); 709 | return dimension; 710 | } 711 | 712 | // Filters this dimension using the specified range, value, or null. 713 | // If the range is null, this is equivalent to filterAll. 714 | // If the range is an array, this is equivalent to filterRange. 715 | // Otherwise, this is equivalent to filterExact. 716 | function filter(range) { 717 | return range == null 718 | ? filterAll() : Array.isArray(range) 719 | ? filterRange(range) 720 | : filterExact(range); 721 | } 722 | 723 | // Filters this dimension to select the exact value. 724 | function filterExact(value) { 725 | return filterIndex((refilter = crossfilter_filterExact(bisect, value))(values)); 726 | } 727 | 728 | // Filters this dimension to select the specified range [lo, hi]. 729 | // The lower bound is inclusive, and the upper bound is exclusive. 730 | function filterRange(range) { 731 | return filterIndex((refilter = crossfilter_filterRange(bisect, range))(values)); 732 | } 733 | 734 | // Clears any filters on this dimension. 735 | function filterAll() { 736 | return filterIndex((refilter = crossfilter_filterAll)(values)); 737 | } 738 | 739 | // Returns the top K selected records based on this dimension's order. 740 | // Note: observes this dimension's filter, unlike group and groupAll. 741 | function top(k) { 742 | var array = [], 743 | i = hi0, 744 | j; 745 | 746 | while (--i >= lo0 && k > 0) { 747 | if (!filters[j = index[i]]) { 748 | array.push(data[j]); 749 | --k; 750 | } 751 | } 752 | 753 | return array; 754 | } 755 | 756 | // Returns the bottom K selected records based on this dimension's order. 757 | // Note: observes this dimension's filter, unlike group and groupAll. 758 | function bottom(k) { 759 | var array = [], 760 | i = lo0, 761 | j; 762 | 763 | while (i < hi0 && k > 0) { 764 | if (!filters[j = index[i]]) { 765 | array.push(data[j]); 766 | --k; 767 | } 768 | i++; 769 | } 770 | 771 | return array; 772 | } 773 | 774 | // Adds a new group to this dimension, using the specified key function. 775 | function group(key) { 776 | var group = { 777 | top: top, 778 | all: all, 779 | reduce: reduce, 780 | reduceCount: reduceCount, 781 | reduceSum: reduceSum, 782 | order: order, 783 | orderNatural: orderNatural, 784 | size: size 785 | }; 786 | 787 | var groups, // array of {key, value} 788 | groupIndex, // object id ↦ group id 789 | groupWidth = 8, 790 | groupCapacity = crossfilter_capacity(groupWidth), 791 | k = 0, // cardinality 792 | select, 793 | heap, 794 | reduceAdd, 795 | reduceRemove, 796 | reduceInitial, 797 | update = crossfilter_null, 798 | reset = crossfilter_null, 799 | resetNeeded = true; 800 | 801 | if (arguments.length < 1) key = crossfilter_identity; 802 | 803 | // The group listens to the crossfilter for when any dimension changes, so 804 | // that it can update the associated reduce values. It must also listen to 805 | // the parent dimension for when data is added, and compute new keys. 806 | filterListeners.push(update); 807 | indexListeners.push(add); 808 | 809 | // Incorporate any existing data into the grouping. 810 | add(values, index, 0, n); 811 | 812 | // Incorporates the specified new values into this group. 813 | // This function is responsible for updating groups and groupIndex. 814 | function add(newValues, newIndex, n0, n1) { 815 | var oldGroups = groups, 816 | reIndex = crossfilter_index(k, groupCapacity), 817 | add = reduceAdd, 818 | initial = reduceInitial, 819 | k0 = k, // old cardinality 820 | i0 = 0, // index of old group 821 | i1 = 0, // index of new record 822 | j, // object id 823 | g0, // old group 824 | x0, // old key 825 | x1, // new key 826 | g, // group to add 827 | x; // key of group to add 828 | 829 | // If a reset is needed, we don't need to update the reduce values. 830 | if (resetNeeded) add = initial = crossfilter_null; 831 | 832 | // Reset the new groups (k is a lower bound). 833 | // Also, make sure that groupIndex exists and is long enough. 834 | groups = new Array(k), k = 0; 835 | groupIndex = k0 > 1 ? crossfilter_arrayLengthen(groupIndex, n) : crossfilter_index(n, groupCapacity); 836 | 837 | // Get the first old key (x0 of g0), if it exists. 838 | if (k0) x0 = (g0 = oldGroups[0]).key; 839 | 840 | // Find the first new key (x1). 841 | x1 = key(newValues[i1]); 842 | 843 | // While new keys remain… 844 | while (i1 < n1) { 845 | 846 | // Determine the lesser of the two current keys; new and old. 847 | // If there are no old keys remaining, then always add the new key. 848 | if (g0 && x0 <= x1) { 849 | g = g0, x = x0; 850 | 851 | // Record the new index of the old group. 852 | reIndex[i0] = k; 853 | 854 | // Retrieve the next old key. 855 | if (g0 = oldGroups[++i0]) x0 = g0.key; 856 | } else { 857 | g = {key: x1, value: initial()}, x = x1; 858 | } 859 | 860 | // Add the lesser group. 861 | groups[k] = g; 862 | 863 | // Add any selected records belonging to the added group, while 864 | // advancing the new key and populating the associated group index. 865 | while (x1 <= x || !(x1 <= x1) && !(x <= x)) { 866 | groupIndex[j = newIndex[i1] + n0] = k; 867 | if (!(filters[j] & zero)) g.value = add(g.value, data[j]); 868 | if (++i1 >= n1) break; 869 | x1 = key(newValues[i1]); 870 | } 871 | 872 | groupIncrement(); 873 | } 874 | 875 | // Add any remaining old groups that were greater than all new keys. 876 | // No incremental reduce is needed; these groups have no new records. 877 | // Also record the new index of the old group. 878 | while (i0 < k0) { 879 | groups[reIndex[i0] = k] = oldGroups[i0++]; 880 | groupIncrement(); 881 | } 882 | 883 | // If we added any new groups before any old groups, 884 | // update the group index of all the old records. 885 | if (k > i0) for (i0 = 0; i0 < n0; ++i0) { 886 | groupIndex[i0] = reIndex[groupIndex[i0]]; 887 | } 888 | 889 | // Modify the update and reset behavior based on the cardinality. 890 | // If the cardinality is less than or equal to one, then the groupIndex 891 | // is not needed. If the cardinality is zero, then there are no records 892 | // and therefore no groups to update or reset. Note that we also must 893 | // change the registered listener to point to the new method. 894 | j = filterListeners.indexOf(update); 895 | if (k > 1) { 896 | update = updateMany; 897 | reset = resetMany; 898 | } else { 899 | if (k === 1) { 900 | update = updateOne; 901 | reset = resetOne; 902 | } else { 903 | update = crossfilter_null; 904 | reset = crossfilter_null; 905 | } 906 | groupIndex = null; 907 | } 908 | filterListeners[j] = update; 909 | 910 | // Count the number of added groups, 911 | // and widen the group index as needed. 912 | function groupIncrement() { 913 | if (++k === groupCapacity) { 914 | reIndex = crossfilter_arrayWiden(reIndex, groupWidth <<= 1); 915 | groupIndex = crossfilter_arrayWiden(groupIndex, groupWidth); 916 | groupCapacity = crossfilter_capacity(groupWidth); 917 | } 918 | } 919 | } 920 | 921 | // Reduces the specified selected or deselected records. 922 | // This function is only used when the cardinality is greater than 1. 923 | function updateMany(filterOne, added, removed) { 924 | if (filterOne === one || resetNeeded) return; 925 | 926 | var i, 927 | k, 928 | n, 929 | g; 930 | 931 | // Add the added values. 932 | for (i = 0, n = added.length; i < n; ++i) { 933 | if (!(filters[k = added[i]] & zero)) { 934 | g = groups[groupIndex[k]]; 935 | g.value = reduceAdd(g.value, data[k]); 936 | } 937 | } 938 | 939 | // Remove the removed values. 940 | for (i = 0, n = removed.length; i < n; ++i) { 941 | if ((filters[k = removed[i]] & zero) === filterOne) { 942 | g = groups[groupIndex[k]]; 943 | g.value = reduceRemove(g.value, data[k]); 944 | } 945 | } 946 | } 947 | 948 | // Reduces the specified selected or deselected records. 949 | // This function is only used when the cardinality is 1. 950 | function updateOne(filterOne, added, removed) { 951 | if (filterOne === one || resetNeeded) return; 952 | 953 | var i, 954 | k, 955 | n, 956 | g = groups[0]; 957 | 958 | // Add the added values. 959 | for (i = 0, n = added.length; i < n; ++i) { 960 | if (!(filters[k = added[i]] & zero)) { 961 | g.value = reduceAdd(g.value, data[k]); 962 | } 963 | } 964 | 965 | // Remove the removed values. 966 | for (i = 0, n = removed.length; i < n; ++i) { 967 | if ((filters[k = removed[i]] & zero) === filterOne) { 968 | g.value = reduceRemove(g.value, data[k]); 969 | } 970 | } 971 | } 972 | 973 | // Recomputes the group reduce values from scratch. 974 | // This function is only used when the cardinality is greater than 1. 975 | function resetMany() { 976 | var i, 977 | g; 978 | 979 | // Reset all group values. 980 | for (i = 0; i < k; ++i) { 981 | groups[i].value = reduceInitial(); 982 | } 983 | 984 | // Add any selected records. 985 | for (i = 0; i < n; ++i) { 986 | if (!(filters[i] & zero)) { 987 | g = groups[groupIndex[i]]; 988 | g.value = reduceAdd(g.value, data[i]); 989 | } 990 | } 991 | } 992 | 993 | // Recomputes the group reduce values from scratch. 994 | // This function is only used when the cardinality is 1. 995 | function resetOne() { 996 | var i, 997 | g = groups[0]; 998 | 999 | // Reset the singleton group values. 1000 | g.value = reduceInitial(); 1001 | 1002 | // Add any selected records. 1003 | for (i = 0; i < n; ++i) { 1004 | if (!(filters[i] & zero)) { 1005 | g.value = reduceAdd(g.value, data[i]); 1006 | } 1007 | } 1008 | } 1009 | 1010 | // Returns the array of group values, in the dimension's natural order. 1011 | function all() { 1012 | if (resetNeeded) reset(), resetNeeded = false; 1013 | return groups; 1014 | } 1015 | 1016 | // Returns a new array containing the top K group values, in reduce order. 1017 | function top(k) { 1018 | var top = select(all(), 0, groups.length, k); 1019 | return heap.sort(top, 0, top.length); 1020 | } 1021 | 1022 | // Sets the reduce behavior for this group to use the specified functions. 1023 | // This method lazily recomputes the reduce values, waiting until needed. 1024 | function reduce(add, remove, initial) { 1025 | reduceAdd = add; 1026 | reduceRemove = remove; 1027 | reduceInitial = initial; 1028 | resetNeeded = true; 1029 | return group; 1030 | } 1031 | 1032 | // A convenience method for reducing by count. 1033 | function reduceCount() { 1034 | return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero); 1035 | } 1036 | 1037 | // A convenience method for reducing by sum(value). 1038 | function reduceSum(value) { 1039 | return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero); 1040 | } 1041 | 1042 | // Sets the reduce order, using the specified accessor. 1043 | function order(value) { 1044 | select = heapselect_by(valueOf); 1045 | heap = heap_by(valueOf); 1046 | function valueOf(d) { return value(d.value); } 1047 | return group; 1048 | } 1049 | 1050 | // A convenience method for natural ordering by reduce value. 1051 | function orderNatural() { 1052 | return order(crossfilter_identity); 1053 | } 1054 | 1055 | // Returns the cardinality of this group, irrespective of any filters. 1056 | function size() { 1057 | return k; 1058 | } 1059 | 1060 | return reduceCount().orderNatural(); 1061 | } 1062 | 1063 | // A convenience function for generating a singleton group. 1064 | function groupAll() { 1065 | var g = group(crossfilter_null), all = g.all; 1066 | delete g.all; 1067 | delete g.top; 1068 | delete g.order; 1069 | delete g.orderNatural; 1070 | delete g.size; 1071 | g.value = function() { return all()[0].value; }; 1072 | return g; 1073 | } 1074 | 1075 | return dimension; 1076 | } 1077 | 1078 | // A convenience method for groupAll on a dummy dimension. 1079 | // This implementation can be optimized since it is always cardinality 1. 1080 | function groupAll() { 1081 | var group = { 1082 | reduce: reduce, 1083 | reduceCount: reduceCount, 1084 | reduceSum: reduceSum, 1085 | value: value 1086 | }; 1087 | 1088 | var reduceValue, 1089 | reduceAdd, 1090 | reduceRemove, 1091 | reduceInitial, 1092 | resetNeeded = true; 1093 | 1094 | // The group listens to the crossfilter for when any dimension changes, so 1095 | // that it can update the reduce value. It must also listen to the parent 1096 | // dimension for when data is added. 1097 | filterListeners.push(update); 1098 | dataListeners.push(add); 1099 | 1100 | // For consistency; actually a no-op since resetNeeded is true. 1101 | add(data, 0, n); 1102 | 1103 | // Incorporates the specified new values into this group. 1104 | function add(newData, n0) { 1105 | var i; 1106 | 1107 | if (resetNeeded) return; 1108 | 1109 | // Add the added values. 1110 | for (i = n0; i < n; ++i) { 1111 | if (!filters[i]) { 1112 | reduceValue = reduceAdd(reduceValue, data[i]); 1113 | } 1114 | } 1115 | } 1116 | 1117 | // Reduces the specified selected or deselected records. 1118 | function update(filterOne, added, removed) { 1119 | var i, 1120 | k, 1121 | n; 1122 | 1123 | if (resetNeeded) return; 1124 | 1125 | // Add the added values. 1126 | for (i = 0, n = added.length; i < n; ++i) { 1127 | if (!filters[k = added[i]]) { 1128 | reduceValue = reduceAdd(reduceValue, data[k]); 1129 | } 1130 | } 1131 | 1132 | // Remove the removed values. 1133 | for (i = 0, n = removed.length; i < n; ++i) { 1134 | if (filters[k = removed[i]] === filterOne) { 1135 | reduceValue = reduceRemove(reduceValue, data[k]); 1136 | } 1137 | } 1138 | } 1139 | 1140 | // Recomputes the group reduce value from scratch. 1141 | function reset() { 1142 | var i; 1143 | 1144 | reduceValue = reduceInitial(); 1145 | 1146 | for (i = 0; i < n; ++i) { 1147 | if (!filters[i]) { 1148 | reduceValue = reduceAdd(reduceValue, data[i]); 1149 | } 1150 | } 1151 | } 1152 | 1153 | // Sets the reduce behavior for this group to use the specified functions. 1154 | // This method lazily recomputes the reduce value, waiting until needed. 1155 | function reduce(add, remove, initial) { 1156 | reduceAdd = add; 1157 | reduceRemove = remove; 1158 | reduceInitial = initial; 1159 | resetNeeded = true; 1160 | return group; 1161 | } 1162 | 1163 | // A convenience method for reducing by count. 1164 | function reduceCount() { 1165 | return reduce(crossfilter_reduceIncrement, crossfilter_reduceDecrement, crossfilter_zero); 1166 | } 1167 | 1168 | // A convenience method for reducing by sum(value). 1169 | function reduceSum(value) { 1170 | return reduce(crossfilter_reduceAdd(value), crossfilter_reduceSubtract(value), crossfilter_zero); 1171 | } 1172 | 1173 | // Returns the computed reduce value. 1174 | function value() { 1175 | if (resetNeeded) reset(), resetNeeded = false; 1176 | return reduceValue; 1177 | } 1178 | 1179 | return reduceCount(); 1180 | } 1181 | 1182 | // Returns the number of records in this crossfilter, irrespective of any filters. 1183 | function size() { 1184 | return n; 1185 | } 1186 | 1187 | return arguments.length 1188 | ? add(arguments[0]) 1189 | : crossfilter; 1190 | } 1191 | 1192 | // Returns an array of size n, big enough to store ids up to m. 1193 | function crossfilter_index(n, m) { 1194 | return (m < 0x101 1195 | ? crossfilter_array8 : m < 0x10001 1196 | ? crossfilter_array16 1197 | : crossfilter_array32)(n); 1198 | } 1199 | 1200 | // Constructs a new array of size n, with sequential values from 0 to n - 1. 1201 | function crossfilter_range(n) { 1202 | var range = crossfilter_index(n, n); 1203 | for (var i = -1; ++i < n;) range[i] = i; 1204 | return range; 1205 | } 1206 | 1207 | function crossfilter_capacity(w) { 1208 | return w === 8 1209 | ? 0x100 : w === 16 1210 | ? 0x10000 1211 | : 0x100000000; 1212 | } 1213 | })(this); 1214 | -------------------------------------------------------------------------------- /www/d3.v2.min.js: -------------------------------------------------------------------------------- 1 | (function(){function e(a,b){try{for(var c in b)Object.defineProperty(a.prototype,c,{value:b[c],enumerable:!1})}catch(d){a.prototype=b}}function g(a){var b=-1,c=a.length,d=[];while(++b=0?a.substring(b):(b=a.length,""),d=[];while(b>0)d.push(a.substring(b-=3,b+3));return d.reverse().join(",")+c}function I(a,b){var c=Math.pow(10,Math.abs(8-b)*3);return{scale:b>8?function(a){return a/c}:function(a){return a*c},symbol:a}}function O(a){return function(b){return b<=0?0:b>=1?1:a(b)}}function P(a){return function(b){return 1-a(1-b)}}function Q(a){return function(b){return.5*(b<.5?a(2*b):2-a(2-2*b))}}function R(a){return a}function S(a){return function(b){return Math.pow(b,a)}}function T(a){return 1-Math.cos(a*Math.PI/2)}function U(a){return Math.pow(2,10*(a-1))}function V(a){return 1-Math.sqrt(1-a*a)}function W(a,b){var c;return arguments.length<2&&(b=.45),arguments.length<1?(a=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/a),function(d){return 1+a*Math.pow(2,10*-d)*Math.sin((d-c)*2*Math.PI/b)}}function X(a){return a||(a=1.70158),function(b){return b*b*((a+1)*b-a)}}function Y(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375}function Z(){d3.event.stopPropagation(),d3.event.preventDefault()}function $(){var a=d3.event,b;while(b=a.sourceEvent)a=b;return a}function _(a){var b=new A,c=0,d=arguments.length;while(++c360?a-=360:a<0&&(a+=360),a<60?d+(e-d)*a/60:a<180?e:a<240?d+(e-d)*(240-a)/60:d}function g(a){return Math.round(f(a)*255)}var d,e;return a%=360,a<0&&(a+=360),b=b<0?0:b>1?1:b,c=c<0?0:c>1?1:c,e=c<=.5?c*(1+b):c+b-c*b,d=2*c-e,be(g(a+120),g(a),g(a-120))}function bo(a){return j(a,bu),a}function bv(a){return function(){return bp(a,this)}}function bw(a){return function(){return bq(a,this)}}function by(a,b){function f(){if(b=this.classList)return b.add(a);var b=this.className,d=b.baseVal!=null,e=d?b.baseVal:b;c.lastIndex=0,c.test(e)||(e=w(e+" "+a),d?b.baseVal=e:this.className=e)}function g(){if(b=this.classList)return b.remove(a);var b=this.className,d=b.baseVal!=null,e=d?b.baseVal:b;e=w(e.replace(c," ")),d?b.baseVal=e:this.className=e}function h(){(b.apply(this,arguments)?f:g).call(this)}var c=new RegExp("(^|\\s+)"+d3.requote(a)+"(\\s+|$)","g");if(arguments.length<2){var d=this.node();if(e=d.classList)return e.contains(a);var e=d.className;return c.lastIndex=0,c.test(e.baseVal!=null?e.baseVal:e)}return this.each(typeof b=="function"?h:b?f:g)}function bz(a){return{__data__:a}}function bA(a){return function(){return bt(this,a)}}function bB(a){return arguments.length||(a=d3.ascending),function(b,c){return a(b&&b.__data__,c&&c.__data__)}}function bD(a){return j(a,bE),a}function bF(a,b,c){j(a,bJ);var d=new k,e=d3.dispatch("start","end"),f=bR;return a.id=b,a.time=c,a.tween=function(b,c){return arguments.length<2?d.get(b):(c==null?d.remove(b):d.set(b,c),a)},a.ease=function(b){return arguments.length?(f=typeof b=="function"?b:d3.ease.apply(d3,arguments),a):f},a.each=function(b,c){return arguments.length<2?bS.call(a,b):(e.on(b,c),a)},d3.timer(function(g){return a.each(function(h,i,j){function p(a){return o.active>b?r():(o.active=b,d.forEach(function(a,b){(b=b.call(l,h,i))&&k.push(b)}),e.start.call(l,h,i),q(a)||d3.timer(q,0,c),1)}function q(a){if(o.active!==b)return r();var c=(a-m)/n,d=f(c),g=k.length;while(g>0)k[--g].call(l,d);if(c>=1)return r(),bL=b,e.end.call(l,h,i),bL=0,1}function r(){return--o.count||delete l.__transition__,1}var k=[],l=this,m=a[j][i].delay,n=a[j][i].duration,o=l.__transition__||(l.__transition__={active:0,count:0});++o.count,m<=g?p(g):d3.timer(p,m,c)}),1},0,c),a}function bH(a,b,c){return c!=""&&bG}function bI(a,b){function d(a,d,e){var f=b.call(this,a,d);return f==null?e!=""&&bG:e!=f&&c(e,f)}function e(a,d,e){return e!=b&&c(e,b)}var c=bb(a);return typeof b=="function"?d:b==null?bH:(b+="",e)}function bS(a){var b=bL,c=bR,d=bP,e=bQ;bL=this.id,bR=this.ease();for(var f=0,g=this.length;f=c.delay&&(c.flush=c.callback(a)),c=c.next;var d=bX()-b;d>24?(isFinite(d)&&(clearTimeout(bV),bV=setTimeout(bW,d)),bU=0):(bU=1,bY(bW))}function bX(){var a=null,b=bT,c=Infinity;while(b)b.flush?b=a?a.next=b.next:bT=b.next:(c=Math.min(c,b.then+b.delay),b=(a=b).next);return c}function bZ(a){var b=[a.a,a.b],c=[a.c,a.d],d=b_(b),e=b$(b,c),f=b_(ca(c,b,-e))||0;b[0]*c[1]2?cq:cp,i=d?bd:bc;return e=g(a,b,i,c),f=g(b,a,i,d3.interpolate),h}function h(a){return e(a)}var e,f;return h.invert=function(a){return f(a)},h.domain=function(b){return arguments.length?(a=b.map(Number),g()):a},h.range=function(a){return arguments.length?(b=a,g()):b},h.rangeRound=function(a){return h.range(a).interpolate(d3.interpolateRound)},h.clamp=function(a){return arguments.length?(d=a,g()):d},h.interpolate=function(a){return arguments.length?(c=a,g()):c},h.ticks=function(b){return cn(a,b)},h.tickFormat=function(b){return co(a,b)},h.nice=function(){return ch(a,cl),g()},h.copy=function(){return cj(a,b,c,d)},g()}function ck(a,b){return d3.rebind(a,b,"range","rangeRound","interpolate","clamp")}function cl(a){return a=Math.pow(10,Math.round(Math.log(a)/Math.LN10)-1),{floor:function(b){return Math.floor(b/a)*a},ceil:function(b){return Math.ceil(b/a)*a}}}function cm(a,b){var c=cf(a),d=c[1]-c[0],e=Math.pow(10,Math.floor(Math.log(d/b)/Math.LN10)),f=b/d*e;return f<=.15?e*=10:f<=.35?e*=5:f<=.75&&(e*=2),c[0]=Math.ceil(c[0]/e)*e,c[1]=Math.floor(c[1]/e)*e+e*.5,c[2]=e,c}function cn(a,b){return d3.range.apply(d3,cm(a,b))}function co(a,b){return d3.format(",."+Math.max(0,-Math.floor(Math.log(cm(a,b)[2])/Math.LN10+.01))+"f")}function cp(a,b,c,d){var e=c(a[0],a[1]),f=d(b[0],b[1]);return function(a){return f(e(a))}}function cq(a,b,c,d){var e=[],f=[],g=0,h=Math.min(a.length,b.length)-1;a[h]0;j--)e.push(c(f)*j)}else{for(;fi;g--);e=e.slice(f,g)}return e},d.tickFormat=function(a,e){arguments.length<2&&(e=cs);if(arguments.length<1)return e;var f=a/d.ticks().length,g=b===cu?(h=-1e-12,Math.floor):(h=1e-12,Math.ceil),h;return function(a){return a/c(g(b(a)+h))0?0:-a)/Math.LN10}function cv(a,b){function e(b){return a(c(b))}var c=cw(b),d=cw(1/b);return e.invert=function(b){return d(a.invert(b))},e.domain=function(b){return arguments.length?(a.domain(b.map(c)),e):a.domain().map(d)},e.ticks=function(a){return cn(e.domain(),a)},e.tickFormat=function(a){return co(e.domain(),a)},e.nice=function(){return e.domain(ch(e.domain(),cl))},e.exponent=function(a){if(!arguments.length)return b;var f=e.domain();return c=cw(b=a),d=cw(1/b),e.domain(f)},e.copy=function(){return cv(a.copy(),b)},ck(e,a)}function cw(a){return function(b){return b<0?-Math.pow(-b,a):Math.pow(b,a)}}function cx(a,b){function f(b){return d[((c.get(b)||c.set(b,a.push(b)))-1)%d.length]}function g(b,c){return d3.range(a.length).map(function(a){return b+c*a})}var c,d,e;return f.domain=function(d){if(!arguments.length)return a;a=[],c=new k;var e=-1,g=d.length,h;while(++e1){h=b[1],f=a[i],i++,d+="C"+(e[0]+g[0])+","+(e[1]+g[1])+","+(f[0]-h[0])+","+(f[1]-h[1])+","+f[0]+","+f[1];for(var j=2;j9&&(f=c*3/Math.sqrt(f),g[h]=f*d,g[h+1]=f*e));h=-1;while(++h<=i)f=(a[Math.min(i,h+1)][0]-a[Math.max(0,h-1)][0])/(6*(1+g[h]*g[h])),b.push([f||0,g[h]*f||0]);return b}function di(a){return a.length<3?cQ(a):a[0]+cW(a,dh(a))}function dj(a){var b,c=-1,d=a.length,e,f;while(++c1){var d=cf(a.domain()),e,f=-1,g=b.length,h=(b[1]-b[0])/++c,i,j;while(++f0;)(j=+b[f]-i*h)>=d[0]&&e.push(j);for(--f,i=0;++id&&(c=b,d=e);return c}function ea(a){return a.reduce(eb,0)}function eb(a,b){return a+b[1]}function ec(a,b){return ed(a,Math.ceil(Math.log(b.length)/Math.LN2+1))}function ed(a,b){var c=-1,d=+a[0],e=(a[1]-d)/b,f=[];while(++c<=b)f[c]=e*c+d;return f}function ee(a){return[d3.min(a),d3.max(a)]}function ef(a,b){return d3.rebind(a,b,"sort","children","value"),a.links=ej,a.nodes=function(b){return ek=!0,(a.nodes=a)(b)},a}function eg(a){return a.children}function eh(a){return a.value}function ei(a,b){return b.value-a.value}function ej(a){return d3.merge(a.map(function(a){return(a.children||[]).map(function(b){return{source:a,target:b}})}))}function el(a,b){return a.value-b.value}function em(a,b){var c=a._pack_next;a._pack_next=b,b._pack_prev=a,b._pack_next=c,c._pack_prev=b}function en(a,b){a._pack_next=b,b._pack_prev=a}function eo(a,b){var c=b.x-a.x,d=b.y-a.y,e=a.r+b.r;return e*e-c*c-d*d>.001}function ep(a){function l(a){b=Math.min(a.x-a.r,b),c=Math.max(a.x+a.r,c),d=Math.min(a.y-a.r,d),e=Math.max(a.y+a.r,e)}var b=Infinity,c=-Infinity,d=Infinity,e=-Infinity,f=a.length,g,h,i,j,k;a.forEach(eq),g=a[0],g.x=-g.r,g.y=0,l(g);if(f>1){h=a[1],h.x=h.r,h.y=0,l(h);if(f>2){i=a[2],eu(g,h,i),l(i),em(g,i),g._pack_prev=i,em(i,h),h=g._pack_next;for(var m=3;m0&&(a=d)}return a}function eD(a,b){return a.x-b.x}function eE(a,b){return b.x-a.x}function eF(a,b){return a.depth-b.depth}function eG(a,b){function c(a,d){var e=a.children;if(e&&(i=e.length)){var f,g=null,h=-1,i;while(++h=0)f=d[e]._tree,f.prelim+=b,f.mod+=b,b+=f.shift+(c+=f.change)}function eI(a,b,c){a=a._tree,b=b._tree;var d=c/(b.number-a.number);a.change+=d,b.change-=d,b.shift+=c,b.prelim+=c,b.mod+=c}function eJ(a,b,c){return a._tree.ancestor.parent==b.parent?a._tree.ancestor:c}function eK(a){return{x:a.x,y:a.y,dx:a.dx,dy:a.dy}}function eL(a,b){var c=a.x+b[3],d=a.y+b[0],e=a.dx-b[1]-b[3],f=a.dy-b[0]-b[2];return e<0&&(c+=e/2,e=0),f<0&&(d+=f/2,f=0),{x:c,y:d,dx:e,dy:f}}function eM(a){return a.map(eN).join(",")}function eN(a){return/[",\n]/.test(a)?'"'+a.replace(/\"/g,'""')+'"':a}function eP(a,b){return function(c){return c&&a.hasOwnProperty(c.type)?a[c.type](c):b}}function eQ(a){return"m0,"+a+"a"+a+","+a+" 0 1,1 0,"+ -2*a+"a"+a+","+a+" 0 1,1 0,"+2*a+"z"}function eR(a,b){eS.hasOwnProperty(a.type)&&eS[a.type](a,b)}function eT(a,b){eR(a.geometry,b)}function eU(a,b){for(var c=a.features,d=0,e=c.length;d0}function fg(a,b,c){return(c[0]-b[0])*(a[1]-b[1])<(c[1]-b[1])*(a[0]-b[0])}function fh(a,b,c,d){var e=a[0],f=b[0],g=c[0],h=d[0],i=a[1],j=b[1],k=c[1],l=d[1],m=e-g,n=f-e,o=h-g,p=i-k,q=j-i,r=l-k,s=(o*p-r*m)/(r*n-o*q);return[e+s*n,i+s*q]}function fj(a,b){var c={list:a.map(function(a,b){return{index:b,x:a[0],y:a[1]}}).sort(function(a,b){return a.yb.y?1:a.xb.x?1:0}),bottomSite:null},d={list:[],leftEnd:null,rightEnd:null,init:function(){d.leftEnd=d.createHalfEdge(null,"l"),d.rightEnd=d.createHalfEdge(null,"l"),d.leftEnd.r=d.rightEnd,d.rightEnd.l=d.leftEnd,d.list.unshift(d.leftEnd,d.rightEnd)},createHalfEdge:function(a,b){return{edge:a,side:b,vertex:null,l:null,r:null}},insert:function(a,b){b.l=a,b.r=a.r,a.r.l=b,a.r=b},leftBound:function(a){var b=d.leftEnd;do b=b.r;while(b!=d.rightEnd&&e.rightOf(b,a));return b=b.l,b},del:function(a){a.l.r=a.r,a.r.l=a.l,a.edge=null},right:function(a){return a.r},left:function(a){return a.l},leftRegion:function(a){return a.edge==null?c.bottomSite:a.edge.region[a.side]},rightRegion:function(a){return a.edge==null?c.bottomSite:a.edge.region[fi[a.side]]}},e={bisect:function(a,b){var c={region:{l:a,r:b},ep:{l:null,r:null}},d=b.x-a.x,e=b.y-a.y,f=d>0?d:-d,g=e>0?e:-e;return c.c=a.x*d+a.y*e+(d*d+e*e)*.5,f>g?(c.a=1,c.b=e/d,c.c/=d):(c.b=1,c.a=d/e,c.c/=e),c},intersect:function(a,b){var c=a.edge,d=b.edge;if(!c||!d||c.region.r==d.region.r)return null;var e=c.a*d.b-c.b*d.a;if(Math.abs(e)<1e-10)return null;var f=(c.c*d.b-d.c*c.b)/e,g=(d.c*c.a-c.c*d.a)/e,h=c.region.r,i=d.region.r,j,k;h.y=k.region.r.x;return l&&j.side==="l"||!l&&j.side==="r"?null:{x:f,y:g}},rightOf:function(a,b){var c=a.edge,d=c.region.r,e=b.x>d.x;if(e&&a.side==="l")return 1;if(!e&&a.side==="r")return 0;if(c.a===1){var f=b.y-d.y,g=b.x-d.x,h=0,i=0;!e&&c.b<0||e&&c.b>=0?i=h=f>=c.b*g:(i=b.x+b.y*c.b>c.c,c.b<0&&(i=!i),i||(h=1));if(!h){var j=d.x-c.region.l.x;i=c.b*(g*g-f*f)m*m+n*n}return a.side==="l"?i:!i},endPoint:function(a,c,d){a.ep[c]=d;if(!a.ep[fi[c]])return;b(a)},distance:function(a,b){var c=a.x-b.x,d=a.y-b.y;return Math.sqrt(c*c+d*d)}},f={list:[],insert:function(a,b,c){a.vertex=b,a.ystar=b.y+c;for(var d=0,e=f.list,g=e.length;dh.ystar||a.ystar==h.ystar&&b.x>h.vertex.x)continue;break}e.splice(d,0,a)},del:function(a){for(var b=0,c=f.list,d=c.length;bo.y&&(p=n,n=o,o=p,t="r"),s=e.bisect(n,o),m=d.createHalfEdge(s,t),d.insert(k,m),e.endPoint(s,fi[t],r),q=e.intersect(k,m),q&&(f.del(k),f.insert(k,q,e.distance(q,n))),q=e.intersect(m,l),q&&f.insert(m,q,e.distance(q,n));else break}for(i=d.right(d.leftEnd);i!=d.rightEnd;i=d.right(i))b(i.edge)}function fk(){return{leaf:!0,nodes:[],point:null}}function fl(a,b,c,d,e,f){if(!a(b,c,d,e,f)){var g=(c+e)*.5,h=(d+f)*.5,i=b.nodes;i[0]&&fl(a,i[0],c,d,g,h),i[1]&&fl(a,i[1],g,d,e,h),i[2]&&fl(a,i[2],c,h,g,f),i[3]&&fl(a,i[3],g,h,e,f)}}function fm(a){return{x:a[0],y:a[1]}}function fo(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function fq(a,b,c,d){var e,f,g=0,h=b.length,i=c.length;while(g=i)return-1;e=b.charCodeAt(g++);if(e==37){f=fw[b.charAt(g++)];if(!f||(d=f(a,c,d))<0)return-1}else if(e!=c.charCodeAt(d++))return-1}return d}function fx(a,b,c){return fz.test(b.substring(c,c+=3))?c:-1}function fy(a,b,c){fA.lastIndex=0;var d=fA.exec(b.substring(c,c+10));return d?c+=d[0].length:-1}function fC(a,b,c){var d=fD.get(b.substring(c,c+=3).toLowerCase());return d==null?-1:(a.m=d,c)}function fE(a,b,c){fF.lastIndex=0;var d=fF.exec(b.substring(c,c+12));return d?(a.m=fG.get(d[0].toLowerCase()),c+=d[0].length):-1}function fI(a,b,c){return fq(a,fv.c.toString(),b,c)}function fJ(a,b,c){return fq(a,fv.x.toString(),b,c)}function fK(a,b,c){return fq(a,fv.X.toString(),b,c)}function fL(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+4));return d?(a.y=+d[0],c+=d[0].length):-1}function fM(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+2));return d?(a.y=fN()+ +d[0],c+=d[0].length):-1}function fN(){return~~((new Date).getFullYear()/1e3)*1e3}function fO(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+2));return d?(a.m=d[0]-1,c+=d[0].length):-1}function fP(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+2));return d?(a.d=+d[0],c+=d[0].length):-1}function fQ(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+2));return d?(a.H=+d[0],c+=d[0].length):-1}function fR(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+2));return d?(a.M=+d[0],c+=d[0].length):-1}function fS(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+2));return d?(a.S=+d[0],c+=d[0].length):-1}function fT(a,b,c){fU.lastIndex=0;var d=fU.exec(b.substring(c,c+3));return d?(a.L=+d[0],c+=d[0].length):-1}function fV(a,b,c){var d=fW.get(b.substring(c,c+=2).toLowerCase());return d==null?-1:(a.p=d,c)}function fX(a){var b=a.getTimezoneOffset(),c=b>0?"-":"+",d=~~(Math.abs(b)/60),e=Math.abs(b)%60;return c+fr(d)+fr(e)}function fZ(a){return a.toISOString()}function f$(a,b,c){function d(b){var c=a(b),d=f(c,1);return b-c1)while(gb?1:a>=b?0:NaN},d3.descending=function(a,b){return ba?1:b>=a?0:NaN},d3.mean=function(a,b){var c=a.length,d,e=0,f=-1,g=0;if(arguments.length===1)while(++f1&&(a=a.map(b)),a=a.filter(s),a.length?d3.quantile(a.sort(d3.ascending),.5):undefined},d3.min=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++cf&&(e=f)}else{while(++cf&&(e=f)}return e},d3.max=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++ce&&(e=f)}else{while(++ce&&(e=f)}return e},d3.extent=function(a,b){var c=-1,d=a.length,e,f,g;if(arguments.length===1){while(++cf&&(e=f),gf&&(e=f),g1);return a+b*c*Math.sqrt(-2*Math.log(e)/e)}}},d3.sum=function(a,b){var c=0,d=a.length,e,f=-1;if(arguments.length===1)while(++f>1;a.call(b,b[f],f)>1;c0&&(e=f);return e},d3.last=function(a,b){var c=0,d=a.length,e=a[0],f;arguments.length===1&&(b=d3.ascending);while(++c=b.length)return e?e.call(a,c):d?c.sort(d):c;var h=-1,i=c.length,j=b[g++],l,m,n=new k,o,p={};while(++h=b.length)return a;var e=[],f=c[d++],h;for(h in a)e.push({key:h,values:g(a[h],d)});return f&&e.sort(function(a,b){return f(a.key,b.key)}),e}var a={},b=[],c=[],d,e;return a.map=function(a){return f(a,0)},a.entries=function(a){return g(f(a,0),0)},a.key=function(c){return b.push(c),a},a.sortKeys=function(d){return c[b.length-1]=d,a},a.sortValues=function(b){return d=b,a},a.rollup=function(b){return e=b,a},a},d3.keys=function(a){var b=[];for(var c in a)b.push(c);return b},d3.values=function(a){var b=[];for(var c in a)b.push(a[c]);return b},d3.entries=function(a){var b=[];for(var c in a)b.push({key:c,value:a[c]});return b},d3.permute=function(a,b){var c=[],d=-1,e=b.length;while(++db)d.push(g/e);else while((g=a+c*++f)=200&&a<300||a===304?d:null)}},d.send(null)},d3.text=function(a,b,c){function d(a){c(a&&a.responseText)}arguments.length<3&&(c=b,b=null),d3.xhr(a,b,d)},d3.json=function(a,b){d3.text(a,"application/json",function(a){b(a?JSON.parse(a):null)})},d3.html=function(a,b){d3.text(a,"text/html",function(a){if(a!=null){var c=document.createRange();c.selectNode(document.body),a=c.createContextualFragment(a)}b(a)})},d3.xml=function(a,b,c){function d(a){c(a&&a.responseXML)}arguments.length<3&&(c=b,b=null),d3.xhr(a,b,d)};var z={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};d3.ns={prefix:z,qualify:function(a){var b=a.indexOf(":"),c=a;return b>=0&&(c=a.substring(0,b),a=a.substring(b+1)),z.hasOwnProperty(c)?{space:z[c],local:a}:a}},d3.dispatch=function(){var a=new A,b=-1,c=arguments.length;while(++b0&&(d=a.substring(c+1),a=a.substring(0,c)),arguments.length<2?this[a].on(d):this[a].on(d,b)},d3.format=function(a){var b=C.exec(a),c=b[1]||" ",d=b[3]||"",e=b[5],f=+b[6],g=b[7],h=b[8],i=b[9],j=1,k="",l=!1;h&&(h=+h.substring(1)),e&&(c="0",g&&(f-=Math.floor((f-1)/4)));switch(i){case"n":g=!0,i="g";break;case"%":j=100,k="%",i="f";break;case"p":j=100,k="%",i="r";break;case"d":l=!0,h=0;break;case"s":j=-1,i="r"}return i=="r"&&!h&&(i="g"),i=D.get(i)||F,function(a){if(l&&a%1)return"";var b=a<0&&(a=-a)?"−":d;if(j<0){var m=d3.formatPrefix(a,h);a=m.scale(a),k=m.symbol}else a*=j;a=i(a,h);if(e){var n=a.length+b.length;n=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,D=d3.map({g:function(a,b){return a.toPrecision(b)},e:function(a,b){return a.toExponential(b)},f:function(a,b){return a.toFixed(b)},r:function(a,b){return d3.round(a,b=E(a,b)).toFixed(Math.max(0,Math.min(20,b)))}}),H=["y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y"].map(I);d3.formatPrefix=function(a,b){var c=0;return a&&(a<0&&(a*=-1),b&&(a=d3.round(a,E(a,b))),c=1+Math.floor(1e-12+Math.log(a)/Math.LN10),c=Math.max(-24,Math.min(24,Math.floor((c<=0?c+1:c-1)/3)*3))),H[8+c/3]};var J=S(2),K=S(3),L=function(){return R},M=d3.map({linear:L,poly:S,quad:function(){return J},cubic:function(){return K},sin:function(){return T},exp:function(){return U},circle:function(){return V},elastic:W,back:X,bounce:function(){return Y}}),N=d3.map({"in":R,out:P,"in-out":Q,"out-in":function(a){return Q(P(a))}});d3.ease=function(a){var b=a.indexOf("-"),c=b>=0?a.substring(0,b):a,d=b>=0?a.substring(b+1):"in";return c=M.get(c)||L,d=N.get(d)||R,O(d(c.apply(null,Array.prototype.slice.call(arguments,1))))},d3.event=null,d3.interpolate=function(a,b){var c=d3.interpolators.length,d;while(--c>=0&&!(d=d3.interpolators[c](a,b)));return d},d3.interpolateNumber=function(a,b){return b-=a,function(c){return a+b*c}},d3.interpolateRound=function(a,b){return b-=a,function(c){return Math.round(a+b*c)}},d3.interpolateString=function(a,b){var c,d,e,f=0,g=0,h=[],i=[],j,k;ba.lastIndex=0;for(d=0;c=ba.exec(b);++d)c.index&&h.push(b.substring(f,g=c.index)),i.push({i:h.length,x:c[0]}),h.push(null),f=ba.lastIndex;f1){while(++e=0;)if(f=c[d])e&&e!==f.nextSibling&&e.parentNode.insertBefore(f,e),e=f;return this},bu.sort=function(a){a=bB.apply(this,arguments);for(var b=-1,c=this.length;++b0&&(a=a.substring(0,e)),arguments.length<2?(e=this.node()[d])&&e._:this.each(function(e,f){function i(a){var c=d3.event;d3.event=a;try{b.call(g,g.__data__,f)}finally{d3.event=c}}var g=this,h=g[d];h&&(g.removeEventListener(a,h,h.$),delete g[d]),b&&(g.addEventListener(a,g[d]=i,i.$=c),i._=b)})},bu.each=function(a){for(var b=-1,c=this.length;++b=cG?e?"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"M0,"+e+"A"+e+","+e+" 0 1,0 0,"+ -e+"A"+e+","+e+" 0 1,0 0,"+e+"Z":"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"Z":e?"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L"+e*m+","+e*n+"A"+e+","+e+" 0 "+j+",0 "+e*k+","+e*l+"Z":"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L0,0"+"Z"}var a=cH,b=cI,c=cJ,d=cK;return e.innerRadius=function(b){return arguments.length?(a=q(b),e):a},e.outerRadius=function(a){return arguments.length?(b=q(a),e):b},e.startAngle=function(a){return arguments.length?(c=q(a),e):c},e.endAngle=function(a){return arguments.length?(d=q(a),e):d},e.centroid=function(){var e=(a.apply(this,arguments)+b.apply(this,arguments))/2,f=(c.apply(this,arguments)+d.apply(this,arguments))/2+cF;return[Math.cos(f)*e,Math.sin(f)*e]},e};var cF=-Math.PI/2,cG=2*Math.PI-1e-6;d3.svg.line=function(){return cL(n)};var cO="linear",cP=d3.map({linear:cQ,"step-before":cR,"step-after":cS,basis:cY,"basis-open":cZ,"basis-closed":c$,bundle:c_,cardinal:cV,"cardinal-open":cT,"cardinal-closed":cU,monotone:di}),db=[0,2/3,1/3,0],dc=[0,1/3,2/3,0],dd=[0,1/6,2/3,1/6];d3.svg.line.radial=function(){var a=cL(dj);return a.radius=a.x,delete a.x,a.angle=a.y,delete a.y,a},cR.reverse=cS,cS.reverse=cR,d3.svg.area=function(){return dk(Object)},d3.svg.area.radial=function(){var a=dk(dj);return a.radius=a.x,delete a.x,a.innerRadius=a.x0,delete a.x0,a.outerRadius=a.x1,delete a.x1,a.angle=a.y,delete a.y,a.startAngle=a.y0,delete a.y0,a.endAngle=a.y1,delete a.y1,a},d3.svg.chord=function(){function f(c,d){var e=g(this,a,c,d),f=g(this,b,c,d);return"M"+e.p0+i(e.r,e.p1,e.a1-e.a0)+(h(e,f)?j(e.r,e.p1,e.r,e.p0):j(e.r,e.p1,f.r,f.p0)+i(f.r,f.p1,f.a1-f.a0)+j(f.r,f.p1,e.r,e.p0))+"Z"}function g(a,b,f,g){var h=b.call(a,f,g),i=c.call(a,h,g),j=d.call(a,h,g)+cF,k=e.call(a,h,g)+cF;return{r:i,a0:j,a1:k,p0:[i*Math.cos(j),i*Math.sin(j)],p1:[i*Math.cos(k),i*Math.sin(k)]}}function h(a,b){return a.a0==b.a0&&a.a1==b.a1}function i(a,b,c){return"A"+a+","+a+" 0 "+ +(c>Math.PI)+",1 "+b}function j(a,b,c,d){return"Q 0,0 "+d}var a=dl,b=dm,c=dn,d=cJ,e=cK;return f.radius=function(a){return arguments.length?(c=q(a),f):c},f.source=function(b){return arguments.length?(a=q(b),f):a},f.target=function(a){return arguments.length?(b=q(a),f):b},f.startAngle=function(a){return arguments.length?(d=q(a),f):d},f.endAngle=function(a){return arguments.length?(e=q(a),f):e},f},d3.svg.diagonal=function(){function d(d,e){var f=a.call(this,d,e),g=b.call(this,d,e),h=(f.y+g.y)/2,i=[f,{x:f.x,y:h},{x:g.x,y:h},g];return i=i.map(c),"M"+i[0]+"C"+i[1]+" "+i[2]+" "+i[3]}var a=dl,b=dm,c=dr;return d.source=function(b){return arguments.length?(a=q(b),d):a},d.target=function(a){return arguments.length?(b=q(a),d):b},d.projection=function(a){return arguments.length?(c=a,d):c},d},d3.svg.diagonal.radial=function(){var a=d3.svg.diagonal(),b=dr,c=a.projection;return a.projection=function(a){return arguments.length?c(ds(b=a)):b},a},d3.svg.mouse=d3.mouse,d3.svg.touches=d3.touches,d3.svg.symbol=function(){function c(c,d){return(dw 3 | .get(a.call(this,c,d))||dv)(b.call(this,c,d))}var a=du,b=dt;return c.type=function(b){return arguments.length?(a=q(b),c):a},c.size=function(a){return arguments.length?(b=q(a),c):b},c};var dw=d3.map({circle:dv,cross:function(a){var b=Math.sqrt(a/5)/2;return"M"+ -3*b+","+ -b+"H"+ -b+"V"+ -3*b+"H"+b+"V"+ -b+"H"+3*b+"V"+b+"H"+b+"V"+3*b+"H"+ -b+"V"+b+"H"+ -3*b+"Z"},diamond:function(a){var b=Math.sqrt(a/(2*dy)),c=b*dy;return"M0,"+ -b+"L"+c+",0"+" 0,"+b+" "+ -c+",0"+"Z"},square:function(a){var b=Math.sqrt(a)/2;return"M"+ -b+","+ -b+"L"+b+","+ -b+" "+b+","+b+" "+ -b+","+b+"Z"},"triangle-down":function(a){var b=Math.sqrt(a/dx),c=b*dx/2;return"M0,"+c+"L"+b+","+ -c+" "+ -b+","+ -c+"Z"},"triangle-up":function(a){var b=Math.sqrt(a/dx),c=b*dx/2;return"M0,"+ -c+"L"+b+","+c+" "+ -b+","+c+"Z"}});d3.svg.symbolTypes=dw.keys();var dx=Math.sqrt(3),dy=Math.tan(30*Math.PI/180);d3.svg.axis=function(){function k(k){k.each(function(){var k=d3.select(this),l=h==null?a.ticks?a.ticks.apply(a,g):a.domain():h,m=i==null?a.tickFormat?a.tickFormat.apply(a,g):String:i,n=dB(a,l,j),o=k.selectAll(".minor").data(n,String),p=o.enter().insert("line","g").attr("class","tick minor").style("opacity",1e-6),q=d3.transition(o.exit()).style("opacity",1e-6).remove(),r=d3.transition(o).style("opacity",1),s=k.selectAll("g").data(l,String),t=s.enter().insert("g","path").style("opacity",1e-6),u=d3.transition(s.exit()).style("opacity",1e-6).remove(),v=d3.transition(s).style("opacity",1),w,x=cg(a),y=k.selectAll(".domain").data([0]),z=y.enter().append("path").attr("class","domain"),A=d3.transition(y),B=a.copy(),C=this.__chart__||B;this.__chart__=B,t.append("line").attr("class","tick"),t.append("text"),v.select("text").text(m);switch(b){case"bottom":w=dz,p.attr("y2",d),r.attr("x2",0).attr("y2",d),t.select("line").attr("y2",c),t.select("text").attr("y",Math.max(c,0)+f),v.select("line").attr("x2",0).attr("y2",c),v.select("text").attr("x",0).attr("y",Math.max(c,0)+f).attr("dy",".71em").attr("text-anchor","middle"),A.attr("d","M"+x[0]+","+e+"V0H"+x[1]+"V"+e);break;case"top":w=dz,p.attr("y2",-d),r.attr("x2",0).attr("y2",-d),t.select("line").attr("y2",-c),t.select("text").attr("y",-(Math.max(c,0)+f)),v.select("line").attr("x2",0).attr("y2",-c),v.select("text").attr("x",0).attr("y",-(Math.max(c,0)+f)).attr("dy","0em").attr("text-anchor","middle"),A.attr("d","M"+x[0]+","+ -e+"V0H"+x[1]+"V"+ -e);break;case"left":w=dA,p.attr("x2",-d),r.attr("x2",-d).attr("y2",0),t.select("line").attr("x2",-c),t.select("text").attr("x",-(Math.max(c,0)+f)),v.select("line").attr("x2",-c).attr("y2",0),v.select("text").attr("x",-(Math.max(c,0)+f)).attr("y",0).attr("dy",".32em").attr("text-anchor","end"),A.attr("d","M"+ -e+","+x[0]+"H0V"+x[1]+"H"+ -e);break;case"right":w=dA,p.attr("x2",d),r.attr("x2",d).attr("y2",0),t.select("line").attr("x2",c),t.select("text").attr("x",Math.max(c,0)+f),v.select("line").attr("x2",c).attr("y2",0),v.select("text").attr("x",Math.max(c,0)+f).attr("y",0).attr("dy",".32em").attr("text-anchor","start"),A.attr("d","M"+e+","+x[0]+"H0V"+x[1]+"H"+e)}if(a.ticks)t.call(w,C),v.call(w,B),u.call(w,B),p.call(w,C),r.call(w,B),q.call(w,B);else{var D=B.rangeBand()/2,E=function(a){return B(a)+D};t.call(w,E),v.call(w,E)}})}var a=d3.scale.linear(),b="bottom",c=6,d=6,e=6,f=3,g=[10],h=null,i,j=0;return k.scale=function(b){return arguments.length?(a=b,k):a},k.orient=function(a){return arguments.length?(b=a,k):b},k.ticks=function(){return arguments.length?(g=arguments,k):g},k.tickValues=function(a){return arguments.length?(h=a,k):h},k.tickFormat=function(a){return arguments.length?(i=a,k):i},k.tickSize=function(a,b,f){if(!arguments.length)return c;var g=arguments.length-1;return c=+a,d=g>1?+b:c,e=g>0?+arguments[g]:c,k},k.tickPadding=function(a){return arguments.length?(f=+a,k):f},k.tickSubdivide=function(a){return arguments.length?(j=+a,k):j},k},d3.svg.brush=function(){function g(a){a.each(function(){var a=d3.select(this),e=a.selectAll(".background").data([0]),f=a.selectAll(".extent").data([0]),l=a.selectAll(".resize").data(d,String),m;a.style("pointer-events","all").on("mousedown.brush",k).on("touchstart.brush",k),e.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),f.enter().append("rect").attr("class","extent").style("cursor","move"),l.enter().append("g").attr("class",function(a){return"resize "+a}).style("cursor",function(a){return dC[a]}).append("rect").attr("x",function(a){return/[ew]$/.test(a)?-3:null}).attr("y",function(a){return/^[ns]/.test(a)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),l.style("display",g.empty()?"none":null),l.exit().remove(),b&&(m=cg(b),e.attr("x",m[0]).attr("width",m[1]-m[0]),i(a)),c&&(m=cg(c),e.attr("y",m[0]).attr("height",m[1]-m[0]),j(a)),h(a)})}function h(a){a.selectAll(".resize").attr("transform",function(a){return"translate("+e[+/e$/.test(a)][0]+","+e[+/^s/.test(a)][1]+")"})}function i(a){a.select(".extent").attr("x",e[0][0]),a.selectAll(".extent,.n>rect,.s>rect").attr("width",e[1][0]-e[0][0])}function j(a){a.select(".extent").attr("y",e[0][1]),a.selectAll(".extent,.e>rect,.w>rect").attr("height",e[1][1]-e[0][1])}function k(){function x(){var a=d3.event.changedTouches;return a?d3.touches(d,a)[0]:d3.mouse(d)}function y(){d3.event.keyCode==32&&(q||(r=null,s[0]-=e[1][0],s[1]-=e[1][1],q=2),Z())}function z(){d3.event.keyCode==32&&q==2&&(s[0]+=e[1][0],s[1]+=e[1][1],q=0,Z())}function A(){var a=x(),d=!1;t&&(a[0]+=t[0],a[1]+=t[1]),q||(d3.event.altKey?(r||(r=[(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2]),s[0]=e[+(a[0]0?e=c:e=0:c>0&&(b.start({type:"start",alpha:e=c}),d3.timer(a.tick)),a):e},a.start=function(){function q(a,c){var d=t(b),e=-1,f=d.length,g;while(++ee&&(e=h),d.push(h)}for(g=0;g0){f=-1;while(++f=i[0]&&o<=i[1]&&(k=g[d3.bisect(j,o,1,m)-1],k.y+=n,k.push(e[f]))}return g}var a=!0,b=Number,c=ee,d=ec;return e.value=function(a){return arguments.length?(b=a,e):b},e.range=function(a){return arguments.length?(c=q(a),e):c},e.bins=function(a){return arguments.length?(d=typeof a=="number"?function(b){return ed(b,a)}:q(a),e):d},e.frequency=function(b){return arguments.length?(a=!!b,e):a},e},d3.layout.hierarchy=function(){function e(f,h,i){var j=b.call(g,f,h),k=ek?f:{data:f};k.depth=h,i.push(k);if(j&&(m=j.length)){var l=-1,m,n=k.children=[],o=0,p=h+1;while(++l0&&(eI(eJ(g,a,d),a,m),i+=m,j+=m),k+=g._tree.mod,i+=e._tree.mod,l+=h._tree.mod,j+=f._tree.mod;g&&!eB(f)&&(f._tree.thread=g,f._tree.mod+=k-j),e&&!eA(h)&&(h._tree.thread=e,h._tree.mod+=i-l,d=a)}return d}var f=a.call(this,d,e),g=f[0];eG(g,function(a,b){a._tree={ancestor:a,prelim:0,mod:0,change:0,shift:0,number:b?b._tree.number+1:0}}),h(g),i(g,-g._tree.prelim);var k=eC(g,eE),l=eC(g,eD),m=eC(g,eF),n=k.x-b(k,l)/2,o=l.x+b(l,k)/2,p=m.depth||1;return eG(g,function(a){a.x=(a.x-n)/(o-n)*c[0],a.y=a.depth/p*c[1],delete a._tree}),f}var a=d3.layout.hierarchy().sort(null).value(null),b=ez,c=[1,1];return d.separation=function(a){return arguments.length?(b=a,d):b},d.size=function(a){return arguments.length?(c=a,d):c},ef(d,a)},d3.layout.treemap=function(){function i(a,b){var c=-1,d=a.length,e,f;while(++c0)d.push(g=f[o-1]),d.area+=g.area,(k=l(d,n))<=h?(f.pop(),h=k):(d.area-=d.pop().area,m(d,n,c,!1),n=Math.min(c.dx,c.dy),d.length=d.area=0,h=Infinity);d.length&&(m(d,n,c,!0),d.length=d.area=0),b.forEach(j)}}function k(a){var b=a.children;if(b&&b.length){var c=e(a),d=b.slice(),f,g=[];i(d,c.dx*c.dy/a.value),g.area=0;while(f=d.pop())g.push(f),g.area+=f.area,f.z!=null&&(m(g,f.z?c.dx:c.dy,c,!d.length),g.length=g.area=0);b.forEach(k)}}function l(a,b){var c=a.area,d,e=0,f=Infinity,g=-1,i=a.length;while(++ge&&(e=d)}return c*=c,b*=b,c?Math.max(b*e*h/c,c/(b*f*h)):Infinity}function m(a,c,d,e){var f=-1,g=a.length,h=d.x,i=d.y,j=c?b(a.area/c):0,k;if(c==d.dx){if(e||j>d.dy)j=d.dy;while(++fd.dx)j=d.dx;while(++f=a.length)return d;if(i)return i=!1,c;var b=f.lastIndex;if(a.charCodeAt(b)===34){var e=b;while(e++50?b:f<-140?c:g<21?d:a)(e)}var a=d3.geo.albers(),b=d3.geo.albers().origin([-160,60]).parallels([55,65]),c=d3.geo.albers().origin([-160,20]).parallels([8,18]),d=d3.geo.albers().origin([-60,10]).parallels([8,18]);return e.scale=function(f){return arguments.length?(a.scale(f),b.scale(f*.6),c.scale(f),d.scale(f*1.5),e.translate(a.translate())):a.scale()},e.translate=function(f){if(!arguments.length)return a.translate();var g=a.scale()/1e3,h=f[0],i=f[1];return a.translate(f),b.translate([h-400*g,i+170*g]),c.translate([h-190*g,i+200*g]),d.translate([h+580*g,i+430*g]),e},e.scale(a.scale())},d3.geo.bonne=function(){function g(g){var h=g[0]*eO-c,i=g[1]*eO-d;if(e){var j=f+e-i,k=h*Math.cos(i)/j;h=j*Math.sin(k),i=j*Math.cos(k)-f}else h*=Math.cos(i),i*=-1;return[a*h+b[0],a*i+b[1]]}var a=200,b=[480,250],c,d,e,f;return g.invert=function(d){var g=(d[0]-b[0])/a,h=(d[1]-b[1])/a;if(e){var i=f+h,j=Math.sqrt(g*g+i*i);h=f+e-j,g=c+j*Math.atan2(g,i)/Math.cos(h)}else h*=-1,g/=Math.cos(h);return[g/eO,h/eO]},g.parallel=function(a){return arguments.length?(f=1/Math.tan(e=a*eO),g):e/eO},g.origin=function(a){return arguments.length?(c=a[0]*eO,d=a[1]*eO,g):[c/eO,d/eO]},g.scale=function(b){return arguments.length?(a=+b,g):a},g.translate=function(a){return arguments.length?(b=[+a[0],+a[1]],g):b},g.origin([0,0]).parallel(45)},d3.geo.equirectangular=function(){function c(c){var d=c[0]/360,e=-c[1]/360;return[a*d+b[0],a*e+b[1]]}var a=500,b=[480,250];return c.invert=function(c){var d=(c[0]-b[0])/a,e=(c[1]-b[1])/a;return[360*d,-360*e]},c.scale=function(b){return arguments.length?(a=+b,c):a},c.translate=function(a){return arguments.length?(b=[+a[0],+a[1]],c):b},c},d3.geo.mercator=function(){function c(c){var d=c[0]/360,e=-(Math.log(Math.tan(Math.PI/4+c[1]*eO/2))/eO)/360;return[a*d+b[0],a*Math.max(-0.5,Math.min(.5,e))+b[1]]}var a=500,b=[480,250];return c.invert=function(c){var d=(c[0]-b[0])/a,e=(c[1]-b[1])/a;return[360*d,2*Math.atan(Math.exp(-360*e*eO))/eO-90]},c.scale=function(b){return arguments.length?(a=+b,c):a},c.translate=function(a){return arguments.length?(b=[+a[0],+a[1]],c):b},c},d3.geo.path=function(){function d(c,d){return typeof a=="function"&&(b=eQ(a.apply(this,arguments))),f(c)||null}function e(a){return c(a).join(",")}function h(a){var b=k(a[0]),c=0,d=a.length;while(++c0){b.push("M");while(++h0){b.push("M");while(++kd&&(d=a),fe&&(e=f)}),[[b,c],[d,e]]};var eS={Feature:eT,FeatureCollection:eU,GeometryCollection:eV,LineString:eW,MultiLineString:eX,MultiPoint:eW,MultiPolygon:eY,Point:eZ,Polygon:e$};d3.geo.circle=function(){function e(){}function f(a){return d.distance(a)=k*k+l*l?d[f].index=-1:(d[m].index=-1,o=d[f].angle,m=f,n=g)):(o=d[f].angle,m=f,n=g);e.push(h);for(f=0,g=0;f<2;++g)d[g].index!==-1&&(e.push(d[g].index),f++);p=e.length;for(;g=0?(c=a.ep.r,d=a.ep.l):(c=a.ep.l,d=a.ep.r),a.a===1?(g=c?c.y:-1e6,e=a.c-a.b*g,h=d?d.y:1e6,f=a.c-a.b*h):(e=c?c.x:-1e6,g=a.c-a.a*e,f=d?d.x:1e6,h=a.c-a.a*f);var i=[e,g],j=[f,h];b[a.region.l.index].push(i,j),b[a.region.r.index].push(i,j)}),b.map(function(b,c){var d=a[c][0],e=a[c][1];return b.forEach(function(a){a.angle=Math.atan2(a[0]-d,a[1]-e)}),b.sort(function(a,b){return a.angle-b.angle}).filter(function(a,c){return!c||a.angle-b[c-1].angle>1e-10})})};var fi={l:"r",r:"l"};d3.geom.delaunay=function(a){var b=a.map(function(){return[]}),c=[];return fj(a,function(c){b[c.region.l.index].push(a[c.region.r.index])}),b.forEach(function(b,d){var e=a[d],f=e[0],g=e[1];b.forEach(function(a){a.angle=Math.atan2(a[0]-f,a[1]-g)}),b.sort(function(a,b){return a.angle-b.angle});for(var h=0,i=b.length-1;h=g,j=b.y>=h,l=(j<<1)+i;a.leaf=!1,a=a.nodes[l]||(a.nodes[l]=fk()),i?c=g:e=g,j?d=h:f=h,k(a,b,c,d,e,f)}var f,g=-1,h=a.length;h&&isNaN(a[0].x)&&(a=a.map(fm));if(arguments.length<5)if(arguments.length===3)e=d=c,c=b;else{b=c=Infinity,d=e=-Infinity;while(++gd&&(d=f.x),f.y>e&&(e=f.y);var i=d-b,j=e-c;i>j?e=c+i:d=b+j}var m=fk();return m.add=function(a){k(m,a,b,c,d,e)},m.visit=function(a){fl(a,m,b,c,d,e)},a.forEach(m.add),m},d3.time={};var fn=Date;fo.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){fp.setUTCDate.apply(this._,arguments)},setDay:function(){fp.setUTCDay.apply(this._,arguments)},setFullYear:function(){fp.setUTCFullYear.apply(this._,arguments)},setHours:function(){fp.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){fp.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){fp.setUTCMinutes.apply(this._,arguments)},setMonth:function(){fp.setUTCMonth.apply(this._,arguments)},setSeconds:function(){fp.setUTCSeconds.apply(this._,arguments)},setTime:function(){fp.setTime.apply(this._,arguments)}};var fp=Date.prototype;d3.time.format=function(a){function c(c){var d=[],e=-1,f=0,g,h;while(++e=12?"PM":"AM"},S:function(a){return fr(a.getSeconds())},U:function(a){return fr(d3.time.sundayOfYear(a))},w:function(a){return a.getDay()},W:function(a){return fr(d3.time.mondayOfYear(a))},x:d3.time.format("%m/%d/%y"),X:d3.time.format("%H:%M:%S"),y:function(a){return fr(a.getFullYear()%100)},Y:function(a){return ft(a.getFullYear()%1e4)},Z:fX,"%":function(a){return"%"}},fw={a:fx,A:fy,b:fC,B:fE,c:fI,d:fP,e:fP,H:fQ,I:fQ,L:fT,m:fO,M:fR,p:fV,S:fS,x:fJ,X:fK,y:fM,Y:fL},fz=/^(?:sun|mon|tue|wed|thu|fri|sat)/i,fA=/^(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)/i,fB=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],fD=d3.map({jan:0,feb:1,mar:2,apr:3,may:4,jun:5,jul:6,aug:7,sep:8,oct:9,nov:10,dec:11}),fF=/^(?:January|February|March|April|May|June|July|August|September|October|November|December)/ig,fG=d3.map({january:0,february:1,march:2,april:3,may:4,june:5,july:6,august:7,september:8,october:9,november:10,december:11}),fH=["January","February","March","April","May","June","July","August","September","October","November","December"],fU=/\s*\d+/,fW=d3.map({am:0,pm:1});d3.time.format.utc=function(a){function c(a){try{fn=fo;var c=new fn;return c._=a,b(c)}finally{fn=Date}}var b=d3.time.format(a);return c.parse=function(a){try{fn=fo;var c=b.parse(a);return c&&c._}finally{fn=Date}},c.toString=b.toString,c};var fY=d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");d3.time.format.iso=Date.prototype.toISOString?fZ:fY,fZ.parse=function(a){return new Date(a)},fZ.toString=fY.toString,d3.time.second=f$(function(a){return new fn(Math.floor(a/1e3)*1e3)},function(a,b){a.setTime(a.getTime()+Math.floor(b)*1e3)},function(a){return a.getSeconds()}),d3.time.seconds=d3.time.second.range,d3.time.seconds.utc=d3.time.second.utc.range,d3.time.minute=f$(function(a){return new fn(Math.floor(a/6e4)*6e4)},function(a,b){a.setTime(a.getTime()+Math.floor(b)*6e4)},function(a){return a.getMinutes()}),d3.time.minutes=d3.time.minute.range,d3.time.minutes.utc=d3.time.minute.utc.range,d3.time.hour=f$(function(a){var b=a.getTimezoneOffset()/60;return new fn((Math.floor(a/36e5-b)+b)*36e5)},function(a,b){a.setTime(a.getTime()+Math.floor(b)*36e5)},function(a){return a.getHours()}),d3.time.hours=d3.time.hour.range,d3.time.hours.utc=d3.time.hour.utc.range,d3.time.day=f$(function(a){return new fn(a.getFullYear(),a.getMonth(),a.getDate())},function(a,b){a.setDate(a.getDate()+b)},function(a){return a.getDate()-1}),d3.time.days=d3.time.day.range,d3.time.days.utc=d3.time.day.utc.range,d3.time.dayOfYear=function(a){var b=d3.time.year(a);return Math.floor((a-b)/864e5-(a.getTimezoneOffset()-b.getTimezoneOffset())/1440)},fB.forEach(function(a,b){a=a.toLowerCase(),b=7-b;var c=d3.time[a]=f$(function(a){return(a=d3.time.day(a)).setDate(a.getDate()-(a.getDay()+b)%7),a},function(a,b){a.setDate(a.getDate()+Math.floor(b)*7)},function(a){var c=d3.time.year(a).getDay();return Math.floor((d3.time.dayOfYear(a)+(c+b)%7)/7)-(c!==b)});d3.time[a+"s"]=c.range,d3.time[a+"s"].utc=c.utc.range,d3.time[a+"OfYear"]=function(a){var c=d3.time.year(a).getDay();return Math.floor((d3.time.dayOfYear(a)+(c+b)%7)/7)}}),d3.time.week=d3.time.sunday,d3.time.weeks=d3.time.sunday.range,d3.time.weeks.utc=d3.time.sunday.utc.range,d3.time.weekOfYear=d3.time.sundayOfYear,d3.time.month=f$(function(a){return new fn(a.getFullYear(),a.getMonth(),1)},function(a,b){a.setMonth(a.getMonth()+b)},function(a){return a.getMonth()}),d3.time.months=d3.time.month.range,d3.time.months.utc=d3.time.month.utc.range,d3.time.year=f$(function(a){return new fn(a.getFullYear(),0,1)},function(a,b){a.setFullYear(a.getFullYear()+b)},function(a){return a.getFullYear()}),d3.time.years=d3.time.year.range,d3.time.years.utc=d3.time.year.utc.range;var gg=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],gh=[[d3.time.second,1],[d3.time.second,5],[d3.time.second,15],[d3.time.second,30],[d3.time.minute,1],[d3.time.minute,5],[d3.time.minute,15],[d3.time.minute,30],[d3.time.hour,1],[d3.time.hour,3],[d3.time.hour,6],[d3.time.hour,12],[d3.time.day,1],[d3.time.day,2],[d3.time.week,1],[d3.time.month,1],[d3.time.month,3],[d3.time.year,1]],gi=[[d3.time.format("%Y"),function(a){return!0}],[d3.time.format("%B"),function(a){return a.getMonth()}],[d3.time.format("%b %d"),function(a){return a.getDate()!=1}],[d3.time.format("%a %d"),function(a){return a.getDay()&&a.getDate()!=1}],[d3.time.format("%I %p"),function(a){return a.getHours()}],[d3.time.format("%I:%M"),function(a){return a.getMinutes()}],[d3.time.format(":%S"),function(a){return a.getSeconds()}],[d3.time.format(".%L"),function(a){return a.getMilliseconds()}]],gj=d3.scale.linear(),gk=gd(gi);gh.year=function(a,b){return gj.domain(a.map(gf)).ticks(b).map(ge)},d3.time.scale=function(){return ga(d3.scale.linear(),gh,gk)};var gl=gh.map(function(a){return[a[0].utc,a[1]]}),gm=[[d3.time.format.utc("%Y"),function(a){return!0}],[d3.time.format.utc("%B"),function(a){return a.getUTCMonth()}],[d3.time.format.utc("%b %d"),function(a){return a.getUTCDate()!=1}],[d3.time.format.utc("%a %d"),function(a){return a.getUTCDay()&&a.getUTCDate()!=1}],[d3.time.format.utc("%I %p"),function(a){return a.getUTCHours()}],[d3.time.format.utc("%I:%M"),function(a){return a.getUTCMinutes()}],[d3.time.format.utc(":%S"),function(a){return a.getUTCSeconds()}],[d3.time.format.utc(".%L"),function(a){return a.getUTCMilliseconds()}]],gn=gd(gm);gl.year=function(a,b){return gj.domain(a.map(gp)).ticks(b).map(go)},d3.time.scale.utc=function(){return ga(d3.scale.linear(),gl,gn)}})(); --------------------------------------------------------------------------------