vis-util 5.0.0 → 5.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/declarations/deep-object-assign.d.ts +0 -2
- package/declarations/deep-object-assign.d.ts.map +1 -1
- package/declarations/entry-esnext.d.ts +1 -0
- package/declarations/entry-esnext.d.ts.map +1 -1
- package/declarations/random/alea.d.ts +0 -1
- package/declarations/random/alea.d.ts.map +1 -1
- package/declarations/shared/configurator-types.d.ts +13 -7
- package/declarations/shared/configurator-types.d.ts.map +1 -1
- package/declarations/util.d.ts +0 -58
- package/declarations/util.d.ts.map +1 -1
- package/esnext/esm/vis-util.js +2351 -73
- package/esnext/esm/vis-util.js.map +1 -1
- package/esnext/esm/vis-util.min.js +3 -3
- package/esnext/esm/vis-util.min.js.map +1 -1
- package/esnext/umd/vis-util.js +2364 -77
- package/esnext/umd/vis-util.js.map +1 -1
- package/esnext/umd/vis-util.min.js +3 -3
- package/esnext/umd/vis-util.min.js.map +1 -1
- package/package.json +24 -27
- package/peer/esm/vis-util.js +9378 -3038
- package/peer/esm/vis-util.js.map +1 -1
- package/peer/esm/vis-util.min.js +9 -3
- package/peer/esm/vis-util.min.js.map +1 -1
- package/peer/umd/vis-util.js +11208 -4861
- package/peer/umd/vis-util.js.map +1 -1
- package/peer/umd/vis-util.min.js +9 -3
- package/peer/umd/vis-util.min.js.map +1 -1
- package/standalone/esm/vis-util.js +9378 -3038
- package/standalone/esm/vis-util.js.map +1 -1
- package/standalone/esm/vis-util.min.js +9 -3
- package/standalone/esm/vis-util.min.js.map +1 -1
- package/standalone/umd/vis-util.js +11208 -4861
- package/standalone/umd/vis-util.js.map +1 -1
- package/standalone/umd/vis-util.min.js +9 -3
- package/standalone/umd/vis-util.min.js.map +1 -1
package/esnext/umd/vis-util.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* utilitie collection for visjs
|
|
6
6
|
*
|
|
7
|
-
* @version 5.0.
|
|
8
|
-
* @date
|
|
7
|
+
* @version 5.0.3
|
|
8
|
+
* @date 2022-03-04T18:53:16.725Z
|
|
9
9
|
*
|
|
10
10
|
* @copyright (c) 2011-2017 Almende B.V, http://almende.com
|
|
11
11
|
* @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs
|
|
@@ -25,10 +25,15 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
(function (global, factory) {
|
|
28
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
29
|
-
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
30
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.vis = global.vis || {}));
|
|
31
|
-
}(this, (function (exports) {
|
|
28
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('component-emitter'), require('@egjs/hammerjs')) :
|
|
29
|
+
typeof define === 'function' && define.amd ? define(['exports', 'component-emitter', '@egjs/hammerjs'], factory) :
|
|
30
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.vis = global.vis || {}, global.Emitter, global.Hammer));
|
|
31
|
+
})(this, (function (exports, Emitter, RealHammer) {
|
|
32
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
33
|
+
|
|
34
|
+
var Emitter__default = /*#__PURE__*/_interopDefaultLegacy(Emitter);
|
|
35
|
+
var RealHammer__default = /*#__PURE__*/_interopDefaultLegacy(RealHammer);
|
|
36
|
+
|
|
32
37
|
/**
|
|
33
38
|
* Use this symbol to delete properies in deepObjectAssign.
|
|
34
39
|
*/
|
|
@@ -38,7 +43,6 @@
|
|
|
38
43
|
*
|
|
39
44
|
* @param base - The base object that fullfils the whole interface T.
|
|
40
45
|
* @param updates - Updates that may change or delete props.
|
|
41
|
-
*
|
|
42
46
|
* @returns A brand new instance with all the supplied objects deeply merged.
|
|
43
47
|
*/
|
|
44
48
|
function pureDeepObjectAssign(base, ...updates) {
|
|
@@ -48,7 +52,6 @@
|
|
|
48
52
|
* Deep version of object assign with additional deleting by the DELETE symbol.
|
|
49
53
|
*
|
|
50
54
|
* @param values - Objects to be deeply merged.
|
|
51
|
-
*
|
|
52
55
|
* @returns The first object from values.
|
|
53
56
|
*/
|
|
54
57
|
function deepObjectAssign(...values) {
|
|
@@ -61,9 +64,7 @@
|
|
|
61
64
|
*
|
|
62
65
|
* @remarks
|
|
63
66
|
* This doesn't strip the DELETE symbols so they may end up in the final object.
|
|
64
|
-
*
|
|
65
67
|
* @param values - Objects to be deeply merged.
|
|
66
|
-
*
|
|
67
68
|
* @returns The first object from values.
|
|
68
69
|
*/
|
|
69
70
|
function deepObjectAssignNonentry(...values) {
|
|
@@ -98,7 +99,6 @@
|
|
|
98
99
|
* Deep clone given object or array. In case of primitive simply return.
|
|
99
100
|
*
|
|
100
101
|
* @param a - Anything.
|
|
101
|
-
*
|
|
102
102
|
* @returns Deep cloned object/array or unchanged a.
|
|
103
103
|
*/
|
|
104
104
|
function clone(a) {
|
|
@@ -141,7 +141,6 @@
|
|
|
141
141
|
*
|
|
142
142
|
* @param seed - All supplied arguments will be used as a seed. In case nothing
|
|
143
143
|
* is supplied the current time will be used to seed the generator.
|
|
144
|
-
*
|
|
145
144
|
* @returns A ready to use seeded generator.
|
|
146
145
|
*/
|
|
147
146
|
function Alea(...seed) {
|
|
@@ -151,7 +150,6 @@
|
|
|
151
150
|
* An implementation of [[Alea]] without user input validation.
|
|
152
151
|
*
|
|
153
152
|
* @param seed - The data that will be used to seed the generator.
|
|
154
|
-
*
|
|
155
153
|
* @returns A ready to use seeded generator.
|
|
156
154
|
*/
|
|
157
155
|
function AleaImplementation(seed) {
|
|
@@ -175,7 +173,6 @@
|
|
|
175
173
|
* random numbers.
|
|
176
174
|
*
|
|
177
175
|
* @param seed - Arbitrary data that will be used as the seed.
|
|
178
|
-
*
|
|
179
176
|
* @returns Three numbers to use as initial values for [[AleaImplementation]].
|
|
180
177
|
*/
|
|
181
178
|
function mashSeed(...seed) {
|
|
@@ -223,6 +220,202 @@
|
|
|
223
220
|
};
|
|
224
221
|
}
|
|
225
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Setup a mock hammer.js object, for unit testing.
|
|
225
|
+
*
|
|
226
|
+
* Inspiration: https://github.com/uber/deck.gl/pull/658
|
|
227
|
+
*
|
|
228
|
+
* @returns {{on: noop, off: noop, destroy: noop, emit: noop, get: get}}
|
|
229
|
+
*/
|
|
230
|
+
function hammerMock() {
|
|
231
|
+
const noop = () => {};
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
on: noop,
|
|
235
|
+
off: noop,
|
|
236
|
+
destroy: noop,
|
|
237
|
+
emit: noop,
|
|
238
|
+
|
|
239
|
+
get() {
|
|
240
|
+
return {
|
|
241
|
+
set: noop,
|
|
242
|
+
};
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const Hammer$1 =
|
|
248
|
+
typeof window !== "undefined"
|
|
249
|
+
? window.Hammer || RealHammer__default["default"]
|
|
250
|
+
: function () {
|
|
251
|
+
// hammer.js is only available in a browser, not in node.js. Replacing it with a mock object.
|
|
252
|
+
return hammerMock();
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Turn an element into an clickToUse element.
|
|
257
|
+
* When not active, the element has a transparent overlay. When the overlay is
|
|
258
|
+
* clicked, the mode is changed to active.
|
|
259
|
+
* When active, the element is displayed with a blue border around it, and
|
|
260
|
+
* the interactive contents of the element can be used. When clicked outside
|
|
261
|
+
* the element, the elements mode is changed to inactive.
|
|
262
|
+
*
|
|
263
|
+
* @param {Element} container
|
|
264
|
+
* @class Activator
|
|
265
|
+
*/
|
|
266
|
+
function Activator$1(container) {
|
|
267
|
+
this._cleanupQueue = [];
|
|
268
|
+
|
|
269
|
+
this.active = false;
|
|
270
|
+
|
|
271
|
+
this._dom = {
|
|
272
|
+
container,
|
|
273
|
+
overlay: document.createElement("div"),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
this._dom.overlay.classList.add("vis-overlay");
|
|
277
|
+
|
|
278
|
+
this._dom.container.appendChild(this._dom.overlay);
|
|
279
|
+
this._cleanupQueue.push(() => {
|
|
280
|
+
this._dom.overlay.parentNode.removeChild(this._dom.overlay);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const hammer = Hammer$1(this._dom.overlay);
|
|
284
|
+
hammer.on("tap", this._onTapOverlay.bind(this));
|
|
285
|
+
this._cleanupQueue.push(() => {
|
|
286
|
+
hammer.destroy();
|
|
287
|
+
// FIXME: cleaning up hammer instances doesn't work (Timeline not removed
|
|
288
|
+
// from memory)
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// block all touch events (except tap)
|
|
292
|
+
const events = [
|
|
293
|
+
"tap",
|
|
294
|
+
"doubletap",
|
|
295
|
+
"press",
|
|
296
|
+
"pinch",
|
|
297
|
+
"pan",
|
|
298
|
+
"panstart",
|
|
299
|
+
"panmove",
|
|
300
|
+
"panend",
|
|
301
|
+
];
|
|
302
|
+
events.forEach((event) => {
|
|
303
|
+
hammer.on(event, (event) => {
|
|
304
|
+
event.srcEvent.stopPropagation();
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// attach a click event to the window, in order to deactivate when clicking outside the timeline
|
|
309
|
+
if (document && document.body) {
|
|
310
|
+
this._onClick = (event) => {
|
|
311
|
+
if (!_hasParent(event.target, container)) {
|
|
312
|
+
this.deactivate();
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
document.body.addEventListener("click", this._onClick);
|
|
316
|
+
this._cleanupQueue.push(() => {
|
|
317
|
+
document.body.removeEventListener("click", this._onClick);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// prepare escape key listener for deactivating when active
|
|
322
|
+
this._escListener = (event) => {
|
|
323
|
+
if (
|
|
324
|
+
"key" in event
|
|
325
|
+
? event.key === "Escape"
|
|
326
|
+
: event.keyCode === 27 /* the keyCode is for IE11 */
|
|
327
|
+
) {
|
|
328
|
+
this.deactivate();
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// turn into an event emitter
|
|
334
|
+
Emitter__default["default"](Activator$1.prototype);
|
|
335
|
+
|
|
336
|
+
// The currently active activator
|
|
337
|
+
Activator$1.current = null;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Destroy the activator. Cleans up all created DOM and event listeners
|
|
341
|
+
*/
|
|
342
|
+
Activator$1.prototype.destroy = function () {
|
|
343
|
+
this.deactivate();
|
|
344
|
+
|
|
345
|
+
for (const callback of this._cleanupQueue.splice(0).reverse()) {
|
|
346
|
+
callback();
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Activate the element
|
|
352
|
+
* Overlay is hidden, element is decorated with a blue shadow border
|
|
353
|
+
*/
|
|
354
|
+
Activator$1.prototype.activate = function () {
|
|
355
|
+
// we allow only one active activator at a time
|
|
356
|
+
if (Activator$1.current) {
|
|
357
|
+
Activator$1.current.deactivate();
|
|
358
|
+
}
|
|
359
|
+
Activator$1.current = this;
|
|
360
|
+
|
|
361
|
+
this.active = true;
|
|
362
|
+
this._dom.overlay.style.display = "none";
|
|
363
|
+
this._dom.container.classList.add("vis-active");
|
|
364
|
+
|
|
365
|
+
this.emit("change");
|
|
366
|
+
this.emit("activate");
|
|
367
|
+
|
|
368
|
+
// ugly hack: bind ESC after emitting the events, as the Network rebinds all
|
|
369
|
+
// keyboard events on a 'change' event
|
|
370
|
+
document.body.addEventListener("keydown", this._escListener);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Deactivate the element
|
|
375
|
+
* Overlay is displayed on top of the element
|
|
376
|
+
*/
|
|
377
|
+
Activator$1.prototype.deactivate = function () {
|
|
378
|
+
this.active = false;
|
|
379
|
+
this._dom.overlay.style.display = "block";
|
|
380
|
+
this._dom.container.classList.remove("vis-active");
|
|
381
|
+
document.body.removeEventListener("keydown", this._escListener);
|
|
382
|
+
|
|
383
|
+
this.emit("change");
|
|
384
|
+
this.emit("deactivate");
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Handle a tap event: activate the container
|
|
389
|
+
*
|
|
390
|
+
* @param {Event} event The event
|
|
391
|
+
* @private
|
|
392
|
+
*/
|
|
393
|
+
Activator$1.prototype._onTapOverlay = function (event) {
|
|
394
|
+
// activate the container
|
|
395
|
+
this.activate();
|
|
396
|
+
event.srcEvent.stopPropagation();
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Test whether the element has the requested parent element somewhere in
|
|
401
|
+
* its chain of parent nodes.
|
|
402
|
+
*
|
|
403
|
+
* @param {HTMLElement} element
|
|
404
|
+
* @param {HTMLElement} parent
|
|
405
|
+
* @returns {boolean} Returns true when the parent is found somewhere in the
|
|
406
|
+
* chain of parent nodes.
|
|
407
|
+
* @private
|
|
408
|
+
*/
|
|
409
|
+
function _hasParent(element, parent) {
|
|
410
|
+
while (element) {
|
|
411
|
+
if (element === parent) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
element = element.parentNode;
|
|
415
|
+
}
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
|
|
226
419
|
// utility functions
|
|
227
420
|
// parse ASP.Net Date pattern,
|
|
228
421
|
// for example '/Date(1198908717056)/' or '/Date(1198908717056-0700)/'
|
|
@@ -237,7 +430,6 @@
|
|
|
237
430
|
* Test whether given object is a number.
|
|
238
431
|
*
|
|
239
432
|
* @param value - Input value of unknown type.
|
|
240
|
-
*
|
|
241
433
|
* @returns True if number, false otherwise.
|
|
242
434
|
*/
|
|
243
435
|
function isNumber(value) {
|
|
@@ -263,7 +455,6 @@
|
|
|
263
455
|
* Test whether given object is a string.
|
|
264
456
|
*
|
|
265
457
|
* @param value - Input value of unknown type.
|
|
266
|
-
*
|
|
267
458
|
* @returns True if string, false otherwise.
|
|
268
459
|
*/
|
|
269
460
|
function isString(value) {
|
|
@@ -273,7 +464,6 @@
|
|
|
273
464
|
* Test whether given object is a object (not primitive or null).
|
|
274
465
|
*
|
|
275
466
|
* @param value - Input value of unknown type.
|
|
276
|
-
*
|
|
277
467
|
* @returns True if not null object, false otherwise.
|
|
278
468
|
*/
|
|
279
469
|
function isObject(value) {
|
|
@@ -283,7 +473,6 @@
|
|
|
283
473
|
* Test whether given object is a Date, or a String containing a Date.
|
|
284
474
|
*
|
|
285
475
|
* @param value - Input value of unknown type.
|
|
286
|
-
*
|
|
287
476
|
* @returns True if Date instance or string date representation, false otherwise.
|
|
288
477
|
*/
|
|
289
478
|
function isDate(value) {
|
|
@@ -360,7 +549,6 @@
|
|
|
360
549
|
*
|
|
361
550
|
* @param target - The target object to copy to.
|
|
362
551
|
* @param source - The source object from which to copy properties.
|
|
363
|
-
*
|
|
364
552
|
* @returns The target object.
|
|
365
553
|
*/
|
|
366
554
|
const extend = Object.assign;
|
|
@@ -369,11 +557,9 @@
|
|
|
369
557
|
*
|
|
370
558
|
* @remarks
|
|
371
559
|
* Only properties with defined values are copied.
|
|
372
|
-
*
|
|
373
560
|
* @param props - Properties to be copied to a.
|
|
374
561
|
* @param a - The target.
|
|
375
562
|
* @param others - The sources.
|
|
376
|
-
*
|
|
377
563
|
* @returns Argument a.
|
|
378
564
|
*/
|
|
379
565
|
function selectiveExtend(props, a, ...others) {
|
|
@@ -398,12 +584,10 @@
|
|
|
398
584
|
* Previous version of this routine implied that multiple source objects could
|
|
399
585
|
* be used; however, the implementation was **wrong**. Since multiple (\>1)
|
|
400
586
|
* sources weren't used anywhere in the `vis.js` code, this has been removed
|
|
401
|
-
*
|
|
402
587
|
* @param props - Names of first-level properties to copy over.
|
|
403
588
|
* @param a - Target object.
|
|
404
589
|
* @param b - Source object.
|
|
405
590
|
* @param allowDeletion - If true, delete property in a if explicitly set to null in b.
|
|
406
|
-
*
|
|
407
591
|
* @returns Argument a.
|
|
408
592
|
*/
|
|
409
593
|
function selectiveDeepExtend(props, a, b, allowDeletion = false) {
|
|
@@ -443,13 +627,11 @@
|
|
|
443
627
|
* The properties of `b` are considered for copying. Properties which are
|
|
444
628
|
* themselves objects are are also extended. Only properties with defined
|
|
445
629
|
* values are copied.
|
|
446
|
-
*
|
|
447
630
|
* @param propsToExclude - Names of properties which should *not* be copied.
|
|
448
631
|
* @param a - Object to extend.
|
|
449
632
|
* @param b - Object to take properties from for extension.
|
|
450
633
|
* @param allowDeletion - If true, delete properties in a that are explicitly
|
|
451
634
|
* set to null in b.
|
|
452
|
-
*
|
|
453
635
|
* @returns Argument a.
|
|
454
636
|
*/
|
|
455
637
|
function selectiveNotDeepExtend(propsToExclude, a, b, allowDeletion = false) {
|
|
@@ -497,7 +679,6 @@
|
|
|
497
679
|
* (That is the options objects that inherit from others will also get the
|
|
498
680
|
* inherited options).
|
|
499
681
|
* @param allowDeletion - If true, the values of fields that are null will be deleted.
|
|
500
|
-
*
|
|
501
682
|
* @returns Argument a.
|
|
502
683
|
*/
|
|
503
684
|
function deepExtend(a, b, protoExtend = false, allowDeletion = false) {
|
|
@@ -533,7 +714,6 @@
|
|
|
533
714
|
*
|
|
534
715
|
* @param a - First array.
|
|
535
716
|
* @param b - Second array.
|
|
536
|
-
*
|
|
537
717
|
* @returns True if both arrays have the same length and same elements (1 = '1').
|
|
538
718
|
*/
|
|
539
719
|
function equalArray(a, b) {
|
|
@@ -551,7 +731,6 @@
|
|
|
551
731
|
* Get the type of an object, for example exports.getType([]) returns 'Array'.
|
|
552
732
|
*
|
|
553
733
|
* @param object - Input value of unknown type.
|
|
554
|
-
*
|
|
555
734
|
* @returns Detected type.
|
|
556
735
|
*/
|
|
557
736
|
function getType(object) {
|
|
@@ -596,7 +775,6 @@
|
|
|
596
775
|
*
|
|
597
776
|
* @param arr - First part.
|
|
598
777
|
* @param newValue - The value to be aadded into the array.
|
|
599
|
-
*
|
|
600
778
|
* @returns A new array with all items from arr and newValue (which is last).
|
|
601
779
|
*/
|
|
602
780
|
function copyAndExtendArray(arr, newValue) {
|
|
@@ -606,7 +784,6 @@
|
|
|
606
784
|
* Used to extend an array and copy it. This is used to propagate paths recursively.
|
|
607
785
|
*
|
|
608
786
|
* @param arr - The array to be copied.
|
|
609
|
-
*
|
|
610
787
|
* @returns Shallow copy of arr.
|
|
611
788
|
*/
|
|
612
789
|
function copyArray(arr) {
|
|
@@ -616,7 +793,6 @@
|
|
|
616
793
|
* Retrieve the absolute left value of a DOM element.
|
|
617
794
|
*
|
|
618
795
|
* @param elem - A dom element, for example a div.
|
|
619
|
-
*
|
|
620
796
|
* @returns The absolute left position of this element in the browser page.
|
|
621
797
|
*/
|
|
622
798
|
function getAbsoluteLeft(elem) {
|
|
@@ -626,7 +802,6 @@
|
|
|
626
802
|
* Retrieve the absolute right value of a DOM element.
|
|
627
803
|
*
|
|
628
804
|
* @param elem - A dom element, for example a div.
|
|
629
|
-
*
|
|
630
805
|
* @returns The absolute right position of this element in the browser page.
|
|
631
806
|
*/
|
|
632
807
|
function getAbsoluteRight(elem) {
|
|
@@ -636,7 +811,6 @@
|
|
|
636
811
|
* Retrieve the absolute top value of a DOM element.
|
|
637
812
|
*
|
|
638
813
|
* @param elem - A dom element, for example a div.
|
|
639
|
-
*
|
|
640
814
|
* @returns The absolute top position of this element in the browser page.
|
|
641
815
|
*/
|
|
642
816
|
function getAbsoluteTop(elem) {
|
|
@@ -699,7 +873,6 @@
|
|
|
699
873
|
* Convert an object into an array: all objects properties are put into the array. The resulting array is unordered.
|
|
700
874
|
*
|
|
701
875
|
* @param o - Object that contains the properties and methods.
|
|
702
|
-
*
|
|
703
876
|
* @returns An array of unordered values.
|
|
704
877
|
*/
|
|
705
878
|
const toArray = Object.values;
|
|
@@ -709,7 +882,6 @@
|
|
|
709
882
|
* @param object - The object whose property will be updated.
|
|
710
883
|
* @param key - Name of the property to be updated.
|
|
711
884
|
* @param value - The new value to be assigned.
|
|
712
|
-
*
|
|
713
885
|
* @returns Whether the value was updated (true) or already strictly the same in the original object (false).
|
|
714
886
|
*/
|
|
715
887
|
function updateProperty(object, key, value) {
|
|
@@ -725,7 +897,6 @@
|
|
|
725
897
|
* Throttle the given function to be only executed once per animation frame.
|
|
726
898
|
*
|
|
727
899
|
* @param fn - The original function.
|
|
728
|
-
*
|
|
729
900
|
* @returns The throttled function.
|
|
730
901
|
*/
|
|
731
902
|
function throttle(fn) {
|
|
@@ -809,7 +980,6 @@
|
|
|
809
980
|
* Get HTML element which is the target of the event.
|
|
810
981
|
*
|
|
811
982
|
* @param event - The event.
|
|
812
|
-
*
|
|
813
983
|
* @returns The element or null if not obtainable.
|
|
814
984
|
*/
|
|
815
985
|
function getTarget(event = window.event) {
|
|
@@ -840,7 +1010,6 @@
|
|
|
840
1010
|
*
|
|
841
1011
|
* @param element - The element to be tested.
|
|
842
1012
|
* @param parent - The ancestor (not necessarily parent) of the element.
|
|
843
|
-
*
|
|
844
1013
|
* @returns True if parent is an ancestor of the element, false otherwise.
|
|
845
1014
|
*/
|
|
846
1015
|
function hasParent(element, parent) {
|
|
@@ -864,7 +1033,6 @@
|
|
|
864
1033
|
*
|
|
865
1034
|
* @param value - Value to be converted intoboolean, a function will be executed as `(() => unknown)`.
|
|
866
1035
|
* @param defaultValue - If the value or the return value of the function == null then this will be returned.
|
|
867
|
-
*
|
|
868
1036
|
* @returns Corresponding boolean value, if none then the default value, if none then null.
|
|
869
1037
|
*/
|
|
870
1038
|
asBoolean(value, defaultValue) {
|
|
@@ -881,7 +1049,6 @@
|
|
|
881
1049
|
*
|
|
882
1050
|
* @param value - Value to be converted intonumber, a function will be executed as `(() => unknown)`.
|
|
883
1051
|
* @param defaultValue - If the value or the return value of the function == null then this will be returned.
|
|
884
|
-
*
|
|
885
1052
|
* @returns Corresponding **boxed** number value, if none then the default value, if none then null.
|
|
886
1053
|
*/
|
|
887
1054
|
asNumber(value, defaultValue) {
|
|
@@ -898,7 +1065,6 @@
|
|
|
898
1065
|
*
|
|
899
1066
|
* @param value - Value to be converted intostring, a function will be executed as `(() => unknown)`.
|
|
900
1067
|
* @param defaultValue - If the value or the return value of the function == null then this will be returned.
|
|
901
|
-
*
|
|
902
1068
|
* @returns Corresponding **boxed** string value, if none then the default value, if none then null.
|
|
903
1069
|
*/
|
|
904
1070
|
asString(value, defaultValue) {
|
|
@@ -915,7 +1081,6 @@
|
|
|
915
1081
|
*
|
|
916
1082
|
* @param value - Value to be converted intosize, a function will be executed as `(() => unknown)`.
|
|
917
1083
|
* @param defaultValue - If the value or the return value of the function == null then this will be returned.
|
|
918
|
-
*
|
|
919
1084
|
* @returns Corresponding string value (number + 'px'), if none then the default value, if none then null.
|
|
920
1085
|
*/
|
|
921
1086
|
asSize(value, defaultValue) {
|
|
@@ -937,7 +1102,6 @@
|
|
|
937
1102
|
*
|
|
938
1103
|
* @param value - Value to be converted into DOM Element, a function will be executed as `(() => unknown)`.
|
|
939
1104
|
* @param defaultValue - If the value or the return value of the function == null then this will be returned.
|
|
940
|
-
*
|
|
941
1105
|
* @returns The DOM Element, if none then the default value, if none then null.
|
|
942
1106
|
*/
|
|
943
1107
|
asElement(value, defaultValue) {
|
|
@@ -952,9 +1116,7 @@
|
|
|
952
1116
|
*
|
|
953
1117
|
* @remarks
|
|
954
1118
|
* {@link http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb}
|
|
955
|
-
*
|
|
956
1119
|
* @param hex - Hex color string (3 or 6 digits, with or without #).
|
|
957
|
-
*
|
|
958
1120
|
* @returns RGB color object.
|
|
959
1121
|
*/
|
|
960
1122
|
function hexToRGB(hex) {
|
|
@@ -989,7 +1151,6 @@
|
|
|
989
1151
|
*
|
|
990
1152
|
* @param color - The color string (hex, RGB, RGBA).
|
|
991
1153
|
* @param opacity - The new opacity.
|
|
992
|
-
*
|
|
993
1154
|
* @returns RGBA string, for example 'rgba(255, 0, 127, 0.3)'.
|
|
994
1155
|
*/
|
|
995
1156
|
function overrideOpacity(color, opacity) {
|
|
@@ -1019,7 +1180,6 @@
|
|
|
1019
1180
|
* @param red - Red channel.
|
|
1020
1181
|
* @param green - Green channel.
|
|
1021
1182
|
* @param blue - Blue channel.
|
|
1022
|
-
*
|
|
1023
1183
|
* @returns Hex color string (for example: '#0acdc0').
|
|
1024
1184
|
*/
|
|
1025
1185
|
function RGBToHex(red, green, blue) {
|
|
@@ -1030,7 +1190,6 @@
|
|
|
1030
1190
|
*
|
|
1031
1191
|
* @param inputColor - Shorthand color string or input color object.
|
|
1032
1192
|
* @param defaultColor - Full color object to fill in missing values in inputColor.
|
|
1033
|
-
*
|
|
1034
1193
|
* @returns Color object.
|
|
1035
1194
|
*/
|
|
1036
1195
|
function parseColor(inputColor, defaultColor) {
|
|
@@ -1152,11 +1311,9 @@
|
|
|
1152
1311
|
*
|
|
1153
1312
|
* @remarks
|
|
1154
1313
|
* {@link http://www.javascripter.net/faq/rgb2hsv.htm}
|
|
1155
|
-
*
|
|
1156
1314
|
* @param red - Red channel.
|
|
1157
1315
|
* @param green - Green channel.
|
|
1158
1316
|
* @param blue - Blue channel.
|
|
1159
|
-
*
|
|
1160
1317
|
* @returns HSV color object.
|
|
1161
1318
|
*/
|
|
1162
1319
|
function RGBToHSV(red, green, blue) {
|
|
@@ -1236,11 +1393,9 @@
|
|
|
1236
1393
|
*
|
|
1237
1394
|
* @remarks
|
|
1238
1395
|
* {@link https://gist.github.com/mjijackson/5311256}
|
|
1239
|
-
*
|
|
1240
1396
|
* @param h - Hue.
|
|
1241
1397
|
* @param s - Saturation.
|
|
1242
1398
|
* @param v - Value.
|
|
1243
|
-
*
|
|
1244
1399
|
* @returns RGB color object.
|
|
1245
1400
|
*/
|
|
1246
1401
|
function HSVToRGB(h, s, v) {
|
|
@@ -1284,7 +1439,6 @@
|
|
|
1284
1439
|
* @param h - Hue.
|
|
1285
1440
|
* @param s - Saturation.
|
|
1286
1441
|
* @param v - Value.
|
|
1287
|
-
*
|
|
1288
1442
|
* @returns Hex color string.
|
|
1289
1443
|
*/
|
|
1290
1444
|
function HSVToHex(h, s, v) {
|
|
@@ -1295,7 +1449,6 @@
|
|
|
1295
1449
|
* Convert hex color string into HSV \<0, 1\>.
|
|
1296
1450
|
*
|
|
1297
1451
|
* @param hex - Hex color string.
|
|
1298
|
-
*
|
|
1299
1452
|
* @returns HSV color object.
|
|
1300
1453
|
*/
|
|
1301
1454
|
function hexToHSV(hex) {
|
|
@@ -1309,7 +1462,6 @@
|
|
|
1309
1462
|
* Validate hex color string.
|
|
1310
1463
|
*
|
|
1311
1464
|
* @param hex - Unknown string that may contain a color.
|
|
1312
|
-
*
|
|
1313
1465
|
* @returns True if the string is valid, false otherwise.
|
|
1314
1466
|
*/
|
|
1315
1467
|
function isValidHex(hex) {
|
|
@@ -1320,7 +1472,6 @@
|
|
|
1320
1472
|
* Validate RGB color string.
|
|
1321
1473
|
*
|
|
1322
1474
|
* @param rgb - Unknown string that may contain a color.
|
|
1323
|
-
*
|
|
1324
1475
|
* @returns True if the string is valid, false otherwise.
|
|
1325
1476
|
*/
|
|
1326
1477
|
function isValidRGB(rgb) {
|
|
@@ -1330,7 +1481,6 @@
|
|
|
1330
1481
|
* Validate RGBA color string.
|
|
1331
1482
|
*
|
|
1332
1483
|
* @param rgba - Unknown string that may contain a color.
|
|
1333
|
-
*
|
|
1334
1484
|
* @returns True if the string is valid, false otherwise.
|
|
1335
1485
|
*/
|
|
1336
1486
|
function isValidRGBA(rgba) {
|
|
@@ -1342,7 +1492,6 @@
|
|
|
1342
1492
|
*
|
|
1343
1493
|
* @param fields - Names of properties to be bridged.
|
|
1344
1494
|
* @param referenceObject - The original object.
|
|
1345
|
-
*
|
|
1346
1495
|
* @returns A new object inheriting from the referenceObject.
|
|
1347
1496
|
*/
|
|
1348
1497
|
function selectiveBridgeObject(fields, referenceObject) {
|
|
@@ -1367,7 +1516,6 @@
|
|
|
1367
1516
|
* This is used for default options.
|
|
1368
1517
|
*
|
|
1369
1518
|
* @param referenceObject - The original object.
|
|
1370
|
-
*
|
|
1371
1519
|
* @returns The Element if the referenceObject is an Element, or a new object inheriting from the referenceObject.
|
|
1372
1520
|
*/
|
|
1373
1521
|
function bridgeObject(referenceObject) {
|
|
@@ -1393,7 +1541,6 @@
|
|
|
1393
1541
|
*
|
|
1394
1542
|
* @param a - The array to be sorted (in-place).
|
|
1395
1543
|
* @param compare - An order comparator.
|
|
1396
|
-
*
|
|
1397
1544
|
* @returns The argument a.
|
|
1398
1545
|
*/
|
|
1399
1546
|
function insertSort(a, compare) {
|
|
@@ -1521,7 +1668,6 @@
|
|
|
1521
1668
|
* @param comparator - -1 is lower, 0 is equal, 1 is higher.
|
|
1522
1669
|
* @param field - Property name on an item (That is item[field]).
|
|
1523
1670
|
* @param field2 - Second property name on an item (That is item[field][field2]).
|
|
1524
|
-
*
|
|
1525
1671
|
* @returns Index of the found item or -1 if nothing was found.
|
|
1526
1672
|
*/
|
|
1527
1673
|
function binarySearchCustom(orderedItems, comparator, field, field2) {
|
|
@@ -1561,7 +1707,6 @@
|
|
|
1561
1707
|
* @param field - Name of the property in items to be searched.
|
|
1562
1708
|
* @param sidePreference - If the target is between two values, should the index of the before or the after be returned?
|
|
1563
1709
|
* @param comparator - An optional comparator, returning -1, 0, 1 for \<, ===, \>.
|
|
1564
|
-
*
|
|
1565
1710
|
* @returns The index of found value or -1 if nothing was found.
|
|
1566
1711
|
*/
|
|
1567
1712
|
function binarySearchValue(orderedItems, target, field, sidePreference, comparator) {
|
|
@@ -1630,7 +1775,6 @@
|
|
|
1630
1775
|
* Provides no easing and no acceleration.
|
|
1631
1776
|
*
|
|
1632
1777
|
* @param t - Time.
|
|
1633
|
-
*
|
|
1634
1778
|
* @returns Value at time t.
|
|
1635
1779
|
*/
|
|
1636
1780
|
linear(t) {
|
|
@@ -1640,7 +1784,6 @@
|
|
|
1640
1784
|
* Accelerate from zero velocity.
|
|
1641
1785
|
*
|
|
1642
1786
|
* @param t - Time.
|
|
1643
|
-
*
|
|
1644
1787
|
* @returns Value at time t.
|
|
1645
1788
|
*/
|
|
1646
1789
|
easeInQuad(t) {
|
|
@@ -1650,7 +1793,6 @@
|
|
|
1650
1793
|
* Decelerate to zero velocity.
|
|
1651
1794
|
*
|
|
1652
1795
|
* @param t - Time.
|
|
1653
|
-
*
|
|
1654
1796
|
* @returns Value at time t.
|
|
1655
1797
|
*/
|
|
1656
1798
|
easeOutQuad(t) {
|
|
@@ -1660,7 +1802,6 @@
|
|
|
1660
1802
|
* Accelerate until halfway, then decelerate.
|
|
1661
1803
|
*
|
|
1662
1804
|
* @param t - Time.
|
|
1663
|
-
*
|
|
1664
1805
|
* @returns Value at time t.
|
|
1665
1806
|
*/
|
|
1666
1807
|
easeInOutQuad(t) {
|
|
@@ -1670,7 +1811,6 @@
|
|
|
1670
1811
|
* Accelerate from zero velocity.
|
|
1671
1812
|
*
|
|
1672
1813
|
* @param t - Time.
|
|
1673
|
-
*
|
|
1674
1814
|
* @returns Value at time t.
|
|
1675
1815
|
*/
|
|
1676
1816
|
easeInCubic(t) {
|
|
@@ -1680,7 +1820,6 @@
|
|
|
1680
1820
|
* Decelerate to zero velocity.
|
|
1681
1821
|
*
|
|
1682
1822
|
* @param t - Time.
|
|
1683
|
-
*
|
|
1684
1823
|
* @returns Value at time t.
|
|
1685
1824
|
*/
|
|
1686
1825
|
easeOutCubic(t) {
|
|
@@ -1690,7 +1829,6 @@
|
|
|
1690
1829
|
* Accelerate until halfway, then decelerate.
|
|
1691
1830
|
*
|
|
1692
1831
|
* @param t - Time.
|
|
1693
|
-
*
|
|
1694
1832
|
* @returns Value at time t.
|
|
1695
1833
|
*/
|
|
1696
1834
|
easeInOutCubic(t) {
|
|
@@ -1700,7 +1838,6 @@
|
|
|
1700
1838
|
* Accelerate from zero velocity.
|
|
1701
1839
|
*
|
|
1702
1840
|
* @param t - Time.
|
|
1703
|
-
*
|
|
1704
1841
|
* @returns Value at time t.
|
|
1705
1842
|
*/
|
|
1706
1843
|
easeInQuart(t) {
|
|
@@ -1710,7 +1847,6 @@
|
|
|
1710
1847
|
* Decelerate to zero velocity.
|
|
1711
1848
|
*
|
|
1712
1849
|
* @param t - Time.
|
|
1713
|
-
*
|
|
1714
1850
|
* @returns Value at time t.
|
|
1715
1851
|
*/
|
|
1716
1852
|
easeOutQuart(t) {
|
|
@@ -1720,7 +1856,6 @@
|
|
|
1720
1856
|
* Accelerate until halfway, then decelerate.
|
|
1721
1857
|
*
|
|
1722
1858
|
* @param t - Time.
|
|
1723
|
-
*
|
|
1724
1859
|
* @returns Value at time t.
|
|
1725
1860
|
*/
|
|
1726
1861
|
easeInOutQuart(t) {
|
|
@@ -1730,7 +1865,6 @@
|
|
|
1730
1865
|
* Accelerate from zero velocity.
|
|
1731
1866
|
*
|
|
1732
1867
|
* @param t - Time.
|
|
1733
|
-
*
|
|
1734
1868
|
* @returns Value at time t.
|
|
1735
1869
|
*/
|
|
1736
1870
|
easeInQuint(t) {
|
|
@@ -1740,7 +1874,6 @@
|
|
|
1740
1874
|
* Decelerate to zero velocity.
|
|
1741
1875
|
*
|
|
1742
1876
|
* @param t - Time.
|
|
1743
|
-
*
|
|
1744
1877
|
* @returns Value at time t.
|
|
1745
1878
|
*/
|
|
1746
1879
|
easeOutQuint(t) {
|
|
@@ -1750,7 +1883,6 @@
|
|
|
1750
1883
|
* Accelerate until halfway, then decelerate.
|
|
1751
1884
|
*
|
|
1752
1885
|
* @param t - Time.
|
|
1753
|
-
*
|
|
1754
1886
|
* @returns Value at time t.
|
|
1755
1887
|
*/
|
|
1756
1888
|
easeInOutQuint(t) {
|
|
@@ -1806,7 +1938,6 @@
|
|
|
1806
1938
|
* @param pile - Array of objects, no required format.
|
|
1807
1939
|
* @param accessors - Array of property names.
|
|
1808
1940
|
* For example `object['foo']['bar']` → `['foo', 'bar']`.
|
|
1809
|
-
*
|
|
1810
1941
|
* @returns Value of the property with given accessors path from the first pile item where it's not undefined.
|
|
1811
1942
|
*/
|
|
1812
1943
|
function topMost(pile, accessors) {
|
|
@@ -1830,12 +1961,2168 @@
|
|
|
1830
1961
|
return candidate;
|
|
1831
1962
|
}
|
|
1832
1963
|
|
|
1964
|
+
const htmlColors = {
|
|
1965
|
+
black: "#000000",
|
|
1966
|
+
navy: "#000080",
|
|
1967
|
+
darkblue: "#00008B",
|
|
1968
|
+
mediumblue: "#0000CD",
|
|
1969
|
+
blue: "#0000FF",
|
|
1970
|
+
darkgreen: "#006400",
|
|
1971
|
+
green: "#008000",
|
|
1972
|
+
teal: "#008080",
|
|
1973
|
+
darkcyan: "#008B8B",
|
|
1974
|
+
deepskyblue: "#00BFFF",
|
|
1975
|
+
darkturquoise: "#00CED1",
|
|
1976
|
+
mediumspringgreen: "#00FA9A",
|
|
1977
|
+
lime: "#00FF00",
|
|
1978
|
+
springgreen: "#00FF7F",
|
|
1979
|
+
aqua: "#00FFFF",
|
|
1980
|
+
cyan: "#00FFFF",
|
|
1981
|
+
midnightblue: "#191970",
|
|
1982
|
+
dodgerblue: "#1E90FF",
|
|
1983
|
+
lightseagreen: "#20B2AA",
|
|
1984
|
+
forestgreen: "#228B22",
|
|
1985
|
+
seagreen: "#2E8B57",
|
|
1986
|
+
darkslategray: "#2F4F4F",
|
|
1987
|
+
limegreen: "#32CD32",
|
|
1988
|
+
mediumseagreen: "#3CB371",
|
|
1989
|
+
turquoise: "#40E0D0",
|
|
1990
|
+
royalblue: "#4169E1",
|
|
1991
|
+
steelblue: "#4682B4",
|
|
1992
|
+
darkslateblue: "#483D8B",
|
|
1993
|
+
mediumturquoise: "#48D1CC",
|
|
1994
|
+
indigo: "#4B0082",
|
|
1995
|
+
darkolivegreen: "#556B2F",
|
|
1996
|
+
cadetblue: "#5F9EA0",
|
|
1997
|
+
cornflowerblue: "#6495ED",
|
|
1998
|
+
mediumaquamarine: "#66CDAA",
|
|
1999
|
+
dimgray: "#696969",
|
|
2000
|
+
slateblue: "#6A5ACD",
|
|
2001
|
+
olivedrab: "#6B8E23",
|
|
2002
|
+
slategray: "#708090",
|
|
2003
|
+
lightslategray: "#778899",
|
|
2004
|
+
mediumslateblue: "#7B68EE",
|
|
2005
|
+
lawngreen: "#7CFC00",
|
|
2006
|
+
chartreuse: "#7FFF00",
|
|
2007
|
+
aquamarine: "#7FFFD4",
|
|
2008
|
+
maroon: "#800000",
|
|
2009
|
+
purple: "#800080",
|
|
2010
|
+
olive: "#808000",
|
|
2011
|
+
gray: "#808080",
|
|
2012
|
+
skyblue: "#87CEEB",
|
|
2013
|
+
lightskyblue: "#87CEFA",
|
|
2014
|
+
blueviolet: "#8A2BE2",
|
|
2015
|
+
darkred: "#8B0000",
|
|
2016
|
+
darkmagenta: "#8B008B",
|
|
2017
|
+
saddlebrown: "#8B4513",
|
|
2018
|
+
darkseagreen: "#8FBC8F",
|
|
2019
|
+
lightgreen: "#90EE90",
|
|
2020
|
+
mediumpurple: "#9370D8",
|
|
2021
|
+
darkviolet: "#9400D3",
|
|
2022
|
+
palegreen: "#98FB98",
|
|
2023
|
+
darkorchid: "#9932CC",
|
|
2024
|
+
yellowgreen: "#9ACD32",
|
|
2025
|
+
sienna: "#A0522D",
|
|
2026
|
+
brown: "#A52A2A",
|
|
2027
|
+
darkgray: "#A9A9A9",
|
|
2028
|
+
lightblue: "#ADD8E6",
|
|
2029
|
+
greenyellow: "#ADFF2F",
|
|
2030
|
+
paleturquoise: "#AFEEEE",
|
|
2031
|
+
lightsteelblue: "#B0C4DE",
|
|
2032
|
+
powderblue: "#B0E0E6",
|
|
2033
|
+
firebrick: "#B22222",
|
|
2034
|
+
darkgoldenrod: "#B8860B",
|
|
2035
|
+
mediumorchid: "#BA55D3",
|
|
2036
|
+
rosybrown: "#BC8F8F",
|
|
2037
|
+
darkkhaki: "#BDB76B",
|
|
2038
|
+
silver: "#C0C0C0",
|
|
2039
|
+
mediumvioletred: "#C71585",
|
|
2040
|
+
indianred: "#CD5C5C",
|
|
2041
|
+
peru: "#CD853F",
|
|
2042
|
+
chocolate: "#D2691E",
|
|
2043
|
+
tan: "#D2B48C",
|
|
2044
|
+
lightgrey: "#D3D3D3",
|
|
2045
|
+
palevioletred: "#D87093",
|
|
2046
|
+
thistle: "#D8BFD8",
|
|
2047
|
+
orchid: "#DA70D6",
|
|
2048
|
+
goldenrod: "#DAA520",
|
|
2049
|
+
crimson: "#DC143C",
|
|
2050
|
+
gainsboro: "#DCDCDC",
|
|
2051
|
+
plum: "#DDA0DD",
|
|
2052
|
+
burlywood: "#DEB887",
|
|
2053
|
+
lightcyan: "#E0FFFF",
|
|
2054
|
+
lavender: "#E6E6FA",
|
|
2055
|
+
darksalmon: "#E9967A",
|
|
2056
|
+
violet: "#EE82EE",
|
|
2057
|
+
palegoldenrod: "#EEE8AA",
|
|
2058
|
+
lightcoral: "#F08080",
|
|
2059
|
+
khaki: "#F0E68C",
|
|
2060
|
+
aliceblue: "#F0F8FF",
|
|
2061
|
+
honeydew: "#F0FFF0",
|
|
2062
|
+
azure: "#F0FFFF",
|
|
2063
|
+
sandybrown: "#F4A460",
|
|
2064
|
+
wheat: "#F5DEB3",
|
|
2065
|
+
beige: "#F5F5DC",
|
|
2066
|
+
whitesmoke: "#F5F5F5",
|
|
2067
|
+
mintcream: "#F5FFFA",
|
|
2068
|
+
ghostwhite: "#F8F8FF",
|
|
2069
|
+
salmon: "#FA8072",
|
|
2070
|
+
antiquewhite: "#FAEBD7",
|
|
2071
|
+
linen: "#FAF0E6",
|
|
2072
|
+
lightgoldenrodyellow: "#FAFAD2",
|
|
2073
|
+
oldlace: "#FDF5E6",
|
|
2074
|
+
red: "#FF0000",
|
|
2075
|
+
fuchsia: "#FF00FF",
|
|
2076
|
+
magenta: "#FF00FF",
|
|
2077
|
+
deeppink: "#FF1493",
|
|
2078
|
+
orangered: "#FF4500",
|
|
2079
|
+
tomato: "#FF6347",
|
|
2080
|
+
hotpink: "#FF69B4",
|
|
2081
|
+
coral: "#FF7F50",
|
|
2082
|
+
darkorange: "#FF8C00",
|
|
2083
|
+
lightsalmon: "#FFA07A",
|
|
2084
|
+
orange: "#FFA500",
|
|
2085
|
+
lightpink: "#FFB6C1",
|
|
2086
|
+
pink: "#FFC0CB",
|
|
2087
|
+
gold: "#FFD700",
|
|
2088
|
+
peachpuff: "#FFDAB9",
|
|
2089
|
+
navajowhite: "#FFDEAD",
|
|
2090
|
+
moccasin: "#FFE4B5",
|
|
2091
|
+
bisque: "#FFE4C4",
|
|
2092
|
+
mistyrose: "#FFE4E1",
|
|
2093
|
+
blanchedalmond: "#FFEBCD",
|
|
2094
|
+
papayawhip: "#FFEFD5",
|
|
2095
|
+
lavenderblush: "#FFF0F5",
|
|
2096
|
+
seashell: "#FFF5EE",
|
|
2097
|
+
cornsilk: "#FFF8DC",
|
|
2098
|
+
lemonchiffon: "#FFFACD",
|
|
2099
|
+
floralwhite: "#FFFAF0",
|
|
2100
|
+
snow: "#FFFAFA",
|
|
2101
|
+
yellow: "#FFFF00",
|
|
2102
|
+
lightyellow: "#FFFFE0",
|
|
2103
|
+
ivory: "#FFFFF0",
|
|
2104
|
+
white: "#FFFFFF",
|
|
2105
|
+
};
|
|
2106
|
+
|
|
2107
|
+
/**
|
|
2108
|
+
* @param {number} [pixelRatio=1]
|
|
2109
|
+
*/
|
|
2110
|
+
class ColorPicker$1 {
|
|
2111
|
+
/**
|
|
2112
|
+
* @param {number} [pixelRatio=1]
|
|
2113
|
+
*/
|
|
2114
|
+
constructor(pixelRatio = 1) {
|
|
2115
|
+
this.pixelRatio = pixelRatio;
|
|
2116
|
+
this.generated = false;
|
|
2117
|
+
this.centerCoordinates = { x: 289 / 2, y: 289 / 2 };
|
|
2118
|
+
this.r = 289 * 0.49;
|
|
2119
|
+
this.color = { r: 255, g: 255, b: 255, a: 1.0 };
|
|
2120
|
+
this.hueCircle = undefined;
|
|
2121
|
+
this.initialColor = { r: 255, g: 255, b: 255, a: 1.0 };
|
|
2122
|
+
this.previousColor = undefined;
|
|
2123
|
+
this.applied = false;
|
|
2124
|
+
|
|
2125
|
+
// bound by
|
|
2126
|
+
this.updateCallback = () => {};
|
|
2127
|
+
this.closeCallback = () => {};
|
|
2128
|
+
|
|
2129
|
+
// create all DOM elements
|
|
2130
|
+
this._create();
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
/**
|
|
2134
|
+
* this inserts the colorPicker into a div from the DOM
|
|
2135
|
+
*
|
|
2136
|
+
* @param {Element} container
|
|
2137
|
+
*/
|
|
2138
|
+
insertTo(container) {
|
|
2139
|
+
if (this.hammer !== undefined) {
|
|
2140
|
+
this.hammer.destroy();
|
|
2141
|
+
this.hammer = undefined;
|
|
2142
|
+
}
|
|
2143
|
+
this.container = container;
|
|
2144
|
+
this.container.appendChild(this.frame);
|
|
2145
|
+
this._bindHammer();
|
|
2146
|
+
|
|
2147
|
+
this._setSize();
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
/**
|
|
2151
|
+
* the callback is executed on apply and save. Bind it to the application
|
|
2152
|
+
*
|
|
2153
|
+
* @param {Function} callback
|
|
2154
|
+
*/
|
|
2155
|
+
setUpdateCallback(callback) {
|
|
2156
|
+
if (typeof callback === "function") {
|
|
2157
|
+
this.updateCallback = callback;
|
|
2158
|
+
} else {
|
|
2159
|
+
throw new Error(
|
|
2160
|
+
"Function attempted to set as colorPicker update callback is not a function."
|
|
2161
|
+
);
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
/**
|
|
2166
|
+
* the callback is executed on apply and save. Bind it to the application
|
|
2167
|
+
*
|
|
2168
|
+
* @param {Function} callback
|
|
2169
|
+
*/
|
|
2170
|
+
setCloseCallback(callback) {
|
|
2171
|
+
if (typeof callback === "function") {
|
|
2172
|
+
this.closeCallback = callback;
|
|
2173
|
+
} else {
|
|
2174
|
+
throw new Error(
|
|
2175
|
+
"Function attempted to set as colorPicker closing callback is not a function."
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
/**
|
|
2181
|
+
*
|
|
2182
|
+
* @param {string} color
|
|
2183
|
+
* @returns {string}
|
|
2184
|
+
* @private
|
|
2185
|
+
*/
|
|
2186
|
+
_isColorString(color) {
|
|
2187
|
+
if (typeof color === "string") {
|
|
2188
|
+
return htmlColors[color];
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
/**
|
|
2193
|
+
* Set the color of the colorPicker
|
|
2194
|
+
* Supported formats:
|
|
2195
|
+
* 'red' --> HTML color string
|
|
2196
|
+
* '#ffffff' --> hex string
|
|
2197
|
+
* 'rgb(255,255,255)' --> rgb string
|
|
2198
|
+
* 'rgba(255,255,255,1.0)' --> rgba string
|
|
2199
|
+
* {r:255,g:255,b:255} --> rgb object
|
|
2200
|
+
* {r:255,g:255,b:255,a:1.0} --> rgba object
|
|
2201
|
+
*
|
|
2202
|
+
* @param {string | object} color
|
|
2203
|
+
* @param {boolean} [setInitial=true]
|
|
2204
|
+
*/
|
|
2205
|
+
setColor(color, setInitial = true) {
|
|
2206
|
+
if (color === "none") {
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
let rgba;
|
|
2211
|
+
|
|
2212
|
+
// if a html color shorthand is used, convert to hex
|
|
2213
|
+
const htmlColor = this._isColorString(color);
|
|
2214
|
+
if (htmlColor !== undefined) {
|
|
2215
|
+
color = htmlColor;
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
// check format
|
|
2219
|
+
if (isString(color) === true) {
|
|
2220
|
+
if (isValidRGB(color) === true) {
|
|
2221
|
+
const rgbaArray = color
|
|
2222
|
+
.substr(4)
|
|
2223
|
+
.substr(0, color.length - 5)
|
|
2224
|
+
.split(",");
|
|
2225
|
+
rgba = { r: rgbaArray[0], g: rgbaArray[1], b: rgbaArray[2], a: 1.0 };
|
|
2226
|
+
} else if (isValidRGBA(color) === true) {
|
|
2227
|
+
const rgbaArray = color
|
|
2228
|
+
.substr(5)
|
|
2229
|
+
.substr(0, color.length - 6)
|
|
2230
|
+
.split(",");
|
|
2231
|
+
rgba = {
|
|
2232
|
+
r: rgbaArray[0],
|
|
2233
|
+
g: rgbaArray[1],
|
|
2234
|
+
b: rgbaArray[2],
|
|
2235
|
+
a: rgbaArray[3],
|
|
2236
|
+
};
|
|
2237
|
+
} else if (isValidHex(color) === true) {
|
|
2238
|
+
const rgbObj = hexToRGB(color);
|
|
2239
|
+
rgba = { r: rgbObj.r, g: rgbObj.g, b: rgbObj.b, a: 1.0 };
|
|
2240
|
+
}
|
|
2241
|
+
} else {
|
|
2242
|
+
if (color instanceof Object) {
|
|
2243
|
+
if (
|
|
2244
|
+
color.r !== undefined &&
|
|
2245
|
+
color.g !== undefined &&
|
|
2246
|
+
color.b !== undefined
|
|
2247
|
+
) {
|
|
2248
|
+
const alpha = color.a !== undefined ? color.a : "1.0";
|
|
2249
|
+
rgba = { r: color.r, g: color.g, b: color.b, a: alpha };
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
// set color
|
|
2255
|
+
if (rgba === undefined) {
|
|
2256
|
+
throw new Error(
|
|
2257
|
+
"Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: " +
|
|
2258
|
+
JSON.stringify(color)
|
|
2259
|
+
);
|
|
2260
|
+
} else {
|
|
2261
|
+
this._setColor(rgba, setInitial);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
/**
|
|
2266
|
+
* this shows the color picker.
|
|
2267
|
+
* The hue circle is constructed once and stored.
|
|
2268
|
+
*/
|
|
2269
|
+
show() {
|
|
2270
|
+
if (this.closeCallback !== undefined) {
|
|
2271
|
+
this.closeCallback();
|
|
2272
|
+
this.closeCallback = undefined;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
this.applied = false;
|
|
2276
|
+
this.frame.style.display = "block";
|
|
2277
|
+
this._generateHueCircle();
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// ------------------------------------------ PRIVATE ----------------------------- //
|
|
2281
|
+
|
|
2282
|
+
/**
|
|
2283
|
+
* Hide the picker. Is called by the cancel button.
|
|
2284
|
+
* Optional boolean to store the previous color for easy access later on.
|
|
2285
|
+
*
|
|
2286
|
+
* @param {boolean} [storePrevious=true]
|
|
2287
|
+
* @private
|
|
2288
|
+
*/
|
|
2289
|
+
_hide(storePrevious = true) {
|
|
2290
|
+
// store the previous color for next time;
|
|
2291
|
+
if (storePrevious === true) {
|
|
2292
|
+
this.previousColor = Object.assign({}, this.color);
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
if (this.applied === true) {
|
|
2296
|
+
this.updateCallback(this.initialColor);
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
this.frame.style.display = "none";
|
|
2300
|
+
|
|
2301
|
+
// call the closing callback, restoring the onclick method.
|
|
2302
|
+
// this is in a setTimeout because it will trigger the show again before the click is done.
|
|
2303
|
+
setTimeout(() => {
|
|
2304
|
+
if (this.closeCallback !== undefined) {
|
|
2305
|
+
this.closeCallback();
|
|
2306
|
+
this.closeCallback = undefined;
|
|
2307
|
+
}
|
|
2308
|
+
}, 0);
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
/**
|
|
2312
|
+
* bound to the save button. Saves and hides.
|
|
2313
|
+
*
|
|
2314
|
+
* @private
|
|
2315
|
+
*/
|
|
2316
|
+
_save() {
|
|
2317
|
+
this.updateCallback(this.color);
|
|
2318
|
+
this.applied = false;
|
|
2319
|
+
this._hide();
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
/**
|
|
2323
|
+
* Bound to apply button. Saves but does not close. Is undone by the cancel button.
|
|
2324
|
+
*
|
|
2325
|
+
* @private
|
|
2326
|
+
*/
|
|
2327
|
+
_apply() {
|
|
2328
|
+
this.applied = true;
|
|
2329
|
+
this.updateCallback(this.color);
|
|
2330
|
+
this._updatePicker(this.color);
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
/**
|
|
2334
|
+
* load the color from the previous session.
|
|
2335
|
+
*
|
|
2336
|
+
* @private
|
|
2337
|
+
*/
|
|
2338
|
+
_loadLast() {
|
|
2339
|
+
if (this.previousColor !== undefined) {
|
|
2340
|
+
this.setColor(this.previousColor, false);
|
|
2341
|
+
} else {
|
|
2342
|
+
alert("There is no last color to load...");
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
/**
|
|
2347
|
+
* set the color, place the picker
|
|
2348
|
+
*
|
|
2349
|
+
* @param {object} rgba
|
|
2350
|
+
* @param {boolean} [setInitial=true]
|
|
2351
|
+
* @private
|
|
2352
|
+
*/
|
|
2353
|
+
_setColor(rgba, setInitial = true) {
|
|
2354
|
+
// store the initial color
|
|
2355
|
+
if (setInitial === true) {
|
|
2356
|
+
this.initialColor = Object.assign({}, rgba);
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
this.color = rgba;
|
|
2360
|
+
const hsv = RGBToHSV(rgba.r, rgba.g, rgba.b);
|
|
2361
|
+
|
|
2362
|
+
const angleConvert = 2 * Math.PI;
|
|
2363
|
+
const radius = this.r * hsv.s;
|
|
2364
|
+
const x =
|
|
2365
|
+
this.centerCoordinates.x + radius * Math.sin(angleConvert * hsv.h);
|
|
2366
|
+
const y =
|
|
2367
|
+
this.centerCoordinates.y + radius * Math.cos(angleConvert * hsv.h);
|
|
2368
|
+
|
|
2369
|
+
this.colorPickerSelector.style.left =
|
|
2370
|
+
x - 0.5 * this.colorPickerSelector.clientWidth + "px";
|
|
2371
|
+
this.colorPickerSelector.style.top =
|
|
2372
|
+
y - 0.5 * this.colorPickerSelector.clientHeight + "px";
|
|
2373
|
+
|
|
2374
|
+
this._updatePicker(rgba);
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
/**
|
|
2378
|
+
* bound to opacity control
|
|
2379
|
+
*
|
|
2380
|
+
* @param {number} value
|
|
2381
|
+
* @private
|
|
2382
|
+
*/
|
|
2383
|
+
_setOpacity(value) {
|
|
2384
|
+
this.color.a = value / 100;
|
|
2385
|
+
this._updatePicker(this.color);
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
/**
|
|
2389
|
+
* bound to brightness control
|
|
2390
|
+
*
|
|
2391
|
+
* @param {number} value
|
|
2392
|
+
* @private
|
|
2393
|
+
*/
|
|
2394
|
+
_setBrightness(value) {
|
|
2395
|
+
const hsv = RGBToHSV(this.color.r, this.color.g, this.color.b);
|
|
2396
|
+
hsv.v = value / 100;
|
|
2397
|
+
const rgba = HSVToRGB(hsv.h, hsv.s, hsv.v);
|
|
2398
|
+
rgba["a"] = this.color.a;
|
|
2399
|
+
this.color = rgba;
|
|
2400
|
+
this._updatePicker();
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
/**
|
|
2404
|
+
* update the color picker. A black circle overlays the hue circle to mimic the brightness decreasing.
|
|
2405
|
+
*
|
|
2406
|
+
* @param {object} rgba
|
|
2407
|
+
* @private
|
|
2408
|
+
*/
|
|
2409
|
+
_updatePicker(rgba = this.color) {
|
|
2410
|
+
const hsv = RGBToHSV(rgba.r, rgba.g, rgba.b);
|
|
2411
|
+
const ctx = this.colorPickerCanvas.getContext("2d");
|
|
2412
|
+
if (this.pixelRation === undefined) {
|
|
2413
|
+
this.pixelRatio =
|
|
2414
|
+
(window.devicePixelRatio || 1) /
|
|
2415
|
+
(ctx.webkitBackingStorePixelRatio ||
|
|
2416
|
+
ctx.mozBackingStorePixelRatio ||
|
|
2417
|
+
ctx.msBackingStorePixelRatio ||
|
|
2418
|
+
ctx.oBackingStorePixelRatio ||
|
|
2419
|
+
ctx.backingStorePixelRatio ||
|
|
2420
|
+
1);
|
|
2421
|
+
}
|
|
2422
|
+
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
|
|
2423
|
+
|
|
2424
|
+
// clear the canvas
|
|
2425
|
+
const w = this.colorPickerCanvas.clientWidth;
|
|
2426
|
+
const h = this.colorPickerCanvas.clientHeight;
|
|
2427
|
+
ctx.clearRect(0, 0, w, h);
|
|
2428
|
+
|
|
2429
|
+
ctx.putImageData(this.hueCircle, 0, 0);
|
|
2430
|
+
ctx.fillStyle = "rgba(0,0,0," + (1 - hsv.v) + ")";
|
|
2431
|
+
ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
|
|
2432
|
+
ctx.fill();
|
|
2433
|
+
|
|
2434
|
+
this.brightnessRange.value = 100 * hsv.v;
|
|
2435
|
+
this.opacityRange.value = 100 * rgba.a;
|
|
2436
|
+
|
|
2437
|
+
this.initialColorDiv.style.backgroundColor =
|
|
2438
|
+
"rgba(" +
|
|
2439
|
+
this.initialColor.r +
|
|
2440
|
+
"," +
|
|
2441
|
+
this.initialColor.g +
|
|
2442
|
+
"," +
|
|
2443
|
+
this.initialColor.b +
|
|
2444
|
+
"," +
|
|
2445
|
+
this.initialColor.a +
|
|
2446
|
+
")";
|
|
2447
|
+
this.newColorDiv.style.backgroundColor =
|
|
2448
|
+
"rgba(" +
|
|
2449
|
+
this.color.r +
|
|
2450
|
+
"," +
|
|
2451
|
+
this.color.g +
|
|
2452
|
+
"," +
|
|
2453
|
+
this.color.b +
|
|
2454
|
+
"," +
|
|
2455
|
+
this.color.a +
|
|
2456
|
+
")";
|
|
2457
|
+
}
|
|
2458
|
+
|
|
2459
|
+
/**
|
|
2460
|
+
* used by create to set the size of the canvas.
|
|
2461
|
+
*
|
|
2462
|
+
* @private
|
|
2463
|
+
*/
|
|
2464
|
+
_setSize() {
|
|
2465
|
+
this.colorPickerCanvas.style.width = "100%";
|
|
2466
|
+
this.colorPickerCanvas.style.height = "100%";
|
|
2467
|
+
|
|
2468
|
+
this.colorPickerCanvas.width = 289 * this.pixelRatio;
|
|
2469
|
+
this.colorPickerCanvas.height = 289 * this.pixelRatio;
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
/**
|
|
2473
|
+
* create all dom elements
|
|
2474
|
+
* TODO: cleanup, lots of similar dom elements
|
|
2475
|
+
*
|
|
2476
|
+
* @private
|
|
2477
|
+
*/
|
|
2478
|
+
_create() {
|
|
2479
|
+
this.frame = document.createElement("div");
|
|
2480
|
+
this.frame.className = "vis-color-picker";
|
|
2481
|
+
|
|
2482
|
+
this.colorPickerDiv = document.createElement("div");
|
|
2483
|
+
this.colorPickerSelector = document.createElement("div");
|
|
2484
|
+
this.colorPickerSelector.className = "vis-selector";
|
|
2485
|
+
this.colorPickerDiv.appendChild(this.colorPickerSelector);
|
|
2486
|
+
|
|
2487
|
+
this.colorPickerCanvas = document.createElement("canvas");
|
|
2488
|
+
this.colorPickerDiv.appendChild(this.colorPickerCanvas);
|
|
2489
|
+
|
|
2490
|
+
if (!this.colorPickerCanvas.getContext) {
|
|
2491
|
+
const noCanvas = document.createElement("DIV");
|
|
2492
|
+
noCanvas.style.color = "red";
|
|
2493
|
+
noCanvas.style.fontWeight = "bold";
|
|
2494
|
+
noCanvas.style.padding = "10px";
|
|
2495
|
+
noCanvas.innerText = "Error: your browser does not support HTML canvas";
|
|
2496
|
+
this.colorPickerCanvas.appendChild(noCanvas);
|
|
2497
|
+
} else {
|
|
2498
|
+
const ctx = this.colorPickerCanvas.getContext("2d");
|
|
2499
|
+
this.pixelRatio =
|
|
2500
|
+
(window.devicePixelRatio || 1) /
|
|
2501
|
+
(ctx.webkitBackingStorePixelRatio ||
|
|
2502
|
+
ctx.mozBackingStorePixelRatio ||
|
|
2503
|
+
ctx.msBackingStorePixelRatio ||
|
|
2504
|
+
ctx.oBackingStorePixelRatio ||
|
|
2505
|
+
ctx.backingStorePixelRatio ||
|
|
2506
|
+
1);
|
|
2507
|
+
this.colorPickerCanvas
|
|
2508
|
+
.getContext("2d")
|
|
2509
|
+
.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
this.colorPickerDiv.className = "vis-color";
|
|
2513
|
+
|
|
2514
|
+
this.opacityDiv = document.createElement("div");
|
|
2515
|
+
this.opacityDiv.className = "vis-opacity";
|
|
2516
|
+
|
|
2517
|
+
this.brightnessDiv = document.createElement("div");
|
|
2518
|
+
this.brightnessDiv.className = "vis-brightness";
|
|
2519
|
+
|
|
2520
|
+
this.arrowDiv = document.createElement("div");
|
|
2521
|
+
this.arrowDiv.className = "vis-arrow";
|
|
2522
|
+
|
|
2523
|
+
this.opacityRange = document.createElement("input");
|
|
2524
|
+
try {
|
|
2525
|
+
this.opacityRange.type = "range"; // Not supported on IE9
|
|
2526
|
+
this.opacityRange.min = "0";
|
|
2527
|
+
this.opacityRange.max = "100";
|
|
2528
|
+
} catch (err) {
|
|
2529
|
+
// TODO: Add some error handling.
|
|
2530
|
+
}
|
|
2531
|
+
this.opacityRange.value = "100";
|
|
2532
|
+
this.opacityRange.className = "vis-range";
|
|
2533
|
+
|
|
2534
|
+
this.brightnessRange = document.createElement("input");
|
|
2535
|
+
try {
|
|
2536
|
+
this.brightnessRange.type = "range"; // Not supported on IE9
|
|
2537
|
+
this.brightnessRange.min = "0";
|
|
2538
|
+
this.brightnessRange.max = "100";
|
|
2539
|
+
} catch (err) {
|
|
2540
|
+
// TODO: Add some error handling.
|
|
2541
|
+
}
|
|
2542
|
+
this.brightnessRange.value = "100";
|
|
2543
|
+
this.brightnessRange.className = "vis-range";
|
|
2544
|
+
|
|
2545
|
+
this.opacityDiv.appendChild(this.opacityRange);
|
|
2546
|
+
this.brightnessDiv.appendChild(this.brightnessRange);
|
|
2547
|
+
|
|
2548
|
+
const me = this;
|
|
2549
|
+
this.opacityRange.onchange = function () {
|
|
2550
|
+
me._setOpacity(this.value);
|
|
2551
|
+
};
|
|
2552
|
+
this.opacityRange.oninput = function () {
|
|
2553
|
+
me._setOpacity(this.value);
|
|
2554
|
+
};
|
|
2555
|
+
this.brightnessRange.onchange = function () {
|
|
2556
|
+
me._setBrightness(this.value);
|
|
2557
|
+
};
|
|
2558
|
+
this.brightnessRange.oninput = function () {
|
|
2559
|
+
me._setBrightness(this.value);
|
|
2560
|
+
};
|
|
2561
|
+
|
|
2562
|
+
this.brightnessLabel = document.createElement("div");
|
|
2563
|
+
this.brightnessLabel.className = "vis-label vis-brightness";
|
|
2564
|
+
this.brightnessLabel.innerText = "brightness:";
|
|
2565
|
+
|
|
2566
|
+
this.opacityLabel = document.createElement("div");
|
|
2567
|
+
this.opacityLabel.className = "vis-label vis-opacity";
|
|
2568
|
+
this.opacityLabel.innerText = "opacity:";
|
|
2569
|
+
|
|
2570
|
+
this.newColorDiv = document.createElement("div");
|
|
2571
|
+
this.newColorDiv.className = "vis-new-color";
|
|
2572
|
+
this.newColorDiv.innerText = "new";
|
|
2573
|
+
|
|
2574
|
+
this.initialColorDiv = document.createElement("div");
|
|
2575
|
+
this.initialColorDiv.className = "vis-initial-color";
|
|
2576
|
+
this.initialColorDiv.innerText = "initial";
|
|
2577
|
+
|
|
2578
|
+
this.cancelButton = document.createElement("div");
|
|
2579
|
+
this.cancelButton.className = "vis-button vis-cancel";
|
|
2580
|
+
this.cancelButton.innerText = "cancel";
|
|
2581
|
+
this.cancelButton.onclick = this._hide.bind(this, false);
|
|
2582
|
+
|
|
2583
|
+
this.applyButton = document.createElement("div");
|
|
2584
|
+
this.applyButton.className = "vis-button vis-apply";
|
|
2585
|
+
this.applyButton.innerText = "apply";
|
|
2586
|
+
this.applyButton.onclick = this._apply.bind(this);
|
|
2587
|
+
|
|
2588
|
+
this.saveButton = document.createElement("div");
|
|
2589
|
+
this.saveButton.className = "vis-button vis-save";
|
|
2590
|
+
this.saveButton.innerText = "save";
|
|
2591
|
+
this.saveButton.onclick = this._save.bind(this);
|
|
2592
|
+
|
|
2593
|
+
this.loadButton = document.createElement("div");
|
|
2594
|
+
this.loadButton.className = "vis-button vis-load";
|
|
2595
|
+
this.loadButton.innerText = "load last";
|
|
2596
|
+
this.loadButton.onclick = this._loadLast.bind(this);
|
|
2597
|
+
|
|
2598
|
+
this.frame.appendChild(this.colorPickerDiv);
|
|
2599
|
+
this.frame.appendChild(this.arrowDiv);
|
|
2600
|
+
this.frame.appendChild(this.brightnessLabel);
|
|
2601
|
+
this.frame.appendChild(this.brightnessDiv);
|
|
2602
|
+
this.frame.appendChild(this.opacityLabel);
|
|
2603
|
+
this.frame.appendChild(this.opacityDiv);
|
|
2604
|
+
this.frame.appendChild(this.newColorDiv);
|
|
2605
|
+
this.frame.appendChild(this.initialColorDiv);
|
|
2606
|
+
|
|
2607
|
+
this.frame.appendChild(this.cancelButton);
|
|
2608
|
+
this.frame.appendChild(this.applyButton);
|
|
2609
|
+
this.frame.appendChild(this.saveButton);
|
|
2610
|
+
this.frame.appendChild(this.loadButton);
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
/**
|
|
2614
|
+
* bind hammer to the color picker
|
|
2615
|
+
*
|
|
2616
|
+
* @private
|
|
2617
|
+
*/
|
|
2618
|
+
_bindHammer() {
|
|
2619
|
+
this.drag = {};
|
|
2620
|
+
this.pinch = {};
|
|
2621
|
+
this.hammer = new Hammer$1(this.colorPickerCanvas);
|
|
2622
|
+
this.hammer.get("pinch").set({ enable: true });
|
|
2623
|
+
|
|
2624
|
+
this.hammer.on("hammer.input", (event) => {
|
|
2625
|
+
if (event.isFirst) {
|
|
2626
|
+
this._moveSelector(event);
|
|
2627
|
+
}
|
|
2628
|
+
});
|
|
2629
|
+
this.hammer.on("tap", (event) => {
|
|
2630
|
+
this._moveSelector(event);
|
|
2631
|
+
});
|
|
2632
|
+
this.hammer.on("panstart", (event) => {
|
|
2633
|
+
this._moveSelector(event);
|
|
2634
|
+
});
|
|
2635
|
+
this.hammer.on("panmove", (event) => {
|
|
2636
|
+
this._moveSelector(event);
|
|
2637
|
+
});
|
|
2638
|
+
this.hammer.on("panend", (event) => {
|
|
2639
|
+
this._moveSelector(event);
|
|
2640
|
+
});
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
/**
|
|
2644
|
+
* generate the hue circle. This is relatively heavy (200ms) and is done only once on the first time it is shown.
|
|
2645
|
+
*
|
|
2646
|
+
* @private
|
|
2647
|
+
*/
|
|
2648
|
+
_generateHueCircle() {
|
|
2649
|
+
if (this.generated === false) {
|
|
2650
|
+
const ctx = this.colorPickerCanvas.getContext("2d");
|
|
2651
|
+
if (this.pixelRation === undefined) {
|
|
2652
|
+
this.pixelRatio =
|
|
2653
|
+
(window.devicePixelRatio || 1) /
|
|
2654
|
+
(ctx.webkitBackingStorePixelRatio ||
|
|
2655
|
+
ctx.mozBackingStorePixelRatio ||
|
|
2656
|
+
ctx.msBackingStorePixelRatio ||
|
|
2657
|
+
ctx.oBackingStorePixelRatio ||
|
|
2658
|
+
ctx.backingStorePixelRatio ||
|
|
2659
|
+
1);
|
|
2660
|
+
}
|
|
2661
|
+
ctx.setTransform(this.pixelRatio, 0, 0, this.pixelRatio, 0, 0);
|
|
2662
|
+
|
|
2663
|
+
// clear the canvas
|
|
2664
|
+
const w = this.colorPickerCanvas.clientWidth;
|
|
2665
|
+
const h = this.colorPickerCanvas.clientHeight;
|
|
2666
|
+
ctx.clearRect(0, 0, w, h);
|
|
2667
|
+
|
|
2668
|
+
// draw hue circle
|
|
2669
|
+
let x, y, hue, sat;
|
|
2670
|
+
this.centerCoordinates = { x: w * 0.5, y: h * 0.5 };
|
|
2671
|
+
this.r = 0.49 * w;
|
|
2672
|
+
const angleConvert = (2 * Math.PI) / 360;
|
|
2673
|
+
const hfac = 1 / 360;
|
|
2674
|
+
const sfac = 1 / this.r;
|
|
2675
|
+
let rgb;
|
|
2676
|
+
for (hue = 0; hue < 360; hue++) {
|
|
2677
|
+
for (sat = 0; sat < this.r; sat++) {
|
|
2678
|
+
x = this.centerCoordinates.x + sat * Math.sin(angleConvert * hue);
|
|
2679
|
+
y = this.centerCoordinates.y + sat * Math.cos(angleConvert * hue);
|
|
2680
|
+
rgb = HSVToRGB(hue * hfac, sat * sfac, 1);
|
|
2681
|
+
ctx.fillStyle = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")";
|
|
2682
|
+
ctx.fillRect(x - 0.5, y - 0.5, 2, 2);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
ctx.strokeStyle = "rgba(0,0,0,1)";
|
|
2686
|
+
ctx.circle(this.centerCoordinates.x, this.centerCoordinates.y, this.r);
|
|
2687
|
+
ctx.stroke();
|
|
2688
|
+
|
|
2689
|
+
this.hueCircle = ctx.getImageData(0, 0, w, h);
|
|
2690
|
+
}
|
|
2691
|
+
this.generated = true;
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
/**
|
|
2695
|
+
* move the selector. This is called by hammer functions.
|
|
2696
|
+
*
|
|
2697
|
+
* @param {Event} event The event
|
|
2698
|
+
* @private
|
|
2699
|
+
*/
|
|
2700
|
+
_moveSelector(event) {
|
|
2701
|
+
const rect = this.colorPickerDiv.getBoundingClientRect();
|
|
2702
|
+
const left = event.center.x - rect.left;
|
|
2703
|
+
const top = event.center.y - rect.top;
|
|
2704
|
+
|
|
2705
|
+
const centerY = 0.5 * this.colorPickerDiv.clientHeight;
|
|
2706
|
+
const centerX = 0.5 * this.colorPickerDiv.clientWidth;
|
|
2707
|
+
|
|
2708
|
+
const x = left - centerX;
|
|
2709
|
+
const y = top - centerY;
|
|
2710
|
+
|
|
2711
|
+
const angle = Math.atan2(x, y);
|
|
2712
|
+
const radius = 0.98 * Math.min(Math.sqrt(x * x + y * y), centerX);
|
|
2713
|
+
|
|
2714
|
+
const newTop = Math.cos(angle) * radius + centerY;
|
|
2715
|
+
const newLeft = Math.sin(angle) * radius + centerX;
|
|
2716
|
+
|
|
2717
|
+
this.colorPickerSelector.style.top =
|
|
2718
|
+
newTop - 0.5 * this.colorPickerSelector.clientHeight + "px";
|
|
2719
|
+
this.colorPickerSelector.style.left =
|
|
2720
|
+
newLeft - 0.5 * this.colorPickerSelector.clientWidth + "px";
|
|
2721
|
+
|
|
2722
|
+
// set color
|
|
2723
|
+
let h = angle / (2 * Math.PI);
|
|
2724
|
+
h = h < 0 ? h + 1 : h;
|
|
2725
|
+
const s = radius / this.r;
|
|
2726
|
+
const hsv = RGBToHSV(this.color.r, this.color.g, this.color.b);
|
|
2727
|
+
hsv.h = h;
|
|
2728
|
+
hsv.s = s;
|
|
2729
|
+
const rgba = HSVToRGB(hsv.h, hsv.s, hsv.v);
|
|
2730
|
+
rgba["a"] = this.color.a;
|
|
2731
|
+
this.color = rgba;
|
|
2732
|
+
|
|
2733
|
+
// update previews
|
|
2734
|
+
this.initialColorDiv.style.backgroundColor =
|
|
2735
|
+
"rgba(" +
|
|
2736
|
+
this.initialColor.r +
|
|
2737
|
+
"," +
|
|
2738
|
+
this.initialColor.g +
|
|
2739
|
+
"," +
|
|
2740
|
+
this.initialColor.b +
|
|
2741
|
+
"," +
|
|
2742
|
+
this.initialColor.a +
|
|
2743
|
+
")";
|
|
2744
|
+
this.newColorDiv.style.backgroundColor =
|
|
2745
|
+
"rgba(" +
|
|
2746
|
+
this.color.r +
|
|
2747
|
+
"," +
|
|
2748
|
+
this.color.g +
|
|
2749
|
+
"," +
|
|
2750
|
+
this.color.b +
|
|
2751
|
+
"," +
|
|
2752
|
+
this.color.a +
|
|
2753
|
+
")";
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
/**
|
|
2758
|
+
* Wrap given text (last argument) in HTML elements (all preceding arguments).
|
|
2759
|
+
*
|
|
2760
|
+
* @param {...any} rest - List of tag names followed by inner text.
|
|
2761
|
+
* @returns An element or a text node.
|
|
2762
|
+
*/
|
|
2763
|
+
function wrapInTag(...rest) {
|
|
2764
|
+
if (rest.length < 1) {
|
|
2765
|
+
throw new TypeError("Invalid arguments.");
|
|
2766
|
+
} else if (rest.length === 1) {
|
|
2767
|
+
return document.createTextNode(rest[0]);
|
|
2768
|
+
} else {
|
|
2769
|
+
const element = document.createElement(rest[0]);
|
|
2770
|
+
element.appendChild(wrapInTag(...rest.slice(1)));
|
|
2771
|
+
return element;
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
/**
|
|
2776
|
+
* The way this works is for all properties of this.possible options, you can supply the property name in any form to list the options.
|
|
2777
|
+
* Boolean options are recognised as Boolean
|
|
2778
|
+
* Number options should be written as array: [default value, min value, max value, stepsize]
|
|
2779
|
+
* Colors should be written as array: ['color', '#ffffff']
|
|
2780
|
+
* Strings with should be written as array: [option1, option2, option3, ..]
|
|
2781
|
+
*
|
|
2782
|
+
* The options are matched with their counterparts in each of the modules and the values used in the configuration are
|
|
2783
|
+
*/
|
|
2784
|
+
class Configurator$1 {
|
|
2785
|
+
/**
|
|
2786
|
+
* @param {object} parentModule | the location where parentModule.setOptions() can be called
|
|
2787
|
+
* @param {object} defaultContainer | the default container of the module
|
|
2788
|
+
* @param {object} configureOptions | the fully configured and predefined options set found in allOptions.js
|
|
2789
|
+
* @param {number} pixelRatio | canvas pixel ratio
|
|
2790
|
+
* @param {Function} hideOption | custom logic to dynamically hide options
|
|
2791
|
+
*/
|
|
2792
|
+
constructor(
|
|
2793
|
+
parentModule,
|
|
2794
|
+
defaultContainer,
|
|
2795
|
+
configureOptions,
|
|
2796
|
+
pixelRatio = 1,
|
|
2797
|
+
hideOption = () => false
|
|
2798
|
+
) {
|
|
2799
|
+
this.parent = parentModule;
|
|
2800
|
+
this.changedOptions = [];
|
|
2801
|
+
this.container = defaultContainer;
|
|
2802
|
+
this.allowCreation = false;
|
|
2803
|
+
this.hideOption = hideOption;
|
|
2804
|
+
|
|
2805
|
+
this.options = {};
|
|
2806
|
+
this.initialized = false;
|
|
2807
|
+
this.popupCounter = 0;
|
|
2808
|
+
this.defaultOptions = {
|
|
2809
|
+
enabled: false,
|
|
2810
|
+
filter: true,
|
|
2811
|
+
container: undefined,
|
|
2812
|
+
showButton: true,
|
|
2813
|
+
};
|
|
2814
|
+
Object.assign(this.options, this.defaultOptions);
|
|
2815
|
+
|
|
2816
|
+
this.configureOptions = configureOptions;
|
|
2817
|
+
this.moduleOptions = {};
|
|
2818
|
+
this.domElements = [];
|
|
2819
|
+
this.popupDiv = {};
|
|
2820
|
+
this.popupLimit = 5;
|
|
2821
|
+
this.popupHistory = {};
|
|
2822
|
+
this.colorPicker = new ColorPicker$1(pixelRatio);
|
|
2823
|
+
this.wrapper = undefined;
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
/**
|
|
2827
|
+
* refresh all options.
|
|
2828
|
+
* Because all modules parse their options by themselves, we just use their options. We copy them here.
|
|
2829
|
+
*
|
|
2830
|
+
* @param {object} options
|
|
2831
|
+
*/
|
|
2832
|
+
setOptions(options) {
|
|
2833
|
+
if (options !== undefined) {
|
|
2834
|
+
// reset the popup history because the indices may have been changed.
|
|
2835
|
+
this.popupHistory = {};
|
|
2836
|
+
this._removePopup();
|
|
2837
|
+
|
|
2838
|
+
let enabled = true;
|
|
2839
|
+
if (typeof options === "string") {
|
|
2840
|
+
this.options.filter = options;
|
|
2841
|
+
} else if (Array.isArray(options)) {
|
|
2842
|
+
this.options.filter = options.join();
|
|
2843
|
+
} else if (typeof options === "object") {
|
|
2844
|
+
if (options == null) {
|
|
2845
|
+
throw new TypeError("options cannot be null");
|
|
2846
|
+
}
|
|
2847
|
+
if (options.container !== undefined) {
|
|
2848
|
+
this.options.container = options.container;
|
|
2849
|
+
}
|
|
2850
|
+
if (options.filter !== undefined) {
|
|
2851
|
+
this.options.filter = options.filter;
|
|
2852
|
+
}
|
|
2853
|
+
if (options.showButton !== undefined) {
|
|
2854
|
+
this.options.showButton = options.showButton;
|
|
2855
|
+
}
|
|
2856
|
+
if (options.enabled !== undefined) {
|
|
2857
|
+
enabled = options.enabled;
|
|
2858
|
+
}
|
|
2859
|
+
} else if (typeof options === "boolean") {
|
|
2860
|
+
this.options.filter = true;
|
|
2861
|
+
enabled = options;
|
|
2862
|
+
} else if (typeof options === "function") {
|
|
2863
|
+
this.options.filter = options;
|
|
2864
|
+
enabled = true;
|
|
2865
|
+
}
|
|
2866
|
+
if (this.options.filter === false) {
|
|
2867
|
+
enabled = false;
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
this.options.enabled = enabled;
|
|
2871
|
+
}
|
|
2872
|
+
this._clean();
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
/**
|
|
2876
|
+
*
|
|
2877
|
+
* @param {object} moduleOptions
|
|
2878
|
+
*/
|
|
2879
|
+
setModuleOptions(moduleOptions) {
|
|
2880
|
+
this.moduleOptions = moduleOptions;
|
|
2881
|
+
if (this.options.enabled === true) {
|
|
2882
|
+
this._clean();
|
|
2883
|
+
if (this.options.container !== undefined) {
|
|
2884
|
+
this.container = this.options.container;
|
|
2885
|
+
}
|
|
2886
|
+
this._create();
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
/**
|
|
2891
|
+
* Create all DOM elements
|
|
2892
|
+
*
|
|
2893
|
+
* @private
|
|
2894
|
+
*/
|
|
2895
|
+
_create() {
|
|
2896
|
+
this._clean();
|
|
2897
|
+
this.changedOptions = [];
|
|
2898
|
+
|
|
2899
|
+
const filter = this.options.filter;
|
|
2900
|
+
let counter = 0;
|
|
2901
|
+
let show = false;
|
|
2902
|
+
for (const option in this.configureOptions) {
|
|
2903
|
+
if (Object.prototype.hasOwnProperty.call(this.configureOptions, option)) {
|
|
2904
|
+
this.allowCreation = false;
|
|
2905
|
+
show = false;
|
|
2906
|
+
if (typeof filter === "function") {
|
|
2907
|
+
show = filter(option, []);
|
|
2908
|
+
show =
|
|
2909
|
+
show ||
|
|
2910
|
+
this._handleObject(this.configureOptions[option], [option], true);
|
|
2911
|
+
} else if (filter === true || filter.indexOf(option) !== -1) {
|
|
2912
|
+
show = true;
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
if (show !== false) {
|
|
2916
|
+
this.allowCreation = true;
|
|
2917
|
+
|
|
2918
|
+
// linebreak between categories
|
|
2919
|
+
if (counter > 0) {
|
|
2920
|
+
this._makeItem([]);
|
|
2921
|
+
}
|
|
2922
|
+
// a header for the category
|
|
2923
|
+
this._makeHeader(option);
|
|
2924
|
+
|
|
2925
|
+
// get the sub options
|
|
2926
|
+
this._handleObject(this.configureOptions[option], [option]);
|
|
2927
|
+
}
|
|
2928
|
+
counter++;
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
this._makeButton();
|
|
2932
|
+
this._push();
|
|
2933
|
+
//~ this.colorPicker.insertTo(this.container);
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2936
|
+
/**
|
|
2937
|
+
* draw all DOM elements on the screen
|
|
2938
|
+
*
|
|
2939
|
+
* @private
|
|
2940
|
+
*/
|
|
2941
|
+
_push() {
|
|
2942
|
+
this.wrapper = document.createElement("div");
|
|
2943
|
+
this.wrapper.className = "vis-configuration-wrapper";
|
|
2944
|
+
this.container.appendChild(this.wrapper);
|
|
2945
|
+
for (let i = 0; i < this.domElements.length; i++) {
|
|
2946
|
+
this.wrapper.appendChild(this.domElements[i]);
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
this._showPopupIfNeeded();
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2952
|
+
/**
|
|
2953
|
+
* delete all DOM elements
|
|
2954
|
+
*
|
|
2955
|
+
* @private
|
|
2956
|
+
*/
|
|
2957
|
+
_clean() {
|
|
2958
|
+
for (let i = 0; i < this.domElements.length; i++) {
|
|
2959
|
+
this.wrapper.removeChild(this.domElements[i]);
|
|
2960
|
+
}
|
|
2961
|
+
|
|
2962
|
+
if (this.wrapper !== undefined) {
|
|
2963
|
+
this.container.removeChild(this.wrapper);
|
|
2964
|
+
this.wrapper = undefined;
|
|
2965
|
+
}
|
|
2966
|
+
this.domElements = [];
|
|
2967
|
+
|
|
2968
|
+
this._removePopup();
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
/**
|
|
2972
|
+
* get the value from the actualOptions if it exists
|
|
2973
|
+
*
|
|
2974
|
+
* @param {Array} path | where to look for the actual option
|
|
2975
|
+
* @returns {*}
|
|
2976
|
+
* @private
|
|
2977
|
+
*/
|
|
2978
|
+
_getValue(path) {
|
|
2979
|
+
let base = this.moduleOptions;
|
|
2980
|
+
for (let i = 0; i < path.length; i++) {
|
|
2981
|
+
if (base[path[i]] !== undefined) {
|
|
2982
|
+
base = base[path[i]];
|
|
2983
|
+
} else {
|
|
2984
|
+
base = undefined;
|
|
2985
|
+
break;
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
return base;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
/**
|
|
2992
|
+
* all option elements are wrapped in an item
|
|
2993
|
+
*
|
|
2994
|
+
* @param {Array} path | where to look for the actual option
|
|
2995
|
+
* @param {Array.<Element>} domElements
|
|
2996
|
+
* @returns {number}
|
|
2997
|
+
* @private
|
|
2998
|
+
*/
|
|
2999
|
+
_makeItem(path, ...domElements) {
|
|
3000
|
+
if (this.allowCreation === true) {
|
|
3001
|
+
const item = document.createElement("div");
|
|
3002
|
+
item.className =
|
|
3003
|
+
"vis-configuration vis-config-item vis-config-s" + path.length;
|
|
3004
|
+
domElements.forEach((element) => {
|
|
3005
|
+
item.appendChild(element);
|
|
3006
|
+
});
|
|
3007
|
+
this.domElements.push(item);
|
|
3008
|
+
return this.domElements.length;
|
|
3009
|
+
}
|
|
3010
|
+
return 0;
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
/**
|
|
3014
|
+
* header for major subjects
|
|
3015
|
+
*
|
|
3016
|
+
* @param {string} name
|
|
3017
|
+
* @private
|
|
3018
|
+
*/
|
|
3019
|
+
_makeHeader(name) {
|
|
3020
|
+
const div = document.createElement("div");
|
|
3021
|
+
div.className = "vis-configuration vis-config-header";
|
|
3022
|
+
div.innerText = name;
|
|
3023
|
+
this._makeItem([], div);
|
|
3024
|
+
}
|
|
3025
|
+
|
|
3026
|
+
/**
|
|
3027
|
+
* make a label, if it is an object label, it gets different styling.
|
|
3028
|
+
*
|
|
3029
|
+
* @param {string} name
|
|
3030
|
+
* @param {Array} path | where to look for the actual option
|
|
3031
|
+
* @param {string} objectLabel
|
|
3032
|
+
* @returns {HTMLElement}
|
|
3033
|
+
* @private
|
|
3034
|
+
*/
|
|
3035
|
+
_makeLabel(name, path, objectLabel = false) {
|
|
3036
|
+
const div = document.createElement("div");
|
|
3037
|
+
div.className =
|
|
3038
|
+
"vis-configuration vis-config-label vis-config-s" + path.length;
|
|
3039
|
+
if (objectLabel === true) {
|
|
3040
|
+
while (div.firstChild) {
|
|
3041
|
+
div.removeChild(div.firstChild);
|
|
3042
|
+
}
|
|
3043
|
+
div.appendChild(wrapInTag("i", "b", name));
|
|
3044
|
+
} else {
|
|
3045
|
+
div.innerText = name + ":";
|
|
3046
|
+
}
|
|
3047
|
+
return div;
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
/**
|
|
3051
|
+
* make a dropdown list for multiple possible string optoins
|
|
3052
|
+
*
|
|
3053
|
+
* @param {Array.<number>} arr
|
|
3054
|
+
* @param {number} value
|
|
3055
|
+
* @param {Array} path | where to look for the actual option
|
|
3056
|
+
* @private
|
|
3057
|
+
*/
|
|
3058
|
+
_makeDropdown(arr, value, path) {
|
|
3059
|
+
const select = document.createElement("select");
|
|
3060
|
+
select.className = "vis-configuration vis-config-select";
|
|
3061
|
+
let selectedValue = 0;
|
|
3062
|
+
if (value !== undefined) {
|
|
3063
|
+
if (arr.indexOf(value) !== -1) {
|
|
3064
|
+
selectedValue = arr.indexOf(value);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3068
|
+
for (let i = 0; i < arr.length; i++) {
|
|
3069
|
+
const option = document.createElement("option");
|
|
3070
|
+
option.value = arr[i];
|
|
3071
|
+
if (i === selectedValue) {
|
|
3072
|
+
option.selected = "selected";
|
|
3073
|
+
}
|
|
3074
|
+
option.innerText = arr[i];
|
|
3075
|
+
select.appendChild(option);
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
const me = this;
|
|
3079
|
+
select.onchange = function () {
|
|
3080
|
+
me._update(this.value, path);
|
|
3081
|
+
};
|
|
3082
|
+
|
|
3083
|
+
const label = this._makeLabel(path[path.length - 1], path);
|
|
3084
|
+
this._makeItem(path, label, select);
|
|
3085
|
+
}
|
|
3086
|
+
|
|
3087
|
+
/**
|
|
3088
|
+
* make a range object for numeric options
|
|
3089
|
+
*
|
|
3090
|
+
* @param {Array.<number>} arr
|
|
3091
|
+
* @param {number} value
|
|
3092
|
+
* @param {Array} path | where to look for the actual option
|
|
3093
|
+
* @private
|
|
3094
|
+
*/
|
|
3095
|
+
_makeRange(arr, value, path) {
|
|
3096
|
+
const defaultValue = arr[0];
|
|
3097
|
+
const min = arr[1];
|
|
3098
|
+
const max = arr[2];
|
|
3099
|
+
const step = arr[3];
|
|
3100
|
+
const range = document.createElement("input");
|
|
3101
|
+
range.className = "vis-configuration vis-config-range";
|
|
3102
|
+
try {
|
|
3103
|
+
range.type = "range"; // not supported on IE9
|
|
3104
|
+
range.min = min;
|
|
3105
|
+
range.max = max;
|
|
3106
|
+
} catch (err) {
|
|
3107
|
+
// TODO: Add some error handling.
|
|
3108
|
+
}
|
|
3109
|
+
range.step = step;
|
|
3110
|
+
|
|
3111
|
+
// set up the popup settings in case they are needed.
|
|
3112
|
+
let popupString = "";
|
|
3113
|
+
let popupValue = 0;
|
|
3114
|
+
|
|
3115
|
+
if (value !== undefined) {
|
|
3116
|
+
const factor = 1.2;
|
|
3117
|
+
if (value < 0 && value * factor < min) {
|
|
3118
|
+
range.min = Math.ceil(value * factor);
|
|
3119
|
+
popupValue = range.min;
|
|
3120
|
+
popupString = "range increased";
|
|
3121
|
+
} else if (value / factor < min) {
|
|
3122
|
+
range.min = Math.ceil(value / factor);
|
|
3123
|
+
popupValue = range.min;
|
|
3124
|
+
popupString = "range increased";
|
|
3125
|
+
}
|
|
3126
|
+
if (value * factor > max && max !== 1) {
|
|
3127
|
+
range.max = Math.ceil(value * factor);
|
|
3128
|
+
popupValue = range.max;
|
|
3129
|
+
popupString = "range increased";
|
|
3130
|
+
}
|
|
3131
|
+
range.value = value;
|
|
3132
|
+
} else {
|
|
3133
|
+
range.value = defaultValue;
|
|
3134
|
+
}
|
|
3135
|
+
|
|
3136
|
+
const input = document.createElement("input");
|
|
3137
|
+
input.className = "vis-configuration vis-config-rangeinput";
|
|
3138
|
+
input.value = range.value;
|
|
3139
|
+
|
|
3140
|
+
const me = this;
|
|
3141
|
+
range.onchange = function () {
|
|
3142
|
+
input.value = this.value;
|
|
3143
|
+
me._update(Number(this.value), path);
|
|
3144
|
+
};
|
|
3145
|
+
range.oninput = function () {
|
|
3146
|
+
input.value = this.value;
|
|
3147
|
+
};
|
|
3148
|
+
|
|
3149
|
+
const label = this._makeLabel(path[path.length - 1], path);
|
|
3150
|
+
const itemIndex = this._makeItem(path, label, range, input);
|
|
3151
|
+
|
|
3152
|
+
// if a popup is needed AND it has not been shown for this value, show it.
|
|
3153
|
+
if (popupString !== "" && this.popupHistory[itemIndex] !== popupValue) {
|
|
3154
|
+
this.popupHistory[itemIndex] = popupValue;
|
|
3155
|
+
this._setupPopup(popupString, itemIndex);
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
|
|
3159
|
+
/**
|
|
3160
|
+
* make a button object
|
|
3161
|
+
*
|
|
3162
|
+
* @private
|
|
3163
|
+
*/
|
|
3164
|
+
_makeButton() {
|
|
3165
|
+
if (this.options.showButton === true) {
|
|
3166
|
+
const generateButton = document.createElement("div");
|
|
3167
|
+
generateButton.className = "vis-configuration vis-config-button";
|
|
3168
|
+
generateButton.innerText = "generate options";
|
|
3169
|
+
generateButton.onclick = () => {
|
|
3170
|
+
this._printOptions();
|
|
3171
|
+
};
|
|
3172
|
+
generateButton.onmouseover = () => {
|
|
3173
|
+
generateButton.className = "vis-configuration vis-config-button hover";
|
|
3174
|
+
};
|
|
3175
|
+
generateButton.onmouseout = () => {
|
|
3176
|
+
generateButton.className = "vis-configuration vis-config-button";
|
|
3177
|
+
};
|
|
3178
|
+
|
|
3179
|
+
this.optionsContainer = document.createElement("div");
|
|
3180
|
+
this.optionsContainer.className =
|
|
3181
|
+
"vis-configuration vis-config-option-container";
|
|
3182
|
+
|
|
3183
|
+
this.domElements.push(this.optionsContainer);
|
|
3184
|
+
this.domElements.push(generateButton);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
/**
|
|
3189
|
+
* prepare the popup
|
|
3190
|
+
*
|
|
3191
|
+
* @param {string} string
|
|
3192
|
+
* @param {number} index
|
|
3193
|
+
* @private
|
|
3194
|
+
*/
|
|
3195
|
+
_setupPopup(string, index) {
|
|
3196
|
+
if (
|
|
3197
|
+
this.initialized === true &&
|
|
3198
|
+
this.allowCreation === true &&
|
|
3199
|
+
this.popupCounter < this.popupLimit
|
|
3200
|
+
) {
|
|
3201
|
+
const div = document.createElement("div");
|
|
3202
|
+
div.id = "vis-configuration-popup";
|
|
3203
|
+
div.className = "vis-configuration-popup";
|
|
3204
|
+
div.innerText = string;
|
|
3205
|
+
div.onclick = () => {
|
|
3206
|
+
this._removePopup();
|
|
3207
|
+
};
|
|
3208
|
+
this.popupCounter += 1;
|
|
3209
|
+
this.popupDiv = { html: div, index: index };
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
|
|
3213
|
+
/**
|
|
3214
|
+
* remove the popup from the dom
|
|
3215
|
+
*
|
|
3216
|
+
* @private
|
|
3217
|
+
*/
|
|
3218
|
+
_removePopup() {
|
|
3219
|
+
if (this.popupDiv.html !== undefined) {
|
|
3220
|
+
this.popupDiv.html.parentNode.removeChild(this.popupDiv.html);
|
|
3221
|
+
clearTimeout(this.popupDiv.hideTimeout);
|
|
3222
|
+
clearTimeout(this.popupDiv.deleteTimeout);
|
|
3223
|
+
this.popupDiv = {};
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
/**
|
|
3228
|
+
* Show the popup if it is needed.
|
|
3229
|
+
*
|
|
3230
|
+
* @private
|
|
3231
|
+
*/
|
|
3232
|
+
_showPopupIfNeeded() {
|
|
3233
|
+
if (this.popupDiv.html !== undefined) {
|
|
3234
|
+
const correspondingElement = this.domElements[this.popupDiv.index];
|
|
3235
|
+
const rect = correspondingElement.getBoundingClientRect();
|
|
3236
|
+
this.popupDiv.html.style.left = rect.left + "px";
|
|
3237
|
+
this.popupDiv.html.style.top = rect.top - 30 + "px"; // 30 is the height;
|
|
3238
|
+
document.body.appendChild(this.popupDiv.html);
|
|
3239
|
+
this.popupDiv.hideTimeout = setTimeout(() => {
|
|
3240
|
+
this.popupDiv.html.style.opacity = 0;
|
|
3241
|
+
}, 1500);
|
|
3242
|
+
this.popupDiv.deleteTimeout = setTimeout(() => {
|
|
3243
|
+
this._removePopup();
|
|
3244
|
+
}, 1800);
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
/**
|
|
3249
|
+
* make a checkbox for boolean options.
|
|
3250
|
+
*
|
|
3251
|
+
* @param {number} defaultValue
|
|
3252
|
+
* @param {number} value
|
|
3253
|
+
* @param {Array} path | where to look for the actual option
|
|
3254
|
+
* @private
|
|
3255
|
+
*/
|
|
3256
|
+
_makeCheckbox(defaultValue, value, path) {
|
|
3257
|
+
const checkbox = document.createElement("input");
|
|
3258
|
+
checkbox.type = "checkbox";
|
|
3259
|
+
checkbox.className = "vis-configuration vis-config-checkbox";
|
|
3260
|
+
checkbox.checked = defaultValue;
|
|
3261
|
+
if (value !== undefined) {
|
|
3262
|
+
checkbox.checked = value;
|
|
3263
|
+
if (value !== defaultValue) {
|
|
3264
|
+
if (typeof defaultValue === "object") {
|
|
3265
|
+
if (value !== defaultValue.enabled) {
|
|
3266
|
+
this.changedOptions.push({ path: path, value: value });
|
|
3267
|
+
}
|
|
3268
|
+
} else {
|
|
3269
|
+
this.changedOptions.push({ path: path, value: value });
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
const me = this;
|
|
3275
|
+
checkbox.onchange = function () {
|
|
3276
|
+
me._update(this.checked, path);
|
|
3277
|
+
};
|
|
3278
|
+
|
|
3279
|
+
const label = this._makeLabel(path[path.length - 1], path);
|
|
3280
|
+
this._makeItem(path, label, checkbox);
|
|
3281
|
+
}
|
|
3282
|
+
|
|
3283
|
+
/**
|
|
3284
|
+
* make a text input field for string options.
|
|
3285
|
+
*
|
|
3286
|
+
* @param {number} defaultValue
|
|
3287
|
+
* @param {number} value
|
|
3288
|
+
* @param {Array} path | where to look for the actual option
|
|
3289
|
+
* @private
|
|
3290
|
+
*/
|
|
3291
|
+
_makeTextInput(defaultValue, value, path) {
|
|
3292
|
+
const checkbox = document.createElement("input");
|
|
3293
|
+
checkbox.type = "text";
|
|
3294
|
+
checkbox.className = "vis-configuration vis-config-text";
|
|
3295
|
+
checkbox.value = value;
|
|
3296
|
+
if (value !== defaultValue) {
|
|
3297
|
+
this.changedOptions.push({ path: path, value: value });
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3300
|
+
const me = this;
|
|
3301
|
+
checkbox.onchange = function () {
|
|
3302
|
+
me._update(this.value, path);
|
|
3303
|
+
};
|
|
3304
|
+
|
|
3305
|
+
const label = this._makeLabel(path[path.length - 1], path);
|
|
3306
|
+
this._makeItem(path, label, checkbox);
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3309
|
+
/**
|
|
3310
|
+
* make a color field with a color picker for color fields
|
|
3311
|
+
*
|
|
3312
|
+
* @param {Array.<number>} arr
|
|
3313
|
+
* @param {number} value
|
|
3314
|
+
* @param {Array} path | where to look for the actual option
|
|
3315
|
+
* @private
|
|
3316
|
+
*/
|
|
3317
|
+
_makeColorField(arr, value, path) {
|
|
3318
|
+
const defaultColor = arr[1];
|
|
3319
|
+
const div = document.createElement("div");
|
|
3320
|
+
value = value === undefined ? defaultColor : value;
|
|
3321
|
+
|
|
3322
|
+
if (value !== "none") {
|
|
3323
|
+
div.className = "vis-configuration vis-config-colorBlock";
|
|
3324
|
+
div.style.backgroundColor = value;
|
|
3325
|
+
} else {
|
|
3326
|
+
div.className = "vis-configuration vis-config-colorBlock none";
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
value = value === undefined ? defaultColor : value;
|
|
3330
|
+
div.onclick = () => {
|
|
3331
|
+
this._showColorPicker(value, div, path);
|
|
3332
|
+
};
|
|
3333
|
+
|
|
3334
|
+
const label = this._makeLabel(path[path.length - 1], path);
|
|
3335
|
+
this._makeItem(path, label, div);
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
/**
|
|
3339
|
+
* used by the color buttons to call the color picker.
|
|
3340
|
+
*
|
|
3341
|
+
* @param {number} value
|
|
3342
|
+
* @param {HTMLElement} div
|
|
3343
|
+
* @param {Array} path | where to look for the actual option
|
|
3344
|
+
* @private
|
|
3345
|
+
*/
|
|
3346
|
+
_showColorPicker(value, div, path) {
|
|
3347
|
+
// clear the callback from this div
|
|
3348
|
+
div.onclick = function () {};
|
|
3349
|
+
|
|
3350
|
+
this.colorPicker.insertTo(div);
|
|
3351
|
+
this.colorPicker.show();
|
|
3352
|
+
|
|
3353
|
+
this.colorPicker.setColor(value);
|
|
3354
|
+
this.colorPicker.setUpdateCallback((color) => {
|
|
3355
|
+
const colorString =
|
|
3356
|
+
"rgba(" + color.r + "," + color.g + "," + color.b + "," + color.a + ")";
|
|
3357
|
+
div.style.backgroundColor = colorString;
|
|
3358
|
+
this._update(colorString, path);
|
|
3359
|
+
});
|
|
3360
|
+
|
|
3361
|
+
// on close of the colorpicker, restore the callback.
|
|
3362
|
+
this.colorPicker.setCloseCallback(() => {
|
|
3363
|
+
div.onclick = () => {
|
|
3364
|
+
this._showColorPicker(value, div, path);
|
|
3365
|
+
};
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
/**
|
|
3370
|
+
* parse an object and draw the correct items
|
|
3371
|
+
*
|
|
3372
|
+
* @param {object} obj
|
|
3373
|
+
* @param {Array} [path=[]] | where to look for the actual option
|
|
3374
|
+
* @param {boolean} [checkOnly=false]
|
|
3375
|
+
* @returns {boolean}
|
|
3376
|
+
* @private
|
|
3377
|
+
*/
|
|
3378
|
+
_handleObject(obj, path = [], checkOnly = false) {
|
|
3379
|
+
let show = false;
|
|
3380
|
+
const filter = this.options.filter;
|
|
3381
|
+
let visibleInSet = false;
|
|
3382
|
+
for (const subObj in obj) {
|
|
3383
|
+
if (Object.prototype.hasOwnProperty.call(obj, subObj)) {
|
|
3384
|
+
show = true;
|
|
3385
|
+
const item = obj[subObj];
|
|
3386
|
+
const newPath = copyAndExtendArray(path, subObj);
|
|
3387
|
+
if (typeof filter === "function") {
|
|
3388
|
+
show = filter(subObj, path);
|
|
3389
|
+
|
|
3390
|
+
// if needed we must go deeper into the object.
|
|
3391
|
+
if (show === false) {
|
|
3392
|
+
if (
|
|
3393
|
+
!Array.isArray(item) &&
|
|
3394
|
+
typeof item !== "string" &&
|
|
3395
|
+
typeof item !== "boolean" &&
|
|
3396
|
+
item instanceof Object
|
|
3397
|
+
) {
|
|
3398
|
+
this.allowCreation = false;
|
|
3399
|
+
show = this._handleObject(item, newPath, true);
|
|
3400
|
+
this.allowCreation = checkOnly === false;
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
if (show !== false) {
|
|
3406
|
+
visibleInSet = true;
|
|
3407
|
+
const value = this._getValue(newPath);
|
|
3408
|
+
|
|
3409
|
+
if (Array.isArray(item)) {
|
|
3410
|
+
this._handleArray(item, value, newPath);
|
|
3411
|
+
} else if (typeof item === "string") {
|
|
3412
|
+
this._makeTextInput(item, value, newPath);
|
|
3413
|
+
} else if (typeof item === "boolean") {
|
|
3414
|
+
this._makeCheckbox(item, value, newPath);
|
|
3415
|
+
} else if (item instanceof Object) {
|
|
3416
|
+
// skip the options that are not enabled
|
|
3417
|
+
if (!this.hideOption(path, subObj, this.moduleOptions)) {
|
|
3418
|
+
// initially collapse options with an disabled enabled option.
|
|
3419
|
+
if (item.enabled !== undefined) {
|
|
3420
|
+
const enabledPath = copyAndExtendArray(newPath, "enabled");
|
|
3421
|
+
const enabledValue = this._getValue(enabledPath);
|
|
3422
|
+
if (enabledValue === true) {
|
|
3423
|
+
const label = this._makeLabel(subObj, newPath, true);
|
|
3424
|
+
this._makeItem(newPath, label);
|
|
3425
|
+
visibleInSet =
|
|
3426
|
+
this._handleObject(item, newPath) || visibleInSet;
|
|
3427
|
+
} else {
|
|
3428
|
+
this._makeCheckbox(item, enabledValue, newPath);
|
|
3429
|
+
}
|
|
3430
|
+
} else {
|
|
3431
|
+
const label = this._makeLabel(subObj, newPath, true);
|
|
3432
|
+
this._makeItem(newPath, label);
|
|
3433
|
+
visibleInSet =
|
|
3434
|
+
this._handleObject(item, newPath) || visibleInSet;
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
} else {
|
|
3438
|
+
console.error("dont know how to handle", item, subObj, newPath);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
return visibleInSet;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
/**
|
|
3447
|
+
* handle the array type of option
|
|
3448
|
+
*
|
|
3449
|
+
* @param {Array.<number>} arr
|
|
3450
|
+
* @param {number} value
|
|
3451
|
+
* @param {Array} path | where to look for the actual option
|
|
3452
|
+
* @private
|
|
3453
|
+
*/
|
|
3454
|
+
_handleArray(arr, value, path) {
|
|
3455
|
+
if (typeof arr[0] === "string" && arr[0] === "color") {
|
|
3456
|
+
this._makeColorField(arr, value, path);
|
|
3457
|
+
if (arr[1] !== value) {
|
|
3458
|
+
this.changedOptions.push({ path: path, value: value });
|
|
3459
|
+
}
|
|
3460
|
+
} else if (typeof arr[0] === "string") {
|
|
3461
|
+
this._makeDropdown(arr, value, path);
|
|
3462
|
+
if (arr[0] !== value) {
|
|
3463
|
+
this.changedOptions.push({ path: path, value: value });
|
|
3464
|
+
}
|
|
3465
|
+
} else if (typeof arr[0] === "number") {
|
|
3466
|
+
this._makeRange(arr, value, path);
|
|
3467
|
+
if (arr[0] !== value) {
|
|
3468
|
+
this.changedOptions.push({ path: path, value: Number(value) });
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
/**
|
|
3474
|
+
* called to update the network with the new settings.
|
|
3475
|
+
*
|
|
3476
|
+
* @param {number} value
|
|
3477
|
+
* @param {Array} path | where to look for the actual option
|
|
3478
|
+
* @private
|
|
3479
|
+
*/
|
|
3480
|
+
_update(value, path) {
|
|
3481
|
+
const options = this._constructOptions(value, path);
|
|
3482
|
+
|
|
3483
|
+
if (
|
|
3484
|
+
this.parent.body &&
|
|
3485
|
+
this.parent.body.emitter &&
|
|
3486
|
+
this.parent.body.emitter.emit
|
|
3487
|
+
) {
|
|
3488
|
+
this.parent.body.emitter.emit("configChange", options);
|
|
3489
|
+
}
|
|
3490
|
+
this.initialized = true;
|
|
3491
|
+
this.parent.setOptions(options);
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
/**
|
|
3495
|
+
*
|
|
3496
|
+
* @param {string | boolean} value
|
|
3497
|
+
* @param {Array.<string>} path
|
|
3498
|
+
* @param {{}} optionsObj
|
|
3499
|
+
* @returns {{}}
|
|
3500
|
+
* @private
|
|
3501
|
+
*/
|
|
3502
|
+
_constructOptions(value, path, optionsObj = {}) {
|
|
3503
|
+
let pointer = optionsObj;
|
|
3504
|
+
|
|
3505
|
+
// when dropdown boxes can be string or boolean, we typecast it into correct types
|
|
3506
|
+
value = value === "true" ? true : value;
|
|
3507
|
+
value = value === "false" ? false : value;
|
|
3508
|
+
|
|
3509
|
+
for (let i = 0; i < path.length; i++) {
|
|
3510
|
+
if (path[i] !== "global") {
|
|
3511
|
+
if (pointer[path[i]] === undefined) {
|
|
3512
|
+
pointer[path[i]] = {};
|
|
3513
|
+
}
|
|
3514
|
+
if (i !== path.length - 1) {
|
|
3515
|
+
pointer = pointer[path[i]];
|
|
3516
|
+
} else {
|
|
3517
|
+
pointer[path[i]] = value;
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
}
|
|
3521
|
+
return optionsObj;
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
/**
|
|
3525
|
+
* @private
|
|
3526
|
+
*/
|
|
3527
|
+
_printOptions() {
|
|
3528
|
+
const options = this.getOptions();
|
|
3529
|
+
|
|
3530
|
+
while (this.optionsContainer.firstChild) {
|
|
3531
|
+
this.optionsContainer.removeChild(this.optionsContainer.firstChild);
|
|
3532
|
+
}
|
|
3533
|
+
this.optionsContainer.appendChild(
|
|
3534
|
+
wrapInTag("pre", "const options = " + JSON.stringify(options, null, 2))
|
|
3535
|
+
);
|
|
3536
|
+
}
|
|
3537
|
+
|
|
3538
|
+
/**
|
|
3539
|
+
*
|
|
3540
|
+
* @returns {{}} options
|
|
3541
|
+
*/
|
|
3542
|
+
getOptions() {
|
|
3543
|
+
const options = {};
|
|
3544
|
+
for (let i = 0; i < this.changedOptions.length; i++) {
|
|
3545
|
+
this._constructOptions(
|
|
3546
|
+
this.changedOptions[i].value,
|
|
3547
|
+
this.changedOptions[i].path,
|
|
3548
|
+
options
|
|
3549
|
+
);
|
|
3550
|
+
}
|
|
3551
|
+
return options;
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
/**
|
|
3556
|
+
* Popup is a class to create a popup window with some text
|
|
3557
|
+
*/
|
|
3558
|
+
class Popup$1 {
|
|
3559
|
+
/**
|
|
3560
|
+
* @param {Element} container The container object.
|
|
3561
|
+
* @param {string} overflowMethod How the popup should act to overflowing ('flip' or 'cap')
|
|
3562
|
+
*/
|
|
3563
|
+
constructor(container, overflowMethod) {
|
|
3564
|
+
this.container = container;
|
|
3565
|
+
this.overflowMethod = overflowMethod || "cap";
|
|
3566
|
+
|
|
3567
|
+
this.x = 0;
|
|
3568
|
+
this.y = 0;
|
|
3569
|
+
this.padding = 5;
|
|
3570
|
+
this.hidden = false;
|
|
3571
|
+
|
|
3572
|
+
// create the frame
|
|
3573
|
+
this.frame = document.createElement("div");
|
|
3574
|
+
this.frame.className = "vis-tooltip";
|
|
3575
|
+
this.container.appendChild(this.frame);
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3578
|
+
/**
|
|
3579
|
+
* @param {number} x Horizontal position of the popup window
|
|
3580
|
+
* @param {number} y Vertical position of the popup window
|
|
3581
|
+
*/
|
|
3582
|
+
setPosition(x, y) {
|
|
3583
|
+
this.x = parseInt(x);
|
|
3584
|
+
this.y = parseInt(y);
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3587
|
+
/**
|
|
3588
|
+
* Set the content for the popup window. This can be HTML code or text.
|
|
3589
|
+
*
|
|
3590
|
+
* @param {string | Element} content
|
|
3591
|
+
*/
|
|
3592
|
+
setText(content) {
|
|
3593
|
+
if (content instanceof Element) {
|
|
3594
|
+
while (this.frame.firstChild) {
|
|
3595
|
+
this.frame.removeChild(this.frame.firstChild);
|
|
3596
|
+
}
|
|
3597
|
+
this.frame.appendChild(content);
|
|
3598
|
+
} else {
|
|
3599
|
+
// String containing literal text, element has to be used for HTML due to
|
|
3600
|
+
// XSS risks associated with innerHTML (i.e. prevent XSS by accident).
|
|
3601
|
+
this.frame.innerText = content;
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
/**
|
|
3606
|
+
* Show the popup window
|
|
3607
|
+
*
|
|
3608
|
+
* @param {boolean} [doShow] Show or hide the window
|
|
3609
|
+
*/
|
|
3610
|
+
show(doShow) {
|
|
3611
|
+
if (doShow === undefined) {
|
|
3612
|
+
doShow = true;
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
if (doShow === true) {
|
|
3616
|
+
const height = this.frame.clientHeight;
|
|
3617
|
+
const width = this.frame.clientWidth;
|
|
3618
|
+
const maxHeight = this.frame.parentNode.clientHeight;
|
|
3619
|
+
const maxWidth = this.frame.parentNode.clientWidth;
|
|
3620
|
+
|
|
3621
|
+
let left = 0,
|
|
3622
|
+
top = 0;
|
|
3623
|
+
|
|
3624
|
+
if (this.overflowMethod == "flip") {
|
|
3625
|
+
let isLeft = false,
|
|
3626
|
+
isTop = true; // Where around the position it's located
|
|
3627
|
+
|
|
3628
|
+
if (this.y - height < this.padding) {
|
|
3629
|
+
isTop = false;
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
if (this.x + width > maxWidth - this.padding) {
|
|
3633
|
+
isLeft = true;
|
|
3634
|
+
}
|
|
3635
|
+
|
|
3636
|
+
if (isLeft) {
|
|
3637
|
+
left = this.x - width;
|
|
3638
|
+
} else {
|
|
3639
|
+
left = this.x;
|
|
3640
|
+
}
|
|
3641
|
+
|
|
3642
|
+
if (isTop) {
|
|
3643
|
+
top = this.y - height;
|
|
3644
|
+
} else {
|
|
3645
|
+
top = this.y;
|
|
3646
|
+
}
|
|
3647
|
+
} else {
|
|
3648
|
+
top = this.y - height;
|
|
3649
|
+
if (top + height + this.padding > maxHeight) {
|
|
3650
|
+
top = maxHeight - height - this.padding;
|
|
3651
|
+
}
|
|
3652
|
+
if (top < this.padding) {
|
|
3653
|
+
top = this.padding;
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
left = this.x;
|
|
3657
|
+
if (left + width + this.padding > maxWidth) {
|
|
3658
|
+
left = maxWidth - width - this.padding;
|
|
3659
|
+
}
|
|
3660
|
+
if (left < this.padding) {
|
|
3661
|
+
left = this.padding;
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
|
|
3665
|
+
this.frame.style.left = left + "px";
|
|
3666
|
+
this.frame.style.top = top + "px";
|
|
3667
|
+
this.frame.style.visibility = "visible";
|
|
3668
|
+
this.hidden = false;
|
|
3669
|
+
} else {
|
|
3670
|
+
this.hide();
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
|
|
3674
|
+
/**
|
|
3675
|
+
* Hide the popup window
|
|
3676
|
+
*/
|
|
3677
|
+
hide() {
|
|
3678
|
+
this.hidden = true;
|
|
3679
|
+
this.frame.style.left = "0";
|
|
3680
|
+
this.frame.style.top = "0";
|
|
3681
|
+
this.frame.style.visibility = "hidden";
|
|
3682
|
+
}
|
|
3683
|
+
|
|
3684
|
+
/**
|
|
3685
|
+
* Remove the popup window
|
|
3686
|
+
*/
|
|
3687
|
+
destroy() {
|
|
3688
|
+
this.frame.parentNode.removeChild(this.frame); // Remove element from DOM
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
let errorFound = false;
|
|
3693
|
+
let allOptions;
|
|
3694
|
+
|
|
3695
|
+
const VALIDATOR_PRINT_STYLE$1 = "background: #FFeeee; color: #dd0000";
|
|
3696
|
+
|
|
3697
|
+
/**
|
|
3698
|
+
* Used to validate options.
|
|
3699
|
+
*/
|
|
3700
|
+
class Validator$1 {
|
|
3701
|
+
/**
|
|
3702
|
+
* Main function to be called
|
|
3703
|
+
*
|
|
3704
|
+
* @param {object} options
|
|
3705
|
+
* @param {object} referenceOptions
|
|
3706
|
+
* @param {object} subObject
|
|
3707
|
+
* @returns {boolean}
|
|
3708
|
+
* @static
|
|
3709
|
+
*/
|
|
3710
|
+
static validate(options, referenceOptions, subObject) {
|
|
3711
|
+
errorFound = false;
|
|
3712
|
+
allOptions = referenceOptions;
|
|
3713
|
+
let usedOptions = referenceOptions;
|
|
3714
|
+
if (subObject !== undefined) {
|
|
3715
|
+
usedOptions = referenceOptions[subObject];
|
|
3716
|
+
}
|
|
3717
|
+
Validator$1.parse(options, usedOptions, []);
|
|
3718
|
+
return errorFound;
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
/**
|
|
3722
|
+
* Will traverse an object recursively and check every value
|
|
3723
|
+
*
|
|
3724
|
+
* @param {object} options
|
|
3725
|
+
* @param {object} referenceOptions
|
|
3726
|
+
* @param {Array} path | where to look for the actual option
|
|
3727
|
+
* @static
|
|
3728
|
+
*/
|
|
3729
|
+
static parse(options, referenceOptions, path) {
|
|
3730
|
+
for (const option in options) {
|
|
3731
|
+
if (Object.prototype.hasOwnProperty.call(options, option)) {
|
|
3732
|
+
Validator$1.check(option, options, referenceOptions, path);
|
|
3733
|
+
}
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
/**
|
|
3738
|
+
* Check every value. If the value is an object, call the parse function on that object.
|
|
3739
|
+
*
|
|
3740
|
+
* @param {string} option
|
|
3741
|
+
* @param {object} options
|
|
3742
|
+
* @param {object} referenceOptions
|
|
3743
|
+
* @param {Array} path | where to look for the actual option
|
|
3744
|
+
* @static
|
|
3745
|
+
*/
|
|
3746
|
+
static check(option, options, referenceOptions, path) {
|
|
3747
|
+
if (
|
|
3748
|
+
referenceOptions[option] === undefined &&
|
|
3749
|
+
referenceOptions.__any__ === undefined
|
|
3750
|
+
) {
|
|
3751
|
+
Validator$1.getSuggestion(option, referenceOptions, path);
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3755
|
+
let referenceOption = option;
|
|
3756
|
+
let is_object = true;
|
|
3757
|
+
|
|
3758
|
+
if (
|
|
3759
|
+
referenceOptions[option] === undefined &&
|
|
3760
|
+
referenceOptions.__any__ !== undefined
|
|
3761
|
+
) {
|
|
3762
|
+
// NOTE: This only triggers if the __any__ is in the top level of the options object.
|
|
3763
|
+
// THAT'S A REALLY BAD PLACE TO ALLOW IT!!!!
|
|
3764
|
+
// TODO: Examine if needed, remove if possible
|
|
3765
|
+
|
|
3766
|
+
// __any__ is a wildcard. Any value is accepted and will be further analysed by reference.
|
|
3767
|
+
referenceOption = "__any__";
|
|
3768
|
+
|
|
3769
|
+
// if the any-subgroup is not a predefined object in the configurator,
|
|
3770
|
+
// we do not look deeper into the object.
|
|
3771
|
+
is_object = Validator$1.getType(options[option]) === "object";
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
let refOptionObj = referenceOptions[referenceOption];
|
|
3775
|
+
if (is_object && refOptionObj.__type__ !== undefined) {
|
|
3776
|
+
refOptionObj = refOptionObj.__type__;
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
Validator$1.checkFields(
|
|
3780
|
+
option,
|
|
3781
|
+
options,
|
|
3782
|
+
referenceOptions,
|
|
3783
|
+
referenceOption,
|
|
3784
|
+
refOptionObj,
|
|
3785
|
+
path
|
|
3786
|
+
);
|
|
3787
|
+
}
|
|
3788
|
+
|
|
3789
|
+
/**
|
|
3790
|
+
*
|
|
3791
|
+
* @param {string} option | the option property
|
|
3792
|
+
* @param {object} options | The supplied options object
|
|
3793
|
+
* @param {object} referenceOptions | The reference options containing all options and their allowed formats
|
|
3794
|
+
* @param {string} referenceOption | Usually this is the same as option, except when handling an __any__ tag.
|
|
3795
|
+
* @param {string} refOptionObj | This is the type object from the reference options
|
|
3796
|
+
* @param {Array} path | where in the object is the option
|
|
3797
|
+
* @static
|
|
3798
|
+
*/
|
|
3799
|
+
static checkFields(
|
|
3800
|
+
option,
|
|
3801
|
+
options,
|
|
3802
|
+
referenceOptions,
|
|
3803
|
+
referenceOption,
|
|
3804
|
+
refOptionObj,
|
|
3805
|
+
path
|
|
3806
|
+
) {
|
|
3807
|
+
const log = function (message) {
|
|
3808
|
+
console.error(
|
|
3809
|
+
"%c" + message + Validator$1.printLocation(path, option),
|
|
3810
|
+
VALIDATOR_PRINT_STYLE$1
|
|
3811
|
+
);
|
|
3812
|
+
};
|
|
3813
|
+
|
|
3814
|
+
const optionType = Validator$1.getType(options[option]);
|
|
3815
|
+
const refOptionType = refOptionObj[optionType];
|
|
3816
|
+
|
|
3817
|
+
if (refOptionType !== undefined) {
|
|
3818
|
+
// if the type is correct, we check if it is supposed to be one of a few select values
|
|
3819
|
+
if (
|
|
3820
|
+
Validator$1.getType(refOptionType) === "array" &&
|
|
3821
|
+
refOptionType.indexOf(options[option]) === -1
|
|
3822
|
+
) {
|
|
3823
|
+
log(
|
|
3824
|
+
'Invalid option detected in "' +
|
|
3825
|
+
option +
|
|
3826
|
+
'".' +
|
|
3827
|
+
" Allowed values are:" +
|
|
3828
|
+
Validator$1.print(refOptionType) +
|
|
3829
|
+
' not "' +
|
|
3830
|
+
options[option] +
|
|
3831
|
+
'". '
|
|
3832
|
+
);
|
|
3833
|
+
errorFound = true;
|
|
3834
|
+
} else if (optionType === "object" && referenceOption !== "__any__") {
|
|
3835
|
+
path = copyAndExtendArray(path, option);
|
|
3836
|
+
Validator$1.parse(
|
|
3837
|
+
options[option],
|
|
3838
|
+
referenceOptions[referenceOption],
|
|
3839
|
+
path
|
|
3840
|
+
);
|
|
3841
|
+
}
|
|
3842
|
+
} else if (refOptionObj["any"] === undefined) {
|
|
3843
|
+
// type of the field is incorrect and the field cannot be any
|
|
3844
|
+
log(
|
|
3845
|
+
'Invalid type received for "' +
|
|
3846
|
+
option +
|
|
3847
|
+
'". Expected: ' +
|
|
3848
|
+
Validator$1.print(Object.keys(refOptionObj)) +
|
|
3849
|
+
". Received [" +
|
|
3850
|
+
optionType +
|
|
3851
|
+
'] "' +
|
|
3852
|
+
options[option] +
|
|
3853
|
+
'"'
|
|
3854
|
+
);
|
|
3855
|
+
errorFound = true;
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
/**
|
|
3860
|
+
*
|
|
3861
|
+
* @param {object | boolean | number | string | Array.<number> | Date | Node | Moment | undefined | null} object
|
|
3862
|
+
* @returns {string}
|
|
3863
|
+
* @static
|
|
3864
|
+
*/
|
|
3865
|
+
static getType(object) {
|
|
3866
|
+
const type = typeof object;
|
|
3867
|
+
|
|
3868
|
+
if (type === "object") {
|
|
3869
|
+
if (object === null) {
|
|
3870
|
+
return "null";
|
|
3871
|
+
}
|
|
3872
|
+
if (object instanceof Boolean) {
|
|
3873
|
+
return "boolean";
|
|
3874
|
+
}
|
|
3875
|
+
if (object instanceof Number) {
|
|
3876
|
+
return "number";
|
|
3877
|
+
}
|
|
3878
|
+
if (object instanceof String) {
|
|
3879
|
+
return "string";
|
|
3880
|
+
}
|
|
3881
|
+
if (Array.isArray(object)) {
|
|
3882
|
+
return "array";
|
|
3883
|
+
}
|
|
3884
|
+
if (object instanceof Date) {
|
|
3885
|
+
return "date";
|
|
3886
|
+
}
|
|
3887
|
+
if (object.nodeType !== undefined) {
|
|
3888
|
+
return "dom";
|
|
3889
|
+
}
|
|
3890
|
+
if (object._isAMomentObject === true) {
|
|
3891
|
+
return "moment";
|
|
3892
|
+
}
|
|
3893
|
+
return "object";
|
|
3894
|
+
} else if (type === "number") {
|
|
3895
|
+
return "number";
|
|
3896
|
+
} else if (type === "boolean") {
|
|
3897
|
+
return "boolean";
|
|
3898
|
+
} else if (type === "string") {
|
|
3899
|
+
return "string";
|
|
3900
|
+
} else if (type === undefined) {
|
|
3901
|
+
return "undefined";
|
|
3902
|
+
}
|
|
3903
|
+
return type;
|
|
3904
|
+
}
|
|
3905
|
+
|
|
3906
|
+
/**
|
|
3907
|
+
* @param {string} option
|
|
3908
|
+
* @param {object} options
|
|
3909
|
+
* @param {Array.<string>} path
|
|
3910
|
+
* @static
|
|
3911
|
+
*/
|
|
3912
|
+
static getSuggestion(option, options, path) {
|
|
3913
|
+
const localSearch = Validator$1.findInOptions(option, options, path, false);
|
|
3914
|
+
const globalSearch = Validator$1.findInOptions(option, allOptions, [], true);
|
|
3915
|
+
|
|
3916
|
+
const localSearchThreshold = 8;
|
|
3917
|
+
const globalSearchThreshold = 4;
|
|
3918
|
+
|
|
3919
|
+
let msg;
|
|
3920
|
+
if (localSearch.indexMatch !== undefined) {
|
|
3921
|
+
msg =
|
|
3922
|
+
" in " +
|
|
3923
|
+
Validator$1.printLocation(localSearch.path, option, "") +
|
|
3924
|
+
'Perhaps it was incomplete? Did you mean: "' +
|
|
3925
|
+
localSearch.indexMatch +
|
|
3926
|
+
'"?\n\n';
|
|
3927
|
+
} else if (
|
|
3928
|
+
globalSearch.distance <= globalSearchThreshold &&
|
|
3929
|
+
localSearch.distance > globalSearch.distance
|
|
3930
|
+
) {
|
|
3931
|
+
msg =
|
|
3932
|
+
" in " +
|
|
3933
|
+
Validator$1.printLocation(localSearch.path, option, "") +
|
|
3934
|
+
"Perhaps it was misplaced? Matching option found at: " +
|
|
3935
|
+
Validator$1.printLocation(
|
|
3936
|
+
globalSearch.path,
|
|
3937
|
+
globalSearch.closestMatch,
|
|
3938
|
+
""
|
|
3939
|
+
);
|
|
3940
|
+
} else if (localSearch.distance <= localSearchThreshold) {
|
|
3941
|
+
msg =
|
|
3942
|
+
'. Did you mean "' +
|
|
3943
|
+
localSearch.closestMatch +
|
|
3944
|
+
'"?' +
|
|
3945
|
+
Validator$1.printLocation(localSearch.path, option);
|
|
3946
|
+
} else {
|
|
3947
|
+
msg =
|
|
3948
|
+
". Did you mean one of these: " +
|
|
3949
|
+
Validator$1.print(Object.keys(options)) +
|
|
3950
|
+
Validator$1.printLocation(path, option);
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3953
|
+
console.error(
|
|
3954
|
+
'%cUnknown option detected: "' + option + '"' + msg,
|
|
3955
|
+
VALIDATOR_PRINT_STYLE$1
|
|
3956
|
+
);
|
|
3957
|
+
errorFound = true;
|
|
3958
|
+
}
|
|
3959
|
+
|
|
3960
|
+
/**
|
|
3961
|
+
* traverse the options in search for a match.
|
|
3962
|
+
*
|
|
3963
|
+
* @param {string} option
|
|
3964
|
+
* @param {object} options
|
|
3965
|
+
* @param {Array} path | where to look for the actual option
|
|
3966
|
+
* @param {boolean} [recursive=false]
|
|
3967
|
+
* @returns {{closestMatch: string, path: Array, distance: number}}
|
|
3968
|
+
* @static
|
|
3969
|
+
*/
|
|
3970
|
+
static findInOptions(option, options, path, recursive = false) {
|
|
3971
|
+
let min = 1e9;
|
|
3972
|
+
let closestMatch = "";
|
|
3973
|
+
let closestMatchPath = [];
|
|
3974
|
+
const lowerCaseOption = option.toLowerCase();
|
|
3975
|
+
let indexMatch = undefined;
|
|
3976
|
+
for (const op in options) {
|
|
3977
|
+
let distance;
|
|
3978
|
+
if (options[op].__type__ !== undefined && recursive === true) {
|
|
3979
|
+
const result = Validator$1.findInOptions(
|
|
3980
|
+
option,
|
|
3981
|
+
options[op],
|
|
3982
|
+
copyAndExtendArray(path, op)
|
|
3983
|
+
);
|
|
3984
|
+
if (min > result.distance) {
|
|
3985
|
+
closestMatch = result.closestMatch;
|
|
3986
|
+
closestMatchPath = result.path;
|
|
3987
|
+
min = result.distance;
|
|
3988
|
+
indexMatch = result.indexMatch;
|
|
3989
|
+
}
|
|
3990
|
+
} else {
|
|
3991
|
+
if (op.toLowerCase().indexOf(lowerCaseOption) !== -1) {
|
|
3992
|
+
indexMatch = op;
|
|
3993
|
+
}
|
|
3994
|
+
distance = Validator$1.levenshteinDistance(option, op);
|
|
3995
|
+
if (min > distance) {
|
|
3996
|
+
closestMatch = op;
|
|
3997
|
+
closestMatchPath = copyArray(path);
|
|
3998
|
+
min = distance;
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
return {
|
|
4003
|
+
closestMatch: closestMatch,
|
|
4004
|
+
path: closestMatchPath,
|
|
4005
|
+
distance: min,
|
|
4006
|
+
indexMatch: indexMatch,
|
|
4007
|
+
};
|
|
4008
|
+
}
|
|
4009
|
+
|
|
4010
|
+
/**
|
|
4011
|
+
* @param {Array.<string>} path
|
|
4012
|
+
* @param {object} option
|
|
4013
|
+
* @param {string} prefix
|
|
4014
|
+
* @returns {string}
|
|
4015
|
+
* @static
|
|
4016
|
+
*/
|
|
4017
|
+
static printLocation(path, option, prefix = "Problem value found at: \n") {
|
|
4018
|
+
let str = "\n\n" + prefix + "options = {\n";
|
|
4019
|
+
for (let i = 0; i < path.length; i++) {
|
|
4020
|
+
for (let j = 0; j < i + 1; j++) {
|
|
4021
|
+
str += " ";
|
|
4022
|
+
}
|
|
4023
|
+
str += path[i] + ": {\n";
|
|
4024
|
+
}
|
|
4025
|
+
for (let j = 0; j < path.length + 1; j++) {
|
|
4026
|
+
str += " ";
|
|
4027
|
+
}
|
|
4028
|
+
str += option + "\n";
|
|
4029
|
+
for (let i = 0; i < path.length + 1; i++) {
|
|
4030
|
+
for (let j = 0; j < path.length - i; j++) {
|
|
4031
|
+
str += " ";
|
|
4032
|
+
}
|
|
4033
|
+
str += "}\n";
|
|
4034
|
+
}
|
|
4035
|
+
return str + "\n\n";
|
|
4036
|
+
}
|
|
4037
|
+
|
|
4038
|
+
/**
|
|
4039
|
+
* @param {object} options
|
|
4040
|
+
* @returns {string}
|
|
4041
|
+
* @static
|
|
4042
|
+
*/
|
|
4043
|
+
static print(options) {
|
|
4044
|
+
return JSON.stringify(options)
|
|
4045
|
+
.replace(/(")|(\[)|(\])|(,"__type__")/g, "")
|
|
4046
|
+
.replace(/(,)/g, ", ");
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4049
|
+
/**
|
|
4050
|
+
* Compute the edit distance between the two given strings
|
|
4051
|
+
* http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript
|
|
4052
|
+
*
|
|
4053
|
+
* Copyright (c) 2011 Andrei Mackenzie
|
|
4054
|
+
*
|
|
4055
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4056
|
+
*
|
|
4057
|
+
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
4058
|
+
*
|
|
4059
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
4060
|
+
*
|
|
4061
|
+
* @param {string} a
|
|
4062
|
+
* @param {string} b
|
|
4063
|
+
* @returns {Array.<Array.<number>>}}
|
|
4064
|
+
* @static
|
|
4065
|
+
*/
|
|
4066
|
+
static levenshteinDistance(a, b) {
|
|
4067
|
+
if (a.length === 0) return b.length;
|
|
4068
|
+
if (b.length === 0) return a.length;
|
|
4069
|
+
|
|
4070
|
+
const matrix = [];
|
|
4071
|
+
|
|
4072
|
+
// increment along the first column of each row
|
|
4073
|
+
let i;
|
|
4074
|
+
for (i = 0; i <= b.length; i++) {
|
|
4075
|
+
matrix[i] = [i];
|
|
4076
|
+
}
|
|
4077
|
+
|
|
4078
|
+
// increment each column in the first row
|
|
4079
|
+
let j;
|
|
4080
|
+
for (j = 0; j <= a.length; j++) {
|
|
4081
|
+
matrix[0][j] = j;
|
|
4082
|
+
}
|
|
4083
|
+
|
|
4084
|
+
// Fill in the rest of the matrix
|
|
4085
|
+
for (i = 1; i <= b.length; i++) {
|
|
4086
|
+
for (j = 1; j <= a.length; j++) {
|
|
4087
|
+
if (b.charAt(i - 1) == a.charAt(j - 1)) {
|
|
4088
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
4089
|
+
} else {
|
|
4090
|
+
matrix[i][j] = Math.min(
|
|
4091
|
+
matrix[i - 1][j - 1] + 1, // substitution
|
|
4092
|
+
Math.min(
|
|
4093
|
+
matrix[i][j - 1] + 1, // insertion
|
|
4094
|
+
matrix[i - 1][j] + 1
|
|
4095
|
+
)
|
|
4096
|
+
); // deletion
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
|
|
4101
|
+
return matrix[b.length][a.length];
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4105
|
+
const Activator = Activator$1;
|
|
4106
|
+
const ColorPicker = ColorPicker$1;
|
|
4107
|
+
const Configurator = Configurator$1;
|
|
4108
|
+
const Hammer = Hammer$1;
|
|
4109
|
+
const Popup = Popup$1;
|
|
4110
|
+
const VALIDATOR_PRINT_STYLE = VALIDATOR_PRINT_STYLE$1;
|
|
4111
|
+
const Validator = Validator$1;
|
|
4112
|
+
|
|
4113
|
+
exports.Activator = Activator;
|
|
1833
4114
|
exports.Alea = Alea;
|
|
4115
|
+
exports.ColorPicker = ColorPicker;
|
|
4116
|
+
exports.Configurator = Configurator;
|
|
1834
4117
|
exports.DELETE = DELETE;
|
|
1835
4118
|
exports.HSVToHex = HSVToHex;
|
|
1836
4119
|
exports.HSVToRGB = HSVToRGB;
|
|
4120
|
+
exports.Hammer = Hammer;
|
|
4121
|
+
exports.Popup = Popup;
|
|
1837
4122
|
exports.RGBToHSV = RGBToHSV;
|
|
1838
4123
|
exports.RGBToHex = RGBToHex;
|
|
4124
|
+
exports.VALIDATOR_PRINT_STYLE = VALIDATOR_PRINT_STYLE;
|
|
4125
|
+
exports.Validator = Validator;
|
|
1839
4126
|
exports.addClassName = addClassName;
|
|
1840
4127
|
exports.addCssText = addCssText;
|
|
1841
4128
|
exports.addEventListener = addEventListener;
|
|
@@ -1889,5 +4176,5 @@
|
|
|
1889
4176
|
|
|
1890
4177
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
1891
4178
|
|
|
1892
|
-
}))
|
|
4179
|
+
}));
|
|
1893
4180
|
//# sourceMappingURL=vis-util.js.map
|