From 0fccc8d6a0b712728771ccd6d2ba83f6e2d0546d Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 29 Mar 2021 11:19:50 -0400
Subject: [PATCH 01/10] rename _main legend to _isLegend for clarity

---
 src/components/legend/draw.js            | 28 ++++++++++++------------
 src/components/legend/get_legend_data.js |  3 +--
 2 files changed, 15 insertions(+), 16 deletions(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index f91b813c6c4..365a14feed8 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -30,7 +30,7 @@ module.exports = function draw(gd, opts) {
     // Check whether this is the main legend (ie. called without any opts)
     if(!opts) {
         opts = fullLayout.legend || {};
-        opts._main = true;
+        opts._isLegend = true;
         layer = fullLayout._infolayer;
     } else {
         layer = opts.layer;
@@ -42,7 +42,7 @@ module.exports = function draw(gd, opts) {
     if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0;
 
     var legendData;
-    if(opts._main) {
+    if(opts._isLegend) {
         if(!gd.calcdata) return;
         legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts);
     } else {
@@ -52,14 +52,14 @@ module.exports = function draw(gd, opts) {
 
     var hiddenSlices = fullLayout.hiddenlabels || [];
 
-    if(opts._main && (!fullLayout.showlegend || !legendData.length)) {
+    if(opts._isLegend && (!fullLayout.showlegend || !legendData.length)) {
         layer.selectAll('.legend').remove();
         fullLayout._topdefs.select('#' + clipId).remove();
         return Plots.autoMargin(gd, 'legend');
     }
 
     var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) {
-        if(opts._main) s.attr('pointer-events', 'all');
+        if(opts._isLegend) s.attr('pointer-events', 'all');
     });
 
     var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) {
@@ -112,7 +112,7 @@ module.exports = function draw(gd, opts) {
     })
     .each(function() { d3.select(this).call(drawTexts, gd, opts); })
     .call(style, gd, opts)
-    .each(function() { if(opts._main) d3.select(this).call(setupTraceToggle, gd); });
+    .each(function() { if(opts._isLegend) d3.select(this).call(setupTraceToggle, gd); });
 
     Lib.syncOrAsync([
         Plots.previousPromises,
@@ -121,7 +121,7 @@ module.exports = function draw(gd, opts) {
             // IF expandMargin return a Promise (which is truthy),
             // we're under a doAutoMargin redraw, so we don't have to
             // draw the remaining pieces below
-            if(opts._main && expandMargin(gd)) return;
+            if(opts._isLegend && expandMargin(gd)) return;
 
             var gs = fullLayout._size;
             var bw = opts.borderwidth;
@@ -129,7 +129,7 @@ module.exports = function draw(gd, opts) {
             var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width;
             var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight;
 
-            if(opts._main && fullLayout.margin.autoexpand) {
+            if(opts._isLegend && fullLayout.margin.autoexpand) {
                 var lx0 = lx;
                 var ly0 = ly;
 
@@ -146,18 +146,18 @@ module.exports = function draw(gd, opts) {
 
             // Set size and position of all the elements that make up a legend:
             // legend, background and border, scroll box and scroll bar as well as title
-            if(opts._main) Drawing.setTranslate(legend, lx, ly);
+            if(opts._isLegend) Drawing.setTranslate(legend, lx, ly);
 
             // to be safe, remove previous listeners
             scrollBar.on('.drag', null);
             legend.on('wheel', null);
 
-            if(!opts._main || opts._height <= opts._maxHeight || gd._context.staticPlot) {
+            if(!opts._isLegend || opts._height <= opts._maxHeight || gd._context.staticPlot) {
                 // if scrollbar should not be shown.
                 var height = opts._effHeight;
 
-                // if not the main legend, let it be its full size
-                if(!opts._main) height = opts._height;
+                // if unified hover, let it be its full size
+                if(!opts._isLegend) height = opts._height;
 
                 bg.attr({
                     width: opts._width - bw,
@@ -386,7 +386,7 @@ function drawTexts(g, gd, opts) {
     var trace = legendItem.trace;
     var isPieLike = Registry.traceIs(trace, 'pie-like');
     var traceIndex = trace.index;
-    var isEditable = opts._main && gd._context.edits.legendText && !isPieLike;
+    var isEditable = opts._isLegend && gd._context.edits.legendText && !isPieLike;
     var maxNameLength = opts._maxNameLength;
 
     var name;
@@ -491,7 +491,7 @@ function setupTraceToggle(g, gd) {
 }
 
 function textLayout(s, g, gd, opts) {
-    if(!opts._main) s.attr('data-notex', true); // do not process MathJax if not main
+    if(!opts._isLegend) s.attr('data-notex', true); // do not process MathJax for unified hover
     svgTextUtils.convertToTspans(s, gd, function() {
         computeTextDimensions(g, gd, opts);
     });
@@ -499,7 +499,7 @@ function textLayout(s, g, gd, opts) {
 
 function computeTextDimensions(g, gd, opts) {
     var legendItem = g.data()[0][0];
-    if(opts._main && legendItem && !legendItem.trace.showlegend) {
+    if(opts._isLegend && legendItem && !legendItem.trace.showlegend) {
         g.remove();
         return;
     }
diff --git a/src/components/legend/get_legend_data.js b/src/components/legend/get_legend_data.js
index 4fcb91b1e4d..d90d760bfc5 100644
--- a/src/components/legend/get_legend_data.js
+++ b/src/components/legend/get_legend_data.js
@@ -11,7 +11,6 @@ module.exports = function getLegendData(calcdata, opts) {
     var lgroupi = 0;
     var maxNameLength = 0;
     var i, j;
-    var main = opts._main;
 
     function addOneItem(legendGroup, legendItem) {
         // each '' legend group is treated as a separate group
@@ -37,7 +36,7 @@ module.exports = function getLegendData(calcdata, opts) {
         var trace = cd0.trace;
         var lgroup = trace.legendgroup;
 
-        if(main && (!trace.visible || !trace.showlegend)) continue;
+        if(opts._isLegend && (!trace.visible || !trace.showlegend)) continue;
 
         if(Registry.traceIs(trace, 'pie-like')) {
             if(!slicesShown[lgroup]) slicesShown[lgroup] = {};

From 6abbfb2324cf027ed2bd9ff335f48d6e180dda40 Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 29 Mar 2021 11:27:06 -0400
Subject: [PATCH 02/10] use _inHover instead of !_isLegend for clarity

---
 src/components/fx/hover.js               |  1 +
 src/components/legend/draw.js            | 27 ++++++++++++------------
 src/components/legend/get_legend_data.js |  2 +-
 3 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js
index 8e345bd9836..76d00608489 100644
--- a/src/components/fx/hover.js
+++ b/src/components/fx/hover.js
@@ -1045,6 +1045,7 @@ function createHoverText(hoverData, opts, gd) {
         legendOpts.layer = container;
 
         // Draw unified hover label
+        legendOpts._inHover = true;
         legendDraw(gd, legendOpts);
 
         // Position the hover
diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index 365a14feed8..63254ad62e6 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -30,19 +30,20 @@ module.exports = function draw(gd, opts) {
     // Check whether this is the main legend (ie. called without any opts)
     if(!opts) {
         opts = fullLayout.legend || {};
-        opts._isLegend = true;
         layer = fullLayout._infolayer;
     } else {
         layer = opts.layer;
         clipId += '-hover';
     }
 
+    var inHover = !!opts._inHover;
+
     if(!layer) return;
 
     if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0;
 
     var legendData;
-    if(opts._isLegend) {
+    if(!inHover) {
         if(!gd.calcdata) return;
         legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts);
     } else {
@@ -52,14 +53,14 @@ module.exports = function draw(gd, opts) {
 
     var hiddenSlices = fullLayout.hiddenlabels || [];
 
-    if(opts._isLegend && (!fullLayout.showlegend || !legendData.length)) {
+    if(!inHover && (!fullLayout.showlegend || !legendData.length)) {
         layer.selectAll('.legend').remove();
         fullLayout._topdefs.select('#' + clipId).remove();
         return Plots.autoMargin(gd, 'legend');
     }
 
     var legend = Lib.ensureSingle(layer, 'g', 'legend', function(s) {
-        if(opts._isLegend) s.attr('pointer-events', 'all');
+        if(!inHover) s.attr('pointer-events', 'all');
     });
 
     var clipPath = Lib.ensureSingleById(fullLayout._topdefs, 'clipPath', clipId, function(s) {
@@ -112,7 +113,7 @@ module.exports = function draw(gd, opts) {
     })
     .each(function() { d3.select(this).call(drawTexts, gd, opts); })
     .call(style, gd, opts)
-    .each(function() { if(opts._isLegend) d3.select(this).call(setupTraceToggle, gd); });
+    .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); });
 
     Lib.syncOrAsync([
         Plots.previousPromises,
@@ -121,7 +122,7 @@ module.exports = function draw(gd, opts) {
             // IF expandMargin return a Promise (which is truthy),
             // we're under a doAutoMargin redraw, so we don't have to
             // draw the remaining pieces below
-            if(opts._isLegend && expandMargin(gd)) return;
+            if(!inHover && expandMargin(gd)) return;
 
             var gs = fullLayout._size;
             var bw = opts.borderwidth;
@@ -129,7 +130,7 @@ module.exports = function draw(gd, opts) {
             var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width;
             var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight;
 
-            if(opts._isLegend && fullLayout.margin.autoexpand) {
+            if(!inHover && fullLayout.margin.autoexpand) {
                 var lx0 = lx;
                 var ly0 = ly;
 
@@ -146,18 +147,18 @@ module.exports = function draw(gd, opts) {
 
             // Set size and position of all the elements that make up a legend:
             // legend, background and border, scroll box and scroll bar as well as title
-            if(opts._isLegend) Drawing.setTranslate(legend, lx, ly);
+            if(!inHover) Drawing.setTranslate(legend, lx, ly);
 
             // to be safe, remove previous listeners
             scrollBar.on('.drag', null);
             legend.on('wheel', null);
 
-            if(!opts._isLegend || opts._height <= opts._maxHeight || gd._context.staticPlot) {
+            if(inHover || opts._height <= opts._maxHeight || gd._context.staticPlot) {
                 // if scrollbar should not be shown.
                 var height = opts._effHeight;
 
                 // if unified hover, let it be its full size
-                if(!opts._isLegend) height = opts._height;
+                if(inHover) height = opts._height;
 
                 bg.attr({
                     width: opts._width - bw,
@@ -386,7 +387,7 @@ function drawTexts(g, gd, opts) {
     var trace = legendItem.trace;
     var isPieLike = Registry.traceIs(trace, 'pie-like');
     var traceIndex = trace.index;
-    var isEditable = opts._isLegend && gd._context.edits.legendText && !isPieLike;
+    var isEditable = !opts._inHover && gd._context.edits.legendText && !isPieLike;
     var maxNameLength = opts._maxNameLength;
 
     var name;
@@ -491,7 +492,7 @@ function setupTraceToggle(g, gd) {
 }
 
 function textLayout(s, g, gd, opts) {
-    if(!opts._isLegend) s.attr('data-notex', true); // do not process MathJax for unified hover
+    if(opts._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
     svgTextUtils.convertToTspans(s, gd, function() {
         computeTextDimensions(g, gd, opts);
     });
@@ -499,7 +500,7 @@ function textLayout(s, g, gd, opts) {
 
 function computeTextDimensions(g, gd, opts) {
     var legendItem = g.data()[0][0];
-    if(opts._isLegend && legendItem && !legendItem.trace.showlegend) {
+    if(!opts._inHover && legendItem && !legendItem.trace.showlegend) {
         g.remove();
         return;
     }
diff --git a/src/components/legend/get_legend_data.js b/src/components/legend/get_legend_data.js
index d90d760bfc5..7e09fa32337 100644
--- a/src/components/legend/get_legend_data.js
+++ b/src/components/legend/get_legend_data.js
@@ -36,7 +36,7 @@ module.exports = function getLegendData(calcdata, opts) {
         var trace = cd0.trace;
         var lgroup = trace.legendgroup;
 
-        if(opts._isLegend && (!trace.visible || !trace.showlegend)) continue;
+        if(!opts._inHover && (!trace.visible || !trace.showlegend)) continue;
 
         if(Registry.traceIs(trace, 'pie-like')) {
             if(!slicesShown[lgroup]) slicesShown[lgroup] = {};

From ee759471518f53a0bf3a79403cbacee885fa67db Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Wed, 31 Mar 2021 16:16:13 -0400
Subject: [PATCH 03/10] better handle of main title

---
 src/components/legend/draw.js | 53 ++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 23 deletions(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index 63254ad62e6..5d704cae8d9 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -22,6 +22,8 @@ var getLegendData = require('./get_legend_data');
 var style = require('./style');
 var helpers = require('./helpers');
 
+var MAIN_TITLE = 1;
+
 module.exports = function draw(gd, opts) {
     var fullLayout = gd._fullLayout;
     var clipId = 'legend' + fullLayout._uid;
@@ -85,7 +87,7 @@ module.exports = function draw(gd, opts) {
             .call(Drawing.font, title.font)
             .text(title.text);
 
-        textLayout(titleEl, scrollBox, gd, opts); // handle mathjax or multi-line text and compute title height
+        textLayout(titleEl, scrollBox, gd, opts, MAIN_TITLE); // handle mathjax or multi-line text and compute title height
     } else {
         scrollBox.selectAll('.legendtitletext').remove();
     }
@@ -386,7 +388,6 @@ function drawTexts(g, gd, opts) {
     var legendItem = g.data()[0][0];
     var trace = legendItem.trace;
     var isPieLike = Registry.traceIs(trace, 'pie-like');
-    var traceIndex = trace.index;
     var isEditable = !opts._inHover && gd._context.edits.legendText && !isPieLike;
     var maxNameLength = opts._maxNameLength;
 
@@ -432,7 +433,7 @@ function drawTexts(g, gd, opts) {
                     update.name = newName;
                 }
 
-                return Registry.call('_guiRestyle', gd, update, traceIndex);
+                return Registry.call('_guiRestyle', gd, update, trace.index);
             });
     } else {
         textLayout(textEl, g, gd, opts);
@@ -491,14 +492,14 @@ function setupTraceToggle(g, gd) {
     });
 }
 
-function textLayout(s, g, gd, opts) {
+function textLayout(s, g, gd, opts, aTitle) {
     if(opts._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
     svgTextUtils.convertToTspans(s, gd, function() {
-        computeTextDimensions(g, gd, opts);
+        computeTextDimensions(g, gd, opts, aTitle);
     });
 }
 
-function computeTextDimensions(g, gd, opts) {
+function computeTextDimensions(g, gd, opts, aTitle) {
     var legendItem = g.data()[0][0];
     if(!opts._inHover && legendItem && !legendItem.trace.showlegend) {
         g.remove();
@@ -509,7 +510,7 @@ function computeTextDimensions(g, gd, opts) {
     var mathjaxNode = mathjaxGroup.node();
     if(!opts) opts = gd._fullLayout.legend;
     var bw = opts.borderwidth;
-    var lineHeight = (legendItem ? opts : opts.title).font.size * LINE_SPACING;
+    var lineHeight = (aTitle === MAIN_TITLE ? opts.title : opts).font.size * LINE_SPACING;
     var height, width;
 
     if(mathjaxNode) {
@@ -518,14 +519,14 @@ function computeTextDimensions(g, gd, opts) {
         height = mathjaxBB.height;
         width = mathjaxBB.width;
 
-        if(legendItem) {
+        if(aTitle === MAIN_TITLE) {
+            Drawing.setTranslate(mathjaxGroup, bw, bw + height * 0.75);
+        } else { // legend item
             Drawing.setTranslate(mathjaxGroup, 0, height * 0.25);
-        } else { // case of title
-            Drawing.setTranslate(mathjaxGroup, bw, height * 0.75 + bw);
         }
     } else {
-        var textEl = g.select(legendItem ?
-            '.legendtext' : '.legendtitletext'
+        var textEl = g.select(aTitle === MAIN_TITLE ?
+            '.legendtitletext' : '.legendtext'
         );
         var textLines = svgTextUtils.lineCount(textEl);
         var textNode = textEl.node();
@@ -535,22 +536,26 @@ function computeTextDimensions(g, gd, opts) {
 
         // approximation to height offset to center the font
         // to avoid getBoundingClientRect
-        var textY = lineHeight * ((textLines - 1) / 2 - 0.3);
-        if(legendItem) {
-            var textGap = opts.itemwidth + constants.itemGap * 2;
-            svgTextUtils.positionText(textEl, textGap, -textY);
-        } else { // case of title
-            svgTextUtils.positionText(textEl, constants.titlePad + bw, lineHeight + bw);
+        if(aTitle === MAIN_TITLE) {
+            svgTextUtils.positionText(textEl,
+                bw + constants.titlePad,
+                bw + lineHeight
+            );
+        } else { // legend item
+            svgTextUtils.positionText(textEl,
+                opts.itemwidth + constants.itemGap * 2,
+                -lineHeight * ((textLines - 1) / 2 - 0.3)
+            );
         }
     }
 
-    if(legendItem) {
+    if(aTitle === MAIN_TITLE) {
+        opts._titleWidth = width;
+        opts._titleHeight = height;
+    } else { // legend item
         legendItem.lineHeight = lineHeight;
         legendItem.height = Math.max(height, 16) + 3;
         legendItem.width = width;
-    } else { // case of title
-        opts._titleWidth = width;
-        opts._titleHeight = height;
     }
 }
 
@@ -599,6 +604,8 @@ function computeLegendDimensions(gd, groups, traces, opts) {
     var isBelowPlotArea = opts.y < 0 || (opts.y === 0 && yanchor === 'top');
     var isAbovePlotArea = opts.y > 1 || (opts.y === 1 && yanchor === 'bottom');
 
+    var traceGroupGap = opts.tracegroupgap;
+
     // - if below/above plot area, give it the maximum potential margin-push value
     // - otherwise, extend the height of the plot area
     opts._maxHeight = Math.max(
@@ -681,7 +688,7 @@ function computeLegendDimensions(gd, groups, traces, opts) {
                 if((next + bw + groupOffsetX) > opts._maxWidth) {
                     maxRowWidth = Math.max(maxRowWidth, groupOffsetX);
                     groupOffsetX = 0;
-                    groupOffsetY += maxGroupHeightInRow + opts.tracegroupgap;
+                    groupOffsetY += maxGroupHeightInRow + traceGroupGap;
                     maxGroupHeightInRow = offsetY;
                 }
 

From 2daab813cfd65f4cd739c2e32b3543c7e6f5425d Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 5 Apr 2021 15:00:25 -0400
Subject: [PATCH 04/10] rename confusing opts in legend/draw to container i.e.
 legend/hover

---
 src/components/legend/draw.js | 250 +++++++++++++++++-----------------
 1 file changed, 125 insertions(+), 125 deletions(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index 5d704cae8d9..3f865788fb1 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -24,21 +24,21 @@ var helpers = require('./helpers');
 
 var MAIN_TITLE = 1;
 
-module.exports = function draw(gd, opts) {
+module.exports = function draw(gd, container) {
     var fullLayout = gd._fullLayout;
     var clipId = 'legend' + fullLayout._uid;
     var layer;
 
-    // Check whether this is the main legend (ie. called without any opts)
-    if(!opts) {
-        opts = fullLayout.legend || {};
+    // Check whether this is the main legend (ie. called without any container)
+    if(!container) {
+        container = fullLayout.legend || {};
         layer = fullLayout._infolayer;
     } else {
-        layer = opts.layer;
+        layer = container.layer;
         clipId += '-hover';
     }
 
-    var inHover = !!opts._inHover;
+    var inHover = !!container._inHover;
 
     if(!layer) return;
 
@@ -47,10 +47,10 @@ module.exports = function draw(gd, opts) {
     var legendData;
     if(!inHover) {
         if(!gd.calcdata) return;
-        legendData = fullLayout.showlegend && getLegendData(gd.calcdata, opts);
+        legendData = fullLayout.showlegend && getLegendData(gd.calcdata, container);
     } else {
-        if(!opts.entries) return;
-        legendData = getLegendData(opts.entries, opts);
+        if(!container.entries) return;
+        legendData = getLegendData(container.entries, container);
     }
 
     var hiddenSlices = fullLayout.hiddenlabels || [];
@@ -72,22 +72,22 @@ module.exports = function draw(gd, opts) {
     var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) {
         s.attr('shape-rendering', 'crispEdges');
     });
-    bg.call(Color.stroke, opts.bordercolor)
-        .call(Color.fill, opts.bgcolor)
-        .style('stroke-width', opts.borderwidth + 'px');
+    bg.call(Color.stroke, container.bordercolor)
+        .call(Color.fill, container.bgcolor)
+        .style('stroke-width', container.borderwidth + 'px');
 
     var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox');
 
-    var title = opts.title;
-    opts._titleWidth = 0;
-    opts._titleHeight = 0;
+    var title = container.title;
+    container._titleWidth = 0;
+    container._titleHeight = 0;
     if(title.text) {
         var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext');
         titleEl.attr('text-anchor', 'start')
             .call(Drawing.font, title.font)
             .text(title.text);
 
-        textLayout(titleEl, scrollBox, gd, opts, MAIN_TITLE); // handle mathjax or multi-line text and compute title height
+        textLayout(titleEl, scrollBox, gd, container, MAIN_TITLE); // handle mathjax or multi-line text and compute title height
     } else {
         scrollBox.selectAll('.legendtitletext').remove();
     }
@@ -113,13 +113,13 @@ module.exports = function draw(gd, opts) {
             return trace.visible === 'legendonly' ? 0.5 : 1;
         }
     })
-    .each(function() { d3.select(this).call(drawTexts, gd, opts); })
-    .call(style, gd, opts)
+    .each(function() { d3.select(this).call(drawTexts, gd, container); })
+    .call(style, gd, container)
     .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); });
 
     Lib.syncOrAsync([
         Plots.previousPromises,
-        function() { return computeLegendDimensions(gd, groups, traces, opts); },
+        function() { return computeLegendDimensions(gd, groups, traces, container); },
         function() {
             // IF expandMargin return a Promise (which is truthy),
             // we're under a doAutoMargin redraw, so we don't have to
@@ -127,17 +127,17 @@ module.exports = function draw(gd, opts) {
             if(!inHover && expandMargin(gd)) return;
 
             var gs = fullLayout._size;
-            var bw = opts.borderwidth;
+            var bw = container.borderwidth;
 
-            var lx = gs.l + gs.w * opts.x - FROM_TL[getXanchor(opts)] * opts._width;
-            var ly = gs.t + gs.h * (1 - opts.y) - FROM_TL[getYanchor(opts)] * opts._effHeight;
+            var lx = gs.l + gs.w * container.x - FROM_TL[getXanchor(container)] * container._width;
+            var ly = gs.t + gs.h * (1 - container.y) - FROM_TL[getYanchor(container)] * container._effHeight;
 
             if(!inHover && fullLayout.margin.autoexpand) {
                 var lx0 = lx;
                 var ly0 = ly;
 
-                lx = Lib.constrain(lx, 0, fullLayout.width - opts._width);
-                ly = Lib.constrain(ly, 0, fullLayout.height - opts._effHeight);
+                lx = Lib.constrain(lx, 0, fullLayout.width - container._width);
+                ly = Lib.constrain(ly, 0, fullLayout.height - container._effHeight);
 
                 if(lx !== lx0) {
                     Lib.log('Constrain legend.x to make legend fit inside graph');
@@ -155,15 +155,15 @@ module.exports = function draw(gd, opts) {
             scrollBar.on('.drag', null);
             legend.on('wheel', null);
 
-            if(inHover || opts._height <= opts._maxHeight || gd._context.staticPlot) {
+            if(inHover || container._height <= container._maxHeight || gd._context.staticPlot) {
                 // if scrollbar should not be shown.
-                var height = opts._effHeight;
+                var height = container._effHeight;
 
                 // if unified hover, let it be its full size
-                if(inHover) height = opts._height;
+                if(inHover) height = container._height;
 
                 bg.attr({
-                    width: opts._width - bw,
+                    width: container._width - bw,
                     height: height - bw,
                     x: bw / 2,
                     y: bw / 2
@@ -172,7 +172,7 @@ module.exports = function draw(gd, opts) {
                 Drawing.setTranslate(scrollBox, 0, 0);
 
                 clipPath.select('rect').attr({
-                    width: opts._width - 2 * bw,
+                    width: container._width - 2 * bw,
                     height: height - 2 * bw,
                     x: bw,
                     y: bw
@@ -181,36 +181,36 @@ module.exports = function draw(gd, opts) {
                 Drawing.setClipUrl(scrollBox, clipId, gd);
 
                 Drawing.setRect(scrollBar, 0, 0, 0, 0);
-                delete opts._scrollY;
+                delete container._scrollY;
             } else {
                 var scrollBarHeight = Math.max(constants.scrollBarMinHeight,
-                    opts._effHeight * opts._effHeight / opts._height);
-                var scrollBarYMax = opts._effHeight -
+                    container._effHeight * container._effHeight / container._height);
+                var scrollBarYMax = container._effHeight -
                     scrollBarHeight -
                     2 * constants.scrollBarMargin;
-                var scrollBoxYMax = opts._height - opts._effHeight;
+                var scrollBoxYMax = container._height - container._effHeight;
                 var scrollRatio = scrollBarYMax / scrollBoxYMax;
 
-                var scrollBoxY = Math.min(opts._scrollY || 0, scrollBoxYMax);
+                var scrollBoxY = Math.min(container._scrollY || 0, scrollBoxYMax);
 
                 // increase the background and clip-path width
                 // by the scrollbar width and margin
                 bg.attr({
-                    width: opts._width -
+                    width: container._width -
                         2 * bw +
                         constants.scrollBarWidth +
                         constants.scrollBarMargin,
-                    height: opts._effHeight - bw,
+                    height: container._effHeight - bw,
                     x: bw / 2,
                     y: bw / 2
                 });
 
                 clipPath.select('rect').attr({
-                    width: opts._width -
+                    width: container._width -
                         2 * bw +
                         constants.scrollBarWidth +
                         constants.scrollBarMargin,
-                    height: opts._effHeight - 2 * bw,
+                    height: container._effHeight - 2 * bw,
                     x: bw,
                     y: bw + scrollBoxY
                 });
@@ -222,7 +222,7 @@ module.exports = function draw(gd, opts) {
                 // scroll legend by mousewheel or touchpad swipe up/down
                 legend.on('wheel', function() {
                     scrollBoxY = Lib.constrain(
-                        opts._scrollY +
+                        container._scrollY +
                             ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax),
                         0, scrollBoxYMax);
                     scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
@@ -288,12 +288,12 @@ module.exports = function draw(gd, opts) {
             }
 
             function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) {
-                opts._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
+                container._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
                 Drawing.setTranslate(scrollBox, 0, -scrollBoxY);
 
                 Drawing.setRect(
                     scrollBar,
-                    opts._width,
+                    container._width,
                     constants.scrollBarMargin + scrollBoxY * scrollRatio,
                     constants.scrollBarWidth,
                     scrollBarHeight
@@ -320,8 +320,8 @@ module.exports = function draw(gd, opts) {
 
                         Drawing.setTranslate(legend, newX, newY);
 
-                        xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, opts.xanchor);
-                        yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, opts.yanchor);
+                        xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, container.xanchor);
+                        yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, container.yanchor);
                     },
                     doneFn: function() {
                         if(xf !== undefined && yf !== undefined) {
@@ -384,15 +384,15 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
     }
 }
 
-function drawTexts(g, gd, opts) {
+function drawTexts(g, gd, container) {
     var legendItem = g.data()[0][0];
     var trace = legendItem.trace;
     var isPieLike = Registry.traceIs(trace, 'pie-like');
-    var isEditable = !opts._inHover && gd._context.edits.legendText && !isPieLike;
-    var maxNameLength = opts._maxNameLength;
+    var isEditable = !container._inHover && gd._context.edits.legendText && !isPieLike;
+    var maxNameLength = container._maxNameLength;
 
     var name;
-    if(!opts.entries) {
+    if(!container.entries) {
         name = isPieLike ? legendItem.label : trace.name;
         if(trace._meta) {
             name = Lib.templateString(name, trace._meta);
@@ -404,18 +404,18 @@ function drawTexts(g, gd, opts) {
     var textEl = Lib.ensureSingle(g, 'text', 'legendtext');
 
     textEl.attr('text-anchor', 'start')
-        .call(Drawing.font, opts.font)
+        .call(Drawing.font, container.font)
         .text(isEditable ? ensureLength(name, maxNameLength) : name);
 
-    var textGap = opts.itemwidth + constants.itemGap * 2;
+    var textGap = container.itemwidth + constants.itemGap * 2;
     svgTextUtils.positionText(textEl, textGap, 0);
 
     if(isEditable) {
         textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name})
-            .call(textLayout, g, gd, opts)
+            .call(textLayout, g, gd, container)
             .on('edit', function(newName) {
                 this.text(ensureLength(newName, maxNameLength))
-                    .call(textLayout, g, gd, opts);
+                    .call(textLayout, g, gd, container);
 
                 var fullInput = legendItem.trace._fullInput || {};
                 var update = {};
@@ -436,7 +436,7 @@ function drawTexts(g, gd, opts) {
                 return Registry.call('_guiRestyle', gd, update, trace.index);
             });
     } else {
-        textLayout(textEl, g, gd, opts);
+        textLayout(textEl, g, gd, container);
     }
 }
 
@@ -492,25 +492,25 @@ function setupTraceToggle(g, gd) {
     });
 }
 
-function textLayout(s, g, gd, opts, aTitle) {
-    if(opts._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
+function textLayout(s, g, gd, container, aTitle) {
+    if(container._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
     svgTextUtils.convertToTspans(s, gd, function() {
-        computeTextDimensions(g, gd, opts, aTitle);
+        computeTextDimensions(g, gd, container, aTitle);
     });
 }
 
-function computeTextDimensions(g, gd, opts, aTitle) {
+function computeTextDimensions(g, gd, container, aTitle) {
     var legendItem = g.data()[0][0];
-    if(!opts._inHover && legendItem && !legendItem.trace.showlegend) {
+    if(!container._inHover && legendItem && !legendItem.trace.showlegend) {
         g.remove();
         return;
     }
 
     var mathjaxGroup = g.select('g[class*=math-group]');
     var mathjaxNode = mathjaxGroup.node();
-    if(!opts) opts = gd._fullLayout.legend;
-    var bw = opts.borderwidth;
-    var lineHeight = (aTitle === MAIN_TITLE ? opts.title : opts).font.size * LINE_SPACING;
+    if(!container) container = gd._fullLayout.legend;
+    var bw = container.borderwidth;
+    var lineHeight = (aTitle === MAIN_TITLE ? container.title : container).font.size * LINE_SPACING;
     var height, width;
 
     if(mathjaxNode) {
@@ -543,15 +543,15 @@ function computeTextDimensions(g, gd, opts, aTitle) {
             );
         } else { // legend item
             svgTextUtils.positionText(textEl,
-                opts.itemwidth + constants.itemGap * 2,
+                container.itemwidth + constants.itemGap * 2,
                 -lineHeight * ((textLines - 1) / 2 - 0.3)
             );
         }
     }
 
     if(aTitle === MAIN_TITLE) {
-        opts._titleWidth = width;
-        opts._titleHeight = height;
+        container._titleWidth = width;
+        container._titleHeight = height;
     } else { // legend item
         legendItem.lineHeight = lineHeight;
         legendItem.height = Math.max(height, 16) + 3;
@@ -559,17 +559,17 @@ function computeTextDimensions(g, gd, opts, aTitle) {
     }
 }
 
-function getTitleSize(opts) {
+function getTitleSize(container) {
     var w = 0;
     var h = 0;
 
-    var side = opts.title.side;
+    var side = container.title.side;
     if(side) {
         if(side.indexOf('left') !== -1) {
-            w = opts._titleWidth;
+            w = container._titleWidth;
         }
         if(side.indexOf('top') !== -1) {
-            h = opts._titleHeight;
+            h = container._titleHeight;
         }
     }
 
@@ -586,70 +586,70 @@ function getTitleSize(opts) {
  *  - _width: legend width
  *  - _maxWidth (for orientation:h only): maximum width before starting new row
  */
-function computeLegendDimensions(gd, groups, traces, opts) {
+function computeLegendDimensions(gd, groups, traces, container) {
     var fullLayout = gd._fullLayout;
-    if(!opts) opts = fullLayout.legend;
+    if(!container) container = fullLayout.legend;
     var gs = fullLayout._size;
 
-    var isVertical = helpers.isVertical(opts);
-    var isGrouped = helpers.isGrouped(opts);
+    var isVertical = helpers.isVertical(container);
+    var isGrouped = helpers.isGrouped(container);
 
-    var bw = opts.borderwidth;
+    var bw = container.borderwidth;
     var bw2 = 2 * bw;
     var itemGap = constants.itemGap;
-    var textGap = opts.itemwidth + itemGap * 2;
+    var textGap = container.itemwidth + itemGap * 2;
     var endPad = 2 * (bw + itemGap);
 
-    var yanchor = getYanchor(opts);
-    var isBelowPlotArea = opts.y < 0 || (opts.y === 0 && yanchor === 'top');
-    var isAbovePlotArea = opts.y > 1 || (opts.y === 1 && yanchor === 'bottom');
+    var yanchor = getYanchor(container);
+    var isBelowPlotArea = container.y < 0 || (container.y === 0 && yanchor === 'top');
+    var isAbovePlotArea = container.y > 1 || (container.y === 1 && yanchor === 'bottom');
 
-    var traceGroupGap = opts.tracegroupgap;
+    var traceGroupGap = container.tracegroupgap;
 
     // - if below/above plot area, give it the maximum potential margin-push value
     // - otherwise, extend the height of the plot area
-    opts._maxHeight = Math.max(
+    container._maxHeight = Math.max(
         (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h,
         30
     );
 
     var toggleRectWidth = 0;
-    opts._width = 0;
-    opts._height = 0;
-    var titleSize = getTitleSize(opts);
+    container._width = 0;
+    container._height = 0;
+    var titleSize = getTitleSize(container);
 
     if(isVertical) {
         traces.each(function(d) {
             var h = d[0].height;
             Drawing.setTranslate(this,
                 bw + titleSize[0],
-                bw + titleSize[1] + opts._height + h / 2 + itemGap
+                bw + titleSize[1] + container._height + h / 2 + itemGap
             );
-            opts._height += h;
-            opts._width = Math.max(opts._width, d[0].width);
+            container._height += h;
+            container._width = Math.max(container._width, d[0].width);
         });
 
-        toggleRectWidth = textGap + opts._width;
-        opts._width += itemGap + textGap + bw2;
-        opts._height += endPad;
+        toggleRectWidth = textGap + container._width;
+        container._width += itemGap + textGap + bw2;
+        container._height += endPad;
 
         if(isGrouped) {
             groups.each(function(d, i) {
-                Drawing.setTranslate(this, 0, i * opts.tracegroupgap);
+                Drawing.setTranslate(this, 0, i * container.tracegroupgap);
             });
-            opts._height += (opts._lgroupsLength - 1) * opts.tracegroupgap;
+            container._height += (container._lgroupsLength - 1) * container.tracegroupgap;
         }
     } else {
-        var xanchor = getXanchor(opts);
-        var isLeftOfPlotArea = opts.x < 0 || (opts.x === 0 && xanchor === 'right');
-        var isRightOfPlotArea = opts.x > 1 || (opts.x === 1 && xanchor === 'left');
+        var xanchor = getXanchor(container);
+        var isLeftOfPlotArea = container.x < 0 || (container.x === 0 && xanchor === 'right');
+        var isRightOfPlotArea = container.x > 1 || (container.x === 1 && xanchor === 'left');
         var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea;
         var hw = fullLayout.width / 2;
 
         // - if placed within x-margins, extend the width of the plot area
         // - else if below/above plot area and anchored in the margin, extend to opposite margin,
         // - otherwise give it the maximum potential margin-push value
-        opts._maxWidth = Math.max(
+        container._maxWidth = Math.max(
             isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) :
             isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) :
             gs.w,
@@ -685,7 +685,7 @@ function computeLegendDimensions(gd, groups, traces, opts) {
 
                 var next = maxWidthInGroup + itemGap;
 
-                if((next + bw + groupOffsetX) > opts._maxWidth) {
+                if((next + bw + groupOffsetX) > container._maxWidth) {
                     maxRowWidth = Math.max(maxRowWidth, groupOffsetX);
                     groupOffsetX = 0;
                     groupOffsetY += maxGroupHeightInRow + traceGroupGap;
@@ -697,11 +697,11 @@ function computeLegendDimensions(gd, groups, traces, opts) {
                 groupOffsetX += next;
             });
 
-            opts._width = Math.max(maxRowWidth, groupOffsetX) + bw;
-            opts._height = groupOffsetY + maxGroupHeightInRow + endPad;
+            container._width = Math.max(maxRowWidth, groupOffsetX) + bw;
+            container._height = groupOffsetY + maxGroupHeightInRow + endPad;
         } else {
             var nTraces = traces.size();
-            var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < opts._maxWidth;
+            var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < container._maxWidth;
 
             var maxItemHeightInRow = 0;
             var offsetX = 0;
@@ -712,11 +712,11 @@ function computeLegendDimensions(gd, groups, traces, opts) {
                 var w = textGap + d[0].width;
                 var next = (oneRowLegend ? w : maxItemWidth) + itemGap;
 
-                if((next + bw + offsetX - itemGap) >= opts._maxWidth) {
+                if((next + bw + offsetX - itemGap) >= container._maxWidth) {
                     maxRowWidth = Math.max(maxRowWidth, rowWidth);
                     offsetX = 0;
                     offsetY += maxItemHeightInRow;
-                    opts._height += maxItemHeightInRow;
+                    container._height += maxItemHeightInRow;
                     maxItemHeightInRow = 0;
                 }
 
@@ -731,30 +731,30 @@ function computeLegendDimensions(gd, groups, traces, opts) {
             });
 
             if(oneRowLegend) {
-                opts._width = offsetX + bw2;
-                opts._height = maxItemHeightInRow + endPad;
+                container._width = offsetX + bw2;
+                container._height = maxItemHeightInRow + endPad;
             } else {
-                opts._width = Math.max(maxRowWidth, rowWidth) + bw2;
-                opts._height += maxItemHeightInRow + endPad;
+                container._width = Math.max(maxRowWidth, rowWidth) + bw2;
+                container._height += maxItemHeightInRow + endPad;
             }
         }
     }
 
-    opts._width = Math.ceil(
+    container._width = Math.ceil(
         Math.max(
-            opts._width + titleSize[0],
-            opts._titleWidth + 2 * (bw + constants.titlePad)
+            container._width + titleSize[0],
+            container._titleWidth + 2 * (bw + constants.titlePad)
         )
     );
 
-    opts._height = Math.ceil(
+    container._height = Math.ceil(
         Math.max(
-            opts._height + titleSize[1],
-            opts._titleHeight + 2 * (bw + constants.itemGap)
+            container._height + titleSize[1],
+            container._titleHeight + 2 * (bw + constants.itemGap)
         )
     );
 
-    opts._effHeight = Math.min(opts._height, opts._maxHeight);
+    container._effHeight = Math.min(container._height, container._maxHeight);
 
     var edits = gd._context.edits;
     var isEditable = edits.legendText || edits.legendPosition;
@@ -769,28 +769,28 @@ function computeLegendDimensions(gd, groups, traces, opts) {
 
 function expandMargin(gd) {
     var fullLayout = gd._fullLayout;
-    var opts = fullLayout.legend;
-    var xanchor = getXanchor(opts);
-    var yanchor = getYanchor(opts);
+    var container = fullLayout.legend;
+    var xanchor = getXanchor(container);
+    var yanchor = getYanchor(container);
 
     return Plots.autoMargin(gd, 'legend', {
-        x: opts.x,
-        y: opts.y,
-        l: opts._width * (FROM_TL[xanchor]),
-        r: opts._width * (FROM_BR[xanchor]),
-        b: opts._effHeight * (FROM_BR[yanchor]),
-        t: opts._effHeight * (FROM_TL[yanchor])
+        x: container.x,
+        y: container.y,
+        l: container._width * (FROM_TL[xanchor]),
+        r: container._width * (FROM_BR[xanchor]),
+        b: container._effHeight * (FROM_BR[yanchor]),
+        t: container._effHeight * (FROM_TL[yanchor])
     });
 }
 
-function getXanchor(opts) {
-    return Lib.isRightAnchor(opts) ? 'right' :
-        Lib.isCenterAnchor(opts) ? 'center' :
+function getXanchor(container) {
+    return Lib.isRightAnchor(container) ? 'right' :
+        Lib.isCenterAnchor(container) ? 'center' :
         'left';
 }
 
-function getYanchor(opts) {
-    return Lib.isBottomAnchor(opts) ? 'bottom' :
-        Lib.isMiddleAnchor(opts) ? 'middle' :
+function getYanchor(container) {
+    return Lib.isBottomAnchor(container) ? 'bottom' :
+        Lib.isMiddleAnchor(container) ? 'middle' :
         'top';
 }

From 61d01e67165dea1dc295a8b6d01a955a480af2ec Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 5 Apr 2021 15:26:36 -0400
Subject: [PATCH 05/10] refactor if without not

---
 src/components/legend/draw.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index 3f865788fb1..e7297b74196 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -30,12 +30,12 @@ module.exports = function draw(gd, container) {
     var layer;
 
     // Check whether this is the main legend (ie. called without any container)
-    if(!container) {
-        container = fullLayout.legend || {};
-        layer = fullLayout._infolayer;
-    } else {
+    if(container) {
         layer = container.layer;
         clipId += '-hover';
+    } else {
+        container = fullLayout.legend || {};
+        layer = fullLayout._infolayer;
     }
 
     var inHover = !!container._inHover;

From f717986d18147417c875af830f0b6d9d3b17dacf Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 5 Apr 2021 15:27:36 -0400
Subject: [PATCH 06/10] let _inHover handle the case of unified hover

---
 src/components/legend/draw.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index e7297b74196..1c8965a9601 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -30,7 +30,7 @@ module.exports = function draw(gd, container) {
     var layer;
 
     // Check whether this is the main legend (ie. called without any container)
-    if(container) {
+    if(container && container._inHover) {
         layer = container.layer;
         clipId += '-hover';
     } else {

From 0a4fd7397e6a16b8bd6f1561dd82a07913a4b175 Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 5 Apr 2021 16:13:22 -0400
Subject: [PATCH 07/10] more of let _inHover handle unified hover in legend
 draw

---
 src/components/legend/draw.js | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index 1c8965a9601..3d3a8691709 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -30,7 +30,8 @@ module.exports = function draw(gd, container) {
     var layer;
 
     // Check whether this is the main legend (ie. called without any container)
-    if(container && container._inHover) {
+    var inHover = container && container._inHover;
+    if(inHover) {
         layer = container.layer;
         clipId += '-hover';
     } else {
@@ -38,8 +39,6 @@ module.exports = function draw(gd, container) {
         layer = fullLayout._infolayer;
     }
 
-    var inHover = !!container._inHover;
-
     if(!layer) return;
 
     if(!gd._legendMouseDownTime) gd._legendMouseDownTime = 0;

From 9c9ee75146a02f865ee412efaa3ee9003d9d3dbd Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 5 Apr 2021 16:25:54 -0400
Subject: [PATCH 08/10] ensure container passed into legend _draw function

---
 src/components/legend/draw.js | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index 3d3a8691709..b5755bd992a 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -24,18 +24,21 @@ var helpers = require('./helpers');
 
 var MAIN_TITLE = 1;
 
-module.exports = function draw(gd, container) {
+module.exports = function draw(gd, opts) {
+    if(!opts) opts = gd._fullLayout.legend || {};
+    return _draw(gd, opts);
+};
+
+function _draw(gd, container) {
     var fullLayout = gd._fullLayout;
     var clipId = 'legend' + fullLayout._uid;
     var layer;
 
-    // Check whether this is the main legend (ie. called without any container)
-    var inHover = container && container._inHover;
+    var inHover = container._inHover;
     if(inHover) {
         layer = container.layer;
         clipId += '-hover';
     } else {
-        container = fullLayout.legend || {};
         layer = fullLayout._infolayer;
     }
 
@@ -342,7 +345,7 @@ module.exports = function draw(gd, container) {
                 });
             }
         }], gd);
-};
+}
 
 function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
     var trace = legendItem.data()[0][0].trace;

From 7337cb3e4522dd3445f4fd89527f50bad7ebc251 Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 5 Apr 2021 16:39:43 -0400
Subject: [PATCH 09/10] rename legendOpts to mockLegend

---
 src/components/fx/hover.js | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js
index 76d00608489..a8738cf3f6e 100644
--- a/src/components/fx/hover.js
+++ b/src/components/fx/hover.js
@@ -1010,10 +1010,10 @@ function createHoverText(hoverData, opts, gd) {
         };
         var mockLayoutOut = {};
         legendSupplyDefaults(mockLayoutIn, mockLayoutOut, gd._fullData);
-        var legendOpts = mockLayoutOut.legend;
+        var mockLegend = mockLayoutOut.legend;
 
         // prepare items for the legend
-        legendOpts.entries = [];
+        mockLegend.entries = [];
         for(var j = 0; j < hoverData.length; j++) {
             var texts = getHoverLabelText(hoverData[j], true, hovermode, fullLayout, t0);
             var text = texts[0];
@@ -1039,14 +1039,14 @@ function createHoverText(hoverData, opts, gd) {
             }
             pt._distinct = true;
 
-            legendOpts.entries.push([pt]);
+            mockLegend.entries.push([pt]);
         }
-        legendOpts.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;});
-        legendOpts.layer = container;
+        mockLegend.entries.sort(function(a, b) { return a[0].trace.index - b[0].trace.index;});
+        mockLegend.layer = container;
 
         // Draw unified hover label
-        legendOpts._inHover = true;
-        legendDraw(gd, legendOpts);
+        mockLegend._inHover = true;
+        legendDraw(gd, mockLegend);
 
         // Position the hover
         var ly = Lib.mean(hoverData.map(function(c) {return (c.y0 + c.y1) / 2;}));

From ab233979d24b285565016e9cf209ded2c43e2acb Mon Sep 17 00:00:00 2001
From: archmoj <mojtaba@plot.ly>
Date: Mon, 5 Apr 2021 16:49:19 -0400
Subject: [PATCH 10/10] rename container to legendObj i.e. to avoid conflict
 with legend.container

---
 src/components/legend/draw.js | 244 +++++++++++++++++-----------------
 1 file changed, 122 insertions(+), 122 deletions(-)

diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js
index b5755bd992a..d5b614a3506 100644
--- a/src/components/legend/draw.js
+++ b/src/components/legend/draw.js
@@ -29,14 +29,14 @@ module.exports = function draw(gd, opts) {
     return _draw(gd, opts);
 };
 
-function _draw(gd, container) {
+function _draw(gd, legendObj) {
     var fullLayout = gd._fullLayout;
     var clipId = 'legend' + fullLayout._uid;
     var layer;
 
-    var inHover = container._inHover;
+    var inHover = legendObj._inHover;
     if(inHover) {
-        layer = container.layer;
+        layer = legendObj.layer;
         clipId += '-hover';
     } else {
         layer = fullLayout._infolayer;
@@ -49,10 +49,10 @@ function _draw(gd, container) {
     var legendData;
     if(!inHover) {
         if(!gd.calcdata) return;
-        legendData = fullLayout.showlegend && getLegendData(gd.calcdata, container);
+        legendData = fullLayout.showlegend && getLegendData(gd.calcdata, legendObj);
     } else {
-        if(!container.entries) return;
-        legendData = getLegendData(container.entries, container);
+        if(!legendObj.entries) return;
+        legendData = getLegendData(legendObj.entries, legendObj);
     }
 
     var hiddenSlices = fullLayout.hiddenlabels || [];
@@ -74,22 +74,22 @@ function _draw(gd, container) {
     var bg = Lib.ensureSingle(legend, 'rect', 'bg', function(s) {
         s.attr('shape-rendering', 'crispEdges');
     });
-    bg.call(Color.stroke, container.bordercolor)
-        .call(Color.fill, container.bgcolor)
-        .style('stroke-width', container.borderwidth + 'px');
+    bg.call(Color.stroke, legendObj.bordercolor)
+        .call(Color.fill, legendObj.bgcolor)
+        .style('stroke-width', legendObj.borderwidth + 'px');
 
     var scrollBox = Lib.ensureSingle(legend, 'g', 'scrollbox');
 
-    var title = container.title;
-    container._titleWidth = 0;
-    container._titleHeight = 0;
+    var title = legendObj.title;
+    legendObj._titleWidth = 0;
+    legendObj._titleHeight = 0;
     if(title.text) {
         var titleEl = Lib.ensureSingle(scrollBox, 'text', 'legendtitletext');
         titleEl.attr('text-anchor', 'start')
             .call(Drawing.font, title.font)
             .text(title.text);
 
-        textLayout(titleEl, scrollBox, gd, container, MAIN_TITLE); // handle mathjax or multi-line text and compute title height
+        textLayout(titleEl, scrollBox, gd, legendObj, MAIN_TITLE); // handle mathjax or multi-line text and compute title height
     } else {
         scrollBox.selectAll('.legendtitletext').remove();
     }
@@ -115,13 +115,13 @@ function _draw(gd, container) {
             return trace.visible === 'legendonly' ? 0.5 : 1;
         }
     })
-    .each(function() { d3.select(this).call(drawTexts, gd, container); })
-    .call(style, gd, container)
+    .each(function() { d3.select(this).call(drawTexts, gd, legendObj); })
+    .call(style, gd, legendObj)
     .each(function() { if(!inHover) d3.select(this).call(setupTraceToggle, gd); });
 
     Lib.syncOrAsync([
         Plots.previousPromises,
-        function() { return computeLegendDimensions(gd, groups, traces, container); },
+        function() { return computeLegendDimensions(gd, groups, traces, legendObj); },
         function() {
             // IF expandMargin return a Promise (which is truthy),
             // we're under a doAutoMargin redraw, so we don't have to
@@ -129,17 +129,17 @@ function _draw(gd, container) {
             if(!inHover && expandMargin(gd)) return;
 
             var gs = fullLayout._size;
-            var bw = container.borderwidth;
+            var bw = legendObj.borderwidth;
 
-            var lx = gs.l + gs.w * container.x - FROM_TL[getXanchor(container)] * container._width;
-            var ly = gs.t + gs.h * (1 - container.y) - FROM_TL[getYanchor(container)] * container._effHeight;
+            var lx = gs.l + gs.w * legendObj.x - FROM_TL[getXanchor(legendObj)] * legendObj._width;
+            var ly = gs.t + gs.h * (1 - legendObj.y) - FROM_TL[getYanchor(legendObj)] * legendObj._effHeight;
 
             if(!inHover && fullLayout.margin.autoexpand) {
                 var lx0 = lx;
                 var ly0 = ly;
 
-                lx = Lib.constrain(lx, 0, fullLayout.width - container._width);
-                ly = Lib.constrain(ly, 0, fullLayout.height - container._effHeight);
+                lx = Lib.constrain(lx, 0, fullLayout.width - legendObj._width);
+                ly = Lib.constrain(ly, 0, fullLayout.height - legendObj._effHeight);
 
                 if(lx !== lx0) {
                     Lib.log('Constrain legend.x to make legend fit inside graph');
@@ -157,15 +157,15 @@ function _draw(gd, container) {
             scrollBar.on('.drag', null);
             legend.on('wheel', null);
 
-            if(inHover || container._height <= container._maxHeight || gd._context.staticPlot) {
+            if(inHover || legendObj._height <= legendObj._maxHeight || gd._context.staticPlot) {
                 // if scrollbar should not be shown.
-                var height = container._effHeight;
+                var height = legendObj._effHeight;
 
                 // if unified hover, let it be its full size
-                if(inHover) height = container._height;
+                if(inHover) height = legendObj._height;
 
                 bg.attr({
-                    width: container._width - bw,
+                    width: legendObj._width - bw,
                     height: height - bw,
                     x: bw / 2,
                     y: bw / 2
@@ -174,7 +174,7 @@ function _draw(gd, container) {
                 Drawing.setTranslate(scrollBox, 0, 0);
 
                 clipPath.select('rect').attr({
-                    width: container._width - 2 * bw,
+                    width: legendObj._width - 2 * bw,
                     height: height - 2 * bw,
                     x: bw,
                     y: bw
@@ -183,36 +183,36 @@ function _draw(gd, container) {
                 Drawing.setClipUrl(scrollBox, clipId, gd);
 
                 Drawing.setRect(scrollBar, 0, 0, 0, 0);
-                delete container._scrollY;
+                delete legendObj._scrollY;
             } else {
                 var scrollBarHeight = Math.max(constants.scrollBarMinHeight,
-                    container._effHeight * container._effHeight / container._height);
-                var scrollBarYMax = container._effHeight -
+                    legendObj._effHeight * legendObj._effHeight / legendObj._height);
+                var scrollBarYMax = legendObj._effHeight -
                     scrollBarHeight -
                     2 * constants.scrollBarMargin;
-                var scrollBoxYMax = container._height - container._effHeight;
+                var scrollBoxYMax = legendObj._height - legendObj._effHeight;
                 var scrollRatio = scrollBarYMax / scrollBoxYMax;
 
-                var scrollBoxY = Math.min(container._scrollY || 0, scrollBoxYMax);
+                var scrollBoxY = Math.min(legendObj._scrollY || 0, scrollBoxYMax);
 
                 // increase the background and clip-path width
                 // by the scrollbar width and margin
                 bg.attr({
-                    width: container._width -
+                    width: legendObj._width -
                         2 * bw +
                         constants.scrollBarWidth +
                         constants.scrollBarMargin,
-                    height: container._effHeight - bw,
+                    height: legendObj._effHeight - bw,
                     x: bw / 2,
                     y: bw / 2
                 });
 
                 clipPath.select('rect').attr({
-                    width: container._width -
+                    width: legendObj._width -
                         2 * bw +
                         constants.scrollBarWidth +
                         constants.scrollBarMargin,
-                    height: container._effHeight - 2 * bw,
+                    height: legendObj._effHeight - 2 * bw,
                     x: bw,
                     y: bw + scrollBoxY
                 });
@@ -224,7 +224,7 @@ function _draw(gd, container) {
                 // scroll legend by mousewheel or touchpad swipe up/down
                 legend.on('wheel', function() {
                     scrollBoxY = Lib.constrain(
-                        container._scrollY +
+                        legendObj._scrollY +
                             ((d3.event.deltaY / scrollBarYMax) * scrollBoxYMax),
                         0, scrollBoxYMax);
                     scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio);
@@ -290,12 +290,12 @@ function _draw(gd, container) {
             }
 
             function scrollHandler(scrollBoxY, scrollBarHeight, scrollRatio) {
-                container._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
+                legendObj._scrollY = gd._fullLayout.legend._scrollY = scrollBoxY;
                 Drawing.setTranslate(scrollBox, 0, -scrollBoxY);
 
                 Drawing.setRect(
                     scrollBar,
-                    container._width,
+                    legendObj._width,
                     constants.scrollBarMargin + scrollBoxY * scrollRatio,
                     constants.scrollBarWidth,
                     scrollBarHeight
@@ -322,8 +322,8 @@ function _draw(gd, container) {
 
                         Drawing.setTranslate(legend, newX, newY);
 
-                        xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, container.xanchor);
-                        yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, container.yanchor);
+                        xf = dragElement.align(newX, 0, gs.l, gs.l + gs.w, legendObj.xanchor);
+                        yf = dragElement.align(newY, 0, gs.t + gs.h, gs.t, legendObj.yanchor);
                     },
                     doneFn: function() {
                         if(xf !== undefined && yf !== undefined) {
@@ -386,15 +386,15 @@ function clickOrDoubleClick(gd, legend, legendItem, numClicks, evt) {
     }
 }
 
-function drawTexts(g, gd, container) {
+function drawTexts(g, gd, legendObj) {
     var legendItem = g.data()[0][0];
     var trace = legendItem.trace;
     var isPieLike = Registry.traceIs(trace, 'pie-like');
-    var isEditable = !container._inHover && gd._context.edits.legendText && !isPieLike;
-    var maxNameLength = container._maxNameLength;
+    var isEditable = !legendObj._inHover && gd._context.edits.legendText && !isPieLike;
+    var maxNameLength = legendObj._maxNameLength;
 
     var name;
-    if(!container.entries) {
+    if(!legendObj.entries) {
         name = isPieLike ? legendItem.label : trace.name;
         if(trace._meta) {
             name = Lib.templateString(name, trace._meta);
@@ -406,18 +406,18 @@ function drawTexts(g, gd, container) {
     var textEl = Lib.ensureSingle(g, 'text', 'legendtext');
 
     textEl.attr('text-anchor', 'start')
-        .call(Drawing.font, container.font)
+        .call(Drawing.font, legendObj.font)
         .text(isEditable ? ensureLength(name, maxNameLength) : name);
 
-    var textGap = container.itemwidth + constants.itemGap * 2;
+    var textGap = legendObj.itemwidth + constants.itemGap * 2;
     svgTextUtils.positionText(textEl, textGap, 0);
 
     if(isEditable) {
         textEl.call(svgTextUtils.makeEditable, {gd: gd, text: name})
-            .call(textLayout, g, gd, container)
+            .call(textLayout, g, gd, legendObj)
             .on('edit', function(newName) {
                 this.text(ensureLength(newName, maxNameLength))
-                    .call(textLayout, g, gd, container);
+                    .call(textLayout, g, gd, legendObj);
 
                 var fullInput = legendItem.trace._fullInput || {};
                 var update = {};
@@ -438,7 +438,7 @@ function drawTexts(g, gd, container) {
                 return Registry.call('_guiRestyle', gd, update, trace.index);
             });
     } else {
-        textLayout(textEl, g, gd, container);
+        textLayout(textEl, g, gd, legendObj);
     }
 }
 
@@ -494,25 +494,25 @@ function setupTraceToggle(g, gd) {
     });
 }
 
-function textLayout(s, g, gd, container, aTitle) {
-    if(container._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
+function textLayout(s, g, gd, legendObj, aTitle) {
+    if(legendObj._inHover) s.attr('data-notex', true); // do not process MathJax for unified hover
     svgTextUtils.convertToTspans(s, gd, function() {
-        computeTextDimensions(g, gd, container, aTitle);
+        computeTextDimensions(g, gd, legendObj, aTitle);
     });
 }
 
-function computeTextDimensions(g, gd, container, aTitle) {
+function computeTextDimensions(g, gd, legendObj, aTitle) {
     var legendItem = g.data()[0][0];
-    if(!container._inHover && legendItem && !legendItem.trace.showlegend) {
+    if(!legendObj._inHover && legendItem && !legendItem.trace.showlegend) {
         g.remove();
         return;
     }
 
     var mathjaxGroup = g.select('g[class*=math-group]');
     var mathjaxNode = mathjaxGroup.node();
-    if(!container) container = gd._fullLayout.legend;
-    var bw = container.borderwidth;
-    var lineHeight = (aTitle === MAIN_TITLE ? container.title : container).font.size * LINE_SPACING;
+    if(!legendObj) legendObj = gd._fullLayout.legend;
+    var bw = legendObj.borderwidth;
+    var lineHeight = (aTitle === MAIN_TITLE ? legendObj.title : legendObj).font.size * LINE_SPACING;
     var height, width;
 
     if(mathjaxNode) {
@@ -545,15 +545,15 @@ function computeTextDimensions(g, gd, container, aTitle) {
             );
         } else { // legend item
             svgTextUtils.positionText(textEl,
-                container.itemwidth + constants.itemGap * 2,
+                legendObj.itemwidth + constants.itemGap * 2,
                 -lineHeight * ((textLines - 1) / 2 - 0.3)
             );
         }
     }
 
     if(aTitle === MAIN_TITLE) {
-        container._titleWidth = width;
-        container._titleHeight = height;
+        legendObj._titleWidth = width;
+        legendObj._titleHeight = height;
     } else { // legend item
         legendItem.lineHeight = lineHeight;
         legendItem.height = Math.max(height, 16) + 3;
@@ -561,17 +561,17 @@ function computeTextDimensions(g, gd, container, aTitle) {
     }
 }
 
-function getTitleSize(container) {
+function getTitleSize(legendObj) {
     var w = 0;
     var h = 0;
 
-    var side = container.title.side;
+    var side = legendObj.title.side;
     if(side) {
         if(side.indexOf('left') !== -1) {
-            w = container._titleWidth;
+            w = legendObj._titleWidth;
         }
         if(side.indexOf('top') !== -1) {
-            h = container._titleHeight;
+            h = legendObj._titleHeight;
         }
     }
 
@@ -588,70 +588,70 @@ function getTitleSize(container) {
  *  - _width: legend width
  *  - _maxWidth (for orientation:h only): maximum width before starting new row
  */
-function computeLegendDimensions(gd, groups, traces, container) {
+function computeLegendDimensions(gd, groups, traces, legendObj) {
     var fullLayout = gd._fullLayout;
-    if(!container) container = fullLayout.legend;
+    if(!legendObj) legendObj = fullLayout.legend;
     var gs = fullLayout._size;
 
-    var isVertical = helpers.isVertical(container);
-    var isGrouped = helpers.isGrouped(container);
+    var isVertical = helpers.isVertical(legendObj);
+    var isGrouped = helpers.isGrouped(legendObj);
 
-    var bw = container.borderwidth;
+    var bw = legendObj.borderwidth;
     var bw2 = 2 * bw;
     var itemGap = constants.itemGap;
-    var textGap = container.itemwidth + itemGap * 2;
+    var textGap = legendObj.itemwidth + itemGap * 2;
     var endPad = 2 * (bw + itemGap);
 
-    var yanchor = getYanchor(container);
-    var isBelowPlotArea = container.y < 0 || (container.y === 0 && yanchor === 'top');
-    var isAbovePlotArea = container.y > 1 || (container.y === 1 && yanchor === 'bottom');
+    var yanchor = getYanchor(legendObj);
+    var isBelowPlotArea = legendObj.y < 0 || (legendObj.y === 0 && yanchor === 'top');
+    var isAbovePlotArea = legendObj.y > 1 || (legendObj.y === 1 && yanchor === 'bottom');
 
-    var traceGroupGap = container.tracegroupgap;
+    var traceGroupGap = legendObj.tracegroupgap;
 
     // - if below/above plot area, give it the maximum potential margin-push value
     // - otherwise, extend the height of the plot area
-    container._maxHeight = Math.max(
+    legendObj._maxHeight = Math.max(
         (isBelowPlotArea || isAbovePlotArea) ? fullLayout.height / 2 : gs.h,
         30
     );
 
     var toggleRectWidth = 0;
-    container._width = 0;
-    container._height = 0;
-    var titleSize = getTitleSize(container);
+    legendObj._width = 0;
+    legendObj._height = 0;
+    var titleSize = getTitleSize(legendObj);
 
     if(isVertical) {
         traces.each(function(d) {
             var h = d[0].height;
             Drawing.setTranslate(this,
                 bw + titleSize[0],
-                bw + titleSize[1] + container._height + h / 2 + itemGap
+                bw + titleSize[1] + legendObj._height + h / 2 + itemGap
             );
-            container._height += h;
-            container._width = Math.max(container._width, d[0].width);
+            legendObj._height += h;
+            legendObj._width = Math.max(legendObj._width, d[0].width);
         });
 
-        toggleRectWidth = textGap + container._width;
-        container._width += itemGap + textGap + bw2;
-        container._height += endPad;
+        toggleRectWidth = textGap + legendObj._width;
+        legendObj._width += itemGap + textGap + bw2;
+        legendObj._height += endPad;
 
         if(isGrouped) {
             groups.each(function(d, i) {
-                Drawing.setTranslate(this, 0, i * container.tracegroupgap);
+                Drawing.setTranslate(this, 0, i * legendObj.tracegroupgap);
             });
-            container._height += (container._lgroupsLength - 1) * container.tracegroupgap;
+            legendObj._height += (legendObj._lgroupsLength - 1) * legendObj.tracegroupgap;
         }
     } else {
-        var xanchor = getXanchor(container);
-        var isLeftOfPlotArea = container.x < 0 || (container.x === 0 && xanchor === 'right');
-        var isRightOfPlotArea = container.x > 1 || (container.x === 1 && xanchor === 'left');
+        var xanchor = getXanchor(legendObj);
+        var isLeftOfPlotArea = legendObj.x < 0 || (legendObj.x === 0 && xanchor === 'right');
+        var isRightOfPlotArea = legendObj.x > 1 || (legendObj.x === 1 && xanchor === 'left');
         var isBeyondPlotAreaY = isAbovePlotArea || isBelowPlotArea;
         var hw = fullLayout.width / 2;
 
         // - if placed within x-margins, extend the width of the plot area
         // - else if below/above plot area and anchored in the margin, extend to opposite margin,
         // - otherwise give it the maximum potential margin-push value
-        container._maxWidth = Math.max(
+        legendObj._maxWidth = Math.max(
             isLeftOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'left') ? gs.l + gs.w : hw) :
             isRightOfPlotArea ? ((isBeyondPlotAreaY && xanchor === 'right') ? gs.r + gs.w : hw) :
             gs.w,
@@ -687,7 +687,7 @@ function computeLegendDimensions(gd, groups, traces, container) {
 
                 var next = maxWidthInGroup + itemGap;
 
-                if((next + bw + groupOffsetX) > container._maxWidth) {
+                if((next + bw + groupOffsetX) > legendObj._maxWidth) {
                     maxRowWidth = Math.max(maxRowWidth, groupOffsetX);
                     groupOffsetX = 0;
                     groupOffsetY += maxGroupHeightInRow + traceGroupGap;
@@ -699,11 +699,11 @@ function computeLegendDimensions(gd, groups, traces, container) {
                 groupOffsetX += next;
             });
 
-            container._width = Math.max(maxRowWidth, groupOffsetX) + bw;
-            container._height = groupOffsetY + maxGroupHeightInRow + endPad;
+            legendObj._width = Math.max(maxRowWidth, groupOffsetX) + bw;
+            legendObj._height = groupOffsetY + maxGroupHeightInRow + endPad;
         } else {
             var nTraces = traces.size();
-            var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < container._maxWidth;
+            var oneRowLegend = (combinedItemWidth + bw2 + (nTraces - 1) * itemGap) < legendObj._maxWidth;
 
             var maxItemHeightInRow = 0;
             var offsetX = 0;
@@ -714,11 +714,11 @@ function computeLegendDimensions(gd, groups, traces, container) {
                 var w = textGap + d[0].width;
                 var next = (oneRowLegend ? w : maxItemWidth) + itemGap;
 
-                if((next + bw + offsetX - itemGap) >= container._maxWidth) {
+                if((next + bw + offsetX - itemGap) >= legendObj._maxWidth) {
                     maxRowWidth = Math.max(maxRowWidth, rowWidth);
                     offsetX = 0;
                     offsetY += maxItemHeightInRow;
-                    container._height += maxItemHeightInRow;
+                    legendObj._height += maxItemHeightInRow;
                     maxItemHeightInRow = 0;
                 }
 
@@ -733,30 +733,30 @@ function computeLegendDimensions(gd, groups, traces, container) {
             });
 
             if(oneRowLegend) {
-                container._width = offsetX + bw2;
-                container._height = maxItemHeightInRow + endPad;
+                legendObj._width = offsetX + bw2;
+                legendObj._height = maxItemHeightInRow + endPad;
             } else {
-                container._width = Math.max(maxRowWidth, rowWidth) + bw2;
-                container._height += maxItemHeightInRow + endPad;
+                legendObj._width = Math.max(maxRowWidth, rowWidth) + bw2;
+                legendObj._height += maxItemHeightInRow + endPad;
             }
         }
     }
 
-    container._width = Math.ceil(
+    legendObj._width = Math.ceil(
         Math.max(
-            container._width + titleSize[0],
-            container._titleWidth + 2 * (bw + constants.titlePad)
+            legendObj._width + titleSize[0],
+            legendObj._titleWidth + 2 * (bw + constants.titlePad)
         )
     );
 
-    container._height = Math.ceil(
+    legendObj._height = Math.ceil(
         Math.max(
-            container._height + titleSize[1],
-            container._titleHeight + 2 * (bw + constants.itemGap)
+            legendObj._height + titleSize[1],
+            legendObj._titleHeight + 2 * (bw + constants.itemGap)
         )
     );
 
-    container._effHeight = Math.min(container._height, container._maxHeight);
+    legendObj._effHeight = Math.min(legendObj._height, legendObj._maxHeight);
 
     var edits = gd._context.edits;
     var isEditable = edits.legendText || edits.legendPosition;
@@ -771,28 +771,28 @@ function computeLegendDimensions(gd, groups, traces, container) {
 
 function expandMargin(gd) {
     var fullLayout = gd._fullLayout;
-    var container = fullLayout.legend;
-    var xanchor = getXanchor(container);
-    var yanchor = getYanchor(container);
+    var legendObj = fullLayout.legend;
+    var xanchor = getXanchor(legendObj);
+    var yanchor = getYanchor(legendObj);
 
     return Plots.autoMargin(gd, 'legend', {
-        x: container.x,
-        y: container.y,
-        l: container._width * (FROM_TL[xanchor]),
-        r: container._width * (FROM_BR[xanchor]),
-        b: container._effHeight * (FROM_BR[yanchor]),
-        t: container._effHeight * (FROM_TL[yanchor])
+        x: legendObj.x,
+        y: legendObj.y,
+        l: legendObj._width * (FROM_TL[xanchor]),
+        r: legendObj._width * (FROM_BR[xanchor]),
+        b: legendObj._effHeight * (FROM_BR[yanchor]),
+        t: legendObj._effHeight * (FROM_TL[yanchor])
     });
 }
 
-function getXanchor(container) {
-    return Lib.isRightAnchor(container) ? 'right' :
-        Lib.isCenterAnchor(container) ? 'center' :
+function getXanchor(legendObj) {
+    return Lib.isRightAnchor(legendObj) ? 'right' :
+        Lib.isCenterAnchor(legendObj) ? 'center' :
         'left';
 }
 
-function getYanchor(container) {
-    return Lib.isBottomAnchor(container) ? 'bottom' :
-        Lib.isMiddleAnchor(container) ? 'middle' :
+function getYanchor(legendObj) {
+    return Lib.isBottomAnchor(legendObj) ? 'bottom' :
+        Lib.isMiddleAnchor(legendObj) ? 'middle' :
         'top';
 }