datavis-glide 4.0.0-PRE.0

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.
Files changed (62) hide show
  1. package/LICENSE +45 -0
  2. package/README.md +129 -0
  3. package/datavis.js +101 -0
  4. package/dist/wcdatavis.css +1957 -0
  5. package/dist/wcdatavis.min.js +1 -0
  6. package/global-jquery.js +4 -0
  7. package/ie-fixes.js +13 -0
  8. package/index.js +70 -0
  9. package/meteor.js +1 -0
  10. package/package.json +102 -0
  11. package/src/flags.js +6 -0
  12. package/src/graph.js +1079 -0
  13. package/src/graph_renderer.js +85 -0
  14. package/src/grid.js +2777 -0
  15. package/src/grid_control.js +1957 -0
  16. package/src/grid_filter.js +1073 -0
  17. package/src/grid_renderer.js +276 -0
  18. package/src/group_fun_win.js +121 -0
  19. package/src/lang/en-US.js +188 -0
  20. package/src/lang/es-MX.js +188 -0
  21. package/src/lang/fr-FR.js +188 -0
  22. package/src/lang/id-ID.js +188 -0
  23. package/src/lang/nl-NL.js +188 -0
  24. package/src/lang/pt-BR.js +188 -0
  25. package/src/lang/ru-RU.js +188 -0
  26. package/src/lang/th-TH.js +188 -0
  27. package/src/lang/vi-VN.js +188 -0
  28. package/src/lang/zh-Hans-CN.js +188 -0
  29. package/src/operations_palette.js +176 -0
  30. package/src/prefs_modules.js +132 -0
  31. package/src/reg/graph_renderer.js +17 -0
  32. package/src/renderers/graph/chartjs.js +457 -0
  33. package/src/renderers/graph/google.js +584 -0
  34. package/src/renderers/graph/jit.js +61 -0
  35. package/src/renderers/graph/svelte-gantt.js +168 -0
  36. package/src/renderers/grid/dummy.js +79 -0
  37. package/src/renderers/grid/handlebars.js +217 -0
  38. package/src/renderers/grid/squirrelly.js +215 -0
  39. package/src/renderers/grid/table/group_detail.js +1404 -0
  40. package/src/renderers/grid/table/group_summary.js +380 -0
  41. package/src/renderers/grid/table/pivot.js +915 -0
  42. package/src/renderers/grid/table/plain.js +1592 -0
  43. package/src/renderers/grid/table.js +2510 -0
  44. package/src/trans.js +101 -0
  45. package/src/ui/collapsible.js +234 -0
  46. package/src/ui/filters/date.js +283 -0
  47. package/src/ui/grid_filter.js +398 -0
  48. package/src/ui/popup_menu.js +224 -0
  49. package/src/ui/popup_window.js +572 -0
  50. package/src/ui/slider.js +156 -0
  51. package/src/ui/tabs.js +202 -0
  52. package/src/ui/templates.js +131 -0
  53. package/src/ui/toolbar.js +63 -0
  54. package/src/ui/toolbars/grid.js +873 -0
  55. package/src/ui/windows/col_config.js +341 -0
  56. package/src/ui/windows/debug.js +164 -0
  57. package/src/ui/windows/grid_table_opts.js +139 -0
  58. package/src/util/handlebars.js +158 -0
  59. package/src/util/jquery.js +630 -0
  60. package/src/util/misc.js +1058 -0
  61. package/src/util/squirrelly.js +155 -0
  62. package/wcdatavis.css +1601 -0
