@@ -1705,17 +1705,7 @@ Plotly.relayout = function relayout(gd, astr, val) {
17051705 return Promise . resolve ( gd ) ;
17061706 }
17071707
1708- var layout = gd . layout ,
1709- fullLayout = gd . _fullLayout ,
1710- aobj = { } ,
1711- dolegend = false ,
1712- doticks = false ,
1713- dolayoutstyle = false ,
1714- doplot = false ,
1715- docalc = false ,
1716- domodebar = false ,
1717- newkey , axes , keys , xyref , scene , axisAttr , i ;
1718-
1708+ var aobj = { } ;
17191709 if ( typeof astr === 'string' ) aobj [ astr ] = val ;
17201710 else if ( Lib . isPlainObject ( astr ) ) aobj = astr ;
17211711 else {
@@ -1725,30 +1715,84 @@ Plotly.relayout = function relayout(gd, astr, val) {
17251715
17261716 if ( Object . keys ( aobj ) . length ) gd . changed = true ;
17271717
1728- keys = Object . keys ( aobj ) ;
1729- axes = Plotly . Axes . list ( gd ) ;
1718+ var specs = _relayout ( gd , aobj ) ,
1719+ flags = specs . flags ;
1720+
1721+ // clear calcdata if required
1722+ if ( flags . docalc ) gd . calcdata = undefined ;
1723+
1724+ // fill in redraw sequence
1725+ var seq = [ ] ;
1726+
1727+ if ( flags . layoutReplot ) {
1728+ seq . push ( subroutines . layoutReplot ) ;
1729+ }
1730+ else if ( Object . keys ( aobj ) . length ) {
1731+ seq . push ( Plots . previousPromises ) ;
1732+ Plots . supplyDefaults ( gd ) ;
1733+
1734+ if ( flags . dolegend ) seq . push ( subroutines . doLegend ) ;
1735+ if ( flags . dolayoutstyle ) seq . push ( subroutines . layoutStyles ) ;
1736+ if ( flags . doticks ) seq . push ( subroutines . doTicksRelayout ) ;
1737+ if ( flags . domodebar ) seq . push ( subroutines . doModeBar ) ;
1738+ }
1739+
1740+ Queue . add ( gd ,
1741+ relayout , [ gd , specs . undoit ] ,
1742+ relayout , [ gd , specs . redoit ]
1743+ ) ;
1744+
1745+ var plotDone = Lib . syncOrAsync ( seq , gd ) ;
1746+ if ( ! plotDone || ! plotDone . then ) plotDone = Promise . resolve ( gd ) ;
1747+
1748+ return plotDone . then ( function ( ) {
1749+ subroutines . setRangeSliderRange ( gd , specs . eventData ) ;
1750+ gd . emit ( 'plotly_relayout' , specs . eventData ) ;
1751+ return gd ;
1752+ } ) ;
1753+ } ;
1754+
1755+ function _relayout ( gd , aobj ) {
1756+ var layout = gd . layout ,
1757+ fullLayout = gd . _fullLayout ,
1758+ keys = Object . keys ( aobj ) ,
1759+ axes = Plotly . Axes . list ( gd ) ,
1760+ i ;
17301761
1762+ // look for 'allaxes', split out into all axes
1763+ // in case of 3D the axis are nested within a scene which is held in _id
17311764 for ( i = 0 ; i < keys . length ; i ++ ) {
1732- // look for 'allaxes', split out into all axes
17331765 if ( keys [ i ] . indexOf ( 'allaxes' ) === 0 ) {
17341766 for ( var j = 0 ; j < axes . length ; j ++ ) {
1735- // in case of 3D the axis are nested within a scene which is held in _id
1736- scene = axes [ j ] . _id . substr ( 1 ) ;
1737- axisAttr = ( scene . indexOf ( 'scene' ) !== - 1 ) ? ( scene + '.' ) : '' ;
1738- newkey = keys [ i ] . replace ( 'allaxes' , axisAttr + axes [ j ] . _name ) ;
1739- if ( ! aobj [ newkey ] ) { aobj [ newkey ] = aobj [ keys [ i ] ] ; }
1767+ var scene = axes [ j ] . _id . substr ( 1 ) ,
1768+ axisAttr = ( scene . indexOf ( 'scene' ) !== - 1 ) ? ( scene + '.' ) : '' ,
1769+ newkey = keys [ i ] . replace ( 'allaxes' , axisAttr + axes [ j ] . _name ) ;
1770+
1771+ if ( ! aobj [ newkey ] ) aobj [ newkey ] = aobj [ keys [ i ] ] ;
17401772 }
1741- delete aobj [ keys [ i ] ] ;
1742- }
1773+
17431774 delete aobj [ keys [ i ] ] ;
17441775 }
17451776 }
17461777
1778+ // initialize flags
1779+ var flags = {
1780+ dolegend : false ,
1781+ doticks : false ,
1782+ dolayoutstyle : false ,
1783+ doplot : false ,
1784+ docalc : false ,
1785+ domodebar : false ,
1786+ layoutReplot : false
1787+ } ;
1788+
17471789 // copies of the change (and previous values of anything affected)
17481790 // for the undo / redo queue
17491791 var redoit = { } ,
17501792 undoit = { } ;
17511793
1794+ var hw = [ 'height' , 'width' ] ;
1795+
17521796 // for attrs that interact (like scales & autoscales), save the
17531797 // old vals before making the change
17541798 // val=undefined will not set a value, just record what the value was.
@@ -1772,8 +1816,6 @@ Plotly.relayout = function relayout(gd, astr, val) {
17721816 return ( fullLayout [ axName ] || { } ) . autorange ;
17731817 }
17741818
1775- var hw = [ 'height' , 'width' ] ;
1776-
17771819 // alter gd.layout
17781820 for ( var ai in aobj ) {
17791821 var p = Lib . nestedProperty ( layout , ai ) ,
@@ -1826,23 +1868,24 @@ Plotly.relayout = function relayout(gd, astr, val) {
18261868 doextra ( [ ptrunk + '.tick0' , ptrunk + '.dtick' ] , undefined ) ;
18271869 }
18281870 else if ( / [ x y ] a x i s [ 0 - 9 ] * ?$ / . test ( pleaf ) && ! Object . keys ( vi || { } ) . length ) {
1829- docalc = true ;
1871+ flags . docalc = true ;
18301872 }
18311873 else if ( / [ x y ] a x i s [ 0 - 9 ] * \. c a t e g o r y o r d e r $ / . test ( pleafPlus ) ) {
1832- docalc = true ;
1874+ flags . docalc = true ;
18331875 }
18341876 else if ( / [ x y ] a x i s [ 0 - 9 ] * \. c a t e g o r y a r r a y / . test ( pleafPlus ) ) {
1835- docalc = true ;
1877+ flags . docalc = true ;
18361878 }
18371879
18381880 if ( pleafPlus . indexOf ( 'rangeslider' ) !== - 1 ) {
1839- docalc = true ;
1881+ flags . docalc = true ;
18401882 }
18411883
18421884 // toggling log without autorange: need to also recalculate ranges
18431885 // logical XOR (ie are we toggling log)
18441886 if ( pleaf === 'type' && ( ( parentFull . type === 'log' ) !== ( vi === 'log' ) ) ) {
18451887 var ax = parentIn ;
1888+
18461889 if ( ! ax || ! ax . range ) {
18471890 doextra ( ptrunk + '.autorange' , true ) ;
18481891 }
@@ -1881,8 +1924,8 @@ Plotly.relayout = function relayout(gd, astr, val) {
18811924 parentIn . range = [ 1 , 0 ] ;
18821925 }
18831926
1884- if ( parentFull . autorange ) docalc = true ;
1885- else doplot = true ;
1927+ if ( parentFull . autorange ) flags . docalc = true ;
1928+ else flags . doplot = true ;
18861929 }
18871930 // send annotation and shape mods one-by-one through Annotations.draw(),
18881931 // don't set via nestedProperty
@@ -1912,7 +1955,7 @@ Plotly.relayout = function relayout(gd, astr, val) {
19121955
19131956 if ( ( refAutorange ( obji , 'x' ) || refAutorange ( obji , 'y' ) ) &&
19141957 ! Lib . containsAny ( ai , [ 'color' , 'opacity' , 'align' , 'dash' ] ) ) {
1915- docalc = true ;
1958+ flags . docalc = true ;
19161959 }
19171960
19181961 // TODO: combine all edits to a given annotation / shape into one call
@@ -1940,7 +1983,7 @@ Plotly.relayout = function relayout(gd, astr, val) {
19401983
19411984 for ( i = 0 ; i < diff ; i ++ ) fullLayers . push ( { } ) ;
19421985
1943- doplot = true ;
1986+ flags . doplot = true ;
19441987 }
19451988 else if ( p . parts [ 0 ] === 'updatemenus' ) {
19461989 Lib . extendDeepAll ( gd . layout , Lib . objectFromPath ( ai , vi ) ) ;
@@ -1949,165 +1992,101 @@ Plotly.relayout = function relayout(gd, astr, val) {
19491992 diff = ( p . parts [ 2 ] + 1 ) - menus . length ;
19501993
19511994 for ( i = 0 ; i < diff ; i ++ ) menus . push ( { } ) ;
1952- doplot = true ;
1995+ flags . doplot = true ;
19531996 }
19541997 // alter gd.layout
19551998 else {
19561999 // check whether we can short-circuit a full redraw
19572000 // 3d or geo at this point just needs to redraw.
1958- if ( p . parts [ 0 ] . indexOf ( 'scene' ) === 0 ) doplot = true ;
1959- else if ( p . parts [ 0 ] . indexOf ( 'geo' ) === 0 ) doplot = true ;
1960- else if ( p . parts [ 0 ] . indexOf ( 'ternary' ) === 0 ) doplot = true ;
2001+ if ( p . parts [ 0 ] . indexOf ( 'scene' ) === 0 ) flags . doplot = true ;
2002+ else if ( p . parts [ 0 ] . indexOf ( 'geo' ) === 0 ) flags . doplot = true ;
2003+ else if ( p . parts [ 0 ] . indexOf ( 'ternary' ) === 0 ) flags . doplot = true ;
19612004 else if ( fullLayout . _has ( 'gl2d' ) &&
19622005 ( ai . indexOf ( 'axis' ) !== - 1 || p . parts [ 0 ] === 'plot_bgcolor' )
1963- ) doplot = true ;
1964- else if ( ai === 'hiddenlabels' ) docalc = true ;
1965- else if ( p . parts [ 0 ] . indexOf ( 'legend' ) !== - 1 ) dolegend = true ;
1966- else if ( ai . indexOf ( 'title' ) !== - 1 ) doticks = true ;
1967- else if ( p . parts [ 0 ] . indexOf ( 'bgcolor' ) !== - 1 ) dolayoutstyle = true ;
2006+ ) flags . doplot = true ;
2007+ else if ( ai === 'hiddenlabels' ) flags . docalc = true ;
2008+ else if ( p . parts [ 0 ] . indexOf ( 'legend' ) !== - 1 ) flags . dolegend = true ;
2009+ else if ( ai . indexOf ( 'title' ) !== - 1 ) flags . doticks = true ;
2010+ else if ( p . parts [ 0 ] . indexOf ( 'bgcolor' ) !== - 1 ) flags . dolayoutstyle = true ;
19682011 else if ( p . parts . length > 1 &&
19692012 Lib . containsAny ( p . parts [ 1 ] , [ 'tick' , 'exponent' , 'grid' , 'zeroline' ] ) ) {
1970- doticks = true ;
2013+ flags . doticks = true ;
19712014 }
19722015 else if ( ai . indexOf ( '.linewidth' ) !== - 1 &&
19732016 ai . indexOf ( 'axis' ) !== - 1 ) {
1974- doticks = dolayoutstyle = true ;
2017+ flags . doticks = flags . dolayoutstyle = true ;
19752018 }
19762019 else if ( p . parts . length > 1 && p . parts [ 1 ] . indexOf ( 'line' ) !== - 1 ) {
1977- dolayoutstyle = true ;
2020+ flags . dolayoutstyle = true ;
19782021 }
19792022 else if ( p . parts . length > 1 && p . parts [ 1 ] === 'mirror' ) {
1980- doticks = dolayoutstyle = true ;
2023+ flags . doticks = flags . dolayoutstyle = true ;
19812024 }
19822025 else if ( ai === 'margin.pad' ) {
1983- doticks = dolayoutstyle = true ;
2026+ flags . doticks = flags . dolayoutstyle = true ;
19842027 }
19852028 else if ( p . parts [ 0 ] === 'margin' ||
19862029 p . parts [ 1 ] === 'autorange' ||
19872030 p . parts [ 1 ] === 'rangemode' ||
19882031 p . parts [ 1 ] === 'type' ||
19892032 p . parts [ 1 ] === 'domain' ||
19902033 ai . match ( / ^ ( b a r | b o x | f o n t ) / ) ) {
1991- docalc = true ;
2034+ flags . docalc = true ;
19922035 }
19932036 /*
19942037 * hovermode and dragmode don't need any redrawing, since they just
1995- * affect reaction to user input. everything else, assume full replot.
2038+ * affect reaction to user input, everything else, assume full replot.
19962039 * height, width, autosize get dealt with below. Except for the case of
19972040 * of subplots - scenes - which require scene.updateFx to be called.
19982041 */
1999- else if ( [ 'hovermode' , 'dragmode' ] . indexOf ( ai ) !== - 1 ) domodebar = true ;
2042+ else if ( [ 'hovermode' , 'dragmode' ] . indexOf ( ai ) !== - 1 ) flags . domodebar = true ;
20002043 else if ( [ 'hovermode' , 'dragmode' , 'height' ,
20012044 'width' , 'autosize' ] . indexOf ( ai ) === - 1 ) {
2002- doplot = true ;
2045+ flags . doplot = true ;
20032046 }
20042047
20052048 p . set ( vi ) ;
20062049 }
20072050 }
2008- // now all attribute mods are done, as are
2009- // redo and undo so we can save them
2010- Queue . add ( gd , relayout , [ gd , undoit ] , relayout , [ gd , redoit ] ) ;
20112051
20122052 // calculate autosizing - if size hasn't changed,
20132053 // will remove h&w so we don't need to redraw
20142054 if ( aobj . autosize ) aobj = plotAutoSize ( gd , aobj ) ;
2055+ if ( aobj . height || aobj . width || aobj . autosize ) flags . docalc = true ;
20152056
2016- if ( aobj . height || aobj . width || aobj . autosize ) docalc = true ;
2057+ if ( flags . doplot || flags . docalc ) {
2058+ flags . layoutReplot = true ;
2059+ }
20172060
2018- // redraw
2019- // first check if there's still anything to do
2020- var ak = Object . keys ( aobj ) ,
2021- seq = [ Plots . previousPromises ] ;
2061+ // now all attribute mods are done, as are
2062+ // redo and undo so we can save them
20222063
2023- if ( doplot || docalc ) {
2024- seq . push ( function layoutReplot ( ) {
2025- // force plot() to redo the layout
2026- gd . layout = undefined ;
2064+ return {
2065+ flags : flags ,
2066+ undoit : undoit ,
2067+ redoit : redoit ,
2068+ eventData : Lib . extendDeep ( { } , redoit )
2069+ } ;
2070+ }
20272071
2028- // force it to redo calcdata?
2029- if ( docalc ) gd . calcdata = undefined ;
20302072
2031- // replot with the modified layout
2032- return Plotly . plot ( gd , '' , layout ) ;
2033- } ) ;
20342073 }
2035- else if ( ak . length ) {
2036- // if we didn't need to redraw entirely, just do the needed parts
2037- Plots . supplyDefaults ( gd ) ;
2038- fullLayout = gd . _fullLayout ;
20392074
2040- if ( dolegend ) {
2041- seq . push ( function doLegend ( ) {
2042- Registry . getComponentMethod ( 'legend' , 'draw' ) ( gd ) ;
2043- return Plots . previousPromises ( gd ) ;
2044- } ) ;
2045- }
20462075
2047- if ( dolayoutstyle ) seq . push ( layoutStyles ) ;
20482076
2049- if ( doticks ) {
2050- seq . push ( function ( ) {
2051- Plotly . Axes . doTicks ( gd , 'redraw' ) ;
2052- drawMainTitle ( gd ) ;
2053- return Plots . previousPromises ( gd ) ;
2054- } ) ;
2055- }
20562077
2057- // this is decoupled enough it doesn't need async regardless
2058- if ( domodebar ) {
2059- var subplotIds ;
2060- ModeBar . manage ( gd ) ;
20612078
2062- Plotly . Fx . supplyLayoutDefaults ( gd . layout , fullLayout , gd . _fullData ) ;
2063- Plotly . Fx . init ( gd ) ;
20642079
2065- subplotIds = Plots . getSubplotIds ( fullLayout , 'gl3d' ) ;
2066- for ( i = 0 ; i < subplotIds . length ; i ++ ) {
2067- scene = fullLayout [ subplotIds [ i ] ] . _scene ;
2068- scene . updateFx ( fullLayout . dragmode , fullLayout . hovermode ) ;
2069- }
20702080
2071- subplotIds = Plots . getSubplotIds ( fullLayout , 'gl2d' ) ;
2072- for ( i = 0 ; i < subplotIds . length ; i ++ ) {
2073- scene = fullLayout . _plots [ subplotIds [ i ] ] . _scene2d ;
2074- scene . updateFx ( fullLayout ) ;
2075- }
2076-
2077- subplotIds = Plots . getSubplotIds ( fullLayout , 'geo' ) ;
2078- for ( i = 0 ; i < subplotIds . length ; i ++ ) {
2079- var geo = fullLayout [ subplotIds [ i ] ] . _geo ;
2080- geo . updateFx ( fullLayout . hovermode ) ;
2081- }
2082- }
20832081 }
20842082
2085- function setRange ( changes ) {
2086-
2087- var newMin = changes [ 'xaxis.range' ] ? changes [ 'xaxis.range' ] [ 0 ] : changes [ 'xaxis.range[0]' ] ,
2088- newMax = changes [ 'xaxis.range' ] ? changes [ 'xaxis.range' ] [ 1 ] : changes [ 'xaxis.range[1]' ] ;
2089-
2090- var rangeSlider = fullLayout . xaxis && fullLayout . xaxis . rangeslider ?
2091- fullLayout . xaxis . rangeslider : { } ;
2092-
2093- if ( rangeSlider . visible ) {
2094- if ( newMin || newMax ) {
2095- fullLayout . xaxis . rangeslider . setRange ( newMin , newMax ) ;
2096- } else if ( changes [ 'xaxis.autorange' ] ) {
2097- fullLayout . xaxis . rangeslider . setRange ( ) ;
2098- }
2099- }
21002083 }
21012084
2102- var plotDone = Lib . syncOrAsync ( seq , gd ) ;
21032085
21042086 if ( ! plotDone || ! plotDone . then ) plotDone = Promise . resolve ( gd ) ;
21052087
21062088 return plotDone . then ( function ( ) {
2107- var changes = Lib . extendDeep ( { } , redoit ) ;
21082089
2109- setRange ( changes ) ;
2110- gd . emit ( 'plotly_relayout' , changes ) ;
21112090
21122091 return gd ;
21132092 } ) ;
0 commit comments