@datarailsshared/dr_renderer 1.2.42-beta → 1.2.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/pivottable.js CHANGED
@@ -1,1824 +1,1824 @@
1
- // from pivottable@2.23.0
2
- let initPivotTable = function($, window, document) {
3
- var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
4
- slice = [].slice,
5
- bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
6
- hasProp = {}.hasOwnProperty;
7
- /*
8
- Utilities
9
- */
10
- var PivotData, addSeparators, aggregatorTemplates, aggregators, dayNamesEn, derivers, getSort, locales, mthNamesEn, naturalSort, numberFormat, pivotTableRenderer, rd, renderers, rx, rz, sortAs, usFmt, usFmtInt, usFmtPct, zeroPad;
11
- addSeparators = function(nStr, thousandsSep, decimalSep) {
12
- var rgx, x, x1, x2;
13
- nStr += '';
14
- x = nStr.split('.');
15
- x1 = x[0];
16
- x2 = x.length > 1 ? decimalSep + x[1] : '';
17
- rgx = /(\d+)(\d{3})/;
18
- while (rgx.test(x1)) {
19
- x1 = x1.replace(rgx, '$1' + thousandsSep + '$2');
20
- }
21
- return x1 + x2;
22
- };
23
- numberFormat = function(opts) {
24
- var defaults;
25
- defaults = {
26
- digitsAfterDecimal: 2,
27
- scaler: 1,
28
- thousandsSep: ",",
29
- decimalSep: ".",
30
- prefix: "",
31
- suffix: ""
32
- };
33
- opts = $.extend({}, defaults, opts);
34
- return function(x) {
35
- var result;
36
- if (isNaN(x) || !isFinite(x)) {
37
- return "";
38
- }
39
- result = addSeparators((opts.scaler * x).toFixed(opts.digitsAfterDecimal), opts.thousandsSep, opts.decimalSep);
40
- return "" + opts.prefix + result + opts.suffix;
41
- };
42
- };
43
- usFmt = numberFormat();
44
- usFmtInt = numberFormat({
45
- digitsAfterDecimal: 0
46
- });
47
- usFmtPct = numberFormat({
48
- digitsAfterDecimal: 1,
49
- scaler: 100,
50
- suffix: "%"
51
- });
52
- aggregatorTemplates = {
53
- count: function(formatter) {
54
- if (formatter == null) {
55
- formatter = usFmtInt;
56
- }
57
- return function() {
58
- return function(data, rowKey, colKey) {
59
- return {
60
- count: 0,
61
- push: function() {
62
- return this.count++;
63
- },
64
- value: function() {
65
- return this.count;
66
- },
67
- format: formatter
68
- };
69
- };
70
- };
71
- },
72
- uniques: function(fn, formatter) {
73
- if (formatter == null) {
74
- formatter = usFmtInt;
75
- }
76
- return function(arg) {
77
- var attr;
78
- attr = arg[0];
79
- return function(data, rowKey, colKey) {
80
- return {
81
- uniq: [],
82
- push: function(record) {
83
- var ref;
84
- if (ref = record[attr], indexOf.call(this.uniq, ref) < 0) {
85
- return this.uniq.push(record[attr]);
86
- }
87
- },
88
- value: function() {
89
- return fn(this.uniq);
90
- },
91
- format: formatter,
92
- numInputs: attr != null ? 0 : 1
93
- };
94
- };
95
- };
96
- },
97
- sum: function(formatter) {
98
- if (formatter == null) {
99
- formatter = usFmt;
100
- }
101
- return function(arg) {
102
- var attr;
103
- attr = arg[0];
104
- return function(data, rowKey, colKey) {
105
- return {
106
- sum: 0,
107
- push: function(record) {
108
- if (!isNaN(parseFloat(record[attr]))) {
109
- return this.sum += parseFloat(record[attr]);
110
- }
111
- },
112
- value: function() {
113
- return this.sum;
114
- },
115
- format: formatter,
116
- numInputs: attr != null ? 0 : 1
117
- };
118
- };
119
- };
120
- },
121
- extremes: function(mode, formatter) {
122
- if (formatter == null) {
123
- formatter = usFmt;
124
- }
125
- return function(arg) {
126
- var attr;
127
- attr = arg[0];
128
- return function(data, rowKey, colKey) {
129
- return {
130
- val: null,
131
- sorter: getSort(data != null ? data.sorters : void 0, attr),
132
- push: function(record) {
133
- var ref, ref1, ref2, x;
134
- x = record[attr];
135
- if (mode === "min" || mode === "max") {
136
- x = parseFloat(x);
137
- if (!isNaN(x)) {
138
- this.val = Math[mode](x, (ref = this.val) != null ? ref : x);
139
- }
140
- }
141
- if (mode === "first") {
142
- if (this.sorter(x, (ref1 = this.val) != null ? ref1 : x) <= 0) {
143
- this.val = x;
144
- }
145
- }
146
- if (mode === "last") {
147
- if (this.sorter(x, (ref2 = this.val) != null ? ref2 : x) >= 0) {
148
- return this.val = x;
149
- }
150
- }
151
- },
152
- value: function() {
153
- return this.val;
154
- },
155
- format: function(x) {
156
- if (isNaN(x)) {
157
- return x;
158
- } else {
159
- return formatter(x);
160
- }
161
- },
162
- numInputs: attr != null ? 0 : 1
163
- };
164
- };
165
- };
166
- },
167
- quantile: function(q, formatter) {
168
- if (formatter == null) {
169
- formatter = usFmt;
170
- }
171
- return function(arg) {
172
- var attr;
173
- attr = arg[0];
174
- return function(data, rowKey, colKey) {
175
- return {
176
- vals: [],
177
- push: function(record) {
178
- var x;
179
- x = parseFloat(record[attr]);
180
- if (!isNaN(x)) {
181
- return this.vals.push(x);
182
- }
183
- },
184
- value: function() {
185
- var i;
186
- if (this.vals.length === 0) {
187
- return null;
188
- }
189
- this.vals.sort(function(a, b) {
190
- return a - b;
191
- });
192
- i = (this.vals.length - 1) * q;
193
- return (this.vals[Math.floor(i)] + this.vals[Math.ceil(i)]) / 2.0;
194
- },
195
- format: formatter,
196
- numInputs: attr != null ? 0 : 1
197
- };
198
- };
199
- };
200
- },
201
- runningStat: function(mode, ddof, formatter) {
202
- if (mode == null) {
203
- mode = "mean";
204
- }
205
- if (ddof == null) {
206
- ddof = 1;
207
- }
208
- if (formatter == null) {
209
- formatter = usFmt;
210
- }
211
- return function(arg) {
212
- var attr;
213
- attr = arg[0];
214
- return function(data, rowKey, colKey) {
215
- return {
216
- n: 0.0,
217
- m: 0.0,
218
- s: 0.0,
219
- push: function(record) {
220
- var m_new, x;
221
- x = parseFloat(record[attr]);
222
- if (isNaN(x)) {
223
- return;
224
- }
225
- this.n += 1.0;
226
- if (this.n === 1.0) {
227
- return this.m = x;
228
- } else {
229
- m_new = this.m + (x - this.m) / this.n;
230
- this.s = this.s + (x - this.m) * (x - m_new);
231
- return this.m = m_new;
232
- }
233
- },
234
- value: function() {
235
- if (mode === "mean") {
236
- if (this.n === 0) {
237
- return 0 / 0;
238
- } else {
239
- return this.m;
240
- }
241
- }
242
- if (this.n <= ddof) {
243
- return 0;
244
- }
245
- switch (mode) {
246
- case "var":
247
- return this.s / (this.n - ddof);
248
- case "stdev":
249
- return Math.sqrt(this.s / (this.n - ddof));
250
- }
251
- },
252
- format: formatter,
253
- numInputs: attr != null ? 0 : 1
254
- };
255
- };
256
- };
257
- },
258
- sumOverSum: function(formatter) {
259
- if (formatter == null) {
260
- formatter = usFmt;
261
- }
262
- return function(arg) {
263
- var denom, num;
264
- num = arg[0], denom = arg[1];
265
- return function(data, rowKey, colKey) {
266
- return {
267
- sumNum: 0,
268
- sumDenom: 0,
269
- push: function(record) {
270
- if (!isNaN(parseFloat(record[num]))) {
271
- this.sumNum += parseFloat(record[num]);
272
- }
273
- if (!isNaN(parseFloat(record[denom]))) {
274
- return this.sumDenom += parseFloat(record[denom]);
275
- }
276
- },
277
- value: function() {
278
- return this.sumNum / this.sumDenom;
279
- },
280
- format: formatter,
281
- numInputs: (num != null) && (denom != null) ? 0 : 2
282
- };
283
- };
284
- };
285
- },
286
- sumOverSumBound80: function(upper, formatter) {
287
- if (upper == null) {
288
- upper = true;
289
- }
290
- if (formatter == null) {
291
- formatter = usFmt;
292
- }
293
- return function(arg) {
294
- var denom, num;
295
- num = arg[0], denom = arg[1];
296
- return function(data, rowKey, colKey) {
297
- return {
298
- sumNum: 0,
299
- sumDenom: 0,
300
- push: function(record) {
301
- if (!isNaN(parseFloat(record[num]))) {
302
- this.sumNum += parseFloat(record[num]);
303
- }
304
- if (!isNaN(parseFloat(record[denom]))) {
305
- return this.sumDenom += parseFloat(record[denom]);
306
- }
307
- },
308
- value: function() {
309
- var sign;
310
- sign = upper ? 1 : -1;
311
- return (0.821187207574908 / this.sumDenom + this.sumNum / this.sumDenom + 1.2815515655446004 * sign * Math.sqrt(0.410593603787454 / (this.sumDenom * this.sumDenom) + (this.sumNum * (1 - this.sumNum / this.sumDenom)) / (this.sumDenom * this.sumDenom))) / (1 + 1.642374415149816 / this.sumDenom);
312
- },
313
- format: formatter,
314
- numInputs: (num != null) && (denom != null) ? 0 : 2
315
- };
316
- };
317
- };
318
- },
319
- fractionOf: function(wrapped, type, formatter) {
320
- if (type == null) {
321
- type = "total";
322
- }
323
- if (formatter == null) {
324
- formatter = usFmtPct;
325
- }
326
- return function() {
327
- var x;
328
- x = 1 <= arguments.length ? slice.call(arguments, 0) : [];
329
- return function(data, rowKey, colKey) {
330
- return {
331
- selector: {
332
- total: [[], []],
333
- row: [rowKey, []],
334
- col: [[], colKey]
335
- }[type],
336
- inner: wrapped.apply(null, x)(data, rowKey, colKey),
337
- push: function(record) {
338
- return this.inner.push(record);
339
- },
340
- format: formatter,
341
- value: function() {
342
- return this.inner.value() / data.getAggregator.apply(data, this.selector).inner.value();
343
- },
344
- numInputs: wrapped.apply(null, x)().numInputs
345
- };
346
- };
347
- };
348
- }
349
- };
350
- aggregatorTemplates.countUnique = function(f) {
351
- return aggregatorTemplates.uniques((function(x) {
352
- return x.length;
353
- }), f);
354
- };
355
- aggregatorTemplates.listUnique = function(s) {
356
- return aggregatorTemplates.uniques((function(x) {
357
- return x.sort(naturalSort).join(s);
358
- }), (function(x) {
359
- return x;
360
- }));
361
- };
362
- aggregatorTemplates.max = function(f) {
363
- return aggregatorTemplates.extremes('max', f);
364
- };
365
- aggregatorTemplates.min = function(f) {
366
- return aggregatorTemplates.extremes('min', f);
367
- };
368
- aggregatorTemplates.first = function(f) {
369
- return aggregatorTemplates.extremes('first', f);
370
- };
371
- aggregatorTemplates.last = function(f) {
372
- return aggregatorTemplates.extremes('last', f);
373
- };
374
- aggregatorTemplates.median = function(f) {
375
- return aggregatorTemplates.quantile(0.5, f);
376
- };
377
- aggregatorTemplates.average = function(f) {
378
- return aggregatorTemplates.runningStat("mean", 1, f);
379
- };
380
- aggregatorTemplates["var"] = function(ddof, f) {
381
- return aggregatorTemplates.runningStat("var", ddof, f);
382
- };
383
- aggregatorTemplates.stdev = function(ddof, f) {
384
- return aggregatorTemplates.runningStat("stdev", ddof, f);
385
- };
386
- aggregators = (function(tpl) {
387
- return {
388
- "Count": tpl.count(usFmtInt),
389
- "Count Unique Values": tpl.countUnique(usFmtInt),
390
- "List Unique Values": tpl.listUnique(", "),
391
- "Sum": tpl.sum(usFmt),
392
- "Integer Sum": tpl.sum(usFmtInt),
393
- "Average": tpl.average(usFmt),
394
- "Median": tpl.median(usFmt),
395
- "Sample Variance": tpl["var"](1, usFmt),
396
- "Sample Standard Deviation": tpl.stdev(1, usFmt),
397
- "Minimum": tpl.min(usFmt),
398
- "Maximum": tpl.max(usFmt),
399
- "First": tpl.first(usFmt),
400
- "Last": tpl.last(usFmt),
401
- "Sum over Sum": tpl.sumOverSum(usFmt),
402
- "80% Upper Bound": tpl.sumOverSumBound80(true, usFmt),
403
- "80% Lower Bound": tpl.sumOverSumBound80(false, usFmt),
404
- "Sum as Fraction of Total": tpl.fractionOf(tpl.sum(), "total", usFmtPct),
405
- "Sum as Fraction of Rows": tpl.fractionOf(tpl.sum(), "row", usFmtPct),
406
- "Sum as Fraction of Columns": tpl.fractionOf(tpl.sum(), "col", usFmtPct),
407
- "Count as Fraction of Total": tpl.fractionOf(tpl.count(), "total", usFmtPct),
408
- "Count as Fraction of Rows": tpl.fractionOf(tpl.count(), "row", usFmtPct),
409
- "Count as Fraction of Columns": tpl.fractionOf(tpl.count(), "col", usFmtPct)
410
- };
411
- })(aggregatorTemplates);
412
- renderers = {
413
- "Table": function(data, opts) {
414
- return pivotTableRenderer(data, opts);
415
- },
416
- "Table Barchart": function(data, opts) {
417
- return $(pivotTableRenderer(data, opts)).barchart();
418
- },
419
- "Heatmap": function(data, opts) {
420
- return $(pivotTableRenderer(data, opts)).heatmap("heatmap", opts);
421
- },
422
- "Row Heatmap": function(data, opts) {
423
- return $(pivotTableRenderer(data, opts)).heatmap("rowheatmap", opts);
424
- },
425
- "Col Heatmap": function(data, opts) {
426
- return $(pivotTableRenderer(data, opts)).heatmap("colheatmap", opts);
427
- }
428
- };
429
- locales = {
430
- en: {
431
- aggregators: aggregators,
432
- renderers: renderers,
433
- localeStrings: {
434
- renderError: "An error occurred rendering the PivotTable results.",
435
- computeError: "An error occurred computing the PivotTable results.",
436
- uiRenderError: "An error occurred rendering the PivotTable UI.",
437
- selectAll: "Select All",
438
- selectNone: "Select None",
439
- tooMany: "(too many to list)",
440
- filterResults: "Filter values",
441
- apply: "Apply",
442
- cancel: "Cancel",
443
- totals: "Totals",
444
- vs: "vs",
445
- by: "by"
446
- }
447
- }
448
- };
449
- mthNamesEn = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
450
- dayNamesEn = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
451
- zeroPad = function(number) {
452
- return ("0" + number).substr(-2, 2);
453
- };
454
- derivers = {
455
- bin: function(col, binWidth) {
456
- return function(record) {
457
- return record[col] - record[col] % binWidth;
458
- };
459
- },
460
- dateFormat: function(col, formatString, utcOutput, mthNames, dayNames) {
461
- var utc;
462
- if (utcOutput == null) {
463
- utcOutput = false;
464
- }
465
- if (mthNames == null) {
466
- mthNames = mthNamesEn;
467
- }
468
- if (dayNames == null) {
469
- dayNames = dayNamesEn;
470
- }
471
- utc = utcOutput ? "UTC" : "";
472
- return function(record) {
473
- var date;
474
- date = new Date(Date.parse(record[col]));
475
- if (isNaN(date)) {
476
- return "";
477
- }
478
- return formatString.replace(/%(.)/g, function(m, p) {
479
- switch (p) {
480
- case "y":
481
- return date["get" + utc + "FullYear"]();
482
- case "m":
483
- return zeroPad(date["get" + utc + "Month"]() + 1);
484
- case "n":
485
- return mthNames[date["get" + utc + "Month"]()];
486
- case "d":
487
- return zeroPad(date["get" + utc + "Date"]());
488
- case "w":
489
- return dayNames[date["get" + utc + "Day"]()];
490
- case "x":
491
- return date["get" + utc + "Day"]();
492
- case "H":
493
- return zeroPad(date["get" + utc + "Hours"]());
494
- case "M":
495
- return zeroPad(date["get" + utc + "Minutes"]());
496
- case "S":
497
- return zeroPad(date["get" + utc + "Seconds"]());
498
- default:
499
- return "%" + p;
500
- }
501
- });
502
- };
503
- }
504
- };
505
- rx = /(\d+)|(\D+)/g;
506
- rd = /\d/;
507
- rz = /^0/;
508
- naturalSort = (function(_this) {
509
- return function(as, bs) {
510
- var a, a1, b, b1, nas, nbs;
511
- if ((bs != null) && (as == null)) {
512
- return -1;
513
- }
514
- if ((as != null) && (bs == null)) {
515
- return 1;
516
- }
517
- if (typeof as === "number" && isNaN(as)) {
518
- return -1;
519
- }
520
- if (typeof bs === "number" && isNaN(bs)) {
521
- return 1;
522
- }
523
- nas = +as;
524
- nbs = +bs;
525
- if (nas < nbs) {
526
- return -1;
527
- }
528
- if (nas > nbs) {
529
- return 1;
530
- }
531
- if (typeof as === "number" && typeof bs !== "number") {
532
- return -1;
533
- }
534
- if (typeof bs === "number" && typeof as !== "number") {
535
- return 1;
536
- }
537
- if (typeof as === "number" && typeof bs === "number") {
538
- return 0;
539
- }
540
- if (isNaN(nbs) && !isNaN(nas)) {
541
- return -1;
542
- }
543
- if (isNaN(nas) && !isNaN(nbs)) {
544
- return 1;
545
- }
546
- a = String(as).toLowerCase();
547
- b = String(bs).toLowerCase();
548
- if (a === b) {
549
- return 0;
550
- }
551
- if (!(rd.test(a) && rd.test(b))) {
552
- return (a > b ? 1 : -1);
553
- }
554
- a = a.match(rx);
555
- b = b.match(rx);
556
- while (a.length && b.length) {
557
- a1 = a.shift();
558
- b1 = b.shift();
559
- if (a1 !== b1) {
560
- if (rd.test(a1) && rd.test(b1)) {
561
- return a1.replace(rz, ".0") - b1.replace(rz, ".0");
562
- } else {
563
- return (a1 > b1 ? 1 : -1);
564
- }
565
- }
566
- }
567
- return a.length - b.length;
568
- };
569
- })(this);
570
- sortAs = function(order) {
571
- var i, l_mapping, mapping, x;
572
- mapping = {};
573
- l_mapping = {};
574
- for (i in order) {
575
- x = order[i];
576
- mapping[x] = i;
577
- if (typeof x === "string") {
578
- l_mapping[x.toLowerCase()] = i;
579
- }
580
- }
581
- return function(a, b) {
582
- if ((mapping[a] != null) && (mapping[b] != null)) {
583
- return mapping[a] - mapping[b];
584
- } else if (mapping[a] != null) {
585
- return -1;
586
- } else if (mapping[b] != null) {
587
- return 1;
588
- } else if ((l_mapping[a] != null) && (l_mapping[b] != null)) {
589
- return l_mapping[a] - l_mapping[b];
590
- } else if (l_mapping[a] != null) {
591
- return -1;
592
- } else if (l_mapping[b] != null) {
593
- return 1;
594
- } else {
595
- return naturalSort(a, b);
596
- }
597
- };
598
- };
599
- getSort = function(sorters, attr) {
600
- var sort;
601
- if (sorters != null) {
602
- if ($.isFunction(sorters)) {
603
- sort = sorters(attr);
604
- if ($.isFunction(sort)) {
605
- return sort;
606
- }
607
- } else if (sorters[attr] != null) {
608
- return sorters[attr];
609
- }
610
- }
611
- return naturalSort;
612
- };
613
-
614
- /*
615
- Data Model class
616
- */
617
- PivotData = (function() {
618
- function PivotData(input, opts) {
619
- var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9;
620
- if (opts == null) {
621
- opts = {};
622
- }
623
- this.getAggregator = bind(this.getAggregator, this);
624
- this.getRowKeys = bind(this.getRowKeys, this);
625
- this.getColKeys = bind(this.getColKeys, this);
626
- this.sortKeys = bind(this.sortKeys, this);
627
- this.arrSort = bind(this.arrSort, this);
628
- this.input = input;
629
- this.aggregator = (ref = opts.aggregator) != null ? ref : aggregatorTemplates.count()();
630
- this.aggregatorName = (ref1 = opts.aggregatorName) != null ? ref1 : "Count";
631
- this.colAttrs = (ref2 = opts.cols) != null ? ref2 : [];
632
- this.rowAttrs = (ref3 = opts.rows) != null ? ref3 : [];
633
- this.valAttrs = (ref4 = opts.vals) != null ? ref4 : [];
634
- this.sorters = (ref5 = opts.sorters) != null ? ref5 : {};
635
- this.rowOrder = (ref6 = opts.rowOrder) != null ? ref6 : "key_a_to_z";
636
- this.colOrder = (ref7 = opts.colOrder) != null ? ref7 : "key_a_to_z";
637
- this.derivedAttributes = (ref8 = opts.derivedAttributes) != null ? ref8 : {};
638
- this.filter = (ref9 = opts.filter) != null ? ref9 : (function() {
639
- return true;
640
- });
641
- this.tree = {};
642
- this.rowKeys = [];
643
- this.colKeys = [];
644
- this.rowTotals = {};
645
- this.colTotals = {};
646
- this.allTotal = this.aggregator(this, [], []);
647
- this.sorted = false;
648
- PivotData.forEachRecord(this.input, this.derivedAttributes, (function(_this) {
649
- return function(record) {
650
- if (_this.filter(record)) {
651
- return _this.processRecord(record);
652
- }
653
- };
654
- })(this));
655
- }
656
-
657
- PivotData.forEachRecord = function(input, derivedAttributes, f) {
658
- var addRecord, compactRecord, i, j, k, l, len1, record, ref, results, results1, tblCols;
659
- if ($.isEmptyObject(derivedAttributes)) {
660
- addRecord = f;
661
- } else {
662
- addRecord = function(record) {
663
- var k, ref, v;
664
- for (k in derivedAttributes) {
665
- v = derivedAttributes[k];
666
- record[k] = (ref = v(record)) != null ? ref : record[k];
667
- }
668
- return f(record);
669
- };
670
- }
671
- if ($.isFunction(input)) {
672
- return input(addRecord);
673
- } else if ($.isArray(input)) {
674
- if ($.isArray(input[0])) {
675
- results = [];
676
- for (i in input) {
677
- if (!hasProp.call(input, i)) continue;
678
- compactRecord = input[i];
679
- if (!(i > 0)) {
680
- continue;
681
- }
682
- record = {};
683
- ref = input[0];
684
- for (j in ref) {
685
- if (!hasProp.call(ref, j)) continue;
686
- k = ref[j];
687
- record[k] = compactRecord[j];
688
- }
689
- results.push(addRecord(record));
690
- }
691
- return results;
692
- } else {
693
- results1 = [];
694
- for (l = 0, len1 = input.length; l < len1; l++) {
695
- record = input[l];
696
- results1.push(addRecord(record));
697
- }
698
- return results1;
699
- }
700
- } else if (input instanceof $) {
701
- tblCols = [];
702
- $("thead > tr > th", input).each(function(i) {
703
- return tblCols.push($(this).text());
704
- });
705
- return $("tbody > tr", input).each(function(i) {
706
- record = {};
707
- $("td", this).each(function(j) {
708
- return record[tblCols[j]] = $(this).text();
709
- });
710
- return addRecord(record);
711
- });
712
- } else {
713
- throw new Error("unknown input format");
714
- }
715
- };
716
-
717
- PivotData.prototype.forEachMatchingRecord = function(criteria, callback) {
718
- return PivotData.forEachRecord(this.input, this.derivedAttributes, (function(_this) {
719
- return function(record) {
720
- var k, ref, v;
721
- if (!_this.filter(record)) {
722
- return;
723
- }
724
- for (k in criteria) {
725
- v = criteria[k];
726
- if (v !== ((ref = record[k]) != null ? ref : "null")) {
727
- return;
728
- }
729
- }
730
- return callback(record);
731
- };
732
- })(this));
733
- };
734
-
735
- PivotData.prototype.arrSort = function(attrs) {
736
- var a, sortersArr;
737
- sortersArr = (function() {
738
- var l, len1, results;
739
- results = [];
740
- for (l = 0, len1 = attrs.length; l < len1; l++) {
741
- a = attrs[l];
742
- results.push(getSort(this.sorters, a));
743
- }
744
- return results;
745
- }).call(this);
746
- return function(a, b) {
747
- var comparison, i, sorter;
748
- for (i in sortersArr) {
749
- if (!hasProp.call(sortersArr, i)) continue;
750
- sorter = sortersArr[i];
751
- comparison = sorter(a[i], b[i]);
752
- if (comparison !== 0) {
753
- return comparison;
754
- }
755
- }
756
- return 0;
757
- };
758
- };
759
-
760
- PivotData.prototype.sortKeys = function() {
761
- var v;
762
- if (!this.sorted) {
763
- this.sorted = true;
764
- v = (function(_this) {
765
- return function(r, c) {
766
- return _this.getAggregator(r, c).value();
767
- };
768
- })(this);
769
- switch (this.rowOrder) {
770
- case "value_a_to_z":
771
- this.rowKeys.sort((function(_this) {
772
- return function(a, b) {
773
- return naturalSort(v(a, []), v(b, []));
774
- };
775
- })(this));
776
- break;
777
- case "value_z_to_a":
778
- this.rowKeys.sort((function(_this) {
779
- return function(a, b) {
780
- return -naturalSort(v(a, []), v(b, []));
781
- };
782
- })(this));
783
- break;
784
- default:
785
- this.rowKeys.sort(this.arrSort(this.rowAttrs));
786
- }
787
- switch (this.colOrder) {
788
- case "value_a_to_z":
789
- return this.colKeys.sort((function(_this) {
790
- return function(a, b) {
791
- return naturalSort(v([], a), v([], b));
792
- };
793
- })(this));
794
- case "value_z_to_a":
795
- return this.colKeys.sort((function(_this) {
796
- return function(a, b) {
797
- return -naturalSort(v([], a), v([], b));
798
- };
799
- })(this));
800
- default:
801
- return this.colKeys.sort(this.arrSort(this.colAttrs));
802
- }
803
- }
804
- };
805
-
806
- PivotData.prototype.getColKeys = function() {
807
- this.sortKeys();
808
- return this.colKeys;
809
- };
810
-
811
- PivotData.prototype.getRowKeys = function() {
812
- this.sortKeys();
813
- return this.rowKeys;
814
- };
815
-
816
- PivotData.prototype.processRecord = function(record) {
817
- var colKey, flatColKey, flatRowKey, l, len1, len2, n, ref, ref1, ref2, ref3, rowKey, x;
818
- colKey = [];
819
- rowKey = [];
820
- ref = this.colAttrs;
821
- for (l = 0, len1 = ref.length; l < len1; l++) {
822
- x = ref[l];
823
- colKey.push((ref1 = record[x]) != null ? ref1 : "null");
824
- }
825
- ref2 = this.rowAttrs;
826
- for (n = 0, len2 = ref2.length; n < len2; n++) {
827
- x = ref2[n];
828
- rowKey.push((ref3 = record[x]) != null ? ref3 : "null");
829
- }
830
- flatRowKey = rowKey.join(String.fromCharCode(0));
831
- flatColKey = colKey.join(String.fromCharCode(0));
832
- this.allTotal.push(record);
833
- if (rowKey.length !== 0) {
834
- if (!this.rowTotals[flatRowKey]) {
835
- this.rowKeys.push(rowKey);
836
- this.rowTotals[flatRowKey] = this.aggregator(this, rowKey, []);
837
- }
838
- this.rowTotals[flatRowKey].push(record);
839
- }
840
- if (colKey.length !== 0) {
841
- if (!this.colTotals[flatColKey]) {
842
- this.colKeys.push(colKey);
843
- this.colTotals[flatColKey] = this.aggregator(this, [], colKey);
844
- }
845
- this.colTotals[flatColKey].push(record);
846
- }
847
- if (colKey.length !== 0 && rowKey.length !== 0) {
848
- if (!this.tree[flatRowKey]) {
849
- this.tree[flatRowKey] = {};
850
- }
851
- if (!this.tree[flatRowKey][flatColKey]) {
852
- this.tree[flatRowKey][flatColKey] = this.aggregator(this, rowKey, colKey);
853
- }
854
- return this.tree[flatRowKey][flatColKey].push(record);
855
- }
856
- };
857
-
858
- PivotData.prototype.getAggregator = function(rowKey, colKey) {
859
- var agg, flatColKey, flatRowKey;
860
- flatRowKey = rowKey.join(String.fromCharCode(0));
861
- flatColKey = colKey.join(String.fromCharCode(0));
862
- if (rowKey.length === 0 && colKey.length === 0) {
863
- agg = this.allTotal;
864
- } else if (rowKey.length === 0) {
865
- agg = this.colTotals[flatColKey];
866
- } else if (colKey.length === 0) {
867
- agg = this.rowTotals[flatRowKey];
868
- } else {
869
- agg = this.tree[flatRowKey][flatColKey];
870
- }
871
- return agg != null ? agg : {
872
- value: (function() {
873
- return null;
874
- }),
875
- format: function() {
876
- return "";
877
- }
878
- };
879
- };
880
-
881
- return PivotData;
882
-
883
- })();
884
- $.pivotUtilities = {
885
- aggregatorTemplates: aggregatorTemplates,
886
- aggregators: aggregators,
887
- renderers: renderers,
888
- derivers: derivers,
889
- locales: locales,
890
- naturalSort: naturalSort,
891
- numberFormat: numberFormat,
892
- sortAs: sortAs,
893
- PivotData: PivotData
894
- };
895
-
896
- /*
897
- Default Renderer for hierarchical table layout
898
- */
899
- pivotTableRenderer = function(pivotData, opts) {
900
- var aggregator, c, colAttrs, colKey, colKeys, defaults, getClickHandler, i, j, r, result, rowAttrs, rowKey, rowKeys, spanSize, tbody, td, th, thead, totalAggregator, tr, txt, val, x;
901
- defaults = {
902
- table: {
903
- clickCallback: null,
904
- rowTotals: true,
905
- colTotals: true
906
- },
907
- localeStrings: {
908
- totals: "Totals"
909
- }
910
- };
911
- opts = $.extend(true, {}, defaults, opts);
912
- colAttrs = pivotData.colAttrs;
913
- rowAttrs = pivotData.rowAttrs;
914
- rowKeys = pivotData.getRowKeys();
915
- colKeys = pivotData.getColKeys();
916
- if (opts.table.clickCallback) {
917
- getClickHandler = function(value, rowValues, colValues) {
918
- var attr, filters, i;
919
- filters = {};
920
- for (i in colAttrs) {
921
- if (!hasProp.call(colAttrs, i)) continue;
922
- attr = colAttrs[i];
923
- if (colValues[i] != null) {
924
- filters[attr] = colValues[i];
925
- }
926
- }
927
- for (i in rowAttrs) {
928
- if (!hasProp.call(rowAttrs, i)) continue;
929
- attr = rowAttrs[i];
930
- if (rowValues[i] != null) {
931
- filters[attr] = rowValues[i];
932
- }
933
- }
934
- return function(e) {
935
- return opts.table.clickCallback(e, value, filters, pivotData);
936
- };
937
- };
938
- }
939
- result = document.createElement("table");
940
- result.className = "pvtTable";
941
- spanSize = function(arr, i, j) {
942
- var l, len, n, noDraw, ref, ref1, stop, x;
943
- if (i !== 0) {
944
- noDraw = true;
945
- for (x = l = 0, ref = j; 0 <= ref ? l <= ref : l >= ref; x = 0 <= ref ? ++l : --l) {
946
- if (arr[i - 1][x] !== arr[i][x]) {
947
- noDraw = false;
948
- }
949
- }
950
- if (noDraw) {
951
- return -1;
952
- }
953
- }
954
- len = 0;
955
- while (i + len < arr.length) {
956
- stop = false;
957
- for (x = n = 0, ref1 = j; 0 <= ref1 ? n <= ref1 : n >= ref1; x = 0 <= ref1 ? ++n : --n) {
958
- if (arr[i][x] !== arr[i + len][x]) {
959
- stop = true;
960
- }
961
- }
962
- if (stop) {
963
- break;
964
- }
965
- len++;
966
- }
967
- return len;
968
- };
969
- thead = document.createElement("thead");
970
- for (j in colAttrs) {
971
- if (!hasProp.call(colAttrs, j)) continue;
972
- c = colAttrs[j];
973
- tr = document.createElement("tr");
974
- if (parseInt(j) === 0 && rowAttrs.length !== 0) {
975
- th = document.createElement("th");
976
- th.setAttribute("colspan", rowAttrs.length);
977
- th.setAttribute("rowspan", colAttrs.length);
978
- tr.appendChild(th);
979
- }
980
- th = document.createElement("th");
981
- th.className = "pvtAxisLabel";
982
- th.textContent = c;
983
- tr.appendChild(th);
984
- for (i in colKeys) {
985
- if (!hasProp.call(colKeys, i)) continue;
986
- colKey = colKeys[i];
987
- x = spanSize(colKeys, parseInt(i), parseInt(j));
988
- if (x !== -1) {
989
- th = document.createElement("th");
990
- th.className = "pvtColLabel";
991
- th.textContent = colKey[j];
992
- th.setAttribute("colspan", x);
993
- if (parseInt(j) === colAttrs.length - 1 && rowAttrs.length !== 0) {
994
- th.setAttribute("rowspan", 2);
995
- }
996
- tr.appendChild(th);
997
- }
998
- }
999
- if (parseInt(j) === 0 && opts.table.rowTotals) {
1000
- th = document.createElement("th");
1001
- th.className = "pvtTotalLabel pvtRowTotalLabel";
1002
- th.innerHTML = opts.localeStrings.totals;
1003
- th.setAttribute("rowspan", colAttrs.length + (rowAttrs.length === 0 ? 0 : 1));
1004
- tr.appendChild(th);
1005
- }
1006
- thead.appendChild(tr);
1007
- }
1008
- if (rowAttrs.length !== 0) {
1009
- tr = document.createElement("tr");
1010
- for (i in rowAttrs) {
1011
- if (!hasProp.call(rowAttrs, i)) continue;
1012
- r = rowAttrs[i];
1013
- th = document.createElement("th");
1014
- th.className = "pvtAxisLabel";
1015
- th.textContent = r;
1016
- tr.appendChild(th);
1017
- }
1018
- th = document.createElement("th");
1019
- if (colAttrs.length === 0) {
1020
- th.className = "pvtTotalLabel pvtRowTotalLabel";
1021
- th.innerHTML = opts.localeStrings.totals;
1022
- }
1023
- tr.appendChild(th);
1024
- thead.appendChild(tr);
1025
- }
1026
- result.appendChild(thead);
1027
- tbody = document.createElement("tbody");
1028
- for (i in rowKeys) {
1029
- if (!hasProp.call(rowKeys, i)) continue;
1030
- rowKey = rowKeys[i];
1031
- tr = document.createElement("tr");
1032
- for (j in rowKey) {
1033
- if (!hasProp.call(rowKey, j)) continue;
1034
- txt = rowKey[j];
1035
- x = spanSize(rowKeys, parseInt(i), parseInt(j));
1036
- if (x !== -1) {
1037
- th = document.createElement("th");
1038
- th.className = "pvtRowLabel";
1039
- th.textContent = txt;
1040
- th.setAttribute("rowspan", x);
1041
- if (parseInt(j) === rowAttrs.length - 1 && colAttrs.length !== 0) {
1042
- th.setAttribute("colspan", 2);
1043
- }
1044
- tr.appendChild(th);
1045
- }
1046
- }
1047
- for (j in colKeys) {
1048
- if (!hasProp.call(colKeys, j)) continue;
1049
- colKey = colKeys[j];
1050
- aggregator = pivotData.getAggregator(rowKey, colKey);
1051
- val = aggregator.value();
1052
- td = document.createElement("td");
1053
- td.className = "pvtVal row" + i + " col" + j;
1054
- td.textContent = aggregator.format(val);
1055
- td.setAttribute("data-value", val);
1056
- if (getClickHandler != null) {
1057
- td.onclick = getClickHandler(val, rowKey, colKey);
1058
- }
1059
- tr.appendChild(td);
1060
- }
1061
- if (opts.table.rowTotals || colAttrs.length === 0) {
1062
- totalAggregator = pivotData.getAggregator(rowKey, []);
1063
- val = totalAggregator.value();
1064
- td = document.createElement("td");
1065
- td.className = "pvtTotal rowTotal";
1066
- td.textContent = totalAggregator.format(val);
1067
- td.setAttribute("data-value", val);
1068
- if (getClickHandler != null) {
1069
- td.onclick = getClickHandler(val, rowKey, []);
1070
- }
1071
- td.setAttribute("data-for", "row" + i);
1072
- tr.appendChild(td);
1073
- }
1074
- tbody.appendChild(tr);
1075
- }
1076
- if (opts.table.colTotals || rowAttrs.length === 0) {
1077
- tr = document.createElement("tr");
1078
- if (opts.table.colTotals || rowAttrs.length === 0) {
1079
- th = document.createElement("th");
1080
- th.className = "pvtTotalLabel pvtColTotalLabel";
1081
- th.innerHTML = opts.localeStrings.totals;
1082
- th.setAttribute("colspan", rowAttrs.length + (colAttrs.length === 0 ? 0 : 1));
1083
- tr.appendChild(th);
1084
- }
1085
- for (j in colKeys) {
1086
- if (!hasProp.call(colKeys, j)) continue;
1087
- colKey = colKeys[j];
1088
- totalAggregator = pivotData.getAggregator([], colKey);
1089
- val = totalAggregator.value();
1090
- td = document.createElement("td");
1091
- td.className = "pvtTotal colTotal";
1092
- td.textContent = totalAggregator.format(val);
1093
- td.setAttribute("data-value", val);
1094
- if (getClickHandler != null) {
1095
- td.onclick = getClickHandler(val, [], colKey);
1096
- }
1097
- td.setAttribute("data-for", "col" + j);
1098
- tr.appendChild(td);
1099
- }
1100
- if (opts.table.rowTotals || colAttrs.length === 0) {
1101
- totalAggregator = pivotData.getAggregator([], []);
1102
- val = totalAggregator.value();
1103
- td = document.createElement("td");
1104
- td.className = "pvtGrandTotal";
1105
- td.textContent = totalAggregator.format(val);
1106
- td.setAttribute("data-value", val);
1107
- if (getClickHandler != null) {
1108
- td.onclick = getClickHandler(val, [], []);
1109
- }
1110
- tr.appendChild(td);
1111
- }
1112
- tbody.appendChild(tr);
1113
- }
1114
- result.appendChild(tbody);
1115
- result.setAttribute("data-numrows", rowKeys.length);
1116
- result.setAttribute("data-numcols", colKeys.length);
1117
- return result;
1118
- };
1119
-
1120
- /*
1121
- Pivot Table core: create PivotData object and call Renderer on it
1122
- */
1123
- $.fn.pivot = function(input, inputOpts, locale) {
1124
- var defaults, e, localeDefaults, localeStrings, opts, pivotData, result, x;
1125
- if (locale == null) {
1126
- locale = "en";
1127
- }
1128
- if (locales[locale] == null) {
1129
- locale = "en";
1130
- }
1131
- defaults = {
1132
- cols: [],
1133
- rows: [],
1134
- vals: [],
1135
- rowOrder: "key_a_to_z",
1136
- colOrder: "key_a_to_z",
1137
- dataClass: PivotData,
1138
- filter: function() {
1139
- return true;
1140
- },
1141
- aggregator: aggregatorTemplates.count()(),
1142
- aggregatorName: "Count",
1143
- sorters: {},
1144
- derivedAttributes: {},
1145
- renderer: pivotTableRenderer
1146
- };
1147
- localeStrings = $.extend(true, {}, locales.en.localeStrings, locales[locale].localeStrings);
1148
- localeDefaults = {
1149
- rendererOptions: {
1150
- localeStrings: localeStrings
1151
- },
1152
- localeStrings: localeStrings
1153
- };
1154
- opts = $.extend(true, {}, localeDefaults, $.extend({}, defaults, inputOpts));
1155
- result = null;
1156
- try {
1157
- pivotData = new opts.dataClass(input, opts);
1158
- try {
1159
- result = opts.renderer(pivotData, opts.rendererOptions);
1160
- } catch (error) {
1161
- e = error;
1162
- if (typeof console !== "undefined" && console !== null) {
1163
- console.error(e.stack);
1164
- }
1165
- result = $("<span>").html(opts.localeStrings.renderError);
1166
- }
1167
- } catch (error) {
1168
- e = error;
1169
- if (typeof console !== "undefined" && console !== null) {
1170
- console.error(e.stack);
1171
- }
1172
- result = $("<span>").html(opts.localeStrings.computeError);
1173
- }
1174
- x = this[0];
1175
- while (x.hasChildNodes()) {
1176
- x.removeChild(x.lastChild);
1177
- }
1178
- return this.append(result);
1179
- };
1180
-
1181
- /*
1182
- Pivot Table UI: calls Pivot Table core above with options set by user
1183
- */
1184
- $.fn.pivotUI = function(input, inputOpts, overwrite, locale) {
1185
- var a, aggregator, attr, attrLength, attrValues, c, colOrderArrow, defaults, e, existingOpts, fn1, i, initialRender, l, len1, len2, len3, localeDefaults, localeStrings, materializedInput, n, o, opts, ordering, pivotTable, recordsProcessed, ref, ref1, ref2, ref3, refresh, refreshDelayed, renderer, rendererControl, rowOrderArrow, shownAttributes, shownInAggregators, shownInDragDrop, tr1, tr2, uiTable, unused, unusedAttrsVerticalAutoCutoff, unusedAttrsVerticalAutoOverride, x;
1186
- if (overwrite == null) {
1187
- overwrite = false;
1188
- }
1189
- if (locale == null) {
1190
- locale = "en";
1191
- }
1192
- if (locales[locale] == null) {
1193
- locale = "en";
1194
- }
1195
- defaults = {
1196
- derivedAttributes: {},
1197
- aggregators: locales[locale].aggregators,
1198
- renderers: locales[locale].renderers,
1199
- hiddenAttributes: [],
1200
- hiddenFromAggregators: [],
1201
- hiddenFromDragDrop: [],
1202
- menuLimit: 500,
1203
- cols: [],
1204
- rows: [],
1205
- vals: [],
1206
- rowOrder: "key_a_to_z",
1207
- colOrder: "key_a_to_z",
1208
- dataClass: PivotData,
1209
- exclusions: {},
1210
- inclusions: {},
1211
- unusedAttrsVertical: 85,
1212
- autoSortUnusedAttrs: false,
1213
- onRefresh: null,
1214
- showUI: true,
1215
- filter: function() {
1216
- return true;
1217
- },
1218
- sorters: {}
1219
- };
1220
- localeStrings = $.extend(true, {}, locales.en.localeStrings, locales[locale].localeStrings);
1221
- localeDefaults = {
1222
- rendererOptions: {
1223
- localeStrings: localeStrings
1224
- },
1225
- localeStrings: localeStrings
1226
- };
1227
- existingOpts = this.data("pivotUIOptions");
1228
- if ((existingOpts == null) || overwrite) {
1229
- opts = $.extend(true, {}, localeDefaults, $.extend({}, defaults, inputOpts));
1230
- } else {
1231
- opts = existingOpts;
1232
- }
1233
- try {
1234
- attrValues = {};
1235
- materializedInput = [];
1236
- recordsProcessed = 0;
1237
- PivotData.forEachRecord(input, opts.derivedAttributes, function(record) {
1238
- var attr, base, ref, value;
1239
- if (!opts.filter(record)) {
1240
- return;
1241
- }
1242
- materializedInput.push(record);
1243
- for (attr in record) {
1244
- if (!hasProp.call(record, attr)) continue;
1245
- if (attrValues[attr] == null) {
1246
- attrValues[attr] = {};
1247
- if (recordsProcessed > 0) {
1248
- attrValues[attr]["null"] = recordsProcessed;
1249
- }
1250
- }
1251
- }
1252
- for (attr in attrValues) {
1253
- value = (ref = record[attr]) != null ? ref : "null";
1254
- if ((base = attrValues[attr])[value] == null) {
1255
- base[value] = 0;
1256
- }
1257
- attrValues[attr][value]++;
1258
- }
1259
- return recordsProcessed++;
1260
- });
1261
- uiTable = $("<table>", {
1262
- "class": "pvtUi"
1263
- }).attr("cellpadding", 5);
1264
- rendererControl = $("<td>").addClass("pvtUiCell");
1265
- renderer = $("<select>").addClass('pvtRenderer').appendTo(rendererControl).bind("change", function() {
1266
- return refresh();
1267
- });
1268
- ref = opts.renderers;
1269
- for (x in ref) {
1270
- if (!hasProp.call(ref, x)) continue;
1271
- $("<option>").val(x).html(x).appendTo(renderer);
1272
- }
1273
- unused = $("<td>").addClass('pvtAxisContainer pvtUnused pvtUiCell');
1274
- shownAttributes = (function() {
1275
- var results;
1276
- results = [];
1277
- for (a in attrValues) {
1278
- if (indexOf.call(opts.hiddenAttributes, a) < 0) {
1279
- results.push(a);
1280
- }
1281
- }
1282
- return results;
1283
- })();
1284
- shownInAggregators = (function() {
1285
- var l, len1, results;
1286
- results = [];
1287
- for (l = 0, len1 = shownAttributes.length; l < len1; l++) {
1288
- c = shownAttributes[l];
1289
- if (indexOf.call(opts.hiddenFromAggregators, c) < 0) {
1290
- results.push(c);
1291
- }
1292
- }
1293
- return results;
1294
- })();
1295
- shownInDragDrop = (function() {
1296
- var l, len1, results;
1297
- results = [];
1298
- for (l = 0, len1 = shownAttributes.length; l < len1; l++) {
1299
- c = shownAttributes[l];
1300
- if (indexOf.call(opts.hiddenFromDragDrop, c) < 0) {
1301
- results.push(c);
1302
- }
1303
- }
1304
- return results;
1305
- })();
1306
- unusedAttrsVerticalAutoOverride = false;
1307
- if (opts.unusedAttrsVertical === "auto") {
1308
- unusedAttrsVerticalAutoCutoff = 120;
1309
- } else {
1310
- unusedAttrsVerticalAutoCutoff = parseInt(opts.unusedAttrsVertical);
1311
- }
1312
- if (!isNaN(unusedAttrsVerticalAutoCutoff)) {
1313
- attrLength = 0;
1314
- for (l = 0, len1 = shownInDragDrop.length; l < len1; l++) {
1315
- a = shownInDragDrop[l];
1316
- attrLength += a.length;
1317
- }
1318
- unusedAttrsVerticalAutoOverride = attrLength > unusedAttrsVerticalAutoCutoff;
1319
- }
1320
- if (opts.unusedAttrsVertical === true || unusedAttrsVerticalAutoOverride) {
1321
- unused.addClass('pvtVertList');
1322
- } else {
1323
- unused.addClass('pvtHorizList');
1324
- }
1325
- fn1 = function(attr) {
1326
- var attrElem, checkContainer, closeFilterBox, controls, filterItem, filterItemExcluded, finalButtons, hasExcludedItem, len2, n, placeholder, ref1, sorter, triangleLink, v, value, valueCount, valueList, values;
1327
- values = (function() {
1328
- var results;
1329
- results = [];
1330
- for (v in attrValues[attr]) {
1331
- results.push(v);
1332
- }
1333
- return results;
1334
- })();
1335
- hasExcludedItem = false;
1336
- valueList = $("<div>").addClass('pvtFilterBox').hide();
1337
- valueList.append($("<h4>").append($("<span>").text(attr), $("<span>").addClass("count").text("(" + values.length + ")")));
1338
- if (values.length > opts.menuLimit) {
1339
- valueList.append($("<p>").html(opts.localeStrings.tooMany));
1340
- } else {
1341
- if (values.length > 5) {
1342
- controls = $("<p>").appendTo(valueList);
1343
- sorter = getSort(opts.sorters, attr);
1344
- placeholder = opts.localeStrings.filterResults;
1345
- $("<input>", {
1346
- type: "text"
1347
- }).appendTo(controls).attr({
1348
- placeholder: placeholder,
1349
- "class": "pvtSearch"
1350
- }).bind("keyup", function() {
1351
- var accept, accept_gen, filter;
1352
- filter = $(this).val().toLowerCase().trim();
1353
- accept_gen = function(prefix, accepted) {
1354
- return function(v) {
1355
- var real_filter, ref1;
1356
- real_filter = filter.substring(prefix.length).trim();
1357
- if (real_filter.length === 0) {
1358
- return true;
1359
- }
1360
- return ref1 = Math.sign(sorter(v.toLowerCase(), real_filter)), indexOf.call(accepted, ref1) >= 0;
1361
- };
1362
- };
1363
- accept = filter.indexOf(">=") === 0 ? accept_gen(">=", [1, 0]) : filter.indexOf("<=") === 0 ? accept_gen("<=", [-1, 0]) : filter.indexOf(">") === 0 ? accept_gen(">", [1]) : filter.indexOf("<") === 0 ? accept_gen("<", [-1]) : filter.indexOf("~") === 0 ? function(v) {
1364
- if (filter.substring(1).trim().length === 0) {
1365
- return true;
1366
- }
1367
- return v.toLowerCase().match(filter.substring(1));
1368
- } : function(v) {
1369
- return v.toLowerCase().indexOf(filter) !== -1;
1370
- };
1371
- return valueList.find('.pvtCheckContainer p label span.value').each(function() {
1372
- if (accept($(this).text())) {
1373
- return $(this).parent().parent().show();
1374
- } else {
1375
- return $(this).parent().parent().hide();
1376
- }
1377
- });
1378
- });
1379
- controls.append($("<br>"));
1380
- $("<button>", {
1381
- type: "button"
1382
- }).appendTo(controls).html(opts.localeStrings.selectAll).bind("click", function() {
1383
- valueList.find("input:visible:not(:checked)").prop("checked", true).toggleClass("changed");
1384
- return false;
1385
- });
1386
- $("<button>", {
1387
- type: "button"
1388
- }).appendTo(controls).html(opts.localeStrings.selectNone).bind("click", function() {
1389
- valueList.find("input:visible:checked").prop("checked", false).toggleClass("changed");
1390
- return false;
1391
- });
1392
- }
1393
- checkContainer = $("<div>").addClass("pvtCheckContainer").appendTo(valueList);
1394
- ref1 = values.sort(getSort(opts.sorters, attr));
1395
- for (n = 0, len2 = ref1.length; n < len2; n++) {
1396
- value = ref1[n];
1397
- valueCount = attrValues[attr][value];
1398
- filterItem = $("<label>");
1399
- filterItemExcluded = false;
1400
- if (opts.inclusions[attr]) {
1401
- filterItemExcluded = (indexOf.call(opts.inclusions[attr], value) < 0);
1402
- } else if (opts.exclusions[attr]) {
1403
- filterItemExcluded = (indexOf.call(opts.exclusions[attr], value) >= 0);
1404
- }
1405
- hasExcludedItem || (hasExcludedItem = filterItemExcluded);
1406
- $("<input>").attr("type", "checkbox").addClass('pvtFilter').attr("checked", !filterItemExcluded).data("filter", [attr, value]).appendTo(filterItem).bind("change", function() {
1407
- return $(this).toggleClass("changed");
1408
- });
1409
- filterItem.append($("<span>").addClass("value").text(value));
1410
- filterItem.append($("<span>").addClass("count").text("(" + valueCount + ")"));
1411
- checkContainer.append($("<p>").append(filterItem));
1412
- }
1413
- }
1414
- closeFilterBox = function() {
1415
- if (valueList.find("[type='checkbox']").length > valueList.find("[type='checkbox']:checked").length) {
1416
- attrElem.addClass("pvtFilteredAttribute");
1417
- } else {
1418
- attrElem.removeClass("pvtFilteredAttribute");
1419
- }
1420
- valueList.find('.pvtSearch').val('');
1421
- valueList.find('.pvtCheckContainer p').show();
1422
- return valueList.hide();
1423
- };
1424
- finalButtons = $("<p>").appendTo(valueList);
1425
- if (values.length <= opts.menuLimit) {
1426
- $("<button>", {
1427
- type: "button"
1428
- }).text(opts.localeStrings.apply).appendTo(finalButtons).bind("click", function() {
1429
- if (valueList.find(".changed").removeClass("changed").length) {
1430
- refresh();
1431
- }
1432
- return closeFilterBox();
1433
- });
1434
- }
1435
- $("<button>", {
1436
- type: "button"
1437
- }).text(opts.localeStrings.cancel).appendTo(finalButtons).bind("click", function() {
1438
- valueList.find(".changed:checked").removeClass("changed").prop("checked", false);
1439
- valueList.find(".changed:not(:checked)").removeClass("changed").prop("checked", true);
1440
- return closeFilterBox();
1441
- });
1442
- triangleLink = $("<span>").addClass('pvtTriangle').html(" &#x25BE;").bind("click", function(e) {
1443
- var left, ref2, top;
1444
- ref2 = $(e.currentTarget).position(), left = ref2.left, top = ref2.top;
1445
- return valueList.css({
1446
- left: left + 10,
1447
- top: top + 10
1448
- }).show();
1449
- });
1450
- attrElem = $("<li>").addClass("axis_" + i).append($("<span>").addClass('pvtAttr').text(attr).data("attrName", attr).append(triangleLink));
1451
- if (hasExcludedItem) {
1452
- attrElem.addClass('pvtFilteredAttribute');
1453
- }
1454
- return unused.append(attrElem).append(valueList);
1455
- };
1456
- for (i in shownInDragDrop) {
1457
- if (!hasProp.call(shownInDragDrop, i)) continue;
1458
- attr = shownInDragDrop[i];
1459
- fn1(attr);
1460
- }
1461
- tr1 = $("<tr>").appendTo(uiTable);
1462
- aggregator = $("<select>").addClass('pvtAggregator').bind("change", function() {
1463
- return refresh();
1464
- });
1465
- ref1 = opts.aggregators;
1466
- for (x in ref1) {
1467
- if (!hasProp.call(ref1, x)) continue;
1468
- aggregator.append($("<option>").val(x).html(x));
1469
- }
1470
- ordering = {
1471
- key_a_to_z: {
1472
- rowSymbol: "&varr;",
1473
- colSymbol: "&harr;",
1474
- next: "value_a_to_z"
1475
- },
1476
- value_a_to_z: {
1477
- rowSymbol: "&darr;",
1478
- colSymbol: "&rarr;",
1479
- next: "value_z_to_a"
1480
- },
1481
- value_z_to_a: {
1482
- rowSymbol: "&uarr;",
1483
- colSymbol: "&larr;",
1484
- next: "key_a_to_z"
1485
- }
1486
- };
1487
- rowOrderArrow = $("<a>", {
1488
- role: "button"
1489
- }).addClass("pvtRowOrder").data("order", opts.rowOrder).html(ordering[opts.rowOrder].rowSymbol).bind("click", function() {
1490
- $(this).data("order", ordering[$(this).data("order")].next);
1491
- $(this).html(ordering[$(this).data("order")].rowSymbol);
1492
- return refresh();
1493
- });
1494
- colOrderArrow = $("<a>", {
1495
- role: "button"
1496
- }).addClass("pvtColOrder").data("order", opts.colOrder).html(ordering[opts.colOrder].colSymbol).bind("click", function() {
1497
- $(this).data("order", ordering[$(this).data("order")].next);
1498
- $(this).html(ordering[$(this).data("order")].colSymbol);
1499
- return refresh();
1500
- });
1501
- $("<td>").addClass('pvtVals pvtUiCell').appendTo(tr1).append(aggregator).append(rowOrderArrow).append(colOrderArrow).append($("<br>"));
1502
- $("<td>").addClass('pvtAxisContainer pvtHorizList pvtCols pvtUiCell').appendTo(tr1);
1503
- tr2 = $("<tr>").appendTo(uiTable);
1504
- tr2.append($("<td>").addClass('pvtAxisContainer pvtRows pvtUiCell').attr("valign", "top"));
1505
- pivotTable = $("<td>").attr("valign", "top").addClass('pvtRendererArea').appendTo(tr2);
1506
- if (opts.unusedAttrsVertical === true || unusedAttrsVerticalAutoOverride) {
1507
- uiTable.find('tr:nth-child(1)').prepend(rendererControl);
1508
- uiTable.find('tr:nth-child(2)').prepend(unused);
1509
- } else {
1510
- uiTable.prepend($("<tr>").append(rendererControl).append(unused));
1511
- }
1512
- this.html(uiTable);
1513
- ref2 = opts.cols;
1514
- for (n = 0, len2 = ref2.length; n < len2; n++) {
1515
- x = ref2[n];
1516
- this.find(".pvtCols").append(this.find(".axis_" + ($.inArray(x, shownInDragDrop))));
1517
- }
1518
- ref3 = opts.rows;
1519
- for (o = 0, len3 = ref3.length; o < len3; o++) {
1520
- x = ref3[o];
1521
- this.find(".pvtRows").append(this.find(".axis_" + ($.inArray(x, shownInDragDrop))));
1522
- }
1523
- if (opts.aggregatorName != null) {
1524
- this.find(".pvtAggregator").val(opts.aggregatorName);
1525
- }
1526
- if (opts.rendererName != null) {
1527
- this.find(".pvtRenderer").val(opts.rendererName);
1528
- }
1529
- if (!opts.showUI) {
1530
- this.find(".pvtUiCell").hide();
1531
- }
1532
- initialRender = true;
1533
- refreshDelayed = (function(_this) {
1534
- return function() {
1535
- var exclusions, inclusions, len4, newDropdown, numInputsToProcess, pivotUIOptions, pvtVals, ref4, ref5, subopts, t, u, unusedAttrsContainer, vals;
1536
- subopts = {
1537
- derivedAttributes: opts.derivedAttributes,
1538
- localeStrings: opts.localeStrings,
1539
- rendererOptions: opts.rendererOptions,
1540
- sorters: opts.sorters,
1541
- cols: [],
1542
- rows: [],
1543
- dataClass: opts.dataClass
1544
- };
1545
- numInputsToProcess = (ref4 = opts.aggregators[aggregator.val()]([])().numInputs) != null ? ref4 : 0;
1546
- vals = [];
1547
- _this.find(".pvtRows li span.pvtAttr").each(function() {
1548
- return subopts.rows.push($(this).data("attrName"));
1549
- });
1550
- _this.find(".pvtCols li span.pvtAttr").each(function() {
1551
- return subopts.cols.push($(this).data("attrName"));
1552
- });
1553
- _this.find(".pvtVals select.pvtAttrDropdown").each(function() {
1554
- if (numInputsToProcess === 0) {
1555
- return $(this).remove();
1556
- } else {
1557
- numInputsToProcess--;
1558
- if ($(this).val() !== "") {
1559
- return vals.push($(this).val());
1560
- }
1561
- }
1562
- });
1563
- if (numInputsToProcess !== 0) {
1564
- pvtVals = _this.find(".pvtVals");
1565
- for (x = t = 0, ref5 = numInputsToProcess; 0 <= ref5 ? t < ref5 : t > ref5; x = 0 <= ref5 ? ++t : --t) {
1566
- newDropdown = $("<select>").addClass('pvtAttrDropdown').append($("<option>")).bind("change", function() {
1567
- return refresh();
1568
- });
1569
- for (u = 0, len4 = shownInAggregators.length; u < len4; u++) {
1570
- attr = shownInAggregators[u];
1571
- newDropdown.append($("<option>").val(attr).text(attr));
1572
- }
1573
- pvtVals.append(newDropdown);
1574
- }
1575
- }
1576
- if (initialRender) {
1577
- vals = opts.vals;
1578
- i = 0;
1579
- _this.find(".pvtVals select.pvtAttrDropdown").each(function() {
1580
- $(this).val(vals[i]);
1581
- return i++;
1582
- });
1583
- initialRender = false;
1584
- }
1585
- subopts.aggregatorName = aggregator.val();
1586
- subopts.vals = vals;
1587
- subopts.aggregator = opts.aggregators[aggregator.val()](vals);
1588
- subopts.renderer = opts.renderers[renderer.val()];
1589
- subopts.rowOrder = rowOrderArrow.data("order");
1590
- subopts.colOrder = colOrderArrow.data("order");
1591
- exclusions = {};
1592
- _this.find('input.pvtFilter').not(':checked').each(function() {
1593
- var filter;
1594
- filter = $(this).data("filter");
1595
- if (exclusions[filter[0]] != null) {
1596
- return exclusions[filter[0]].push(filter[1]);
1597
- } else {
1598
- return exclusions[filter[0]] = [filter[1]];
1599
- }
1600
- });
1601
- inclusions = {};
1602
- _this.find('input.pvtFilter:checked').each(function() {
1603
- var filter;
1604
- filter = $(this).data("filter");
1605
- if (exclusions[filter[0]] != null) {
1606
- if (inclusions[filter[0]] != null) {
1607
- return inclusions[filter[0]].push(filter[1]);
1608
- } else {
1609
- return inclusions[filter[0]] = [filter[1]];
1610
- }
1611
- }
1612
- });
1613
- subopts.filter = function(record) {
1614
- var excludedItems, k, ref6, ref7;
1615
- if (!opts.filter(record)) {
1616
- return false;
1617
- }
1618
- for (k in exclusions) {
1619
- excludedItems = exclusions[k];
1620
- if (ref6 = "" + ((ref7 = record[k]) != null ? ref7 : 'null'), indexOf.call(excludedItems, ref6) >= 0) {
1621
- return false;
1622
- }
1623
- }
1624
- return true;
1625
- };
1626
- pivotTable.pivot(materializedInput, subopts);
1627
- pivotUIOptions = $.extend({}, opts, {
1628
- cols: subopts.cols,
1629
- rows: subopts.rows,
1630
- colOrder: subopts.colOrder,
1631
- rowOrder: subopts.rowOrder,
1632
- vals: vals,
1633
- exclusions: exclusions,
1634
- inclusions: inclusions,
1635
- inclusionsInfo: inclusions,
1636
- aggregatorName: aggregator.val(),
1637
- rendererName: renderer.val()
1638
- });
1639
- _this.data("pivotUIOptions", pivotUIOptions);
1640
- if (opts.autoSortUnusedAttrs) {
1641
- unusedAttrsContainer = _this.find("td.pvtUnused.pvtAxisContainer");
1642
- $(unusedAttrsContainer).children("li").sort(function(a, b) {
1643
- return naturalSort($(a).text(), $(b).text());
1644
- }).appendTo(unusedAttrsContainer);
1645
- }
1646
- pivotTable.css("opacity", 1);
1647
- if (opts.onRefresh != null) {
1648
- return opts.onRefresh(pivotUIOptions);
1649
- }
1650
- };
1651
- })(this);
1652
- refresh = (function(_this) {
1653
- return function() {
1654
- pivotTable.css("opacity", 0.5);
1655
- return setTimeout(refreshDelayed, 10);
1656
- };
1657
- })(this);
1658
- refresh();
1659
- this.find(".pvtAxisContainer").sortable({
1660
- update: function(e, ui) {
1661
- if (ui.sender == null) {
1662
- return refresh();
1663
- }
1664
- },
1665
- connectWith: this.find(".pvtAxisContainer"),
1666
- items: 'li',
1667
- placeholder: 'pvtPlaceholder'
1668
- });
1669
- } catch (error) {
1670
- e = error;
1671
- if (typeof console !== "undefined" && console !== null) {
1672
- console.error(e.stack);
1673
- }
1674
- this.html(opts.localeStrings.uiRenderError);
1675
- }
1676
- return this;
1677
- };
1678
-
1679
- /*
1680
- Heatmap post-processing
1681
- */
1682
- $.fn.heatmap = function(scope, opts) {
1683
- var colorScaleGenerator, heatmapper, i, j, l, n, numCols, numRows, ref, ref1, ref2;
1684
- if (scope == null) {
1685
- scope = "heatmap";
1686
- }
1687
- numRows = this.data("numrows");
1688
- numCols = this.data("numcols");
1689
- colorScaleGenerator = opts != null ? (ref = opts.heatmap) != null ? ref.colorScaleGenerator : void 0 : void 0;
1690
- if (colorScaleGenerator == null) {
1691
- colorScaleGenerator = function(values) {
1692
- var max, min;
1693
- min = Math.min.apply(Math, values);
1694
- max = Math.max.apply(Math, values);
1695
- return function(x) {
1696
- var nonRed;
1697
- nonRed = 255 - Math.round(255 * (x - min) / (max - min));
1698
- return "rgb(255," + nonRed + "," + nonRed + ")";
1699
- };
1700
- };
1701
- }
1702
- heatmapper = (function(_this) {
1703
- return function(scope) {
1704
- var colorScale, forEachCell, values;
1705
- forEachCell = function(f) {
1706
- return _this.find(scope).each(function() {
1707
- var x;
1708
- x = $(this).data("value");
1709
- if ((x != null) && isFinite(x)) {
1710
- return f(x, $(this));
1711
- }
1712
- });
1713
- };
1714
- values = [];
1715
- forEachCell(function(x) {
1716
- return values.push(x);
1717
- });
1718
- colorScale = colorScaleGenerator(values);
1719
- return forEachCell(function(x, elem) {
1720
- return elem.css("background-color", colorScale(x));
1721
- });
1722
- };
1723
- })(this);
1724
- switch (scope) {
1725
- case "heatmap":
1726
- heatmapper(".pvtVal");
1727
- break;
1728
- case "rowheatmap":
1729
- for (i = l = 0, ref1 = numRows; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
1730
- heatmapper(".pvtVal.row" + i);
1731
- }
1732
- break;
1733
- case "colheatmap":
1734
- for (j = n = 0, ref2 = numCols; 0 <= ref2 ? n < ref2 : n > ref2; j = 0 <= ref2 ? ++n : --n) {
1735
- heatmapper(".pvtVal.col" + j);
1736
- }
1737
- }
1738
- heatmapper(".pvtTotal.rowTotal");
1739
- heatmapper(".pvtTotal.colTotal");
1740
- return this;
1741
- };
1742
-
1743
- /*
1744
- Barchart post-processing
1745
- */
1746
- return $.fn.barchart = function(opts) {
1747
- var barcharter, i, l, numCols, numRows, ref;
1748
- numRows = this.data("numrows");
1749
- numCols = this.data("numcols");
1750
- barcharter = (function(_this) {
1751
- return function(scope) {
1752
- var forEachCell, max, min, range, scaler, values;
1753
- forEachCell = function(f) {
1754
- return _this.find(scope).each(function() {
1755
- var x;
1756
- x = $(this).data("value");
1757
- if ((x != null) && isFinite(x)) {
1758
- return f(x, $(this));
1759
- }
1760
- });
1761
- };
1762
- values = [];
1763
- forEachCell(function(x) {
1764
- return values.push(x);
1765
- });
1766
- max = Math.max.apply(Math, values);
1767
- if (max < 0) {
1768
- max = 0;
1769
- }
1770
- range = max;
1771
- min = Math.min.apply(Math, values);
1772
- if (min < 0) {
1773
- range = max - min;
1774
- }
1775
- scaler = function(x) {
1776
- return 100 * x / (1.4 * range);
1777
- };
1778
- return forEachCell(function(x, elem) {
1779
- var bBase, bgColor, text, wrapper;
1780
- text = elem.text();
1781
- wrapper = $("<div>").css({
1782
- "position": "relative",
1783
- "height": "55px"
1784
- });
1785
- bgColor = "gray";
1786
- bBase = 0;
1787
- if (min < 0) {
1788
- bBase = scaler(-min);
1789
- }
1790
- if (x < 0) {
1791
- bBase += scaler(x);
1792
- bgColor = "darkred";
1793
- x = -x;
1794
- }
1795
- wrapper.append($("<div>").css({
1796
- "position": "absolute",
1797
- "bottom": bBase + "%",
1798
- "left": 0,
1799
- "right": 0,
1800
- "height": scaler(x) + "%",
1801
- "background-color": bgColor
1802
- }));
1803
- wrapper.append($("<div>").text(text).css({
1804
- "position": "relative",
1805
- "padding-left": "5px",
1806
- "padding-right": "5px"
1807
- }));
1808
- return elem.css({
1809
- "padding": 0,
1810
- "padding-top": "5px",
1811
- "text-align": "center"
1812
- }).html(wrapper);
1813
- });
1814
- };
1815
- })(this);
1816
- for (i = l = 0, ref = numRows; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) {
1817
- barcharter(".pvtVal.row" + i);
1818
- }
1819
- barcharter(".pvtTotal.colTotal");
1820
- return this;
1821
- };
1822
- };
1823
-
1
+ // from pivottable@2.23.0
2
+ let initPivotTable = function($, window, document) {
3
+ var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
4
+ slice = [].slice,
5
+ bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
6
+ hasProp = {}.hasOwnProperty;
7
+ /*
8
+ Utilities
9
+ */
10
+ var PivotData, addSeparators, aggregatorTemplates, aggregators, dayNamesEn, derivers, getSort, locales, mthNamesEn, naturalSort, numberFormat, pivotTableRenderer, rd, renderers, rx, rz, sortAs, usFmt, usFmtInt, usFmtPct, zeroPad;
11
+ addSeparators = function(nStr, thousandsSep, decimalSep) {
12
+ var rgx, x, x1, x2;
13
+ nStr += '';
14
+ x = nStr.split('.');
15
+ x1 = x[0];
16
+ x2 = x.length > 1 ? decimalSep + x[1] : '';
17
+ rgx = /(\d+)(\d{3})/;
18
+ while (rgx.test(x1)) {
19
+ x1 = x1.replace(rgx, '$1' + thousandsSep + '$2');
20
+ }
21
+ return x1 + x2;
22
+ };
23
+ numberFormat = function(opts) {
24
+ var defaults;
25
+ defaults = {
26
+ digitsAfterDecimal: 2,
27
+ scaler: 1,
28
+ thousandsSep: ",",
29
+ decimalSep: ".",
30
+ prefix: "",
31
+ suffix: ""
32
+ };
33
+ opts = $.extend({}, defaults, opts);
34
+ return function(x) {
35
+ var result;
36
+ if (isNaN(x) || !isFinite(x)) {
37
+ return "";
38
+ }
39
+ result = addSeparators((opts.scaler * x).toFixed(opts.digitsAfterDecimal), opts.thousandsSep, opts.decimalSep);
40
+ return "" + opts.prefix + result + opts.suffix;
41
+ };
42
+ };
43
+ usFmt = numberFormat();
44
+ usFmtInt = numberFormat({
45
+ digitsAfterDecimal: 0
46
+ });
47
+ usFmtPct = numberFormat({
48
+ digitsAfterDecimal: 1,
49
+ scaler: 100,
50
+ suffix: "%"
51
+ });
52
+ aggregatorTemplates = {
53
+ count: function(formatter) {
54
+ if (formatter == null) {
55
+ formatter = usFmtInt;
56
+ }
57
+ return function() {
58
+ return function(data, rowKey, colKey) {
59
+ return {
60
+ count: 0,
61
+ push: function() {
62
+ return this.count++;
63
+ },
64
+ value: function() {
65
+ return this.count;
66
+ },
67
+ format: formatter
68
+ };
69
+ };
70
+ };
71
+ },
72
+ uniques: function(fn, formatter) {
73
+ if (formatter == null) {
74
+ formatter = usFmtInt;
75
+ }
76
+ return function(arg) {
77
+ var attr;
78
+ attr = arg[0];
79
+ return function(data, rowKey, colKey) {
80
+ return {
81
+ uniq: [],
82
+ push: function(record) {
83
+ var ref;
84
+ if (ref = record[attr], indexOf.call(this.uniq, ref) < 0) {
85
+ return this.uniq.push(record[attr]);
86
+ }
87
+ },
88
+ value: function() {
89
+ return fn(this.uniq);
90
+ },
91
+ format: formatter,
92
+ numInputs: attr != null ? 0 : 1
93
+ };
94
+ };
95
+ };
96
+ },
97
+ sum: function(formatter) {
98
+ if (formatter == null) {
99
+ formatter = usFmt;
100
+ }
101
+ return function(arg) {
102
+ var attr;
103
+ attr = arg[0];
104
+ return function(data, rowKey, colKey) {
105
+ return {
106
+ sum: 0,
107
+ push: function(record) {
108
+ if (!isNaN(parseFloat(record[attr]))) {
109
+ return this.sum += parseFloat(record[attr]);
110
+ }
111
+ },
112
+ value: function() {
113
+ return this.sum;
114
+ },
115
+ format: formatter,
116
+ numInputs: attr != null ? 0 : 1
117
+ };
118
+ };
119
+ };
120
+ },
121
+ extremes: function(mode, formatter) {
122
+ if (formatter == null) {
123
+ formatter = usFmt;
124
+ }
125
+ return function(arg) {
126
+ var attr;
127
+ attr = arg[0];
128
+ return function(data, rowKey, colKey) {
129
+ return {
130
+ val: null,
131
+ sorter: getSort(data != null ? data.sorters : void 0, attr),
132
+ push: function(record) {
133
+ var ref, ref1, ref2, x;
134
+ x = record[attr];
135
+ if (mode === "min" || mode === "max") {
136
+ x = parseFloat(x);
137
+ if (!isNaN(x)) {
138
+ this.val = Math[mode](x, (ref = this.val) != null ? ref : x);
139
+ }
140
+ }
141
+ if (mode === "first") {
142
+ if (this.sorter(x, (ref1 = this.val) != null ? ref1 : x) <= 0) {
143
+ this.val = x;
144
+ }
145
+ }
146
+ if (mode === "last") {
147
+ if (this.sorter(x, (ref2 = this.val) != null ? ref2 : x) >= 0) {
148
+ return this.val = x;
149
+ }
150
+ }
151
+ },
152
+ value: function() {
153
+ return this.val;
154
+ },
155
+ format: function(x) {
156
+ if (isNaN(x)) {
157
+ return x;
158
+ } else {
159
+ return formatter(x);
160
+ }
161
+ },
162
+ numInputs: attr != null ? 0 : 1
163
+ };
164
+ };
165
+ };
166
+ },
167
+ quantile: function(q, formatter) {
168
+ if (formatter == null) {
169
+ formatter = usFmt;
170
+ }
171
+ return function(arg) {
172
+ var attr;
173
+ attr = arg[0];
174
+ return function(data, rowKey, colKey) {
175
+ return {
176
+ vals: [],
177
+ push: function(record) {
178
+ var x;
179
+ x = parseFloat(record[attr]);
180
+ if (!isNaN(x)) {
181
+ return this.vals.push(x);
182
+ }
183
+ },
184
+ value: function() {
185
+ var i;
186
+ if (this.vals.length === 0) {
187
+ return null;
188
+ }
189
+ this.vals.sort(function(a, b) {
190
+ return a - b;
191
+ });
192
+ i = (this.vals.length - 1) * q;
193
+ return (this.vals[Math.floor(i)] + this.vals[Math.ceil(i)]) / 2.0;
194
+ },
195
+ format: formatter,
196
+ numInputs: attr != null ? 0 : 1
197
+ };
198
+ };
199
+ };
200
+ },
201
+ runningStat: function(mode, ddof, formatter) {
202
+ if (mode == null) {
203
+ mode = "mean";
204
+ }
205
+ if (ddof == null) {
206
+ ddof = 1;
207
+ }
208
+ if (formatter == null) {
209
+ formatter = usFmt;
210
+ }
211
+ return function(arg) {
212
+ var attr;
213
+ attr = arg[0];
214
+ return function(data, rowKey, colKey) {
215
+ return {
216
+ n: 0.0,
217
+ m: 0.0,
218
+ s: 0.0,
219
+ push: function(record) {
220
+ var m_new, x;
221
+ x = parseFloat(record[attr]);
222
+ if (isNaN(x)) {
223
+ return;
224
+ }
225
+ this.n += 1.0;
226
+ if (this.n === 1.0) {
227
+ return this.m = x;
228
+ } else {
229
+ m_new = this.m + (x - this.m) / this.n;
230
+ this.s = this.s + (x - this.m) * (x - m_new);
231
+ return this.m = m_new;
232
+ }
233
+ },
234
+ value: function() {
235
+ if (mode === "mean") {
236
+ if (this.n === 0) {
237
+ return 0 / 0;
238
+ } else {
239
+ return this.m;
240
+ }
241
+ }
242
+ if (this.n <= ddof) {
243
+ return 0;
244
+ }
245
+ switch (mode) {
246
+ case "var":
247
+ return this.s / (this.n - ddof);
248
+ case "stdev":
249
+ return Math.sqrt(this.s / (this.n - ddof));
250
+ }
251
+ },
252
+ format: formatter,
253
+ numInputs: attr != null ? 0 : 1
254
+ };
255
+ };
256
+ };
257
+ },
258
+ sumOverSum: function(formatter) {
259
+ if (formatter == null) {
260
+ formatter = usFmt;
261
+ }
262
+ return function(arg) {
263
+ var denom, num;
264
+ num = arg[0], denom = arg[1];
265
+ return function(data, rowKey, colKey) {
266
+ return {
267
+ sumNum: 0,
268
+ sumDenom: 0,
269
+ push: function(record) {
270
+ if (!isNaN(parseFloat(record[num]))) {
271
+ this.sumNum += parseFloat(record[num]);
272
+ }
273
+ if (!isNaN(parseFloat(record[denom]))) {
274
+ return this.sumDenom += parseFloat(record[denom]);
275
+ }
276
+ },
277
+ value: function() {
278
+ return this.sumNum / this.sumDenom;
279
+ },
280
+ format: formatter,
281
+ numInputs: (num != null) && (denom != null) ? 0 : 2
282
+ };
283
+ };
284
+ };
285
+ },
286
+ sumOverSumBound80: function(upper, formatter) {
287
+ if (upper == null) {
288
+ upper = true;
289
+ }
290
+ if (formatter == null) {
291
+ formatter = usFmt;
292
+ }
293
+ return function(arg) {
294
+ var denom, num;
295
+ num = arg[0], denom = arg[1];
296
+ return function(data, rowKey, colKey) {
297
+ return {
298
+ sumNum: 0,
299
+ sumDenom: 0,
300
+ push: function(record) {
301
+ if (!isNaN(parseFloat(record[num]))) {
302
+ this.sumNum += parseFloat(record[num]);
303
+ }
304
+ if (!isNaN(parseFloat(record[denom]))) {
305
+ return this.sumDenom += parseFloat(record[denom]);
306
+ }
307
+ },
308
+ value: function() {
309
+ var sign;
310
+ sign = upper ? 1 : -1;
311
+ return (0.821187207574908 / this.sumDenom + this.sumNum / this.sumDenom + 1.2815515655446004 * sign * Math.sqrt(0.410593603787454 / (this.sumDenom * this.sumDenom) + (this.sumNum * (1 - this.sumNum / this.sumDenom)) / (this.sumDenom * this.sumDenom))) / (1 + 1.642374415149816 / this.sumDenom);
312
+ },
313
+ format: formatter,
314
+ numInputs: (num != null) && (denom != null) ? 0 : 2
315
+ };
316
+ };
317
+ };
318
+ },
319
+ fractionOf: function(wrapped, type, formatter) {
320
+ if (type == null) {
321
+ type = "total";
322
+ }
323
+ if (formatter == null) {
324
+ formatter = usFmtPct;
325
+ }
326
+ return function() {
327
+ var x;
328
+ x = 1 <= arguments.length ? slice.call(arguments, 0) : [];
329
+ return function(data, rowKey, colKey) {
330
+ return {
331
+ selector: {
332
+ total: [[], []],
333
+ row: [rowKey, []],
334
+ col: [[], colKey]
335
+ }[type],
336
+ inner: wrapped.apply(null, x)(data, rowKey, colKey),
337
+ push: function(record) {
338
+ return this.inner.push(record);
339
+ },
340
+ format: formatter,
341
+ value: function() {
342
+ return this.inner.value() / data.getAggregator.apply(data, this.selector).inner.value();
343
+ },
344
+ numInputs: wrapped.apply(null, x)().numInputs
345
+ };
346
+ };
347
+ };
348
+ }
349
+ };
350
+ aggregatorTemplates.countUnique = function(f) {
351
+ return aggregatorTemplates.uniques((function(x) {
352
+ return x.length;
353
+ }), f);
354
+ };
355
+ aggregatorTemplates.listUnique = function(s) {
356
+ return aggregatorTemplates.uniques((function(x) {
357
+ return x.sort(naturalSort).join(s);
358
+ }), (function(x) {
359
+ return x;
360
+ }));
361
+ };
362
+ aggregatorTemplates.max = function(f) {
363
+ return aggregatorTemplates.extremes('max', f);
364
+ };
365
+ aggregatorTemplates.min = function(f) {
366
+ return aggregatorTemplates.extremes('min', f);
367
+ };
368
+ aggregatorTemplates.first = function(f) {
369
+ return aggregatorTemplates.extremes('first', f);
370
+ };
371
+ aggregatorTemplates.last = function(f) {
372
+ return aggregatorTemplates.extremes('last', f);
373
+ };
374
+ aggregatorTemplates.median = function(f) {
375
+ return aggregatorTemplates.quantile(0.5, f);
376
+ };
377
+ aggregatorTemplates.average = function(f) {
378
+ return aggregatorTemplates.runningStat("mean", 1, f);
379
+ };
380
+ aggregatorTemplates["var"] = function(ddof, f) {
381
+ return aggregatorTemplates.runningStat("var", ddof, f);
382
+ };
383
+ aggregatorTemplates.stdev = function(ddof, f) {
384
+ return aggregatorTemplates.runningStat("stdev", ddof, f);
385
+ };
386
+ aggregators = (function(tpl) {
387
+ return {
388
+ "Count": tpl.count(usFmtInt),
389
+ "Count Unique Values": tpl.countUnique(usFmtInt),
390
+ "List Unique Values": tpl.listUnique(", "),
391
+ "Sum": tpl.sum(usFmt),
392
+ "Integer Sum": tpl.sum(usFmtInt),
393
+ "Average": tpl.average(usFmt),
394
+ "Median": tpl.median(usFmt),
395
+ "Sample Variance": tpl["var"](1, usFmt),
396
+ "Sample Standard Deviation": tpl.stdev(1, usFmt),
397
+ "Minimum": tpl.min(usFmt),
398
+ "Maximum": tpl.max(usFmt),
399
+ "First": tpl.first(usFmt),
400
+ "Last": tpl.last(usFmt),
401
+ "Sum over Sum": tpl.sumOverSum(usFmt),
402
+ "80% Upper Bound": tpl.sumOverSumBound80(true, usFmt),
403
+ "80% Lower Bound": tpl.sumOverSumBound80(false, usFmt),
404
+ "Sum as Fraction of Total": tpl.fractionOf(tpl.sum(), "total", usFmtPct),
405
+ "Sum as Fraction of Rows": tpl.fractionOf(tpl.sum(), "row", usFmtPct),
406
+ "Sum as Fraction of Columns": tpl.fractionOf(tpl.sum(), "col", usFmtPct),
407
+ "Count as Fraction of Total": tpl.fractionOf(tpl.count(), "total", usFmtPct),
408
+ "Count as Fraction of Rows": tpl.fractionOf(tpl.count(), "row", usFmtPct),
409
+ "Count as Fraction of Columns": tpl.fractionOf(tpl.count(), "col", usFmtPct)
410
+ };
411
+ })(aggregatorTemplates);
412
+ renderers = {
413
+ "Table": function(data, opts) {
414
+ return pivotTableRenderer(data, opts);
415
+ },
416
+ "Table Barchart": function(data, opts) {
417
+ return $(pivotTableRenderer(data, opts)).barchart();
418
+ },
419
+ "Heatmap": function(data, opts) {
420
+ return $(pivotTableRenderer(data, opts)).heatmap("heatmap", opts);
421
+ },
422
+ "Row Heatmap": function(data, opts) {
423
+ return $(pivotTableRenderer(data, opts)).heatmap("rowheatmap", opts);
424
+ },
425
+ "Col Heatmap": function(data, opts) {
426
+ return $(pivotTableRenderer(data, opts)).heatmap("colheatmap", opts);
427
+ }
428
+ };
429
+ locales = {
430
+ en: {
431
+ aggregators: aggregators,
432
+ renderers: renderers,
433
+ localeStrings: {
434
+ renderError: "An error occurred rendering the PivotTable results.",
435
+ computeError: "An error occurred computing the PivotTable results.",
436
+ uiRenderError: "An error occurred rendering the PivotTable UI.",
437
+ selectAll: "Select All",
438
+ selectNone: "Select None",
439
+ tooMany: "(too many to list)",
440
+ filterResults: "Filter values",
441
+ apply: "Apply",
442
+ cancel: "Cancel",
443
+ totals: "Totals",
444
+ vs: "vs",
445
+ by: "by"
446
+ }
447
+ }
448
+ };
449
+ mthNamesEn = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
450
+ dayNamesEn = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
451
+ zeroPad = function(number) {
452
+ return ("0" + number).substr(-2, 2);
453
+ };
454
+ derivers = {
455
+ bin: function(col, binWidth) {
456
+ return function(record) {
457
+ return record[col] - record[col] % binWidth;
458
+ };
459
+ },
460
+ dateFormat: function(col, formatString, utcOutput, mthNames, dayNames) {
461
+ var utc;
462
+ if (utcOutput == null) {
463
+ utcOutput = false;
464
+ }
465
+ if (mthNames == null) {
466
+ mthNames = mthNamesEn;
467
+ }
468
+ if (dayNames == null) {
469
+ dayNames = dayNamesEn;
470
+ }
471
+ utc = utcOutput ? "UTC" : "";
472
+ return function(record) {
473
+ var date;
474
+ date = new Date(Date.parse(record[col]));
475
+ if (isNaN(date)) {
476
+ return "";
477
+ }
478
+ return formatString.replace(/%(.)/g, function(m, p) {
479
+ switch (p) {
480
+ case "y":
481
+ return date["get" + utc + "FullYear"]();
482
+ case "m":
483
+ return zeroPad(date["get" + utc + "Month"]() + 1);
484
+ case "n":
485
+ return mthNames[date["get" + utc + "Month"]()];
486
+ case "d":
487
+ return zeroPad(date["get" + utc + "Date"]());
488
+ case "w":
489
+ return dayNames[date["get" + utc + "Day"]()];
490
+ case "x":
491
+ return date["get" + utc + "Day"]();
492
+ case "H":
493
+ return zeroPad(date["get" + utc + "Hours"]());
494
+ case "M":
495
+ return zeroPad(date["get" + utc + "Minutes"]());
496
+ case "S":
497
+ return zeroPad(date["get" + utc + "Seconds"]());
498
+ default:
499
+ return "%" + p;
500
+ }
501
+ });
502
+ };
503
+ }
504
+ };
505
+ rx = /(\d+)|(\D+)/g;
506
+ rd = /\d/;
507
+ rz = /^0/;
508
+ naturalSort = (function(_this) {
509
+ return function(as, bs) {
510
+ var a, a1, b, b1, nas, nbs;
511
+ if ((bs != null) && (as == null)) {
512
+ return -1;
513
+ }
514
+ if ((as != null) && (bs == null)) {
515
+ return 1;
516
+ }
517
+ if (typeof as === "number" && isNaN(as)) {
518
+ return -1;
519
+ }
520
+ if (typeof bs === "number" && isNaN(bs)) {
521
+ return 1;
522
+ }
523
+ nas = +as;
524
+ nbs = +bs;
525
+ if (nas < nbs) {
526
+ return -1;
527
+ }
528
+ if (nas > nbs) {
529
+ return 1;
530
+ }
531
+ if (typeof as === "number" && typeof bs !== "number") {
532
+ return -1;
533
+ }
534
+ if (typeof bs === "number" && typeof as !== "number") {
535
+ return 1;
536
+ }
537
+ if (typeof as === "number" && typeof bs === "number") {
538
+ return 0;
539
+ }
540
+ if (isNaN(nbs) && !isNaN(nas)) {
541
+ return -1;
542
+ }
543
+ if (isNaN(nas) && !isNaN(nbs)) {
544
+ return 1;
545
+ }
546
+ a = String(as).toLowerCase();
547
+ b = String(bs).toLowerCase();
548
+ if (a === b) {
549
+ return 0;
550
+ }
551
+ if (!(rd.test(a) && rd.test(b))) {
552
+ return (a > b ? 1 : -1);
553
+ }
554
+ a = a.match(rx);
555
+ b = b.match(rx);
556
+ while (a.length && b.length) {
557
+ a1 = a.shift();
558
+ b1 = b.shift();
559
+ if (a1 !== b1) {
560
+ if (rd.test(a1) && rd.test(b1)) {
561
+ return a1.replace(rz, ".0") - b1.replace(rz, ".0");
562
+ } else {
563
+ return (a1 > b1 ? 1 : -1);
564
+ }
565
+ }
566
+ }
567
+ return a.length - b.length;
568
+ };
569
+ })(this);
570
+ sortAs = function(order) {
571
+ var i, l_mapping, mapping, x;
572
+ mapping = {};
573
+ l_mapping = {};
574
+ for (i in order) {
575
+ x = order[i];
576
+ mapping[x] = i;
577
+ if (typeof x === "string") {
578
+ l_mapping[x.toLowerCase()] = i;
579
+ }
580
+ }
581
+ return function(a, b) {
582
+ if ((mapping[a] != null) && (mapping[b] != null)) {
583
+ return mapping[a] - mapping[b];
584
+ } else if (mapping[a] != null) {
585
+ return -1;
586
+ } else if (mapping[b] != null) {
587
+ return 1;
588
+ } else if ((l_mapping[a] != null) && (l_mapping[b] != null)) {
589
+ return l_mapping[a] - l_mapping[b];
590
+ } else if (l_mapping[a] != null) {
591
+ return -1;
592
+ } else if (l_mapping[b] != null) {
593
+ return 1;
594
+ } else {
595
+ return naturalSort(a, b);
596
+ }
597
+ };
598
+ };
599
+ getSort = function(sorters, attr) {
600
+ var sort;
601
+ if (sorters != null) {
602
+ if ($.isFunction(sorters)) {
603
+ sort = sorters(attr);
604
+ if ($.isFunction(sort)) {
605
+ return sort;
606
+ }
607
+ } else if (sorters[attr] != null) {
608
+ return sorters[attr];
609
+ }
610
+ }
611
+ return naturalSort;
612
+ };
613
+
614
+ /*
615
+ Data Model class
616
+ */
617
+ PivotData = (function() {
618
+ function PivotData(input, opts) {
619
+ var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9;
620
+ if (opts == null) {
621
+ opts = {};
622
+ }
623
+ this.getAggregator = bind(this.getAggregator, this);
624
+ this.getRowKeys = bind(this.getRowKeys, this);
625
+ this.getColKeys = bind(this.getColKeys, this);
626
+ this.sortKeys = bind(this.sortKeys, this);
627
+ this.arrSort = bind(this.arrSort, this);
628
+ this.input = input;
629
+ this.aggregator = (ref = opts.aggregator) != null ? ref : aggregatorTemplates.count()();
630
+ this.aggregatorName = (ref1 = opts.aggregatorName) != null ? ref1 : "Count";
631
+ this.colAttrs = (ref2 = opts.cols) != null ? ref2 : [];
632
+ this.rowAttrs = (ref3 = opts.rows) != null ? ref3 : [];
633
+ this.valAttrs = (ref4 = opts.vals) != null ? ref4 : [];
634
+ this.sorters = (ref5 = opts.sorters) != null ? ref5 : {};
635
+ this.rowOrder = (ref6 = opts.rowOrder) != null ? ref6 : "key_a_to_z";
636
+ this.colOrder = (ref7 = opts.colOrder) != null ? ref7 : "key_a_to_z";
637
+ this.derivedAttributes = (ref8 = opts.derivedAttributes) != null ? ref8 : {};
638
+ this.filter = (ref9 = opts.filter) != null ? ref9 : (function() {
639
+ return true;
640
+ });
641
+ this.tree = {};
642
+ this.rowKeys = [];
643
+ this.colKeys = [];
644
+ this.rowTotals = {};
645
+ this.colTotals = {};
646
+ this.allTotal = this.aggregator(this, [], []);
647
+ this.sorted = false;
648
+ PivotData.forEachRecord(this.input, this.derivedAttributes, (function(_this) {
649
+ return function(record) {
650
+ if (_this.filter(record)) {
651
+ return _this.processRecord(record);
652
+ }
653
+ };
654
+ })(this));
655
+ }
656
+
657
+ PivotData.forEachRecord = function(input, derivedAttributes, f) {
658
+ var addRecord, compactRecord, i, j, k, l, len1, record, ref, results, results1, tblCols;
659
+ if ($.isEmptyObject(derivedAttributes)) {
660
+ addRecord = f;
661
+ } else {
662
+ addRecord = function(record) {
663
+ var k, ref, v;
664
+ for (k in derivedAttributes) {
665
+ v = derivedAttributes[k];
666
+ record[k] = (ref = v(record)) != null ? ref : record[k];
667
+ }
668
+ return f(record);
669
+ };
670
+ }
671
+ if ($.isFunction(input)) {
672
+ return input(addRecord);
673
+ } else if ($.isArray(input)) {
674
+ if ($.isArray(input[0])) {
675
+ results = [];
676
+ for (i in input) {
677
+ if (!hasProp.call(input, i)) continue;
678
+ compactRecord = input[i];
679
+ if (!(i > 0)) {
680
+ continue;
681
+ }
682
+ record = {};
683
+ ref = input[0];
684
+ for (j in ref) {
685
+ if (!hasProp.call(ref, j)) continue;
686
+ k = ref[j];
687
+ record[k] = compactRecord[j];
688
+ }
689
+ results.push(addRecord(record));
690
+ }
691
+ return results;
692
+ } else {
693
+ results1 = [];
694
+ for (l = 0, len1 = input.length; l < len1; l++) {
695
+ record = input[l];
696
+ results1.push(addRecord(record));
697
+ }
698
+ return results1;
699
+ }
700
+ } else if (input instanceof $) {
701
+ tblCols = [];
702
+ $("thead > tr > th", input).each(function(i) {
703
+ return tblCols.push($(this).text());
704
+ });
705
+ return $("tbody > tr", input).each(function(i) {
706
+ record = {};
707
+ $("td", this).each(function(j) {
708
+ return record[tblCols[j]] = $(this).text();
709
+ });
710
+ return addRecord(record);
711
+ });
712
+ } else {
713
+ throw new Error("unknown input format");
714
+ }
715
+ };
716
+
717
+ PivotData.prototype.forEachMatchingRecord = function(criteria, callback) {
718
+ return PivotData.forEachRecord(this.input, this.derivedAttributes, (function(_this) {
719
+ return function(record) {
720
+ var k, ref, v;
721
+ if (!_this.filter(record)) {
722
+ return;
723
+ }
724
+ for (k in criteria) {
725
+ v = criteria[k];
726
+ if (v !== ((ref = record[k]) != null ? ref : "null")) {
727
+ return;
728
+ }
729
+ }
730
+ return callback(record);
731
+ };
732
+ })(this));
733
+ };
734
+
735
+ PivotData.prototype.arrSort = function(attrs) {
736
+ var a, sortersArr;
737
+ sortersArr = (function() {
738
+ var l, len1, results;
739
+ results = [];
740
+ for (l = 0, len1 = attrs.length; l < len1; l++) {
741
+ a = attrs[l];
742
+ results.push(getSort(this.sorters, a));
743
+ }
744
+ return results;
745
+ }).call(this);
746
+ return function(a, b) {
747
+ var comparison, i, sorter;
748
+ for (i in sortersArr) {
749
+ if (!hasProp.call(sortersArr, i)) continue;
750
+ sorter = sortersArr[i];
751
+ comparison = sorter(a[i], b[i]);
752
+ if (comparison !== 0) {
753
+ return comparison;
754
+ }
755
+ }
756
+ return 0;
757
+ };
758
+ };
759
+
760
+ PivotData.prototype.sortKeys = function() {
761
+ var v;
762
+ if (!this.sorted) {
763
+ this.sorted = true;
764
+ v = (function(_this) {
765
+ return function(r, c) {
766
+ return _this.getAggregator(r, c).value();
767
+ };
768
+ })(this);
769
+ switch (this.rowOrder) {
770
+ case "value_a_to_z":
771
+ this.rowKeys.sort((function(_this) {
772
+ return function(a, b) {
773
+ return naturalSort(v(a, []), v(b, []));
774
+ };
775
+ })(this));
776
+ break;
777
+ case "value_z_to_a":
778
+ this.rowKeys.sort((function(_this) {
779
+ return function(a, b) {
780
+ return -naturalSort(v(a, []), v(b, []));
781
+ };
782
+ })(this));
783
+ break;
784
+ default:
785
+ this.rowKeys.sort(this.arrSort(this.rowAttrs));
786
+ }
787
+ switch (this.colOrder) {
788
+ case "value_a_to_z":
789
+ return this.colKeys.sort((function(_this) {
790
+ return function(a, b) {
791
+ return naturalSort(v([], a), v([], b));
792
+ };
793
+ })(this));
794
+ case "value_z_to_a":
795
+ return this.colKeys.sort((function(_this) {
796
+ return function(a, b) {
797
+ return -naturalSort(v([], a), v([], b));
798
+ };
799
+ })(this));
800
+ default:
801
+ return this.colKeys.sort(this.arrSort(this.colAttrs));
802
+ }
803
+ }
804
+ };
805
+
806
+ PivotData.prototype.getColKeys = function() {
807
+ this.sortKeys();
808
+ return this.colKeys;
809
+ };
810
+
811
+ PivotData.prototype.getRowKeys = function() {
812
+ this.sortKeys();
813
+ return this.rowKeys;
814
+ };
815
+
816
+ PivotData.prototype.processRecord = function(record) {
817
+ var colKey, flatColKey, flatRowKey, l, len1, len2, n, ref, ref1, ref2, ref3, rowKey, x;
818
+ colKey = [];
819
+ rowKey = [];
820
+ ref = this.colAttrs;
821
+ for (l = 0, len1 = ref.length; l < len1; l++) {
822
+ x = ref[l];
823
+ colKey.push((ref1 = record[x]) != null ? ref1 : "null");
824
+ }
825
+ ref2 = this.rowAttrs;
826
+ for (n = 0, len2 = ref2.length; n < len2; n++) {
827
+ x = ref2[n];
828
+ rowKey.push((ref3 = record[x]) != null ? ref3 : "null");
829
+ }
830
+ flatRowKey = rowKey.join(String.fromCharCode(0));
831
+ flatColKey = colKey.join(String.fromCharCode(0));
832
+ this.allTotal.push(record);
833
+ if (rowKey.length !== 0) {
834
+ if (!this.rowTotals[flatRowKey]) {
835
+ this.rowKeys.push(rowKey);
836
+ this.rowTotals[flatRowKey] = this.aggregator(this, rowKey, []);
837
+ }
838
+ this.rowTotals[flatRowKey].push(record);
839
+ }
840
+ if (colKey.length !== 0) {
841
+ if (!this.colTotals[flatColKey]) {
842
+ this.colKeys.push(colKey);
843
+ this.colTotals[flatColKey] = this.aggregator(this, [], colKey);
844
+ }
845
+ this.colTotals[flatColKey].push(record);
846
+ }
847
+ if (colKey.length !== 0 && rowKey.length !== 0) {
848
+ if (!this.tree[flatRowKey]) {
849
+ this.tree[flatRowKey] = {};
850
+ }
851
+ if (!this.tree[flatRowKey][flatColKey]) {
852
+ this.tree[flatRowKey][flatColKey] = this.aggregator(this, rowKey, colKey);
853
+ }
854
+ return this.tree[flatRowKey][flatColKey].push(record);
855
+ }
856
+ };
857
+
858
+ PivotData.prototype.getAggregator = function(rowKey, colKey) {
859
+ var agg, flatColKey, flatRowKey;
860
+ flatRowKey = rowKey.join(String.fromCharCode(0));
861
+ flatColKey = colKey.join(String.fromCharCode(0));
862
+ if (rowKey.length === 0 && colKey.length === 0) {
863
+ agg = this.allTotal;
864
+ } else if (rowKey.length === 0) {
865
+ agg = this.colTotals[flatColKey];
866
+ } else if (colKey.length === 0) {
867
+ agg = this.rowTotals[flatRowKey];
868
+ } else {
869
+ agg = this.tree[flatRowKey][flatColKey];
870
+ }
871
+ return agg != null ? agg : {
872
+ value: (function() {
873
+ return null;
874
+ }),
875
+ format: function() {
876
+ return "";
877
+ }
878
+ };
879
+ };
880
+
881
+ return PivotData;
882
+
883
+ })();
884
+ $.pivotUtilities = {
885
+ aggregatorTemplates: aggregatorTemplates,
886
+ aggregators: aggregators,
887
+ renderers: renderers,
888
+ derivers: derivers,
889
+ locales: locales,
890
+ naturalSort: naturalSort,
891
+ numberFormat: numberFormat,
892
+ sortAs: sortAs,
893
+ PivotData: PivotData
894
+ };
895
+
896
+ /*
897
+ Default Renderer for hierarchical table layout
898
+ */
899
+ pivotTableRenderer = function(pivotData, opts) {
900
+ var aggregator, c, colAttrs, colKey, colKeys, defaults, getClickHandler, i, j, r, result, rowAttrs, rowKey, rowKeys, spanSize, tbody, td, th, thead, totalAggregator, tr, txt, val, x;
901
+ defaults = {
902
+ table: {
903
+ clickCallback: null,
904
+ rowTotals: true,
905
+ colTotals: true
906
+ },
907
+ localeStrings: {
908
+ totals: "Totals"
909
+ }
910
+ };
911
+ opts = $.extend(true, {}, defaults, opts);
912
+ colAttrs = pivotData.colAttrs;
913
+ rowAttrs = pivotData.rowAttrs;
914
+ rowKeys = pivotData.getRowKeys();
915
+ colKeys = pivotData.getColKeys();
916
+ if (opts.table.clickCallback) {
917
+ getClickHandler = function(value, rowValues, colValues) {
918
+ var attr, filters, i;
919
+ filters = {};
920
+ for (i in colAttrs) {
921
+ if (!hasProp.call(colAttrs, i)) continue;
922
+ attr = colAttrs[i];
923
+ if (colValues[i] != null) {
924
+ filters[attr] = colValues[i];
925
+ }
926
+ }
927
+ for (i in rowAttrs) {
928
+ if (!hasProp.call(rowAttrs, i)) continue;
929
+ attr = rowAttrs[i];
930
+ if (rowValues[i] != null) {
931
+ filters[attr] = rowValues[i];
932
+ }
933
+ }
934
+ return function(e) {
935
+ return opts.table.clickCallback(e, value, filters, pivotData);
936
+ };
937
+ };
938
+ }
939
+ result = document.createElement("table");
940
+ result.className = "pvtTable";
941
+ spanSize = function(arr, i, j) {
942
+ var l, len, n, noDraw, ref, ref1, stop, x;
943
+ if (i !== 0) {
944
+ noDraw = true;
945
+ for (x = l = 0, ref = j; 0 <= ref ? l <= ref : l >= ref; x = 0 <= ref ? ++l : --l) {
946
+ if (arr[i - 1][x] !== arr[i][x]) {
947
+ noDraw = false;
948
+ }
949
+ }
950
+ if (noDraw) {
951
+ return -1;
952
+ }
953
+ }
954
+ len = 0;
955
+ while (i + len < arr.length) {
956
+ stop = false;
957
+ for (x = n = 0, ref1 = j; 0 <= ref1 ? n <= ref1 : n >= ref1; x = 0 <= ref1 ? ++n : --n) {
958
+ if (arr[i][x] !== arr[i + len][x]) {
959
+ stop = true;
960
+ }
961
+ }
962
+ if (stop) {
963
+ break;
964
+ }
965
+ len++;
966
+ }
967
+ return len;
968
+ };
969
+ thead = document.createElement("thead");
970
+ for (j in colAttrs) {
971
+ if (!hasProp.call(colAttrs, j)) continue;
972
+ c = colAttrs[j];
973
+ tr = document.createElement("tr");
974
+ if (parseInt(j) === 0 && rowAttrs.length !== 0) {
975
+ th = document.createElement("th");
976
+ th.setAttribute("colspan", rowAttrs.length);
977
+ th.setAttribute("rowspan", colAttrs.length);
978
+ tr.appendChild(th);
979
+ }
980
+ th = document.createElement("th");
981
+ th.className = "pvtAxisLabel";
982
+ th.textContent = c;
983
+ tr.appendChild(th);
984
+ for (i in colKeys) {
985
+ if (!hasProp.call(colKeys, i)) continue;
986
+ colKey = colKeys[i];
987
+ x = spanSize(colKeys, parseInt(i), parseInt(j));
988
+ if (x !== -1) {
989
+ th = document.createElement("th");
990
+ th.className = "pvtColLabel";
991
+ th.textContent = colKey[j];
992
+ th.setAttribute("colspan", x);
993
+ if (parseInt(j) === colAttrs.length - 1 && rowAttrs.length !== 0) {
994
+ th.setAttribute("rowspan", 2);
995
+ }
996
+ tr.appendChild(th);
997
+ }
998
+ }
999
+ if (parseInt(j) === 0 && opts.table.rowTotals) {
1000
+ th = document.createElement("th");
1001
+ th.className = "pvtTotalLabel pvtRowTotalLabel";
1002
+ th.innerHTML = opts.localeStrings.totals;
1003
+ th.setAttribute("rowspan", colAttrs.length + (rowAttrs.length === 0 ? 0 : 1));
1004
+ tr.appendChild(th);
1005
+ }
1006
+ thead.appendChild(tr);
1007
+ }
1008
+ if (rowAttrs.length !== 0) {
1009
+ tr = document.createElement("tr");
1010
+ for (i in rowAttrs) {
1011
+ if (!hasProp.call(rowAttrs, i)) continue;
1012
+ r = rowAttrs[i];
1013
+ th = document.createElement("th");
1014
+ th.className = "pvtAxisLabel";
1015
+ th.textContent = r;
1016
+ tr.appendChild(th);
1017
+ }
1018
+ th = document.createElement("th");
1019
+ if (colAttrs.length === 0) {
1020
+ th.className = "pvtTotalLabel pvtRowTotalLabel";
1021
+ th.innerHTML = opts.localeStrings.totals;
1022
+ }
1023
+ tr.appendChild(th);
1024
+ thead.appendChild(tr);
1025
+ }
1026
+ result.appendChild(thead);
1027
+ tbody = document.createElement("tbody");
1028
+ for (i in rowKeys) {
1029
+ if (!hasProp.call(rowKeys, i)) continue;
1030
+ rowKey = rowKeys[i];
1031
+ tr = document.createElement("tr");
1032
+ for (j in rowKey) {
1033
+ if (!hasProp.call(rowKey, j)) continue;
1034
+ txt = rowKey[j];
1035
+ x = spanSize(rowKeys, parseInt(i), parseInt(j));
1036
+ if (x !== -1) {
1037
+ th = document.createElement("th");
1038
+ th.className = "pvtRowLabel";
1039
+ th.textContent = txt;
1040
+ th.setAttribute("rowspan", x);
1041
+ if (parseInt(j) === rowAttrs.length - 1 && colAttrs.length !== 0) {
1042
+ th.setAttribute("colspan", 2);
1043
+ }
1044
+ tr.appendChild(th);
1045
+ }
1046
+ }
1047
+ for (j in colKeys) {
1048
+ if (!hasProp.call(colKeys, j)) continue;
1049
+ colKey = colKeys[j];
1050
+ aggregator = pivotData.getAggregator(rowKey, colKey);
1051
+ val = aggregator.value();
1052
+ td = document.createElement("td");
1053
+ td.className = "pvtVal row" + i + " col" + j;
1054
+ td.textContent = aggregator.format(val);
1055
+ td.setAttribute("data-value", val);
1056
+ if (getClickHandler != null) {
1057
+ td.onclick = getClickHandler(val, rowKey, colKey);
1058
+ }
1059
+ tr.appendChild(td);
1060
+ }
1061
+ if (opts.table.rowTotals || colAttrs.length === 0) {
1062
+ totalAggregator = pivotData.getAggregator(rowKey, []);
1063
+ val = totalAggregator.value();
1064
+ td = document.createElement("td");
1065
+ td.className = "pvtTotal rowTotal";
1066
+ td.textContent = totalAggregator.format(val);
1067
+ td.setAttribute("data-value", val);
1068
+ if (getClickHandler != null) {
1069
+ td.onclick = getClickHandler(val, rowKey, []);
1070
+ }
1071
+ td.setAttribute("data-for", "row" + i);
1072
+ tr.appendChild(td);
1073
+ }
1074
+ tbody.appendChild(tr);
1075
+ }
1076
+ if (opts.table.colTotals || rowAttrs.length === 0) {
1077
+ tr = document.createElement("tr");
1078
+ if (opts.table.colTotals || rowAttrs.length === 0) {
1079
+ th = document.createElement("th");
1080
+ th.className = "pvtTotalLabel pvtColTotalLabel";
1081
+ th.innerHTML = opts.localeStrings.totals;
1082
+ th.setAttribute("colspan", rowAttrs.length + (colAttrs.length === 0 ? 0 : 1));
1083
+ tr.appendChild(th);
1084
+ }
1085
+ for (j in colKeys) {
1086
+ if (!hasProp.call(colKeys, j)) continue;
1087
+ colKey = colKeys[j];
1088
+ totalAggregator = pivotData.getAggregator([], colKey);
1089
+ val = totalAggregator.value();
1090
+ td = document.createElement("td");
1091
+ td.className = "pvtTotal colTotal";
1092
+ td.textContent = totalAggregator.format(val);
1093
+ td.setAttribute("data-value", val);
1094
+ if (getClickHandler != null) {
1095
+ td.onclick = getClickHandler(val, [], colKey);
1096
+ }
1097
+ td.setAttribute("data-for", "col" + j);
1098
+ tr.appendChild(td);
1099
+ }
1100
+ if (opts.table.rowTotals || colAttrs.length === 0) {
1101
+ totalAggregator = pivotData.getAggregator([], []);
1102
+ val = totalAggregator.value();
1103
+ td = document.createElement("td");
1104
+ td.className = "pvtGrandTotal";
1105
+ td.textContent = totalAggregator.format(val);
1106
+ td.setAttribute("data-value", val);
1107
+ if (getClickHandler != null) {
1108
+ td.onclick = getClickHandler(val, [], []);
1109
+ }
1110
+ tr.appendChild(td);
1111
+ }
1112
+ tbody.appendChild(tr);
1113
+ }
1114
+ result.appendChild(tbody);
1115
+ result.setAttribute("data-numrows", rowKeys.length);
1116
+ result.setAttribute("data-numcols", colKeys.length);
1117
+ return result;
1118
+ };
1119
+
1120
+ /*
1121
+ Pivot Table core: create PivotData object and call Renderer on it
1122
+ */
1123
+ $.fn.pivot = function(input, inputOpts, locale) {
1124
+ var defaults, e, localeDefaults, localeStrings, opts, pivotData, result, x;
1125
+ if (locale == null) {
1126
+ locale = "en";
1127
+ }
1128
+ if (locales[locale] == null) {
1129
+ locale = "en";
1130
+ }
1131
+ defaults = {
1132
+ cols: [],
1133
+ rows: [],
1134
+ vals: [],
1135
+ rowOrder: "key_a_to_z",
1136
+ colOrder: "key_a_to_z",
1137
+ dataClass: PivotData,
1138
+ filter: function() {
1139
+ return true;
1140
+ },
1141
+ aggregator: aggregatorTemplates.count()(),
1142
+ aggregatorName: "Count",
1143
+ sorters: {},
1144
+ derivedAttributes: {},
1145
+ renderer: pivotTableRenderer
1146
+ };
1147
+ localeStrings = $.extend(true, {}, locales.en.localeStrings, locales[locale].localeStrings);
1148
+ localeDefaults = {
1149
+ rendererOptions: {
1150
+ localeStrings: localeStrings
1151
+ },
1152
+ localeStrings: localeStrings
1153
+ };
1154
+ opts = $.extend(true, {}, localeDefaults, $.extend({}, defaults, inputOpts));
1155
+ result = null;
1156
+ try {
1157
+ pivotData = new opts.dataClass(input, opts);
1158
+ try {
1159
+ result = opts.renderer(pivotData, opts.rendererOptions);
1160
+ } catch (error) {
1161
+ e = error;
1162
+ if (typeof console !== "undefined" && console !== null) {
1163
+ console.error(e.stack);
1164
+ }
1165
+ result = $("<span>").html(opts.localeStrings.renderError);
1166
+ }
1167
+ } catch (error) {
1168
+ e = error;
1169
+ if (typeof console !== "undefined" && console !== null) {
1170
+ console.error(e.stack);
1171
+ }
1172
+ result = $("<span>").html(opts.localeStrings.computeError);
1173
+ }
1174
+ x = this[0];
1175
+ while (x.hasChildNodes()) {
1176
+ x.removeChild(x.lastChild);
1177
+ }
1178
+ return this.append(result);
1179
+ };
1180
+
1181
+ /*
1182
+ Pivot Table UI: calls Pivot Table core above with options set by user
1183
+ */
1184
+ $.fn.pivotUI = function(input, inputOpts, overwrite, locale) {
1185
+ var a, aggregator, attr, attrLength, attrValues, c, colOrderArrow, defaults, e, existingOpts, fn1, i, initialRender, l, len1, len2, len3, localeDefaults, localeStrings, materializedInput, n, o, opts, ordering, pivotTable, recordsProcessed, ref, ref1, ref2, ref3, refresh, refreshDelayed, renderer, rendererControl, rowOrderArrow, shownAttributes, shownInAggregators, shownInDragDrop, tr1, tr2, uiTable, unused, unusedAttrsVerticalAutoCutoff, unusedAttrsVerticalAutoOverride, x;
1186
+ if (overwrite == null) {
1187
+ overwrite = false;
1188
+ }
1189
+ if (locale == null) {
1190
+ locale = "en";
1191
+ }
1192
+ if (locales[locale] == null) {
1193
+ locale = "en";
1194
+ }
1195
+ defaults = {
1196
+ derivedAttributes: {},
1197
+ aggregators: locales[locale].aggregators,
1198
+ renderers: locales[locale].renderers,
1199
+ hiddenAttributes: [],
1200
+ hiddenFromAggregators: [],
1201
+ hiddenFromDragDrop: [],
1202
+ menuLimit: 500,
1203
+ cols: [],
1204
+ rows: [],
1205
+ vals: [],
1206
+ rowOrder: "key_a_to_z",
1207
+ colOrder: "key_a_to_z",
1208
+ dataClass: PivotData,
1209
+ exclusions: {},
1210
+ inclusions: {},
1211
+ unusedAttrsVertical: 85,
1212
+ autoSortUnusedAttrs: false,
1213
+ onRefresh: null,
1214
+ showUI: true,
1215
+ filter: function() {
1216
+ return true;
1217
+ },
1218
+ sorters: {}
1219
+ };
1220
+ localeStrings = $.extend(true, {}, locales.en.localeStrings, locales[locale].localeStrings);
1221
+ localeDefaults = {
1222
+ rendererOptions: {
1223
+ localeStrings: localeStrings
1224
+ },
1225
+ localeStrings: localeStrings
1226
+ };
1227
+ existingOpts = this.data("pivotUIOptions");
1228
+ if ((existingOpts == null) || overwrite) {
1229
+ opts = $.extend(true, {}, localeDefaults, $.extend({}, defaults, inputOpts));
1230
+ } else {
1231
+ opts = existingOpts;
1232
+ }
1233
+ try {
1234
+ attrValues = {};
1235
+ materializedInput = [];
1236
+ recordsProcessed = 0;
1237
+ PivotData.forEachRecord(input, opts.derivedAttributes, function(record) {
1238
+ var attr, base, ref, value;
1239
+ if (!opts.filter(record)) {
1240
+ return;
1241
+ }
1242
+ materializedInput.push(record);
1243
+ for (attr in record) {
1244
+ if (!hasProp.call(record, attr)) continue;
1245
+ if (attrValues[attr] == null) {
1246
+ attrValues[attr] = {};
1247
+ if (recordsProcessed > 0) {
1248
+ attrValues[attr]["null"] = recordsProcessed;
1249
+ }
1250
+ }
1251
+ }
1252
+ for (attr in attrValues) {
1253
+ value = (ref = record[attr]) != null ? ref : "null";
1254
+ if ((base = attrValues[attr])[value] == null) {
1255
+ base[value] = 0;
1256
+ }
1257
+ attrValues[attr][value]++;
1258
+ }
1259
+ return recordsProcessed++;
1260
+ });
1261
+ uiTable = $("<table>", {
1262
+ "class": "pvtUi"
1263
+ }).attr("cellpadding", 5);
1264
+ rendererControl = $("<td>").addClass("pvtUiCell");
1265
+ renderer = $("<select>").addClass('pvtRenderer').appendTo(rendererControl).bind("change", function() {
1266
+ return refresh();
1267
+ });
1268
+ ref = opts.renderers;
1269
+ for (x in ref) {
1270
+ if (!hasProp.call(ref, x)) continue;
1271
+ $("<option>").val(x).html(x).appendTo(renderer);
1272
+ }
1273
+ unused = $("<td>").addClass('pvtAxisContainer pvtUnused pvtUiCell');
1274
+ shownAttributes = (function() {
1275
+ var results;
1276
+ results = [];
1277
+ for (a in attrValues) {
1278
+ if (indexOf.call(opts.hiddenAttributes, a) < 0) {
1279
+ results.push(a);
1280
+ }
1281
+ }
1282
+ return results;
1283
+ })();
1284
+ shownInAggregators = (function() {
1285
+ var l, len1, results;
1286
+ results = [];
1287
+ for (l = 0, len1 = shownAttributes.length; l < len1; l++) {
1288
+ c = shownAttributes[l];
1289
+ if (indexOf.call(opts.hiddenFromAggregators, c) < 0) {
1290
+ results.push(c);
1291
+ }
1292
+ }
1293
+ return results;
1294
+ })();
1295
+ shownInDragDrop = (function() {
1296
+ var l, len1, results;
1297
+ results = [];
1298
+ for (l = 0, len1 = shownAttributes.length; l < len1; l++) {
1299
+ c = shownAttributes[l];
1300
+ if (indexOf.call(opts.hiddenFromDragDrop, c) < 0) {
1301
+ results.push(c);
1302
+ }
1303
+ }
1304
+ return results;
1305
+ })();
1306
+ unusedAttrsVerticalAutoOverride = false;
1307
+ if (opts.unusedAttrsVertical === "auto") {
1308
+ unusedAttrsVerticalAutoCutoff = 120;
1309
+ } else {
1310
+ unusedAttrsVerticalAutoCutoff = parseInt(opts.unusedAttrsVertical);
1311
+ }
1312
+ if (!isNaN(unusedAttrsVerticalAutoCutoff)) {
1313
+ attrLength = 0;
1314
+ for (l = 0, len1 = shownInDragDrop.length; l < len1; l++) {
1315
+ a = shownInDragDrop[l];
1316
+ attrLength += a.length;
1317
+ }
1318
+ unusedAttrsVerticalAutoOverride = attrLength > unusedAttrsVerticalAutoCutoff;
1319
+ }
1320
+ if (opts.unusedAttrsVertical === true || unusedAttrsVerticalAutoOverride) {
1321
+ unused.addClass('pvtVertList');
1322
+ } else {
1323
+ unused.addClass('pvtHorizList');
1324
+ }
1325
+ fn1 = function(attr) {
1326
+ var attrElem, checkContainer, closeFilterBox, controls, filterItem, filterItemExcluded, finalButtons, hasExcludedItem, len2, n, placeholder, ref1, sorter, triangleLink, v, value, valueCount, valueList, values;
1327
+ values = (function() {
1328
+ var results;
1329
+ results = [];
1330
+ for (v in attrValues[attr]) {
1331
+ results.push(v);
1332
+ }
1333
+ return results;
1334
+ })();
1335
+ hasExcludedItem = false;
1336
+ valueList = $("<div>").addClass('pvtFilterBox').hide();
1337
+ valueList.append($("<h4>").append($("<span>").text(attr), $("<span>").addClass("count").text("(" + values.length + ")")));
1338
+ if (values.length > opts.menuLimit) {
1339
+ valueList.append($("<p>").html(opts.localeStrings.tooMany));
1340
+ } else {
1341
+ if (values.length > 5) {
1342
+ controls = $("<p>").appendTo(valueList);
1343
+ sorter = getSort(opts.sorters, attr);
1344
+ placeholder = opts.localeStrings.filterResults;
1345
+ $("<input>", {
1346
+ type: "text"
1347
+ }).appendTo(controls).attr({
1348
+ placeholder: placeholder,
1349
+ "class": "pvtSearch"
1350
+ }).bind("keyup", function() {
1351
+ var accept, accept_gen, filter;
1352
+ filter = $(this).val().toLowerCase().trim();
1353
+ accept_gen = function(prefix, accepted) {
1354
+ return function(v) {
1355
+ var real_filter, ref1;
1356
+ real_filter = filter.substring(prefix.length).trim();
1357
+ if (real_filter.length === 0) {
1358
+ return true;
1359
+ }
1360
+ return ref1 = Math.sign(sorter(v.toLowerCase(), real_filter)), indexOf.call(accepted, ref1) >= 0;
1361
+ };
1362
+ };
1363
+ accept = filter.indexOf(">=") === 0 ? accept_gen(">=", [1, 0]) : filter.indexOf("<=") === 0 ? accept_gen("<=", [-1, 0]) : filter.indexOf(">") === 0 ? accept_gen(">", [1]) : filter.indexOf("<") === 0 ? accept_gen("<", [-1]) : filter.indexOf("~") === 0 ? function(v) {
1364
+ if (filter.substring(1).trim().length === 0) {
1365
+ return true;
1366
+ }
1367
+ return v.toLowerCase().match(filter.substring(1));
1368
+ } : function(v) {
1369
+ return v.toLowerCase().indexOf(filter) !== -1;
1370
+ };
1371
+ return valueList.find('.pvtCheckContainer p label span.value').each(function() {
1372
+ if (accept($(this).text())) {
1373
+ return $(this).parent().parent().show();
1374
+ } else {
1375
+ return $(this).parent().parent().hide();
1376
+ }
1377
+ });
1378
+ });
1379
+ controls.append($("<br>"));
1380
+ $("<button>", {
1381
+ type: "button"
1382
+ }).appendTo(controls).html(opts.localeStrings.selectAll).bind("click", function() {
1383
+ valueList.find("input:visible:not(:checked)").prop("checked", true).toggleClass("changed");
1384
+ return false;
1385
+ });
1386
+ $("<button>", {
1387
+ type: "button"
1388
+ }).appendTo(controls).html(opts.localeStrings.selectNone).bind("click", function() {
1389
+ valueList.find("input:visible:checked").prop("checked", false).toggleClass("changed");
1390
+ return false;
1391
+ });
1392
+ }
1393
+ checkContainer = $("<div>").addClass("pvtCheckContainer").appendTo(valueList);
1394
+ ref1 = values.sort(getSort(opts.sorters, attr));
1395
+ for (n = 0, len2 = ref1.length; n < len2; n++) {
1396
+ value = ref1[n];
1397
+ valueCount = attrValues[attr][value];
1398
+ filterItem = $("<label>");
1399
+ filterItemExcluded = false;
1400
+ if (opts.inclusions[attr]) {
1401
+ filterItemExcluded = (indexOf.call(opts.inclusions[attr], value) < 0);
1402
+ } else if (opts.exclusions[attr]) {
1403
+ filterItemExcluded = (indexOf.call(opts.exclusions[attr], value) >= 0);
1404
+ }
1405
+ hasExcludedItem || (hasExcludedItem = filterItemExcluded);
1406
+ $("<input>").attr("type", "checkbox").addClass('pvtFilter').attr("checked", !filterItemExcluded).data("filter", [attr, value]).appendTo(filterItem).bind("change", function() {
1407
+ return $(this).toggleClass("changed");
1408
+ });
1409
+ filterItem.append($("<span>").addClass("value").text(value));
1410
+ filterItem.append($("<span>").addClass("count").text("(" + valueCount + ")"));
1411
+ checkContainer.append($("<p>").append(filterItem));
1412
+ }
1413
+ }
1414
+ closeFilterBox = function() {
1415
+ if (valueList.find("[type='checkbox']").length > valueList.find("[type='checkbox']:checked").length) {
1416
+ attrElem.addClass("pvtFilteredAttribute");
1417
+ } else {
1418
+ attrElem.removeClass("pvtFilteredAttribute");
1419
+ }
1420
+ valueList.find('.pvtSearch').val('');
1421
+ valueList.find('.pvtCheckContainer p').show();
1422
+ return valueList.hide();
1423
+ };
1424
+ finalButtons = $("<p>").appendTo(valueList);
1425
+ if (values.length <= opts.menuLimit) {
1426
+ $("<button>", {
1427
+ type: "button"
1428
+ }).text(opts.localeStrings.apply).appendTo(finalButtons).bind("click", function() {
1429
+ if (valueList.find(".changed").removeClass("changed").length) {
1430
+ refresh();
1431
+ }
1432
+ return closeFilterBox();
1433
+ });
1434
+ }
1435
+ $("<button>", {
1436
+ type: "button"
1437
+ }).text(opts.localeStrings.cancel).appendTo(finalButtons).bind("click", function() {
1438
+ valueList.find(".changed:checked").removeClass("changed").prop("checked", false);
1439
+ valueList.find(".changed:not(:checked)").removeClass("changed").prop("checked", true);
1440
+ return closeFilterBox();
1441
+ });
1442
+ triangleLink = $("<span>").addClass('pvtTriangle').html(" &#x25BE;").bind("click", function(e) {
1443
+ var left, ref2, top;
1444
+ ref2 = $(e.currentTarget).position(), left = ref2.left, top = ref2.top;
1445
+ return valueList.css({
1446
+ left: left + 10,
1447
+ top: top + 10
1448
+ }).show();
1449
+ });
1450
+ attrElem = $("<li>").addClass("axis_" + i).append($("<span>").addClass('pvtAttr').text(attr).data("attrName", attr).append(triangleLink));
1451
+ if (hasExcludedItem) {
1452
+ attrElem.addClass('pvtFilteredAttribute');
1453
+ }
1454
+ return unused.append(attrElem).append(valueList);
1455
+ };
1456
+ for (i in shownInDragDrop) {
1457
+ if (!hasProp.call(shownInDragDrop, i)) continue;
1458
+ attr = shownInDragDrop[i];
1459
+ fn1(attr);
1460
+ }
1461
+ tr1 = $("<tr>").appendTo(uiTable);
1462
+ aggregator = $("<select>").addClass('pvtAggregator').bind("change", function() {
1463
+ return refresh();
1464
+ });
1465
+ ref1 = opts.aggregators;
1466
+ for (x in ref1) {
1467
+ if (!hasProp.call(ref1, x)) continue;
1468
+ aggregator.append($("<option>").val(x).html(x));
1469
+ }
1470
+ ordering = {
1471
+ key_a_to_z: {
1472
+ rowSymbol: "&varr;",
1473
+ colSymbol: "&harr;",
1474
+ next: "value_a_to_z"
1475
+ },
1476
+ value_a_to_z: {
1477
+ rowSymbol: "&darr;",
1478
+ colSymbol: "&rarr;",
1479
+ next: "value_z_to_a"
1480
+ },
1481
+ value_z_to_a: {
1482
+ rowSymbol: "&uarr;",
1483
+ colSymbol: "&larr;",
1484
+ next: "key_a_to_z"
1485
+ }
1486
+ };
1487
+ rowOrderArrow = $("<a>", {
1488
+ role: "button"
1489
+ }).addClass("pvtRowOrder").data("order", opts.rowOrder).html(ordering[opts.rowOrder].rowSymbol).bind("click", function() {
1490
+ $(this).data("order", ordering[$(this).data("order")].next);
1491
+ $(this).html(ordering[$(this).data("order")].rowSymbol);
1492
+ return refresh();
1493
+ });
1494
+ colOrderArrow = $("<a>", {
1495
+ role: "button"
1496
+ }).addClass("pvtColOrder").data("order", opts.colOrder).html(ordering[opts.colOrder].colSymbol).bind("click", function() {
1497
+ $(this).data("order", ordering[$(this).data("order")].next);
1498
+ $(this).html(ordering[$(this).data("order")].colSymbol);
1499
+ return refresh();
1500
+ });
1501
+ $("<td>").addClass('pvtVals pvtUiCell').appendTo(tr1).append(aggregator).append(rowOrderArrow).append(colOrderArrow).append($("<br>"));
1502
+ $("<td>").addClass('pvtAxisContainer pvtHorizList pvtCols pvtUiCell').appendTo(tr1);
1503
+ tr2 = $("<tr>").appendTo(uiTable);
1504
+ tr2.append($("<td>").addClass('pvtAxisContainer pvtRows pvtUiCell').attr("valign", "top"));
1505
+ pivotTable = $("<td>").attr("valign", "top").addClass('pvtRendererArea').appendTo(tr2);
1506
+ if (opts.unusedAttrsVertical === true || unusedAttrsVerticalAutoOverride) {
1507
+ uiTable.find('tr:nth-child(1)').prepend(rendererControl);
1508
+ uiTable.find('tr:nth-child(2)').prepend(unused);
1509
+ } else {
1510
+ uiTable.prepend($("<tr>").append(rendererControl).append(unused));
1511
+ }
1512
+ this.html(uiTable);
1513
+ ref2 = opts.cols;
1514
+ for (n = 0, len2 = ref2.length; n < len2; n++) {
1515
+ x = ref2[n];
1516
+ this.find(".pvtCols").append(this.find(".axis_" + ($.inArray(x, shownInDragDrop))));
1517
+ }
1518
+ ref3 = opts.rows;
1519
+ for (o = 0, len3 = ref3.length; o < len3; o++) {
1520
+ x = ref3[o];
1521
+ this.find(".pvtRows").append(this.find(".axis_" + ($.inArray(x, shownInDragDrop))));
1522
+ }
1523
+ if (opts.aggregatorName != null) {
1524
+ this.find(".pvtAggregator").val(opts.aggregatorName);
1525
+ }
1526
+ if (opts.rendererName != null) {
1527
+ this.find(".pvtRenderer").val(opts.rendererName);
1528
+ }
1529
+ if (!opts.showUI) {
1530
+ this.find(".pvtUiCell").hide();
1531
+ }
1532
+ initialRender = true;
1533
+ refreshDelayed = (function(_this) {
1534
+ return function() {
1535
+ var exclusions, inclusions, len4, newDropdown, numInputsToProcess, pivotUIOptions, pvtVals, ref4, ref5, subopts, t, u, unusedAttrsContainer, vals;
1536
+ subopts = {
1537
+ derivedAttributes: opts.derivedAttributes,
1538
+ localeStrings: opts.localeStrings,
1539
+ rendererOptions: opts.rendererOptions,
1540
+ sorters: opts.sorters,
1541
+ cols: [],
1542
+ rows: [],
1543
+ dataClass: opts.dataClass
1544
+ };
1545
+ numInputsToProcess = (ref4 = opts.aggregators[aggregator.val()]([])().numInputs) != null ? ref4 : 0;
1546
+ vals = [];
1547
+ _this.find(".pvtRows li span.pvtAttr").each(function() {
1548
+ return subopts.rows.push($(this).data("attrName"));
1549
+ });
1550
+ _this.find(".pvtCols li span.pvtAttr").each(function() {
1551
+ return subopts.cols.push($(this).data("attrName"));
1552
+ });
1553
+ _this.find(".pvtVals select.pvtAttrDropdown").each(function() {
1554
+ if (numInputsToProcess === 0) {
1555
+ return $(this).remove();
1556
+ } else {
1557
+ numInputsToProcess--;
1558
+ if ($(this).val() !== "") {
1559
+ return vals.push($(this).val());
1560
+ }
1561
+ }
1562
+ });
1563
+ if (numInputsToProcess !== 0) {
1564
+ pvtVals = _this.find(".pvtVals");
1565
+ for (x = t = 0, ref5 = numInputsToProcess; 0 <= ref5 ? t < ref5 : t > ref5; x = 0 <= ref5 ? ++t : --t) {
1566
+ newDropdown = $("<select>").addClass('pvtAttrDropdown').append($("<option>")).bind("change", function() {
1567
+ return refresh();
1568
+ });
1569
+ for (u = 0, len4 = shownInAggregators.length; u < len4; u++) {
1570
+ attr = shownInAggregators[u];
1571
+ newDropdown.append($("<option>").val(attr).text(attr));
1572
+ }
1573
+ pvtVals.append(newDropdown);
1574
+ }
1575
+ }
1576
+ if (initialRender) {
1577
+ vals = opts.vals;
1578
+ i = 0;
1579
+ _this.find(".pvtVals select.pvtAttrDropdown").each(function() {
1580
+ $(this).val(vals[i]);
1581
+ return i++;
1582
+ });
1583
+ initialRender = false;
1584
+ }
1585
+ subopts.aggregatorName = aggregator.val();
1586
+ subopts.vals = vals;
1587
+ subopts.aggregator = opts.aggregators[aggregator.val()](vals);
1588
+ subopts.renderer = opts.renderers[renderer.val()];
1589
+ subopts.rowOrder = rowOrderArrow.data("order");
1590
+ subopts.colOrder = colOrderArrow.data("order");
1591
+ exclusions = {};
1592
+ _this.find('input.pvtFilter').not(':checked').each(function() {
1593
+ var filter;
1594
+ filter = $(this).data("filter");
1595
+ if (exclusions[filter[0]] != null) {
1596
+ return exclusions[filter[0]].push(filter[1]);
1597
+ } else {
1598
+ return exclusions[filter[0]] = [filter[1]];
1599
+ }
1600
+ });
1601
+ inclusions = {};
1602
+ _this.find('input.pvtFilter:checked').each(function() {
1603
+ var filter;
1604
+ filter = $(this).data("filter");
1605
+ if (exclusions[filter[0]] != null) {
1606
+ if (inclusions[filter[0]] != null) {
1607
+ return inclusions[filter[0]].push(filter[1]);
1608
+ } else {
1609
+ return inclusions[filter[0]] = [filter[1]];
1610
+ }
1611
+ }
1612
+ });
1613
+ subopts.filter = function(record) {
1614
+ var excludedItems, k, ref6, ref7;
1615
+ if (!opts.filter(record)) {
1616
+ return false;
1617
+ }
1618
+ for (k in exclusions) {
1619
+ excludedItems = exclusions[k];
1620
+ if (ref6 = "" + ((ref7 = record[k]) != null ? ref7 : 'null'), indexOf.call(excludedItems, ref6) >= 0) {
1621
+ return false;
1622
+ }
1623
+ }
1624
+ return true;
1625
+ };
1626
+ pivotTable.pivot(materializedInput, subopts);
1627
+ pivotUIOptions = $.extend({}, opts, {
1628
+ cols: subopts.cols,
1629
+ rows: subopts.rows,
1630
+ colOrder: subopts.colOrder,
1631
+ rowOrder: subopts.rowOrder,
1632
+ vals: vals,
1633
+ exclusions: exclusions,
1634
+ inclusions: inclusions,
1635
+ inclusionsInfo: inclusions,
1636
+ aggregatorName: aggregator.val(),
1637
+ rendererName: renderer.val()
1638
+ });
1639
+ _this.data("pivotUIOptions", pivotUIOptions);
1640
+ if (opts.autoSortUnusedAttrs) {
1641
+ unusedAttrsContainer = _this.find("td.pvtUnused.pvtAxisContainer");
1642
+ $(unusedAttrsContainer).children("li").sort(function(a, b) {
1643
+ return naturalSort($(a).text(), $(b).text());
1644
+ }).appendTo(unusedAttrsContainer);
1645
+ }
1646
+ pivotTable.css("opacity", 1);
1647
+ if (opts.onRefresh != null) {
1648
+ return opts.onRefresh(pivotUIOptions);
1649
+ }
1650
+ };
1651
+ })(this);
1652
+ refresh = (function(_this) {
1653
+ return function() {
1654
+ pivotTable.css("opacity", 0.5);
1655
+ return setTimeout(refreshDelayed, 10);
1656
+ };
1657
+ })(this);
1658
+ refresh();
1659
+ this.find(".pvtAxisContainer").sortable({
1660
+ update: function(e, ui) {
1661
+ if (ui.sender == null) {
1662
+ return refresh();
1663
+ }
1664
+ },
1665
+ connectWith: this.find(".pvtAxisContainer"),
1666
+ items: 'li',
1667
+ placeholder: 'pvtPlaceholder'
1668
+ });
1669
+ } catch (error) {
1670
+ e = error;
1671
+ if (typeof console !== "undefined" && console !== null) {
1672
+ console.error(e.stack);
1673
+ }
1674
+ this.html(opts.localeStrings.uiRenderError);
1675
+ }
1676
+ return this;
1677
+ };
1678
+
1679
+ /*
1680
+ Heatmap post-processing
1681
+ */
1682
+ $.fn.heatmap = function(scope, opts) {
1683
+ var colorScaleGenerator, heatmapper, i, j, l, n, numCols, numRows, ref, ref1, ref2;
1684
+ if (scope == null) {
1685
+ scope = "heatmap";
1686
+ }
1687
+ numRows = this.data("numrows");
1688
+ numCols = this.data("numcols");
1689
+ colorScaleGenerator = opts != null ? (ref = opts.heatmap) != null ? ref.colorScaleGenerator : void 0 : void 0;
1690
+ if (colorScaleGenerator == null) {
1691
+ colorScaleGenerator = function(values) {
1692
+ var max, min;
1693
+ min = Math.min.apply(Math, values);
1694
+ max = Math.max.apply(Math, values);
1695
+ return function(x) {
1696
+ var nonRed;
1697
+ nonRed = 255 - Math.round(255 * (x - min) / (max - min));
1698
+ return "rgb(255," + nonRed + "," + nonRed + ")";
1699
+ };
1700
+ };
1701
+ }
1702
+ heatmapper = (function(_this) {
1703
+ return function(scope) {
1704
+ var colorScale, forEachCell, values;
1705
+ forEachCell = function(f) {
1706
+ return _this.find(scope).each(function() {
1707
+ var x;
1708
+ x = $(this).data("value");
1709
+ if ((x != null) && isFinite(x)) {
1710
+ return f(x, $(this));
1711
+ }
1712
+ });
1713
+ };
1714
+ values = [];
1715
+ forEachCell(function(x) {
1716
+ return values.push(x);
1717
+ });
1718
+ colorScale = colorScaleGenerator(values);
1719
+ return forEachCell(function(x, elem) {
1720
+ return elem.css("background-color", colorScale(x));
1721
+ });
1722
+ };
1723
+ })(this);
1724
+ switch (scope) {
1725
+ case "heatmap":
1726
+ heatmapper(".pvtVal");
1727
+ break;
1728
+ case "rowheatmap":
1729
+ for (i = l = 0, ref1 = numRows; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
1730
+ heatmapper(".pvtVal.row" + i);
1731
+ }
1732
+ break;
1733
+ case "colheatmap":
1734
+ for (j = n = 0, ref2 = numCols; 0 <= ref2 ? n < ref2 : n > ref2; j = 0 <= ref2 ? ++n : --n) {
1735
+ heatmapper(".pvtVal.col" + j);
1736
+ }
1737
+ }
1738
+ heatmapper(".pvtTotal.rowTotal");
1739
+ heatmapper(".pvtTotal.colTotal");
1740
+ return this;
1741
+ };
1742
+
1743
+ /*
1744
+ Barchart post-processing
1745
+ */
1746
+ return $.fn.barchart = function(opts) {
1747
+ var barcharter, i, l, numCols, numRows, ref;
1748
+ numRows = this.data("numrows");
1749
+ numCols = this.data("numcols");
1750
+ barcharter = (function(_this) {
1751
+ return function(scope) {
1752
+ var forEachCell, max, min, range, scaler, values;
1753
+ forEachCell = function(f) {
1754
+ return _this.find(scope).each(function() {
1755
+ var x;
1756
+ x = $(this).data("value");
1757
+ if ((x != null) && isFinite(x)) {
1758
+ return f(x, $(this));
1759
+ }
1760
+ });
1761
+ };
1762
+ values = [];
1763
+ forEachCell(function(x) {
1764
+ return values.push(x);
1765
+ });
1766
+ max = Math.max.apply(Math, values);
1767
+ if (max < 0) {
1768
+ max = 0;
1769
+ }
1770
+ range = max;
1771
+ min = Math.min.apply(Math, values);
1772
+ if (min < 0) {
1773
+ range = max - min;
1774
+ }
1775
+ scaler = function(x) {
1776
+ return 100 * x / (1.4 * range);
1777
+ };
1778
+ return forEachCell(function(x, elem) {
1779
+ var bBase, bgColor, text, wrapper;
1780
+ text = elem.text();
1781
+ wrapper = $("<div>").css({
1782
+ "position": "relative",
1783
+ "height": "55px"
1784
+ });
1785
+ bgColor = "gray";
1786
+ bBase = 0;
1787
+ if (min < 0) {
1788
+ bBase = scaler(-min);
1789
+ }
1790
+ if (x < 0) {
1791
+ bBase += scaler(x);
1792
+ bgColor = "darkred";
1793
+ x = -x;
1794
+ }
1795
+ wrapper.append($("<div>").css({
1796
+ "position": "absolute",
1797
+ "bottom": bBase + "%",
1798
+ "left": 0,
1799
+ "right": 0,
1800
+ "height": scaler(x) + "%",
1801
+ "background-color": bgColor
1802
+ }));
1803
+ wrapper.append($("<div>").text(text).css({
1804
+ "position": "relative",
1805
+ "padding-left": "5px",
1806
+ "padding-right": "5px"
1807
+ }));
1808
+ return elem.css({
1809
+ "padding": 0,
1810
+ "padding-top": "5px",
1811
+ "text-align": "center"
1812
+ }).html(wrapper);
1813
+ });
1814
+ };
1815
+ })(this);
1816
+ for (i = l = 0, ref = numRows; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) {
1817
+ barcharter(".pvtVal.row" + i);
1818
+ }
1819
+ barcharter(".pvtTotal.colTotal");
1820
+ return this;
1821
+ };
1822
+ };
1823
+
1824
1824
  module.exports = initPivotTable;