@hpcc-js/common 3.6.5 → 3.7.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 (54) hide show
  1. package/LICENSE +43 -43
  2. package/README.md +59 -59
  3. package/dist/index.js +4 -4
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.umd.cjs +1 -1
  6. package/dist/index.umd.cjs.map +1 -1
  7. package/package.json +2 -2
  8. package/src/CanvasWidget.ts +31 -31
  9. package/src/Class.ts +72 -72
  10. package/src/Database.ts +860 -860
  11. package/src/Entity.ts +235 -235
  12. package/src/EntityCard.ts +66 -66
  13. package/src/EntityPin.ts +103 -103
  14. package/src/EntityRect.css +15 -15
  15. package/src/EntityRect.ts +254 -254
  16. package/src/EntityVertex.ts +86 -86
  17. package/src/FAChar.css +2 -2
  18. package/src/FAChar.ts +89 -89
  19. package/src/HTMLWidget.ts +191 -191
  20. package/src/IList.ts +4 -4
  21. package/src/IMenu.ts +5 -5
  22. package/src/Icon.css +9 -9
  23. package/src/Icon.ts +176 -176
  24. package/src/Image.ts +104 -104
  25. package/src/List.css +13 -13
  26. package/src/List.ts +102 -102
  27. package/src/Menu.css +23 -23
  28. package/src/Menu.ts +139 -139
  29. package/src/Palette.ts +341 -341
  30. package/src/Platform.ts +125 -125
  31. package/src/ProgressBar.ts +115 -115
  32. package/src/PropertyExt.ts +770 -770
  33. package/src/ResizeSurface.css +39 -39
  34. package/src/ResizeSurface.ts +225 -225
  35. package/src/SVGWidget.ts +583 -583
  36. package/src/SVGZoomWidget.css +12 -12
  37. package/src/SVGZoomWidget.ts +427 -427
  38. package/src/Shape.css +3 -3
  39. package/src/Shape.ts +186 -186
  40. package/src/Surface.css +35 -35
  41. package/src/Surface.ts +364 -364
  42. package/src/Text.css +4 -4
  43. package/src/Text.ts +131 -131
  44. package/src/TextBox.css +4 -4
  45. package/src/TextBox.ts +183 -183
  46. package/src/TitleBar.css +114 -114
  47. package/src/TitleBar.ts +407 -407
  48. package/src/Transition.ts +45 -45
  49. package/src/Utility.ts +843 -839
  50. package/src/Widget.css +8 -8
  51. package/src/Widget.ts +731 -731
  52. package/src/WidgetArray.ts +15 -15
  53. package/src/__package__.ts +3 -3
  54. package/src/index.ts +55 -55
