From e7a2ffadd4fe002dfe5bfcf9dee2d04c93aeb962 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Mon, 25 Dec 2023 22:30:37 -0300
Subject: [PATCH 01/32] Add/calc tickmode "proportional" in `cartesian/`

- Create tickmode "proportional"
- Map fractional tickvals to axis values w/ axis range:
  - Set ax.(minor?).tickvals to
  - Do that right before `arrayTicks()` is called
- Every instance of `tickmode === "array"` gets `...|| "proportional"`

This works well since tickmode "proportional" really just adds a preprocess step to
tickmode "array".

TODO:

- Check if graph is reversed
- Find where ticks are redrawn on zoom and make redraw proportional (will probably fix below)
- Figure out why ticks not redrawn on double click/home
- Add docs in layout
---
 src/plot_api/plot_api.js                 |  2 +-
 src/plots/cartesian/axes.js              | 26 +++++++++++++++++++-----
 src/plots/cartesian/layout_attributes.js |  2 +-
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index a104e88bb11..cadec9351fc 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2945,7 +2945,7 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
         // so newContainer won't have them.
         if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
             var tickMode = newContainer.tickmode;
-            if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue;
+            if(tickMode === 'auto' || tickMode === 'array' || tickMode === 'proportional' || !tickMode) continue;
         }
         // FIXME: Similarly for axis ranges for 3D
         // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index dfdb0e5166d..123b77102d7 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -682,10 +682,10 @@ axes.prepTicks = function(ax, opts) {
             if(ax._name === 'radialaxis') nt *= 2;
         }
 
