@@ -116,20 +116,36 @@ function npGet(cont, parts) {
116
116
117
117
/*
118
118
* Can this value be deleted? We can delete any empty object (null, undefined, [], {})
119
- * EXCEPT empty data arrays. Info arrays can be safely deleted, but not deleting them
120
- * has no ill effects other than leaving a trace or layout object with some cruft in it.
121
- * But deleting data arrays can change the meaning of the object, as `[]` means there is
119
+ * EXCEPT empty data arrays, {} inside an array, or anything INSIDE an *args* array.
120
+ *
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.
123
+ *
124
+ * Deleting data arrays can change the meaning of the object, as `[]` means there is
122
125
* data for this attribute, it's just empty right now while `undefined` means the data
123
- * should be filled in with defaults to match other data arrays. So we do some simple
124
- * tests here to find known non-data arrays but don't worry too much about not deleting
125
- * some arrays that would actually be safe to delete.
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.
126
137
*/
127
- var INFO_PATTERNS = / ( ^ | .) ( ( d o m a i n | r a n g e ) ( \. [ x y ] ) ? | a r g s | p a r a l l e l s ) $ / ;
138
+ var INFO_PATTERNS = / ( ^ | \. ) ( ( d o m a i n | r a n g e ) ( \. [ x y ] ) ? | a r g s | p a r a l l e l s ) $ / ;
139
+ var ARGS_PATTERN = / ( ^ | \. ) a r g s \[ / ;
128
140
function isDeletable ( val , propStr ) {
129
- if ( ! emptyObj ( val ) ) return false ;
141
+ if ( ! emptyObj ( val ) ||
142
+ ( isPlainObject ( val ) && propStr . charAt ( propStr . length - 1 ) === ']' ) ||
143
+ ( propStr . match ( ARGS_PATTERN ) && val !== undefined )
144
+ ) {
145
+ return false ;
146
+ }
130
147
if ( ! isArray ( val ) ) return true ;
131
148
132
- // domain and range are special - they show up in lots of places so hard code here.
133
149
if ( propStr . match ( INFO_PATTERNS ) ) return true ;
134
150
135
151
var match = containerArrayMatch ( propStr ) ;
@@ -186,8 +202,11 @@ function npSet(cont, parts, propStr) {
186
202
}
187
203
188
204
function joinPropStr ( propStr , newPart ) {
189
- if ( ! propStr ) return newPart ;
190
- return propStr + isNumeric ( newPart ) ? ( '[' + newPart + ']' ) : ( '.' + newPart ) ;
205
+ var toAdd = newPart ;
206
+ if ( isNumeric ( newPart ) ) toAdd = '[' + newPart + ']' ;
207
+ else if ( propStr ) toAdd = '.' + newPart ;
208
+
209
+ return propStr + toAdd ;
191
210
}
192
211
193
212
// handle special -1 array index
@@ -244,11 +263,7 @@ function pruneContainers(containerLevels) {
244
263
remainingKeys = false ;
245
264
if ( isArray ( curCont ) ) {
246
265
for ( j = curCont . length - 1 ; j >= 0 ; j -- ) {
247
- // If there's a plain object in an array, it's a container array
248
- // so we don't delete empty containers because they still have meaning.
249
- // `editContainerArray` handles the API for adding/removing objects
250
- // in this case.
251
- if ( emptyObj ( curCont [ j ] ) && ! isPlainObject ( curCont [ j ] ) ) {
266
+ if ( isDeletable ( curCont [ j ] , joinPropStr ( propPart , j ) ) ) {
252
267
if ( remainingKeys ) curCont [ j ] = undefined ;
253
268
else curCont . pop ( ) ;
254
269
}
0 commit comments