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.
- package/LICENSE +45 -0
- package/README.md +129 -0
- package/datavis.js +101 -0
- package/dist/wcdatavis.css +1957 -0
- package/dist/wcdatavis.min.js +1 -0
- package/global-jquery.js +4 -0
- package/ie-fixes.js +13 -0
- package/index.js +70 -0
- package/meteor.js +1 -0
- package/package.json +102 -0
- package/src/flags.js +6 -0
- package/src/graph.js +1079 -0
- package/src/graph_renderer.js +85 -0
- package/src/grid.js +2777 -0
- package/src/grid_control.js +1957 -0
- package/src/grid_filter.js +1073 -0
- package/src/grid_renderer.js +276 -0
- package/src/group_fun_win.js +121 -0
- package/src/lang/en-US.js +188 -0
- package/src/lang/es-MX.js +188 -0
- package/src/lang/fr-FR.js +188 -0
- package/src/lang/id-ID.js +188 -0
- package/src/lang/nl-NL.js +188 -0
- package/src/lang/pt-BR.js +188 -0
- package/src/lang/ru-RU.js +188 -0
- package/src/lang/th-TH.js +188 -0
- package/src/lang/vi-VN.js +188 -0
- package/src/lang/zh-Hans-CN.js +188 -0
- package/src/operations_palette.js +176 -0
- package/src/prefs_modules.js +132 -0
- package/src/reg/graph_renderer.js +17 -0
- package/src/renderers/graph/chartjs.js +457 -0
- package/src/renderers/graph/google.js +584 -0
- package/src/renderers/graph/jit.js +61 -0
- package/src/renderers/graph/svelte-gantt.js +168 -0
- package/src/renderers/grid/dummy.js +79 -0
- package/src/renderers/grid/handlebars.js +217 -0
- package/src/renderers/grid/squirrelly.js +215 -0
- package/src/renderers/grid/table/group_detail.js +1404 -0
- package/src/renderers/grid/table/group_summary.js +380 -0
- package/src/renderers/grid/table/pivot.js +915 -0
- package/src/renderers/grid/table/plain.js +1592 -0
- package/src/renderers/grid/table.js +2510 -0
- package/src/trans.js +101 -0
- package/src/ui/collapsible.js +234 -0
- package/src/ui/filters/date.js +283 -0
- package/src/ui/grid_filter.js +398 -0
- package/src/ui/popup_menu.js +224 -0
- package/src/ui/popup_window.js +572 -0
- package/src/ui/slider.js +156 -0
- package/src/ui/tabs.js +202 -0
- package/src/ui/templates.js +131 -0
- package/src/ui/toolbar.js +63 -0
- package/src/ui/toolbars/grid.js +873 -0
- package/src/ui/windows/col_config.js +341 -0
- package/src/ui/windows/debug.js +164 -0
- package/src/ui/windows/grid_table_opts.js +139 -0
- package/src/util/handlebars.js +158 -0
- package/src/util/jquery.js +630 -0
- package/src/util/misc.js +1058 -0
- package/src/util/squirrelly.js +155 -0
- package/wcdatavis.css +1601 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
/* global google */
|
|
2
|
+
|
|
3
|
+
import _ from 'underscore';
|
|
4
|
+
import moment from 'moment';
|
|
5
|
+
import numeral from 'numeral';
|
|
6
|
+
import jQuery from 'jquery';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
dataURItoBlob,
|
|
10
|
+
deepCopy,
|
|
11
|
+
deepDefaults,
|
|
12
|
+
getProp,
|
|
13
|
+
loadScript,
|
|
14
|
+
makeSubclass,
|
|
15
|
+
mixinLogging,
|
|
16
|
+
setProp,
|
|
17
|
+
} from '../../util/misc.js';
|
|
18
|
+
import {AggregateInfo, GROUP_FUNCTION_REGISTRY, OrdMap, Source} from 'datavis-ace';
|
|
19
|
+
import { GraphRenderer } from '../../graph_renderer.js';
|
|
20
|
+
import { trans } from '../../trans.js';
|
|
21
|
+
|
|
22
|
+
// GraphRendererGoogle {{{1
|
|
23
|
+
|
|
24
|
+
var GraphRendererGoogle = makeSubclass('GraphRendererGoogle', GraphRenderer, null, {
|
|
25
|
+
graphTypes: OrdMap.fromArray([{
|
|
26
|
+
value: 'area',
|
|
27
|
+
name: 'Area Chart',
|
|
28
|
+
modes: ['plain'],
|
|
29
|
+
}, {
|
|
30
|
+
value: 'line',
|
|
31
|
+
name: 'Line Chart',
|
|
32
|
+
modes: ['plain'],
|
|
33
|
+
}, {
|
|
34
|
+
value: 'bar',
|
|
35
|
+
name: 'Bar Chart',
|
|
36
|
+
modes: ['plain', 'group', 'pivot'],
|
|
37
|
+
}, {
|
|
38
|
+
value: 'column',
|
|
39
|
+
name: 'Column Chart',
|
|
40
|
+
modes: ['plain', 'group', 'pivot'],
|
|
41
|
+
}, {
|
|
42
|
+
value: 'pie',
|
|
43
|
+
name: 'Pie Chart',
|
|
44
|
+
modes: ['plain', 'group', 'pivot'],
|
|
45
|
+
}, {
|
|
46
|
+
value: 'gantt',
|
|
47
|
+
name: 'Gantt Chart',
|
|
48
|
+
modes: ['plain'],
|
|
49
|
+
}], 'value')
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
mixinLogging(GraphRendererGoogle);
|
|
53
|
+
|
|
54
|
+
// #draw_plain {{{2
|
|
55
|
+
|
|
56
|
+
GraphRendererGoogle.prototype.draw_plain = function (data, typeInfo, dt, config) {
|
|
57
|
+
var self = this;
|
|
58
|
+
|
|
59
|
+
if (config == null) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
var convertType = function (t) {
|
|
64
|
+
switch (t) {
|
|
65
|
+
case 'currency':
|
|
66
|
+
return 'number';
|
|
67
|
+
default:
|
|
68
|
+
return t;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
var getRealValue = function (f, x) {
|
|
73
|
+
if (typeInfo.get(f).type === 'date' && moment.isMoment(x.value)) {
|
|
74
|
+
return {v: x.value.toDate(), f: x.orig};
|
|
75
|
+
}
|
|
76
|
+
else if (['number', 'currency'].indexOf(typeInfo.get(f).type) >= 0 && numeral.isNumeral(x.value)) {
|
|
77
|
+
return {v: x.value._value, f: x.orig};
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
return x.value;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
switch (config.graphType) {
|
|
85
|
+
case 'gantt':
|
|
86
|
+
var makeDate = function (x) {
|
|
87
|
+
return typeof x === 'string' ? moment(x).toDate() :
|
|
88
|
+
moment.isMoment(x) ? x.toDate() :
|
|
89
|
+
null;
|
|
90
|
+
};
|
|
91
|
+
var cols = [{
|
|
92
|
+
field: 'ID',
|
|
93
|
+
type: 'string',
|
|
94
|
+
default: (function () {
|
|
95
|
+
var id = 1;
|
|
96
|
+
return function () { return '' + id++; };
|
|
97
|
+
})()
|
|
98
|
+
}, {
|
|
99
|
+
field: 'Task',
|
|
100
|
+
type: 'string',
|
|
101
|
+
required: true
|
|
102
|
+
}, {
|
|
103
|
+
field: 'Resource',
|
|
104
|
+
type: 'string',
|
|
105
|
+
required: true
|
|
106
|
+
}, {
|
|
107
|
+
field: 'Start',
|
|
108
|
+
type: 'date',
|
|
109
|
+
default: null,
|
|
110
|
+
transform: makeDate
|
|
111
|
+
}, {
|
|
112
|
+
field: 'End',
|
|
113
|
+
type: 'date',
|
|
114
|
+
default: null,
|
|
115
|
+
transform: makeDate
|
|
116
|
+
}, {
|
|
117
|
+
field: 'Duration',
|
|
118
|
+
type: 'number',
|
|
119
|
+
default: null
|
|
120
|
+
}, {
|
|
121
|
+
field: 'Completion',
|
|
122
|
+
type: 'number',
|
|
123
|
+
default: null
|
|
124
|
+
}, {
|
|
125
|
+
field: 'Dependencies',
|
|
126
|
+
type: 'string',
|
|
127
|
+
default: ''
|
|
128
|
+
}];
|
|
129
|
+
|
|
130
|
+
// Make sure that all the fields that we need are in the data.
|
|
131
|
+
|
|
132
|
+
var missingRequired = false;
|
|
133
|
+
_.each(cols, function (c) {
|
|
134
|
+
if (c.required && !typeInfo.isSet(c.field)) {
|
|
135
|
+
self.logError(self.makeLogTag() + ' Missing required data field: %s', c.field);
|
|
136
|
+
missingRequired = true;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
if (missingRequired) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_.each(cols, function (c) {
|
|
144
|
+
// Add columns to the Google Charts data table.
|
|
145
|
+
dt.addColumn(c.type, c.field);
|
|
146
|
+
|
|
147
|
+
// Make sure data is decoded.
|
|
148
|
+
if (typeInfo.isSet(c.field)) {
|
|
149
|
+
Source.decodeAll(data.dataByRowId, c.field, typeInfo);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
_.each(data.data, function (row) {
|
|
154
|
+
var newRow = [];
|
|
155
|
+
_.each(cols, function (c) {
|
|
156
|
+
if (typeInfo.isSet(c.field)) {
|
|
157
|
+
var v = row.rowData[c.field].value;
|
|
158
|
+
newRow.push(c.transform != null ? c.transform(v) : v);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
newRow.push(typeof c.default === 'function' ? c.default() : c.default);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
dt.addRow(newRow);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
break;
|
|
168
|
+
default:
|
|
169
|
+
dt.addColumn(convertType(typeInfo.get(config.categoryField).type), config.categoryField);
|
|
170
|
+
|
|
171
|
+
_.each(config.valueFields, function (field) {
|
|
172
|
+
dt.addColumn(convertType(typeInfo.get(field).type), field);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
_.each(config.valueFields, function (field) {
|
|
176
|
+
Source.decodeAll(data.dataByRowId, field, typeInfo);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
_.each(data.data, function (row) {
|
|
180
|
+
var newRow;
|
|
181
|
+
|
|
182
|
+
newRow = _.map([config.categoryField].concat(config.valueFields), function (f) {
|
|
183
|
+
return getRealValue(f, row.rowData[f]);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
dt.addRow(newRow);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return config;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// #draw_group {{{2
|
|
194
|
+
|
|
195
|
+
GraphRendererGoogle.prototype.draw_group = function (data, typeInfo, dt, config) {
|
|
196
|
+
var self = this;
|
|
197
|
+
|
|
198
|
+
if (typeof config === 'function') {
|
|
199
|
+
config = config(data.groupFields);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
config = deepDefaults(config, {
|
|
203
|
+
graphType: 'column',
|
|
204
|
+
categoryField: data.groupFields[0],
|
|
205
|
+
valueFields: [{
|
|
206
|
+
name: 'Count',
|
|
207
|
+
fun: 'count'
|
|
208
|
+
}]
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
var valueAxis = config.graphType === 'bar' ? 'hAxis' : 'vAxis';
|
|
212
|
+
|
|
213
|
+
// dt.addColumn(typeInfo.get(config.categoryField).type, config.categoryField);
|
|
214
|
+
dt.addColumn('string', config.categoryField);
|
|
215
|
+
|
|
216
|
+
if (config.aggType != null && config.aggNum != null) {
|
|
217
|
+
var aggInfo = getProp(data, 'agg', 'info', config.aggType, config.aggNum);
|
|
218
|
+
if (aggInfo == null) {
|
|
219
|
+
self.logError(self.makeLogTag() + ' The specified aggregate does not exist: ' + config.aggType + '[' + config.aggNum + ']');
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
if (data.agg.results[config.aggType][config.aggNum] == null) {
|
|
223
|
+
self.logError(self.makeLogTag() + ' No results exist for the specified aggregate: ' + config.aggType + '[' + config.aggNum + ']');
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
var name = aggInfo.name || aggInfo.instance.getFullName();
|
|
227
|
+
var aggResultType = aggInfo.instance.getType();
|
|
228
|
+
|
|
229
|
+
if (aggResultType === 'currency') {
|
|
230
|
+
aggResultType = 'number';
|
|
231
|
+
setProp('currency', config, 'options', valueAxis, 'format');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
dt.addColumn(aggResultType, name);
|
|
235
|
+
setProp(name, config, 'options', valueAxis, 'title');
|
|
236
|
+
|
|
237
|
+
_.each(data.rowVals, function (rowVal, rowValIdx) {
|
|
238
|
+
var newRow = [rowVal.join(', ')];
|
|
239
|
+
|
|
240
|
+
var aggResult = data.agg.results[config.aggType][config.aggNum][rowValIdx];
|
|
241
|
+
if (aggResultType === 'number') {
|
|
242
|
+
aggResult = +aggResult;
|
|
243
|
+
}
|
|
244
|
+
newRow.push(aggResult);
|
|
245
|
+
dt.addRow(newRow);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
var ai = [];
|
|
250
|
+
|
|
251
|
+
// For each value field, create the AggregateInfo instance that will manage it. Also create a
|
|
252
|
+
// column for the result in the data table.
|
|
253
|
+
|
|
254
|
+
_.each(config.valueFields, function (v) {
|
|
255
|
+
var aggInfo = new AggregateInfo('group', v, 0, null /* colConfig */, self.typeInfo, null /* convert */);
|
|
256
|
+
dt.addColumn(aggInfo.instance.getType(), v.name || aggInfo.instance.getFullName());
|
|
257
|
+
ai.push(aggInfo);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Go through each rowval and create a row for it in the data table. Every value field gets its
|
|
261
|
+
// own column, which is the result of the corresponding aggregate function specified above.
|
|
262
|
+
|
|
263
|
+
_.each(data.rowVals, function (rowVal, rowValIdx) {
|
|
264
|
+
var newRow = [rowVal.join(', ')];
|
|
265
|
+
|
|
266
|
+
_.each(ai, function (aggInfo) {
|
|
267
|
+
var aggResult = aggInfo.instance.calculate(_.flatten(data.data[rowValIdx]));
|
|
268
|
+
newRow.push(aggResult);
|
|
269
|
+
if (aggInfo.debug) {
|
|
270
|
+
self.logDebug(self.makeLogTag() + ' Group aggregate (%s) : Group [%s] = %s',
|
|
271
|
+
aggInfo.instance.name + (aggInfo.name ? ' -> ' + aggInfo.name : ''),
|
|
272
|
+
rowVal.join(', '),
|
|
273
|
+
JSON.stringify(aggResult));
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
dt.addRow(newRow);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return config;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// #draw_pivot {{{2
|
|
285
|
+
|
|
286
|
+
GraphRendererGoogle.prototype.draw_pivot = function (data, typeInfo, dt, config) {
|
|
287
|
+
var self = this;
|
|
288
|
+
|
|
289
|
+
if (typeof config === 'function') {
|
|
290
|
+
config = config(data.groupFields, data.pivotFields);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
config = deepDefaults(config, {
|
|
294
|
+
graphType: 'column',
|
|
295
|
+
categoryField: data.groupFields[0],
|
|
296
|
+
valueFields: [{
|
|
297
|
+
fun: 'count'
|
|
298
|
+
}],
|
|
299
|
+
options: {
|
|
300
|
+
isStacked: true
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
var valueAxis = config.graphType === 'bar' ? 'hAxis' : 'vAxis';
|
|
305
|
+
|
|
306
|
+
dt.addColumn('string', config.categoryField);
|
|
307
|
+
|
|
308
|
+
if (config.aggType != null && config.aggNum != null) {
|
|
309
|
+
var aggInfo = getProp(data, 'agg', 'info', config.aggType, config.aggNum);
|
|
310
|
+
if (aggInfo == null) {
|
|
311
|
+
self.logError(self.makeLogTag() + ' The specified aggregate does not exist: ' + config.aggType + '[' + config.aggNum + ']');
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
if (data.agg.results[config.aggType][config.aggNum] == null) {
|
|
315
|
+
self.logError(self.makeLogTag() + ' No results exist for the specified aggregate: ' + config.aggType + '[' + config.aggNum + ']');
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
var name = aggInfo.name || aggInfo.instance.getFullName();
|
|
319
|
+
var aggResultType = aggInfo.instance.getType();
|
|
320
|
+
|
|
321
|
+
if (aggResultType === 'currency') {
|
|
322
|
+
aggResultType = 'number';
|
|
323
|
+
setProp('currency', config, 'options', valueAxis, 'format');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
switch (config.aggType) {
|
|
327
|
+
case 'cell':
|
|
328
|
+
_.each(data.colVals, function (colVal) {
|
|
329
|
+
dt.addColumn(aggResultType, colVal.join(', '));
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
setProp(name, config, 'options', valueAxis, 'title');
|
|
333
|
+
|
|
334
|
+
_.each(data.rowVals, function (rowVal, rowValIdx) {
|
|
335
|
+
var newRow = [rowVal.join(', ')];
|
|
336
|
+
|
|
337
|
+
_.each(data.colVals, function (colVal, colValIdx) {
|
|
338
|
+
var aggResult = data.agg.results[config.aggType][config.aggNum][rowValIdx][colValIdx];
|
|
339
|
+
if (aggResultType === 'number') {
|
|
340
|
+
aggResult = +aggResult;
|
|
341
|
+
}
|
|
342
|
+
newRow.push(aggResult);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
dt.addRow(newRow);
|
|
346
|
+
});
|
|
347
|
+
break;
|
|
348
|
+
case 'group':
|
|
349
|
+
dt.addColumn(aggResultType, name);
|
|
350
|
+
setProp(name, config, 'options', valueAxis, 'title');
|
|
351
|
+
|
|
352
|
+
_.each(data.rowVals, function (rowVal, rowValIdx) {
|
|
353
|
+
var newRow = [rowVal.join(', ')];
|
|
354
|
+
|
|
355
|
+
var aggResult = data.agg.results[config.aggType][config.aggNum][rowValIdx];
|
|
356
|
+
if (aggResultType === 'number') {
|
|
357
|
+
aggResult = +aggResult;
|
|
358
|
+
}
|
|
359
|
+
newRow.push(aggResult);
|
|
360
|
+
dt.addRow(newRow);
|
|
361
|
+
});
|
|
362
|
+
break;
|
|
363
|
+
case 'pivot':
|
|
364
|
+
dt.addColumn(aggResultType, name);
|
|
365
|
+
setProp(name, config, 'options', valueAxis, 'title');
|
|
366
|
+
|
|
367
|
+
_.each(data.colVals, function (colVal, colValIdx) {
|
|
368
|
+
var newRow = [colVal.join(', ')];
|
|
369
|
+
|
|
370
|
+
var aggResult = data.agg.results[config.aggType][config.aggNum][colValIdx];
|
|
371
|
+
if (aggResultType === 'number') {
|
|
372
|
+
aggResult = +aggResult;
|
|
373
|
+
}
|
|
374
|
+
newRow.push(aggResult);
|
|
375
|
+
dt.addRow(newRow);
|
|
376
|
+
});
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
var ai = [];
|
|
382
|
+
|
|
383
|
+
// For each value field, create the AggregateInfo instance that will manage it. Also create
|
|
384
|
+
// columns for the results (one for each colval) in the data table.
|
|
385
|
+
|
|
386
|
+
_.each(config.valueFields, function (v) {
|
|
387
|
+
var aggInfo = new AggregateInfo('cell', v, 0, null /* colConfig */, self.typeInfo, null /* convert */);
|
|
388
|
+
|
|
389
|
+
_.each(data.colVals, function (colVal) {
|
|
390
|
+
dt.addColumn(aggInfo.instance.getType(), colVal.join(', '));
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
ai.push(aggInfo);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
_.each(data.rowVals, function (rowVal, rowValIndex) {
|
|
397
|
+
var newRow = [rowVal.join(', ')];
|
|
398
|
+
|
|
399
|
+
_.each(data.colVals, function (colVal, colValIndex) {
|
|
400
|
+
_.each(ai, function (aggInfo) {
|
|
401
|
+
var aggResult = aggInfo.instance.calculate(data.data[rowValIndex][colValIndex]);
|
|
402
|
+
newRow.push(aggResult);
|
|
403
|
+
if (aggInfo.debug) {
|
|
404
|
+
self.logDebug(self.makeLogTag() + ' Group aggregate (%s) : RowVal [%s] x ColVal [%s] = %s',
|
|
405
|
+
aggInfo.instance.name + (aggInfo.name ? ' -> ' + aggInfo.name : ''),
|
|
406
|
+
rowVal.join(', '),
|
|
407
|
+
colVal.join(', '),
|
|
408
|
+
JSON.stringify(aggResult));
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
dt.addRow(newRow);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return config;
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// #_ensureGoogleChartsLoaded {{{2
|
|
421
|
+
|
|
422
|
+
GraphRendererGoogle.prototype._ensureGoogleChartsLoaded = function (cont) {
|
|
423
|
+
var self = this;
|
|
424
|
+
|
|
425
|
+
return loadScript('https://www.gstatic.com/charts/loader.js', function (wasAlreadyLoaded, k) {
|
|
426
|
+
var cb = function () {
|
|
427
|
+
k();
|
|
428
|
+
cont();
|
|
429
|
+
};
|
|
430
|
+
if (!wasAlreadyLoaded) {
|
|
431
|
+
self.logDebug(self.makeLogTag() + ' Loading support for Google Charts');
|
|
432
|
+
window.google.charts.load('current', {'packages': ['corechart', 'gantt']});
|
|
433
|
+
window.google.charts.setOnLoadCallback(cb);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
cb();
|
|
437
|
+
}
|
|
438
|
+
}, {
|
|
439
|
+
needAsyncSetup: true
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// #draw {{{2
|
|
444
|
+
|
|
445
|
+
GraphRendererGoogle.prototype.draw = function (devConfig, userConfig) {
|
|
446
|
+
var self = this;
|
|
447
|
+
|
|
448
|
+
if (!self.hasRun) {
|
|
449
|
+
self.super['GraphRenderer'].addRedrawHandlers();
|
|
450
|
+
}
|
|
451
|
+
self.hasRun = true;
|
|
452
|
+
|
|
453
|
+
devConfig = devConfig || {};
|
|
454
|
+
userConfig = userConfig || {};
|
|
455
|
+
|
|
456
|
+
self._ensureGoogleChartsLoaded(function () {
|
|
457
|
+
self.view.getData(function (ok, data) {
|
|
458
|
+
self.view.getTypeInfo(function (ok, typeInfo) {
|
|
459
|
+
self.elt.children().remove();
|
|
460
|
+
|
|
461
|
+
var makeMessage = function (msg) {
|
|
462
|
+
jQuery('<div>')
|
|
463
|
+
.addClass('wcdv_graph_message_container')
|
|
464
|
+
.css({ 'height': self.opts.height + 'px' })
|
|
465
|
+
.append(
|
|
466
|
+
jQuery('<div>')
|
|
467
|
+
.addClass('wcdv_graph_message')
|
|
468
|
+
.text(msg)
|
|
469
|
+
)
|
|
470
|
+
.appendTo(self.elt);
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
if (data.data.length === 0) {
|
|
474
|
+
makeMessage(trans('DATA.NO_DATA'));
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
var config = null;
|
|
479
|
+
var dt = new google.visualization.DataTable();
|
|
480
|
+
|
|
481
|
+
if (data.isPlain) {
|
|
482
|
+
config = self.draw_plain(data, typeInfo, dt, getProp(userConfig, 'plain', 'graphs', getProp(userConfig, 'plain', 'current')) || devConfig.whenPlain);
|
|
483
|
+
}
|
|
484
|
+
else if (data.isGroup) {
|
|
485
|
+
config = self.draw_group(data, typeInfo, dt, getProp(userConfig, 'group', 'graphs', getProp(userConfig, 'group', 'current')) || devConfig.whenGroup);
|
|
486
|
+
}
|
|
487
|
+
else if (data.isPivot) {
|
|
488
|
+
config = self.draw_pivot(data, typeInfo, dt, getProp(userConfig, 'pivot', 'graphs', getProp(userConfig, 'pivot', 'current')) || devConfig.whenPivot);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (config == null) {
|
|
492
|
+
makeMessage(trans('DATA.NOTHING_TO_GRAPH'));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
var ctor = {
|
|
497
|
+
area: 'AreaChart',
|
|
498
|
+
bar: 'BarChart',
|
|
499
|
+
column: 'ColumnChart',
|
|
500
|
+
line: 'LineChart',
|
|
501
|
+
pie: 'PieChart',
|
|
502
|
+
gantt: 'Gantt'
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
// This is the object that's actually passed to the chart's draw() method. All the options
|
|
506
|
+
// in the Google documentation should go into this object.
|
|
507
|
+
|
|
508
|
+
var options = {
|
|
509
|
+
title: self.opts.title,
|
|
510
|
+
width: self.opts.width,
|
|
511
|
+
height: self.opts.height,
|
|
512
|
+
isStacked: config.stacked,
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
var categoryAxis = config.graphType === 'bar' ? 'vAxis' : 'hAxis';
|
|
516
|
+
|
|
517
|
+
if (config.graphType === 'pie') {
|
|
518
|
+
options.chartArea = {
|
|
519
|
+
top: '5%',
|
|
520
|
+
left: '5%',
|
|
521
|
+
width: '90%',
|
|
522
|
+
height: '90%'
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
setProp(config.categoryField, options, categoryAxis, 'title');
|
|
527
|
+
|
|
528
|
+
jQuery.extend(true, options, config.options);
|
|
529
|
+
|
|
530
|
+
var chart = new google.visualization[ctor[config.graphType]](self.elt.get(0));
|
|
531
|
+
|
|
532
|
+
google.visualization.events.addListener(chart, 'ready', function () {
|
|
533
|
+
var blob = null;
|
|
534
|
+
if (typeof chart.getImageURI === 'function') {
|
|
535
|
+
blob = dataURItoBlob(chart.getImageURI());
|
|
536
|
+
}
|
|
537
|
+
self.graph._setExportBlob(blob);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
google.visualization.events.addListener(chart, 'select', function () {
|
|
541
|
+
var sel = chart.getSelection();
|
|
542
|
+
_.each(sel, function (o) {
|
|
543
|
+
self.logDebug(self.makeLogTag() + ' User selected element in graph: row = %s, column = %s, value = %s, formattedValue = %s', o.row, o.column, dt.getValue(o.row, o.column), dt.getFormattedValue(o.row, o.column));
|
|
544
|
+
|
|
545
|
+
var filter = deepCopy(self.view.getFilter());
|
|
546
|
+
|
|
547
|
+
_.each(data.rowVals[o.row], function (x, i) {
|
|
548
|
+
var gs = data.groupSpec[i];
|
|
549
|
+
filter[data.groupFields[i]] = gs.fun != null
|
|
550
|
+
? GROUP_FUNCTION_REGISTRY.get(gs.fun).valueToFilter(x)
|
|
551
|
+
: { '$eq': x };
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
if (data.isPivot) {
|
|
555
|
+
// Offset column by one because the category is stored in the first column of the Google
|
|
556
|
+
// DataTable, but that obviously doesn't exist in the View.
|
|
557
|
+
|
|
558
|
+
_.each(data.colVals[o.column - 1], function (x, i) {
|
|
559
|
+
var ps = data.pivotSpec[i];
|
|
560
|
+
filter[data.pivotFields[i]] = ps.fun != null
|
|
561
|
+
? GROUP_FUNCTION_REGISTRY.get(ps.fun).valueToFilter(x)
|
|
562
|
+
: { '$eq': x };
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
self.logDebug(self.makeLogTag() + ' Creating new perspective: filter = %O', filter);
|
|
567
|
+
|
|
568
|
+
window.setTimeout(function () {
|
|
569
|
+
self.view.prefs.addPerspective(null, 'Drill Down', { view: { filter: filter } }, { isTemporary: true }, null, { onDuplicate: 'replace' });
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
self.logDebug(self.makeLogTag() + ' Starting draw: [config = %O ; options = %O]', config, options);
|
|
575
|
+
chart.draw(dt, options);
|
|
576
|
+
self.fire('draw', null, config);
|
|
577
|
+
});
|
|
578
|
+
}, 'Drawing Google graph');
|
|
579
|
+
});
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
// Exports {{{1
|
|
583
|
+
|
|
584
|
+
export default GraphRendererGoogle;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import _ from 'underscore';
|
|
2
|
+
import jQuery from 'jquery';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
makeSubclass,
|
|
6
|
+
} from '../../util/misc.js';
|
|
7
|
+
|
|
8
|
+
import { GraphRenderer } from '../../graph_renderer.js';
|
|
9
|
+
|
|
10
|
+
// GraphRendererJit {{{1
|
|
11
|
+
|
|
12
|
+
var GraphRendererJit = makeSubclass('GraphRendererJit', GraphRenderer);
|
|
13
|
+
|
|
14
|
+
// #draw {{{2
|
|
15
|
+
|
|
16
|
+
GraphRendererJit.prototype.draw = function (elt) {
|
|
17
|
+
var self = this;
|
|
18
|
+
|
|
19
|
+
elt.children().remove();
|
|
20
|
+
|
|
21
|
+
self.view.getData(function (ok, data) {
|
|
22
|
+
self.view.getTypeInfo(function () {
|
|
23
|
+
var ctor = {
|
|
24
|
+
area: 'AreaChart',
|
|
25
|
+
bar: 'BarChart',
|
|
26
|
+
line: 'LineChart'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
var json = {
|
|
30
|
+
label: [],
|
|
31
|
+
values: []
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
_.each(self.opts.valueFields, function (f) {
|
|
35
|
+
json.label.push(f);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
_.each(data.data, function (row) {
|
|
39
|
+
var newRow = {};
|
|
40
|
+
newRow.label = row.rowData[self.opts.categoryField].value;
|
|
41
|
+
newRow.values = _.map(self.opts.valueFields, function (f) {
|
|
42
|
+
return row.rowData[f].value;
|
|
43
|
+
});
|
|
44
|
+
json.values.push(newRow);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
var options = {
|
|
48
|
+
injectInto: elt.attr('id')
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
jQuery.extend(true, options, self.opts.options);
|
|
52
|
+
|
|
53
|
+
var chart = new $jit[ctor[self.opts.type]](options);
|
|
54
|
+
chart.loadJSON(json);
|
|
55
|
+
});
|
|
56
|
+
}, 'Drawing JIT graph');
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Exports {{{1
|
|
60
|
+
|
|
61
|
+
export default GraphRendererJit;
|