-        if(!(ax.minor && ax.minor.tickmode !== 'array')) {
+        if(!(ax.minor && (ax.minor.tickmode !== 'array' && ax.minor.tickmode !== 'proportional'))) {
             // add a couple of extra digits for filling in ticks when we
             // have explicit tickvals without tick text
-            if(ax.tickmode === 'array') nt *= 100;
+            if(ax.tickmode === 'array' || ax.tickmode === 'proportional') nt *= 100;
         }
 
         ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt;
@@ -944,9 +944,25 @@ axes.calcTicks = function calcTicks(ax, opts) {
             axes.prepTicks(mockAx, opts);
         }
 
+        console.log("mode:" + mockAx.tickmode)
+        if (mockAx.tickmode === 'proportional') { // TODO: if we look at autorange, can we get rid of buffer
+          var distance = maxRange - minRange;
+          var vals = []
+          if(major) {
+            vals = Lib.nestedProperty(ax, "tickvals")
+          } else {
+            vals = Lib.nestedProperty(ax.minor, "tickvals")
+          }
+          var mappedVals = vals.get().map(function(x) { return minRange+(distance*x) })
+          vals.set(mappedVals)
+          // TODO: What happens if range reversed
+          // TODO: Needs to be recalculated on auto
+          // TODO: Disappears on double click
+        }
+
         // now that we've figured out the auto values for formatting
         // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array') {
+        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'proportional') {
             if(major) {
                 tickVals = [];
                 ticksOut = arrayTicks(ax);
@@ -1617,7 +1633,7 @@ axes.tickFirst = function(ax, opts) {
 // more precision for hovertext
 axes.tickText = function(ax, x, hover, noSuffixPrefix) {
     var out = tickTextObj(ax, x);
-    var arrayMode = ax.tickmode === 'array';
+    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'proportional');
     var extraPrecision = hover || arrayMode;
     var axType = ax.type;
     // TODO multicategory, if we allow ticktext / tickvals
@@ -3333,7 +3349,7 @@ axes.drawGrid = function(gd, ax, opts) {
 
     var counterAx = opts.counterAxis;
     if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
-        var isArrayMode = ax.tickmode === 'array';
+        var isArrayMode = (ax.tickmode === 'array' || ax.tickmode === 'proportional');
         for(var i = 0; i < majorVals.length; i++) {
             var xi = majorVals[i].x;
             if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js
index bfa540f082c..88c7593fa7e 100644
--- a/src/plots/cartesian/layout_attributes.js
+++ b/src/plots/cartesian/layout_attributes.js
@@ -14,7 +14,7 @@ var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
 
 var minorTickmode = {
     valType: 'enumerated',
-    values: ['auto', 'linear', 'array'],
+    values: ['auto', 'linear', 'array', 'proportional'],
     editType: 'ticks',
     impliedEdits: {tick0: undefined, dtick: undefined},
     description: [

From 6d48a3b87df9111ab7c7b7a98e1d726a799adb49 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 01:22:09 -0300
Subject: [PATCH 02/32] Fix bug by which tickvals forever expands

The algo has to set the tickvals property back to original value after
spoofing it. This does that.
---
 src/plots/cartesian/axes.js | 46 +++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 22 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 123b77102d7..70f67a7fa8e 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -944,33 +944,35 @@ axes.calcTicks = function calcTicks(ax, opts) {
             axes.prepTicks(mockAx, opts);
         }
 
-        console.log("mode:" + mockAx.tickmode)
-        if (mockAx.tickmode === 'proportional') { // TODO: if we look at autorange, can we get rid of buffer
+        // tickmode 'proportional' is just 'array' but with a pre-calc step
+        // original comment:
+        // now that we've figured out the auto values for formatting
+        // in case we're missing some ticktext, we can break out for array ticks
+        if (mockAx.tickmode === 'array' ||  mockAx.tickmode === 'proportional') {
+
+          // Mapping proportions to array:
+          var valsProp
+          var proportionalVals
+          var mappedVals
           var distance = maxRange - minRange;
-          var vals = []
+          if (mockAx.tickmode === 'proportional') {
+            valsProp = major ? Lib.nestedProperty(ax, "tickvals") : Lib.nestedProperty(ax.minor, "tickvals")
+            proportionalVals = valsProp.get()
+            mappedVals = proportionalVals.map(function(v) { return minRange+(distance*v) })
+            valsProp.set(mappedVals)
+          }
+          // Original
           if(major) {
-            vals = Lib.nestedProperty(ax, "tickvals")
+              tickVals = [];
+              ticksOut = arrayTicks(ax);
           } else {
-            vals = Lib.nestedProperty(ax.minor, "tickvals")
+              minorTickVals = [];
+              minorTicks = arrayTicks(ax);
           }
-          var mappedVals = vals.get().map(function(x) { return minRange+(distance*x) })
-          vals.set(mappedVals)
-          // TODO: What happens if range reversed
-          // TODO: Needs to be recalculated on auto
-          // TODO: Disappears on double click
-        }
 
-        // now that we've figured out the auto values for formatting
-        // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'proportional') {
-            if(major) {
-                tickVals = [];
-                ticksOut = arrayTicks(ax);
-            } else {
-                minorTickVals = [];
-                minorTicks = arrayTicks(ax);
-            }
-            continue;
+          // Reset tickvals back to proportional
+          if (mockAx.tickmode === 'proportional') valsProp.set(proportionalVals)
+          continue;
         }
 
         // fill tickVals based on overlaying axis

From 00e884ae1c165d3e7f890d855e4c5e13f7fbdb77 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 01:48:56 -0300
Subject: [PATCH 03/32] Make tickmode proportional calculate reversed axes

---
 src/plots/cartesian/axes.js | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 70f67a7fa8e..dc7ab182c79 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -951,17 +951,18 @@ axes.calcTicks = function calcTicks(ax, opts) {
         if (mockAx.tickmode === 'array' ||  mockAx.tickmode === 'proportional') {
 
           // Mapping proportions to array:
-          var valsProp
-          var proportionalVals
-          var mappedVals
+          var valsProp, proportionalVals, mappedVals;
           var distance = maxRange - minRange;
+          var start = !axrev ? minRange : maxRange
+          if (axrev) distance *= -1;
           if (mockAx.tickmode === 'proportional') {
             valsProp = major ? Lib.nestedProperty(ax, "tickvals") : Lib.nestedProperty(ax.minor, "tickvals")
             proportionalVals = valsProp.get()
-            mappedVals = proportionalVals.map(function(v) { return minRange+(distance*v) })
+            mappedVals = proportionalVals.map(function(v) { return start + (distance*v) })
             valsProp.set(mappedVals)
           }
-          // Original
+
+          // Original 'array' only code
           if(major) {
               tickVals = [];
               ticksOut = arrayTicks(ax);

From 931199e91bc7eae47cd1f812dd85dfefd846d053 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 02:19:59 -0300
Subject: [PATCH 04/32] Use lib.simpleMap(); Refactor var names.

---
 src/plots/cartesian/axes.js | 51 ++++++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 24 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index dc7ab182c79..be2f1b653f1 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -950,30 +950,33 @@ axes.calcTicks = function calcTicks(ax, opts) {
         // in case we're missing some ticktext, we can break out for array ticks
         if (mockAx.tickmode === 'array' ||  mockAx.tickmode === 'proportional') {
 
-          // Mapping proportions to array:
-          var valsProp, proportionalVals, mappedVals;
-          var distance = maxRange - minRange;
-          var start = !axrev ? minRange : maxRange
-          if (axrev) distance *= -1;
-          if (mockAx.tickmode === 'proportional') {
-            valsProp = major ? Lib.nestedProperty(ax, "tickvals") : Lib.nestedProperty(ax.minor, "tickvals")
-            proportionalVals = valsProp.get()
-            mappedVals = proportionalVals.map(function(v) { return start + (distance*v) })
-            valsProp.set(mappedVals)
-          }
-
-          // Original 'array' only code
-          if(major) {
-              tickVals = [];
-              ticksOut = arrayTicks(ax);
-          } else {
-              minorTickVals = [];
-              minorTicks = arrayTicks(ax);
-          }
-
-          // Reset tickvals back to proportional
-          if (mockAx.tickmode === 'proportional') valsProp.set(proportionalVals)
-          continue;
+            // Mapping proportions to array:
+            var valsProp, fractionalVals;
+            var width = maxRange - minRange;
+            var offset = !axrev ? minRange : maxRange;
+            if (axrev) width *= -1;
+            if (mockAx.tickmode === 'proportional') {
+                valsProp = major ? Lib.nestedProperty(ax, "tickvals") : Lib.nestedProperty(ax.minor, "tickvals");
+                fractionalVals = valsProp.get();
+                
+                var mappedVals = Lib.simpleMap(fractionalVals, function(fraction, offset, width) { 
+                    return offset + (width*fraction);
+                }, offset, width);
+                valsProp.set(mappedVals);
+            }
+
+            // Original 'array' only code
+            if(major) {
+                tickVals = [];
+                ticksOut = arrayTicks(ax);
+            } else {
+                minorTickVals = [];
+                minorTicks = arrayTicks(ax);
+            }
+
+            // Reset tickvals back to proportional
+            if (mockAx.tickmode === 'proportional') valsProp.set(fractionalVals);
+            continue;
         }
 
         // fill tickVals based on overlaying axis

From fced9ce08dd2cd1279f4a60130c5bafef380f486 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 03:00:45 -0300
Subject: [PATCH 05/32] Add log math to tickmode proportional

---
 src/plots/cartesian/axes.js | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index be2f1b653f1..a3c15f3ea1d 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -953,15 +953,18 @@ axes.calcTicks = function calcTicks(ax, opts) {
             // Mapping proportions to array:
             var valsProp, fractionalVals;
             var width = maxRange - minRange;
+            console.log("minRange = " + minRange + " maxRange = " + maxRange)
             var offset = !axrev ? minRange : maxRange;
             if (axrev) width *= -1;
             if (mockAx.tickmode === 'proportional') {
                 valsProp = major ? Lib.nestedProperty(ax, "tickvals") : Lib.nestedProperty(ax.minor, "tickvals");
                 fractionalVals = valsProp.get();
-                
-                var mappedVals = Lib.simpleMap(fractionalVals, function(fraction, offset, width) { 
-                    return offset + (width*fraction);
-                }, offset, width);
+                var mappedVals = Lib.simpleMap(fractionalVals, function(fraction, offset, width, type) {
+                    var mapped = offset + (width*fraction);
+                    console.log(mapped)
+                    console.log(type)
+                    return (type === "log") ? Math.pow(10, mapped) : mapped
+                }, offset, width, type);
                 valsProp.set(mappedVals);
             }
 

From b2abf089fd856b892343298a65e463875c97a522 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 03:01:49 -0300
Subject: [PATCH 06/32] Remove console debug messages

---
 src/plots/cartesian/axes.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index a3c15f3ea1d..5d58aaddbb5 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -953,7 +953,6 @@ axes.calcTicks = function calcTicks(ax, opts) {
             // Mapping proportions to array:
             var valsProp, fractionalVals;
             var width = maxRange - minRange;
-            console.log("minRange = " + minRange + " maxRange = " + maxRange)
             var offset = !axrev ? minRange : maxRange;
             if (axrev) width *= -1;
             if (mockAx.tickmode === 'proportional') {
@@ -961,8 +960,6 @@ axes.calcTicks = function calcTicks(ax, opts) {
                 fractionalVals = valsProp.get();
                 var mappedVals = Lib.simpleMap(fractionalVals, function(fraction, offset, width, type) {
                     var mapped = offset + (width*fraction);
-                    console.log(mapped)
-                    console.log(type)
                     return (type === "log") ? Math.pow(10, mapped) : mapped
                 }, offset, width, type);
                 valsProp.set(mappedVals);

From 910163ef079d4403c0b3a36525b0f4e14b529442 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 03:32:35 -0300
Subject: [PATCH 07/32] Add description in layout_attributes.js

---
 src/plots/cartesian/layout_attributes.js | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js
index 88c7593fa7e..30f0cc11ecc 100644
--- a/src/plots/cartesian/layout_attributes.js
+++ b/src/plots/cartesian/layout_attributes.js
@@ -23,9 +23,12 @@ var minorTickmode = {
         'If *linear*, the placement of the ticks is determined by',
         'a starting position `tick0` and a tick step `dtick`',
         '(*linear* is the default value if `tick0` and `dtick` are provided).',
-        'If *array*, the placement of the ticks is set via `tickvals`',
-        'and the tick text is `ticktext`.',
-        '(*array* is the default value if `tickvals` is provided).'
+        'If *array*, the placement of the ticks is set via `tickvals`,',
+        'which are actual values, and the tick text is `ticktext`.',
+        '(*array* is the default value if `tickvals` is provided).',
+        'If *proportional*, the placement is similiar to *array* except that',
+        '`tickvals` are fractions between 0 and 1 representing distance on',
+        'the corresponding axis.'
     ].join(' ')
 };
 

From e21af96c4bd9f48a8610fbbfd6676f92b7deef4f Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 04:18:29 -0300
Subject: [PATCH 08/32] Fix linter errors

---
 src/plots/cartesian/axes.js | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 5d58aaddbb5..ca67ce9723d 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -948,19 +948,18 @@ axes.calcTicks = function calcTicks(ax, opts) {
         // original comment:
         // now that we've figured out the auto values for formatting
         // in case we're missing some ticktext, we can break out for array ticks
-        if (mockAx.tickmode === 'array' ||  mockAx.tickmode === 'proportional') {
-
+        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'proportional') {
             // Mapping proportions to array:
             var valsProp, fractionalVals;
             var width = maxRange - minRange;
             var offset = !axrev ? minRange : maxRange;
-            if (axrev) width *= -1;
-            if (mockAx.tickmode === 'proportional') {
-                valsProp = major ? Lib.nestedProperty(ax, "tickvals") : Lib.nestedProperty(ax.minor, "tickvals");
+            if(axrev) width *= -1;
+            if(mockAx.tickmode === 'proportional') {
+                valsProp = major ? Lib.nestedProperty(ax, 'tickvals') : Lib.nestedProperty(ax.minor, 'tickvals');
                 fractionalVals = valsProp.get();
                 var mappedVals = Lib.simpleMap(fractionalVals, function(fraction, offset, width, type) {
-                    var mapped = offset + (width*fraction);
-                    return (type === "log") ? Math.pow(10, mapped) : mapped
+                    var mapped = offset + (width * fraction);
+                    return (type === 'log') ? Math.pow(10, mapped) : mapped;
                 }, offset, width, type);
                 valsProp.set(mappedVals);
             }
@@ -975,7 +974,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
             }
 
             // Reset tickvals back to proportional
-            if (mockAx.tickmode === 'proportional') valsProp.set(fractionalVals);
+            if(mockAx.tickmode === 'proportional') valsProp.set(fractionalVals);
             continue;
         }
 

From ddfaad72daca0d32f528ebb88079145789ed760d Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 04:19:06 -0300
Subject: [PATCH 09/32] update plot-scheme diff

---
 test/plot-schema.json | 246 +++++++++++++++++++++++++-----------------
 1 file changed, 148 insertions(+), 98 deletions(-)

diff --git a/test/plot-schema.json b/test/plot-schema.json
index 9ad961d8701..eace0628931 100644
--- a/test/plot-schema.json
+++ b/test/plot-schema.json
@@ -1462,14 +1462,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -4203,14 +4204,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -4868,14 +4870,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -6053,14 +6056,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -6681,14 +6685,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -7309,14 +7314,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -9109,14 +9115,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -9513,14 +9520,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -9923,14 +9931,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -11038,14 +11047,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "ticks": {
@@ -11766,7 +11776,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -11774,6 +11784,7 @@
       "auto",
       "linear",
       "array",
+      "proportional",
       "sync"
      ]
     },
@@ -12378,14 +12389,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "ticks": {
@@ -12857,7 +12869,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -12865,6 +12877,7 @@
       "auto",
       "linear",
       "array",
+      "proportional",
       "sync"
      ]
     },
@@ -13934,14 +13947,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -15483,14 +15497,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -19781,14 +19796,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -20808,14 +20824,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -21864,14 +21881,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -22938,14 +22956,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -24353,14 +24372,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -25339,14 +25359,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -26680,14 +26701,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -28517,14 +28539,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -29715,14 +29738,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -31290,14 +31314,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -32496,14 +32521,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -33666,14 +33692,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -35355,14 +35382,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -36878,14 +36906,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "plot",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -37801,14 +37830,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -39051,14 +39081,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -41073,14 +41104,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -42064,14 +42096,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -45782,14 +45815,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -48063,14 +48097,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -48674,14 +48709,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -50185,14 +50221,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -52102,14 +52139,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -54154,14 +54192,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -56168,14 +56207,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -57406,14 +57446,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -59302,14 +59343,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -61198,14 +61240,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -63133,14 +63176,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -64999,14 +65043,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -66432,14 +66477,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -67931,14 +67977,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -68919,14 +68966,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -71319,14 +71367,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -73779,14 +73828,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {

From d42dd7378a2cc32cca36154b5e93b818441e08c5 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 26 Dec 2023 23:26:03 -0300
Subject: [PATCH 10/32] Restore proportional array later in loop:

Plotly does some math on the ticks, sometimes comrparing major and minor
values, so we have to store both in their own separate values and then
restore them to their attribute at the very end so plotly has them
throughout the calculating process.
---
 src/plots/cartesian/axes.js | 52 ++++++++++++++++++++++++++-----------
 1 file changed, 37 insertions(+), 15 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index ca67ce9723d..1eea98149f0 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -920,7 +920,12 @@ axes.calcTicks = function calcTicks(ax, opts) {
     var minorTicks = [];
 
     var tickVals = [];
+    var tickFractionalVals = [];
+    tickFractionalVals._isSet = false
+
     var minorTickVals = [];
+    var minorTickFractionalVals = [];
+    minorTickFractionalVals._isSet = false
 
     var hasMinor = ax.minor && (ax.minor.ticks || ax.minor.showgrid);
 
@@ -950,18 +955,29 @@ axes.calcTicks = function calcTicks(ax, opts) {
         // in case we're missing some ticktext, we can break out for array ticks
         if(mockAx.tickmode === 'array' || mockAx.tickmode === 'proportional') {
             // Mapping proportions to array:
-            var valsProp, fractionalVals;
-            var width = maxRange - minRange;
-            var offset = !axrev ? minRange : maxRange;
-            if(axrev) width *= -1;
             if(mockAx.tickmode === 'proportional') {
-                valsProp = major ? Lib.nestedProperty(ax, 'tickvals') : Lib.nestedProperty(ax.minor, 'tickvals');
-                fractionalVals = valsProp.get();
-                var mappedVals = Lib.simpleMap(fractionalVals, function(fraction, offset, width, type) {
-                    var mapped = offset + (width * fraction);
-                    return (type === 'log') ? Math.pow(10, mapped) : mapped;
-                }, offset, width, type);
-                valsProp.set(mappedVals);
+                var width = (maxRange - minRange);
+                if(axrev) width *= -1;
+                var offset = !axrev ? minRange : maxRange;
+               
+                var currentFractionalVals = [];
+                var currentValsProp;
+                if(major) {
+                    currentValsProp = Lib.nestedProperty(ax, 'tickvals'); // Do we need this?
+                    currentFractionalVals = tickFractionalVals = currentValsProp.get();
+                    tickFractionalVals._isSet = true
+                } else {
+                    currentValsProp = Lib.nestedProperty(ax.minor, 'tickvals');
+                    currentFractionalVals = minorTickFractionalVals = currentValsProp.get();
+                    minorTickFractionalVals._isSet = true
+                }
+
+                var mappedVals = Lib.simpleMap(currentFractionalVals, 
+                    function(fraction, offset, width, type) {
+                        var mapped = offset + (width * fraction);
+                        return (type === 'log') ? Math.pow(10, mapped) : mapped;
+                    }, offset, width, type);
+                currentValsProp.set(mappedVals);
             }
 
             // Original 'array' only code
@@ -972,11 +988,8 @@ axes.calcTicks = function calcTicks(ax, opts) {
                 minorTickVals = [];
                 minorTicks = arrayTicks(ax);
             }
-
-            // Reset tickvals back to proportional
-            if(mockAx.tickmode === 'proportional') valsProp.set(fractionalVals);
             continue;
-        }
+        } 
 
         // fill tickVals based on overlaying axis
         if(mockAx.tickmode === 'sync') {
@@ -1225,6 +1238,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
             ticksOut.push(t);
         }
     }
+    
     ticksOut = ticksOut.concat(minorTicks);
 
     ax._inCalcTicks = false;
@@ -1234,6 +1248,14 @@ axes.calcTicks = function calcTicks(ax, opts) {
         ticksOut[0].noTick = true;
     }
 
+    // Reset tickvals back to proportional
+    if (tickFractionalVals._isSet) {
+        Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals)
+    }
+    if (minorTickFractionalVals._isSet){
+        Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
+    }
+    
     return ticksOut;
 };
 

From 5d7b1b91e87232eec44aaa393d9a273861832b63 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Thu, 28 Dec 2023 18:32:25 -0300
Subject: [PATCH 11/32] Remove _isSet flag from tickVals before resetting

---
 src/plots/cartesian/axes.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 1eea98149f0..bd40a4af049 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -1250,9 +1250,11 @@ axes.calcTicks = function calcTicks(ax, opts) {
 
     // Reset tickvals back to proportional
     if (tickFractionalVals._isSet) {
+        delete tickFractionalVals._isSet
         Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals)
     }
     if (minorTickFractionalVals._isSet){
+        delete tickFractionalVals._isSet
         Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
     }
     

From db1f82e2f6b4f9e664877021efe758a47fd774e3 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Thu, 28 Dec 2023 18:33:07 -0300
Subject: [PATCH 12/32] Add parameterized tests:

These tests currently fail but this commit currently doesn't include
fundamental bug fixes.
---
 test/jasmine/tests/pikul_test.js | 151 +++++++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)
 create mode 100644 test/jasmine/tests/pikul_test.js

diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
new file mode 100644
index 00000000000..5e9ceff8b5d
--- /dev/null
+++ b/test/jasmine/tests/pikul_test.js
@@ -0,0 +1,151 @@
+
+var Plotly = require('../../../lib/index');
+
+var createGraphDiv = require('../assets/create_graph_div');
+var destroyGraphDiv = require('../assets/destroy_graph_div');
+
+// Boilerplate taken from axes_test.js
+describe('tickmode proportional', function() {
+  var gd;
+
+  beforeEach(function() {
+      gd = createGraphDiv();
+  });
+
+  afterEach(destroyGraphDiv);
+  
+  // These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
+  // Passed as tickLen argument to specify major or minor tick config
+  const MAJOR = 202, MINOR = 101; 
+  function generateTickConfig(tickLen){
+    // Intentionally configure to produce a single `(x|y)tick` class per tick
+    // labels and tick marks each produce one, so one or the other
+    standardConfig = {tickmode: 'proportional', ticklen: tickLen, showticklabels: false};
+
+    // Tick values will be random:
+    var n = Math.floor(Math.random() * 100);
+    tickVals = [];
+
+    for(let i = 0; i <= n; i++){
+      intermediate = (Math.trunc(Math.random()*150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
+      tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
+    }
+    standardConfig['tickvals'] = tickVals;
+    return standardConfig;
+  }
+
+  function binaryToString(bin) {
+    if (bin == 0b1) return "xMajor";
+    if (bin == 0b10) return "xMinor";
+    if (bin == 0b100) return "yMajor";
+    if (bin == 0b1000) return "yMinor";
+  }
+  // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
+  // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
+  for(let tickConfig = 1; tickConfig <= 15; tickConfig++) {
+      (function(tickConfig) { // tickConfig needs to be a closure otherwise it won't get the parameterized value
+          it('maps proportional values to correct range values', function(done) {
+              var xMajor = tickConfig & 0b0001; // check if xMajor position is 1 (True)
+              var xMinor = tickConfig & 0b0010;
+              var yMajor = tickConfig & 0b0100;
+              var yMinor = tickConfig & 0b1000;
+              xMajorConfig = xMajor ? generateTickConfig(MAJOR) : {}; // generate separate configs for each
+              xMinorConfig = xMinor ? generateTickConfig(MAJOR) : {};
+              yMajorConfig = yMajor ? generateTickConfig(MINOR) : {};
+              yMinorConfig = yMinor ? generateTickConfig(MINOR) : {};
+              var configInfo = ""
+              configInfo += xMajor ? "\n" + `xMajor: ${xMajorConfig['tickvals'].length} non-unique vals` : "";
+              configInfo += xMinor ? "\n" + `xMinor: ${xMinorConfig['tickvals'].length} non-unique vals` : "";
+              configInfo += yMajor ? "\n" + `yMajor: ${yMajorConfig['tickvals'].length} non-unique vals` : "";
+              configInfo += yMinor ? "\n" + `yMinor: ${yMinorConfig['tickvals'].length} non-unique vals` : "";
+              Plotly.newPlot(gd, {
+                  data: [{
+                      x: [0, 1],
+                      y: [0, 1]
+                  }],
+                  layout: {
+                      width: 400,
+                      height: 400,
+                      margin: { t: 40, b: 40, l: 40, r: 40, },
+                      xaxis: {
+                          range: [0, 10],
+                          ...xMajorConfig, // explode config into this key
+                          minor: xMinorConfig, // set config to this key
+                      },
+                      yaxis: { // same as above
+                          autorange: true,
+                          ...yMajorConfig,
+                          minor: yMinorConfig,
+                      },
+              }}).then(function() {
+                  // This regex is for extracting geometric position of... should have used getBoundingClientRect()
+                  // 
+                  // regex: `.source` converts to string, laid out this way to make for easier reading
+                  const funcName = "translate" + /\(/.source; // literally simplest way to regex '('
+                  const integerPart = /\d+/.source; // numbers left of decimal
+                  const fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
+                  const floatNum = integerPart + fractionalPart; // all together
+                  const any = /.+/.source;
+                  const close = /\)/.source;
+                  const re = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens are capture not fn()
+                  for(let runNumber = 0b1; runNumber <= 0b1000; runNumber <<= 0b1) { // Check all ticks on all axes ☺
+                      var elementName = "";
+                      var targetConfig;
+                      var runInfo = "\n Checking: " + binaryToString(runNumber);
+                      if(runNumber & xMajor) { // ie. this run wants xMajor and xMajor was set in config above
+                          elementName = "xtick";
+                          targetConfig = xMajorConfig;
+                      } else if (runNumber & xMinor) {
+                          elementName = "xtick";
+                          targetConfig = xMinorConfig;
+                      } else if (runNumber & yMajor) {
+                          elementName = "ytick";
+                          targetConfig = yMajorConfig;
+                      } else if (runNumber & yMinor) {
+                          elementName = "ytick";
+                          targetConfig = yMinorConfig;
+                      } else continue; // This test isn't doing that type of test
+                      
+                      var tickElements = document.getElementsByClassName(elementName);
+                      var tickValsUnique = [...new Set(targetConfig['tickvals'])];
+                      var expectedTickLen = String(targetConfig['ticklen'])
+
+                      if(tickElements.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
+                      
+                      // Filter out major/minor and grab geometry
+                      transformVals = []; // "transform" ie the positional property
+                      for(let i = 0; i < tickElements.length; i++) {
+                          if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
+                          var translate = tickElements[i].getAttribute("transform");
+                          transformVals.push(Number(translate.match(re)[1]));
+                      }
+                      
+                      var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n" +
+                        `tickVals/Unique: (${targetConfig['tickvals'].length}/${tickValsUnique.length}) {tickValsUnique}`;
+                      
+                      expect(transformVals.length).toBe(tickValsUnique.length, 
+                        "test html vs tickvals failed" + runInfo + configInfo + debugInfo);
+
+                      
+                      // To test geometries without using fixed point or data values
+                      // We can check consistency of y = mx+b
+                      // if x = 0 then y = b, but we may not have a 0 valued x
+                      // m = (y1 - y2) / (x1 - x2)
+                      // b = y1 - mx1
+                      y = transformVals;
+                      x = tickValsUnique;
+                      var m, b;
+                      var b_index = x.indexOf(0);
+                      
+                      m = (y[0] - y[1]) / (x[0] - x[1]);
+                      b = (b_index != -1) ? b = y[b_index] : y[0] - m*x[0];
+                      
+                      for(let i = 0; i < x.length; i++) {
+                         expect(y[i]).toBeCloseTo(m * x[i] + b, 1, "y=mx+b test failed" + runInfo + configInfo + debugInfo) // not sure about toBeCloseTo
+                      }
+                  }
+              }).then(done, done.fail);
+          }); 
+     })(tickConfig);
+  }
+});

From a2ac023dc49be50c6dc21f9ae527afe9d5c1c6d0 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Thu, 28 Dec 2023 20:15:27 -0300
Subject: [PATCH 13/32] Fix tests to pass if #6828 is addressed

---
 test/jasmine/tests/pikul_test.js | 263 ++++++++++++++++++-------------
 1 file changed, 153 insertions(+), 110 deletions(-)

diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index 5e9ceff8b5d..0b4c3017097 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -5,7 +5,7 @@ var createGraphDiv = require('../assets/create_graph_div');
 var destroyGraphDiv = require('../assets/destroy_graph_div');
 
 // Boilerplate taken from axes_test.js
-describe('tickmode proportional', function() {
+describe('When generating axes w/ `tickmode`:"proportional",', function() {
   var gd;
 
   beforeEach(function() {
@@ -16,7 +16,7 @@ describe('tickmode proportional', function() {
   
   // These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
   // Passed as tickLen argument to specify major or minor tick config
-  const MAJOR = 202, MINOR = 101; 
+  const MAJOR = 10, MINOR = 5; 
   function generateTickConfig(tickLen){
     // Intentionally configure to produce a single `(x|y)tick` class per tick
     // labels and tick marks each produce one, so one or the other
@@ -33,119 +33,162 @@ describe('tickmode proportional', function() {
     standardConfig['tickvals'] = tickVals;
     return standardConfig;
   }
-
-  function binaryToString(bin) {
-    if (bin == 0b1) return "xMajor";
-    if (bin == 0b10) return "xMinor";
-    if (bin == 0b100) return "yMajor";
-    if (bin == 0b1000) return "yMinor";
+  
+  // See comment below for explanation of parameterization
+  const XMAJOR = 0b0001;
+  const XMINOR = 0b0010;
+  const YMAJOR = 0b0100;
+  const YMINOR = 0b1000;
+  // Converts binary to list of tick types indicated by binary
+  function binaryToTickType(bin) {
+    str = [];
+    if (bin & XMAJOR) str.push("xMajor");
+    if (bin & XMINOR) str.push("xMinor");
+    if (bin & YMAJOR) str.push("yMajor");
+    if (bin & YMINOR) str.push("yMinor");
+    if (str.length) {
+      return str.join(', ');
+    }
+    return "";
   }
+
   // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
   // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
-  for(let tickConfig = 1; tickConfig <= 15; tickConfig++) {
-      (function(tickConfig) { // tickConfig needs to be a closure otherwise it won't get the parameterized value
-          it('maps proportional values to correct range values', function(done) {
-              var xMajor = tickConfig & 0b0001; // check if xMajor position is 1 (True)
-              var xMinor = tickConfig & 0b0010;
-              var yMajor = tickConfig & 0b0100;
-              var yMinor = tickConfig & 0b1000;
-              xMajorConfig = xMajor ? generateTickConfig(MAJOR) : {}; // generate separate configs for each
-              xMinorConfig = xMinor ? generateTickConfig(MAJOR) : {};
-              yMajorConfig = yMajor ? generateTickConfig(MINOR) : {};
-              yMinorConfig = yMinor ? generateTickConfig(MINOR) : {};
-              var configInfo = ""
-              configInfo += xMajor ? "\n" + `xMajor: ${xMajorConfig['tickvals'].length} non-unique vals` : "";
-              configInfo += xMinor ? "\n" + `xMinor: ${xMinorConfig['tickvals'].length} non-unique vals` : "";
-              configInfo += yMajor ? "\n" + `yMajor: ${yMajorConfig['tickvals'].length} non-unique vals` : "";
-              configInfo += yMinor ? "\n" + `yMinor: ${yMinorConfig['tickvals'].length} non-unique vals` : "";
-              Plotly.newPlot(gd, {
-                  data: [{
-                      x: [0, 1],
-                      y: [0, 1]
-                  }],
-                  layout: {
-                      width: 400,
-                      height: 400,
-                      margin: { t: 40, b: 40, l: 40, r: 40, },
-                      xaxis: {
-                          range: [0, 10],
-                          ...xMajorConfig, // explode config into this key
-                          minor: xMinorConfig, // set config to this key
-                      },
-                      yaxis: { // same as above
-                          autorange: true,
-                          ...yMajorConfig,
-                          minor: yMinorConfig,
-                      },
-              }}).then(function() {
-                  // This regex is for extracting geometric position of... should have used getBoundingClientRect()
-                  // 
-                  // regex: `.source` converts to string, laid out this way to make for easier reading
-                  const funcName = "translate" + /\(/.source; // literally simplest way to regex '('
-                  const integerPart = /\d+/.source; // numbers left of decimal
-                  const fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                  const floatNum = integerPart + fractionalPart; // all together
-                  const any = /.+/.source;
-                  const close = /\)/.source;
-                  const re = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens are capture not fn()
-                  for(let runNumber = 0b1; runNumber <= 0b1000; runNumber <<= 0b1) { // Check all ticks on all axes ☺
-                      var elementName = "";
-                      var targetConfig;
-                      var runInfo = "\n Checking: " + binaryToString(runNumber);
-                      if(runNumber & xMajor) { // ie. this run wants xMajor and xMajor was set in config above
-                          elementName = "xtick";
-                          targetConfig = xMajorConfig;
-                      } else if (runNumber & xMinor) {
-                          elementName = "xtick";
-                          targetConfig = xMinorConfig;
-                      } else if (runNumber & yMajor) {
-                          elementName = "ytick";
-                          targetConfig = yMajorConfig;
-                      } else if (runNumber & yMinor) {
-                          elementName = "ytick";
-                          targetConfig = yMinorConfig;
-                      } else continue; // This test isn't doing that type of test
-                      
-                      var tickElements = document.getElementsByClassName(elementName);
-                      var tickValsUnique = [...new Set(targetConfig['tickvals'])];
-                      var expectedTickLen = String(targetConfig['ticklen'])
+  // We add a fourth to switch between linear and log
+  for(let tickConfig = 1; tickConfig <= 0b1111; tickConfig++) {
+      var graphTypes = [
+        { type:'linear' },
+        { type:'log'},
+        { type:'date'},
+        { type:'category'},
+      ];
+      for (let graphTypeIndex = 0; graphTypeIndex < graphTypes.length; graphTypeIndex++) {
+          var xGraphType = graphTypes[graphTypeIndex]; // Only with X for now
+          (function(tickConfig, xGraphType) { // wrap in func or else it() can't see variable because of javascript closure scope
+              it('fraction mapping to geometries for config ' + binaryToTickType(tickConfig) , function(done) {
+                  // We will check all four tick sets, these will resolve to true or false:
+                  var xMajor = tickConfig & XMAJOR;
+                  var xMinor = tickConfig & XMINOR;
+                  var yMajor = tickConfig & YMAJOR;
+                  var yMinor = tickConfig & YMINOR;
+                  ticksOff = {ticklen: 0, showticklabels: false};
+                  var xMajorConfig = xMajor ? generateTickConfig(MAJOR) : ticksOff; // generate separate configs for each
+                  var xMinorConfig = xMinor ? generateTickConfig(MINOR) : ticksOff;
+                  var yMajorConfig = yMajor ? generateTickConfig(MAJOR) : ticksOff;
+                  var yMinorConfig = yMinor ? generateTickConfig(MINOR) : ticksOff;
+                  var configInfo = ""
+                  configInfo += xMajor ? "\n " + `xMajor: ${[...new Set(xMajorConfig['tickvals'])].length} unique vals` : "";
+                  configInfo += xMinor ? "\n " + `xMinor: ${[...new Set(xMinorConfig['tickvals'])].length} unique vals` : "";
+                  configInfo += yMajor ? "\n " + `yMajor: ${[...new Set(yMajorConfig['tickvals'])].length} unique vals` : "";
+                  configInfo += yMinor ? "\n " + `yMinor: ${[...new Set(yMinorConfig['tickvals'])].length} unique vals` : "";
+                  Plotly.newPlot(gd, {
+                      data: [{
+                          x: [0, 1],
+                          y: [0, 1]
+                      }],
+                      layout: {
+                          width: 400,
+                          height: 400,
+                          margin: { t: 40, b: 40, l: 40, r: 40, },
+                          ...xGraphType,
+                          xaxis: {
+                              autorange: true,
+                              ...xMajorConfig, // explode config into this key
+                              minor: xMinorConfig, // set config to this key
+                          },
+                          yaxis: { // same as above
+                              autorange: true,
+                              ...yMajorConfig,
+                              minor: yMinorConfig,
+                          },
+                  }}).then(function() {
+                      // This regex is for extracting geometric position of... should have used getBoundingClientRect()
+                      // 
+                      // regex: `.source` converts to string, laid out this way to make for easier reading
+                      const funcName = "translate" + /\(/.source; // literally simplest way to regex '('
+                      const integerPart = /\d+/.source; // numbers left of decimal
+                      const fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
+                      const floatNum = integerPart + fractionalPart; // all together
+                      const any = /.+/.source;
+                      const close = /\)/.source;
+                      const reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens are capture not fn()
+                      const reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close); // parens are capture not fn()
 
-                      if(tickElements.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
-                      
-                      // Filter out major/minor and grab geometry
-                      transformVals = []; // "transform" ie the positional property
-                      for(let i = 0; i < tickElements.length; i++) {
-                          if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
-                          var translate = tickElements[i].getAttribute("transform");
-                          transformVals.push(Number(translate.match(re)[1]));
-                      }
-                      
-                      var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n" +
-                        `tickVals/Unique: (${targetConfig['tickvals'].length}/${tickValsUnique.length}) {tickValsUnique}`;
-                      
-                      expect(transformVals.length).toBe(tickValsUnique.length, 
-                        "test html vs tickvals failed" + runInfo + configInfo + debugInfo);
+                      for(let runNumber = 0b1; runNumber <= 0b1000; runNumber <<= 0b1) { // Check all ticks on all axes ☺
+                          var runInfo = "\n Checking: " + binaryToTickType(runNumber);
+                          var elementName = "";
+                          var targetConfig;
+                          var re;
+                          if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
+                              elementName = "xtick";
+                              targetConfig = xMajorConfig;
+                              re = reX;
+                          } else if(runNumber & xMinor) {
+                              elementName = "xtick";
+                              targetConfig = xMinorConfig;
+                              re = reX;
+                          } else if(runNumber & yMajor) {
+                              elementName = "ytick";
+                              targetConfig = yMajorConfig;
+                              re = reY;
+                          } else if(runNumber & yMinor) {
+                              elementName = "ytick";
+                              targetConfig = yMinorConfig;
+                              re = reY;
+                          } else continue; // This run would have been to check ticks that don't exist
+                          
+                          var tickElements = document.getElementsByClassName(elementName);
+                          var tickValsUnique = [...new Set(targetConfig['tickvals'])];
+                          var expectedTickLen = String(targetConfig['ticklen'])
+
+                          
+                          // Filter out major/minor and grab geometry
+                          transformVals = []; // "transform" ie the positional property
+                          for(let i = 0; i < tickElements.length; i++) {
+                              if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
+                              var translate = tickElements[i].getAttribute("transform");
+                              transformVals.push(Number(translate.match(re)[1]));
+                          }
+                          
+                          var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n " +
+                            `tickVals/Unique: (${targetConfig['tickvals'].length}/${tickValsUnique.length}) {tickValsUnique}`;
+                          
+                          expect(transformVals.length).toBe(tickValsUnique.length, 
+                            "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo);
+                          if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
+
+                          
+                          // To test geometries without using fixed point or data values
+                          // We can check consistency of y = mx+b
+                          // if x = 0 then y = b, but we may not have a 0 valued x
+                          // m = (y1 - y2) / (x1 - x2)
+                          // b = y1 - mx1
+                          y = transformVals;
+                          x = tickValsUnique;
+                          var m, b;
+                          var b_index = x.indexOf(0);
+                          
+                          m = (y[0] - y[1]) / (x[0] - x[1]);
+                          b = (b_index != -1) ? b = y[b_index] : y[0] - m*x[0];
+                          
+                          calculatedY = [];
+                          for(let i = 0; i < x.length; i++) calculatedY.push(m*x[i] + b);
+                          
 
-                      
-                      // To test geometries without using fixed point or data values
-                      // We can check consistency of y = mx+b
-                      // if x = 0 then y = b, but we may not have a 0 valued x
-                      // m = (y1 - y2) / (x1 - x2)
-                      // b = y1 - mx1
-                      y = transformVals;
-                      x = tickValsUnique;
-                      var m, b;
-                      var b_index = x.indexOf(0);
-                      
-                      m = (y[0] - y[1]) / (x[0] - x[1]);
-                      b = (b_index != -1) ? b = y[b_index] : y[0] - m*x[0];
-                      
-                      for(let i = 0; i < x.length; i++) {
-                         expect(y[i]).toBeCloseTo(m * x[i] + b, 1, "y=mx+b test failed" + runInfo + configInfo + debugInfo) // not sure about toBeCloseTo
+                          /* **** Close this comment line to manually inspect output --> 
+                          yout = [];
+                          ycalcout = [];
+                          for (i = 0; i < Math.min(x.length, 10); i++) {
+                               yout.push(Number.parseFloat(y[i]).toFixed(2));
+                               ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
+                          }
+                          console.log(yout);
+                          console.log(ycalcout);/* */
+                          expect(y).toBeCloseToArray(calculatedY, `y=mx+b test failed comparing\n${y}\n${calculatedY}`);
                       }
-                  }
-              }).then(done, done.fail);
-          }); 
-     })(tickConfig);
+                  }).then(done, done.fail);
+              }); 
+          })(tickConfig, xGraphType);
+      }
   }
 });

From 1fedfc73a86d95fbbfe8258a4963dd69bfd8399b Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Thu, 28 Dec 2023 20:39:51 -0300
Subject: [PATCH 14/32] Clean up commenting/address lint errors.

---
 src/plots/cartesian/axes.js      | 28 +++++++++++------------
 test/jasmine/tests/pikul_test.js | 38 ++++++++++++++++----------------
 2 files changed, 33 insertions(+), 33 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index bd40a4af049..5bc4e95ca87 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -921,11 +921,11 @@ axes.calcTicks = function calcTicks(ax, opts) {
 
     var tickVals = [];
     var tickFractionalVals = [];
-    tickFractionalVals._isSet = false
+    tickFractionalVals._isSet = false;
 
     var minorTickVals = [];
     var minorTickFractionalVals = [];
-    minorTickFractionalVals._isSet = false
+    minorTickFractionalVals._isSet = false;
 
     var hasMinor = ax.minor && (ax.minor.ticks || ax.minor.showgrid);
 
@@ -958,21 +958,21 @@ axes.calcTicks = function calcTicks(ax, opts) {
             if(mockAx.tickmode === 'proportional') {
                 var width = (maxRange - minRange);
                 if(axrev) width *= -1;
-                var offset = !axrev ? minRange : maxRange;
-               
+                var offset = !axrev ? minRange : maxRange
+
                 var currentFractionalVals = [];
                 var currentValsProp;
                 if(major) {
                     currentValsProp = Lib.nestedProperty(ax, 'tickvals'); // Do we need this?
                     currentFractionalVals = tickFractionalVals = currentValsProp.get();
-                    tickFractionalVals._isSet = true
+                    tickFractionalVals._isSet = true;
                 } else {
                     currentValsProp = Lib.nestedProperty(ax.minor, 'tickvals');
                     currentFractionalVals = minorTickFractionalVals = currentValsProp.get();
-                    minorTickFractionalVals._isSet = true
+                    minorTickFractionalVals._isSet = true;
                 }
 
-                var mappedVals = Lib.simpleMap(currentFractionalVals, 
+                var mappedVals = Lib.simpleMap(currentFractionalVals,
                     function(fraction, offset, width, type) {
                         var mapped = offset + (width * fraction);
                         return (type === 'log') ? Math.pow(10, mapped) : mapped;
@@ -989,7 +989,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
                 minorTicks = arrayTicks(ax);
             }
             continue;
-        } 
+        }
 
         // fill tickVals based on overlaying axis
         if(mockAx.tickmode === 'sync') {
@@ -1249,15 +1249,15 @@ axes.calcTicks = function calcTicks(ax, opts) {
     }
 
     // Reset tickvals back to proportional
-    if (tickFractionalVals._isSet) {
-        delete tickFractionalVals._isSet
-        Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals)
+    if(tickFractionalVals._isSet) {
+        delete tickFractionalVals._isSet;
+        Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals);
     }
-    if (minorTickFractionalVals._isSet){
-        delete tickFractionalVals._isSet
+    if(minorTickFractionalVals._isSet) {
+        delete tickFractionalVals._isSet;
         Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
     }
-    
+
     return ticksOut;
 };
 
diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index 0b4c3017097..a20bea58664 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -15,8 +15,9 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
   afterEach(destroyGraphDiv);
   
   // These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
-  // Passed as tickLen argument to specify major or minor tick config
-  const MAJOR = 10, MINOR = 5; 
+  // Passed as tickLen argument to specify a major or minor tick config
+  const MAJOR = 10;
+  const MINOR = 5; 
   function generateTickConfig(tickLen){
     // Intentionally configure to produce a single `(x|y)tick` class per tick
     // labels and tick marks each produce one, so one or the other
@@ -54,8 +55,8 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
 
   // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
   // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
-  // We add a fourth to switch between linear and log
   for(let tickConfig = 1; tickConfig <= 0b1111; tickConfig++) {
+      // Parameterize graph types as well
       var graphTypes = [
         { type:'linear' },
         { type:'log'},
@@ -63,24 +64,24 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
         { type:'category'},
       ];
       for (let graphTypeIndex = 0; graphTypeIndex < graphTypes.length; graphTypeIndex++) {
-          var xGraphType = graphTypes[graphTypeIndex]; // Only with X for now
           (function(tickConfig, xGraphType) { // wrap in func or else it() can't see variable because of javascript closure scope
               it('fraction mapping to geometries for config ' + binaryToTickType(tickConfig) , function(done) {
-                  // We will check all four tick sets, these will resolve to true or false:
-                  var xMajor = tickConfig & XMAJOR;
-                  var xMinor = tickConfig & XMINOR;
-                  var yMajor = tickConfig & YMAJOR;
+                  var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
+                  var xMinor = tickConfig & XMINOR; // does this config include xminor?
+                  var yMajor = tickConfig & YMAJOR; // ... etc
                   var yMinor = tickConfig & YMINOR;
                   ticksOff = {ticklen: 0, showticklabels: false};
-                  var xMajorConfig = xMajor ? generateTickConfig(MAJOR) : ticksOff; // generate separate configs for each
+                  var xMajorConfig = xMajor ? generateTickConfig(MAJOR) : ticksOff; // generate configs
                   var xMinorConfig = xMinor ? generateTickConfig(MINOR) : ticksOff;
                   var yMajorConfig = yMajor ? generateTickConfig(MAJOR) : ticksOff;
                   var yMinorConfig = yMinor ? generateTickConfig(MINOR) : ticksOff;
-                  var configInfo = ""
+                  var configInfo = "" // for debugging
                   configInfo += xMajor ? "\n " + `xMajor: ${[...new Set(xMajorConfig['tickvals'])].length} unique vals` : "";
                   configInfo += xMinor ? "\n " + `xMinor: ${[...new Set(xMinorConfig['tickvals'])].length} unique vals` : "";
                   configInfo += yMajor ? "\n " + `yMajor: ${[...new Set(yMajorConfig['tickvals'])].length} unique vals` : "";
                   configInfo += yMinor ? "\n " + `yMinor: ${[...new Set(yMinorConfig['tickvals'])].length} unique vals` : "";
+
+                  // stolen from axes_test.js
                   Plotly.newPlot(gd, {
                       data: [{
                           x: [0, 1],
@@ -102,8 +103,7 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
                               minor: yMinorConfig,
                           },
                   }}).then(function() {
-                      // This regex is for extracting geometric position of... should have used getBoundingClientRect()
-                      // 
+                      // This regex is for extracting geometric position of a tick
                       // regex: `.source` converts to string, laid out this way to make for easier reading
                       const funcName = "translate" + /\(/.source; // literally simplest way to regex '('
                       const integerPart = /\d+/.source; // numbers left of decimal
@@ -111,8 +111,8 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
                       const floatNum = integerPart + fractionalPart; // all together
                       const any = /.+/.source;
                       const close = /\)/.source;
-                      const reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens are capture not fn()
-                      const reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close); // parens are capture not fn()
+                      const reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
+                      const reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
 
                       for(let runNumber = 0b1; runNumber <= 0b1000; runNumber <<= 0b1) { // Check all ticks on all axes ☺
                           var runInfo = "\n Checking: " + binaryToTickType(runNumber);
@@ -155,12 +155,13 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
                           
                           expect(transformVals.length).toBe(tickValsUnique.length, 
                             "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo);
+                          
                           if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
 
                           
-                          // To test geometries without using fixed point or data values
-                          // We can check consistency of y = mx+b
-                          // if x = 0 then y = b, but we may not have a 0 valued x
+                          // To test geometries without using fixed point or data values...
+                          // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
+                          // If x = 0 then y = b, but we may not have a 0 valued x
                           // m = (y1 - y2) / (x1 - x2)
                           // b = y1 - mx1
                           y = transformVals;
@@ -173,7 +174,6 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
                           
                           calculatedY = [];
                           for(let i = 0; i < x.length; i++) calculatedY.push(m*x[i] + b);
-                          
 
                           /* **** Close this comment line to manually inspect output --> 
                           yout = [];
@@ -188,7 +188,7 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
                       }
                   }).then(done, done.fail);
               }); 
-          })(tickConfig, xGraphType);
+          })(tickConfig, graphTypes[graphTypeIndex]);
       }
   }
 });

From e8f2052ace718e900ae6098b49e0ea75d20fd2b7 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Thu, 28 Dec 2023 20:58:22 -0300
Subject: [PATCH 15/32] Fix a bit more lint

---
 src/plots/cartesian/axes.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 5bc4e95ca87..4f45360e931 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -958,7 +958,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
             if(mockAx.tickmode === 'proportional') {
                 var width = (maxRange - minRange);
                 if(axrev) width *= -1;
-                var offset = !axrev ? minRange : maxRange
+                var offset = !axrev ? minRange : maxRange;
 
                 var currentFractionalVals = [];
                 var currentValsProp;
@@ -1238,7 +1238,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
             ticksOut.push(t);
         }
     }
-    
+
     ticksOut = ticksOut.concat(minorTicks);
 
     ax._inCalcTicks = false;

From 7e93370273c03be6d7319ef90a04373957d8ea0a Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 3 Jan 2024 06:24:57 -0500
Subject: [PATCH 16/32] Change tickmode 'proportional' to 'domain array'

---
 src/plot_api/plot_api.js                 |  2 +-
 src/plots/cartesian/axes.js              | 16 ++++++++--------
 src/plots/cartesian/layout_attributes.js |  4 ++--
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index cadec9351fc..07a486b2efb 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2945,7 +2945,7 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
         // so newContainer won't have them.
         if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
             var tickMode = newContainer.tickmode;
-            if(tickMode === 'auto' || tickMode === 'array' || tickMode === 'proportional' || !tickMode) continue;
+            if(tickMode === 'auto' || tickMode === 'array' || tickMode === 'domain array' || !tickMode) continue;
         }
         // FIXME: Similarly for axis ranges for 3D
         // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 4f45360e931..e1e7e73e02f 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -682,10 +682,10 @@ axes.prepTicks = function(ax, opts) {
             if(ax._name === 'radialaxis') nt *= 2;
         }
 
-        if(!(ax.minor && (ax.minor.tickmode !== 'array' && ax.minor.tickmode !== 'proportional'))) {
+        if(!(ax.minor && (ax.minor.tickmode !== 'array' && ax.minor.tickmode !== 'domain array'))) {
             // add a couple of extra digits for filling in ticks when we
             // have explicit tickvals without tick text
-            if(ax.tickmode === 'array' || ax.tickmode === 'proportional') nt *= 100;
+            if(ax.tickmode === 'array' || ax.tickmode === 'domain array') nt *= 100;
         }
 
         ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt;
@@ -949,13 +949,13 @@ axes.calcTicks = function calcTicks(ax, opts) {
             axes.prepTicks(mockAx, opts);
         }
 
-        // tickmode 'proportional' is just 'array' but with a pre-calc step
+        // tickmode 'domain array' is just 'array' but with a pre-calc step
         // original comment:
         // now that we've figured out the auto values for formatting
         // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'proportional') {
+        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array') {
             // Mapping proportions to array:
-            if(mockAx.tickmode === 'proportional') {
+            if(mockAx.tickmode === 'domain array') {
                 var width = (maxRange - minRange);
                 if(axrev) width *= -1;
                 var offset = !axrev ? minRange : maxRange;
@@ -1248,7 +1248,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
         ticksOut[0].noTick = true;
     }
 
-    // Reset tickvals back to proportional
+    // Reset tickvals back to domain array
     if(tickFractionalVals._isSet) {
         delete tickFractionalVals._isSet;
         Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals);
@@ -1662,7 +1662,7 @@ axes.tickFirst = function(ax, opts) {
 // more precision for hovertext
 axes.tickText = function(ax, x, hover, noSuffixPrefix) {
     var out = tickTextObj(ax, x);
-    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'proportional');
+    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array');
     var extraPrecision = hover || arrayMode;
     var axType = ax.type;
     // TODO multicategory, if we allow ticktext / tickvals
@@ -3378,7 +3378,7 @@ axes.drawGrid = function(gd, ax, opts) {
 
     var counterAx = opts.counterAxis;
     if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
-        var isArrayMode = (ax.tickmode === 'array' || ax.tickmode === 'proportional');
+        var isArrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array');
         for(var i = 0; i < majorVals.length; i++) {
             var xi = majorVals[i].x;
             if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js
index 30f0cc11ecc..767d13fa621 100644
--- a/src/plots/cartesian/layout_attributes.js
+++ b/src/plots/cartesian/layout_attributes.js
@@ -14,7 +14,7 @@ var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
 
 var minorTickmode = {
     valType: 'enumerated',
-    values: ['auto', 'linear', 'array', 'proportional'],
+    values: ['auto', 'linear', 'array', 'domain array'],
     editType: 'ticks',
     impliedEdits: {tick0: undefined, dtick: undefined},
     description: [
@@ -26,7 +26,7 @@ var minorTickmode = {
         'If *array*, the placement of the ticks is set via `tickvals`,',
         'which are actual values, and the tick text is `ticktext`.',
         '(*array* is the default value if `tickvals` is provided).',
-        'If *proportional*, the placement is similiar to *array* except that',
+        'If *domain array*, the placement is similiar to *array* except that',
         '`tickvals` are fractions between 0 and 1 representing distance on',
         'the corresponding axis.'
     ].join(' ')

From afba178df70ee72bd995bfa107b6bb9a07a6e9c4 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 3 Jan 2024 06:48:27 -0500
Subject: [PATCH 17/32] Add tickmode 'full domain'

---
 src/plot_api/plot_api.js                 |  6 +++-
 src/plots/cartesian/axes.js              | 42 ++++++++++++++++++++----
 src/plots/cartesian/layout_attributes.js |  6 +++-
 test/jasmine/tests/pikul_test.js         |  4 +--
 4 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index 07a486b2efb..d8d18e52e49 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2945,7 +2945,11 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
         // so newContainer won't have them.
         if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
             var tickMode = newContainer.tickmode;
-            if(tickMode === 'auto' || tickMode === 'array' || tickMode === 'domain array' || !tickMode) continue;
+            if(tickMode === 'auto'
+              || tickMode === 'array'
+              || tickMode === 'domain array'
+              || tickMode === 'full domain'
+              || !tickMode) continue;
         }
         // FIXME: Similarly for axis ranges for 3D
         // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index e1e7e73e02f..22158e150da 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -682,10 +682,13 @@ axes.prepTicks = function(ax, opts) {
             if(ax._name === 'radialaxis') nt *= 2;
         }
 
-        if(!(ax.minor && (ax.minor.tickmode !== 'array' && ax.minor.tickmode !== 'domain array'))) {
+        if(!(ax.minor &&
+          (ax.minor.tickmode !== 'array' 
+            && ax.minor.tickmode !== 'domain array' 
+            && ax.minor.tickmode !== 'full domain'))) {
             // add a couple of extra digits for filling in ticks when we
             // have explicit tickvals without tick text
-            if(ax.tickmode === 'array' || ax.tickmode === 'domain array') nt *= 100;
+            if(ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain') nt *= 100;
         }
 
         ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt;
@@ -948,14 +951,37 @@ axes.calcTicks = function calcTicks(ax, opts) {
         } else {
             axes.prepTicks(mockAx, opts);
         }
-
+        
+        if(mockAx.tickmode === 'full domain'){
+            nt = mockAx.nticks;  // does mockAx have nitkcs?
+            var tickVals = []; 
+            if (nt == 0) {
+              // pass
+            } else if (nt == 1) {
+                tickVals = [0];
+            } else if (nt == 2) {
+                tickVals = [0, 1];
+            } else {
+                var increment = 1/(nt-2);
+                tickVals.push(0);
+                for (let i = 0; i < nt-2; i++) {
+                    tickVals.push((i+1)*incremente);
+                }
+                tickVals.push(1);
+            }
+            if (major) {
+                Lib.nestedProperty(ax, 'tickvals').set(tickVals);
+            } else {
+                Lib.nestedProperty(ax.minor, 'tickvals').set(tickVals);
+            }
+        }
         // tickmode 'domain array' is just 'array' but with a pre-calc step
         // original comment:
         // now that we've figured out the auto values for formatting
         // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array') {
+        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain' ) {
             // Mapping proportions to array:
-            if(mockAx.tickmode === 'domain array') {
+            if(mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
                 var width = (maxRange - minRange);
                 if(axrev) width *= -1;
                 var offset = !axrev ? minRange : maxRange;
@@ -1251,10 +1277,12 @@ axes.calcTicks = function calcTicks(ax, opts) {
     // Reset tickvals back to domain array
     if(tickFractionalVals._isSet) {
         delete tickFractionalVals._isSet;
+        if (ax.tickmode === 'full domain') tickFractionalVals = [];
         Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals);
     }
     if(minorTickFractionalVals._isSet) {
         delete tickFractionalVals._isSet;
+        if (ax.minor.tickmode === 'full domain') tickFractionalVals = [];
         Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
     }
 
@@ -1662,7 +1690,7 @@ axes.tickFirst = function(ax, opts) {
 // more precision for hovertext
 axes.tickText = function(ax, x, hover, noSuffixPrefix) {
     var out = tickTextObj(ax, x);
-    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array');
+    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
     var extraPrecision = hover || arrayMode;
     var axType = ax.type;
     // TODO multicategory, if we allow ticktext / tickvals
@@ -3378,7 +3406,7 @@ axes.drawGrid = function(gd, ax, opts) {
 
     var counterAx = opts.counterAxis;
     if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
-        var isArrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array');
+        var isArrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
         for(var i = 0; i < majorVals.length; i++) {
             var xi = majorVals[i].x;
             if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js
index 767d13fa621..d00fa6e9d0c 100644
--- a/src/plots/cartesian/layout_attributes.js
+++ b/src/plots/cartesian/layout_attributes.js
@@ -14,7 +14,7 @@ var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
 
 var minorTickmode = {
     valType: 'enumerated',
-    values: ['auto', 'linear', 'array', 'domain array'],
+    values: ['auto', 'linear', 'array', 'domain array', 'full domain'],
     editType: 'ticks',
     impliedEdits: {tick0: undefined, dtick: undefined},
     description: [
@@ -26,6 +26,10 @@ var minorTickmode = {
         'If *array*, the placement of the ticks is set via `tickvals`,',
         'which are actual values, and the tick text is `ticktext`.',
         '(*array* is the default value if `tickvals` is provided).',
+        'If *full domain*, the number of ticks is set bia `nticks` but ticks',
+        'are placed first at both axis ends and then at equal proportions',
+        'between the axis. So `nticks=5` would put ticks at both ends and',
+        'every quarter.',
         'If *domain array*, the placement is similiar to *array* except that',
         '`tickvals` are fractions between 0 and 1 representing distance on',
         'the corresponding axis.'
diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index a20bea58664..2bf48c2f772 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -5,7 +5,7 @@ var createGraphDiv = require('../assets/create_graph_div');
 var destroyGraphDiv = require('../assets/destroy_graph_div');
 
 // Boilerplate taken from axes_test.js
-describe('When generating axes w/ `tickmode`:"proportional",', function() {
+describe('When generating axes w/ `tickmode`:"domain array",', function() {
   var gd;
 
   beforeEach(function() {
@@ -21,7 +21,7 @@ describe('When generating axes w/ `tickmode`:"proportional",', function() {
   function generateTickConfig(tickLen){
     // Intentionally configure to produce a single `(x|y)tick` class per tick
     // labels and tick marks each produce one, so one or the other
-    standardConfig = {tickmode: 'proportional', ticklen: tickLen, showticklabels: false};
+    standardConfig = {tickmode: 'domain array', ticklen: tickLen, showticklabels: false};
 
     // Tick values will be random:
     var n = Math.floor(Math.random() * 100);

From aca7a92d2abd4582b0c2f07a0f6b803bbd24de4b Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 3 Jan 2024 09:45:24 -0500
Subject: [PATCH 18/32] Set default so `full domain` doesn't set to `auto`

---
 src/plots/cartesian/tick_value_defaults.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/plots/cartesian/tick_value_defaults.js b/src/plots/cartesian/tick_value_defaults.js
index 92d83983186..1f4bcf7a174 100644
--- a/src/plots/cartesian/tick_value_defaults.js
+++ b/src/plots/cartesian/tick_value_defaults.js
@@ -25,8 +25,7 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe
         _dtick ? 'linear' :
         'auto';
     var tickmode = coerce(prefix + 'tickmode', tickmodeDefault);
-
-    if(tickmode === 'auto' || tickmode === 'sync') {
+    if(tickmode === 'auto' || tickmode === 'sync' || tickmode === 'full domain') {
         coerce(prefix + 'nticks');
     } else if(tickmode === 'linear') {
         // dtick is usually a positive number, but there are some

From 195dfa59914c639ec1ef25c9307b8c29eb419456 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 3 Jan 2024 09:46:59 -0500
Subject: [PATCH 19/32] Fix various errata/typos

---
 src/plots/cartesian/axes.js | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 22158e150da..353fe408f31 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -951,9 +951,10 @@ axes.calcTicks = function calcTicks(ax, opts) {
         } else {
             axes.prepTicks(mockAx, opts);
         }
-        
+
         if(mockAx.tickmode === 'full domain'){
-            nt = mockAx.nticks;  // does mockAx have nitkcs?
+            var nt = mockAx.nticks;  // does mockAx have nitkcs?
+            if(nt === undefined) nt = 0;
             var tickVals = []; 
             if (nt == 0) {
               // pass
@@ -962,10 +963,10 @@ axes.calcTicks = function calcTicks(ax, opts) {
             } else if (nt == 2) {
                 tickVals = [0, 1];
             } else {
-                var increment = 1/(nt-2);
+                var increment = 1/(nt-1); // (nt-2) + 1
                 tickVals.push(0);
                 for (let i = 0; i < nt-2; i++) {
-                    tickVals.push((i+1)*incremente);
+                    tickVals.push((i+1)*increment);
                 }
                 tickVals.push(1);
             }

From a788ccc50cffb50d5fcf3a1b103a8f61104afcfa Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 3 Jan 2024 09:47:06 -0500
Subject: [PATCH 20/32] Add test for full-domain

---
 test/jasmine/tests/pikul_test.js | 169 +++++++++++++++++++++++++++++--
 1 file changed, 158 insertions(+), 11 deletions(-)

diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index 2bf48c2f772..21243db617a 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -5,7 +5,7 @@ var createGraphDiv = require('../assets/create_graph_div');
 var destroyGraphDiv = require('../assets/destroy_graph_div');
 
 // Boilerplate taken from axes_test.js
-describe('When generating axes w/ `tickmode`:"domain array",', function() {
+describe('Generating ticks with `tickmode`,', function() {
   var gd;
 
   beforeEach(function() {
@@ -18,20 +18,25 @@ describe('When generating axes w/ `tickmode`:"domain array",', function() {
   // Passed as tickLen argument to specify a major or minor tick config
   const MAJOR = 10;
   const MINOR = 5; 
-  function generateTickConfig(tickLen){
+  function generateTickConfig(tickLen, tickmode, nticks){
+    if (tickmode === undefined) tickmode = 'domain array';
     // Intentionally configure to produce a single `(x|y)tick` class per tick
     // labels and tick marks each produce one, so one or the other
-    standardConfig = {tickmode: 'domain array', ticklen: tickLen, showticklabels: false};
+    standardConfig = {tickmode: tickmode, ticklen: tickLen, showticklabels: false};
 
     // Tick values will be random:
-    var n = Math.floor(Math.random() * 100);
-    tickVals = [];
+    if (tickmode === 'domain array') {
+      var n = Math.floor(Math.random() * 100);
+      tickVals = [];
 
-    for(let i = 0; i <= n; i++){
-      intermediate = (Math.trunc(Math.random()*150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
-      tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
+      for(let i = 0; i <= n; i++){
+        intermediate = (Math.trunc(Math.random()*150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
+        tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
+      }
+      standardConfig['tickvals'] = tickVals;
+    } else if (tickmode === 'full domain') {
+      standardConfig['nticks'] = nticks;
     }
-    standardConfig['tickvals'] = tickVals;
     return standardConfig;
   }
   
@@ -63,9 +68,9 @@ describe('When generating axes w/ `tickmode`:"domain array",', function() {
         { type:'date'},
         { type:'category'},
       ];
-      for (let graphTypeIndex = 0; graphTypeIndex < graphTypes.length; graphTypeIndex++) {
+      for(let graphTypeIndex = 0; graphTypeIndex < graphTypes.length; graphTypeIndex++) {
           (function(tickConfig, xGraphType) { // wrap in func or else it() can't see variable because of javascript closure scope
-              it('fraction mapping to geometries for config ' + binaryToTickType(tickConfig) , function(done) {
+              it('"domain array" and config  ' + binaryToTickType(tickConfig) , function(done) {
                   var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
                   var xMinor = tickConfig & XMINOR; // does this config include xminor?
                   var yMajor = tickConfig & YMAJOR; // ... etc
@@ -188,6 +193,148 @@ describe('When generating axes w/ `tickmode`:"domain array",', function() {
                       }
                   }).then(done, done.fail);
               }); 
+              for(let nticks_param = 0; nticks_param < 5; nticks_param++) {
+                  (function(nticks_param) { 
+                      it('"full domain" and config  ' + binaryToTickType(tickConfig) , function(done) {
+                          var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
+                          var xMinor = tickConfig & XMINOR; // does this config include xminor?
+                          var yMajor = tickConfig & YMAJOR; // ... etc
+                          var yMinor = tickConfig & YMINOR;
+                          ticksOff = {ticklen: 0, showticklabels: false};
+                          var xMajorConfig = xMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff; // generate configs
+                          var xMinorConfig = xMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
+                          var yMajorConfig = yMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff;
+                          var yMinorConfig = yMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
+                          var configInfo = "" // for debugging
+                          configInfo += xMajor ? "\n " + `xMajor nticks: ${xMajorConfig['nticks']}` : "";
+                          configInfo += xMinor ? "\n " + `xMinor nticks: ${xMinorConfig['nticks']}` : "";
+                          configInfo += yMajor ? "\n " + `yMajor nticks: ${yMajorConfig['nticks']}` : "";
+                          configInfo += yMinor ? "\n " + `yMinor nticks: ${yMinorConfig['nticks']}` : "";
+
+                          // stolen from axes_test.js
+                          Plotly.newPlot(gd, {
+                              data: [{
+                                  x: [0, 1],
+                                  y: [0, 1]
+                              }],
+                              layout: {
+                                  width: 400,
+                                  height: 400,
+                                  margin: { t: 40, b: 40, l: 40, r: 40, },
+                                  ...xGraphType,
+                                  xaxis: {
+                                      autorange: true,
+                                      ...xMajorConfig, // explode config into this key
+                                      minor: xMinorConfig, // set config to this key
+                                  },
+                                  yaxis: { // same as above
+                                      autorange: true,
+                                      ...yMajorConfig,
+                                      minor: yMinorConfig,
+                                  },
+                          }}).then(function() {
+                              // This regex is for extracting geometric position of a tick
+                              // regex: `.source` converts to string, laid out this way to make for easier reading
+                              const funcName = "translate" + /\(/.source; // literally simplest way to regex '('
+                              const integerPart = /\d+/.source; // numbers left of decimal
+                              const fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
+                              const floatNum = integerPart + fractionalPart; // all together
+                              const any = /.+/.source;
+                              const close = /\)/.source;
+                              const reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
+                              const reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
+
+                              for(let runNumber = 0b1; runNumber <= 0b1000; runNumber <<= 0b1) { // Check all ticks on all axes ☺
+                                  var runInfo = "\n Checking: " + binaryToTickType(runNumber);
+                                  var elementName = "";
+                                  var targetConfig;
+                                  var re;
+                                  if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
+                                      elementName = "xtick";
+                                      targetConfig = xMajorConfig;
+                                      re = reX;
+                                  } else if(runNumber & xMinor) {
+                                      elementName = "xtick";
+                                      targetConfig = xMinorConfig;
+                                      re = reX;
+                                  } else if(runNumber & yMajor) {
+                                      elementName = "ytick";
+                                      targetConfig = yMajorConfig;
+                                      re = reY;
+                                  } else if(runNumber & yMinor) {
+                                      elementName = "ytick";
+                                      targetConfig = yMinorConfig;
+                                      re = reY;
+                                  } else continue; // This run would have been to check ticks that don't exist
+                                  
+                                  var tickElements = document.getElementsByClassName(elementName);
+                                  var nt = targetConfig['nticks'];
+                                  var expectedTickLen = String(targetConfig['ticklen'])
+                                  var tickValsUnique = new Array(); 
+                                  if (nt == 0) {
+                                    // pass
+                                  } else if (nt == 1) {
+                                      tickValsUnique = [0];
+                                  } else if (nt == 2) {
+                                      tickValsUnique = [0, 1];
+                                  } else {
+                                      var increment = 1/(nt-1); // (nt-2) + 1
+                                      tickValsUnique.push(0);
+                                      for (let i = 0; i < nt-2; i++) {
+                                          tickValsUnique.push((i+1)*increment);
+                                      }
+                                      tickValsUnique.push(1);
+                                  }
+                                  // Filter out major/minor and grab geometry
+                                  transformVals = []; // "transform" ie the positional property
+                                  for(let i = 0; i < tickElements.length; i++) {
+                                      if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
+                                      var translate = tickElements[i].getAttribute("transform");
+                                      var match = translate.match(re);
+                                      if (match === null) continue;
+                                      transformVals.push(Number(match[1]));
+                                  }
+                                  
+                                  var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n " +
+                                    `nticks: ${tickValsUnique.length}`;
+                                  
+                                  expect(transformVals.length).toBe(tickValsUnique.length, 
+                                    "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo);
+                                  
+                                  if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
+
+                                  
+                                  // To test geometries without using fixed point or data values...
+                                  // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
+                                  // If x = 0 then y = b, but we may not have a 0 valued x
+                                  // m = (y1 - y2) / (x1 - x2)
+                                  // b = y1 - mx1
+                                  y = transformVals;
+                                  x = tickValsUnique;
+                                  var m, b;
+                                  var b_index = x.indexOf(0);
+                                  
+                                  m = (y[0] - y[1]) / (x[0] - x[1]);
+                                  b = (b_index != -1) ? b = y[b_index] : y[0] - m*x[0];
+                                  
+                                  calculatedY = [];
+                                  for(let i = 0; i < x.length; i++) calculatedY.push(m*x[i] + b);
+
+                                  /* **** Close this comment line to manually inspect output --> 
+                                  yout = [];
+                                  ycalcout = [];
+                                  for (i = 0; i < Math.min(x.length, 10); i++) {
+                                       yout.push(Number.parseFloat(y[i]).toFixed(2));
+                                       ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
+                                  }
+                                  console.log(yout);
+                                  console.log(ycalcout);/* */
+                                  expect(y).toBeCloseToArray(calculatedY, 1, `y=mx+b test failed comparing\n${y}\n${calculatedY}`);
+                              }
+                          }).then(done, done.fail);
+                      }); 
+                  })(nticks_param);
+              }
           })(tickConfig, graphTypes[graphTypeIndex]);
       }
   }

From 3089c4a85c639734b6cb92d2341b30fd19c05526 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Tue, 9 Jan 2024 17:55:17 -0500
Subject: [PATCH 21/32] Restructure parameterization and fix linting

---
 src/plot_api/plot_api.js         |  10 +-
 src/plots/cartesian/axes.js      |  29 +-
 test/jasmine/tests/pikul_test.js | 680 ++++++++++++++++---------------
 3 files changed, 370 insertions(+), 349 deletions(-)

diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index d8d18e52e49..573117820af 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2945,11 +2945,11 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
         // so newContainer won't have them.
         if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
             var tickMode = newContainer.tickmode;
-            if(tickMode === 'auto'
-              || tickMode === 'array'
-              || tickMode === 'domain array'
-              || tickMode === 'full domain'
-              || !tickMode) continue;
+            if(tickMode === 'auto' ||
+              tickMode === 'array' ||
+              tickMode === 'domain array' ||
+              tickMode === 'full domain' ||
+              !tickMode) continue;
         }
         // FIXME: Similarly for axis ranges for 3D
         // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index e919f5f9b51..99353b44b44 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -683,9 +683,9 @@ axes.prepTicks = function(ax, opts) {
         }
 
         if(!(ax.minor &&
-          (ax.minor.tickmode !== 'array' 
-            && ax.minor.tickmode !== 'domain array' 
-            && ax.minor.tickmode !== 'full domain'))) {
+          (ax.minor.tickmode !== 'array' &&
+            ax.minor.tickmode !== 'domain array' &&
+            ax.minor.tickmode !== 'full domain'))) {
             // add a couple of extra digits for filling in ticks when we
             // have explicit tickvals without tick text
             if(ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain') nt *= 100;
@@ -952,25 +952,24 @@ axes.calcTicks = function calcTicks(ax, opts) {
             axes.prepTicks(mockAx, opts);
         }
 
-        if(mockAx.tickmode === 'full domain'){
+        if(mockAx.tickmode === 'full domain') {
             var nt = mockAx.nticks;  // does mockAx have nitkcs?
             if(nt === undefined) nt = 0;
-            var tickVals = []; 
-            if (nt == 0) {
+            if(nt === 0) {
               // pass
-            } else if (nt == 1) {
+            } else if(nt === 1) {
                 tickVals = [0];
-            } else if (nt == 2) {
+            } else if(nt === 2) {
                 tickVals = [0, 1];
             } else {
-                var increment = 1/(nt-1); // (nt-2) + 1
+                var increment = 1 / (nt - 1); // (nt-2) + 1
                 tickVals.push(0);
-                for (let i = 0; i < nt-2; i++) {
-                    tickVals.push((i+1)*increment);
+                for(var tickIndex = 0; tickIndex < nt - 2; tickIndex++) {
+                    tickVals.push((tickIndex + 1) * increment);
                 }
                 tickVals.push(1);
             }
-            if (major) {
+            if(major) {
                 Lib.nestedProperty(ax, 'tickvals').set(tickVals);
             } else {
                 Lib.nestedProperty(ax.minor, 'tickvals').set(tickVals);
@@ -980,7 +979,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
         // original comment:
         // now that we've figured out the auto values for formatting
         // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain' ) {
+        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
             // Mapping proportions to array:
             if(mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
                 var width = (maxRange - minRange);
@@ -1278,12 +1277,12 @@ axes.calcTicks = function calcTicks(ax, opts) {
     // Reset tickvals back to domain array
     if(tickFractionalVals._isSet) {
         delete tickFractionalVals._isSet;
-        if (ax.tickmode === 'full domain') tickFractionalVals = [];
+        if(ax.tickmode === 'full domain') tickFractionalVals = [];
         Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals);
     }
     if(minorTickFractionalVals._isSet) {
         delete tickFractionalVals._isSet;
-        if (ax.minor.tickmode === 'full domain') tickFractionalVals = [];
+        if(ax.minor.tickmode === 'full domain') tickFractionalVals = [];
         Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
     }
 
diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index 21243db617a..4227fdc8611 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -6,336 +6,358 @@ var destroyGraphDiv = require('../assets/destroy_graph_div');
 
 // Boilerplate taken from axes_test.js
 describe('Generating ticks with `tickmode`,', function() {
-  var gd;
-
-  beforeEach(function() {
-      gd = createGraphDiv();
-  });
-
-  afterEach(destroyGraphDiv);
-  
-  // These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
-  // Passed as tickLen argument to specify a major or minor tick config
-  const MAJOR = 10;
-  const MINOR = 5; 
-  function generateTickConfig(tickLen, tickmode, nticks){
-    if (tickmode === undefined) tickmode = 'domain array';
-    // Intentionally configure to produce a single `(x|y)tick` class per tick
-    // labels and tick marks each produce one, so one or the other
-    standardConfig = {tickmode: tickmode, ticklen: tickLen, showticklabels: false};
-
-    // Tick values will be random:
-    if (tickmode === 'domain array') {
-      var n = Math.floor(Math.random() * 100);
-      tickVals = [];
-
-      for(let i = 0; i <= n; i++){
-        intermediate = (Math.trunc(Math.random()*150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
-        tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
-      }
-      standardConfig['tickvals'] = tickVals;
-    } else if (tickmode === 'full domain') {
-      standardConfig['nticks'] = nticks;
+    var gd;
+
+    beforeEach(function() {
+        gd = createGraphDiv();
+    });
+
+    afterEach(destroyGraphDiv);
+
+    /* ***** THIS SECTION ALL HAS TO DO WITH PARAMETERIZATION ***** */
+
+    /* SECTION 1: PARAMETERIZE POSSIBLE TICK CONFIGURATIONS: */
+    // These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
+    // Passed as tickLen argument to specify a major or minor tick config
+    var MAJOR = 10;
+    var MINOR = 5;
+    function generateTickConfig(tickLen, tickmode, nticks) {
+        if(tickmode === undefined) tickmode = 'domain array';
+        // Intentionally configure to produce a single `(x|y)tick` class per tick
+        // labels and tick marks each produce one, so one or the other
+        var standardConfig = {tickmode: tickmode, ticklen: tickLen, showticklabels: false};
+
+        // Tick values will be random:
+        if(tickmode === 'domain array') {
+            var n = Math.floor(Math.random() * 100);
+            var tickVals = [];
+
+            for(var i = 0; i <= n; i++) {
+                var intermediate = (Math.trunc(Math.random() * 150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
+                tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
+            }
+            standardConfig.tickvals = tickVals;
+        } else if(tickmode === 'full domain') {
+            standardConfig.nticks = nticks;
+        }
+        return standardConfig;
+    }
+
+    // See comment below for explanation of parameterization
+    var XMAJOR = 1;// 0b0001;
+    var XMINOR = 2;// 0b0010;
+    var YMAJOR = 4;// 0b0100;
+    var YMINOR = 8;// 0b1000;
+    // Converts binary to list of tick types indicated by binary
+    function binaryToTickType(bin) {
+        var str = [];
+        if(bin & XMAJOR) str.push('xMajor');
+        if(bin & XMINOR) str.push('xMinor');
+        if(bin & YMAJOR) str.push('yMajor');
+        if(bin & YMINOR) str.push('yMinor');
+        if(str.length) {
+            return str.join(', ');
+        }
+        return '';
     }
-    return standardConfig;
-  }
-  
-  // See comment below for explanation of parameterization
-  const XMAJOR = 0b0001;
-  const XMINOR = 0b0010;
-  const YMAJOR = 0b0100;
-  const YMINOR = 0b1000;
-  // Converts binary to list of tick types indicated by binary
-  function binaryToTickType(bin) {
-    str = [];
-    if (bin & XMAJOR) str.push("xMajor");
-    if (bin & XMINOR) str.push("xMinor");
-    if (bin & YMAJOR) str.push("yMajor");
-    if (bin & YMINOR) str.push("yMinor");
-    if (str.length) {
-      return str.join(', ');
+
+    /* SECTION TWO: PARAMETERIZE POSSIBLE TYPES OF GRAPH */
+    var graphTypes = [
+        { type: 'linear' },
+        { type: 'log'},
+        { type: 'date'},
+        { type: 'category'},
+    ];
+
+    /* SECTION THREE: This function will return parameterized values */
+
+    function getParameters(op) {
+        // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
+        // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
+        if (op === undefined) return {tickConfig: 0, graphTypeIndex: 0};
+        if (++op.tickConfig > 15) op.tickConfig = 0;
+        else return op;
+        if (++op.graphTypeIndex >= graphTypes.length) return false;
+        return op;
     }
-    return "";
-  }
-
-  // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
-  // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
-  for(let tickConfig = 1; tickConfig <= 0b1111; tickConfig++) {
-      // Parameterize graph types as well
-      var graphTypes = [
-        { type:'linear' },
-        { type:'log'},
-        { type:'date'},
-        { type:'category'},
-      ];
-      for(let graphTypeIndex = 0; graphTypeIndex < graphTypes.length; graphTypeIndex++) {
-          (function(tickConfig, xGraphType) { // wrap in func or else it() can't see variable because of javascript closure scope
-              it('"domain array" and config  ' + binaryToTickType(tickConfig) , function(done) {
-                  var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
-                  var xMinor = tickConfig & XMINOR; // does this config include xminor?
-                  var yMajor = tickConfig & YMAJOR; // ... etc
-                  var yMinor = tickConfig & YMINOR;
-                  ticksOff = {ticklen: 0, showticklabels: false};
-                  var xMajorConfig = xMajor ? generateTickConfig(MAJOR) : ticksOff; // generate configs
-                  var xMinorConfig = xMinor ? generateTickConfig(MINOR) : ticksOff;
-                  var yMajorConfig = yMajor ? generateTickConfig(MAJOR) : ticksOff;
-                  var yMinorConfig = yMinor ? generateTickConfig(MINOR) : ticksOff;
-                  var configInfo = "" // for debugging
-                  configInfo += xMajor ? "\n " + `xMajor: ${[...new Set(xMajorConfig['tickvals'])].length} unique vals` : "";
-                  configInfo += xMinor ? "\n " + `xMinor: ${[...new Set(xMinorConfig['tickvals'])].length} unique vals` : "";
-                  configInfo += yMajor ? "\n " + `yMajor: ${[...new Set(yMajorConfig['tickvals'])].length} unique vals` : "";
-                  configInfo += yMinor ? "\n " + `yMinor: ${[...new Set(yMinorConfig['tickvals'])].length} unique vals` : "";
-
-                  // stolen from axes_test.js
-                  Plotly.newPlot(gd, {
-                      data: [{
-                          x: [0, 1],
-                          y: [0, 1]
-                      }],
-                      layout: {
-                          width: 400,
-                          height: 400,
-                          margin: { t: 40, b: 40, l: 40, r: 40, },
-                          ...xGraphType,
-                          xaxis: {
-                              autorange: true,
-                              ...xMajorConfig, // explode config into this key
-                              minor: xMinorConfig, // set config to this key
-                          },
-                          yaxis: { // same as above
-                              autorange: true,
-                              ...yMajorConfig,
-                              minor: yMinorConfig,
-                          },
-                  }}).then(function() {
-                      // This regex is for extracting geometric position of a tick
-                      // regex: `.source` converts to string, laid out this way to make for easier reading
-                      const funcName = "translate" + /\(/.source; // literally simplest way to regex '('
-                      const integerPart = /\d+/.source; // numbers left of decimal
-                      const fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                      const floatNum = integerPart + fractionalPart; // all together
-                      const any = /.+/.source;
-                      const close = /\)/.source;
-                      const reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
-                      const reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
-
-                      for(let runNumber = 0b1; runNumber <= 0b1000; runNumber <<= 0b1) { // Check all ticks on all axes ☺
-                          var runInfo = "\n Checking: " + binaryToTickType(runNumber);
-                          var elementName = "";
-                          var targetConfig;
-                          var re;
-                          if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
-                              elementName = "xtick";
-                              targetConfig = xMajorConfig;
-                              re = reX;
-                          } else if(runNumber & xMinor) {
-                              elementName = "xtick";
-                              targetConfig = xMinorConfig;
-                              re = reX;
-                          } else if(runNumber & yMajor) {
-                              elementName = "ytick";
-                              targetConfig = yMajorConfig;
-                              re = reY;
-                          } else if(runNumber & yMinor) {
-                              elementName = "ytick";
-                              targetConfig = yMinorConfig;
-                              re = reY;
-                          } else continue; // This run would have been to check ticks that don't exist
-                          
-                          var tickElements = document.getElementsByClassName(elementName);
-                          var tickValsUnique = [...new Set(targetConfig['tickvals'])];
-                          var expectedTickLen = String(targetConfig['ticklen'])
-
-                          
-                          // Filter out major/minor and grab geometry
-                          transformVals = []; // "transform" ie the positional property
-                          for(let i = 0; i < tickElements.length; i++) {
-                              if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
-                              var translate = tickElements[i].getAttribute("transform");
-                              transformVals.push(Number(translate.match(re)[1]));
-                          }
-                          
-                          var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n " +
-                            `tickVals/Unique: (${targetConfig['tickvals'].length}/${tickValsUnique.length}) {tickValsUnique}`;
-                          
-                          expect(transformVals.length).toBe(tickValsUnique.length, 
+    // DO TESTS
+    it('"domain array" and config', function(done) {
+        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
+            //console.log(parameters); view parameterization
+            var xGraphType = graphTypes[parameters.graphTypeIndex];
+            var tickConfig = parameters.tickConfig;
+            var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
+            var xMinor = tickConfig & XMINOR; // does this config include xminor?
+            var yMajor = tickConfig & YMAJOR; // ... etc
+            var yMinor = tickConfig & YMINOR;
+            var ticksOff = {ticklen: 0, showticklabels: false};
+            var xMajorConfig = xMajor ? generateTickConfig(MAJOR) : ticksOff; // generate configs
+            var xMinorConfig = xMinor ? generateTickConfig(MINOR) : ticksOff;
+            var yMajorConfig = yMajor ? generateTickConfig(MAJOR) : ticksOff;
+            var yMinorConfig = yMinor ? generateTickConfig(MINOR) : ticksOff;
+            var configInfo = ''; // for debugging
+            /* configInfo += xMajor ? '\n ' + 'xMajor: ' + new Set(xMajorConfig.tickvals).length + ' unique vals' : '';
+            configInfo += xMinor ? '\n ' + 'xMinor: ' + new Set(xMinorConfig.tickvals).length + ' unique vals' : '';
+            configInfo += yMajor ? '\n ' + 'yMajor: ' + new Set(yMajorConfig.tickvals).length + ' unique vals' : '';
+            configInfo += yMinor ? '\n ' + 'yMinor: ' + new Set(yMinorConfig.tickvals).length + ' unique vals' : '';*/
+            // TODO no set?
+
+            var plotlyDict = {
+                data: [{
+                    x: [0, 1],
+                    y: [0, 1]
+                }],
+                layout: {
+                    width: 400,
+                    height: 400,
+                    margin: { t: 40, b: 40, l: 40, r: 40, },
+                    type: xGraphType,
+                    xaxis: {
+                        ...xMajorConfig, // explode config into this key
+                        minor: xMinorConfig,
+                    },
+                    yaxis: {
+                        ...yMajorConfig,
+                        minor: yMinorConfig,
+                    },
+                }
+            };
+            // stolen from axes_test.js
+            Plotly.newPlot(gd, plotlyDict).then(function() {
+                // This regex is for extracting geometric position of a tick
+                // regex: `.source` converts to string, laid out this way to make for easier reading
+                var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
+                var integerPart = /\d+/.source; // numbers left of decimal
+                var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
+                var floatNum = integerPart + fractionalPart; // all together
+                var any = /.+/.source;
+                var close = /\)/.source;
+                var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
+                var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
+
+                for(var runNumber = 1; runNumber <= 8; runNumber <<= 1) { // Check all ticks on all axes ☺
+                    var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
+                    var elementName = '';
+                    var targetConfig;
+                    var re;
+                    if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
+                        elementName = 'xtick';
+                        targetConfig = xMajorConfig;
+                        re = reX;
+                    } else if(runNumber & xMinor) {
+                        elementName = 'xtick';
+                        targetConfig = xMinorConfig;
+                        re = reX;
+                    } else if(runNumber & yMajor) {
+                        elementName = 'ytick';
+                        targetConfig = yMajorConfig;
+                        re = reY;
+                    } else if(runNumber & yMinor) {
+                        elementName = 'ytick';
+                        targetConfig = yMinorConfig;
+                        re = reY;
+                    } else continue; // This run would have been to check ticks that don't exist
+
+                    var tickElements = document.getElementsByClassName(elementName);
+                    var tickValsUnique = targetConfig.tickvals.filter(function(value, index, data) { return data.indexOf(value) === index; });
+                    var expectedTickLen = String(targetConfig.ticklen);
+
+
+                    // Filter out major/minor and grab geometry
+                    var transformVals = []; // "transform" ie the positional property
+                    for(var i = 0; i < tickElements.length; i++) {
+                        if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
+                        var translate = tickElements[i].getAttribute('transform');
+                        transformVals.push(Number(translate.match(re)[1]));
+                    }
+
+                    var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
+                        'tickVals/Unique: (' + targetConfig.tickvals.length + '/' + tickValsUnique.length + ') ' + tickValsUnique;
+
+                    expect(transformVals.length).toBe(tickValsUnique.length,
+                        'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
+
+                    if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
+
+
+                    // To test geometries without using fixed point or data values...
+                    // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
+                    // If x = 0 then y = b, but we may not have a 0 valued x
+                    // m = (y1 - y2) / (x1 - x2)
+                    // b = y1 - mx1
+                    var y = transformVals;
+                    var x = tickValsUnique;
+                    var m, b;
+                    var bIndex = x.indexOf(0);
+
+                    m = (y[0] - y[1]) / (x[0] - x[1]);
+                    b = (bIndex !== -1) ? b = y[bIndex] : y[0] - m * x[0];
+
+                    var calculatedY = [];
+                    for(var k = 0; k < x.length; k++) { // linter didn't like I being here
+                        calculatedY.push(m * x[k] + b);
+                    }
+
+                    /* **** Close this comment line to manually inspect output -->
+                  yout = [];
+                  ycalcout = [];
+                  for (i = 0; i < Math.min(x.length, 10); i++) {
+                       yout.push(Number.parseFloat(y[i]).toFixed(2));
+                       ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
+                  }
+                  console.log(yout);
+                  console.log(ycalcout);/* */
+                    expect(y).toBeCloseToArray(calculatedY, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
+                }
+            }).then(done, done.fail);
+        }
+    });
+    it('"full domain" and config ', function(done) {
+        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
+            //console.log(parameters); // view parameterization
+            var xGraphType = graphTypes[parameters.graphTypeIndex];
+            var tickConfig = parameters.tickConfig;
+
+            for(var nticks_param = 0; nticks_param < 5; nticks_param++) { // TODO: We've removed the closure function, move this INSIDE, then move graphtypes inside, then move tickconfig inside
+                // console.log(parameters); // for viewing parameterization
+                // console.log(" " + nticks_param); // for viewing parameterization
+                var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
+                var xMinor = tickConfig & XMINOR; // does this config include xminor?
+                var yMajor = tickConfig & YMAJOR; // ... etc
+                var yMinor = tickConfig & YMINOR;
+                ticksOff = {ticklen: 0, showticklabels: false};
+                var xMajorConfig = xMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff; // generate configs
+                var xMinorConfig = xMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
+                var yMajorConfig = yMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff;
+                var yMinorConfig = yMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
+                var configInfo = "" // for debugging
+                configInfo += xMajor ? "\n " + `xMajor nticks: ${xMajorConfig['nticks']}` : "";
+                configInfo += xMinor ? "\n " + `xMinor nticks: ${xMinorConfig['nticks']}` : "";
+                configInfo += yMajor ? "\n " + `yMajor nticks: ${yMajorConfig['nticks']}` : "";
+                configInfo += yMinor ? "\n " + `yMinor nticks: ${yMinorConfig['nticks']}` : "";
+
+                // stolen from axes_test.js
+                Plotly.newPlot(gd, {
+                    data: [{
+                        x: [0, 1],
+                        y: [0, 1]
+                    }],
+                    layout: {
+                        width: 400,
+                        height: 400,
+                        margin: { t: 40, b: 40, l: 40, r: 40, },
+                        ...xGraphType,
+                        xaxis: {
+                            autorange: true,
+                            ...xMajorConfig, // explode config into this key
+                            minor: xMinorConfig, // set config to this key
+                        },
+                        yaxis: { // same as above
+                            autorange: true,
+                            ...yMajorConfig,
+                            minor: yMinorConfig,
+                        },
+                }}).then(function() {
+                    // This regex is for extracting geometric position of a tick
+                    // regex: `.source` converts to string, laid out this way to make for easier reading
+                    var funcName = "translate" + /\(/.source; // literally simplest way to regex '('
+                    var integerPart = /\d+/.source; // numbers left of decimal
+                    var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
+                    var floatNum = integerPart + fractionalPart; // all together
+                    var any = /.+/.source;
+                    var close = /\)/.source;
+                    var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
+                    var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
+
+                    for(var runNumber = 1; runNumber <= 15; runNumber <<= 1) { // Check all ticks on all axes ☺
+                        var runInfo = "\n Checking: " + binaryToTickType(runNumber);
+                        var elementName = "";
+                        var targetConfig;
+                        var re;
+                        if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
+                            elementName = "xtick";
+                            targetConfig = xMajorConfig;
+                            re = reX;
+                        } else if(runNumber & xMinor) {
+                            elementName = "xtick";
+                            targetConfig = xMinorConfig;
+                            re = reX;
+                        } else if(runNumber & yMajor) {
+                            elementName = "ytick";
+                            targetConfig = yMajorConfig;
+                            re = reY;
+                        } else if(runNumber & yMinor) {
+                            elementName = "ytick";
+                            targetConfig = yMinorConfig;
+                            re = reY;
+                        } else continue; // This run would have been to check ticks that don't exist
+
+                        var tickElements = document.getElementsByClassName(elementName);
+                        var nt = targetConfig['nticks'];
+                        var expectedTickLen = String(targetConfig['ticklen'])
+                        var tickValsUnique = new Array();
+                        if (nt == 0) {
+                            // pass
+                        } else if (nt == 1) {
+                            tickValsUnique = [0];
+                        } else if (nt == 2) {
+                            tickValsUnique = [0, 1];
+                        } else {
+                            var increment = 1/(nt-1); // (nt-2) + 1
+                            tickValsUnique.push(0);
+                            for (var i = 0; i < nt-2; i++) {
+                                tickValsUnique.push((i+1)*increment);
+                            }
+                            tickValsUnique.push(1);
+                        }
+                        // Filter out major/minor and grab geometry
+                        transformVals = []; // "transform" ie the positional property
+                        for(var i = 0; i < tickElements.length; i++) {
+                            if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
+                            var translate = tickElements[i].getAttribute("transform");
+                            var match = translate.match(re);
+                            if (match === null) continue;
+                            transformVals.push(Number(match[1]));
+                        }
+
+                        var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n " +
+                            `nticks: ${tickValsUnique.length}`;
+
+                        expect(transformVals.length).toBe(tickValsUnique.length,
                             "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo);
-                          
-                          if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
-
-                          
-                          // To test geometries without using fixed point or data values...
-                          // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
-                          // If x = 0 then y = b, but we may not have a 0 valued x
-                          // m = (y1 - y2) / (x1 - x2)
-                          // b = y1 - mx1
-                          y = transformVals;
-                          x = tickValsUnique;
-                          var m, b;
-                          var b_index = x.indexOf(0);
-                          
-                          m = (y[0] - y[1]) / (x[0] - x[1]);
-                          b = (b_index != -1) ? b = y[b_index] : y[0] - m*x[0];
-                          
-                          calculatedY = [];
-                          for(let i = 0; i < x.length; i++) calculatedY.push(m*x[i] + b);
-
-                          /* **** Close this comment line to manually inspect output --> 
-                          yout = [];
-                          ycalcout = [];
-                          for (i = 0; i < Math.min(x.length, 10); i++) {
-                               yout.push(Number.parseFloat(y[i]).toFixed(2));
-                               ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
-                          }
-                          console.log(yout);
-                          console.log(ycalcout);/* */
-                          expect(y).toBeCloseToArray(calculatedY, `y=mx+b test failed comparing\n${y}\n${calculatedY}`);
-                      }
-                  }).then(done, done.fail);
-              }); 
-              for(let nticks_param = 0; nticks_param < 5; nticks_param++) {
-                  (function(nticks_param) { 
-                      it('"full domain" and config  ' + binaryToTickType(tickConfig) , function(done) {
-                          var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
-                          var xMinor = tickConfig & XMINOR; // does this config include xminor?
-                          var yMajor = tickConfig & YMAJOR; // ... etc
-                          var yMinor = tickConfig & YMINOR;
-                          ticksOff = {ticklen: 0, showticklabels: false};
-                          var xMajorConfig = xMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff; // generate configs
-                          var xMinorConfig = xMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
-                          var yMajorConfig = yMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff;
-                          var yMinorConfig = yMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
-                          var configInfo = "" // for debugging
-                          configInfo += xMajor ? "\n " + `xMajor nticks: ${xMajorConfig['nticks']}` : "";
-                          configInfo += xMinor ? "\n " + `xMinor nticks: ${xMinorConfig['nticks']}` : "";
-                          configInfo += yMajor ? "\n " + `yMajor nticks: ${yMajorConfig['nticks']}` : "";
-                          configInfo += yMinor ? "\n " + `yMinor nticks: ${yMinorConfig['nticks']}` : "";
-
-                          // stolen from axes_test.js
-                          Plotly.newPlot(gd, {
-                              data: [{
-                                  x: [0, 1],
-                                  y: [0, 1]
-                              }],
-                              layout: {
-                                  width: 400,
-                                  height: 400,
-                                  margin: { t: 40, b: 40, l: 40, r: 40, },
-                                  ...xGraphType,
-                                  xaxis: {
-                                      autorange: true,
-                                      ...xMajorConfig, // explode config into this key
-                                      minor: xMinorConfig, // set config to this key
-                                  },
-                                  yaxis: { // same as above
-                                      autorange: true,
-                                      ...yMajorConfig,
-                                      minor: yMinorConfig,
-                                  },
-                          }}).then(function() {
-                              // This regex is for extracting geometric position of a tick
-                              // regex: `.source` converts to string, laid out this way to make for easier reading
-                              const funcName = "translate" + /\(/.source; // literally simplest way to regex '('
-                              const integerPart = /\d+/.source; // numbers left of decimal
-                              const fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                              const floatNum = integerPart + fractionalPart; // all together
-                              const any = /.+/.source;
-                              const close = /\)/.source;
-                              const reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
-                              const reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
-
-                              for(let runNumber = 0b1; runNumber <= 0b1000; runNumber <<= 0b1) { // Check all ticks on all axes ☺
-                                  var runInfo = "\n Checking: " + binaryToTickType(runNumber);
-                                  var elementName = "";
-                                  var targetConfig;
-                                  var re;
-                                  if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
-                                      elementName = "xtick";
-                                      targetConfig = xMajorConfig;
-                                      re = reX;
-                                  } else if(runNumber & xMinor) {
-                                      elementName = "xtick";
-                                      targetConfig = xMinorConfig;
-                                      re = reX;
-                                  } else if(runNumber & yMajor) {
-                                      elementName = "ytick";
-                                      targetConfig = yMajorConfig;
-                                      re = reY;
-                                  } else if(runNumber & yMinor) {
-                                      elementName = "ytick";
-                                      targetConfig = yMinorConfig;
-                                      re = reY;
-                                  } else continue; // This run would have been to check ticks that don't exist
-                                  
-                                  var tickElements = document.getElementsByClassName(elementName);
-                                  var nt = targetConfig['nticks'];
-                                  var expectedTickLen = String(targetConfig['ticklen'])
-                                  var tickValsUnique = new Array(); 
-                                  if (nt == 0) {
-                                    // pass
-                                  } else if (nt == 1) {
-                                      tickValsUnique = [0];
-                                  } else if (nt == 2) {
-                                      tickValsUnique = [0, 1];
-                                  } else {
-                                      var increment = 1/(nt-1); // (nt-2) + 1
-                                      tickValsUnique.push(0);
-                                      for (let i = 0; i < nt-2; i++) {
-                                          tickValsUnique.push((i+1)*increment);
-                                      }
-                                      tickValsUnique.push(1);
-                                  }
-                                  // Filter out major/minor and grab geometry
-                                  transformVals = []; // "transform" ie the positional property
-                                  for(let i = 0; i < tickElements.length; i++) {
-                                      if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
-                                      var translate = tickElements[i].getAttribute("transform");
-                                      var match = translate.match(re);
-                                      if (match === null) continue;
-                                      transformVals.push(Number(match[1]));
-                                  }
-                                  
-                                  var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n " +
-                                    `nticks: ${tickValsUnique.length}`;
-                                  
-                                  expect(transformVals.length).toBe(tickValsUnique.length, 
-                                    "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo);
-                                  
-                                  if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
-
-                                  
-                                  // To test geometries without using fixed point or data values...
-                                  // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
-                                  // If x = 0 then y = b, but we may not have a 0 valued x
-                                  // m = (y1 - y2) / (x1 - x2)
-                                  // b = y1 - mx1
-                                  y = transformVals;
-                                  x = tickValsUnique;
-                                  var m, b;
-                                  var b_index = x.indexOf(0);
-                                  
-                                  m = (y[0] - y[1]) / (x[0] - x[1]);
-                                  b = (b_index != -1) ? b = y[b_index] : y[0] - m*x[0];
-                                  
-                                  calculatedY = [];
-                                  for(let i = 0; i < x.length; i++) calculatedY.push(m*x[i] + b);
-
-                                  /* **** Close this comment line to manually inspect output --> 
-                                  yout = [];
-                                  ycalcout = [];
-                                  for (i = 0; i < Math.min(x.length, 10); i++) {
-                                       yout.push(Number.parseFloat(y[i]).toFixed(2));
-                                       ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
-                                  }
-                                  console.log(yout);
-                                  console.log(ycalcout);/* */
-                                  expect(y).toBeCloseToArray(calculatedY, 1, `y=mx+b test failed comparing\n${y}\n${calculatedY}`);
-                              }
-                          }).then(done, done.fail);
-                      }); 
-                  })(nticks_param);
-              }
-          })(tickConfig, graphTypes[graphTypeIndex]);
-      }
-  }
+
+                        if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
+
+
+                        // To test geometries without using fixed point or data values...
+                        // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
+                        // If x = 0 then y = b, but we may not have a 0 valued x
+                        // m = (y1 - y2) / (x1 - x2)
+                        // b = y1 - mx1
+                        y = transformVals;
+                        x = tickValsUnique;
+                        var m, b;
+                        var bIndex = x.indexOf(0);
+
+                        m = (y[0] - y[1]) / (x[0] - x[1]);
+                        b = (bIndex != -1) ? b = y[bIndex] : y[0] - m*x[0];
+
+                        calculatedY = [];
+                        for(var i = 0; i < x.length; i++) calculatedY.push(m*x[i] + b);
+
+                        /* **** Close this comment line to manually inspect output -->
+                  yout = [];
+                  ycalcout = [];
+                  for (i = 0; i < Math.min(x.length, 10); i++) {
+                       yout.push(Number.parseFloat(y[i]).toFixed(2));
+                       ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
+                  }
+                  console.log(yout);
+                  console.log(ycalcout);/**/
+                        expect(y).toBeCloseToArray(calculatedY, 1, `y=mx+b test failed comparing\n${y}\n${calculatedY}`);
+                    }
+                }).then(done, done.fail);
+            }
+        }
+    });
 });

From 99d336a9115bbde2cbfdd945f345249469dfc9a7 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Fri, 12 Jan 2024 00:23:10 -0500
Subject: [PATCH 22/32] Intermediate commit w/ partial work- new strat:

So, parameterization is good in testing.

Promises + Jasmine + Karma w/o let or const make it hard to do manually.

There should be a parameterization library.

There is a way to make it all work (moving all loops outside of
it/describe).

Which is good, because then each parameter can be reported as a separate test,
which improves reporting. But that solution is generally unpopular.
---
 test/jasmine/tests/pikul_test.js | 489 +++++++++++++++++--------------
 1 file changed, 263 insertions(+), 226 deletions(-)

diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index 4227fdc8611..097be31f3ff 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -4,6 +4,10 @@ var Plotly = require('../../../lib/index');
 var createGraphDiv = require('../assets/create_graph_div');
 var destroyGraphDiv = require('../assets/destroy_graph_div');
 
+function makeSet(data) { // Returns array of only unique values from array
+    if(data === undefined || data.length === undefined || data.length === 0) return [];
+    return data.filter(function(value, index, array) { return array.indexOf(value) === index; });
+}
 // Boilerplate taken from axes_test.js
 describe('Generating ticks with `tickmode`,', function() {
     var gd;
@@ -21,12 +25,12 @@ describe('Generating ticks with `tickmode`,', function() {
     // Passed as tickLen argument to specify a major or minor tick config
     var MAJOR = 10;
     var MINOR = 5;
+    function ticksOff() { return {ticklen: 0, showticklabels: false, nticks: 0}; } // es5 neither has copy() nor const
     function generateTickConfig(tickLen, tickmode, nticks) {
         if(tickmode === undefined) tickmode = 'domain array';
         // Intentionally configure to produce a single `(x|y)tick` class per tick
         // labels and tick marks each produce one, so one or the other
         var standardConfig = {tickmode: tickmode, ticklen: tickLen, showticklabels: false};
-
         // Tick values will be random:
         if(tickmode === 'domain array') {
             var n = Math.floor(Math.random() * 100);
@@ -40,10 +44,15 @@ describe('Generating ticks with `tickmode`,', function() {
         } else if(tickmode === 'full domain') {
             standardConfig.nticks = nticks;
         }
+        console.log("Generated Config:");
+        console.log(standardConfig);
         return standardConfig;
     }
+    function areTicks(config) {
+        return (config !== undefined && config.ticklen !== undefined && config.ticklen !== 0);
+    }
 
-    // See comment below for explanation of parameterization
+    // numbers 0 through 15 are all possible combination of 4 boolean values (0001, 0010, 0011, 0100, 0101 ,etc)
     var XMAJOR = 1;// 0b0001;
     var XMINOR = 2;// 0b0010;
     var YMAJOR = 4;// 0b0100;
@@ -74,170 +83,36 @@ describe('Generating ticks with `tickmode`,', function() {
     function getParameters(op) {
         // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
         // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
-        if (op === undefined) return {tickConfig: 0, graphTypeIndex: 0};
-        if (++op.tickConfig > 15) op.tickConfig = 0;
+        if(op === undefined) return {tickConfig: 15, graphTypeIndex: 4}; // only run one parameterization
+        if(++op.tickConfig > 15) op.tickConfig = 0;
         else return op;
-        if (++op.graphTypeIndex >= graphTypes.length) return false;
+        if(++op.graphTypeIndex >= graphTypes.length) return false;
         return op;
     }
     // DO TESTS
     it('"domain array" and config', function(done) {
         for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
-            //console.log(parameters); view parameterization
+            console.log("domain array");
+            console.log(parameters); //view parameterization
             var xGraphType = graphTypes[parameters.graphTypeIndex];
             var tickConfig = parameters.tickConfig;
-            var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
-            var xMinor = tickConfig & XMINOR; // does this config include xminor?
-            var yMajor = tickConfig & YMAJOR; // ... etc
-            var yMinor = tickConfig & YMINOR;
-            var ticksOff = {ticklen: 0, showticklabels: false};
-            var xMajorConfig = xMajor ? generateTickConfig(MAJOR) : ticksOff; // generate configs
-            var xMinorConfig = xMinor ? generateTickConfig(MINOR) : ticksOff;
-            var yMajorConfig = yMajor ? generateTickConfig(MAJOR) : ticksOff;
-            var yMinorConfig = yMinor ? generateTickConfig(MINOR) : ticksOff;
-            var configInfo = ''; // for debugging
-            /* configInfo += xMajor ? '\n ' + 'xMajor: ' + new Set(xMajorConfig.tickvals).length + ' unique vals' : '';
-            configInfo += xMinor ? '\n ' + 'xMinor: ' + new Set(xMinorConfig.tickvals).length + ' unique vals' : '';
-            configInfo += yMajor ? '\n ' + 'yMajor: ' + new Set(yMajorConfig.tickvals).length + ' unique vals' : '';
-            configInfo += yMinor ? '\n ' + 'yMinor: ' + new Set(yMinorConfig.tickvals).length + ' unique vals' : '';*/
-            // TODO no set?
-
-            var plotlyDict = {
-                data: [{
-                    x: [0, 1],
-                    y: [0, 1]
-                }],
-                layout: {
-                    width: 400,
-                    height: 400,
-                    margin: { t: 40, b: 40, l: 40, r: 40, },
-                    type: xGraphType,
-                    xaxis: {
-                        ...xMajorConfig, // explode config into this key
-                        minor: xMinorConfig,
-                    },
-                    yaxis: {
-                        ...yMajorConfig,
-                        minor: yMinorConfig,
-                    },
-                }
-            };
-            // stolen from axes_test.js
-            Plotly.newPlot(gd, plotlyDict).then(function() {
-                // This regex is for extracting geometric position of a tick
-                // regex: `.source` converts to string, laid out this way to make for easier reading
-                var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
-                var integerPart = /\d+/.source; // numbers left of decimal
-                var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                var floatNum = integerPart + fractionalPart; // all together
-                var any = /.+/.source;
-                var close = /\)/.source;
-                var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
-                var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
-
-                for(var runNumber = 1; runNumber <= 8; runNumber <<= 1) { // Check all ticks on all axes ☺
-                    var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
-                    var elementName = '';
-                    var targetConfig;
-                    var re;
-                    if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
-                        elementName = 'xtick';
-                        targetConfig = xMajorConfig;
-                        re = reX;
-                    } else if(runNumber & xMinor) {
-                        elementName = 'xtick';
-                        targetConfig = xMinorConfig;
-                        re = reX;
-                    } else if(runNumber & yMajor) {
-                        elementName = 'ytick';
-                        targetConfig = yMajorConfig;
-                        re = reY;
-                    } else if(runNumber & yMinor) {
-                        elementName = 'ytick';
-                        targetConfig = yMinorConfig;
-                        re = reY;
-                    } else continue; // This run would have been to check ticks that don't exist
-
-                    var tickElements = document.getElementsByClassName(elementName);
-                    var tickValsUnique = targetConfig.tickvals.filter(function(value, index, data) { return data.indexOf(value) === index; });
-                    var expectedTickLen = String(targetConfig.ticklen);
-
-
-                    // Filter out major/minor and grab geometry
-                    var transformVals = []; // "transform" ie the positional property
-                    for(var i = 0; i < tickElements.length; i++) {
-                        if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
-                        var translate = tickElements[i].getAttribute('transform');
-                        transformVals.push(Number(translate.match(re)[1]));
-                    }
 
-                    var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
-                        'tickVals/Unique: (' + targetConfig.tickvals.length + '/' + tickValsUnique.length + ') ' + tickValsUnique;
+            var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR) : ticksOff(); // generate configs
+            xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR) : ticksOff();
 
-                    expect(transformVals.length).toBe(tickValsUnique.length,
-                        'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
+            var yConfig = (tickConfig & MAJOR) ? generateTickConfig(MAJOR) : ticksOff();
+            yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR) : ticksOff();
 
-                    if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
-
-
-                    // To test geometries without using fixed point or data values...
-                    // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
-                    // If x = 0 then y = b, but we may not have a 0 valued x
-                    // m = (y1 - y2) / (x1 - x2)
-                    // b = y1 - mx1
-                    var y = transformVals;
-                    var x = tickValsUnique;
-                    var m, b;
-                    var bIndex = x.indexOf(0);
-
-                    m = (y[0] - y[1]) / (x[0] - x[1]);
-                    b = (bIndex !== -1) ? b = y[bIndex] : y[0] - m * x[0];
-
-                    var calculatedY = [];
-                    for(var k = 0; k < x.length; k++) { // linter didn't like I being here
-                        calculatedY.push(m * x[k] + b);
-                    }
-
-                    /* **** Close this comment line to manually inspect output -->
-                  yout = [];
-                  ycalcout = [];
-                  for (i = 0; i < Math.min(x.length, 10); i++) {
-                       yout.push(Number.parseFloat(y[i]).toFixed(2));
-                       ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
-                  }
-                  console.log(yout);
-                  console.log(ycalcout);/* */
-                    expect(y).toBeCloseToArray(calculatedY, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
-                }
-            }).then(done, done.fail);
-        }
-    });
-    it('"full domain" and config ', function(done) {
-        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
-            //console.log(parameters); // view parameterization
-            var xGraphType = graphTypes[parameters.graphTypeIndex];
-            var tickConfig = parameters.tickConfig;
+            var configInfo = ''; // for debugging
+            configInfo += areTicks(xConfig) ? '\n ' + 'xMajor: ' + makeSet(xConfig.tickvals).length + ' unique vals' : '';
+            configInfo += areTicks(xConfig.minor) ? '\n ' + 'xMinor: ' + makeSet(xConfig.minor.tickvals).length + ' unique vals' : '';
+            configInfo += areTicks(yConfig) ? '\n ' + 'yMajor: ' + makeSet(yConfig.tickvals).length + ' unique vals' : '';
+            configInfo += areTicks(yConfig.minor) ? '\n ' + 'yMinor: ' + makeSet(yConfig.minor.tickvals).length + ' unique vals' : '';
 
-            for(var nticks_param = 0; nticks_param < 5; nticks_param++) { // TODO: We've removed the closure function, move this INSIDE, then move graphtypes inside, then move tickconfig inside
-                // console.log(parameters); // for viewing parameterization
-                // console.log(" " + nticks_param); // for viewing parameterization
-                var xMajor = tickConfig & XMAJOR; // does this config include xmajor?
-                var xMinor = tickConfig & XMINOR; // does this config include xminor?
-                var yMajor = tickConfig & YMAJOR; // ... etc
-                var yMinor = tickConfig & YMINOR;
-                ticksOff = {ticklen: 0, showticklabels: false};
-                var xMajorConfig = xMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff; // generate configs
-                var xMinorConfig = xMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
-                var yMajorConfig = yMajor ? generateTickConfig(MAJOR, 'full domain', nticks_param) : ticksOff;
-                var yMinorConfig = yMinor ? generateTickConfig(MINOR, 'full domain', nticks_param) : ticksOff;
-                var configInfo = "" // for debugging
-                configInfo += xMajor ? "\n " + `xMajor nticks: ${xMajorConfig['nticks']}` : "";
-                configInfo += xMinor ? "\n " + `xMinor nticks: ${xMinorConfig['nticks']}` : "";
-                configInfo += yMajor ? "\n " + `yMajor nticks: ${yMajorConfig['nticks']}` : "";
-                configInfo += yMinor ? "\n " + `yMinor nticks: ${yMinorConfig['nticks']}` : "";
-
-                // stolen from axes_test.js
-                Plotly.newPlot(gd, {
+            var variablesToInject = {configInfo: configInfo, gd: gd, parameters: parameters, xconfig: xConfig, yconfig: yConfig, xGraphType: xGraphType};
+            (function(scopeLock) {
+            // stolen from axes_test.js
+                Plotly.newPlot(scopeLock.gd, {
                     data: [{
                         x: [0, 1],
                         y: [0, 1]
@@ -246,21 +121,20 @@ describe('Generating ticks with `tickmode`,', function() {
                         width: 400,
                         height: 400,
                         margin: { t: 40, b: 40, l: 40, r: 40, },
-                        ...xGraphType,
-                        xaxis: {
-                            autorange: true,
-                            ...xMajorConfig, // explode config into this key
-                            minor: xMinorConfig, // set config to this key
-                        },
-                        yaxis: { // same as above
-                            autorange: true,
-                            ...yMajorConfig,
-                            minor: yMinorConfig,
-                        },
-                }}).then(function() {
+                        type: scopelock.xGraphType,
+                        xaxis: scopelock.xConfig, // explode config into this key
+                        yaxis: yConfig,
+                    }
+                }
+
+                ).then(function() {
+                    var tickConfig = scopeLock.parameters.tickConfig;
+                    var xConfig = scopeLock.parameters.xConfig;
+                    var yConfig = scopeLock.parameters.yConfig;
+                    var configInfo = scopeLock.configInfo;
                     // This regex is for extracting geometric position of a tick
                     // regex: `.source` converts to string, laid out this way to make for easier reading
-                    var funcName = "translate" + /\(/.source; // literally simplest way to regex '('
+                    var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
                     var integerPart = /\d+/.source; // numbers left of decimal
                     var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
                     var floatNum = integerPart + fractionalPart; // all together
@@ -269,62 +143,54 @@ describe('Generating ticks with `tickmode`,', function() {
                     var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
                     var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
 
-                    for(var runNumber = 1; runNumber <= 15; runNumber <<= 1) { // Check all ticks on all axes ☺
-                        var runInfo = "\n Checking: " + binaryToTickType(runNumber);
-                        var elementName = "";
+                    for(var runNumber = 1; runNumber <= 8; runNumber <<= 1) { // Check all ticks on all axes ☺
+                        if(!(runNumber & tickConfig)) {
+                            console.log("Continuing because..." + String(runNumber) + " & " + String(tickConfig))
+                            continue;
+                        }
+                        var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
+                        var elementName = '';
                         var targetConfig;
                         var re;
-                        if(runNumber & xMajor) { // ie. (this run wants xMajor) & (xMajor was set in config above)
-                            elementName = "xtick";
-                            targetConfig = xMajorConfig;
+                        if(runNumber & XMAJOR) { // ie. (this run wants xMajor) & (xMajor was set in config above)
+                            elementName = 'xtick';
+                            targetConfig = xConfig;
                             re = reX;
-                        } else if(runNumber & xMinor) {
-                            elementName = "xtick";
-                            targetConfig = xMinorConfig;
+                        } else if(runNumber & XMINOR) {
+                            elementName = 'xtick';
+                            targetConfig = xConfig.minor;
                             re = reX;
-                        } else if(runNumber & yMajor) {
-                            elementName = "ytick";
-                            targetConfig = yMajorConfig;
+                        } else if(runNumber & YMAJOR) {
+                            elementName = 'ytick';
+                            targetConfig = yConfig;
                             re = reY;
-                        } else if(runNumber & yMinor) {
-                            elementName = "ytick";
-                            targetConfig = yMinorConfig;
+                        } else if(runNumber & YMINOR) {
+                            elementName = 'ytick';
+                            targetConfig = yConfig.minor;
                             re = reY;
-                        } else continue; // This run would have been to check ticks that don't exist
-
-                        var tickElements = document.getElementsByClassName(elementName);
-                        var nt = targetConfig['nticks'];
-                        var expectedTickLen = String(targetConfig['ticklen'])
-                        var tickValsUnique = new Array();
-                        if (nt == 0) {
-                            // pass
-                        } else if (nt == 1) {
-                            tickValsUnique = [0];
-                        } else if (nt == 2) {
-                            tickValsUnique = [0, 1];
                         } else {
-                            var increment = 1/(nt-1); // (nt-2) + 1
-                            tickValsUnique.push(0);
-                            for (var i = 0; i < nt-2; i++) {
-                                tickValsUnique.push((i+1)*increment);
-                            }
-                            tickValsUnique.push(1);
+                            console.log("Shouldn't have gotten here");
+                            continue; // This run would have been to check ticks that don't exist
                         }
+
+                        var tickElements = document.getElementsByClassName(elementName);
+                        var tickValsUnique = makeSet(targetConfig.tickvals);
+                        var expectedTickLen = String(targetConfig.ticklen);
+
+
                         // Filter out major/minor and grab geometry
-                        transformVals = []; // "transform" ie the positional property
+                        var transformVals = []; // "transform" ie the positional property
                         for(var i = 0; i < tickElements.length; i++) {
-                            if(!tickElements[i].getAttribute("d").endsWith(expectedTickLen)) continue;
-                            var translate = tickElements[i].getAttribute("transform");
-                            var match = translate.match(re);
-                            if (match === null) continue;
-                            transformVals.push(Number(match[1]));
+                            if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
+                            var translate = tickElements[i].getAttribute('transform');
+                            transformVals.push(Number(translate.match(re)[1]));
                         }
 
-                        var debugInfo = "\n " + `tickElements: (${tickElements.length}) ${tickElements}` + "\n " +
-                            `nticks: ${tickValsUnique.length}`;
+                        var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
+                            'tickVals/Unique: (' + targetConfig.tickvals.length + '/' + tickValsUnique.length + ') ' + tickValsUnique;
 
                         expect(transformVals.length).toBe(tickValsUnique.length,
-                            "filtered tick elements vs tickvals failed" + runInfo + configInfo + debugInfo);
+                            'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
 
                         if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
 
@@ -334,29 +200,200 @@ describe('Generating ticks with `tickmode`,', function() {
                         // If x = 0 then y = b, but we may not have a 0 valued x
                         // m = (y1 - y2) / (x1 - x2)
                         // b = y1 - mx1
-                        y = transformVals;
-                        x = tickValsUnique;
+                        var y = transformVals;
+                        var x = tickValsUnique;
                         var m, b;
                         var bIndex = x.indexOf(0);
 
                         m = (y[0] - y[1]) / (x[0] - x[1]);
-                        b = (bIndex != -1) ? b = y[bIndex] : y[0] - m*x[0];
-
-                        calculatedY = [];
-                        for(var i = 0; i < x.length; i++) calculatedY.push(m*x[i] + b);
-
-                        /* **** Close this comment line to manually inspect output -->
-                  yout = [];
-                  ycalcout = [];
-                  for (i = 0; i < Math.min(x.length, 10); i++) {
-                       yout.push(Number.parseFloat(y[i]).toFixed(2));
-                       ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
-                  }
-                  console.log(yout);
-                  console.log(ycalcout);/**/
-                        expect(y).toBeCloseToArray(calculatedY, 1, `y=mx+b test failed comparing\n${y}\n${calculatedY}`);
+                        b = (bIndex !== -1) ? b = y[bIndex] : y[0] - m * x[0];
+
+                        var calculatedY = [];
+                        for(var k = 0; k < x.length; k++) { // linter didn't like I being here
+                            calculatedY.push(m * x[k] + b);
+                        }
+
+                        /* THIS WOULD BE TO MANUALLY INSPECT OUTPUT */
+                        // yout = [];
+                        // ycalcout = [];
+                        // for (i = 0; i < Math.min(x.length, 10); i++) {
+                        //     yout.push(Number.parseFloat(y[i]).toFixed(2));
+                        //     ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
+                        // }
+                        // console.log(yout);
+                        // console.log(ycalcout);
+                        expect(y).toBeCloseToArray(calculatedY, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
                     }
                 }).then(done, done.fail);
+            })(variablesToInject);
+        }
+    });
+    fit('"full domain" and config ', function(done) {
+        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
+            console.log("\nMain Loop Start:");
+            console.log(parameters); // for viewing parameterization
+            var xGraphType = graphTypes[parameters.graphTypeIndex];
+            var tickConfig = parameters.tickConfig;
+            for(var nTicksParameter = 1; nTicksParameter < 3; nTicksParameter++) { // TODO
+                console.log("Nticks loop start:");
+                console.log(" " + String(nTicksParameter)); // for viewing parameterization
+
+                console.log("xConfig");
+                var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
+                xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
+                console.log("returned");
+                console.log(xConfig);
+                console.log("yConfig");
+                var yConfig = (tickConfig & MAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
+                yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
+                console.log("returned");
+                console.log(yConfig);
+                var variablesToInject = {gd: JSON.parse(JSON.stringify(gd)), xConfig: xConfig, yConfig: yConfig, xGraphType: xGraphType, tickConfig: tickConfig, nTicksParameter: nTicksParameter};
+                (function(scopeLock) {
+                    console.log("Variables received by scope locking function:");
+                    console.log(" nTicks "+ scopeLock.nTicksParameter);
+                    console.log(" xConfig ");
+                    console.log(scopeLock.xConfig);
+                    console.log(" yConfig ")
+                    console.log(scopeLock.yConfig);
+                    console.log(" xGraphType "+ scopeLock.xGraphType);
+                    console.log(" tickConfig "+ scopeLock.tickConfig);
+                    Plotly.newPlot(scopeLock.gd, {
+                        data: [{
+                            x: [0, 1],
+                            y: [0, 1]
+                        }],
+                        layout: {
+                            width: 400,
+                            height: 400,
+                            margin: { t: 40, b: 40, l: 40, r: 40, },
+                            type: scopeLock.xGraphType,
+                            xaxis: scopeLock.xConfig,
+                            yaxis: scopeLock.yConfig,
+                        }
+                    }).then(function() {
+                            console.log("\n-full domain");
+                            console.log("tickConfig: " + String(scopeLock.tickConfig)); // view parameterization
+                            console.log("nTicks: " + String(scopeLock.nTicksParameter));
+                            var tickConfig = scopeLock.tickConfig;
+                            var xConfig = scopeLock.xConfig;
+                            var yConfig = scopeLock.yConfig;
+                            var configInfo = scopeLock.configInfo;
+                            // This regex is for extracting geometric position of a tick
+                            // regex: `.source` converts to string, laid out this way to make for easier reading
+                            var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
+                            var integerPart = /\d+/.source; // numbers left of decimal
+                            var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
+                            var floatNum = integerPart + fractionalPart; // all together
+                            var any = /.+/.source;
+                            var close = /\)/.source;
+                            var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
+                            var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
+
+                            for(var runNumber = 1; runNumber <= 15; runNumber <<= 1) { // Check all ticks on all axes ☺
+                                if(!(runNumber & tickConfig)) {
+                                    console.log("Skipping a run number " + String(runNumber) + " because config doesn't include it");
+                                    continue;
+                                }
+                                var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
+                                var elementName = '';
+                                var targetConfig;
+                                var re;
+                                if(runNumber & XMAJOR) { // ie. (this run wants xMajor) & (xMajor was set in config above)
+                                    elementName = 'xtick';
+                                    targetConfig = xConfig;
+                                    re = reX;
+                                } else if(runNumber & XMINOR) {
+                                    elementName = 'xtick';
+                                    targetConfig = xConfig.minor;
+                                    re = reX;
+                                } else if(runNumber & YMAJOR) {
+                                    elementName = 'ytick';
+                                    targetConfig = yConfig;
+                                    re = reY;
+                                } else if(runNumber & YMINOR) {
+                                    elementName = 'ytick';
+                                    targetConfig = yConfig.minor;
+                                    re = reY;
+                                } else {
+                                    console.log("Shouldn't reach this continue");
+                                    continue;
+                                }
+
+                                var tickElements = scopeLock.gd.getElementsByClassName(elementName); // Document may have changed and there is nothing we can do about that.
+                                console.log("Found elements");
+                                console.log("For run: " + String(runNumber) + " " + runInfo);
+                                console.log(targetConfig);
+                                var nt = targetConfig.nticks;
+                                var expectedTickLen = String(targetConfig.ticklen);
+                                var tickValsUnique = new Array();
+                                if(nt === 0) {
+                                // pass
+                                } else if(nt === 1) {
+                                    tickValsUnique = [0];
+                                } else if(nt === 2) {
+                                    tickValsUnique = [0, 1];
+                                } else {
+                                    var increment = 1 / (nt - 1); // (nt-2) + 1
+                                    tickValsUnique.push(0);
+                                    for(var i = 0; i < nt - 2; i++) {
+                                        tickValsUnique.push((i + 1) * increment);
+                                    }
+                                    tickValsUnique.push(1);
+                                }
+                                console.log("Expecting tick vals: " + String(tickValsUnique));
+                                console.log("Length: " + String(tickValsUnique.length));
+                                // Filter out major/minor and grab geometry
+                                var transformVals = []; // "transform" ie the positional property
+                                console.log("Found ticks: " + String(tickElements.length));
+                                for(var i = 0; i < tickElements.length; i++) {
+                                    if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
+                                    console.log("Found a relevant tick:");
+                                    console.log(tickElements[i]);
+                                    var translate = tickElements[i].getAttribute('transform');
+                                    var match = translate.match(re);
+                                    if(match === null) continue;
+                                    transformVals.push(Number(match[1]));
+                                }
+                                console.log("Found filtered ticks: " + String(transformVals.length));
+                                var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
+                                'nticks: ' + tickValsUnique.length; // Why is this 0
+
+                                expect(transformVals.length).toBe(tickValsUnique.length,
+                                'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
+
+                                if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
+
+
+                                // To test geometries without using fixed point or data values...
+                                // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
+                                // If x = 0 then y = b, but we may not have a 0 valued x
+                                // m = (y1 - y2) / (x1 - x2)
+                                // b = y1 - mx1
+                                var y = transformVals;
+                                var x = tickValsUnique;
+                                var m, b;
+                                var bIndex = x.indexOf(0);
+
+                                m = (y[0] - y[1]) / (x[0] - x[1]);
+                                b = (bIndex != -1) ? b = y[bIndex] : y[0] - m * x[0];
+
+                                calculatedY = [];
+                                for(var i = 0; i < x.length; i++) calculatedY.push(m * x[i] + b);
+
+                                /* **** Close this comment line to manually inspect output --> */
+                                // yout = [];
+                                // ycalcout = [];
+                                // for (i = 0; i < Math.min(x.length, 10); i++) {
+                                //     yout.push(Number.parseFloat(y[i]).toFixed(2));
+                                //     ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
+                                // }
+                                // console.log(yout);
+                                // console.log(ycalcout);
+                                expect(y).toBeCloseToArray(calculatedY, 1, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
+                            }
+                    }).then(done, done.fail);
+                }(variablesToInject));
             }
         }
     });

From 3502d49dd73c2bbbccfdc16e3b16711bcea32e11 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Fri, 12 Jan 2024 00:40:35 -0500
Subject: [PATCH 23/32] Revert branch to equal master

---
 src/plot_api/plot_api.js                   |   6 +-
 src/plots/cartesian/axes.js                |  83 +----
 src/plots/cartesian/layout_attributes.js   |  15 +-
 src/plots/cartesian/tick_value_defaults.js |   3 +-
 test/jasmine/tests/pikul_test.js           | 400 ---------------------
 test/plot-schema.json                      | 246 +++++--------
 6 files changed, 110 insertions(+), 643 deletions(-)
 delete mode 100644 test/jasmine/tests/pikul_test.js

diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index 573117820af..a104e88bb11 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2945,11 +2945,7 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
         // so newContainer won't have them.
         if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
             var tickMode = newContainer.tickmode;
-            if(tickMode === 'auto' ||
-              tickMode === 'array' ||
-              tickMode === 'domain array' ||
-              tickMode === 'full domain' ||
-              !tickMode) continue;
+            if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue;
         }
         // FIXME: Similarly for axis ranges for 3D
         // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index fff20a0a9c4..ac24750aae9 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -682,13 +682,10 @@ axes.prepTicks = function(ax, opts) {
             if(ax._name === 'radialaxis') nt *= 2;
         }
 
-        if(!(ax.minor &&
-          (ax.minor.tickmode !== 'array' &&
-            ax.minor.tickmode !== 'domain array' &&
-            ax.minor.tickmode !== 'full domain'))) {
+        if(!(ax.minor && ax.minor.tickmode !== 'array')) {
             // add a couple of extra digits for filling in ticks when we
             // have explicit tickvals without tick text
-            if(ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain') nt *= 100;
+            if(ax.tickmode === 'array') nt *= 100;
         }
 
         ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt;
@@ -923,12 +920,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
     var minorTicks = [];
 
     var tickVals = [];
-    var tickFractionalVals = [];
-    tickFractionalVals._isSet = false;
-
     var minorTickVals = [];
-    var minorTickFractionalVals = [];
-    minorTickFractionalVals._isSet = false;
 
     var hasMinor = ax.minor && (ax.minor.ticks || ax.minor.showgrid);
 
@@ -952,61 +944,9 @@ axes.calcTicks = function calcTicks(ax, opts) {
             axes.prepTicks(mockAx, opts);
         }
 
-        if(mockAx.tickmode === 'full domain') {
-            var nt = mockAx.nticks;  // does mockAx have nitkcs?
-            if(nt === undefined) nt = 0;
-            if(nt === 0) {
-              // pass
-            } else if(nt === 1) {
-                tickVals = [0];
-            } else if(nt === 2) {
-                tickVals = [0, 1];
-            } else {
-                var increment = 1 / (nt - 1); // (nt-2) + 1
-                tickVals.push(0);
-                for(var tickIndex = 0; tickIndex < nt - 2; tickIndex++) {
-                    tickVals.push((tickIndex + 1) * increment);
-                }
-                tickVals.push(1);
-            }
-            if(major) {
-                Lib.nestedProperty(ax, 'tickvals').set(tickVals);
-            } else {
-                Lib.nestedProperty(ax.minor, 'tickvals').set(tickVals);
-            }
-        }
-        // tickmode 'domain array' is just 'array' but with a pre-calc step
-        // original comment:
         // now that we've figured out the auto values for formatting
         // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
-            // Mapping proportions to array:
-            if(mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
-                var width = (maxRange - minRange);
-                if(axrev) width *= -1;
-                var offset = !axrev ? minRange : maxRange;
-
-                var currentFractionalVals = [];
-                var currentValsProp;
-                if(major) {
-                    currentValsProp = Lib.nestedProperty(ax, 'tickvals'); // Do we need this?
-                    currentFractionalVals = tickFractionalVals = currentValsProp.get();
-                    tickFractionalVals._isSet = true;
-                } else {
-                    currentValsProp = Lib.nestedProperty(ax.minor, 'tickvals');
-                    currentFractionalVals = minorTickFractionalVals = currentValsProp.get();
-                    minorTickFractionalVals._isSet = true;
-                }
-
-                var mappedVals = Lib.simpleMap(currentFractionalVals,
-                    function(fraction, offset, width, type) {
-                        var mapped = offset + (width * fraction);
-                        return (type === 'log') ? Math.pow(10, mapped) : mapped;
-                    }, offset, width, type);
-                currentValsProp.set(mappedVals);
-            }
-
-            // Original 'array' only code
+        if(mockAx.tickmode === 'array') {
             if(major) {
                 tickVals = [];
                 ticksOut = arrayTicks(ax, !isMinor);
@@ -1264,7 +1204,6 @@ axes.calcTicks = function calcTicks(ax, opts) {
             ticksOut.push(t);
         }
     }
-
     ticksOut = ticksOut.concat(minorTicks);
 
     ax._inCalcTicks = false;
@@ -1274,18 +1213,6 @@ axes.calcTicks = function calcTicks(ax, opts) {
         ticksOut[0].noTick = true;
     }
 
-    // Reset tickvals back to domain array
-    if(tickFractionalVals._isSet) {
-        delete tickFractionalVals._isSet;
-        if(ax.tickmode === 'full domain') tickFractionalVals = [];
-        Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals);
-    }
-    if(minorTickFractionalVals._isSet) {
-        delete tickFractionalVals._isSet;
-        if(ax.minor.tickmode === 'full domain') tickFractionalVals = [];
-        Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
-    }
-
     return ticksOut;
 };
 
@@ -1690,7 +1617,7 @@ axes.tickFirst = function(ax, opts) {
 // more precision for hovertext
 axes.tickText = function(ax, x, hover, noSuffixPrefix) {
     var out = tickTextObj(ax, x);
-    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
+    var arrayMode = ax.tickmode === 'array';
     var extraPrecision = hover || arrayMode;
     var axType = ax.type;
     // TODO multicategory, if we allow ticktext / tickvals
@@ -3406,7 +3333,7 @@ axes.drawGrid = function(gd, ax, opts) {
 
     var counterAx = opts.counterAxis;
     if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
-        var isArrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
+        var isArrayMode = ax.tickmode === 'array';
         for(var i = 0; i < majorVals.length; i++) {
             var xi = majorVals[i].x;
             if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js
index d00fa6e9d0c..bfa540f082c 100644
--- a/src/plots/cartesian/layout_attributes.js
+++ b/src/plots/cartesian/layout_attributes.js
@@ -14,7 +14,7 @@ var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
 
 var minorTickmode = {
     valType: 'enumerated',
-    values: ['auto', 'linear', 'array', 'domain array', 'full domain'],
+    values: ['auto', 'linear', 'array'],
     editType: 'ticks',
     impliedEdits: {tick0: undefined, dtick: undefined},
     description: [
@@ -23,16 +23,9 @@ var minorTickmode = {
         'If *linear*, the placement of the ticks is determined by',
         'a starting position `tick0` and a tick step `dtick`',
         '(*linear* is the default value if `tick0` and `dtick` are provided).',
-        'If *array*, the placement of the ticks is set via `tickvals`,',
-        'which are actual values, and the tick text is `ticktext`.',
-        '(*array* is the default value if `tickvals` is provided).',
-        'If *full domain*, the number of ticks is set bia `nticks` but ticks',
-        'are placed first at both axis ends and then at equal proportions',
-        'between the axis. So `nticks=5` would put ticks at both ends and',
-        'every quarter.',
-        'If *domain array*, the placement is similiar to *array* except that',
-        '`tickvals` are fractions between 0 and 1 representing distance on',
-        'the corresponding axis.'
+        'If *array*, the placement of the ticks is set via `tickvals`',
+        'and the tick text is `ticktext`.',
+        '(*array* is the default value if `tickvals` is provided).'
     ].join(' ')
 };
 
diff --git a/src/plots/cartesian/tick_value_defaults.js b/src/plots/cartesian/tick_value_defaults.js
index 25b394587a1..68b9207ee62 100644
--- a/src/plots/cartesian/tick_value_defaults.js
+++ b/src/plots/cartesian/tick_value_defaults.js
@@ -29,7 +29,8 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe
         _dtick ? 'linear' :
         'auto';
     var tickmode = coerce(prefix + 'tickmode', tickmodeDefault);
-    if(tickmode === 'auto' || tickmode === 'sync' || tickmode === 'full domain') {
+
+    if(tickmode === 'auto' || tickmode === 'sync') {
         coerce(prefix + 'nticks');
     } else if(tickmode === 'linear') {
         // dtick is usually a positive number, but there are some
diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
deleted file mode 100644
index 097be31f3ff..00000000000
--- a/test/jasmine/tests/pikul_test.js
+++ /dev/null
@@ -1,400 +0,0 @@
-
-var Plotly = require('../../../lib/index');
-
-var createGraphDiv = require('../assets/create_graph_div');
-var destroyGraphDiv = require('../assets/destroy_graph_div');
-
-function makeSet(data) { // Returns array of only unique values from array
-    if(data === undefined || data.length === undefined || data.length === 0) return [];
-    return data.filter(function(value, index, array) { return array.indexOf(value) === index; });
-}
-// Boilerplate taken from axes_test.js
-describe('Generating ticks with `tickmode`,', function() {
-    var gd;
-
-    beforeEach(function() {
-        gd = createGraphDiv();
-    });
-
-    afterEach(destroyGraphDiv);
-
-    /* ***** THIS SECTION ALL HAS TO DO WITH PARAMETERIZATION ***** */
-
-    /* SECTION 1: PARAMETERIZE POSSIBLE TICK CONFIGURATIONS: */
-    // These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
-    // Passed as tickLen argument to specify a major or minor tick config
-    var MAJOR = 10;
-    var MINOR = 5;
-    function ticksOff() { return {ticklen: 0, showticklabels: false, nticks: 0}; } // es5 neither has copy() nor const
-    function generateTickConfig(tickLen, tickmode, nticks) {
-        if(tickmode === undefined) tickmode = 'domain array';
-        // Intentionally configure to produce a single `(x|y)tick` class per tick
-        // labels and tick marks each produce one, so one or the other
-        var standardConfig = {tickmode: tickmode, ticklen: tickLen, showticklabels: false};
-        // Tick values will be random:
-        if(tickmode === 'domain array') {
-            var n = Math.floor(Math.random() * 100);
-            var tickVals = [];
-
-            for(var i = 0; i <= n; i++) {
-                var intermediate = (Math.trunc(Math.random() * 150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
-                tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
-            }
-            standardConfig.tickvals = tickVals;
-        } else if(tickmode === 'full domain') {
-            standardConfig.nticks = nticks;
-        }
-        console.log("Generated Config:");
-        console.log(standardConfig);
-        return standardConfig;
-    }
-    function areTicks(config) {
-        return (config !== undefined && config.ticklen !== undefined && config.ticklen !== 0);
-    }
-
-    // numbers 0 through 15 are all possible combination of 4 boolean values (0001, 0010, 0011, 0100, 0101 ,etc)
-    var XMAJOR = 1;// 0b0001;
-    var XMINOR = 2;// 0b0010;
-    var YMAJOR = 4;// 0b0100;
-    var YMINOR = 8;// 0b1000;
-    // Converts binary to list of tick types indicated by binary
-    function binaryToTickType(bin) {
-        var str = [];
-        if(bin & XMAJOR) str.push('xMajor');
-        if(bin & XMINOR) str.push('xMinor');
-        if(bin & YMAJOR) str.push('yMajor');
-        if(bin & YMINOR) str.push('yMinor');
-        if(str.length) {
-            return str.join(', ');
-        }
-        return '';
-    }
-
-    /* SECTION TWO: PARAMETERIZE POSSIBLE TYPES OF GRAPH */
-    var graphTypes = [
-        { type: 'linear' },
-        { type: 'log'},
-        { type: 'date'},
-        { type: 'category'},
-    ];
-
-    /* SECTION THREE: This function will return parameterized values */
-
-    function getParameters(op) {
-        // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
-        // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
-        if(op === undefined) return {tickConfig: 15, graphTypeIndex: 4}; // only run one parameterization
-        if(++op.tickConfig > 15) op.tickConfig = 0;
-        else return op;
-        if(++op.graphTypeIndex >= graphTypes.length) return false;
-        return op;
-    }
-    // DO TESTS
-    it('"domain array" and config', function(done) {
-        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
-            console.log("domain array");
-            console.log(parameters); //view parameterization
-            var xGraphType = graphTypes[parameters.graphTypeIndex];
-            var tickConfig = parameters.tickConfig;
-
-            var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR) : ticksOff(); // generate configs
-            xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR) : ticksOff();
-
-            var yConfig = (tickConfig & MAJOR) ? generateTickConfig(MAJOR) : ticksOff();
-            yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR) : ticksOff();
-
-            var configInfo = ''; // for debugging
-            configInfo += areTicks(xConfig) ? '\n ' + 'xMajor: ' + makeSet(xConfig.tickvals).length + ' unique vals' : '';
-            configInfo += areTicks(xConfig.minor) ? '\n ' + 'xMinor: ' + makeSet(xConfig.minor.tickvals).length + ' unique vals' : '';
-            configInfo += areTicks(yConfig) ? '\n ' + 'yMajor: ' + makeSet(yConfig.tickvals).length + ' unique vals' : '';
-            configInfo += areTicks(yConfig.minor) ? '\n ' + 'yMinor: ' + makeSet(yConfig.minor.tickvals).length + ' unique vals' : '';
-
-            var variablesToInject = {configInfo: configInfo, gd: gd, parameters: parameters, xconfig: xConfig, yconfig: yConfig, xGraphType: xGraphType};
-            (function(scopeLock) {
-            // stolen from axes_test.js
-                Plotly.newPlot(scopeLock.gd, {
-                    data: [{
-                        x: [0, 1],
-                        y: [0, 1]
-                    }],
-                    layout: {
-                        width: 400,
-                        height: 400,
-                        margin: { t: 40, b: 40, l: 40, r: 40, },
-                        type: scopelock.xGraphType,
-                        xaxis: scopelock.xConfig, // explode config into this key
-                        yaxis: yConfig,
-                    }
-                }
-
-                ).then(function() {
-                    var tickConfig = scopeLock.parameters.tickConfig;
-                    var xConfig = scopeLock.parameters.xConfig;
-                    var yConfig = scopeLock.parameters.yConfig;
-                    var configInfo = scopeLock.configInfo;
-                    // This regex is for extracting geometric position of a tick
-                    // regex: `.source` converts to string, laid out this way to make for easier reading
-                    var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
-                    var integerPart = /\d+/.source; // numbers left of decimal
-                    var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                    var floatNum = integerPart + fractionalPart; // all together
-                    var any = /.+/.source;
-                    var close = /\)/.source;
-                    var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
-                    var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
-
-                    for(var runNumber = 1; runNumber <= 8; runNumber <<= 1) { // Check all ticks on all axes ☺
-                        if(!(runNumber & tickConfig)) {
-                            console.log("Continuing because..." + String(runNumber) + " & " + String(tickConfig))
-                            continue;
-                        }
-                        var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
-                        var elementName = '';
-                        var targetConfig;
-                        var re;
-                        if(runNumber & XMAJOR) { // ie. (this run wants xMajor) & (xMajor was set in config above)
-                            elementName = 'xtick';
-                            targetConfig = xConfig;
-                            re = reX;
-                        } else if(runNumber & XMINOR) {
-                            elementName = 'xtick';
-                            targetConfig = xConfig.minor;
-                            re = reX;
-                        } else if(runNumber & YMAJOR) {
-                            elementName = 'ytick';
-                            targetConfig = yConfig;
-                            re = reY;
-                        } else if(runNumber & YMINOR) {
-                            elementName = 'ytick';
-                            targetConfig = yConfig.minor;
-                            re = reY;
-                        } else {
-                            console.log("Shouldn't have gotten here");
-                            continue; // This run would have been to check ticks that don't exist
-                        }
-
-                        var tickElements = document.getElementsByClassName(elementName);
-                        var tickValsUnique = makeSet(targetConfig.tickvals);
-                        var expectedTickLen = String(targetConfig.ticklen);
-
-
-                        // Filter out major/minor and grab geometry
-                        var transformVals = []; // "transform" ie the positional property
-                        for(var i = 0; i < tickElements.length; i++) {
-                            if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
-                            var translate = tickElements[i].getAttribute('transform');
-                            transformVals.push(Number(translate.match(re)[1]));
-                        }
-
-                        var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
-                            'tickVals/Unique: (' + targetConfig.tickvals.length + '/' + tickValsUnique.length + ') ' + tickValsUnique;
-
-                        expect(transformVals.length).toBe(tickValsUnique.length,
-                            'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
-
-                        if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
-
-
-                        // To test geometries without using fixed point or data values...
-                        // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
-                        // If x = 0 then y = b, but we may not have a 0 valued x
-                        // m = (y1 - y2) / (x1 - x2)
-                        // b = y1 - mx1
-                        var y = transformVals;
-                        var x = tickValsUnique;
-                        var m, b;
-                        var bIndex = x.indexOf(0);
-
-                        m = (y[0] - y[1]) / (x[0] - x[1]);
-                        b = (bIndex !== -1) ? b = y[bIndex] : y[0] - m * x[0];
-
-                        var calculatedY = [];
-                        for(var k = 0; k < x.length; k++) { // linter didn't like I being here
-                            calculatedY.push(m * x[k] + b);
-                        }
-
-                        /* THIS WOULD BE TO MANUALLY INSPECT OUTPUT */
-                        // yout = [];
-                        // ycalcout = [];
-                        // for (i = 0; i < Math.min(x.length, 10); i++) {
-                        //     yout.push(Number.parseFloat(y[i]).toFixed(2));
-                        //     ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
-                        // }
-                        // console.log(yout);
-                        // console.log(ycalcout);
-                        expect(y).toBeCloseToArray(calculatedY, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
-                    }
-                }).then(done, done.fail);
-            })(variablesToInject);
-        }
-    });
-    fit('"full domain" and config ', function(done) {
-        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
-            console.log("\nMain Loop Start:");
-            console.log(parameters); // for viewing parameterization
-            var xGraphType = graphTypes[parameters.graphTypeIndex];
-            var tickConfig = parameters.tickConfig;
-            for(var nTicksParameter = 1; nTicksParameter < 3; nTicksParameter++) { // TODO
-                console.log("Nticks loop start:");
-                console.log(" " + String(nTicksParameter)); // for viewing parameterization
-
-                console.log("xConfig");
-                var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
-                xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
-                console.log("returned");
-                console.log(xConfig);
-                console.log("yConfig");
-                var yConfig = (tickConfig & MAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
-                yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
-                console.log("returned");
-                console.log(yConfig);
-                var variablesToInject = {gd: JSON.parse(JSON.stringify(gd)), xConfig: xConfig, yConfig: yConfig, xGraphType: xGraphType, tickConfig: tickConfig, nTicksParameter: nTicksParameter};
-                (function(scopeLock) {
-                    console.log("Variables received by scope locking function:");
-                    console.log(" nTicks "+ scopeLock.nTicksParameter);
-                    console.log(" xConfig ");
-                    console.log(scopeLock.xConfig);
-                    console.log(" yConfig ")
-                    console.log(scopeLock.yConfig);
-                    console.log(" xGraphType "+ scopeLock.xGraphType);
-                    console.log(" tickConfig "+ scopeLock.tickConfig);
-                    Plotly.newPlot(scopeLock.gd, {
-                        data: [{
-                            x: [0, 1],
-                            y: [0, 1]
-                        }],
-                        layout: {
-                            width: 400,
-                            height: 400,
-                            margin: { t: 40, b: 40, l: 40, r: 40, },
-                            type: scopeLock.xGraphType,
-                            xaxis: scopeLock.xConfig,
-                            yaxis: scopeLock.yConfig,
-                        }
-                    }).then(function() {
-                            console.log("\n-full domain");
-                            console.log("tickConfig: " + String(scopeLock.tickConfig)); // view parameterization
-                            console.log("nTicks: " + String(scopeLock.nTicksParameter));
-                            var tickConfig = scopeLock.tickConfig;
-                            var xConfig = scopeLock.xConfig;
-                            var yConfig = scopeLock.yConfig;
-                            var configInfo = scopeLock.configInfo;
-                            // This regex is for extracting geometric position of a tick
-                            // regex: `.source` converts to string, laid out this way to make for easier reading
-                            var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
-                            var integerPart = /\d+/.source; // numbers left of decimal
-                            var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                            var floatNum = integerPart + fractionalPart; // all together
-                            var any = /.+/.source;
-                            var close = /\)/.source;
-                            var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
-                            var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
-
-                            for(var runNumber = 1; runNumber <= 15; runNumber <<= 1) { // Check all ticks on all axes ☺
-                                if(!(runNumber & tickConfig)) {
-                                    console.log("Skipping a run number " + String(runNumber) + " because config doesn't include it");
-                                    continue;
-                                }
-                                var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
-                                var elementName = '';
-                                var targetConfig;
-                                var re;
-                                if(runNumber & XMAJOR) { // ie. (this run wants xMajor) & (xMajor was set in config above)
-                                    elementName = 'xtick';
-                                    targetConfig = xConfig;
-                                    re = reX;
-                                } else if(runNumber & XMINOR) {
-                                    elementName = 'xtick';
-                                    targetConfig = xConfig.minor;
-                                    re = reX;
-                                } else if(runNumber & YMAJOR) {
-                                    elementName = 'ytick';
-                                    targetConfig = yConfig;
-                                    re = reY;
-                                } else if(runNumber & YMINOR) {
-                                    elementName = 'ytick';
-                                    targetConfig = yConfig.minor;
-                                    re = reY;
-                                } else {
-                                    console.log("Shouldn't reach this continue");
-                                    continue;
-                                }
-
-                                var tickElements = scopeLock.gd.getElementsByClassName(elementName); // Document may have changed and there is nothing we can do about that.
-                                console.log("Found elements");
-                                console.log("For run: " + String(runNumber) + " " + runInfo);
-                                console.log(targetConfig);
-                                var nt = targetConfig.nticks;
-                                var expectedTickLen = String(targetConfig.ticklen);
-                                var tickValsUnique = new Array();
-                                if(nt === 0) {
-                                // pass
-                                } else if(nt === 1) {
-                                    tickValsUnique = [0];
-                                } else if(nt === 2) {
-                                    tickValsUnique = [0, 1];
-                                } else {
-                                    var increment = 1 / (nt - 1); // (nt-2) + 1
-                                    tickValsUnique.push(0);
-                                    for(var i = 0; i < nt - 2; i++) {
-                                        tickValsUnique.push((i + 1) * increment);
-                                    }
-                                    tickValsUnique.push(1);
-                                }
-                                console.log("Expecting tick vals: " + String(tickValsUnique));
-                                console.log("Length: " + String(tickValsUnique.length));
-                                // Filter out major/minor and grab geometry
-                                var transformVals = []; // "transform" ie the positional property
-                                console.log("Found ticks: " + String(tickElements.length));
-                                for(var i = 0; i < tickElements.length; i++) {
-                                    if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
-                                    console.log("Found a relevant tick:");
-                                    console.log(tickElements[i]);
-                                    var translate = tickElements[i].getAttribute('transform');
-                                    var match = translate.match(re);
-                                    if(match === null) continue;
-                                    transformVals.push(Number(match[1]));
-                                }
-                                console.log("Found filtered ticks: " + String(transformVals.length));
-                                var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
-                                'nticks: ' + tickValsUnique.length; // Why is this 0
-
-                                expect(transformVals.length).toBe(tickValsUnique.length,
-                                'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
-
-                                if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
-
-
-                                // To test geometries without using fixed point or data values...
-                                // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
-                                // If x = 0 then y = b, but we may not have a 0 valued x
-                                // m = (y1 - y2) / (x1 - x2)
-                                // b = y1 - mx1
-                                var y = transformVals;
-                                var x = tickValsUnique;
-                                var m, b;
-                                var bIndex = x.indexOf(0);
-
-                                m = (y[0] - y[1]) / (x[0] - x[1]);
-                                b = (bIndex != -1) ? b = y[bIndex] : y[0] - m * x[0];
-
-                                calculatedY = [];
-                                for(var i = 0; i < x.length; i++) calculatedY.push(m * x[i] + b);
-
-                                /* **** Close this comment line to manually inspect output --> */
-                                // yout = [];
-                                // ycalcout = [];
-                                // for (i = 0; i < Math.min(x.length, 10); i++) {
-                                //     yout.push(Number.parseFloat(y[i]).toFixed(2));
-                                //     ycalcout.push(Number.parseFloat(calculatedY[i]).toFixed(2));
-                                // }
-                                // console.log(yout);
-                                // console.log(ycalcout);
-                                expect(y).toBeCloseToArray(calculatedY, 1, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
-                            }
-                    }).then(done, done.fail);
-                }(variablesToInject));
-            }
-        }
-    });
-});
diff --git a/test/plot-schema.json b/test/plot-schema.json
index 9770d83a38d..9f5b82beb8a 100644
--- a/test/plot-schema.json
+++ b/test/plot-schema.json
@@ -1462,15 +1462,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -4204,15 +4203,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -4870,15 +4868,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -6056,15 +6053,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -6685,15 +6681,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -7314,15 +7309,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -9115,15 +9109,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -9520,15 +9513,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -9931,15 +9923,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -11047,15 +11038,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "ticks": {
@@ -11776,7 +11766,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -11784,7 +11774,6 @@
       "auto",
       "linear",
       "array",
-      "proportional",
       "sync"
      ]
     },
@@ -12389,15 +12378,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "ticks": {
@@ -12869,7 +12857,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -12877,7 +12865,6 @@
       "auto",
       "linear",
       "array",
-      "proportional",
       "sync"
      ]
     },
@@ -13947,15 +13934,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -15497,15 +15483,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -19796,15 +19781,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -20824,15 +20808,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -21881,15 +21864,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -22956,15 +22938,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -24372,15 +24353,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -25359,15 +25339,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -26701,15 +26680,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -28539,15 +28517,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -29738,15 +29715,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -31314,15 +31290,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -32521,15 +32496,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -33692,15 +33666,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -35382,15 +35355,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -36906,15 +36878,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "plot",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -37830,15 +37801,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -39081,15 +39051,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -41104,15 +41073,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -42096,15 +42064,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -45815,15 +45782,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -48097,15 +48063,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -48709,15 +48674,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -50221,15 +50185,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -52139,15 +52102,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -54192,15 +54154,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -56207,15 +56168,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -57446,15 +57406,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -59343,15 +59302,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -61240,15 +61198,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -63176,15 +63133,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -65043,15 +64999,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -66477,15 +66432,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -67977,15 +67931,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -68966,15 +68919,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {
@@ -71367,15 +71319,14 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array",
-        "proportional"
+        "array"
        ]
       },
       "tickprefix": {
@@ -73828,15 +73779,14 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array",
-       "proportional"
+       "array"
       ]
      },
      "tickprefix": {

From 09ebcfc946638891d9026b1a7401bd11e71ba5fc Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Fri, 12 Jan 2024 14:20:14 -0500
Subject: [PATCH 24/32] Lint and refactor

---
 test/jasmine/tests/pikul_test.js | 474 ++++++++++++++++---------------
 1 file changed, 240 insertions(+), 234 deletions(-)

diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index 097be31f3ff..0011e1fb7e5 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -4,115 +4,153 @@ var Plotly = require('../../../lib/index');
 var createGraphDiv = require('../assets/create_graph_div');
 var destroyGraphDiv = require('../assets/destroy_graph_div');
 
-function makeSet(data) { // Returns array of only unique values from array
+
+// ******* UTILTIES ******* //
+
+// makeSet() returns array copy w/o duplicates.
+function makeSet(data) {
     if(data === undefined || data.length === undefined || data.length === 0) return [];
     return data.filter(function(value, index, array) { return array.indexOf(value) === index; });
 }
-// Boilerplate taken from axes_test.js
-describe('Generating ticks with `tickmode`,', function() {
-    var gd;
-
-    beforeEach(function() {
-        gd = createGraphDiv();
-    });
-
-    afterEach(destroyGraphDiv);
-
-    /* ***** THIS SECTION ALL HAS TO DO WITH PARAMETERIZATION ***** */
-
-    /* SECTION 1: PARAMETERIZE POSSIBLE TICK CONFIGURATIONS: */
-    // These enums are `ticklen`s- it's how DOM analysis differentiates wrt major/minor
-    // Passed as tickLen argument to specify a major or minor tick config
-    var MAJOR = 10;
-    var MINOR = 5;
-    function ticksOff() { return {ticklen: 0, showticklabels: false, nticks: 0}; } // es5 neither has copy() nor const
-    function generateTickConfig(tickLen, tickmode, nticks) {
-        if(tickmode === undefined) tickmode = 'domain array';
-        // Intentionally configure to produce a single `(x|y)tick` class per tick
-        // labels and tick marks each produce one, so one or the other
-        var standardConfig = {tickmode: tickmode, ticklen: tickLen, showticklabels: false};
-        // Tick values will be random:
-        if(tickmode === 'domain array') {
-            var n = Math.floor(Math.random() * 100);
-            var tickVals = [];
-
-            for(var i = 0; i <= n; i++) {
-                var intermediate = (Math.trunc(Math.random() * 150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
-                tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
-            }
-            standardConfig.tickvals = tickVals;
-        } else if(tickmode === 'full domain') {
-            standardConfig.nticks = nticks;
-        }
-        console.log("Generated Config:");
-        console.log(standardConfig);
-        return standardConfig;
-    }
-    function areTicks(config) {
-        return (config !== undefined && config.ticklen !== undefined && config.ticklen !== 0);
-    }
 
-    // numbers 0 through 15 are all possible combination of 4 boolean values (0001, 0010, 0011, 0100, 0101 ,etc)
-    var XMAJOR = 1;// 0b0001;
-    var XMINOR = 2;// 0b0010;
-    var YMAJOR = 4;// 0b0100;
-    var YMINOR = 8;// 0b1000;
-    // Converts binary to list of tick types indicated by binary
-    function binaryToTickType(bin) {
-        var str = [];
-        if(bin & XMAJOR) str.push('xMajor');
-        if(bin & XMINOR) str.push('xMinor');
-        if(bin & YMAJOR) str.push('yMajor');
-        if(bin & YMINOR) str.push('yMinor');
-        if(str.length) {
-            return str.join(', ');
+// `reX` is regex to get position of an 'xtick', `reY` is for a 'ytick'
+// Note: `.source` converts regex to string, laid out this way to make for easier reading
+var funcName = 'translate' + /\(/.source; // "translate("
+var integerPart = /\d+/.source; // numbers left of decimal
+var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
+var floatNum = integerPart + fractionalPart; // all together
+var any = /.+/.source; // any text
+var close = /\)/.source; // ")"
+var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens '(', '),' are regex capture symbols not characters
+var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
+
+
+/* ****** PARAMETERIZATION ******* */
+
+/* TICK CONFIG GENERATORS */
+var MAJOR = 10; // `ticklen:10`
+var MINOR = 5; // `ticklen:5`
+
+// ticksOff() generates a config for no ticks
+function ticksOff() { return {ticklen: 0, showticklabels: false, nticks: 0}; }
+
+// generateTickConfig() can generate randomized but valid configs for `tickmode` "domain array" and "full domain"
+function generateTickConfig(tickLen, tickmode, nticks) {
+    if(tickmode === undefined) tickmode = 'domain array'; // default
+
+    var standardConfig = {tickmode: tickmode, ticklen: tickLen, showticklabels: false}; // no labels!
+    // We analyze DOM to find number and position of ticks, labels make it harder.
+
+
+    // Tick values will be random:
+    if(tickmode === 'domain array') { // 'domain array' will have random tick proportions
+        var n = Math.floor(Math.random() * 100);
+        var tickVals = [];
+
+        for(var i = 0; i <= n; i++) {
+            // NOTE: MEANT TO BE DIFFERENT EVERYTIME
+            var intermediate = (Math.trunc(Math.random() * 150) - 25) / 100; // Number between -.25 and 1.25 w/ 2 decimals max
+            tickVals.push(Math.min(Math.max(intermediate, 0), 1)); // 2 decimal number between 0 and 1 w/ higher odds of 0 or 1
         }
-        return '';
+        standardConfig.tickvals = tickVals;
+    } else if(tickmode === 'full domain') { // TODO: full domain _could_ have a random number of ticks
+        standardConfig.nticks = nticks;
     }
+    return standardConfig;
+}
 
-    /* SECTION TWO: PARAMETERIZE POSSIBLE TYPES OF GRAPH */
-    var graphTypes = [
-        { type: 'linear' },
-        { type: 'log'},
-        { type: 'date'},
-        { type: 'category'},
-    ];
-
-    /* SECTION THREE: This function will return parameterized values */
-
-    function getParameters(op) {
-        // the var `tickConfig` represents every possible configuration. It is in an int 0-15.
-        // The binary is 0001, 0010, 0011, etc. IE every possible combination of 4 booleans.
-        if(op === undefined) return {tickConfig: 15, graphTypeIndex: 4}; // only run one parameterization
-        if(++op.tickConfig > 15) op.tickConfig = 0;
-        else return op;
-        if(++op.graphTypeIndex >= graphTypes.length) return false;
-        return op;
+// areTicks() returns true if `config`, an axis config, contains ticks: false otherwise.
+function areTicks(config) { // Check if ticks exists in a generated config
+    return (config !== undefined && config.ticklen !== undefined && config.ticklen !== 0);
+}
+
+/* LOOP THROUGH ALL POSSIBLE COMBINATIONS:
+ * xAxis major has ticks, xAxis minor has ticks, yAxis major does not, yAxis minor does, etc */
+
+// numbers 0 through 15 are all possible combination of 4 boolean values (0001, 0010, 0011, 0100, 0101, etc)
+var XMAJOR = 1;// 0b0001;
+var XMINOR = 2;// 0b0010;
+var YMAJOR = 4;// 0b0100;
+var YMINOR = 8;// 0b1000;
+
+// binaryToTickType converts binary to info string
+function binaryToTickType(bin) {
+    var str = [];
+    if(bin & XMAJOR) str.push('xMajor');
+    if(bin & XMINOR) str.push('xMinor');
+    if(bin & YMAJOR) str.push('yMajor');
+    if(bin & YMINOR) str.push('yMinor');
+    if(str.length) {
+        return str.join(', ');
     }
-    // DO TESTS
-    it('"domain array" and config', function(done) {
-        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
-            console.log("domain array");
-            console.log(parameters); //view parameterization
-            var xGraphType = graphTypes[parameters.graphTypeIndex];
-            var tickConfig = parameters.tickConfig;
-
-            var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR) : ticksOff(); // generate configs
-            xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR) : ticksOff();
-
-            var yConfig = (tickConfig & MAJOR) ? generateTickConfig(MAJOR) : ticksOff();
-            yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR) : ticksOff();
-
-            var configInfo = ''; // for debugging
-            configInfo += areTicks(xConfig) ? '\n ' + 'xMajor: ' + makeSet(xConfig.tickvals).length + ' unique vals' : '';
-            configInfo += areTicks(xConfig.minor) ? '\n ' + 'xMinor: ' + makeSet(xConfig.minor.tickvals).length + ' unique vals' : '';
-            configInfo += areTicks(yConfig) ? '\n ' + 'yMajor: ' + makeSet(yConfig.tickvals).length + ' unique vals' : '';
-            configInfo += areTicks(yConfig.minor) ? '\n ' + 'yMinor: ' + makeSet(yConfig.minor.tickvals).length + ' unique vals' : '';
-
-            var variablesToInject = {configInfo: configInfo, gd: gd, parameters: parameters, xconfig: xConfig, yconfig: yConfig, xGraphType: xGraphType};
-            (function(scopeLock) {
-            // stolen from axes_test.js
-                Plotly.newPlot(scopeLock.gd, {
+    return 'None';
+}
+
+/* PARAMETERIZE POSSIBLE TYPES OF GRAPH */
+var graphTypes = [
+    { type: 'linear' },
+    { type: 'log'},
+    { type: 'date'},
+    { type: 'category'},
+];
+
+/* getParameters() will loop through all possible parameters, initializing it the first time, and return false the last */
+/* it's for for-loops */
+function getParameters(op) {
+    // Initializize
+    if(op === undefined) return {tickConfig: 0, graphTypeIndex: 0};
+
+    // Loop through 15 possible tickConfigs
+    if(++op.tickConfig > 15) op.tickConfig = 0;
+    else return op;
+
+    // Loop through 4 graph types after each full loop above
+    if(++op.graphTypeIndex >= graphTypes.length) return false;
+    return op;
+}
+// Loops MUST be outside tests do to scopes (and better for output, honestly)
+for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
+    // Give parameters there own variable
+    var xGraphType = graphTypes[parameters.graphTypeIndex];
+    var tickConfig = parameters.tickConfig;
+
+    // Linters don't like variable redeclaration in subscope so make all testing same scope
+    var paramInfo = 'on axes ' + binaryToTickType(tickConfig) + ' for graph type: ' + xGraphType.type;
+
+    var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR) : ticksOff(); // generate configs
+    xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR) : ticksOff();
+
+    var yConfig = (tickConfig & YMAJOR) ? generateTickConfig(MAJOR) : ticksOff();
+    yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR) : ticksOff();
+
+    // Configs are random, so we should inspect if test fails:
+    var configInfo = '';
+    configInfo += areTicks(xConfig) ? '\n ' + 'xMajor: ' + makeSet(xConfig.tickvals).length + ' unique vals' : '';
+    configInfo += areTicks(xConfig.minor) ? '\n ' + 'xMinor: ' + makeSet(xConfig.minor.tickvals).length + ' unique vals' : '';
+    configInfo += areTicks(yConfig) ? '\n ' + 'yMajor: ' + makeSet(yConfig.tickvals).length + ' unique vals' : '';
+    configInfo += areTicks(yConfig.minor) ? '\n ' + 'yMinor: ' + makeSet(yConfig.minor.tickvals).length + ' unique vals' : '';
+
+    // variablesToInject + closure function(scopeLock) is a necessary result of using a version w promises but w/o `let`
+    var variablesToInject = {
+        xConfig: xConfig, // Generated xConfig
+        yConfig: yConfig, // Generated yConfig
+        xGraphType: xGraphType, // graphType parameter
+        tickConfig: tickConfig, // tickConfig parameter
+        paramInfo: paramInfo, // info string
+        configInfo: configInfo // info string
+    };
+    (function(scopeLock) {
+        describe('`tickmode`:"domain array"', function() {
+            var gd;
+
+            beforeEach(function() {
+                gd = createGraphDiv();
+            });
+
+            afterEach(destroyGraphDiv);
+
+            it('should create ticks correctly ' + scopeLock.paramInfo, function(done) {
+                Plotly.newPlot(gd, {
                     data: [{
                         x: [0, 1],
                         y: [0, 1]
@@ -121,37 +159,26 @@ describe('Generating ticks with `tickmode`,', function() {
                         width: 400,
                         height: 400,
                         margin: { t: 40, b: 40, l: 40, r: 40, },
-                        type: scopelock.xGraphType,
-                        xaxis: scopelock.xConfig, // explode config into this key
-                        yaxis: yConfig,
+                        type: scopeLock.xGraphType,
+                        xaxis: scopeLock.xConfig,
+                        yaxis: scopeLock.yConfig,
                     }
                 }
-
                 ).then(function() {
-                    var tickConfig = scopeLock.parameters.tickConfig;
-                    var xConfig = scopeLock.parameters.xConfig;
-                    var yConfig = scopeLock.parameters.yConfig;
+                    var tickConfig = scopeLock.tickConfig;
+                    var xConfig = scopeLock.xConfig;
+                    var yConfig = scopeLock.yConfig;
                     var configInfo = scopeLock.configInfo;
-                    // This regex is for extracting geometric position of a tick
-                    // regex: `.source` converts to string, laid out this way to make for easier reading
-                    var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
-                    var integerPart = /\d+/.source; // numbers left of decimal
-                    var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                    var floatNum = integerPart + fractionalPart; // all together
-                    var any = /.+/.source;
-                    var close = /\)/.source;
-                    var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
-                    var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
-
-                    for(var runNumber = 1; runNumber <= 8; runNumber <<= 1) { // Check all ticks on all axes ☺
-                        if(!(runNumber & tickConfig)) {
-                            console.log("Continuing because..." + String(runNumber) + " & " + String(tickConfig))
-                            continue;
-                        }
-                        var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
+                    for(var runNumber = 1; runNumber <= 15; runNumber <<= 1) {
+                        if(!(runNumber & tickConfig)) continue;
+                        var debugInfo = 'Configured Axes: ' + binaryToTickType(tickConfig);
+                        debugInfo += '\n Checking: ' + binaryToTickType(runNumber);
+
                         var elementName = '';
                         var targetConfig;
                         var re;
+
+                        // Determine which runNumber we're in
                         if(runNumber & XMAJOR) { // ie. (this run wants xMajor) & (xMajor was set in config above)
                             elementName = 'xtick';
                             targetConfig = xConfig;
@@ -168,33 +195,31 @@ describe('Generating ticks with `tickmode`,', function() {
                             elementName = 'ytick';
                             targetConfig = yConfig.minor;
                             re = reY;
-                        } else {
-                            console.log("Shouldn't have gotten here");
-                            continue; // This run would have been to check ticks that don't exist
-                        }
-
-                        var tickElements = document.getElementsByClassName(elementName);
+                        } else continue;
+                        var tickElements = gd.getElementsByClassName(elementName);
                         var tickValsUnique = makeSet(targetConfig.tickvals);
                         var expectedTickLen = String(targetConfig.ticklen);
 
+                        // This is the info I want to see on any failed test
+                        debugInfo += '\n Found ' + String(tickElements.length) + ' tick DOM elements.';
+                        debugInfo += '\n Expecting ' + String(tickValsUnique.length) + ' legitimate elements:';
+                        debugInfo += String(tickValsUnique);
+                        debugInfo += '\n Original Length: ' + String(targetConfig.tickvals.length);
 
                         // Filter out major/minor and grab geometry
                         var transformVals = []; // "transform" ie the positional property
-                        for(var i = 0; i < tickElements.length; i++) {
+                        for(var i = 0; i < tickElements.length; i++) { // TODO it is helpful to dump html here if there is a problem
                             if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
                             var translate = tickElements[i].getAttribute('transform');
                             transformVals.push(Number(translate.match(re)[1]));
                         }
-
-                        var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
-                            'tickVals/Unique: (' + targetConfig.tickvals.length + '/' + tickValsUnique.length + ') ' + tickValsUnique;
+                        debugInfo += '\n Filtered Elements Length: ' + String(transformVals.length) + ':';
+                        debugInfo += transformVals;
 
                         expect(transformVals.length).toBe(tickValsUnique.length,
-                            'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
-
+                            'filtered tick elements vs tickvals failed\n' + debugInfo + configInfo);
                         if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
 
-
                         // To test geometries without using fixed point or data values...
                         // we can check consistency of y = mx+b! (y is DOM position, x is proportion)
                         // If x = 0 then y = b, but we may not have a 0 valued x
@@ -225,80 +250,67 @@ describe('Generating ticks with `tickmode`,', function() {
                         expect(y).toBeCloseToArray(calculatedY, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
                     }
                 }).then(done, done.fail);
-            })(variablesToInject);
-        }
-    });
-    fit('"full domain" and config ', function(done) {
-        for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
-            console.log("\nMain Loop Start:");
-            console.log(parameters); // for viewing parameterization
-            var xGraphType = graphTypes[parameters.graphTypeIndex];
-            var tickConfig = parameters.tickConfig;
-            for(var nTicksParameter = 1; nTicksParameter < 3; nTicksParameter++) { // TODO
-                console.log("Nticks loop start:");
-                console.log(" " + String(nTicksParameter)); // for viewing parameterization
-
-                console.log("xConfig");
-                var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
-                xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
-                console.log("returned");
-                console.log(xConfig);
-                console.log("yConfig");
-                var yConfig = (tickConfig & MAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
-                yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
-                console.log("returned");
-                console.log(yConfig);
-                var variablesToInject = {gd: JSON.parse(JSON.stringify(gd)), xConfig: xConfig, yConfig: yConfig, xGraphType: xGraphType, tickConfig: tickConfig, nTicksParameter: nTicksParameter};
-                (function(scopeLock) {
-                    console.log("Variables received by scope locking function:");
-                    console.log(" nTicks "+ scopeLock.nTicksParameter);
-                    console.log(" xConfig ");
-                    console.log(scopeLock.xConfig);
-                    console.log(" yConfig ")
-                    console.log(scopeLock.yConfig);
-                    console.log(" xGraphType "+ scopeLock.xGraphType);
-                    console.log(" tickConfig "+ scopeLock.tickConfig);
-                    Plotly.newPlot(scopeLock.gd, {
-                        data: [{
-                            x: [0, 1],
-                            y: [0, 1]
-                        }],
-                        layout: {
-                            width: 400,
-                            height: 400,
-                            margin: { t: 40, b: 40, l: 40, r: 40, },
-                            type: scopeLock.xGraphType,
-                            xaxis: scopeLock.xConfig,
-                            yaxis: scopeLock.yConfig,
-                        }
-                    }).then(function() {
-                            console.log("\n-full domain");
-                            console.log("tickConfig: " + String(scopeLock.tickConfig)); // view parameterization
-                            console.log("nTicks: " + String(scopeLock.nTicksParameter));
+            });
+        });
+    })(variablesToInject);
+}
+
+// One loop should be separated from the other loop by scope, but we still have not `let`!
+(function() {
+    for(var parameters = getParameters(); parameters; parameters = getParameters(parameters)) {
+        // Give parameters there own variable
+        var xGraphType = graphTypes[parameters.graphTypeIndex];
+        var tickConfig = parameters.tickConfig;
+        // This next test has another parameter, since we can test it with multiple numbers of ticks
+        for(var nTicksParameter = 0; nTicksParameter < 5; nTicksParameter++) {
+            var xConfig = (tickConfig & XMAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
+            xConfig.minor = (tickConfig & XMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
+            var yConfig = (tickConfig & MAJOR) ? generateTickConfig(MAJOR, 'full domain', nTicksParameter) : ticksOff();
+            yConfig.minor = (tickConfig & YMINOR) ? generateTickConfig(MINOR, 'full domain', nTicksParameter) : ticksOff();
+
+            var paramInfo = 'on axes ' + binaryToTickType(tickConfig) + ' with ' + String(nTicksParameter) + ' ticks each, for graph type: ' + xGraphType.type;
+
+            // variablesToInject + closurer function(scopeLock) is a necessary result of using a version w promises but w/o `let`
+            var variablesToInject = {xConfig: xConfig, yConfig: yConfig, xGraphType: xGraphType, tickConfig: tickConfig, nTicksParameter: nTicksParameter, paramInfo: paramInfo};
+            (function(scopeLock) {
+                describe('`tickmode`:"full domain"', function() {
+                    var gd;
+
+                    beforeEach(function() {
+                        gd = createGraphDiv();
+                    });
+
+                    afterEach(destroyGraphDiv);
+
+                    it('should create ticks correctly ' + scopeLock.paramInfo, function(done) {
+                        Plotly.newPlot(gd, {
+                            data: [{
+                                x: [0, 1],
+                                y: [0, 1]
+                            }],
+                            layout: {
+                                width: 400,
+                                height: 400,
+                                margin: { t: 40, b: 40, l: 40, r: 40, },
+                                type: scopeLock.xGraphType,
+                                xaxis: scopeLock.xConfig,
+                                yaxis: scopeLock.yConfig,
+                            }
+                        }).then(function() {
                             var tickConfig = scopeLock.tickConfig;
                             var xConfig = scopeLock.xConfig;
                             var yConfig = scopeLock.yConfig;
-                            var configInfo = scopeLock.configInfo;
-                            // This regex is for extracting geometric position of a tick
-                            // regex: `.source` converts to string, laid out this way to make for easier reading
-                            var funcName = 'translate' + /\(/.source; // literally simplest way to regex '('
-                            var integerPart = /\d+/.source; // numbers left of decimal
-                            var fractionalPart = /(?:\.\d+)?/.source; // decimal + numbers to right
-                            var floatNum = integerPart + fractionalPart; // all together
-                            var any = /.+/.source;
-                            var close = /\)/.source;
-                            var reX = new RegExp(funcName + '(' + floatNum + '),' + any + close); // parens () are capture not fn()
-                            var reY = new RegExp(funcName + any + ',(' + floatNum + ')' + close);
-
-                            for(var runNumber = 1; runNumber <= 15; runNumber <<= 1) { // Check all ticks on all axes ☺
-                                if(!(runNumber & tickConfig)) {
-                                    console.log("Skipping a run number " + String(runNumber) + " because config doesn't include it");
-                                    continue;
-                                }
+
+                            // This for loop only executes four times! It's bitshift, not increment! It's to checks all 4 axes.
+                            for(var runNumber = 1; runNumber <= 15; runNumber <<= 1) {
+                                if(!(runNumber & tickConfig)) continue;
                                 var runInfo = '\n Checking: ' + binaryToTickType(runNumber);
+
                                 var elementName = '';
                                 var targetConfig;
                                 var re;
+
+                                // Determine which runNumber we're in
                                 if(runNumber & XMAJOR) { // ie. (this run wants xMajor) & (xMajor was set in config above)
                                     elementName = 'xtick';
                                     targetConfig = xConfig;
@@ -315,20 +327,14 @@ describe('Generating ticks with `tickmode`,', function() {
                                     elementName = 'ytick';
                                     targetConfig = yConfig.minor;
                                     re = reY;
-                                } else {
-                                    console.log("Shouldn't reach this continue");
-                                    continue;
-                                }
+                                } else continue;
 
-                                var tickElements = scopeLock.gd.getElementsByClassName(elementName); // Document may have changed and there is nothing we can do about that.
-                                console.log("Found elements");
-                                console.log("For run: " + String(runNumber) + " " + runInfo);
-                                console.log(targetConfig);
+                                // Determine where ticks _should be_
                                 var nt = targetConfig.nticks;
                                 var expectedTickLen = String(targetConfig.ticklen);
                                 var tickValsUnique = new Array();
                                 if(nt === 0) {
-                                // pass
+                                    // pass
                                 } else if(nt === 1) {
                                     tickValsUnique = [0];
                                 } else if(nt === 2) {
@@ -341,26 +347,25 @@ describe('Generating ticks with `tickmode`,', function() {
                                     }
                                     tickValsUnique.push(1);
                                 }
-                                console.log("Expecting tick vals: " + String(tickValsUnique));
-                                console.log("Length: " + String(tickValsUnique.length));
-                                // Filter out major/minor and grab geometry
+
+                                // Get actual geometries
+                                var tickElements = gd.getElementsByClassName(elementName);
                                 var transformVals = []; // "transform" ie the positional property
-                                console.log("Found ticks: " + String(tickElements.length));
-                                for(var i = 0; i < tickElements.length; i++) {
-                                    if(!tickElements[i].getAttribute('d').endsWith(expectedTickLen)) continue;
-                                    console.log("Found a relevant tick:");
-                                    console.log(tickElements[i]);
-                                    var translate = tickElements[i].getAttribute('transform');
+
+                                // Figure out which ticks are relevant to us for this config
+                                for(var j = 0; j < tickElements.length; j++) {
+                                    if(!tickElements[j].getAttribute('d').endsWith(expectedTickLen)) continue;
+                                    var translate = tickElements[j].getAttribute('transform');
                                     var match = translate.match(re);
                                     if(match === null) continue;
                                     transformVals.push(Number(match[1]));
                                 }
-                                console.log("Found filtered ticks: " + String(transformVals.length));
+
                                 var debugInfo = '\n ' + 'tickElements: (' + tickElements.length + ') ' + tickElements + '\n ' +
-                                'nticks: ' + tickValsUnique.length; // Why is this 0
+                                    'nticks: ' + tickValsUnique.length; // Could contain whole html, more helpful
 
                                 expect(transformVals.length).toBe(tickValsUnique.length,
-                                'filtered tick elements vs tickvals failed' + runInfo + configInfo + debugInfo);
+                                    'filtered tick elements vs tickvals failed' + runInfo + + debugInfo);
 
                                 if(transformVals.length < 2) return; // Can't test proportions with < 2 ticks (since no fixed reference)
 
@@ -376,10 +381,10 @@ describe('Generating ticks with `tickmode`,', function() {
                                 var bIndex = x.indexOf(0);
 
                                 m = (y[0] - y[1]) / (x[0] - x[1]);
-                                b = (bIndex != -1) ? b = y[bIndex] : y[0] - m * x[0];
+                                b = (bIndex !== -1) ? b = y[bIndex] : y[0] - m * x[0];
 
-                                calculatedY = [];
-                                for(var i = 0; i < x.length; i++) calculatedY.push(m * x[i] + b);
+                                var calculatedY = [];
+                                for(var k = 0; k < x.length; k++) calculatedY.push(m * x[k] + b);
 
                                 /* **** Close this comment line to manually inspect output --> */
                                 // yout = [];
@@ -392,9 +397,10 @@ describe('Generating ticks with `tickmode`,', function() {
                                 // console.log(ycalcout);
                                 expect(y).toBeCloseToArray(calculatedY, 1, 'y=mx+b test failed comparing\n' + y + '\n' + calculatedY);
                             }
-                    }).then(done, done.fail);
-                }(variablesToInject));
-            }
+                        }).then(done, done.fail);
+                    });
+                });
+            }(variablesToInject));
         }
-    });
-});
+    }
+})();

From eb8272353fcf50712c17fa0827f87130058a28e0 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Fri, 12 Jan 2024 14:40:26 -0500
Subject: [PATCH 25/32] Revert completely to old tick algo

---
 src/plot_api/plot_api.js                   |   6 +-
 src/plots/cartesian/axes.js                |  83 ++++++-
 src/plots/cartesian/layout_attributes.js   |  15 +-
 src/plots/cartesian/tick_value_defaults.js |   3 +-
 test/plot-schema.json                      | 246 +++++++++++++--------
 5 files changed, 243 insertions(+), 110 deletions(-)

diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index a104e88bb11..573117820af 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2945,7 +2945,11 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
         // so newContainer won't have them.
         if((key === 'tick0' || key === 'dtick') && outerparts[0] !== 'geo') {
             var tickMode = newContainer.tickmode;
-            if(tickMode === 'auto' || tickMode === 'array' || !tickMode) continue;
+            if(tickMode === 'auto' ||
+              tickMode === 'array' ||
+              tickMode === 'domain array' ||
+              tickMode === 'full domain' ||
+              !tickMode) continue;
         }
         // FIXME: Similarly for axis ranges for 3D
         // contourcarpet doesn't HAVE zmin/zmax, they're just auto-added. It needs them.
diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index ac24750aae9..fff20a0a9c4 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -682,10 +682,13 @@ axes.prepTicks = function(ax, opts) {
             if(ax._name === 'radialaxis') nt *= 2;
         }
 
-        if(!(ax.minor && ax.minor.tickmode !== 'array')) {
+        if(!(ax.minor &&
+          (ax.minor.tickmode !== 'array' &&
+            ax.minor.tickmode !== 'domain array' &&
+            ax.minor.tickmode !== 'full domain'))) {
             // add a couple of extra digits for filling in ticks when we
             // have explicit tickvals without tick text
-            if(ax.tickmode === 'array') nt *= 100;
+            if(ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain') nt *= 100;
         }
 
         ax._roughDTick = Math.abs(rng[1] - rng[0]) / nt;
@@ -920,7 +923,12 @@ axes.calcTicks = function calcTicks(ax, opts) {
     var minorTicks = [];
 
     var tickVals = [];
+    var tickFractionalVals = [];
+    tickFractionalVals._isSet = false;
+
     var minorTickVals = [];
+    var minorTickFractionalVals = [];
+    minorTickFractionalVals._isSet = false;
 
     var hasMinor = ax.minor && (ax.minor.ticks || ax.minor.showgrid);
 
@@ -944,9 +952,61 @@ axes.calcTicks = function calcTicks(ax, opts) {
             axes.prepTicks(mockAx, opts);
         }
 
+        if(mockAx.tickmode === 'full domain') {
+            var nt = mockAx.nticks;  // does mockAx have nitkcs?
+            if(nt === undefined) nt = 0;
+            if(nt === 0) {
+              // pass
+            } else if(nt === 1) {
+                tickVals = [0];
+            } else if(nt === 2) {
+                tickVals = [0, 1];
+            } else {
+                var increment = 1 / (nt - 1); // (nt-2) + 1
+                tickVals.push(0);
+                for(var tickIndex = 0; tickIndex < nt - 2; tickIndex++) {
+                    tickVals.push((tickIndex + 1) * increment);
+                }
+                tickVals.push(1);
+            }
+            if(major) {
+                Lib.nestedProperty(ax, 'tickvals').set(tickVals);
+            } else {
+                Lib.nestedProperty(ax.minor, 'tickvals').set(tickVals);
+            }
+        }
+        // tickmode 'domain array' is just 'array' but with a pre-calc step
+        // original comment:
         // now that we've figured out the auto values for formatting
         // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array') {
+        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
+            // Mapping proportions to array:
+            if(mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
+                var width = (maxRange - minRange);
+                if(axrev) width *= -1;
+                var offset = !axrev ? minRange : maxRange;
+
+                var currentFractionalVals = [];
+                var currentValsProp;
+                if(major) {
+                    currentValsProp = Lib.nestedProperty(ax, 'tickvals'); // Do we need this?
+                    currentFractionalVals = tickFractionalVals = currentValsProp.get();
+                    tickFractionalVals._isSet = true;
+                } else {
+                    currentValsProp = Lib.nestedProperty(ax.minor, 'tickvals');
+                    currentFractionalVals = minorTickFractionalVals = currentValsProp.get();
+                    minorTickFractionalVals._isSet = true;
+                }
+
+                var mappedVals = Lib.simpleMap(currentFractionalVals,
+                    function(fraction, offset, width, type) {
+                        var mapped = offset + (width * fraction);
+                        return (type === 'log') ? Math.pow(10, mapped) : mapped;
+                    }, offset, width, type);
+                currentValsProp.set(mappedVals);
+            }
+
+            // Original 'array' only code
             if(major) {
                 tickVals = [];
                 ticksOut = arrayTicks(ax, !isMinor);
@@ -1204,6 +1264,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
             ticksOut.push(t);
         }
     }
+
     ticksOut = ticksOut.concat(minorTicks);
 
     ax._inCalcTicks = false;
@@ -1213,6 +1274,18 @@ axes.calcTicks = function calcTicks(ax, opts) {
         ticksOut[0].noTick = true;
     }
 
+    // Reset tickvals back to domain array
+    if(tickFractionalVals._isSet) {
+        delete tickFractionalVals._isSet;
+        if(ax.tickmode === 'full domain') tickFractionalVals = [];
+        Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals);
+    }
+    if(minorTickFractionalVals._isSet) {
+        delete tickFractionalVals._isSet;
+        if(ax.minor.tickmode === 'full domain') tickFractionalVals = [];
+        Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
+    }
+
     return ticksOut;
 };
 
@@ -1617,7 +1690,7 @@ axes.tickFirst = function(ax, opts) {
 // more precision for hovertext
 axes.tickText = function(ax, x, hover, noSuffixPrefix) {
     var out = tickTextObj(ax, x);
-    var arrayMode = ax.tickmode === 'array';
+    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
     var extraPrecision = hover || arrayMode;
     var axType = ax.type;
     // TODO multicategory, if we allow ticktext / tickvals
@@ -3333,7 +3406,7 @@ axes.drawGrid = function(gd, ax, opts) {
 
     var counterAx = opts.counterAxis;
     if(counterAx && axes.shouldShowZeroLine(gd, ax, counterAx)) {
-        var isArrayMode = ax.tickmode === 'array';
+        var isArrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
         for(var i = 0; i < majorVals.length; i++) {
             var xi = majorVals[i].x;
             if(isArrayMode ? !xi : (Math.abs(xi) < ax.dtick / 100)) {
diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js
index bfa540f082c..d00fa6e9d0c 100644
--- a/src/plots/cartesian/layout_attributes.js
+++ b/src/plots/cartesian/layout_attributes.js
@@ -14,7 +14,7 @@ var DAY_OF_WEEK = constants.WEEKDAY_PATTERN;
 
 var minorTickmode = {
     valType: 'enumerated',
-    values: ['auto', 'linear', 'array'],
+    values: ['auto', 'linear', 'array', 'domain array', 'full domain'],
     editType: 'ticks',
     impliedEdits: {tick0: undefined, dtick: undefined},
     description: [
@@ -23,9 +23,16 @@ var minorTickmode = {
         'If *linear*, the placement of the ticks is determined by',
         'a starting position `tick0` and a tick step `dtick`',
         '(*linear* is the default value if `tick0` and `dtick` are provided).',
-        'If *array*, the placement of the ticks is set via `tickvals`',
-        'and the tick text is `ticktext`.',
-        '(*array* is the default value if `tickvals` is provided).'
+        'If *array*, the placement of the ticks is set via `tickvals`,',
+        'which are actual values, and the tick text is `ticktext`.',
+        '(*array* is the default value if `tickvals` is provided).',
+        'If *full domain*, the number of ticks is set bia `nticks` but ticks',
+        'are placed first at both axis ends and then at equal proportions',
+        'between the axis. So `nticks=5` would put ticks at both ends and',
+        'every quarter.',
+        'If *domain array*, the placement is similiar to *array* except that',
+        '`tickvals` are fractions between 0 and 1 representing distance on',
+        'the corresponding axis.'
     ].join(' ')
 };
 
diff --git a/src/plots/cartesian/tick_value_defaults.js b/src/plots/cartesian/tick_value_defaults.js
index 68b9207ee62..25b394587a1 100644
--- a/src/plots/cartesian/tick_value_defaults.js
+++ b/src/plots/cartesian/tick_value_defaults.js
@@ -29,8 +29,7 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe
         _dtick ? 'linear' :
         'auto';
     var tickmode = coerce(prefix + 'tickmode', tickmodeDefault);
-
-    if(tickmode === 'auto' || tickmode === 'sync') {
+    if(tickmode === 'auto' || tickmode === 'sync' || tickmode === 'full domain') {
         coerce(prefix + 'nticks');
     } else if(tickmode === 'linear') {
         // dtick is usually a positive number, but there are some
diff --git a/test/plot-schema.json b/test/plot-schema.json
index 9f5b82beb8a..9770d83a38d 100644
--- a/test/plot-schema.json
+++ b/test/plot-schema.json
@@ -1462,14 +1462,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -4203,14 +4204,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -4868,14 +4870,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -6053,14 +6056,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -6681,14 +6685,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -7309,14 +7314,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -9109,14 +9115,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -9513,14 +9520,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -9923,14 +9931,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -11038,14 +11047,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "ticks": {
@@ -11766,7 +11776,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -11774,6 +11784,7 @@
       "auto",
       "linear",
       "array",
+      "proportional",
       "sync"
      ]
     },
@@ -12378,14 +12389,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "ticks": {
@@ -12857,7 +12869,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -12865,6 +12877,7 @@
       "auto",
       "linear",
       "array",
+      "proportional",
       "sync"
      ]
     },
@@ -13934,14 +13947,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -15483,14 +15497,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -19781,14 +19796,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -20808,14 +20824,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -21864,14 +21881,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -22938,14 +22956,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -24353,14 +24372,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -25339,14 +25359,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -26680,14 +26701,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -28517,14 +28539,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -29715,14 +29738,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -31290,14 +31314,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -32496,14 +32521,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -33666,14 +33692,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -35355,14 +35382,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -36878,14 +36906,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "plot",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -37801,14 +37830,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -39051,14 +39081,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -41073,14 +41104,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -42064,14 +42096,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -45782,14 +45815,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -48063,14 +48097,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -48674,14 +48709,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -50185,14 +50221,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -52102,14 +52139,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -54154,14 +54192,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -56168,14 +56207,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -57406,14 +57446,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -59302,14 +59343,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -61198,14 +61240,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -63133,14 +63176,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -64999,14 +65043,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -66432,14 +66477,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -67931,14 +67977,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -68919,14 +68966,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {
@@ -71319,14 +71367,15 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
        "values": [
         "auto",
         "linear",
-        "array"
+        "array",
+        "proportional"
        ]
       },
       "tickprefix": {
@@ -73779,14 +73828,15 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals` and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided).",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
       "values": [
        "auto",
        "linear",
-       "array"
+       "array",
+       "proportional"
       ]
      },
      "tickprefix": {

From fe6f8d05828a789205448376c2d0f32ef71652a2 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Fri, 12 Jan 2024 16:13:36 -0500
Subject: [PATCH 26/32] update git-scheme diff

---
 test/plot-schema.json | 250 +++++++++++++++++++++++++-----------------
 1 file changed, 150 insertions(+), 100 deletions(-)

diff --git a/test/plot-schema.json b/test/plot-schema.json
index 9770d83a38d..743ac71e7da 100644
--- a/test/plot-schema.json
+++ b/test/plot-schema.json
@@ -1462,7 +1462,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -1470,7 +1470,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -4204,7 +4205,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -4212,7 +4213,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -4870,7 +4872,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -4878,7 +4880,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -6056,7 +6059,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -6064,7 +6067,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -6685,7 +6689,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -6693,7 +6697,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -7314,7 +7319,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -7322,7 +7327,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -9115,7 +9121,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -9123,7 +9129,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -9520,7 +9527,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -9528,7 +9535,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -9931,7 +9939,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "plot",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -9939,7 +9947,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -11047,7 +11056,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -11055,7 +11064,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "ticks": {
@@ -11776,7 +11786,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -11784,7 +11794,8 @@
       "auto",
       "linear",
       "array",
-      "proportional",
+      "domain array",
+      "full domain",
       "sync"
      ]
     },
@@ -12389,7 +12400,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "ticks",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -12397,7 +12408,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "ticks": {
@@ -12869,7 +12881,7 @@
      "valType": "number"
     },
     "tickmode": {
-     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
+     "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis. If *sync*, the number of ticks will sync with the overlayed axis set by `overlaying` property.",
      "editType": "ticks",
      "impliedEdits": {},
      "valType": "enumerated",
@@ -12877,7 +12889,8 @@
       "auto",
       "linear",
       "array",
-      "proportional",
+      "domain array",
+      "full domain",
       "sync"
      ]
     },
@@ -13947,7 +13960,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -13955,7 +13968,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -15497,7 +15511,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -15505,7 +15519,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -19796,7 +19811,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -19804,7 +19819,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -20824,7 +20840,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -20832,7 +20848,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -21881,7 +21898,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -21889,7 +21906,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -22956,7 +22974,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -22964,7 +22982,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -24372,7 +24391,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -24380,7 +24399,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -25359,7 +25379,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -25367,7 +25387,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -26701,7 +26722,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -26709,7 +26730,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -28539,7 +28561,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -28547,7 +28569,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -29738,7 +29761,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -29746,7 +29769,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -31314,7 +31338,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -31322,7 +31346,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -32521,7 +32546,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -32529,7 +32554,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -33692,7 +33718,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -33700,7 +33726,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -35382,7 +35409,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -35390,7 +35417,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -36906,7 +36934,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "plot",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -36914,7 +36942,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -37830,7 +37859,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -37838,7 +37867,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -39081,7 +39111,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -39089,7 +39119,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -41104,7 +41135,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -41112,7 +41143,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -42096,7 +42128,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -42104,7 +42136,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -45815,7 +45848,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -45823,7 +45856,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -48097,7 +48131,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -48105,7 +48139,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -48709,7 +48744,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -48717,7 +48752,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -50221,7 +50257,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -50229,7 +50265,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -52139,7 +52176,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -52147,7 +52184,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -54192,7 +54230,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -54200,7 +54238,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -56207,7 +56246,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -56215,7 +56254,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -57446,7 +57486,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -57454,7 +57494,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -59343,7 +59384,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "calc",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -59351,7 +59392,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -61240,7 +61282,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -61248,7 +61290,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -63176,7 +63219,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -63184,7 +63227,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -65043,7 +65087,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -65051,7 +65095,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -66477,7 +66522,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "colorbars",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -66485,7 +66530,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -67977,7 +68023,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -67985,7 +68031,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -68966,7 +69013,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -68974,7 +69021,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {
@@ -71367,7 +71415,7 @@
        "valType": "number"
       },
       "tickmode": {
-       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+       "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
        "editType": "colorbars",
        "impliedEdits": {},
        "valType": "enumerated",
@@ -71375,7 +71423,8 @@
         "auto",
         "linear",
         "array",
-        "proportional"
+        "domain array",
+        "full domain"
        ]
       },
       "tickprefix": {
@@ -73828,7 +73877,7 @@
       "valType": "number"
      },
      "tickmode": {
-      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *proportional*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
+      "description": "Sets the tick mode for this axis. If *auto*, the number of ticks is set via `nticks`. If *linear*, the placement of the ticks is determined by a starting position `tick0` and a tick step `dtick` (*linear* is the default value if `tick0` and `dtick` are provided). If *array*, the placement of the ticks is set via `tickvals`, which are actual values, and the tick text is `ticktext`. (*array* is the default value if `tickvals` is provided). If *full domain*, the number of ticks is set bia `nticks` but ticks are placed first at both axis ends and then at equal proportions between the axis. So `nticks=5` would put ticks at both ends and every quarter. If *domain array*, the placement is similiar to *array* except that `tickvals` are fractions between 0 and 1 representing distance on the corresponding axis.",
       "editType": "calc",
       "impliedEdits": {},
       "valType": "enumerated",
@@ -73836,7 +73885,8 @@
        "auto",
        "linear",
        "array",
-       "proportional"
+       "domain array",
+       "full domain"
       ]
      },
      "tickprefix": {

From 7176049a33dd039e5970a171fc4bdf8631c51662 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 24 Jan 2024 21:11:30 -0500
Subject: [PATCH 27/32] Remove category test in prep for disallowing

---
 test/jasmine/tests/pikul_test.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/test/jasmine/tests/pikul_test.js b/test/jasmine/tests/pikul_test.js
index 0011e1fb7e5..903b31c4579 100644
--- a/test/jasmine/tests/pikul_test.js
+++ b/test/jasmine/tests/pikul_test.js
@@ -91,7 +91,6 @@ var graphTypes = [
     { type: 'linear' },
     { type: 'log'},
     { type: 'date'},
-    { type: 'category'},
 ];
 
 /* getParameters() will loop through all possible parameters, initializing it the first time, and return false the last */

From 15387ed3f8a8a1bd9cc0d7631730c951b8356f99 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 24 Jan 2024 21:11:41 -0500
Subject: [PATCH 28/32] Refactor as to not override ax.tickvals:

- Use private ax._mappedTickvals instead
- Remove use of Lib.nestedProperty during refactor
---
 src/plots/cartesian/axes.js | 119 ++++++++++++++++--------------------
 1 file changed, 53 insertions(+), 66 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index fff20a0a9c4..63376f27fcd 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -918,17 +918,11 @@ axes.calcTicks = function calcTicks(ax, opts) {
     var maxRange = Math.max(rng[0], rng[1]);
 
     var maxTicks = Math.max(1000, ax._length || 0);
-
     var ticksOut = [];
     var minorTicks = [];
 
     var tickVals = [];
-    var tickFractionalVals = [];
-    tickFractionalVals._isSet = false;
-
     var minorTickVals = [];
-    var minorTickFractionalVals = [];
-    minorTickFractionalVals._isSet = false;
 
     var hasMinor = ax.minor && (ax.minor.ticks || ax.minor.showgrid);
 
@@ -952,67 +946,62 @@ axes.calcTicks = function calcTicks(ax, opts) {
             axes.prepTicks(mockAx, opts);
         }
 
-        if(mockAx.tickmode === 'full domain') {
-            var nt = mockAx.nticks;  // does mockAx have nitkcs?
-            if(nt === undefined) nt = 0;
-            if(nt === 0) {
-              // pass
-            } else if(nt === 1) {
-                tickVals = [0];
-            } else if(nt === 2) {
-                tickVals = [0, 1];
-            } else {
-                var increment = 1 / (nt - 1); // (nt-2) + 1
-                tickVals.push(0);
-                for(var tickIndex = 0; tickIndex < nt - 2; tickIndex++) {
-                    tickVals.push((tickIndex + 1) * increment);
+
+        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
+            if(mockAx.tickmode === 'full domain') { // TODO: Change for minor, note: if minor we already have major
+                var nt = mockAx.nticks;
+                var fractionalTickvals = []
+                if(nt === undefined) nt = 0;
+                if(nt === 0) {
+                    // pass
+                } else if(nt === 1) {
+                    fractionalTickvals = [.5];
+                } else if(nt === 2) {
+                    fractionalTickvals = [0, 1];
+                } else {
+                    var increment = 1 / (nt - 1); // (nt-2) + 1
+                    fractionalTickvals = [0];
+                    for(var tickIndex = 0; tickIndex < nt - 2; tickIndex++) {
+                        fractionalTickvals.push((tickIndex + 1) * increment);
+                    }
+                    fractionalTickvals.push(1);
                 }
-                tickVals.push(1);
             }
-            if(major) {
-                Lib.nestedProperty(ax, 'tickvals').set(tickVals);
-            } else {
-                Lib.nestedProperty(ax.minor, 'tickvals').set(tickVals);
-            }
-        }
-        // tickmode 'domain array' is just 'array' but with a pre-calc step
-        // original comment:
-        // now that we've figured out the auto values for formatting
-        // in case we're missing some ticktext, we can break out for array ticks
-        if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
-            // Mapping proportions to array:
-            if(mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
-                var width = (maxRange - minRange);
-                if(axrev) width *= -1;
-                var offset = !axrev ? minRange : maxRange;
-
-                var currentFractionalVals = [];
-                var currentValsProp;
+            if(mockAx.tickmode === 'domain array') {
                 if(major) {
-                    currentValsProp = Lib.nestedProperty(ax, 'tickvals'); // Do we need this?
-                    currentFractionalVals = tickFractionalVals = currentValsProp.get();
-                    tickFractionalVals._isSet = true;
+                    fractionalTickvals = ax.tickvals;
                 } else {
-                    currentValsProp = Lib.nestedProperty(ax.minor, 'tickvals');
-                    currentFractionalVals = minorTickFractionalVals = currentValsProp.get();
-                    minorTickFractionalVals._isSet = true;
+                    fractionalTickvals = ax.minor.tickvals; // TODO needs to be calculate for minor based on major
                 }
+            }
 
-                var mappedVals = Lib.simpleMap(currentFractionalVals,
+            if(mockAx.tickmode !== 'array') {
+                var width = (maxRange - minRange); // TODO: inspect this value for log, it shouldn't work!
+                if(axrev) width *= -1;
+                var offset = !axrev ? minRange : maxRange; // TODO: inspect this value for log
+                var mappedVals = Lib.simpleMap(fractionalTickvals,
                     function(fraction, offset, width, type) {
                         var mapped = offset + (width * fraction);
                         return (type === 'log') ? Math.pow(10, mapped) : mapped;
                     }, offset, width, type);
-                currentValsProp.set(mappedVals);
+                // reminder: ranges w/ type log use the exponent whereas ticks use the absolute value
+                // TODO: do some inspection here: it freaks me out doin arithmetic on possible exponents
+                if(major) {
+                    ax._mappedTickvals = mappedVals;
+                } else {
+                    ax.minor._mappedTickvals = mappedVals;
+                }
             }
+            // now that we've figured out the auto values for formatting
+            // in case we're missing some ticktext, we can break out for array ticks
 
             // Original 'array' only code
             if(major) {
                 tickVals = [];
-                ticksOut = arrayTicks(ax, !isMinor);
+                ticksOut = arrayTicks(ax, !isMinor); // ie arrayTicks(ax, majorOnly = !False)
             } else {
                 minorTickVals = [];
-                minorTicks = arrayTicks(ax, !isMinor);
+                minorTicks = arrayTicks(ax, !isMinor); // ie arrayTicks(ax, majorOnly = !True)
             }
             continue;
         }
@@ -1274,18 +1263,6 @@ axes.calcTicks = function calcTicks(ax, opts) {
         ticksOut[0].noTick = true;
     }
 
-    // Reset tickvals back to domain array
-    if(tickFractionalVals._isSet) {
-        delete tickFractionalVals._isSet;
-        if(ax.tickmode === 'full domain') tickFractionalVals = [];
-        Lib.nestedProperty(ax, 'tickvals').set(tickFractionalVals);
-    }
-    if(minorTickFractionalVals._isSet) {
-        delete tickFractionalVals._isSet;
-        if(ax.minor.tickmode === 'full domain') tickFractionalVals = [];
-        Lib.nestedProperty(ax.minor, 'tickvals').set(minorTickFractionalVals);
-    }
-
     return ticksOut;
 };
 
@@ -1354,7 +1331,12 @@ function arrayTicks(ax, majorOnly) {
     for(var isMinor = 0; isMinor <= 1; isMinor++) {
         if((majorOnly !== undefined) && ((majorOnly && isMinor) || (majorOnly === false && !isMinor))) continue;
         if(isMinor && !ax.minor) continue;
-        var vals = !isMinor ? ax.tickvals : ax.minor.tickvals;
+        var vals;
+        if(!isMinor){
+            vals = ax.tickmode === 'array' ? ax.tickvals : ax._mappedTickvals;
+        } else {
+            vals = ax.tickmode === 'array' ? ax.minor.tickvals : ax.minor._mappedTickvals;
+        }
         var text = !isMinor ? ax.ticktext : [];
         if(!vals) continue;
 
@@ -1690,19 +1672,24 @@ axes.tickFirst = function(ax, opts) {
 // more precision for hovertext
 axes.tickText = function(ax, x, hover, noSuffixPrefix) {
     var out = tickTextObj(ax, x);
-    var arrayMode = (ax.tickmode === 'array' || ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
+    var arrayMode = ax.tickmode === 'array'
+    var fractionalMode = (ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
     var extraPrecision = hover || arrayMode;
     var axType = ax.type;
     // TODO multicategory, if we allow ticktext / tickvals
     var tickVal2l = axType === 'category' ? ax.d2l_noadd : ax.d2l;
     var i;
 
-    if(arrayMode && Lib.isArrayOrTypedArray(ax.ticktext)) {
+    if((arrayMode || fractionalMode) && Lib.isArrayOrTypedArray(ax.ticktext)) {
         var rng = Lib.simpleMap(ax.range, ax.r2l);
         var minDiff = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / 10000;
 
         for(i = 0; i < ax.ticktext.length; i++) {
-            if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;
+            if(arrayMode) {
+                if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break;
+            } else {
+                if(Math.abs(x - tickVal2l(ax._mappedTickvals[i])) < minDiff) break;
+            }
         }
         if(i < ax.ticktext.length) {
             out.text = String(ax.ticktext[i]);

From 2ab0b41c5343e854e1a840cd09eb5f82619c0593 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 24 Jan 2024 21:25:43 -0500
Subject: [PATCH 29/32] Rework some if-then to golf mode (?:)

---
 src/plots/cartesian/axes.js | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 63376f27fcd..73c38821fe7 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -968,11 +968,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
                 }
             }
             if(mockAx.tickmode === 'domain array') {
-                if(major) {
-                    fractionalTickvals = ax.tickvals;
-                } else {
-                    fractionalTickvals = ax.minor.tickvals; // TODO needs to be calculate for minor based on major
-                }
+                fractionalTickvals = (major ? ax : ax.minor).tickvals;
             }
 
             if(mockAx.tickmode !== 'array') {
@@ -986,11 +982,7 @@ axes.calcTicks = function calcTicks(ax, opts) {
                     }, offset, width, type);
                 // reminder: ranges w/ type log use the exponent whereas ticks use the absolute value
                 // TODO: do some inspection here: it freaks me out doin arithmetic on possible exponents
-                if(major) {
-                    ax._mappedTickvals = mappedVals;
-                } else {
-                    ax.minor._mappedTickvals = mappedVals;
-                }
+                (major ? ax : ax.minor)._mappedTickvals = mappedVals;
             }
             // now that we've figured out the auto values for formatting
             // in case we're missing some ticktext, we can break out for array ticks
@@ -1331,12 +1323,8 @@ function arrayTicks(ax, majorOnly) {
     for(var isMinor = 0; isMinor <= 1; isMinor++) {
         if((majorOnly !== undefined) && ((majorOnly && isMinor) || (majorOnly === false && !isMinor))) continue;
         if(isMinor && !ax.minor) continue;
-        var vals;
-        if(!isMinor){
-            vals = ax.tickmode === 'array' ? ax.tickvals : ax._mappedTickvals;
-        } else {
-            vals = ax.tickmode === 'array' ? ax.minor.tickvals : ax.minor._mappedTickvals;
-        }
+        var targetAxis = (!isMinor ? ax : ax.minor)
+        var vals = ax.tickmode === 'array' ? targetAxis.tickvals : targetAxis._mappedTickvals;
         var text = !isMinor ? ax.ticktext : [];
         if(!vals) continue;
 

From f1a4db892ffa86b7db8d51ec780ba015d5e6ac7d Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 24 Jan 2024 21:27:25 -0500
Subject: [PATCH 30/32] Lint

---
 src/plots/cartesian/axes.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 73c38821fe7..f0f086bf47c 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -948,14 +948,14 @@ axes.calcTicks = function calcTicks(ax, opts) {
 
 
         if(mockAx.tickmode === 'array' || mockAx.tickmode === 'domain array' || mockAx.tickmode === 'full domain') {
+            var fractionalTickvals = [];
             if(mockAx.tickmode === 'full domain') { // TODO: Change for minor, note: if minor we already have major
                 var nt = mockAx.nticks;
-                var fractionalTickvals = []
                 if(nt === undefined) nt = 0;
                 if(nt === 0) {
                     // pass
                 } else if(nt === 1) {
-                    fractionalTickvals = [.5];
+                    fractionalTickvals = [0.5];
                 } else if(nt === 2) {
                     fractionalTickvals = [0, 1];
                 } else {
@@ -1323,7 +1323,7 @@ function arrayTicks(ax, majorOnly) {
     for(var isMinor = 0; isMinor <= 1; isMinor++) {
         if((majorOnly !== undefined) && ((majorOnly && isMinor) || (majorOnly === false && !isMinor))) continue;
         if(isMinor && !ax.minor) continue;
-        var targetAxis = (!isMinor ? ax : ax.minor)
+        var targetAxis = (!isMinor ? ax : ax.minor);
         var vals = ax.tickmode === 'array' ? targetAxis.tickvals : targetAxis._mappedTickvals;
         var text = !isMinor ? ax.ticktext : [];
         if(!vals) continue;
@@ -1660,7 +1660,7 @@ axes.tickFirst = function(ax, opts) {
 // more precision for hovertext
 axes.tickText = function(ax, x, hover, noSuffixPrefix) {
     var out = tickTextObj(ax, x);
-    var arrayMode = ax.tickmode === 'array'
+    var arrayMode = ax.tickmode === 'array';
     var fractionalMode = (ax.tickmode === 'domain array' || ax.tickmode === 'full domain');
     var extraPrecision = hover || arrayMode;
     var axType = ax.type;

From ee17af22f393c79c7c30f153f3c3bb38d48e4787 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 24 Jan 2024 21:54:42 -0500
Subject: [PATCH 31/32] Make 'auto' to function as 'array' when [] present

---
 src/plots/cartesian/axes.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index f0f086bf47c..fad9833443a 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -1324,7 +1324,8 @@ function arrayTicks(ax, majorOnly) {
         if((majorOnly !== undefined) && ((majorOnly && isMinor) || (majorOnly === false && !isMinor))) continue;
         if(isMinor && !ax.minor) continue;
         var targetAxis = (!isMinor ? ax : ax.minor);
-        var vals = ax.tickmode === 'array' ? targetAxis.tickvals : targetAxis._mappedTickvals;
+        // if auto w/ array in tickval, is implicit 'array'
+        var vals = ax.tickmode === 'array' || ax.tickmode === 'auto' ? targetAxis.tickvals : targetAxis._mappedTickvals;
         var text = !isMinor ? ax.ticktext : [];
         if(!vals) continue;
 

From 93e4bb82cf83dcce528c4606258ba2c6d8f985e5 Mon Sep 17 00:00:00 2001
From: Andrew Pikul <ajpikul@gmail.com>
Date: Wed, 24 Jan 2024 22:11:37 -0500
Subject: [PATCH 32/32] Refix incorrect patch ee17af22f393c

---
 src/plots/cartesian/axes.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index fad9833443a..8a21d2f85db 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -1323,9 +1323,10 @@ function arrayTicks(ax, majorOnly) {
     for(var isMinor = 0; isMinor <= 1; isMinor++) {
         if((majorOnly !== undefined) && ((majorOnly && isMinor) || (majorOnly === false && !isMinor))) continue;
         if(isMinor && !ax.minor) continue;
+
         var targetAxis = (!isMinor ? ax : ax.minor);
-        // if auto w/ array in tickval, is implicit 'array'
-        var vals = ax.tickmode === 'array' || ax.tickmode === 'auto' ? targetAxis.tickvals : targetAxis._mappedTickvals;
+        var vals = (targetAxis.tickmode === 'array') ? targetAxis.tickvals : targetAxis._mappedTickvals;
+
         var text = !isMinor ? ax.ticktext : [];
         if(!vals) continue;