@@ -1299,7 +1299,7 @@ plots.modifyFrames = function(gd, operations) {
12991299 */
13001300plots . computeFrame = function ( gd , frameName ) {
13011301 var frameLookup = gd . _transitionData . _frameHash ;
1302- var i , traceIndices , traceIndex , expandedObj , destIndex , copy ;
1302+ var i , traceIndices , traceIndex , destIndex ;
13031303
13041304 var framePtr = frameLookup [ frameName ] ;
13051305
@@ -1326,9 +1326,7 @@ plots.computeFrame = function(gd, frameName) {
13261326 // Merge, starting with the last and ending with the desired frame:
13271327 while ( ( framePtr = frameStack . pop ( ) ) ) {
13281328 if ( framePtr . layout ) {
1329- copy = Lib . extendDeepNoArrays ( { } , framePtr . layout ) ;
1330- expandedObj = Lib . expandObjectPaths ( copy ) ;
1331- result . layout = Lib . extendDeepNoArrays ( result . layout || { } , expandedObj ) ;
1329+ result . layout = plots . extendLayout ( result . layout , framePtr . layout ) ;
13321330 }
13331331
13341332 if ( framePtr . data ) {
@@ -1363,16 +1361,99 @@ plots.computeFrame = function(gd, frameName) {
13631361 result . traces [ destIndex ] = traceIndex ;
13641362 }
13651363
1366- copy = Lib . extendDeepNoArrays ( { } , framePtr . data [ i ] ) ;
1367- expandedObj = Lib . expandObjectPaths ( copy ) ;
1368- result . data [ destIndex ] = Lib . extendDeepNoArrays ( result . data [ destIndex ] || { } , expandedObj ) ;
1364+ result . data [ destIndex ] = plots . extendTrace ( result . data [ destIndex ] , framePtr . data [ i ] ) ;
13691365 }
13701366 }
13711367 }
13721368
13731369 return result ;
13741370} ;
13751371
1372+ /**
1373+ * Extend an object, treating container arrays very differently by extracting
1374+ * their contents and merging them separately.
1375+ *
1376+ * This exists so that we can extendDeepNoArrays and avoid stepping into data
1377+ * arrays without knowledge of the plot schema, but so that we may also manually
1378+ * recurse into known container arrays, such as transforms.
1379+ *
1380+ * See extendTrace and extendLayout below for usage.
1381+ */
1382+ plots . extendObjectWithContainers = function ( dest , src , containerPaths ) {
1383+ var containerProp , containerVal , i , j , srcProp , destProp , srcContainer , destContainer ;
1384+ var copy = Lib . extendDeepNoArrays ( { } , src || { } ) ;
1385+ var expandedObj = Lib . expandObjectPaths ( copy ) ;
1386+ var containerObj = { } ;
1387+
1388+ // Step through and extract any container properties. Otherwise extendDeepNoArrays
1389+ // will clobber any existing properties with an empty array and then supplyDefaults
1390+ // will reset everything to defaults.
1391+ if ( containerPaths && containerPaths . length ) {
1392+ for ( i = 0 ; i < containerPaths . length ; i ++ ) {
1393+ containerProp = Lib . nestedProperty ( expandedObj , containerPaths [ i ] ) ;
1394+ containerVal = containerProp . get ( ) ;
1395+ containerProp . set ( null ) ;
1396+ Lib . nestedProperty ( containerObj , containerPaths [ i ] ) . set ( containerVal ) ;
1397+ }
1398+ }
1399+
1400+ dest = Lib . extendDeepNoArrays ( dest || { } , expandedObj ) ;
1401+
1402+ if ( containerPaths && containerPaths . length ) {
1403+ for ( i = 0 ; i < containerPaths . length ; i ++ ) {
1404+ srcProp = Lib . nestedProperty ( containerObj , containerPaths [ i ] ) ;
1405+ srcContainer = srcProp . get ( ) ;
1406+
1407+ if ( ! srcContainer ) continue ;
1408+
1409+ destProp = Lib . nestedProperty ( dest , containerPaths [ i ] ) ;
1410+
1411+ destContainer = destProp . get ( ) ;
1412+ if ( ! Array . isArray ( destContainer ) ) {
1413+ destContainer = [ ] ;
1414+ destProp . set ( destContainer ) ;
1415+ }
1416+
1417+ for ( j = 0 ; j < srcContainer . length ; j ++ ) {
1418+ destContainer [ j ] = plots . extendObjectWithContainers ( destContainer [ j ] , srcContainer [ j ] ) ;
1419+ }
1420+ }
1421+ }
1422+
1423+ return dest ;
1424+ } ;
1425+
1426+ /*
1427+ * Extend a trace definition. This method:
1428+ *
1429+ * 1. directly transfers any array references
1430+ * 2. manually recurses into container arrays like transforms
1431+ *
1432+ * The result is the original object reference with the new contents merged in.
1433+ */
1434+ plots . extendTrace = function ( destTrace , srcTrace ) {
1435+ return plots . extendObjectWithContainers ( destTrace , srcTrace , [ 'transforms' ] ) ;
1436+ } ;
1437+
1438+ /*
1439+ * Extend a layout definition. This method:
1440+ *
1441+ * 1. directly transfers any array references (not critically important for
1442+ * layout since there aren't really data arrays)
1443+ * 2. manually recurses into container arrays like annotations
1444+ *
1445+ * The result is the original object reference with the new contents merged in.
1446+ */
1447+ plots . extendLayout = function ( destLayout , srcLayout ) {
1448+ return plots . extendObjectWithContainers ( destLayout , srcLayout , [
1449+ 'annotations' ,
1450+ 'shapes' ,
1451+ 'images' ,
1452+ 'sliders' ,
1453+ 'updatemenus'
1454+ ] ) ;
1455+ } ;
1456+
13761457/**
13771458 * Transition to a set of new data and layout properties
13781459 *
@@ -1411,16 +1492,7 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts)
14111492
14121493 transitionedTraces . push ( traceIdx ) ;
14131494
1414- // This is a multi-step process. First clone w/o arrays so that
1415- // we're not modifying the original:
1416- var update = Lib . extendDeepNoArrays ( { } , data [ i ] ) ;
1417-
1418- // Then expand object paths since we don't obey object-overwrite
1419- // semantics here:
1420- update = Lib . expandObjectPaths ( update ) ;
1421-
1422- // Finally apply the update (without copying arrays, of course):
1423- Lib . extendDeepNoArrays ( gd . data [ traceIndices [ i ] ] , update ) ;
1495+ gd . data [ traceIndices [ i ] ] = plots . extendTrace ( gd . data [ traceIndices [ i ] ] , data [ i ] ) ;
14241496 }
14251497
14261498 // Follow the same procedure. Clone it so we don't mangle the input, then
0 commit comments