@@ -0,0 +1,915 @@
1
+ // Imports {{{1
2
+
3
+ import _ from 'underscore';
4
+ import jQuery from 'jquery';
5
+
6
+ import {
7
+ format,
8
+ getElement,
9
+ getProp,
10
+ getPropDef,
11
+ isElement,
12
+ makeSubclass,
13
+ mixinLogging,
14
+ setPropDef,
15
+ setTableCell,
16
+ setElement,
17
+ } from '../../../util/misc.js';
18
+
19
+ import {ComputedView, GROUP_FUNCTION_REGISTRY} from 'datavis-ace';
20
+ import {GridRenderer} from '../../../grid_renderer.js';
21
+
22
+ import handlebarsUtil from '../../../util/handlebars.js';
23
+ import GridTable from '../table.js';
24
+
25
+ // GridTablePivot {{{1
26
+ // Constructor {{{2
27
+
28
+ /**
29
+ * A grid table used for showing data that's been pivotted by the view.
30
+ *
31
+ * @class
32
+ * @extends GridTable
33
+ */
34
+
35
+ var GridTablePivot = makeSubclass('GridTablePivot', GridTable, function (grid, defn, view, features, opts, timing, id) {
36
+ var self = this;
37
+
38
+ self.super['GridTable'].ctor.apply(self, arguments);
39
+
40
+ self.features.limit = false;
41
+ self.features.footer = false;
42
+ self.features.rowSelect = false;
43
+ self.features.columnResize = false;
44
+ self.features.columnReorder = false;
45
+
46
+ self.logDebug(self.makeLogTag() + ' Constructing grid table; features = %O', features);
47
+
48
+ setPropDef(['rowVals', 'cells', 'groupAggregates', 'addCols'], self.opts, 'displayOrder');
49
+ });
50
+
51
+ mixinLogging(GridTablePivot);
52
+
53
+ // #canRender {{{2
54
+
55
+ /**
56
+ * Responds whether or not this grid table can render the type of data requested.
57
+ *
58
+ * @param {string} what
59
+ * The kind of data the caller wants us to show. Must be one of: plain, group, or pivot.
60
+ *
61
+ * @return {boolean}
62
+ * True if this grid table can render that kind of data, false if it can't.
63
+ */
64
+
65
+ GridTablePivot.prototype.canRender = function (what) {
66
+ return ['pivot'].indexOf(what) >= 0;
67
+ };
68
+
69
+ // #drawHeader {{{2
70
+
71
+ GridTablePivot.prototype.drawHeader = function (columns, data, typeInfo, opts) {
72
+ var self = this,
73
+ aggInfo,
74
+ tr,
75
+ span,
76
+ headingThControls,
77
+ headingThContainer,
78
+ th;
79
+
80
+ // +---------------------------+--------------------------------------+-----------+
81
+ // | | COLVAL 1.1 | COLVAL 1.2 | |
82
+ // +---------------------------+------------+------------+------------+-----------+
83
+ // | | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | |
84
+ // +-------------+-------------+------------+------------+------------+-----------+
85
+ // | GROUP FIELD | GROUP FIELD | | GROUP AGG |
86
+ // +-------------+-------------+--------------------------------------+-----------+
87
+ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
88
+
89
+ var displayRowVals = function (tr) {
90
+ _.each(data.groupFields, function (field, fieldIdx) {
91
+ var fcc = self.colConfig.get(field) || {};
92
+ span = jQuery('<span>').addClass('wcdv_heading_title').text(fcc.displayText || field);
93
+ self.csv.addCol(fcc.displayText || field);
94
+
95
+ headingThControls = jQuery('<div>');
96
+
97
+ headingThContainer = jQuery('<div>')
98
+ .addClass('wcdv_heading_container')
99
+ .append(span, headingThControls);
100
+
101
+ th = jQuery('<th>', { scope: 'col' })
102
+ .dvAttr({
103
+ 'gfi': fieldIdx,
104
+ 'field': field,
105
+ 'draggable-origin': 'GRID_TABLE_HEADER'
106
+ })
107
+ .append(headingThContainer)
108
+ ._makeDraggableField();
109
+
110
+ self._addSortingToHeader(data, 'vertical', {groupFieldIndex: fieldIdx}, headingThControls.get(0), getPropDef([], data, 'agg', 'info', 'cell'));
111
+
112
+ self.setCss(th, field);
113
+
114
+ self.ui.thMap[field] = th;
115
+ tr.append(th);
116
+ });
117
+ };
118
+
119
+ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
120
+ // +---------------------------+--------------------------------------+-----------+
121
+ // | | COLVAL 1.1 | COLVAL 1.2 | |
122
+ // +---------------------------+------------+------------+------------+-----------+
123
+ // | | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | |
124
+ // +-------------+-------------+------------+------------+------------+-----------+
125
+ // | GROUP FIELD | GROUP FIELD | | GROUP AGG |
126
+ // +-------------+-------------+--------------------------------------+-----------+
127
+
128
+ var displayRowVals_padding = function (tr) {
129
+ if (data.groupFields.length > 1) {
130
+ tr.append(jQuery('<th>', { colspan: data.groupFields.length - 1 }));
131
+ for (var i = 0; i < data.groupFields.length - 1; i += 1) {
132
+ self.csv.addCol('');
133
+ }
134
+ }
135
+ };
136
+
137
+ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
138
+ // +-------------+-------------+--------------------------------------+-----------+
139
+ // | | PIVOT FIELD | COLVAL 1.1 | COLVAL 1.2 | |
140
+ // +-------------+-------------+------------+------------+------------+-----------+
141
+ // | | PIVOT FIELD | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | |
142
+ // +-------------+-------------+------------+------------+------------+-----------+
143
+ // | GROUP FIELD | GROUP FIELD | | GROUP AGG |
144
+ // +-------------+-------------+--------------------------------------+-----------+
145
+
146
+ var displayCells = function (tr, pivotFieldIdx, displayOrderIndex) {
147
+ var colVal, colValIndex;
148
+ var ai = self._getAggInfo(data);
149
+ var df = self._getDisplayFormat();
150
+ // Indicates that we're on the last pivot field, i.e. the last row of the table header.
151
+ var isLastPivotField = pivotFieldIdx === data.pivotFields.length - 1;
152
+ var pivotField = data.pivotFields[pivotFieldIdx];
153
+
154
+ var fcc = self.colConfig.get(pivotField) || {};
155
+ var pivotSpec = data.pivotSpec[pivotFieldIdx];
156
+ var fti = self.typeInfo.get(pivotField);
157
+
158
+ if (pivotSpec.fun != null) {
159
+ fti = {
160
+ type: GROUP_FUNCTION_REGISTRY.get(pivotSpec.fun).resultType
161
+ };
162
+ }
163
+
164
+ // ↓↓↓↓↓↓↓↓↓↓↓↓↓
165
+ // +-------------+-------------+--------------------------------------+-----------+
166
+ // | | PIVOT FIELD | COLVAL 1.1 | COLVAL 1.2 | |
167
+ // +-------------+-------------+------------+------------+------------+-----------+
168
+
169
+ var span = jQuery('<span>').addClass('wcdv_heading_title').text(fcc.displayText || pivotField);
170
+ self.csv.addCol(fcc.displayText || pivotField);
171
+
172
+ var headingThControls = jQuery('<div>');
173
+
174
+ var headingThContainer = jQuery('<div>')
175
+ .addClass('wcdv_heading_container')
176
+ .append(span, headingThControls);
177
+
178
+ var th = jQuery('<th>', { scope: 'col' })
179
+ .dvAttr({
180
+ 'field': pivotField,
181
+ 'draggable-origin': 'GRID_TABLE_HEADER'
182
+ })
183
+ .append(headingThContainer)
184
+ ._makeDraggableField();
185
+
186
+ self._addSortingToHeader(data, 'horizontal', {pivotFieldIndex: pivotFieldIdx}, headingThControls.get(0), getPropDef([], data, 'agg', 'info', 'cell'));
187
+
188
+ self.setCss(th, pivotField);
189
+
190
+ self.ui.thMap[pivotField] = th;
191
+ tr.append(th);
192
+
193
+
194
+ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
195
+ // +-------------+-------------+--------------------------------------+-----------+
196
+ // | | PIVOT FIELD | COLVAL 1.1 | COLVAL 1.2 | |
197
+ // +-------------+-------------+------------+------------+------------+-----------+
198
+
199
+ // Create headers for the fields that we've pivotted by. The headers are the column values for
200
+ // those fields.
201
+ //
202
+ // +--------------------------------------------------+----------------+
203
+ // | PIVOT COLVAL 1 | PIVOT COLVAL 2 | < PIVOT FIELD #1
204
+ // +----------------+----------------+----------------+----------------+
205
+ // | PIVOT COLVAL A | PIVOT COLVAL B | PIVOT COLVAL C | PIVOT COLVAL A | < PIVOT FIELD #2
206
+ // +----------------+----------------+----------------+----------------+
207
+ //
208
+ // Col Vals = [[1,A], [1,B], [2,A]]
209
+ //
210
+ // When rendering the headers for Pivot Field #1, we go through the col vals and find that "1"
211
+ // is repeated three times. We don't make a cell for each one, instead we just increment
212
+ // lastColValCount. When the col val changes to "2", we set the colspan on the previous cell to
213
+ // be however many of that col val we found.
214
+
215
+ var lastColVal = null;
216
+ var lastColValCount = 0;
217
+
218
+ for (colValIndex = 0; colValIndex < data.colVals.length; colValIndex += 1) {
219
+ colVal = data.colVals[colValIndex][pivotFieldIdx];
220
+ colVal = format(self.colConfig.get(pivotField), fti, colVal);
221
+
222
+ if (colVal !== lastColVal || isLastPivotField) {
223
+ if (lastColVal !== null) {
224
+ // The we've hit a different colVal so count up how many of the last one we had to
225
+ // determine the column span. In the above example, there are three "Kennedy" and two
226
+ // "Roosevelt" so those are the colspans that we would set.
227
+
228
+ var colSpan = lastColValCount;
229
+
230
+ if ((df.cell.length || ai.cell.length) >= 2) {
231
+ colSpan *= ai.cell.length;
232
+ }
233
+
234
+ th.attr('colspan', colSpan);
235
+ tr.append(th);
236
+
237
+ for (var i = 0; i < colSpan - 1; i += 1) {
238
+ self.csv.addCol('');
239
+ }
240
+ }
241
+
242
+ // Update the tracking information and reset the counter to one.
243
+
244
+ lastColVal = colVal;
245
+ lastColValCount = 1;
246
+
247
+ span = jQuery('<span>').addClass('wcdv_heading_title');
248
+ setElement(span, colVal, {
249
+ field: pivotField,
250
+ colConfig: fcc,
251
+ typeInfo: fti
252
+ });
253
+ self.csv.addCol(span.text());
254
+
255
+ headingThControls = jQuery('<div>');
256
+
257
+ headingThContainer = jQuery('<div>')
258
+ .addClass('wcdv_heading_container')
259
+ .append(span, headingThControls);
260
+
261
+ th = jQuery('<th>', { scope: 'col' })
262
+ .dvAttr('cvi', colValIndex)
263
+ .append(headingThContainer);
264
+
265
+ self.setCss(th, colVal);
266
+
267
+ // We only allow sorting on the final
268
+
269
+ if (isLastPivotField) {
270
+ self._addSortingToHeader(data, 'vertical', {colVal: data.colVals[colValIndex], aggNum: 0}, headingThControls.get(0), getPropDef([], data, 'agg', 'info', 'cell'));
271
+ }
272
+
273
+ if ((df.cell.length || ai.cell.length) === 1) {
274
+ aggInfo = data.agg.info.cell[0];
275
+ self.setAlignment(th, aggInfo.colConfig[0], aggInfo.typeInfo[0], aggInfo.instance.getType());
276
+ }
277
+ else if ((df.cell.length || ai.cell.length) > 1) {
278
+ self.setAlignment(th, null, null, null, 'center');
279
+ }
280
+
281
+ if (self.opts.drawInternalBorders || (df.cell.length || ai.cell.length) > 1) {
282
+ th.addClass('wcdv_pivot_colval_boundary');
283
+ }
284
+ }
285
+ else {
286
+ lastColValCount += 1;
287
+ }
288
+ }
289
+
290
+ // Same logic as when the colVal changes.
291
+
292
+ var colSpan = lastColValCount;
293
+
294
+ if ((df.cell.length || ai.cell.length) >= 2) {
295
+ colSpan *= (df.cell.length || ai.cell.length);
296
+ }
297
+
298
+ if (th != null) {
299
+ th.attr('colspan', colSpan);
300
+ tr.append(th);
301
+ }
302
+
303
+ for (i = 0; i < colSpan - 1; i += 1) {
304
+ self.csv.addCol('');
305
+ }
306
+ };
307
+
308
+ // +---------------------------+--------------------------------------+-----------+
309
+ // | | COLVAL 1.1 | COLVAL 1.2 | |
310
+ // +---------------------------+------------+------------+------------+-----------+
311
+ // | | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | |
312
+ // +-------------+-------------+------------+------------+------------+-----------+
313
+ // | GROUP FIELD | GROUP FIELD | // // // // // // // // // | GROUP AGG |
314
+ // +-------------+-------------+--------------------------------------+-----------+
315
+ // ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
316
+
317
+ var displayCells_padding = function (tr) {
318
+ var ai = self._getAggInfo(data);
319
+ var df = self._getDisplayFormat();
320
+
321
+ var numCols = df.cell.length || ai.cell.length;
322
+
323
+ var hr = jQuery('<hr>', {
324
+ class: 'wcdv_hr_gradient'
325
+ });
326
+ var div = jQuery('<div>&nbsp;</div>');
327
+ var th = jQuery('<th>', {
328
+ class: 'wcdv_pivot_colval_boundary wcdv_cell_empty',
329
+ colspan: data.colVals.length * Math.max(numCols, 1)
330
+ });
331
+ div.appendTo(th);
332
+ th.appendTo(tr);
333
+ for (var i = 0; i < data.colVals.length * Math.max(numCols, 1); i += 1) {
334
+ self.csv.addCol('');
335
+ }
336
+ };
337
+
338
+ // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
339
+ // +---------------------------+--------------------------------------+----------------------+
340
+ // | | COLVAL 1.1 | COLVAL 1.2 | |
341
+ // +---------------------------+------------+------------+------------+----------------------+
342
+ // | | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | |
343
+ // +-------------+-------------+------------+------------+------------+-----------+----------+
344
+ // | GROUP FIELD | GROUP FIELD | // // // // // // // // // | GROUP AGG | ADD COLS |
345
+ // +-------------+-------------+--------------------------------------+-----------+----------+
346
+
347
+ var displayGroupAggregates_padding = function (tr, displayOrderIndex, displayOrderMax) {
348
+ var ai = self._getAggInfo(data);
349
+
350
+ var numCols = ai.group.length + getPropDef(0, self.opts, 'addCols', 'length');
351
+
352
+ if (numCols > 0) {
353
+ var th = jQuery('<th>', { colspan: numCols });
354
+ if (displayOrderIndex > 0) {
355
+ th.addClass('wcdv_bld'); // border-left: double
356
+ }
357
+ if (displayOrderIndex < displayOrderMax - 1) {
358
+ th.addClass('wcdv_brd'); // border-right: double
359
+ }
360
+ tr.append(th);
361
+ }
362
+ };
363
+
364
+ // +---------------------------+--------------------------------------+-----------+
365
+ // | | COLVAL 1.1 | COLVAL 1.2 | |
366
+ // +---------------------------+------------+------------+------------+-----------+
367
+ // | | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | |
368
+ // +-------------+-------------+------------+------------+------------+-----------+
369
+ // | GROUP FIELD | GROUP FIELD | // // // // // // // // // | GROUP AGG |
370
+ // +-------------+-------------+--------------------------------------+-----------+
371
+ // ↑↑↑↑↑↑↑↑↑↑↑
372
+
373
+ var displayGroupAggregates = function (tr, displayOrderIndex, displayOrderMax) {
374
+ self.drawHeader_aggregates(data, tr, displayOrderIndex, displayOrderMax);
375
+ self.drawHeader_addCols(tr, typeInfo, opts);
376
+ };
377
+
378
+ // +---------------------------+--------------------------------------+-----------+
379
+ // | | COLVAL 1.1 | COLVAL 1.2 | | ←---
380
+ // +---------------------------+------------+------------+------------+-----------+ ←---
381
+ // | | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | | ←---
382
+ // +-------------+-------------+------------+------------+------------+-----------+
383
+ // | GROUP FIELD | GROUP FIELD | // // // // // // // // // | GROUP AGG |
384
+ // +-------------+-------------+--------------------------------------+-----------+
385
+
386
+ for (var pivotFieldIdx = 0; pivotFieldIdx < data.pivotFields.length; pivotFieldIdx += 1) {
387
+ self.csv.addRow();
388
+ tr = jQuery('<tr>')
389
+ .dvAttr('pfi', pivotFieldIdx);
390
+ _.each(self.opts.displayOrder, function (what, displayOrderIndex) {
391
+ if (typeof what === 'string') {
392
+ switch (what) {
393
+ case 'rowVals':
394
+ displayRowVals_padding(tr);
395
+ break;
396
+ case 'cells':
397
+ displayCells(tr, pivotFieldIdx);
398
+ break;
399
+ case 'groupAggregates':
400
+ displayGroupAggregates_padding(tr, displayOrderIndex, self.opts.displayOrder.length);
401
+ break;
402
+ }
403
+ }
404
+ });
405
+ tr.appendTo(self.ui.thead);
406
+ }
407
+
408
+ // +---------------------------+--------------------------------------+-----------+
409
+ // | | COLVAL 1.1 | COLVAL 1.2 | |
410
+ // +---------------------------+------------+------------+------------+-----------+
411
+ // | | COLVAL 2.1 | COLVAL 2.2 | COLVAL 2.1 | |
412
+ // +-------------+-------------+------------+------------+------------+-----------+
413
+ // | GROUP FIELD | GROUP FIELD | // // // // // // // // // | GROUP AGG | ←---
414
+ // +-------------+-------------+--------------------------------------+-----------+
415
+
416
+ self.csv.addRow();
417
+ tr = jQuery('<tr>');
418
+ _.each(self.opts.displayOrder, function (what, displayOrderIndex) {
419
+ if (typeof what === 'string') {
420
+ switch (what) {
421
+ case 'rowVals':
422
+ displayRowVals(tr);
423
+ break;
424
+ case 'cells':
425
+ displayCells_padding(tr);
426
+ break;
427
+ case 'groupAggregates':
428
+ displayGroupAggregates(tr, displayOrderIndex, self.opts.displayOrder.length);
429
+ break;
430
+ }
431
+ }
432
+ });
433
+ tr.appendTo(self.ui.thead);
434
+ };
435
+
436
+ // #drawBody {{{2
437
+
438
+ GridTablePivot.prototype.drawBody = function (data, typeInfo, columns, cont, opts) {
439
+ var self = this;
440
+
441
+ var aggType
442
+ , aggInfo;
443
+
444
+ opts = opts || {};
445
+ opts.pivotConfig = opts.pivotConfig || {};
446
+
447
+ var ai = self._getAggInfo(data);
448
+ var df = self._getDisplayFormat();
449
+
450
+ if (data.groupFields.length === 0) {
451
+ if (typeof cont === 'function') {
452
+ return cont();
453
+ }
454
+ else {
455
+ return;
456
+ }
457
+ }
458
+
459
+ self._setupFullValueWin(data);
460
+
461
+ // Setup the handlebars environment to reference our data.
462
+
463
+ var handlebarsEnv = handlebarsUtil.makeEnv();
464
+ handlebarsUtil.addHelpers(handlebarsEnv, self.data);
465
+
466
+ // Compile all templates; if there's an error then the template can still be used, it just
467
+ // produces the error message instead of doing something useful.
468
+
469
+ var templates = {};
470
+
471
+ _.each(df, function (tmplStrs, type) {
472
+ templates[type] = _.map(tmplStrs, function (str) {
473
+ var t;
474
+ try {
475
+ t = handlebarsEnv.compile(str);
476
+ }
477
+ catch (e) {
478
+ t = function () {
479
+ return e.message;
480
+ };
481
+ }
482
+ return t;
483
+ });
484
+ });
485
+
486
+ // ===========================================================================
487
+ // DATA AND GROUP AGGREGATES
488
+ // ===========================================================================
489
+
490
+
491
+ _.each(data.data, function (rowGroup, groupNum) {
492
+ self.csv.addRow();
493
+
494
+ var tr = document.createElement('tr');
495
+ tr.setAttribute('data-wcdv-rvi', groupNum);
496
+
497
+ _.each(self.opts.displayOrder, function (what, displayOrderIndex) {
498
+ if (typeof what === 'string') {
499
+ switch (what) {
500
+ case 'rowVals':
501
+ self.drawBody_rowVals(data, tr, groupNum);
502
+ break;
503
+ case 'cells':
504
+ var rowAgg = [];
505
+
506
+ // Create the cells that show the result of the aggregate function for all rows matching the
507
+ // column values at the same index.
508
+ //
509
+ // EXAMPLE
510
+ // -------
511
+ //
512
+ // pivotFields = ["State"]
513
+ // colVals = ["IL", "IN", "MI", "OH"]
514
+ //
515
+ // Column #1: agg(rowGroup[0]) - rows in the group w/ State = "IL"
516
+ // Column #2: agg(rowGroup[1]) - rows in the group w/ State = "IN"
517
+ // Column #3: agg(rowGroup[2]) - rows in the group w/ State = "MI"
518
+ // Column #4: agg(rowGroup[3]) - rows in the group w/ State = "OH"
519
+
520
+ _.each(rowGroup, function (colGroup, pivotNum) {
521
+ if (df.cell.length > 0) {
522
+ _.each(df.cell, function (dispFmt, dfCellIndex) {
523
+ var td = document.createElement('td');
524
+ td.classList.add('wcdv_pivot_cell');
525
+ td.setAttribute('data-wcdv-rvi', groupNum);
526
+ td.setAttribute('data-wcdv-cvi', pivotNum);
527
+
528
+ td.innerHTML = templates.cell[dfCellIndex]({
529
+ rowValIdx: groupNum,
530
+ colValIdx: pivotNum
531
+ });
532
+
533
+ if (_.every(data.groupSpec, function (gs) { return gs.fun == null; })
534
+ && _.every(data.pivotSpec, function (ps) { return ps.fun == null; })) {
535
+ self._addDrillDownClass(td);
536
+ }
537
+
538
+ if (self.opts.drawInternalBorders) {
539
+ td.classList.add('wcdv_pivot_colval_boundary');
540
+ }
541
+
542
+ tr.appendChild(td);
543
+ });
544
+ }
545
+ else if (ai.cell.length > 0) {
546
+ // Every cell aggregate function is going to make a separate cell.
547
+ _.each(ai.cell, function (aggInfo, aiCellIndex) {
548
+ var aggNum = aggInfo.aggNum;
549
+ var aggType = aggInfo.instance.getType();
550
+ var agg = data.agg.results.cell[aggNum];
551
+ var aggResult = agg[groupNum][pivotNum];
552
+
553
+ var td = document.createElement('td');
554
+ td.classList.add('wcdv_pivot_cell');
555
+ td.setAttribute('data-wcdv-rvi', groupNum);
556
+ td.setAttribute('data-wcdv-cvi', pivotNum);
557
+ td.setAttribute('data-wcdv-agg-scope', 'cell');
558
+ td.setAttribute('data-wcdv-agg-num', aggInfo.aggNum);
559
+
560
+ rowAgg.push(aggResult);
561
+
562
+ var text;
563
+
564
+ if (aggResult instanceof jQuery) {
565
+ aggResult = aggResult.get(0);
566
+ }
567
+
568
+ if (aggResult instanceof Element) {
569
+ td.appendChild(aggResult);
570
+ self.csv.addCol(aggResult.innerText);
571
+ }
572
+ else if (self.opts.hideBottomValueAggResults && aggResult === aggInfo.instance.bottomValue) {
573
+ td.innerHTML = '&nbsp;';
574
+ self.csv.addCol('');
575
+ }
576
+ else {
577
+ if (aggInfo.instance.inheritFormatting) {
578
+ text = format(aggInfo.colConfig[0], aggInfo.typeInfo[0], aggResult, {
579
+ overrideType: aggType
580
+ });
581
+ setTableCell(td, text, {
582
+ field: aggInfo.fields[0],
583
+ colConfig: aggInfo.colConfig[0],
584
+ typeInfo: aggInfo.typeInfo[0]
585
+ });
586
+ }
587
+ else {
588
+ text = format(null, null, aggResult, {
589
+ overrideType: aggType,
590
+ decode: false
591
+ });
592
+ setTableCell(td, text);
593
+ }
594
+ self.csv.addCol(td.innerText);
595
+ }
596
+
597
+ if (_.every(data.groupSpec, function (gs) { return gs.fun == null; })
598
+ && _.every(data.pivotSpec, function (ps) { return ps.fun == null; })) {
599
+ self._addDrillDownClass(td);
600
+ }
601
+
602
+ if ((self.opts.drawInternalBorders || ai.cell.length > 1) && aiCellIndex === 0) {
603
+ td.classList.add('wcdv_pivot_colval_boundary');
604
+ }
605
+
606
+ // REMOVED: How do we let the user set sizes &c. when doing a pivot table?
607
+ // self.setCss(td, col);
608
+
609
+ self.setAlignment(td, aggInfo.colConfig[0], aggInfo.typeInfo[0], aggType);
610
+
611
+ tr.appendChild(td);
612
+ });
613
+ }
614
+ else {
615
+ // There's no cell aggregate functions, so there isn't anything to put in the cell.
616
+ tr.appendChild(document.createElement('td'));
617
+ }
618
+ });
619
+ break;
620
+ case 'groupAggregates':
621
+ self.drawBody_groupAggregates(data, tr, groupNum, displayOrderIndex, self.opts.displayOrder.length);
622
+ break;
623
+ case 'addCols':
624
+ // Generate the user's custom-defined additional columns. If the `value` function returns an
625
+ // Element or jQuery instance, we just put that in the <TD> that we make. Otherwise (e.g. it
626
+ // returns a string or number) we format it according to the type of the field that the pivot
627
+ // function was operating on.
628
+ //
629
+ // EXAMPLE:
630
+ //
631
+ // Aggregate Function = sum
632
+ // Aggregate Field = Amount : number -> $0,0.00
633
+ //
634
+ // If the `value` function adds up the sums, yielding a grand total of them all, then we format
635
+ // that using Numeral exactly as specified for the "Amount" field.
636
+
637
+ _.each(self.opts.addCols, function (addCol) {
638
+ var addColResult = addCol.value(data.data, groupNum, rowAgg, aggType);
639
+ var td = document.createElement('td');
640
+ var addColText;
641
+
642
+ if (addColResult instanceof jQuery) {
643
+ addColResult = addColResult.get(0);
644
+ }
645
+
646
+ if (addColResult instanceof Element) {
647
+ td.appendChild(addColResult);
648
+ self.csv.addCol(addColResult.innerText);
649
+ }
650
+ else {
651
+ if (false && aggInfo.instance.inheritFormatting) {
652
+ addColText = format(aggInfo.colConfig[0], aggInfo.typeInfo[0], addColResult, {
653
+ alwaysFormat: true
654
+ });
655
+ }
656
+ else {
657
+ addColText = format(null, null, addColResult, {
658
+ alwaysFormat: true,
659
+ decode: false
660
+ });
661
+ }
662
+ td.innerText = addColText;
663
+ self.csv.addCol(addColText);
664
+ }
665
+
666
+ if (getProp(opts, 'pivotConfig', 'aggField')) {
667
+ self.setAlignment(td, self.colConfig.get(opts.pivotConfig.aggField), typeInfo.get(opts.pivotConfig.aggField));
668
+ }
669
+
670
+ tr.appendChild(td);
671
+ });
672
+ break;
673
+ }
674
+ }
675
+ });
676
+
677
+ self.ui.tbody.append(tr);
678
+ });
679
+
680
+ // ===========================================================================
681
+ // PIVOT AGGREGATES
682
+ // ===========================================================================
683
+
684
+ _.each(ai.pivot, function (aggInfo, aiPivotIndex) {
685
+ var span,
686
+ text,
687
+ aggNum = aggInfo.aggNum,
688
+ aggResult,
689
+ headingThControls,
690
+ headingThContainer,
691
+ th,
692
+ tr,
693
+ i,
694
+ td;
695
+
696
+ tr = jQuery('<tr>');
697
+ self.csv.addRow();
698
+
699
+ // Add a class to the first row so it gets the double-bar outline.
700
+
701
+ if (aiPivotIndex === 0) {
702
+ tr.addClass('wcdv_btd'); // border-top: double
703
+ }
704
+
705
+ _.each(self.opts.displayOrder, function (what) {
706
+ if (typeof what === 'string') {
707
+ switch (what) {
708
+ case 'rowVals':
709
+
710
+ // Insert the name of the aggregate function in the header. This will take up as many columns
711
+ // as there are group fields.
712
+
713
+ if (data.groupFields.length > 1) {
714
+ for (i = 0; i < data.groupFields.length - 1; i += 1) {
715
+ self.csv.addCol('');
716
+ }
717
+ }
718
+
719
+ self.csv.addCol(aggInfo.instance.getFullName());
720
+ span = jQuery('<span>').addClass('wcdv_heading_title').text(aggInfo.instance.getFullName());
721
+
722
+ headingThControls = jQuery('<div>');
723
+
724
+ headingThContainer = jQuery('<div>')
725
+ .addClass('wcdv_heading_container')
726
+ .append(span, headingThControls);
727
+
728
+ th = jQuery('<th>')
729
+ .attr({'colspan': data.groupFields.length})
730
+ .append(headingThContainer)
731
+ .appendTo(tr);
732
+
733
+ // Add sorting to the header we just created.
734
+
735
+ self._addSortingToHeader(data, 'horizontal', {aggType: 'pivot', aggNum: aggNum}, headingThControls.get(0), getPropDef([], data, 'agg', 'info', 'cell'));
736
+
737
+ break;
738
+ case 'cells':
739
+ _.each(data.colVals, function (colVal, colValIdx) {
740
+ // Add padding cells in the CSV output so that the pivot aggregates appear staggered. Since
741
+ // we can't do rowspan in CSV like we can in HTML.
742
+
743
+ for (var i = 0; i < aiPivotIndex; i += 1) {
744
+ self.csv.addCol('');
745
+ }
746
+
747
+ var td = jQuery('<td>').dvAttr({
748
+ 'cvi': colValIdx,
749
+ 'agg-scope': 'pivot',
750
+ 'agg-num': aggInfo.aggNum
751
+ });
752
+ var aggResult = data.agg.results.pivot[aggNum][colValIdx];
753
+
754
+ if (isElement(aggResult)) {
755
+ td.append(aggResult);
756
+ self.csv.addCol(getElement(aggResult).innerText);
757
+ }
758
+ else {
759
+ if (aggInfo.instance.inheritFormatting) {
760
+ text = format(aggInfo.colConfig[0], aggInfo.typeInfo[0], aggResult, {
761
+ overrideType: aggInfo.instance.getType()
762
+ });
763
+ setTableCell(td, text, {
764
+ field: aggInfo.fields[0],
765
+ colConfig: aggInfo.colConfig[0],
766
+ typeInfo: aggInfo.typeInfo[0]
767
+ });
768
+ }
769
+ else {
770
+ text = format(null, null, aggResult, {
771
+ overrideType: aggInfo.instance.getType(),
772
+ decode: false
773
+ });
774
+ setTableCell(td, text);
775
+ }
776
+ self.csv.addCol(td.text());
777
+ }
778
+
779
+ if (_.every(data.pivotSpec, function (ps) { return ps.fun == null; })) {
780
+ self._addDrillDownClass(td.get(0));
781
+ }
782
+
783
+ if ((df.cell.length || ai.cell.length) > 1) {
784
+ td.attr('colspan', (df.cell.length || ai.cell.length));
785
+ }
786
+
787
+ if (self.opts.drawInternalBorders || ai.cell.length > 1) {
788
+ td.addClass('wcdv_pivot_colval_boundary');
789
+ }
790
+
791
+ self.setAlignment(td, aggInfo.colConfig[0], aggInfo.typeInfo[0], aggInfo.instance.getType());
792
+ td.appendTo(tr);
793
+
794
+ // Add padding cells in the CSV output so that the pivot aggregates appear staggered. Since
795
+ // we can't do rowspan in CSV like we can in HTML.
796
+
797
+ for (i = aiPivotIndex + 1; i < ai.pivot.length; i += 1) {
798
+ self.csv.addCol('');
799
+ }
800
+ });
801
+ break;
802
+ case 'groupAggregates':
803
+
804
+ // =========================================================================
805
+ // ALL AGGREGATES
806
+ // =========================================================================
807
+
808
+ if (getProp(data, 'agg', 'info', 'all', aggNum)) {
809
+ for (i = 0; i < aiPivotIndex; i += 1) {
810
+ td = jQuery('<td><div>&nbsp;</div></td>');
811
+ if (self.opts.drawInternalBorders || ai.cell.length > 1) {
812
+ td.addClass(i === 0 ? 'wcdv_pivot_aggregate_boundary' : 'wcdv_pivot_colval_boundary');
813
+ }
814
+ td.addClass('wcdv_cell_empty');
815
+ self.csv.addCol('');
816
+ td.appendTo(tr);
817
+ }
818
+
819
+ aggInfo = data.agg.info.all[aggNum];
820
+ aggResult = data.agg.results.all[aggNum];
821
+ td = jQuery('<td>');
822
+ td.attr('data-wcdv-agg-scope', 'all');
823
+ td.attr('data-wcdv-agg-num', aggInfo.aggNum);
824
+
825
+ if (isElement(aggResult)) {
826
+ td.append(aggResult);
827
+ self.csv.addCol(getElement(aggResult).innerText);
828
+ }
829
+ else {
830
+ if (aggInfo.instance.inheritFormatting) {
831
+ text = format(aggInfo.colConfig[0], aggInfo.typeInfo[0], aggResult, {
832
+ overrideType: aggInfo.instance.getType()
833
+ });
834
+ setTableCell(td, text, {
835
+ field: aggInfo.fields[0],
836
+ colConfig: aggInfo.colConfig[0],
837
+ typeInfo: aggInfo.typeInfo[0]
838
+ });
839
+ }
840
+ else {
841
+ text = format(null, null, aggResult, {
842
+ overrideType: aggInfo.instance.getType(),
843
+ decode: false
844
+ });
845
+ setTableCell(td, text);
846
+ }
847
+ self.csv.addCol(td.text());
848
+ }
849
+
850
+ if (self.opts.drawInternalBorders || ai.cell.length > 1) {
851
+ td.addClass(aiPivotIndex === 0 ? 'wcdv_pivot_aggregate_boundary' : 'wcdv_pivot_colval_boundary');
852
+ }
853
+
854
+ self.setAlignment(td, aggInfo.colConfig[0], aggInfo.typeInfo[0], aggInfo.instance.getType());
855
+ td.appendTo(tr);
856
+
857
+ for (var i = aiPivotIndex + 1; i < ai.cell.length; i += 1) {
858
+ td = jQuery('<td><div>&nbsp;</div></td>');
859
+ if (self.opts.drawInternalBorders || ai.cell.length > 1) {
860
+ td.addClass('wcdv_pivot_colval_boundary');
861
+ }
862
+ td.addClass('wcdv_cell_empty');
863
+ self.csv.addCol('');
864
+ td.appendTo(tr);
865
+ }
866
+ }
867
+ break;
868
+ }
869
+ }
870
+ });
871
+
872
+ tr.appendTo(self.ui.tbody);
873
+ });
874
+
875
+ self.csv.finish(function () {
876
+ self.logDebug(self.makeLogTag() + ' Finished generating CSV file', self.toString());
877
+ self.csvLock.unlock();
878
+ self.fire('generateCsvProgress', null, 100);
879
+ self.fire('csvReady');
880
+ });
881
+
882
+ if (typeof cont === 'function') {
883
+ return cont();
884
+ }
885
+ };
886
+
887
+ // #addWorkHandler {{{2
888
+
889
+ GridTablePivot.prototype.addWorkHandler = function () {
890
+ var self = this;
891
+
892
+ self.view.on(ComputedView.events.workEnd, function (info, ops) {
893
+ if (self._destroyed) { return; }
894
+ self.logDebug(self.makeLogTag('handler(workEnd)') + ' ComputedView has finished doing work');
895
+
896
+ if (!ops.pivot) {
897
+ self.logDebug(self.makeLogTag('handler(workEnd)') + ' Unable to render this data: %O', ops);
898
+ self.fire('unableToRender', null, ops);
899
+ return;
900
+ }
901
+
902
+ self.logDebug(self.makeLogTag('handler(workEnd)') + ' Redrawing because the view has done work');
903
+ self.draw(self.root);
904
+ }, { who: self });
905
+ };
906
+
907
+ // Registry {{{1
908
+
909
+ GridRenderer.registry.set('table_pivot', GridTablePivot);
910
+
911
+ // Exports {{{1
912
+
913
+ export {
914
+ GridTablePivot
915
+ };