package/src/Database.ts CHANGED
@@ -1,860 +1,860 @@
1
- import { deviation as d3Deviation, max as d3Max, mean as d3Mean, median as d3Median, min as d3Min, sum as d3Sum, variance as d3Variance } from "d3-array";
2
- import { map as d3Map, nest as d3Nest } from "d3-collection";
3
- import { csvFormatRows as d3CsvFormatRows, csvParse as d3CsvParse, tsvFormatRows as d3TsvFormatRows, tsvParse as d3TsvParse } from "d3-dsv";
4
- import { format as d3Format } from "d3-format";
5
- import { timeFormat as d3TimeFormat, timeParse as d3TimeParse } from "d3-time-format";
6
- import { PropertyExt } from "./PropertyExt.ts";
7
- import * as Utility from "./Utility.ts";
8
-
9
- const d3Aggr = {
10
- min: d3Min,
11
- max: d3Max,
12
- mean: d3Mean,
13
- median: d3Median,
14
- variance: d3Variance,
15
- deviation: d3Deviation,
16
- sum: d3Sum
17
- };
18
-
19
- let lastFoundFormat = null;
20
-
21
- export interface INestedColumn {
22
- label: string;
23
- columns: Array<string | INestedColumn>;
24
- }
25
-
26
- // Field ---
27
- export type FieldType = "string" | "number" | "boolean" | "date" | "time" | "hidden" | "nested";
28
-
29
- export class Field extends PropertyExt {
30
- private _owner: Grid;
31
- idx: number;
32
- protected _children: Field[] = [];
33
-
34
- constructor(id?) {
35
- super();
36
-
37
- this._id = id || this._id;
38
- }
39
-
40
- owner(): Grid;
41
- owner(_: Grid): this;
42
- owner(_?: Grid): Grid | this {
43
- if (!arguments.length) return this._owner;
44
- this._owner = _;
45
- return this;
46
- }
47
-
48
- valid(): boolean {
49
- return !!this.label();
50
- }
51
-
52
- checksum(): string {
53
- return Utility.checksum(this.label() + this.type() + this.mask() + this.format());
54
- }
55
-
56
- typeTransformer(_: any) {
57
- switch (this.type()) {
58
- case "number":
59
- return Number(_);
60
- case "string":
61
- return String(_);
62
- case "boolean":
63
- return typeof (_) === "string" && ["false", "off", "0"].indexOf(_.toLowerCase()) >= 0 ? false : Boolean(_);
64
- case "time":
65
- case "date":
66
- return this.maskTransformer(_);
67
- }
68
- return _;
69
- }
70
-
71
- maskTransformer(_) {
72
- return this.formatter(this.mask()).parse(String(_));
73
- }
74
-
75
- formatTransformer(_) {
76
- return this.formatter(this.format())(_);
77
- }
78
-
79
- parse(_) {
80
- if (!_) {
81
- return _;
82
- }
83
- try {
84
- return this.typeTransformer(_);
85
- } catch (e) {
86
- console.warn("Unable to parse: " + _);
87
- return null;
88
- }
89
- }
90
-
91
- transform(_) {
92
- if (!_) {
93
- return _;
94
- }
95
- try {
96
- return this.formatTransformer(this.typeTransformer(_));
97
- } catch (e) {
98
- console.warn("Unable to transform: " + _);
99
- return null;
100
- }
101
- }
102
-
103
- clone() {
104
- const context = this;
105
- const retVal = new Field(this._id);
106
- cloneProp(retVal, "label");
107
- cloneProp(retVal, "type");
108
- cloneProp(retVal, "mask");
109
- cloneProp(retVal, "format");
110
-
111
- function cloneProp(dest, key) {
112
- dest[key + "_default"](context[key + "_default"]());
113
- if (context[key + "_exists"]()) {
114
- dest[key](context[key]());
115
- }
116
- }
117
- return retVal;
118
- }
119
-
120
- formatter(format) {
121
- let retVal;
122
- if (!format) {
123
- retVal = function (_) {
124
- return _;
125
- };
126
- retVal.parse = function (_) {
127
- return _;
128
- };
129
- return retVal;
130
- }
131
- switch (this.type()) {
132
- case "time":
133
- case "date":
134
- return d3TimeFormat(format);
135
- }
136
- retVal = d3Format(format);
137
- retVal.parse = function (_) {
138
- return _;
139
- };
140
- return retVal;
141
- }
142
-
143
- children(): Field[];
144
- children(_: Array<string | INestedColumn | Field>, asDefault?: boolean): this;
145
- children(_?: Array<string | INestedColumn | Field>, asDefault?: boolean): Field[] | this {
146
- if (_ === void 0) return this._children;
147
- this.type("nested");
148
- const fieldsArr = this._children;
149
- this._children = _.map((field: string | INestedColumn | Field, idx): Field => {
150
- if (field instanceof Field) {
151
- fieldsArr[idx] = field;
152
- return field;
153
- } else if (typeof field === "string") {
154
- if (asDefault) {
155
- return (fieldsArr[idx] || new Field()).label_default(field);
156
- } else {
157
- return (fieldsArr[idx] || new Field()).label(field);
158
- }
159
- } else {
160
- if (asDefault) {
161
- return (fieldsArr[idx] || new Field())
162
- .label_default(field.label)
163
- .children(field.columns) as Field
164
- ;
165
- } else {
166
- return (fieldsArr[idx] || new Field())
167
- .label(field.label)
168
- .children(field.columns) as Field
169
- ;
170
- }
171
- }
172
- }, this);
173
- return this;
174
- }
175
-
176
- }
177
- Field.prototype._class += " common_Database.Field";
178
-
179
- export interface Field {
180
- label(): string;
181
- label(_: string): this;
182
- label_default(): string;
183
- label_default(_: string): this;
184
- type(): FieldType;
185
- type(_: FieldType): this;
186
- mask(): string;
187
- mask(_: string): this;
188
- format(): string;
189
- format(_: string): this;
190
- }
191
-
192
- Field.prototype.publish("label", "", "string", "Label", null, { optional: true });
193
- Field.prototype.publish("type", "", "set", "Type", ["", "string", "number", "boolean", "date", "time", "hidden", "nested"], { optional: true });
194
- Field.prototype.publish("mask", "", "string", "Time Mask", null, { disable: (w: any) => w.type() !== "time", optional: true });
195
- Field.prototype.publish("format", "", "string", "Format", null, { optional: true });
196
-
197
- // Grid ---
198
- export class Grid extends PropertyExt {
199
- _dataChecksum: boolean;
200
- _dataVersion: number;
201
-
202
- private _data = [];
203
- private _dataChecksums = [];
204
-
205
- constructor(dataChecksum?) {
206
- super();
207
- dataChecksum = dataChecksum || false;
208
- this._dataChecksum = dataChecksum;
209
- this._dataVersion = 0;
210
- this.clear();
211
- }
212
-
213
- clear() {
214
- this.fields([]);
215
- this._data = [];
216
- this._dataChecksums = [];
217
- ++this._dataVersion;
218
- return this;
219
- }
220
-
221
- // Backward compatability ---
222
- resetColumns() {
223
- const fields = this.fields() as Field[];
224
- this.legacyColumns([]);
225
- this.legacyColumns(fields.map(function (field) {
226
- return field.label();
227
- }));
228
- }
229
-
230
- legacyColumns(_?, asDefault?): any | Grid {
231
- if (!arguments.length) return this.row(0);
232
- this.row(0, _, asDefault);
233
- return this;
234
- }
235
-
236
- legacyData(_?, _clone?) {
237
- return Grid.prototype.data.apply(this, arguments);
238
- }
239
-
240
- // Meta ---
241
- schema() {
242
- }
243
-
244
- field(idx) {
245
- return this.fields()[idx];
246
- }
247
-
248
- fieldByLabel(_, ignoreCase?) {
249
- return this.fields().filter(function (field: Field, idx) {
250
- field.idx = idx;
251
- return ignoreCase ? field.label().toLowerCase() === _.toLowerCase() : field.label() === _;
252
- })[0];
253
- }
254
-
255
- data(_?, clone?): any | Grid {
256
- if (!arguments.length) return this._data;
257
- this._data = clone ? _.map(function (d) { return d.map(function (d2) { return d2; }); }) : _;
258
- this._dataCalcChecksum();
259
- return this;
260
- }
261
-
262
- parsedData() {
263
- const context = this;
264
- return this._data.map(function (row) {
265
- return row.map(function (cell, idx) {
266
- return context.fields()[idx].parse(cell);
267
- });
268
- });
269
- }
270
-
271
- formattedData() {
272
- return this._data.map(row => {
273
- return this.fields().map((field, idx) => {
274
- return field.transform(row[idx]);
275
- });
276
- });
277
- }
278
-
279
- fieldsChecksum() {
280
- return Utility.checksum(this.fields().map(function (field) { return field.checksum(); }));
281
- }
282
-
283
- dataChecksum() {
284
- return Utility.checksum(this._dataChecksum ? this._dataChecksums : this._dataVersion);
285
- }
286
-
287
- checksum() {
288
- return Utility.checksum([this.dataChecksum(), this.fieldsChecksum()]);
289
- }
290
-
291
- // Row Access ---
292
- private _dataCalcChecksum(idx?) {
293
- ++this._dataVersion;
294
- if (this._dataChecksum) {
295
- if (arguments.length) {
296
- this._dataChecksums[idx] = Utility.checksum(this._data[idx]);
297
- } else {
298
- this._dataChecksums = this._data.map(function (row) { return Utility.checksum(row); });
299
- }
300
- }
301
- return this;
302
- }
303
-
304
- row(row?, _?, asDefault?): any | Grid {
305
- if (arguments.length < 2) return row === 0 ? this.fields().map(function (d) { return d.label(); }) : this._data[row - 1];
306
- if (row === 0) {
307
- const fieldsArr = this.fields();
308
- this.fields(_.map(function (field: string | INestedColumn, idx) {
309
- if (typeof field === "string") {
310
- if (asDefault) {
311
- return (fieldsArr[idx] || new Field()).label_default(field);
312
- } else {
313
- return (fieldsArr[idx] || new Field()).label(field);
314
- }
315
- } else {
316
- if (asDefault) {
317
- return (fieldsArr[idx] || new Field())
318
- .label_default(field.label)
319
- .children(field.columns)
320
- ;
321
- } else {
322
- return (fieldsArr[idx] || new Field())
323
- .label(field.label)
324
- .children(field.columns)
325
- ;
326
- }
327
- }
328
- }, this));
329
- } else {
330
- this._data[row - 1] = _;
331
- this._dataCalcChecksum(row - 1);
332
- }
333
- return this;
334
- }
335
-
336
- rows(_?): any | Grid {
337
- if (!arguments.length) return [this.row(0)].concat(this._data);
338
- this.row(0, _[0]);
339
- this._data = _.filter(function (_row, idx) { return idx > 0; });
340
- this._dataCalcChecksum();
341
- return this;
342
- }
343
-
344
- // Column Access ---
345
- column(col, _?): any | Grid {
346
- if (arguments.length < 2) return [this.fields()[col].label()].concat(this._data.map(function (row, _idx) { return row[col]; }));
347
- _.forEach(function (d, idx) {
348
- if (idx === 0) {
349
- this.fields()[col] = new Field().label(_[0]);
350
- } else {
351
- this._data[idx - 1][col] = d;
352
- this._dataCalcChecksum(idx - 1);
353
- }
354
- }, this);
355
- return this;
356
- }
357
-
358
- columnData(col, _): any | Grid {
359
- if (arguments.length < 2) return this._data.map(function (row, _idx) { return row[col]; });
360
- _.forEach(function (d, idx) {
361
- this._data[idx][col] = d;
362
- this._dataCalcChecksum(idx);
363
- }, this);
364
- return this;
365
- }
366
-
367
- columns(_?): any | Grid {
368
- if (!arguments.length) return this.fields().map(function (_col, idx) {
369
- return this.column(idx);
370
- }, this);
371
- _.forEach(function (_col, idx) {
372
- this.column(idx, _[idx]);
373
- }, this);
374
- return this;
375
- }
376
-
377
- // Cell Access ---
378
- cell(row, col, _) {
379
- if (arguments.length < 3) return this.row(row)[col];
380
- if (row === 0) {
381
- this.fields()[col] = new Field().label(_);
382
- } else {
383
- this._data[row][col] = _;
384
- this._dataCalcChecksum(row);
385
- }
386
- return this;
387
- }
388
-
389
- // Grid Access ---
390
- grid(_?) {
391
- return Grid.prototype.rows.apply(this, arguments);
392
- }
393
-
394
- // Hipie Helpers ---
395
-
396
- hipieMappings(columns, missingDataString) {
397
- missingDataString = missingDataString || "";
398
- if (!this.fields().length || !this._data.length) {
399
- return [];
400
- }
401
- const mappedColumns = [];
402
- let hasRollup = false;
403
- columns.forEach(function (mapping, idx) {
404
- const mappedColumn = {
405
- groupby: false,
406
- func: "",
407
- params: []
408
- };
409
- if (mapping.hasFunction()) {
410
- mappedColumn.func = mapping.function();
411
- if (mappedColumn.func === "SCALE") {
412
- mappedColumn.groupby = true;
413
- } else {
414
- hasRollup = true;
415
- }
416
-
417
- mapping.params().forEach(function (param) {
418
- const field = this.fieldByLabel(param, true);
419
- mappedColumn.params.push(field ? field.idx : -1);
420
- }, this);
421
- } else {
422
- mappedColumn.groupby = true;
423
- const field = this.fieldByLabel(mapping.id(), true);
424
- mappedColumn.params.push(field ? field.idx : -1);
425
- }
426
- mappedColumns.push(mappedColumn);
427
- }, this);
428
-
429
- if (hasRollup) {
430
- const retVal = [];
431
- this.rollup(mappedColumns.filter(function (mappedColumn) {
432
- return mappedColumn.groupby === true;
433
- }).map(function (d) {
434
- return d.params[0];
435
- }), function (leaves) {
436
- const row = mappedColumns.map(function (mappedColumn) {
437
- const param1 = mappedColumn.params[0];
438
- const param2 = mappedColumn.params[1];
439
- switch (mappedColumn.func) {
440
- case "SUM":
441
- return d3Sum(leaves, function (d) { return d[param1]; });
442
- case "AVE":
443
- return d3Mean(leaves, function (d) { return d[param1] / d[param2]; });
444
- case "MIN":
445
- return d3Min(leaves, function (d) { return d[param1]; });
446
- case "MAX":
447
- return d3Max(leaves, function (d) { return d[param1]; });
448
- case "SCALE":
449
- console.warn("Unexpected function: " + mappedColumn.func);
450
- // All leaves should have the same values, use mean just in case they don't?
451
- return d3Mean(leaves, function (d) { return d[param1] / +param2; });
452
- }
453
- // All leaves should have the same value.
454
- return leaves[0][param1];
455
- });
456
- retVal.push(row);
457
- return row;
458
- });
459
- return retVal;
460
- } else {
461
- return this._data.map(function (row) {
462
- return mappedColumns.map(function (mappedColumn) {
463
- const param1 = mappedColumn.params[0];
464
- const param2 = mappedColumn.params[1];
465
- switch (mappedColumn.func) {
466
- case "SCALE":
467
- return row[param1] / +param2;
468
- case "SUM":
469
- case "AVE":
470
- case "MIN":
471
- case "MAX":
472
- console.warn("Unexpected function: " + mappedColumn.func);
473
- }
474
- return row[param1];
475
- });
476
- });
477
- }
478
- }
479
-
480
- legacyView() {
481
- return new LegacyView(this);
482
- }
483
-
484
- nestView(columnIndicies) {
485
- return new RollupView(this, columnIndicies);
486
- }
487
-
488
- rollupView(columnIndicies, rollupFunc?) {
489
- return new RollupView(this, columnIndicies, rollupFunc);
490
- }
491
-
492
- aggregateView(columnIndicies, aggrType, aggrColumn, aggrDeltaColumn = "") {
493
- const context = this;
494
- return new RollupView(this, columnIndicies, function (values) {
495
- switch (aggrType) {
496
- case null:
497
- case undefined:
498
- case "":
499
- values.aggregate = values.length;
500
- return values;
501
- default:
502
- const columns = context.legacyColumns();
503
- const colIdx = columns.indexOf(aggrColumn);
504
- const deltaIdx = columns.indexOf(aggrDeltaColumn);
505
- values.aggregate = d3Aggr[aggrType](values, function (value) {
506
- return (+value[colIdx] - (deltaIdx >= 0 ? +value[deltaIdx] : 0)) / (deltaIdx >= 0 ? +value[deltaIdx] : 1);
507
- });
508
- return values;
509
- }
510
- });
511
- }
512
-
513
- // Nesting ---
514
- private _nest(columnIndicies, _rollup?) {
515
- if (!(columnIndicies instanceof Array)) {
516
- columnIndicies = [columnIndicies];
517
- }
518
- const nest = d3Nest();
519
- columnIndicies.forEach(function (idx) {
520
- nest.key(function (d) {
521
- return d[idx];
522
- });
523
- });
524
- return nest;
525
- }
526
-
527
- nest(columnIndicies) {
528
- return this._nest(columnIndicies)
529
- .entries(this._data)
530
- ;
531
- }
532
-
533
- rollup(columnIndicies, rollup) {
534
- return this._nest(columnIndicies)
535
- .rollup(rollup)
536
- .entries(this._data)
537
- ;
538
- }
539
-
540
- // Util ---
541
- length() {
542
- return this._data.length + 1;
543
- }
544
-
545
- width() {
546
- return this.fields().length;
547
- }
548
-
549
- pivot() {
550
- this.resetColumns();
551
- this.rows(this.columns());
552
- return this;
553
- }
554
-
555
- clone(deep) {
556
- return new Grid()
557
- .fields(this.fields(), deep)
558
- .data(this.data(), deep)
559
- ;
560
- }
561
-
562
- filter(filter) {
563
- const filterIdx = {};
564
- this.row(0).forEach(function (col, idx) {
565
- filterIdx[col] = idx;
566
- });
567
- return new Grid()
568
- .fields(this.fields(), true)
569
- .data(this.data().filter(function (row) {
570
- for (const key in filter) {
571
- if (filter[key] !== row[filterIdx[key]]) {
572
- return false;
573
- }
574
- }
575
- return true;
576
- }))
577
- ;
578
- }
579
-
580
- analyse(columns) {
581
- if (!(columns instanceof Array)) {
582
- columns = [columns];
583
- }
584
- const retVal = [];
585
- columns.forEach(function (col) {
586
- const rollup = this.rollup(col, function (leaves) {
587
- return leaves.length;
588
- });
589
- retVal.push(rollup);
590
- const keys = rollup.map(function (d) { return d.key; });
591
- this.fields()[col].isBoolean = typeTest(keys, isBoolean);
592
- this.fields()[col].isNumber = typeTest(keys, isNumber);
593
- this.fields()[col].isString = !this.fields()[col].isNumber && typeTest(keys, isString);
594
- this.fields()[col].isUSState = this.fields()[col].isString && typeTest(keys, isUSState);
595
- this.fields()[col].isDateTime = this.fields()[col].isString && typeTest(keys, isDateTime);
596
- this.fields()[col].isDateTimeFormat = lastFoundFormat;
597
- this.fields()[col].isDate = !this.fields()[col].isDateTime && typeTest(keys, isDate);
598
- this.fields()[col].isDateFormat = lastFoundFormat;
599
- this.fields()[col].isTime = this.fields()[col].isString && !this.fields()[col].isDateTime && !this.fields()[col].isDate && typeTest(keys, isTime);
600
- this.fields()[col].isTimeFormat = lastFoundFormat;
601
- }, this);
602
- return retVal;
603
- }
604
-
605
- // Import/Export ---
606
- jsonObj(_?): any | Grid {
607
- if (!arguments.length) return this._data.map(function (row) {
608
- const retVal = {};
609
- this.row(0).forEach(function (col, idx) {
610
- retVal[col] = row[idx];
611
- });
612
- return retVal;
613
- }, this);
614
- this.clear();
615
- this.data(_.map(function (row) {
616
- const retVal = [];
617
- for (const key in row) {
618
- let colIdx = this.row(0).indexOf(key);
619
- if (colIdx < 0) {
620
- colIdx = this.fields().length;
621
- this.fields().push(new Field().label(key));
622
- }
623
- retVal[colIdx] = row[key];
624
- }
625
- return retVal;
626
- }, this));
627
- return this;
628
- }
629
-
630
- json(_: string | object): this;
631
- json(): string;
632
- json(_?: string | object): string | this {
633
- if (!arguments.length) return JSON.stringify(this.jsonObj(), null, " ");
634
- if (typeof (_) === "string") {
635
- _ = JSON.parse(_);
636
- }
637
- this.jsonObj(_);
638
- return this;
639
- }
640
-
641
- csv(_: string): this;
642
- csv(): string;
643
- csv(_?: string): string | this {
644
- if (!arguments.length) {
645
- const temp = document.createElement("div");
646
- return d3CsvFormatRows(this.grid().map(row => {
647
- return row.map(cell => {
648
- return Utility.removeHTMLFromString(cell, temp);
649
- });
650
- }));
651
- }
652
- this.jsonObj(d3CsvParse(_));
653
- return this;
654
- }
655
-
656
- tsv(_: string): this;
657
- tsv(): string;
658
- tsv(_?: string): string | this {
659
- if (!arguments.length) return d3TsvFormatRows(this.grid());
660
- this.jsonObj(d3TsvParse(_));
661
- return this;
662
- }
663
- }
664
- Grid.prototype._class += " common_Database.Grid";
665
- export interface Grid {
666
- fields(): Field[];
667
- fields(_: Field[], clone?: boolean): this;
668
- }
669
-
670
- Grid.prototype.publish("fields", [], "propertyArray", "Fields");
671
- const fieldsOrig = Grid.prototype.fields;
672
- Grid.prototype.fields = function (_?, clone?) {
673
- if (!arguments.length) return fieldsOrig.apply(this, arguments);
674
- return fieldsOrig.call(this, clone ? _.map(function (d) { return d.clone(); }) : _);
675
- };
676
-
677
- // Views ---
678
- export class LegacyView {
679
- _grid;
680
- _parsedData;
681
- _parsedDataChecksum;
682
- _formattedData;
683
- _formattedDataChecksum;
684
-
685
- constructor(grid) {
686
- this._grid = grid;
687
- }
688
-
689
- checksum() {
690
- const value = this._grid.on.apply(this._grid, arguments);
691
- return value === this._grid ? this : value;
692
- }
693
-
694
- fields() {
695
- const value = this._grid.on.apply(this._grid, arguments);
696
- return value === this._grid ? this : value;
697
- }
698
-
699
- grid() {
700
- return this._grid;
701
- }
702
- columns(_?) {
703
- if (!arguments.length) return this._grid.legacyColumns();
704
- this._grid.legacyColumns(_);
705
- return this;
706
- }
707
- rawData(_?) {
708
- if (!arguments.length) return this._grid.legacyData();
709
- this._grid.legacyData(_);
710
- return this;
711
- }
712
- formattedData() {
713
- if (this._formattedDataChecksum !== this._grid.checksum()) {
714
- this._formattedDataChecksum = this._grid.checksum();
715
- this._formattedData = this._grid.formattedData();
716
- }
717
- return this._formattedData;
718
- }
719
- parsedData() {
720
- if (this._parsedDataChecksum !== this._grid.checksum()) {
721
- this._parsedDataChecksum = this._grid.checksum();
722
- this._parsedData = this._grid.parsedData();
723
- }
724
- return this._parsedData;
725
- }
726
- protected _whichData(opts?) {
727
- if (opts) {
728
- if (opts.parsed) {
729
- return this.formattedData();
730
- } else if (opts.formatted) {
731
- return this.formattedData();
732
- }
733
- }
734
- return this.rawData();
735
- }
736
- data(_) {
737
- return LegacyView.prototype.rawData.apply(this, arguments);
738
- }
739
- }
740
-
741
- export class RollupView extends LegacyView {
742
- _columnIndicies;
743
- _rollup;
744
- _nestChecksum;
745
- _nest;
746
-
747
- constructor(grid, columns, rollup?) {
748
- super(grid);
749
- if (!(columns instanceof Array)) {
750
- columns = [columns];
751
- }
752
- this._columnIndicies = columns.filter(function (column) { return column; }).map(function (column) {
753
- switch (typeof column) {
754
- case "string":
755
- return this._grid.fieldByLabel(column).idx;
756
- }
757
- return column;
758
- }, this);
759
-
760
- rollup = rollup || function (d) { return d; };
761
- this._rollup = rollup;
762
- }
763
-
764
- nest() {
765
- if (this._nestChecksum !== this._grid.checksum()) {
766
- this._nestChecksum = this._grid.checksum();
767
-
768
- const nest = d3Nest();
769
- this._columnIndicies.forEach(function (idx) {
770
- nest.key(function (d) {
771
- return d[idx];
772
- });
773
- });
774
- this._nest = nest
775
- .rollup(this._rollup)
776
- ;
777
- }
778
- return this._nest;
779
- }
780
- entries(opts?) {
781
- return this.nest().entries(this._whichData(opts));
782
- }
783
- map(opts) {
784
- return this.nest().map(this._whichData(opts));
785
- }
786
- d3Map(opts) {
787
- return this.nest().map(this._whichData(opts), d3Map);
788
- }
789
- _walkData(entries, prevRow = []) {
790
- let retVal = [];
791
- entries.forEach(function (entry) {
792
- if (entry instanceof Array) {
793
- retVal.push(prevRow.concat([entry]));
794
- } else if (entry.values instanceof Array) {
795
- retVal = retVal.concat(this._walkData(entry.values, prevRow.concat([entry.key])));
796
- } else if (entry.value instanceof Array) {
797
- retVal = retVal.concat(this._walkData(entry.value, prevRow.concat([entry.key])));
798
- }
799
- }, this);
800
- return retVal;
801
- }
802
- data(opts) {
803
- return this._walkData(this.entries(opts));
804
- }
805
- }
806
-
807
- // --- --- ---
808
- function typeTest(cells, test) {
809
- if (!(cells instanceof Array)) {
810
- cells = [cells];
811
- }
812
- return cells.filter(function (d) { return d !== ""; }).every(test);
813
- }
814
- function isBoolean(cell) {
815
- return typeof cell === "boolean";
816
- }
817
- function isNumber(cell) {
818
- return typeof cell === "number" || !isNaN(cell);
819
- }
820
- function isString(cell) {
821
- return typeof cell === "string";
822
- }
823
- const dateTimeFormats = [
824
- ];
825
- const dateFormats = [
826
- "%Y-%m-%d",
827
- "%Y%m%d"
828
- ];
829
- const timeFormats = [
830
- "%H:%M:%S.%LZ",
831
- "%H:%M:%SZ",
832
- "%H:%M:%S"
833
- ];
834
- dateFormats.forEach(function (d) {
835
- timeFormats.forEach(function (t) {
836
- dateTimeFormats.push(d + "T" + t);
837
- });
838
- });
839
- function formatPicker(formats, cell) {
840
- for (let i = 0; i < formats.length; ++i) {
841
- const date = d3TimeParse(formats[i])(cell);
842
- if (date) {
843
- lastFoundFormat = formats[i];
844
- return formats[i];
845
- }
846
- }
847
- return null;
848
- }
849
- function isDateTime(cell) {
850
- return formatPicker(dateTimeFormats, cell);
851
- }
852
- function isDate(cell) {
853
- return formatPicker(dateFormats, cell);
854
- }
855
- function isTime(cell) {
856
- return formatPicker(timeFormats, cell);
857
- }
858
- function isUSState(cell) {
859
- return ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY", "AS", "DC", "FM", "GU", "MH", "MP", "PW", "PR", "VI"].indexOf(String(cell).toUpperCase()) >= 0;
860
- }
1
+ import { deviation as d3Deviation, max as d3Max, mean as d3Mean, median as d3Median, min as d3Min, sum as d3Sum, variance as d3Variance } from "d3-array";
2
+ import { map as d3Map, nest as d3Nest } from "d3-collection";
3
+ import { csvFormatRows as d3CsvFormatRows, csvParse as d3CsvParse, tsvFormatRows as d3TsvFormatRows, tsvParse as d3TsvParse } from "d3-dsv";
4
+ import { format as d3Format } from "d3-format";
5
+ import { timeFormat as d3TimeFormat, timeParse as d3TimeParse } from "d3-time-format";
6
+ import { PropertyExt } from "./PropertyExt.ts";
7
+ import * as Utility from "./Utility.ts";
8
+
9
+ const d3Aggr = {
10
+ min: d3Min,
11
+ max: d3Max,
12
+ mean: d3Mean,
13
+ median: d3Median,
14
+ variance: d3Variance,
15
+ deviation: d3Deviation,
16
+ sum: d3Sum
17
+ };
18
+
19
+ let lastFoundFormat = null;
20
+
21
+ export interface INestedColumn {
22
+ label: string;
23
+ columns: Array<string | INestedColumn>;
24
+ }
25
+
26
+ // Field ---
27
+ export type FieldType = "string" | "number" | "boolean" | "date" | "time" | "hidden" | "nested";
28
+
29
+ export class Field extends PropertyExt {
30
+ private _owner: Grid;
31
+ idx: number;
32
+ protected _children: Field[] = [];
33
+
34
+ constructor(id?) {
35
+ super();
36
+
37
+ this._id = id || this._id;
38
+ }
39
+
40
+ owner(): Grid;
41
+ owner(_: Grid): this;
42
+ owner(_?: Grid): Grid | this {
43
+ if (!arguments.length) return this._owner;
44
+ this._owner = _;
45
+ return this;
46
+ }
47
+
48
+ valid(): boolean {
49
+ return !!this.label();
50
+ }
51
+
52
+ checksum(): string {
53
+ return Utility.checksum(this.label() + this.type() + this.mask() + this.format());
54
+ }
55
+
56
+ typeTransformer(_: any) {
57
+ switch (this.type()) {
58
+ case "number":
59
+ return Number(_);
60
+ case "string":
61
+ return String(_);
62
+ case "boolean":
63
+ return typeof (_) === "string" && ["false", "off", "0"].indexOf(_.toLowerCase()) >= 0 ? false : Boolean(_);
64
+ case "time":
65
+ case "date":
66
+ return this.maskTransformer(_);
67
+ }
68
+ return _;
69
+ }
70
+
71
+ maskTransformer(_) {
72
+ return this.formatter(this.mask()).parse(String(_));
73
+ }
74
+
75
+ formatTransformer(_) {
76
+ return this.formatter(this.format())(_);
77
+ }
78
+
79
+ parse(_) {
80
+ if (!_) {
81
+ return _;
82
+ }
83
+ try {
84
+ return this.typeTransformer(_);
85
+ } catch (e) {
86
+ console.warn("Unable to parse: " + _);
87
+ return null;
88
+ }
89
+ }
90
+
91
+ transform(_) {
92
+ if (!_) {
93
+ return _;
94
+ }
95
+ try {
96
+ return this.formatTransformer(this.typeTransformer(_));
97
+ } catch (e) {
98
+ console.warn("Unable to transform: " + _);
99
+ return null;
100
+ }
101
+ }
102
+
103
+ clone() {
104
+ const context = this;
105
+ const retVal = new Field(this._id);
106
+ cloneProp(retVal, "label");
107
+ cloneProp(retVal, "type");
108
+ cloneProp(retVal, "mask");
109
+ cloneProp(retVal, "format");
110
+
111
+ function cloneProp(dest, key) {
112
+ dest[key + "_default"](context[key + "_default"]());
113
+ if (context[key + "_exists"]()) {
114
+ dest[key](context[key]());
115
+ }
116
+ }
117
+ return retVal;
118
+ }
119
+
120
+ formatter(format) {
121
+ let retVal;
122
+ if (!format) {
123
+ retVal = function (_) {
124
+ return _;
125
+ };
126
+ retVal.parse = function (_) {
127
+ return _;
128
+ };
129
+ return retVal;
130
+ }
131
+ switch (this.type()) {
132
+ case "time":
133
+ case "date":
134
+ return d3TimeFormat(format);
135
+ }
136
+ retVal = d3Format(format);
137
+ retVal.parse = function (_) {
138
+ return _;
139
+ };
140
+ return retVal;
141
+ }
142
+
143
+ children(): Field[];
144
+ children(_: Array<string | INestedColumn | Field>, asDefault?: boolean): this;
145
+ children(_?: Array<string | INestedColumn | Field>, asDefault?: boolean): Field[] | this {
146
+ if (_ === void 0) return this._children;
147
+ this.type("nested");
148
+ const fieldsArr = this._children;
149
+ this._children = _.map((field: string | INestedColumn | Field, idx): Field => {
150
+ if (field instanceof Field) {
151
+ fieldsArr[idx] = field;
152
+ return field;
153
+ } else if (typeof field === "string") {
154
+ if (asDefault) {
155
+ return (fieldsArr[idx] || new Field()).label_default(field);
156
+ } else {
157
+ return (fieldsArr[idx] || new Field()).label(field);
158
+ }
159
+ } else {
160
+ if (asDefault) {
161
+ return (fieldsArr[idx] || new Field())
162
+ .label_default(field.label)
163
+ .children(field.columns) as Field
164
+ ;
165
+ } else {
166
+ return (fieldsArr[idx] || new Field())
167
+ .label(field.label)
168
+ .children(field.columns) as Field
169
+ ;
170
+ }
171
+ }
172
+ }, this);
173
+ return this;
174
+ }
175
+
176
+ }
177
+ Field.prototype._class += " common_Database.Field";
178
+
179
+ export interface Field {
180
+ label(): string;
181
+ label(_: string): this;
182
+ label_default(): string;
183
+ label_default(_: string): this;
184
+ type(): FieldType;
185
+ type(_: FieldType): this;
186
+ mask(): string;
187
+ mask(_: string): this;
188
+ format(): string;
189
+ format(_: string): this;
190
+ }
191
+
192
+ Field.prototype.publish("label", "", "string", "Label", null, { optional: true });
193
+ Field.prototype.publish("type", "", "set", "Type", ["", "string", "number", "boolean", "date", "time", "hidden", "nested"], { optional: true });
194
+ Field.prototype.publish("mask", "", "string", "Time Mask", null, { disable: (w: any) => w.type() !== "time", optional: true });
195
+ Field.prototype.publish("format", "", "string", "Format", null, { optional: true });
196
+
197
+ // Grid ---
198
+ export class Grid extends PropertyExt {
199
+ _dataChecksum: boolean;
200
+ _dataVersion: number;
201
+
202
+ private _data = [];
203
+ private _dataChecksums = [];
204
+
205
+ constructor(dataChecksum?) {
206
+ super();
207
+ dataChecksum = dataChecksum || false;
208
+ this._dataChecksum = dataChecksum;
209
+ this._dataVersion = 0;
210
+ this.clear();
211
+ }
212
+
213
+ clear() {
214
+ this.fields([]);
215
+ this._data = [];
216
+ this._dataChecksums = [];
217
+ ++this._dataVersion;
218
+ return this;
219
+ }
220
+
221
+ // Backward compatability ---
222
+ resetColumns() {
223
+ const fields = this.fields() as Field[];
224
+ this.legacyColumns([]);
225
+ this.legacyColumns(fields.map(function (field) {
226
+ return field.label();
227
+ }));
228
+ }
229
+
230
+ legacyColumns(_?, asDefault?): any | Grid {
231
+ if (!arguments.length) return this.row(0);
232
+ this.row(0, _, asDefault);
233
+ return this;
234
+ }
235
+
236
+ legacyData(_?, _clone?) {
237
+ return Grid.prototype.data.apply(this, arguments);
238
+ }
239
+
240
+ // Meta ---
241
+ schema() {
242
+ }
243
+
244
+ field(idx) {
245
+ return this.fields()[idx];
246
+ }
247
+
248
+ fieldByLabel(_, ignoreCase?) {
249
+ return this.fields().filter(function (field: Field, idx) {
250
+ field.idx = idx;
251
+ return ignoreCase ? field.label().toLowerCase() === _.toLowerCase() : field.label() === _;
252
+ })[0];
253
+ }
254
+
255
+ data(_?, clone?): any | Grid {
256
+ if (!arguments.length) return this._data;
257
+ this._data = clone ? _.map(function (d) { return d.map(function (d2) { return d2; }); }) : _;
258
+ this._dataCalcChecksum();
259
+ return this;
260
+ }
261
+
262
+ parsedData() {
263
+ const context = this;
264
+ return this._data.map(function (row) {
265
+ return row.map(function (cell, idx) {
266
+ return context.fields()[idx].parse(cell);
267
+ });
268
+ });
269
+ }
270
+
271
+ formattedData() {
272
+ return this._data.map(row => {
273
+ return this.fields().map((field, idx) => {
274
+ return field.transform(row[idx]);
275
+ });
276
+ });
277
+ }
278
+
279
+ fieldsChecksum() {
280
+ return Utility.checksum(this.fields().map(function (field) { return field.checksum(); }));
281
+ }
282
+
283
+ dataChecksum() {
284
+ return Utility.checksum(this._dataChecksum ? this._dataChecksums : this._dataVersion);
285
+ }
286
+
287
+ checksum() {
288
+ return Utility.checksum([this.dataChecksum(), this.fieldsChecksum()]);
289
+ }
290
+
291
+ // Row Access ---
292
+ private _dataCalcChecksum(idx?) {
293
+ ++this._dataVersion;
294
+ if (this._dataChecksum) {
295
+ if (arguments.length) {
296
+ this._dataChecksums[idx] = Utility.checksum(this._data[idx]);
297
+ } else {
298
+ this._dataChecksums = this._data.map(function (row) { return Utility.checksum(row); });
299
+ }
300
+ }
301
+ return this;
302
+ }
303
+
304
+ row(row?, _?, asDefault?): any | Grid {
305
+ if (arguments.length < 2) return row === 0 ? this.fields().map(function (d) { return d.label(); }) : this._data[row - 1];
306
+ if (row === 0) {
307
+ const fieldsArr = this.fields();
308
+ this.fields(_.map(function (field: string | INestedColumn, idx) {
309
+ if (typeof field === "string") {
310
+ if (asDefault) {
311
+ return (fieldsArr[idx] || new Field()).label_default(field);
312
+ } else {
313
+ return (fieldsArr[idx] || new Field()).label(field);
314
+ }
315
+ } else {
316
+ if (asDefault) {
317
+ return (fieldsArr[idx] || new Field())
318
+ .label_default(field.label)
319
+ .children(field.columns)
320
+ ;
321
+ } else {
322
+ return (fieldsArr[idx] || new Field())
323
+ .label(field.label)
324
+ .children(field.columns)
325
+ ;
326
+ }
327
+ }
328
+ }, this));
329
+ } else {
330
+ this._data[row - 1] = _;
331
+ this._dataCalcChecksum(row - 1);
332
+ }
333
+ return this;
334
+ }
335
+
336
+ rows(_?): any | Grid {
337
+ if (!arguments.length) return [this.row(0)].concat(this._data);
338
+ this.row(0, _[0]);
339
+ this._data = _.filter(function (_row, idx) { return idx > 0; });
340
+ this._dataCalcChecksum();
341
+ return this;
342
+ }
343
+
344
+ // Column Access ---
345
+ column(col, _?): any | Grid {
346
+ if (arguments.length < 2) return [this.fields()[col].label()].concat(this._data.map(function (row, _idx) { return row[col]; }));
347
+ _.forEach(function (d, idx) {
348
+ if (idx === 0) {
349
+ this.fields()[col] = new Field().label(_[0]);
350
+ } else {
351
+ this._data[idx - 1][col] = d;
352
+ this._dataCalcChecksum(idx - 1);
353
+ }
354
+ }, this);
355
+ return this;
356
+ }
357
+
358
+ columnData(col, _): any | Grid {
359
+ if (arguments.length < 2) return this._data.map(function (row, _idx) { return row[col]; });
360
+ _.forEach(function (d, idx) {
361
+ this._data[idx][col] = d;
362
+ this._dataCalcChecksum(idx);
363
+ }, this);
364
+ return this;
365
+ }
366
+
367
+ columns(_?): any | Grid {
368
+ if (!arguments.length) return this.fields().map(function (_col, idx) {
369
+ return this.column(idx);
370
+ }, this);
371
+ _.forEach(function (_col, idx) {
372
+ this.column(idx, _[idx]);
373
+ }, this);
374
+ return this;
375
+ }
376
+
377
+ // Cell Access ---
378
+ cell(row, col, _) {
379
+ if (arguments.length < 3) return this.row(row)[col];
380
+ if (row === 0) {
381
+ this.fields()[col] = new Field().label(_);
382
+ } else {
383
+ this._data[row][col] = _;
384
+ this._dataCalcChecksum(row);
385
+ }
386
+ return this;
387
+ }
388
+
389
+ // Grid Access ---
390
+ grid(_?) {
391
+ return Grid.prototype.rows.apply(this, arguments);
392
+ }
393
+
394
+ // Hipie Helpers ---
395
+
396
+ hipieMappings(columns, missingDataString) {
397
+ missingDataString = missingDataString || "";
398
+ if (!this.fields().length || !this._data.length) {
399
+ return [];
400
+ }
401
+ const mappedColumns = [];
402
+ let hasRollup = false;
403
+ columns.forEach(function (mapping, idx) {
404
+ const mappedColumn = {
405
+ groupby: false,
406
+ func: "",
407
+ params: []
408
+ };
409
+ if (mapping.hasFunction()) {
410
+ mappedColumn.func = mapping.function();
411
+ if (mappedColumn.func === "SCALE") {
412
+ mappedColumn.groupby = true;
413
+ } else {
414
+ hasRollup = true;
415
+ }
416
+
417
+ mapping.params().forEach(function (param) {
418
+ const field = this.fieldByLabel(param, true);
419
+ mappedColumn.params.push(field ? field.idx : -1);
420
+ }, this);
421
+ } else {
422
+ mappedColumn.groupby = true;
423
+ const field = this.fieldByLabel(mapping.id(), true);
424
+ mappedColumn.params.push(field ? field.idx : -1);
425
+ }
426
+ mappedColumns.push(mappedColumn);
427
+ }, this);
428
+
429
+ if (hasRollup) {
430
+ const retVal = [];
431
+ this.rollup(mappedColumns.filter(function (mappedColumn) {
432
+ return mappedColumn.groupby === true;
433
+ }).map(function (d) {
434
+ return d.params[0];
435
+ }), function (leaves) {
436
+ const row = mappedColumns.map(function (mappedColumn) {
437
+ const param1 = mappedColumn.params[0];
438
+ const param2 = mappedColumn.params[1];
439
+ switch (mappedColumn.func) {
440
+ case "SUM":
441
+ return d3Sum(leaves, function (d) { return d[param1]; });
442
+ case "AVE":
443
+ return d3Mean(leaves, function (d) { return d[param1] / d[param2]; });
444
+ case "MIN":
445
+ return d3Min(leaves, function (d) { return d[param1]; });
446
+ case "MAX":
447
+ return d3Max(leaves, function (d) { return d[param1]; });
448
+ case "SCALE":
449
+ console.warn("Unexpected function: " + mappedColumn.func);
450
+ // All leaves should have the same values, use mean just in case they don't?
451
+ return d3Mean(leaves, function (d) { return d[param1] / +param2; });
452
+ }
453
+ // All leaves should have the same value.
454
+ return leaves[0][param1];
455
+ });
456
+ retVal.push(row);
457
+ return row;
458
+ });
459
+ return retVal;
460
+ } else {
461
+ return this._data.map(function (row) {
462
+ return mappedColumns.map(function (mappedColumn) {
463
+ const param1 = mappedColumn.params[0];
464
+ const param2 = mappedColumn.params[1];
465
+ switch (mappedColumn.func) {
466
+ case "SCALE":
467
+ return row[param1] / +param2;
468
+ case "SUM":
469
+ case "AVE":
470
+ case "MIN":
471
+ case "MAX":
472
+ console.warn("Unexpected function: " + mappedColumn.func);
473
+ }
474
+ return row[param1];
475
+ });
476
+ });
477
+ }
478
+ }
479
+
480
+ legacyView() {
481
+ return new LegacyView(this);
482
+ }
483
+
484
+ nestView(columnIndicies) {
485
+ return new RollupView(this, columnIndicies);
486
+ }
487
+
488
+ rollupView(columnIndicies, rollupFunc?) {
489
+ return new RollupView(this, columnIndicies, rollupFunc);
490
+ }
491
+
492
+ aggregateView(columnIndicies, aggrType, aggrColumn, aggrDeltaColumn = "") {
493
+ const context = this;
494
+ return new RollupView(this, columnIndicies, function (values) {
495
+ switch (aggrType) {
496
+ case null:
497
+ case undefined:
498
+ case "":
499
+ values.aggregate = values.length;
500
+ return values;
501
+ default:
502
+ const columns = context.legacyColumns();
503
+ const colIdx = columns.indexOf(aggrColumn);
504
+ const deltaIdx = columns.indexOf(aggrDeltaColumn);
505
+ values.aggregate = d3Aggr[aggrType](values, function (value) {
506
+ return (+value[colIdx] - (deltaIdx >= 0 ? +value[deltaIdx] : 0)) / (deltaIdx >= 0 ? +value[deltaIdx] : 1);
507
+ });
508
+ return values;
509
+ }
510
+ });
511
+ }
512
+
513
+ // Nesting ---
514
+ private _nest(columnIndicies, _rollup?) {
515
+ if (!(columnIndicies instanceof Array)) {
516
+ columnIndicies = [columnIndicies];
517
+ }
518
+ const nest = d3Nest();
519
+ columnIndicies.forEach(function (idx) {
520
+ nest.key(function (d) {
521
+ return d[idx];
522
+ });
523
+ });
524
+ return nest;
525
+ }
526
+
527
+ nest(columnIndicies) {
528
+ return this._nest(columnIndicies)
529
+ .entries(this._data)
530
+ ;
531
+ }
532
+
533
+ rollup(columnIndicies, rollup) {
534
+ return this._nest(columnIndicies)
535
+ .rollup(rollup)
536
+ .entries(this._data)
537
+ ;
538
+ }
539
+
540
+ // Util ---
541
+ length() {
542
+ return this._data.length + 1;
543
+ }
544
+
545
+ width() {
546
+ return this.fields().length;
547
+ }
548
+
549
+ pivot() {
550
+ this.resetColumns();
551
+ this.rows(this.columns());
552
+ return this;
553
+ }
554
+
555
+ clone(deep) {
556
+ return new Grid()
557
+ .fields(this.fields(), deep)
558
+ .data(this.data(), deep)
559
+ ;
560
+ }
561
+
562
+ filter(filter) {
563
+ const filterIdx = {};
564
+ this.row(0).forEach(function (col, idx) {
565
+ filterIdx[col] = idx;
566
+ });
567
+ return new Grid()
568
+ .fields(this.fields(), true)
569
+ .data(this.data().filter(function (row) {
570
+ for (const key in filter) {
571
+ if (filter[key] !== row[filterIdx[key]]) {
572
+ return false;
573
+ }
574
+ }
575
+ return true;
576
+ }))
577
+ ;
578
+ }
579
+
580
+ analyse(columns) {
581
+ if (!(columns instanceof Array)) {
582
+ columns = [columns];
583
+ }
584
+ const retVal = [];
585
+ columns.forEach(function (col) {
586
+ const rollup = this.rollup(col, function (leaves) {
587
+ return leaves.length;
588
+ });
589
+ retVal.push(rollup);
590
+ const keys = rollup.map(function (d) { return d.key; });
591
+ this.fields()[col].isBoolean = typeTest(keys, isBoolean);
592
+ this.fields()[col].isNumber = typeTest(keys, isNumber);
593
+ this.fields()[col].isString = !this.fields()[col].isNumber && typeTest(keys, isString);
594
+ this.fields()[col].isUSState = this.fields()[col].isString && typeTest(keys, isUSState);
595
+ this.fields()[col].isDateTime = this.fields()[col].isString && typeTest(keys, isDateTime);
596
+ this.fields()[col].isDateTimeFormat = lastFoundFormat;
597
+ this.fields()[col].isDate = !this.fields()[col].isDateTime && typeTest(keys, isDate);
598
+ this.fields()[col].isDateFormat = lastFoundFormat;
599
+ this.fields()[col].isTime = this.fields()[col].isString && !this.fields()[col].isDateTime && !this.fields()[col].isDate && typeTest(keys, isTime);
600
+ this.fields()[col].isTimeFormat = lastFoundFormat;
601
+ }, this);
602
+ return retVal;
603
+ }
604
+
605
+ // Import/Export ---
606
+ jsonObj(_?): any | Grid {
607
+ if (!arguments.length) return this._data.map(function (row) {
608
+ const retVal = {};
609
+ this.row(0).forEach(function (col, idx) {
610
+ retVal[col] = row[idx];
611
+ });
612
+ return retVal;
613
+ }, this);
614
+ this.clear();
615
+ this.data(_.map(function (row) {
616
+ const retVal = [];
617
+ for (const key in row) {
618
+ let colIdx = this.row(0).indexOf(key);
619
+ if (colIdx < 0) {
620
+ colIdx = this.fields().length;
621
+ this.fields().push(new Field().label(key));
622
+ }
623
+ retVal[colIdx] = row[key];
624
+ }
625
+ return retVal;
626
+ }, this));
627
+ return this;
628
+ }
629
+
630
+ json(_: string | object): this;
631
+ json(): string;
632
+ json(_?: string | object): string | this {
633
+ if (!arguments.length) return JSON.stringify(this.jsonObj(), null, " ");
634
+ if (typeof (_) === "string") {
635
+ _ = JSON.parse(_);
636
+ }
637
+ this.jsonObj(_);
638
+ return this;
639
+ }
640
+
641
+ csv(_: string): this;
642
+ csv(): string;
643
+ csv(_?: string): string | this {
644
+ if (!arguments.length) {
645
+ const temp = document.createElement("div");
646
+ return d3CsvFormatRows(this.grid().map(row => {
647
+ return row.map(cell => {
648
+ return Utility.removeHTMLFromString(cell, temp);
649
+ });
650
+ }));
651
+ }
652
+ this.jsonObj(d3CsvParse(_));
653
+ return this;
654
+ }
655
+
656
+ tsv(_: string): this;
657
+ tsv(): string;
658
+ tsv(_?: string): string | this {
659
+ if (!arguments.length) return d3TsvFormatRows(this.grid());
660
+ this.jsonObj(d3TsvParse(_));
661
+ return this;
662
+ }
663
+ }
664
+ Grid.prototype._class += " common_Database.Grid";
665
+ export interface Grid {
666
+ fields(): Field[];
667
+ fields(_: Field[], clone?: boolean): this;
668
+ }
669
+
670
+ Grid.prototype.publish("fields", [], "propertyArray", "Fields");
671
+ const fieldsOrig = Grid.prototype.fields;
672
+ Grid.prototype.fields = function (_?, clone?) {
673
+ if (!arguments.length) return fieldsOrig.apply(this, arguments);
674
+ return fieldsOrig.call(this, clone ? _.map(function (d) { return d.clone(); }) : _);
675
+ };
676
+
677
+ // Views ---
678
+ export class LegacyView {
679
+ _grid;
680
+ _parsedData;
681
+ _parsedDataChecksum;
682
+ _formattedData;
683
+ _formattedDataChecksum;
684
+
685
+ constructor(grid) {
686
+ this._grid = grid;
687
+ }
688
+
689
+ checksum() {
690
+ const value = this._grid.on.apply(this._grid, arguments);
691
+ return value === this._grid ? this : value;
692
+ }
693
+
694
+ fields() {
695
+ const value = this._grid.on.apply(this._grid, arguments);
696
+ return value === this._grid ? this : value;
697
+ }
698
+
699
+ grid() {
700
+ return this._grid;
701
+ }
702
+ columns(_?) {
703
+ if (!arguments.length) return this._grid.legacyColumns();
704
+ this._grid.legacyColumns(_);
705
+ return this;
706
+ }
707
+ rawData(_?) {
708
+ if (!arguments.length) return this._grid.legacyData();
709
+ this._grid.legacyData(_);
710
+ return this;
711
+ }
712
+ formattedData() {
713
+ if (this._formattedDataChecksum !== this._grid.checksum()) {
714
+ this._formattedDataChecksum = this._grid.checksum();
715
+ this._formattedData = this._grid.formattedData();
716
+ }
717
+ return this._formattedData;
718
+ }
719
+ parsedData() {
720
+ if (this._parsedDataChecksum !== this._grid.checksum()) {
721
+ this._parsedDataChecksum = this._grid.checksum();
722
+ this._parsedData = this._grid.parsedData();
723
+ }
724
+ return this._parsedData;
725
+ }
726
+ protected _whichData(opts?) {
727
+ if (opts) {
728
+ if (opts.parsed) {
729
+ return this.formattedData();
730
+ } else if (opts.formatted) {
731
+ return this.formattedData();
732
+ }
733
+ }
734
+ return this.rawData();
735
+ }
736
+ data(_) {
737
+ return LegacyView.prototype.rawData.apply(this, arguments);
738
+ }
739
+ }
740
+
741
+ export class RollupView extends LegacyView {
742
+ _columnIndicies;
743
+ _rollup;
744
+ _nestChecksum;
745
+ _nest;
746
+
747
+ constructor(grid, columns, rollup?) {
748
+ super(grid);
749
+ if (!(columns instanceof Array)) {
750
+ columns = [columns];
751
+ }
752
+ this._columnIndicies = columns.filter(function (column) { return column; }).map(function (column) {
753
+ switch (typeof column) {
754
+ case "string":
755
+ return this._grid.fieldByLabel(column).idx;
756
+ }
757
+ return column;
758
+ }, this);
759
+
760
+ rollup = rollup || function (d) { return d; };
761
+ this._rollup = rollup;
762
+ }
763
+
764
+ nest() {
765
+ if (this._nestChecksum !== this._grid.checksum()) {
766
+ this._nestChecksum = this._grid.checksum();
767
+
768
+ const nest = d3Nest();
769
+ this._columnIndicies.forEach(function (idx) {
770
+ nest.key(function (d) {
771
+ return d[idx];
772
+ });
773
+ });
774
+ this._nest = nest
775
+ .rollup(this._rollup)
776
+ ;
777
+ }
778
+ return this._nest;
779
+ }
780
+ entries(opts?) {
781
+ return this.nest().entries(this._whichData(opts));
782
+ }
783
+ map(opts) {
784
+ return this.nest().map(this._whichData(opts));
785
+ }
786
+ d3Map(opts) {
787
+ return this.nest().map(this._whichData(opts), d3Map);
788
+ }
789
+ _walkData(entries, prevRow = []) {
790
+ let retVal = [];
791
+ entries.forEach(function (entry) {
792
+ if (entry instanceof Array) {
793
+ retVal.push(prevRow.concat([entry]));
794
+ } else if (entry.values instanceof Array) {
795
+ retVal = retVal.concat(this._walkData(entry.values, prevRow.concat([entry.key])));
796
+ } else if (entry.value instanceof Array) {
797
+ retVal = retVal.concat(this._walkData(entry.value, prevRow.concat([entry.key])));
798
+ }
799
+ }, this);
800
+ return retVal;
801
+ }
802
+ data(opts) {
803
+ return this._walkData(this.entries(opts));
804
+ }
805
+ }
806
+
807
+ // --- --- ---
808
+ function typeTest(cells, test) {
809
+ if (!(cells instanceof Array)) {
810
+ cells = [cells];
811
+ }
812
+ return cells.filter(function (d) { return d !== ""; }).every(test);
813
+ }
814
+ function isBoolean(cell) {
815
+ return typeof cell === "boolean";
816
+ }
817
+ function isNumber(cell) {
818
+ return typeof cell === "number" || !isNaN(cell);
819
+ }
820
+ function isString(cell) {
821
+ return typeof cell === "string";
822
+ }
823
+ const dateTimeFormats = [
824
+ ];
825
+ const dateFormats = [
826
+ "%Y-%m-%d",
827
+ "%Y%m%d"
828
+ ];
829
+ const timeFormats = [
830
+ "%H:%M:%S.%LZ",
831
+ "%H:%M:%SZ",
832
+ "%H:%M:%S"
833
+ ];
834
+ dateFormats.forEach(function (d) {
835
+ timeFormats.forEach(function (t) {
836
+ dateTimeFormats.push(d + "T" + t);
837
+ });
838
+ });
839
+ function formatPicker(formats, cell) {
840
+ for (let i = 0; i < formats.length; ++i) {
841
+ const date = d3TimeParse(formats[i])(cell);
842
+ if (date) {
843
+ lastFoundFormat = formats[i];
844
+ return formats[i];
845
+ }
846
+ }
847
+ return null;
848
+ }
849
+ function isDateTime(cell) {
850
+ return formatPicker(dateTimeFormats, cell);
851
+ }
852
+ function isDate(cell) {
853
+ return formatPicker(dateFormats, cell);
854
+ }
855
+ function isTime(cell) {
856
+ return formatPicker(timeFormats, cell);
857
+ }
858
+ function isUSState(cell) {
859
+ return ["AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL", "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK", "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT", "VA", "WA", "WV", "WI", "WY", "AS", "DC", "FM", "GU", "MH", "MP", "PW", "PR", "VI"].indexOf(String(cell).toUpperCase()) >= 0;
860
+ }