-
-
Notifications
You must be signed in to change notification settings - Fork 102
/
multiValues.js
85 lines (77 loc) · 2.8 KB
/
multiValues.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import {MULTI_VALUED} from './constants';
import {isPlainObject} from '../lib';
/**
* Deep-copies the value using JSON. Underscored (private) keys are removed.
* @param {*} value Some nested value from the plotDiv object.
* @returns {*} A deepcopy of the value.
*/
function deepCopyPublic(value) {
if (typeof value === 'undefined') {
return value;
}
const skipPrivateKeys = (key, value) => (key.startsWith('_') ? 0 : value);
return window.JSON.parse(window.JSON.stringify(value, skipPrivateKeys));
}
/*
* WARNING: When using this function, make intoObj and fromObject are copies of
* your objects, so that no mutations occur
*/
function setMultiValuedContainer(intoObj, fromObj, key, config = {}) {
const intoVal = intoObj[key];
const fromVal = fromObj[key];
// don't merge private attrs
if (
(typeof key === 'string' && key.charAt(0) === '_' && key !== '_group') ||
typeof intoVal === 'function' ||
key === 'module'
) {
return;
}
// already a mixture of values, can't get any worse
if (intoVal === MULTI_VALUED) {
return;
} else if (intoVal === void 0) {
// if the original doesn't have the key it's because that key
// doesn't do anything there - so use the new value
// note that if fromObj doesn't have a key in intoObj we will not
// attempt to merge them at all, so this behavior makes the merge
// independent of order.
// WARNING: Careful that data copies were passed in as args here, as mutation can occur
intoObj[key] = fromVal;
} else if (key === 'colorscale') {
// colorscales are arrays... need to stringify before comparing
// (other vals we don't want to stringify, as differences could
// potentially be real, like 'false' and false)
if (String(intoVal) !== String(fromVal)) {
intoObj[key] = MULTI_VALUED;
}
} else if (Array.isArray(intoVal)) {
// in data, other arrays are data, which we don't care about
// for styling purposes
if (!config.searchArrays) {
return;
}
if (!Array.isArray(fromVal)) {
intoObj[key] = MULTI_VALUED;
} else {
// in layout though, we need to recurse into arrays
for (let i = 0; i < fromVal.length; i++) {
setMultiValuedContainer(intoVal, fromVal, i, config);
}
}
} else if (isPlainObject(fromVal)) {
// recurse into objects
if (!isPlainObject(intoVal)) {
throw new Error('tried to merge object into non-object: ' + key);
}
Object.keys(fromVal).forEach(function(key2) {
setMultiValuedContainer(intoVal, fromVal, key2, config);
});
} else if (isPlainObject(intoVal)) {
throw new Error('tried to merge non-object into object: ' + key);
} else if (intoVal !== fromVal) {
// different non-empty values -
intoObj[key] = MULTI_VALUED;
}
}
export {deepCopyPublic, setMultiValuedContainer};