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