@hpcc-js/common 3.6.5 → 3.7.1

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 +4 -4
  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 +10 -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 +40 -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
@@ -1,770 +1,770 @@
1
- import { hashSum } from "@hpcc-js/util";
2
- import { event as d3Event } from "d3-selection";
3
- import { Class } from "./Class.ts";
4
-
5
- const GEN_PUB_STUBS: boolean = false;
6
-
7
- function deepEqual(a, b) {
8
- if (a === b) return true;
9
-
10
- const arrA = Array.isArray(a);
11
- const arrB = Array.isArray(b);
12
- let i;
13
-
14
- if (arrA && arrB) {
15
- if (a.length !== b.length) return false;
16
- for (i = 0; i < a.length; i++)
17
- if (!deepEqual(a[i], b[i])) return false;
18
- return true;
19
- }
20
-
21
- if (arrA != arrB) return false;
22
-
23
- if (a && b && typeof a === "object" && typeof b === "object") {
24
- const keys = Object.keys(a);
25
- if (keys.length !== Object.keys(b).length) return false;
26
-
27
- const dateA = a instanceof Date;
28
- const dateB = b instanceof Date;
29
- if (dateA && dateB) return a.getTime() === b.getTime();
30
- if (dateA != dateB) return false;
31
-
32
- const regexpA = a instanceof RegExp;
33
- const regexpB = b instanceof RegExp;
34
- if (regexpA && regexpB) return a.toString() === b.toString();
35
- if (regexpA != regexpB) return false;
36
-
37
- for (i = 0; i < keys.length; i++)
38
- if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
39
-
40
- for (i = 0; i < keys.length; i++)
41
- if (!deepEqual(a[keys[i]], b[keys[i]])) return false;
42
-
43
- return true;
44
- }
45
-
46
- return false;
47
- }
48
-
49
- const __meta_ = "__meta_";
50
- const __private_ = "__private_";
51
- const __prop_ = "_";
52
- const __prop_data_ = "__prop_";
53
- const __default_ = "__default_";
54
-
55
- function isMeta(key) {
56
- return key.indexOf(__meta_) === 0;
57
- }
58
-
59
- function isPrivate(obj, key) {
60
- return obj[__private_ + key] || obj[__private_ + __meta_ + key];
61
- }
62
-
63
- export interface IAutoExpand extends PropertyExt {
64
- // AutoExpand items may have "incomplete" instances while the user is editing them in the PropertyEditor
65
- owner(): PropertyExt;
66
- owner(_: PropertyExt): this;
67
- valid(): boolean;
68
- }
69
-
70
- export type TagTypes = "Private" | "Shared" | "Basic" | "Intermediate" | "Advanced" | "Theme" | "Serial";
71
- export type PublishTypes = "any" | "number" | "boolean" | "string" | "set" | "array" | "object" | "widget" | "widgetArray" | "propertyArray" | "html-color" | "proxy";
72
- export interface IPublishExt {
73
- override?: boolean;
74
- disable?: (w) => boolean;
75
- validate?: (w) => boolean;
76
- hidden?: (w) => boolean;
77
- optional?: boolean;
78
- tags?: TagTypes[];
79
- autoExpand?: new () => IAutoExpand;
80
- noDeserialize?: boolean;
81
- render?: boolean;
82
- icons?: string[];
83
- editor_input?: (context, widget, cell, param) => void;
84
- saveButton?: string;
85
- saveButtonID?: string;
86
- number?: any;
87
- reset?: boolean;
88
- // Amcharts - really needed?
89
- min?: number;
90
- max?: number;
91
- step?: number;
92
- inputType?: string;
93
- internal?: boolean;
94
- range?: { min: number, max: number, step: number };
95
- multiline?: boolean;
96
- }
97
-
98
- export class Meta {
99
- id;
100
- type: PublishTypes;
101
- origDefaultValue;
102
- defaultValue;
103
- description;
104
- set;
105
- ext: IPublishExt;
106
- checkedAssign;
107
-
108
- constructor(id, defaultValue, type, description, set, ext?: IPublishExt) {
109
- ext = ext || {};
110
- this.id = id;
111
- this.type = type;
112
- this.origDefaultValue = defaultValue;
113
- this.defaultValue = ext.optional && defaultValue === null ? undefined : defaultValue;
114
- this.description = description;
115
- this.set = set;
116
- this.ext = ext;
117
-
118
- switch (type) {
119
- case "any":
120
- this.checkedAssign = _ => _;
121
- break;
122
- case "set":
123
- this.checkedAssign = function (_) {
124
- if ((window as any).__hpcc_debug) {
125
- const options = typeof set === "function" ? set.call(this) : set;
126
- if (options && options.length && options.indexOf(_) < 0) {
127
- console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + JSON.stringify(options));
128
- }
129
- }
130
- return _;
131
- };
132
- break;
133
- case "html-color":
134
- this.checkedAssign = function (_) {
135
- if ((window as any).__hpcc_debug && _ && _ !== "red") {
136
- const litmus = "red";
137
- const d = document.createElement("div");
138
- d.style.color = litmus;
139
- d.style.color = _;
140
- // Element's style.color will be reverted to litmus or set to "" if an invalid color is given
141
- if (d.style.color === litmus || d.style.color === "") {
142
- console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
143
- }
144
- }
145
- return _;
146
- };
147
- break;
148
- case "boolean":
149
- this.checkedAssign = function (_) {
150
- return typeof (_) === "string" && ["false", "off", "0"].indexOf(_.toLowerCase()) >= 0 ? false : Boolean(_);
151
- };
152
- break;
153
- case "number":
154
- this.checkedAssign = function (_) {
155
- return Number(_);
156
- };
157
- break;
158
- case "string":
159
- this.checkedAssign = function (_) {
160
- return String(_);
161
- };
162
- break;
163
- case "array":
164
- this.checkedAssign = function (_) {
165
- if (!(_ instanceof Array)) {
166
- console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
167
- }
168
- return _;
169
- };
170
- break;
171
- case "object":
172
- this.checkedAssign = function (_) {
173
- if (!(_ instanceof Object)) {
174
- console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
175
- }
176
- return _;
177
- };
178
- break;
179
- case "widget":
180
- this.checkedAssign = function (_) {
181
- if (!_._class || _._class.indexOf("common_PropertyExt") < 0) {
182
- console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
183
- }
184
- return _;
185
- };
186
- break;
187
- case "widgetArray":
188
- this.checkedAssign = function (_) {
189
- if (_.some(function (row) { return (!row._class || row._class.indexOf("common_Widget") < 0); })) {
190
- console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
191
- }
192
- return _;
193
- };
194
- break;
195
- case "propertyArray":
196
- this.checkedAssign = function (_) {
197
- if (_.some(function (row) { return !row.publishedProperties; })) {
198
- console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
199
- }
200
- return _;
201
- };
202
- break;
203
- default:
204
- this.checkedAssign = function (_) {
205
- if ((window as any).__hpcc_debug) {
206
- console.error("Unchecked property type for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
207
- }
208
- return _;
209
- };
210
- break;
211
- }
212
- }
213
- }
214
-
215
- class MetaProxy {
216
- id: string;
217
- type;
218
- proxy;
219
- method;
220
- defaultValue;
221
- ext: IPublishExt;
222
-
223
- constructor(id: string, proxy, method, defaultValue, ext?: IPublishExt) {
224
- this.id = id;
225
- this.type = "proxy";
226
- this.proxy = proxy;
227
- this.method = method;
228
- this.defaultValue = defaultValue;
229
- this.ext = ext || {};
230
- }
231
- }
232
-
233
- function isMetaProxy(meta: Meta | MetaProxy): meta is MetaProxy {
234
- return meta.type === "proxy";
235
- }
236
-
237
- export interface IMonitorHandle {
238
- remove(): void;
239
- }
240
-
241
- let propExtID = 0;
242
- export class PropertyExt extends Class {
243
- protected _id: string;
244
- private _watchArrIdx: number;
245
- private _watchArr: any;
246
- private _publishedProperties: Meta[] = [];
247
-
248
- constructor() {
249
- super();
250
- this.calcPublishedProperties();
251
-
252
- this._id = "_pe" + (++propExtID);
253
- this._watchArrIdx = 0;
254
- this._watchArr = {};
255
-
256
- this.publishedProperties(true).forEach(function (meta) {
257
- switch (meta.type) {
258
- case "array":
259
- case "widgetArray":
260
- case "propertyArray":
261
- this[meta.id + "_reset"]();
262
- break;
263
- }
264
- }, this);
265
- }
266
-
267
- id(): string;
268
- id(_: string): this;
269
- id(_?): string | this {
270
- if (!arguments.length) return this._id;
271
- this._id = _;
272
- return this;
273
- }
274
-
275
- // Publish Properties ---
276
- calcPublishedProperties(includePrivate = false, expandProxies = false): void {
277
- this._publishedProperties = [];
278
- const protoStack = [];
279
- let __proto__ = Object.getPrototypeOf(this);
280
- while (__proto__) {
281
- if (__proto__ === PropertyExt.prototype) {
282
- break;
283
- }
284
- protoStack.unshift(__proto__);
285
- __proto__ = Object.getPrototypeOf(__proto__);
286
- }
287
- for (__proto__ of protoStack) {
288
- for (const key in __proto__) {
289
- if (__proto__.hasOwnProperty(key)) {
290
- if (isMeta(key)) {
291
- this._publishedProperties.push(this[key]);
292
- }
293
- }
294
- }
295
- }
296
- }
297
-
298
- resolvePublishedProxy(meta: Meta | MetaProxy) {
299
- let item = this;
300
- while (meta instanceof MetaProxy) {
301
- item = item[meta.proxy];
302
- meta = item.publishedProperty(meta.method);
303
- }
304
- return meta;
305
- }
306
-
307
- publishedProperties(includePrivate = false, expandProxies = false): Meta[] {
308
- return this._publishedProperties.filter(meta => includePrivate || !isPrivate(this, meta.id)).map(meta => {
309
- if (expandProxies && isMetaProxy(meta)) {
310
- const selfProp = meta;
311
- let item = this;
312
- while (meta.type === "proxy") {
313
- item = item[(meta as any).proxy];
314
- meta = item.publishedProperty((meta as any).method);
315
- }
316
- if (meta.id !== selfProp.id) {
317
- meta = JSON.parse(JSON.stringify(meta)); // Clone meta so we can safely replace the id.
318
- meta.id = selfProp.id;
319
- }
320
- }
321
- return meta;
322
- });
323
- }
324
-
325
- widgetWalker(visitor: (item: PropertyExt) => void) {
326
- visitor(this);
327
- this.publishedProperties(false, true).forEach(publishItem => {
328
- switch (publishItem.type) {
329
- case "widget":
330
- const widget: PropertyExt = this[publishItem.id]();
331
- if (widget) {
332
- widget.widgetWalker(visitor);
333
- }
334
- break;
335
- case "widgetArray":
336
- case "propertyArray":
337
- const widgets: PropertyExt[] = this[publishItem.id]();
338
- if (widgets) {
339
- widgets.forEach((widget) => {
340
- widget.widgetWalker(visitor);
341
- });
342
- }
343
- break;
344
- }
345
- });
346
- }
347
-
348
- propertyWalker(visitor: (context: this, publishItem: Meta) => void, filter?: (context: this, publishItem: Meta) => boolean) {
349
- const context = this;
350
- this.publishedProperties(false, true).forEach(function (publishItem) {
351
- if (typeof (filter) !== "function" || filter(context, publishItem)) {
352
- visitor(context, publishItem);
353
- }
354
- });
355
- }
356
-
357
- serialize(): { __class, [id: string]: any } {
358
- const retVal = {
359
- __class: this.classID()
360
- };
361
- for (const prop of this.publishedProperties()) {
362
- if (prop.id === "fields") continue;
363
- const val = (this as any)[prop.id]();
364
- switch (prop.type) {
365
- case "propertyArray":
366
- if ((this as any)[`${prop.id}_modified`]()) {
367
- }
368
- const serialization = val.filter(item => item.valid()).map(item => item.serialize()).filter(item => item !== undefined);
369
- if (serialization) {
370
- retVal[prop.id] = serialization;
371
- }
372
- break;
373
- case "widget":
374
- retVal[prop.id] = val?.serialize();
375
- break;
376
- default:
377
- if ((this as any)[`${prop.id}_modified`]()) {
378
- if (!(val instanceof Object)) {
379
- retVal[prop.id] = val;
380
- }
381
- }
382
- }
383
- }
384
- return retVal;
385
- }
386
-
387
- deserialize(props?: { __class, [id: string]: any }): this {
388
- if (!props) return this;
389
- for (const prop of this.publishedProperties()) {
390
- const val = props[prop.id];
391
- if (val !== undefined) {
392
- switch (prop.type) {
393
- case "propertyArray":
394
- if (prop.ext && prop.ext.autoExpand) {
395
- this[`${prop.id}`](val.map(item => new prop.ext.autoExpand().deserialize(item)));
396
- }
397
- break;
398
- case "widget":
399
- const currVal = this[`${prop.id}`]();
400
- if (currVal.classID() === val.__class) {
401
- currVal.deserialize(val);
402
- } else {
403
- console.warn("Dynamic class initialization not supported.");
404
- }
405
- break;
406
- default:
407
- this[`${prop.id}`](val);
408
- }
409
- }
410
- }
411
- return this;
412
- }
413
-
414
- publishedProperty(id) {
415
- return this[__meta_ + id];
416
- }
417
-
418
- publishedModified() {
419
- return this.publishedProperties().some(function (prop) {
420
- return this[prop.id + "_modified"]();
421
- }, this);
422
- }
423
-
424
- publishReset(privateArr?, exceptionsArr?) {
425
- privateArr = (privateArr || []).map(function (id) { return __meta_ + id; });
426
- exceptionsArr = (exceptionsArr || []).map(function (id) { return __meta_ + id; });
427
- for (const key in this) {
428
- if (isMeta(key)) {
429
- const isPrivateItem = !privateArr.length || (privateArr.length && privateArr.indexOf(key) >= 0);
430
- const isException = exceptionsArr.indexOf(key) >= 0;
431
- if (isPrivateItem && !isException) {
432
- this[__private_ + key] = true;
433
- }
434
- }
435
- }
436
- }
437
- static prevClassID: string = "";
438
- publish(id: string, defaultValue, type?: PublishTypes, description?: string, set?: string[] | (() => string[] | Array<{ value: string, text: string }>) | IPublishExt, ext: IPublishExt = {}): void {
439
- if (GEN_PUB_STUBS) {
440
- if (PropertyExt.prevClassID !== (this as any).constructor.name) {
441
- PropertyExt.prevClassID = (this as any).constructor.name;
442
- console.info(`// ${PropertyExt.prevClassID} ---`);
443
- }
444
- let jsType: string = type;
445
- switch (type) {
446
- case "set":
447
- case "html-color":
448
- jsType = "string";
449
- break;
450
- case "array":
451
- case "widgetArray":
452
- case "propertyArray":
453
- jsType = "any[]";
454
- break;
455
- }
456
- console.info(` ${id}(): ${jsType};
457
- ${id}(_: ${jsType}): this;
458
- ${id}_exists(): boolean;`);
459
- }
460
- if (id.indexOf("_") === 0) {
461
- id = id.slice(1);
462
- }
463
- if (this[__meta_ + id] !== undefined && !ext.override) {
464
- throw new Error(id + " is already published.");
465
- }
466
- const meta = this[__meta_ + id] = new Meta(id, defaultValue, type, description, set, ext);
467
- if (meta.ext.internal) {
468
- this[__private_ + id] = true;
469
- }
470
- Object.defineProperty(this, __prop_ + id, {
471
- set(_) {
472
- if (_ === undefined) {
473
- _ = null;
474
- } else if (_ === "" && meta.ext.optional) {
475
- _ = null;
476
- } else if (_ !== null) {
477
- _ = meta.checkedAssign.call(this, _);
478
- }
479
- this.broadcast(id, _, this[__prop_data_ + id]);
480
- if (_ === null) {
481
- delete this[__prop_data_ + id];
482
- } else {
483
- this[__prop_data_ + id] = _;
484
- }
485
- },
486
-
487
- get() {
488
- if (this[id + "_disabled"]()) return this[id + "_default"]();
489
- return this[__prop_data_ + id] !== undefined ? this[__prop_data_ + id] : this[id + "_default"]();
490
- },
491
- configurable: true
492
- });
493
- if (this[id]) {
494
- } else {
495
- if (type === "propertyArray") {
496
- this[id] = function (_?) {
497
- if (!arguments.length) return this[__prop_ + id];
498
- this[__prop_ + id] = _.map(item => {
499
- if (!meta.ext.noDeserialize && meta.ext.autoExpand && !(item instanceof meta.ext.autoExpand)) {
500
- item = new meta.ext.autoExpand().deserialize(item);
501
- }
502
- item.owner(this);
503
- return item;
504
- });
505
- return this;
506
- };
507
- } else {
508
- this[id] = function (_?) {
509
- if (!arguments.length) return this[__prop_ + id] ?? this[id + "_default"]();
510
- this[__prop_ + id] = _;
511
- return this;
512
- };
513
- }
514
- }
515
- this[id + "_disabled"] = function () {
516
- return ext && ext.disable ? !!ext.disable(this) : false;
517
- };
518
- this[id + "_hidden"] = function () {
519
- return ext && ext.hidden ? !!ext.hidden(this) : false;
520
- };
521
- this[id + "_valid"] = function () {
522
- return ext && ext.validate ? this[id + "_disabled"]() || (ext.optional && !this[id + "_exists"]()) || (ext.autoExpand && !this.valid()) || !!ext.validate(this) : true;
523
- };
524
- this[id + "_modified"] = function () {
525
- if (type === "propertyArray") {
526
- return this[__prop_data_ + id] && (this[__prop_data_ + id].some(item => item.valid()));
527
- }
528
- return this[__prop_data_ + id] !== undefined;
529
- };
530
- this[id + "_exists"] = function () {
531
- if (this[__prop_data_ + id] != null && !(this[__prop_data_ + id] === "" && ext.optional === true)) return true;
532
- if (this[id + "_default"]() != null && !(this[id + "_default"]() === "" && ext.optional === true)) return true;
533
- return false;
534
- };
535
- this[id + "_default"] = function (_?) {
536
- if (!arguments.length) return this[__default_ + id] !== undefined ? this[__default_ + id] : meta.defaultValue;
537
- if (_ === "") {
538
- _ = null;
539
- }
540
- if (_ === null) {
541
- delete this[__default_ + id];
542
- } else {
543
- this[__default_ + id] = _;
544
- }
545
- return this;
546
- };
547
- this[id + "_reset"] = function () {
548
- switch (type) {
549
- case "widget":
550
- if (this[__prop_data_ + id]) {
551
- this[__prop_data_ + id].target(null);
552
- }
553
- break;
554
- case "widgetArray":
555
- if (this[__prop_data_ + id]) {
556
- this[__prop_data_ + id].forEach(function (widget) {
557
- widget.target(null);
558
- });
559
- }
560
- break;
561
- }
562
-
563
- switch (type) {
564
- case "array":
565
- case "widgetArray":
566
- case "propertyArray":
567
- this[__default_ + id] = this[id + "_default"]().map(function (row) { return row; });
568
- break;
569
- }
570
- delete this[__prop_data_ + id];
571
- return this;
572
- };
573
- this[id + "_options"] = function () {
574
- if (typeof set === "function") {
575
- const retVal = meta.ext.optional ? [null] : [];
576
- return retVal.concat(set.apply(this, arguments));
577
- }
578
- return set;
579
- };
580
- }
581
-
582
- publishWidget(prefix, WidgetType, id) {
583
- for (const key in WidgetType.prototype) {
584
- if (key.indexOf("__meta") === 0) {
585
- const publishItem = WidgetType.prototype[key];
586
- this.publishProxy(prefix + __prop_data_ + publishItem.id, id, publishItem.method || publishItem.id);
587
- }
588
- }
589
- }
590
-
591
- publishProxy(id: string, proxy, method?, defaultValue?) {
592
- method = method || id;
593
- if (this[__meta_ + id] !== undefined) {
594
- throw new Error(id + " is already published.");
595
- }
596
- this[__meta_ + id] = new MetaProxy(id, proxy, method, defaultValue);
597
- this[id] = function (_?) {
598
- if (!arguments.length) return defaultValue === undefined || this[id + "_modified"]() ? this[proxy][method]() : defaultValue;
599
- if (defaultValue !== undefined && _ === defaultValue) {
600
- this[proxy][method + "_reset"]();
601
- } else {
602
- this[proxy][method](_);
603
- }
604
- return this;
605
- };
606
- this[id + "_disabled"] = function () {
607
- return this[proxy][method + "_disabled"]();
608
- };
609
- this[id + "_modified"] = function () {
610
- return this[proxy][method + "_modified"]() && (defaultValue === undefined || this[proxy][method]() !== defaultValue);
611
- };
612
- this[id + "_exists"] = function () {
613
- return this[proxy][method + "_exists"]();
614
- };
615
- this[id + "_default"] = function (_?) {
616
- if (!arguments.length) return this[proxy][method + "_default"]();
617
- this[proxy][method + "_default"](_);
618
- return this;
619
- };
620
- this[id + "_reset"] = function () {
621
- this[proxy][method + "_reset"]();
622
- return this;
623
- };
624
- this[id + "_options"] = function () {
625
- return this[proxy][method + "_options"]();
626
- };
627
- }
628
-
629
- monitorProperty(propID: string, func: (id: string, newVal: any, oldVal: any) => void): IMonitorHandle {
630
- const meta = this.publishedProperty(propID);
631
- switch (meta.type) {
632
- case "proxy":
633
- if (this[meta.proxy]) {
634
- return this[meta.proxy].monitorProperty(meta.method, function (_key, newVal, oldVal) {
635
- func(meta.id, newVal, oldVal);
636
- });
637
- } else {
638
- return {
639
- remove: () => { }
640
- };
641
- }
642
- default:
643
- const idx = this._watchArrIdx++;
644
- this._watchArr[idx] = { propertyID: propID, callback: func };
645
- const context = this;
646
- return {
647
- remove: () => {
648
- delete context._watchArr[idx];
649
- }
650
- };
651
- }
652
- }
653
-
654
- monitor(func: (id: string, newVal: any, oldVal: any, source: PropertyExt) => void): { remove: () => void } {
655
- const idx = this._watchArrIdx++;
656
- this._watchArr[idx] = { propertyID: undefined, callback: func };
657
- return {
658
- remove: () => {
659
- delete this._watchArr[idx];
660
- }
661
- };
662
- }
663
-
664
- broadcast(key, newVal, oldVal, source?) {
665
- source = source || this;
666
- if (!deepEqual(newVal, oldVal)) {
667
- for (const idx in this._watchArr) {
668
- const monitor = this._watchArr[idx];
669
- if ((monitor.propertyID === undefined || monitor.propertyID === key) && monitor.callback) {
670
- // console.log(`${this.classID()}->broadcast(${key}, ${newVal}, ${oldVal})`);
671
- setTimeout(function (monitor2) {
672
- monitor2.callback(key, newVal, oldVal, source);
673
- }, 0, monitor);
674
- }
675
- }
676
- }
677
- }
678
-
679
- applyTheme(theme) {
680
- if (!theme) {
681
- return;
682
- }
683
- const clsArr = this._class.split(" ");
684
- for (const i in clsArr) {
685
- if (theme[clsArr[i]]) {
686
- for (const paramName in theme[clsArr[i]]) {
687
- if (paramName === "overrideTags" && theme[clsArr[i]][paramName] instanceof Object) {
688
- for (const param in theme[clsArr[i]][paramName]) {
689
- if (this.publishedProperty(paramName).ext) {
690
- this.publishedProperty(paramName).ext.tags = theme[clsArr[i]][paramName][param];
691
- }
692
- }
693
- continue;
694
- }
695
- if (this.publishedProperty(paramName)) {
696
- this.publishedProperty(paramName).defaultValue = theme[clsArr[i]][paramName];
697
- }
698
- }
699
- }
700
- }
701
- }
702
-
703
- copyPropsTo(other: PropertyExt, ignore: string[] = []): this {
704
- this.publishedProperties(false).filter(meta => ignore.indexOf(meta.id) < 0).forEach(meta => {
705
- if (this[meta.id + "_exists"]()) {
706
- other[meta.id](this[meta.id]());
707
- } else {
708
- other[meta.id + "_reset"]();
709
- }
710
- });
711
- return this;
712
- }
713
-
714
- private metaHash(meta): string {
715
- if (this[meta.id + "_exists"]()) {
716
- let value = this[meta.id]();
717
- const proxyMeta = this.resolvePublishedProxy(meta);
718
- switch (proxyMeta.type) {
719
- case "widget":
720
- value = value.hashSum();
721
- break;
722
- case "widgetArray":
723
- case "propertyArray":
724
- value = hashSum(value.map(v => v.hashSum()));
725
- break;
726
- default:
727
- }
728
- return value;
729
- }
730
- return "";
731
- }
732
-
733
- propertyHash(properties: string[] = [], more = {}): string {
734
- const props: { [key: string]: any } = more;
735
- this.publishedProperties(false).filter(meta => properties.length === 0 || properties.indexOf(meta.id) >= 0).forEach(meta => {
736
- props[meta.id] = this.metaHash(meta);
737
- });
738
- return hashSum(props);
739
- }
740
-
741
- hashSum(ignore: string[] = [], more = {}): string {
742
- ignore = [...ignore, "classed"];
743
- const props: { [key: string]: any } = more;
744
- this.publishedProperties(false).filter(meta => ignore.indexOf(meta.id) < 0).forEach(meta => {
745
- props[meta.id] = this.metaHash(meta);
746
- });
747
- return hashSum(props);
748
- }
749
-
750
- // Events ---
751
- on(eventID, func, stopPropagation = false): this {
752
- const context = this;
753
- this.overrideMethod(eventID, function (...args: any[]) {
754
- const origFunc = args[args.length - 1];
755
- let retVal;
756
- if (stopPropagation) {
757
- if (d3Event && d3Event.stopPropagation) {
758
- d3Event.stopPropagation();
759
- }
760
- [].push.call(args, origFunc);
761
- } else {
762
- retVal = origFunc.apply(context, args);
763
- }
764
- const retVal2 = func.apply(context, args);
765
- return retVal2 !== undefined ? retVal2 : retVal;
766
- });
767
- return this;
768
- }
769
- }
770
- PropertyExt.prototype._class += " common_PropertyExt";
1
+ import { hashSum } from "@hpcc-js/util";
2
+ import { event as d3Event } from "d3-selection";
3
+ import { Class } from "./Class.ts";
4
+
5
+ const GEN_PUB_STUBS: boolean = false;
6
+
7
+ function deepEqual(a, b) {
8
+ if (a === b) return true;
9
+
10
+ const arrA = Array.isArray(a);
11
+ const arrB = Array.isArray(b);
12
+ let i;
13
+
14
+ if (arrA && arrB) {
15
+ if (a.length !== b.length) return false;
16
+ for (i = 0; i < a.length; i++)
17
+ if (!deepEqual(a[i], b[i])) return false;
18
+ return true;
19
+ }
20
+
21
+ if (arrA != arrB) return false;
22
+
23
+ if (a && b && typeof a === "object" && typeof b === "object") {
24
+ const keys = Object.keys(a);
25
+ if (keys.length !== Object.keys(b).length) return false;
26
+
27
+ const dateA = a instanceof Date;
28
+ const dateB = b instanceof Date;
29
+ if (dateA && dateB) return a.getTime() === b.getTime();
30
+ if (dateA != dateB) return false;
31
+
32
+ const regexpA = a instanceof RegExp;
33
+ const regexpB = b instanceof RegExp;
34
+ if (regexpA && regexpB) return a.toString() === b.toString();
35
+ if (regexpA != regexpB) return false;
36
+
37
+ for (i = 0; i < keys.length; i++)
38
+ if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
39
+
40
+ for (i = 0; i < keys.length; i++)
41
+ if (!deepEqual(a[keys[i]], b[keys[i]])) return false;
42
+
43
+ return true;
44
+ }
45
+
46
+ return false;
47
+ }
48
+
49
+ const __meta_ = "__meta_";
50
+ const __private_ = "__private_";
51
+ const __prop_ = "_";
52
+ const __prop_data_ = "__prop_";
53
+ const __default_ = "__default_";
54
+
55
+ function isMeta(key) {
56
+ return key.indexOf(__meta_) === 0;
57
+ }
58
+
59
+ function isPrivate(obj, key) {
60
+ return obj[__private_ + key] || obj[__private_ + __meta_ + key];
61
+ }
62
+
63
+ export interface IAutoExpand extends PropertyExt {
64
+ // AutoExpand items may have "incomplete" instances while the user is editing them in the PropertyEditor
65
+ owner(): PropertyExt;
66
+ owner(_: PropertyExt): this;
67
+ valid(): boolean;
68
+ }
69
+
70
+ export type TagTypes = "Private" | "Shared" | "Basic" | "Intermediate" | "Advanced" | "Theme" | "Serial";
71
+ export type PublishTypes = "any" | "number" | "boolean" | "string" | "set" | "array" | "object" | "widget" | "widgetArray" | "propertyArray" | "html-color" | "proxy";
72
+ export interface IPublishExt {
73
+ override?: boolean;
74
+ disable?: (w) => boolean;
75
+ validate?: (w) => boolean;
76
+ hidden?: (w) => boolean;
77
+ optional?: boolean;
78
+ tags?: TagTypes[];
79
+ autoExpand?: new () => IAutoExpand;
80
+ noDeserialize?: boolean;
81
+ render?: boolean;
82
+ icons?: string[];
83
+ editor_input?: (context, widget, cell, param) => void;
84
+ saveButton?: string;
85
+ saveButtonID?: string;
86
+ number?: any;
87
+ reset?: boolean;
88
+ // Amcharts - really needed?
89
+ min?: number;
90
+ max?: number;
91
+ step?: number;
92
+ inputType?: string;
93
+ internal?: boolean;
94
+ range?: { min: number, max: number, step: number };
95
+ multiline?: boolean;
96
+ }
97
+
98
+ export class Meta {
99
+ id;
100
+ type: PublishTypes;
101
+ origDefaultValue;
102
+ defaultValue;
103
+ description;
104
+ set;
105
+ ext: IPublishExt;
106
+ checkedAssign;
107
+
108
+ constructor(id, defaultValue, type, description, set, ext?: IPublishExt) {
109
+ ext = ext || {};
110
+ this.id = id;
111
+ this.type = type;
112
+ this.origDefaultValue = defaultValue;
113
+ this.defaultValue = ext.optional && defaultValue === null ? undefined : defaultValue;
114
+ this.description = description;
115
+ this.set = set;
116
+ this.ext = ext;
117
+
118
+ switch (type) {
119
+ case "any":
120
+ this.checkedAssign = _ => _;
121
+ break;
122
+ case "set":
123
+ this.checkedAssign = function (_) {
124
+ if ((window as any).__hpcc_debug) {
125
+ const options = typeof set === "function" ? set.call(this) : set;
126
+ if (options && options.length && options.indexOf(_) < 0) {
127
+ console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + JSON.stringify(options));
128
+ }
129
+ }
130
+ return _;
131
+ };
132
+ break;
133
+ case "html-color":
134
+ this.checkedAssign = function (_) {
135
+ if ((window as any).__hpcc_debug && _ && _ !== "red") {
136
+ const litmus = "red";
137
+ const d = document.createElement("div");
138
+ d.style.color = litmus;
139
+ d.style.color = _;
140
+ // Element's style.color will be reverted to litmus or set to "" if an invalid color is given
141
+ if (d.style.color === litmus || d.style.color === "") {
142
+ console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
143
+ }
144
+ }
145
+ return _;
146
+ };
147
+ break;
148
+ case "boolean":
149
+ this.checkedAssign = function (_) {
150
+ return typeof (_) === "string" && ["false", "off", "0"].indexOf(_.toLowerCase()) >= 0 ? false : Boolean(_);
151
+ };
152
+ break;
153
+ case "number":
154
+ this.checkedAssign = function (_) {
155
+ return Number(_);
156
+ };
157
+ break;
158
+ case "string":
159
+ this.checkedAssign = function (_) {
160
+ return String(_);
161
+ };
162
+ break;
163
+ case "array":
164
+ this.checkedAssign = function (_) {
165
+ if (!(_ instanceof Array)) {
166
+ console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
167
+ }
168
+ return _;
169
+ };
170
+ break;
171
+ case "object":
172
+ this.checkedAssign = function (_) {
173
+ if (!(_ instanceof Object)) {
174
+ console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
175
+ }
176
+ return _;
177
+ };
178
+ break;
179
+ case "widget":
180
+ this.checkedAssign = function (_) {
181
+ if (!_._class || _._class.indexOf("common_PropertyExt") < 0) {
182
+ console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
183
+ }
184
+ return _;
185
+ };
186
+ break;
187
+ case "widgetArray":
188
+ this.checkedAssign = function (_) {
189
+ if (_.some(function (row) { return (!row._class || row._class.indexOf("common_Widget") < 0); })) {
190
+ console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
191
+ }
192
+ return _;
193
+ };
194
+ break;
195
+ case "propertyArray":
196
+ this.checkedAssign = function (_) {
197
+ if (_.some(function (row) { return !row.publishedProperties; })) {
198
+ console.error("Invalid value for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
199
+ }
200
+ return _;
201
+ };
202
+ break;
203
+ default:
204
+ this.checkedAssign = function (_) {
205
+ if ((window as any).__hpcc_debug) {
206
+ console.error("Unchecked property type for '" + this.classID() + "." + id + "': " + _ + " expected " + type);
207
+ }
208
+ return _;
209
+ };
210
+ break;
211
+ }
212
+ }
213
+ }
214
+
215
+ class MetaProxy {
216
+ id: string;
217
+ type;
218
+ proxy;
219
+ method;
220
+ defaultValue;
221
+ ext: IPublishExt;
222
+
223
+ constructor(id: string, proxy, method, defaultValue, ext?: IPublishExt) {
224
+ this.id = id;
225
+ this.type = "proxy";
226
+ this.proxy = proxy;
227
+ this.method = method;
228
+ this.defaultValue = defaultValue;
229
+ this.ext = ext || {};
230
+ }
231
+ }
232
+
233
+ function isMetaProxy(meta: Meta | MetaProxy): meta is MetaProxy {
234
+ return meta.type === "proxy";
235
+ }
236
+
237
+ export interface IMonitorHandle {
238
+ remove(): void;
239
+ }
240
+
241
+ let propExtID = 0;
242
+ export class PropertyExt extends Class {
243
+ protected _id: string;
244
+ private _watchArrIdx: number;
245
+ private _watchArr: any;
246
+ private _publishedProperties: Meta[] = [];
247
+
248
+ constructor() {
249
+ super();
250
+ this.calcPublishedProperties();
251
+
252
+ this._id = "_pe" + (++propExtID);
253
+ this._watchArrIdx = 0;
254
+ this._watchArr = {};
255
+
256
+ this.publishedProperties(true).forEach(function (meta) {
257
+ switch (meta.type) {
258
+ case "array":
259
+ case "widgetArray":
260
+ case "propertyArray":
261
+ this[meta.id + "_reset"]();
262
+ break;
263
+ }
264
+ }, this);
265
+ }
266
+
267
+ id(): string;
268
+ id(_: string): this;
269
+ id(_?): string | this {
270
+ if (!arguments.length) return this._id;
271
+ this._id = _;
272
+ return this;
273
+ }
274
+
275
+ // Publish Properties ---
276
+ calcPublishedProperties(includePrivate = false, expandProxies = false): void {
277
+ this._publishedProperties = [];
278
+ const protoStack = [];
279
+ let __proto__ = Object.getPrototypeOf(this);
280
+ while (__proto__) {
281
+ if (__proto__ === PropertyExt.prototype) {
282
+ break;
283
+ }
284
+ protoStack.unshift(__proto__);
285
+ __proto__ = Object.getPrototypeOf(__proto__);
286
+ }
287
+ for (__proto__ of protoStack) {
288
+ for (const key in __proto__) {
289
+ if (__proto__.hasOwnProperty(key)) {
290
+ if (isMeta(key)) {
291
+ this._publishedProperties.push(this[key]);
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ resolvePublishedProxy(meta: Meta | MetaProxy) {
299
+ let item = this;
300
+ while (meta instanceof MetaProxy) {
301
+ item = item[meta.proxy];
302
+ meta = item.publishedProperty(meta.method);
303
+ }
304
+ return meta;
305
+ }
306
+
307
+ publishedProperties(includePrivate = false, expandProxies = false): Meta[] {
308
+ return this._publishedProperties.filter(meta => includePrivate || !isPrivate(this, meta.id)).map(meta => {
309
+ if (expandProxies && isMetaProxy(meta)) {
310
+ const selfProp = meta;
311
+ let item = this;
312
+ while (meta.type === "proxy") {
313
+ item = item[(meta as any).proxy];
314
+ meta = item.publishedProperty((meta as any).method);
315
+ }
316
+ if (meta.id !== selfProp.id) {
317
+ meta = JSON.parse(JSON.stringify(meta)); // Clone meta so we can safely replace the id.
318
+ meta.id = selfProp.id;
319
+ }
320
+ }
321
+ return meta;
322
+ });
323
+ }
324
+
325
+ widgetWalker(visitor: (item: PropertyExt) => void) {
326
+ visitor(this);
327
+ this.publishedProperties(false, true).forEach(publishItem => {
328
+ switch (publishItem.type) {
329
+ case "widget":
330
+ const widget: PropertyExt = this[publishItem.id]();
331
+ if (widget) {
332
+ widget.widgetWalker(visitor);
333
+ }
334
+ break;
335
+ case "widgetArray":
336
+ case "propertyArray":
337
+ const widgets: PropertyExt[] = this[publishItem.id]();
338
+ if (widgets) {
339
+ widgets.forEach((widget) => {
340
+ widget.widgetWalker(visitor);
341
+ });
342
+ }
343
+ break;
344
+ }
345
+ });
346
+ }
347
+
348
+ propertyWalker(visitor: (context: this, publishItem: Meta) => void, filter?: (context: this, publishItem: Meta) => boolean) {
349
+ const context = this;
350
+ this.publishedProperties(false, true).forEach(function (publishItem) {
351
+ if (typeof (filter) !== "function" || filter(context, publishItem)) {
352
+ visitor(context, publishItem);
353
+ }
354
+ });
355
+ }
356
+
357
+ serialize(): { __class, [id: string]: any } {
358
+ const retVal = {
359
+ __class: this.classID()
360
+ };
361
+ for (const prop of this.publishedProperties()) {
362
+ if (prop.id === "fields") continue;
363
+ const val = (this as any)[prop.id]();
364
+ switch (prop.type) {
365
+ case "propertyArray":
366
+ if ((this as any)[`${prop.id}_modified`]()) {
367
+ }
368
+ const serialization = val.filter(item => item.valid()).map(item => item.serialize()).filter(item => item !== undefined);
369
+ if (serialization) {
370
+ retVal[prop.id] = serialization;
371
+ }
372
+ break;
373
+ case "widget":
374
+ retVal[prop.id] = val?.serialize();
375
+ break;
376
+ default:
377
+ if ((this as any)[`${prop.id}_modified`]()) {
378
+ if (!(val instanceof Object)) {
379
+ retVal[prop.id] = val;
380
+ }
381
+ }
382
+ }
383
+ }
384
+ return retVal;
385
+ }
386
+
387
+ deserialize(props?: { __class, [id: string]: any }): this {
388
+ if (!props) return this;
389
+ for (const prop of this.publishedProperties()) {
390
+ const val = props[prop.id];
391
+ if (val !== undefined) {
392
+ switch (prop.type) {
393
+ case "propertyArray":
394
+ if (prop.ext && prop.ext.autoExpand) {
395
+ this[`${prop.id}`](val.map(item => new prop.ext.autoExpand().deserialize(item)));
396
+ }
397
+ break;
398
+ case "widget":
399
+ const currVal = this[`${prop.id}`]();
400
+ if (currVal.classID() === val.__class) {
401
+ currVal.deserialize(val);
402
+ } else {
403
+ console.warn("Dynamic class initialization not supported.");
404
+ }
405
+ break;
406
+ default:
407
+ this[`${prop.id}`](val);
408
+ }
409
+ }
410
+ }
411
+ return this;
412
+ }
413
+
414
+ publishedProperty(id) {
415
+ return this[__meta_ + id];
416
+ }
417
+
418
+ publishedModified() {
419
+ return this.publishedProperties().some(function (prop) {
420
+ return this[prop.id + "_modified"]();
421
+ }, this);
422
+ }
423
+
424
+ publishReset(privateArr?, exceptionsArr?) {
425
+ privateArr = (privateArr || []).map(function (id) { return __meta_ + id; });
426
+ exceptionsArr = (exceptionsArr || []).map(function (id) { return __meta_ + id; });
427
+ for (const key in this) {
428
+ if (isMeta(key)) {
429
+ const isPrivateItem = !privateArr.length || (privateArr.length && privateArr.indexOf(key) >= 0);
430
+ const isException = exceptionsArr.indexOf(key) >= 0;
431
+ if (isPrivateItem && !isException) {
432
+ this[__private_ + key] = true;
433
+ }
434
+ }
435
+ }
436
+ }
437
+ static prevClassID: string = "";
438
+ publish(id: string, defaultValue, type?: PublishTypes, description?: string, set?: string[] | (() => string[] | Array<{ value: string, text: string }>) | IPublishExt, ext: IPublishExt = {}): void {
439
+ if (GEN_PUB_STUBS) {
440
+ if (PropertyExt.prevClassID !== (this as any).constructor.name) {
441
+ PropertyExt.prevClassID = (this as any).constructor.name;
442
+ console.info(`// ${PropertyExt.prevClassID} ---`);
443
+ }
444
+ let jsType: string = type;
445
+ switch (type) {
446
+ case "set":
447
+ case "html-color":
448
+ jsType = "string";
449
+ break;
450
+ case "array":
451
+ case "widgetArray":
452
+ case "propertyArray":
453
+ jsType = "any[]";
454
+ break;
455
+ }
456
+ console.info(` ${id}(): ${jsType};
457
+ ${id}(_: ${jsType}): this;
458
+ ${id}_exists(): boolean;`);
459
+ }
460
+ if (id.indexOf("_") === 0) {
461
+ id = id.slice(1);
462
+ }
463
+ if (this[__meta_ + id] !== undefined && !ext.override) {
464
+ throw new Error(id + " is already published.");
465
+ }
466
+ const meta = this[__meta_ + id] = new Meta(id, defaultValue, type, description, set, ext);
467
+ if (meta.ext.internal) {
468
+ this[__private_ + id] = true;
469
+ }
470
+ Object.defineProperty(this, __prop_ + id, {
471
+ set(_) {
472
+ if (_ === undefined) {
473
+ _ = null;
474
+ } else if (_ === "" && meta.ext.optional) {
475
+ _ = null;
476
+ } else if (_ !== null) {
477
+ _ = meta.checkedAssign.call(this, _);
478
+ }
479
+ this.broadcast(id, _, this[__prop_data_ + id]);
480
+ if (_ === null) {
481
+ delete this[__prop_data_ + id];
482
+ } else {
483
+ this[__prop_data_ + id] = _;
484
+ }
485
+ },
486
+
487
+ get() {
488
+ if (this[id + "_disabled"]()) return this[id + "_default"]();
489
+ return this[__prop_data_ + id] !== undefined ? this[__prop_data_ + id] : this[id + "_default"]();
490
+ },
491
+ configurable: true
492
+ });
493
+ if (this[id]) {
494
+ } else {
495
+ if (type === "propertyArray") {
496
+ this[id] = function (_?) {
497
+ if (!arguments.length) return this[__prop_ + id];
498
+ this[__prop_ + id] = _.map(item => {
499
+ if (!meta.ext.noDeserialize && meta.ext.autoExpand && !(item instanceof meta.ext.autoExpand)) {
500
+ item = new meta.ext.autoExpand().deserialize(item);
501
+ }
502
+ item.owner(this);
503
+ return item;
504
+ });
505
+ return this;
506
+ };
507
+ } else {
508
+ this[id] = function (_?) {
509
+ if (!arguments.length) return this[__prop_ + id] ?? this[id + "_default"]();
510
+ this[__prop_ + id] = _;
511
+ return this;
512
+ };
513
+ }
514
+ }
515
+ this[id + "_disabled"] = function () {
516
+ return ext && ext.disable ? !!ext.disable(this) : false;
517
+ };
518
+ this[id + "_hidden"] = function () {
519
+ return ext && ext.hidden ? !!ext.hidden(this) : false;
520
+ };
521
+ this[id + "_valid"] = function () {
522
+ return ext && ext.validate ? this[id + "_disabled"]() || (ext.optional && !this[id + "_exists"]()) || (ext.autoExpand && !this.valid()) || !!ext.validate(this) : true;
523
+ };
524
+ this[id + "_modified"] = function () {
525
+ if (type === "propertyArray") {
526
+ return this[__prop_data_ + id] && (this[__prop_data_ + id].some(item => item.valid()));
527
+ }
528
+ return this[__prop_data_ + id] !== undefined;
529
+ };
530
+ this[id + "_exists"] = function () {
531
+ if (this[__prop_data_ + id] != null && !(this[__prop_data_ + id] === "" && ext.optional === true)) return true;
532
+ if (this[id + "_default"]() != null && !(this[id + "_default"]() === "" && ext.optional === true)) return true;
533
+ return false;
534
+ };
535
+ this[id + "_default"] = function (_?) {
536
+ if (!arguments.length) return this[__default_ + id] !== undefined ? this[__default_ + id] : meta.defaultValue;
537
+ if (_ === "") {
538
+ _ = null;
539
+ }
540
+ if (_ === null) {
541
+ delete this[__default_ + id];
542
+ } else {
543
+ this[__default_ + id] = _;
544
+ }
545
+ return this;
546
+ };
547
+ this[id + "_reset"] = function () {
548
+ switch (type) {
549
+ case "widget":
550
+ if (this[__prop_data_ + id]) {
551
+ this[__prop_data_ + id].target(null);
552
+ }
553
+ break;
554
+ case "widgetArray":
555
+ if (this[__prop_data_ + id]) {
556
+ this[__prop_data_ + id].forEach(function (widget) {
557
+ widget.target(null);
558
+ });
559
+ }
560
+ break;
561
+ }
562
+
563
+ switch (type) {
564
+ case "array":
565
+ case "widgetArray":
566
+ case "propertyArray":
567
+ this[__default_ + id] = this[id + "_default"]().map(function (row) { return row; });
568
+ break;
569
+ }
570
+ delete this[__prop_data_ + id];
571
+ return this;
572
+ };
573
+ this[id + "_options"] = function () {
574
+ if (typeof set === "function") {
575
+ const retVal = meta.ext.optional ? [null] : [];
576
+ return retVal.concat(set.apply(this, arguments));
577
+ }
578
+ return set;
579
+ };
580
+ }
581
+
582
+ publishWidget(prefix, WidgetType, id) {
583
+ for (const key in WidgetType.prototype) {
584
+ if (key.indexOf("__meta") === 0) {
585
+ const publishItem = WidgetType.prototype[key];
586
+ this.publishProxy(prefix + __prop_data_ + publishItem.id, id, publishItem.method || publishItem.id);
587
+ }
588
+ }
589
+ }
590
+
591
+ publishProxy(id: string, proxy, method?, defaultValue?) {
592
+ method = method || id;
593
+ if (this[__meta_ + id] !== undefined) {
594
+ throw new Error(id + " is already published.");
595
+ }
596
+ this[__meta_ + id] = new MetaProxy(id, proxy, method, defaultValue);
597
+ this[id] = function (_?) {
598
+ if (!arguments.length) return defaultValue === undefined || this[id + "_modified"]() ? this[proxy][method]() : defaultValue;
599
+ if (defaultValue !== undefined && _ === defaultValue) {
600
+ this[proxy][method + "_reset"]();
601
+ } else {
602
+ this[proxy][method](_);
603
+ }
604
+ return this;
605
+ };
606
+ this[id + "_disabled"] = function () {
607
+ return this[proxy][method + "_disabled"]();
608
+ };
609
+ this[id + "_modified"] = function () {
610
+ return this[proxy][method + "_modified"]() && (defaultValue === undefined || this[proxy][method]() !== defaultValue);
611
+ };
612
+ this[id + "_exists"] = function () {
613
+ return this[proxy][method + "_exists"]();
614
+ };
615
+ this[id + "_default"] = function (_?) {
616
+ if (!arguments.length) return this[proxy][method + "_default"]();
617
+ this[proxy][method + "_default"](_);
618
+ return this;
619
+ };
620
+ this[id + "_reset"] = function () {
621
+ this[proxy][method + "_reset"]();
622
+ return this;
623
+ };
624
+ this[id + "_options"] = function () {
625
+ return this[proxy][method + "_options"]();
626
+ };
627
+ }
628
+
629
+ monitorProperty(propID: string, func: (id: string, newVal: any, oldVal: any) => void): IMonitorHandle {
630
+ const meta = this.publishedProperty(propID);
631
+ switch (meta.type) {
632
+ case "proxy":
633
+ if (this[meta.proxy]) {
634
+ return this[meta.proxy].monitorProperty(meta.method, function (_key, newVal, oldVal) {
635
+ func(meta.id, newVal, oldVal);
636
+ });
637
+ } else {
638
+ return {
639
+ remove: () => { }
640
+ };
641
+ }
642
+ default:
643
+ const idx = this._watchArrIdx++;
644
+ this._watchArr[idx] = { propertyID: propID, callback: func };
645
+ const context = this;
646
+ return {
647
+ remove: () => {
648
+ delete context._watchArr[idx];
649
+ }
650
+ };
651
+ }
652
+ }
653
+
654
+ monitor(func: (id: string, newVal: any, oldVal: any, source: PropertyExt) => void): { remove: () => void } {
655
+ const idx = this._watchArrIdx++;
656
+ this._watchArr[idx] = { propertyID: undefined, callback: func };
657
+ return {
658
+ remove: () => {
659
+ delete this._watchArr[idx];
660
+ }
661
+ };
662
+ }
663
+
664
+ broadcast(key, newVal, oldVal, source?) {
665
+ source = source || this;
666
+ if (!deepEqual(newVal, oldVal)) {
667
+ for (const idx in this._watchArr) {
668
+ const monitor = this._watchArr[idx];
669
+ if ((monitor.propertyID === undefined || monitor.propertyID === key) && monitor.callback) {
670
+ // console.log(`${this.classID()}->broadcast(${key}, ${newVal}, ${oldVal})`);
671
+ setTimeout(function (monitor2) {
672
+ monitor2.callback(key, newVal, oldVal, source);
673
+ }, 0, monitor);
674
+ }
675
+ }
676
+ }
677
+ }
678
+
679
+ applyTheme(theme) {
680
+ if (!theme) {
681
+ return;
682
+ }
683
+ const clsArr = this._class.split(" ");
684
+ for (const i in clsArr) {
685
+ if (theme[clsArr[i]]) {
686
+ for (const paramName in theme[clsArr[i]]) {
687
+ if (paramName === "overrideTags" && theme[clsArr[i]][paramName] instanceof Object) {
688
+ for (const param in theme[clsArr[i]][paramName]) {
689
+ if (this.publishedProperty(paramName).ext) {
690
+ this.publishedProperty(paramName).ext.tags = theme[clsArr[i]][paramName][param];
691
+ }
692
+ }
693
+ continue;
694
+ }
695
+ if (this.publishedProperty(paramName)) {
696
+ this.publishedProperty(paramName).defaultValue = theme[clsArr[i]][paramName];
697
+ }
698
+ }
699
+ }
700
+ }
701
+ }
702
+
703
+ copyPropsTo(other: PropertyExt, ignore: string[] = []): this {
704
+ this.publishedProperties(false).filter(meta => ignore.indexOf(meta.id) < 0).forEach(meta => {
705
+ if (this[meta.id + "_exists"]()) {
706
+ other[meta.id](this[meta.id]());
707
+ } else {
708
+ other[meta.id + "_reset"]();
709
+ }
710
+ });
711
+ return this;
712
+ }
713
+
714
+ private metaHash(meta): string {
715
+ if (this[meta.id + "_exists"]()) {
716
+ let value = this[meta.id]();
717
+ const proxyMeta = this.resolvePublishedProxy(meta);
718
+ switch (proxyMeta.type) {
719
+ case "widget":
720
+ value = value.hashSum();
721
+ break;
722
+ case "widgetArray":
723
+ case "propertyArray":
724
+ value = hashSum(value.map(v => v.hashSum()));
725
+ break;
726
+ default:
727
+ }
728
+ return value;
729
+ }
730
+ return "";
731
+ }
732
+
733
+ propertyHash(properties: string[] = [], more = {}): string {
734
+ const props: { [key: string]: any } = more;
735
+ this.publishedProperties(false).filter(meta => properties.length === 0 || properties.indexOf(meta.id) >= 0).forEach(meta => {
736
+ props[meta.id] = this.metaHash(meta);
737
+ });
738
+ return hashSum(props);
739
+ }
740
+
741
+ hashSum(ignore: string[] = [], more = {}): string {
742
+ ignore = [...ignore, "classed"];
743
+ const props: { [key: string]: any } = more;
744
+ this.publishedProperties(false).filter(meta => ignore.indexOf(meta.id) < 0).forEach(meta => {
745
+ props[meta.id] = this.metaHash(meta);
746
+ });
747
+ return hashSum(props);
748
+ }
749
+
750
+ // Events ---
751
+ on(eventID, func, stopPropagation = false): this {
752
+ const context = this;
753
+ this.overrideMethod(eventID, function (...args: any[]) {
754
+ const origFunc = args[args.length - 1];
755
+ let retVal;
756
+ if (stopPropagation) {
757
+ if (d3Event && d3Event.stopPropagation) {
758
+ d3Event.stopPropagation();
759
+ }
760
+ [].push.call(args, origFunc);
761
+ } else {
762
+ retVal = origFunc.apply(context, args);
763
+ }
764
+ const retVal2 = func.apply(context, args);
765
+ return retVal2 !== undefined ? retVal2 : retVal;
766
+ });
767
+ return this;
768
+ }
769
+ }
770
+ PropertyExt.prototype._class += " common_PropertyExt";