@datarailsshared/dr_renderer 1.2.46 → 1.2.47-beta

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