|
11 | 11 |
|
12 | 12 | var isNumeric = require('fast-isnumeric');
|
13 | 13 | var isArrayOrTypedArray = require('./is_array').isArrayOrTypedArray;
|
14 |
| -var isPlainObject = require('./is_plain_object'); |
15 |
| -var containerArrayMatch = require('../plot_api/container_array_match'); |
16 | 14 |
|
17 | 15 | /**
|
18 | 16 | * convert a string s (such as 'xaxis.range[0]')
|
@@ -115,44 +113,21 @@ function npGet(cont, parts) {
|
115 | 113 | }
|
116 | 114 |
|
117 | 115 | /*
|
118 |
| - * Can this value be deleted? We can delete any empty object (null, undefined, [], {}) |
119 |
| - * EXCEPT empty data arrays, {} inside an array, or anything INSIDE an *args* array. |
| 116 | + * Can this value be deleted? We can delete `undefined`, and `null` except INSIDE an |
| 117 | + * *args* array. |
120 | 118 | *
|
121 |
| - * Info arrays can be safely deleted, but not deleting them has no ill effects other |
122 |
| - * than leaving a trace or layout object with some cruft in it. |
| 119 | + * Previously we also deleted some `{}` and `[]`, in order to try and make set/unset |
| 120 | + * a net noop; but this causes far more complication than it's worth, and still had |
| 121 | + * lots of exceptions. See https://github.com/plotly/plotly.js/issues/1410 |
123 | 122 | *
|
124 |
| - * Deleting data arrays can change the meaning of the object, as `[]` means there is |
125 |
| - * data for this attribute, it's just empty right now while `undefined` means the data |
126 |
| - * should be filled in with defaults to match other data arrays. |
127 |
| - * |
128 |
| - * `{}` inside an array means "the default object" which is clearly different from |
129 |
| - * popping it off the end of the array, or setting it `undefined` inside the array. |
130 |
| - * |
131 |
| - * *args* arrays get passed directly to API methods and we should respect precisely |
132 |
| - * what the user has put there - although if the whole *args* array is empty it's fine |
133 |
| - * to delete that. |
134 |
| - * |
135 |
| - * So we do some simple tests here to find known non-data arrays but don't worry too |
136 |
| - * much about not deleting some arrays that would actually be safe to delete. |
| 123 | + * *args* arrays get passed directly to API methods and we should respect null if |
| 124 | + * the user put it there, but otherwise null is deleted as we use it as code |
| 125 | + * in restyle/relayout/update for "delete this value" whereas undefined means |
| 126 | + * "ignore this edit" |
137 | 127 | */
|
138 |
| -var INFO_PATTERNS = /(^|\.)((domain|range)(\.[xy])?|args|parallels)$/; |
139 | 128 | var ARGS_PATTERN = /(^|\.)args\[/;
|
140 | 129 | function isDeletable(val, propStr) {
|
141 |
| - if(!emptyObj(val) || |
142 |
| - (isPlainObject(val) && propStr.charAt(propStr.length - 1) === ']') || |
143 |
| - (propStr.match(ARGS_PATTERN) && val !== undefined) |
144 |
| - ) { |
145 |
| - return false; |
146 |
| - } |
147 |
| - if(!isArrayOrTypedArray(val)) return true; |
148 |
| - |
149 |
| - if(propStr.match(INFO_PATTERNS)) return true; |
150 |
| - |
151 |
| - var match = containerArrayMatch(propStr); |
152 |
| - // if propStr matches the container array itself, index is an empty string |
153 |
| - // otherwise we've matched something inside the container array, which may |
154 |
| - // still be a data array. |
155 |
| - return match && (match.index === ''); |
| 130 | + return (val === undefined) || (val === null && !propStr.match(ARGS_PATTERN)); |
156 | 131 | }
|
157 | 132 |
|
158 | 133 | function npSet(cont, parts, propStr) {
|
@@ -194,8 +169,18 @@ function npSet(cont, parts, propStr) {
|
194 | 169 | }
|
195 | 170 |
|
196 | 171 | if(toDelete) {
|
197 |
| - if(i === parts.length - 1) delete curCont[parts[i]]; |
198 |
| - pruneContainers(containerLevels); |
| 172 | + if(i === parts.length - 1) { |
| 173 | + delete curCont[parts[i]]; |
| 174 | + |
| 175 | + // The one bit of pruning we still do: drop `undefined` from the end of arrays. |
| 176 | + // In case someone has already unset previous items, continue until we hit a |
| 177 | + // non-undefined value. |
| 178 | + if(Array.isArray(curCont) && +parts[i] === curCont.length - 1) { |
| 179 | + while(curCont.length && curCont[curCont.length - 1] === undefined) { |
| 180 | + curCont.pop(); |
| 181 | + } |
| 182 | + } |
| 183 | + } |
199 | 184 | }
|
200 | 185 | else curCont[parts[i]] = val;
|
201 | 186 | };
|
@@ -249,48 +234,6 @@ function checkNewContainer(container, part, nextPart, toDelete) {
|
249 | 234 | return true;
|
250 | 235 | }
|
251 | 236 |
|
252 |
| -function pruneContainers(containerLevels) { |
253 |
| - var i, |
254 |
| - j, |
255 |
| - curCont, |
256 |
| - propPart, |
257 |
| - keys, |
258 |
| - remainingKeys; |
259 |
| - for(i = containerLevels.length - 1; i >= 0; i--) { |
260 |
| - curCont = containerLevels[i][0]; |
261 |
| - propPart = containerLevels[i][1]; |
262 |
| - |
263 |
| - remainingKeys = false; |
264 |
| - if(isArrayOrTypedArray(curCont)) { |
265 |
| - for(j = curCont.length - 1; j >= 0; j--) { |
266 |
| - if(isDeletable(curCont[j], joinPropStr(propPart, j))) { |
267 |
| - if(remainingKeys) curCont[j] = undefined; |
268 |
| - else curCont.pop(); |
269 |
| - } |
270 |
| - else remainingKeys = true; |
271 |
| - } |
272 |
| - } |
273 |
| - else if(typeof curCont === 'object' && curCont !== null) { |
274 |
| - keys = Object.keys(curCont); |
275 |
| - remainingKeys = false; |
276 |
| - for(j = keys.length - 1; j >= 0; j--) { |
277 |
| - if(isDeletable(curCont[keys[j]], joinPropStr(propPart, keys[j]))) { |
278 |
| - delete curCont[keys[j]]; |
279 |
| - } |
280 |
| - else remainingKeys = true; |
281 |
| - } |
282 |
| - } |
283 |
| - if(remainingKeys) return; |
284 |
| - } |
285 |
| -} |
286 |
| - |
287 |
| -function emptyObj(obj) { |
288 |
| - if(obj === undefined || obj === null) return true; |
289 |
| - if(typeof obj !== 'object') return false; // any plain value |
290 |
| - if(isArrayOrTypedArray(obj)) return !obj.length; // [] |
291 |
| - return !Object.keys(obj).length; // {} |
292 |
| -} |
293 |
| - |
294 | 237 | function badContainer(container, propStr, propParts) {
|
295 | 238 | return {
|
296 | 239 | set: function() { throw 'bad container'; },
|
|
0 commit comments