diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js
index 8663b061b01..8f20cb41c23 100644
--- a/src/plots/cartesian/axes.js
+++ b/src/plots/cartesian/axes.js
@@ -202,16 +202,18 @@ axes.saveRangeInitial = function(gd, overwrite) {
     var axList = axes.list(gd, '', true),
         hasOneAxisChanged = false;
 
-    var ax, isNew, hasChanged;
-
     for(var i = 0; i < axList.length; i++) {
-        ax = axList[i];
+        var ax = axList[i];
 
-        isNew = ax._rangeInitial===undefined;
-        hasChanged = isNew ||
-            !(ax.range[0]===ax._rangeInitial[0] && ax.range[1]===ax._rangeInitial[1]);
+        var isNew = (ax._rangeInitial === undefined);
+        var hasChanged = (
+            isNew || !(
+                ax.range[0] === ax._rangeInitial[0] &&
+                ax.range[1] === ax._rangeInitial[1]
+            )
+        );
 
-        if((isNew && ax.autorange===false) || (overwrite && hasChanged)) {
+        if((isNew && ax.autorange === false) || (overwrite && hasChanged)) {
             ax._rangeInitial = ax.range.slice();
             hasOneAxisChanged = true;
         }
diff --git a/src/plots/cartesian/axis_ids.js b/src/plots/cartesian/axis_ids.js
index 0ff9626289c..f2dba39a961 100644
--- a/src/plots/cartesian/axis_ids.js
+++ b/src/plots/cartesian/axis_ids.js
@@ -14,7 +14,7 @@ var Lib = require('../../lib');
 var constants = require('./constants');
 
 
-// convert between axis names (xaxis, xaxis2, etc, elements of td.layout)
+// convert between axis names (xaxis, xaxis2, etc, elements of gd.layout)
 // and axis id's (x, x2, etc). Would probably have ditched 'xaxis'
 // completely in favor of just 'x' if it weren't ingrained in the API etc.
 exports.id2name = function id2name(id) {
@@ -43,8 +43,8 @@ exports.cleanId = function cleanId(id, axLetter) {
 // get all axis object names
 // optionally restricted to only x or y or z by string axLetter
 // and optionally 2D axes only, not those inside 3D scenes
-function listNames(td, axLetter, only2d) {
-    var fullLayout = td._fullLayout;
+function listNames(gd, axLetter, only2d) {
+    var fullLayout = gd._fullLayout;
     if(!fullLayout) return [];
 
     function filterAxis(obj, extra) {
@@ -76,23 +76,23 @@ function listNames(td, axLetter, only2d) {
 }
 
 // get all axis objects, as restricted in listNames
-exports.list = function(td, axletter, only2d) {
-    return listNames(td, axletter, only2d)
+exports.list = function(gd, axletter, only2d) {
+    return listNames(gd, axletter, only2d)
         .map(function(axName) {
-            return Lib.nestedProperty(td._fullLayout, axName).get();
+            return Lib.nestedProperty(gd._fullLayout, axName).get();
         });
 };
 
 // get all axis ids, optionally restricted by letter
 // this only makes sense for 2d axes
-exports.listIds = function(td, axletter) {
-    return listNames(td, axletter, true).map(exports.name2id);
+exports.listIds = function(gd, axletter) {
+    return listNames(gd, axletter, true).map(exports.name2id);
 };
 
 // get an axis object from its id 'x','x2' etc
 // optionally, id can be a subplot (ie 'x2y3') and type gets x or y from it
-exports.getFromId = function(td, id, type) {
-    var fullLayout = td._fullLayout;
+exports.getFromId = function(gd, id, type) {
+    var fullLayout = gd._fullLayout;
 
     if(type === 'x') id = id.replace(/y[0-9]*/,'');
     else if(type === 'y') id = id.replace(/x[0-9]*/,'');
@@ -101,8 +101,8 @@ exports.getFromId = function(td, id, type) {
 };
 
 // get an axis object of specified type from the containing trace
-exports.getFromTrace = function(td, fullTrace, type) {
-    var fullLayout = td._fullLayout;
+exports.getFromTrace = function(gd, fullTrace, type) {
+    var fullLayout = gd._fullLayout;
     var ax = null;
 
     if(Plots.traceIs(fullTrace, 'gl3d')) {
@@ -112,7 +112,7 @@ exports.getFromTrace = function(td, fullTrace, type) {
         }
     }
     else {
-        ax = exports.getFromId(td, fullTrace[type + 'axis'] || type);
+        ax = exports.getFromId(gd, fullTrace[type + 'axis'] || type);
     }
 
     return ax;
diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js
index 3548cc7faab..c418bcdce6c 100644
--- a/src/plots/cartesian/graph_interact.js
+++ b/src/plots/cartesian/graph_interact.js
@@ -1807,15 +1807,24 @@ function dragBox(gd, plotinfo, x, y, w, h, ns, ew) {
         else if(doubleClickConfig === 'reset') {
             for(i = 0; i < axList.length; i++) {
                 ax = axList[i];
-                attrs[ax._name + '.range'] = ax._rangeInitial.slice();
+
+                if(!ax._rangeInitial) {
+                    attrs[ax._name + '.autorange'] = true;
+                }
+                else {
+                    attrs[ax._name + '.range'] = ax._rangeInitial.slice();
+                }
             }
         }
         else if(doubleClickConfig === 'reset+autosize') {
             for(i = 0; i < axList.length; i++) {
                 ax = axList[i];
+
                 if(ax.fixedrange) continue;
                 if(ax._rangeInitial === undefined ||
-                    ax.range[0]===ax._rangeInitial[0] && ax.range[1]===ax._rangeInitial[1]) {
+                    ax.range[0] === ax._rangeInitial[0] &&
+                    ax.range[1] === ax._rangeInitial[1]
+                ) {
                     attrs[ax._name + '.autorange'] = true;
                 }
                 else attrs[ax._name + '.range'] = ax._rangeInitial.slice();
diff --git a/test/jasmine/tests/click_test.js b/test/jasmine/tests/click_test.js
index f680d2c6963..5740f7f7f73 100644
--- a/test/jasmine/tests/click_test.js
+++ b/test/jasmine/tests/click_test.js
@@ -5,11 +5,11 @@ var DBLCLICKDELAY = require('@src/plots/cartesian/constants').DBLCLICKDELAY;
 var createGraphDiv = require('../assets/create_graph_div');
 var destroyGraphDiv = require('../assets/destroy_graph_div');
 var mouseEvent = require('../assets/mouse_event');
+var customMatchers = require('../assets/custom_matchers');
 
 
 describe('click interactions', function() {
     var mock = require('@mocks/14.json'),
-        mockCopy = Lib.extendDeep({}, mock),
         gd;
 
     var pointPos = [351, 223],
@@ -34,17 +34,16 @@ describe('click interactions', function() {
         }, DBLCLICKDELAY / 2);
     }
 
-    beforeEach(function(done) {
-        gd = createGraphDiv();
-
-        Plotly.plot(gd, mockCopy.data, mockCopy.layout)
-            .then(done);
-    });
-
     describe('click events', function() {
         var futureData;
 
-        beforeEach(function() {
+        beforeEach(function(done) {
+            gd = createGraphDiv();
+
+            var mockCopy = Lib.extendDeep({}, mock);
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout)
+                .then(done);
+
             gd.on('plotly_click', function(data) {
                 futureData = data;
             });
@@ -74,10 +73,17 @@ describe('click interactions', function() {
     describe('double click events', function() {
         var futureData;
 
-        beforeEach(function() {
+        beforeEach(function(done) {
+            gd = createGraphDiv();
+
+            var mockCopy = Lib.extendDeep({}, mock);
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout)
+                .then(done);
+
             gd.on('plotly_doubleclick', function(data) {
                 futureData = data;
             });
+
         });
 
         it('should return null', function(done) {
@@ -87,4 +93,215 @@ describe('click interactions', function() {
             });
         });
     });
+
+    describe('double click interactions', function() {
+        var mockCopy;
+
+        var autoRangeX = [-3.011967491973726, 2.1561305597186564],
+            autoRangeY = [-0.9910086301469277, 1.389382716298284];
+
+        var setRangeX = [-3, 1],
+            setRangeY = [-0.5, 1];
+
+        var zoomRangeX = [-2, 0],
+            zoomRangeY = [0, 0.5];
+
+        var update = {
+            'xaxis.range[0]': zoomRangeX[0],
+            'xaxis.range[1]': zoomRangeX[1],
+            'yaxis.range[0]': zoomRangeY[0],
+            'yaxis.range[1]': zoomRangeY[1]
+        };
+
+        beforeEach(function() {
+            jasmine.addMatchers(customMatchers);
+
+            gd = createGraphDiv();
+            mockCopy = Lib.extendDeep({}, mock);
+        });
+
+        function setRanges(mockCopy) {
+            mockCopy.layout.xaxis.autorange = false;
+            mockCopy.layout.xaxis.range = setRangeX.slice();
+
+            mockCopy.layout.yaxis.autorange = false;
+            mockCopy.layout.yaxis.range = setRangeY.slice();
+
+            return mockCopy;
+        }
+
+        it('when set to \'reset+autorange\' (the default) should work when \'autorange\' is on', function(done) {
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function(){
+                expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                Plotly.relayout(gd, update).then(function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(zoomRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(zoomRangeY);
+
+                    doubleClick(blankPos[0], blankPos[1], function() {
+                        expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                        expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                        done();
+                    });
+                });
+            });
+        });
+
+        it('when set to \'reset+autorange\' (the default) should reset to set range on double click', function(done) {
+            mockCopy = setRanges(mockCopy);
+
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function(){
+                expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                Plotly.relayout(gd, update).then(function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(zoomRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(zoomRangeY);
+
+                    doubleClick(blankPos[0], blankPos[1], function() {
+                        expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                        expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                        done();
+                    });
+                });
+            });
+        });
+
+        it('when set to \'reset+autorange\' (the default) should autosize on 1st double click and reset on 2nd', function(done) {
+            mockCopy = setRanges(mockCopy);
+
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function(){
+                expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                doubleClick(blankPos[0], blankPos[1], function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                    doubleClick(blankPos[0], blankPos[1], function() {
+                        expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                        expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                        done();
+                    });
+                });
+            });
+        });
+
+        it('when set to \'reset\' should work when \'autorange\' is on', function(done) {
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout, { doubleClick: 'reset' }).then(function() {
+                expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                Plotly.relayout(gd, update).then(function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(zoomRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(zoomRangeY);
+
+                    doubleClick(blankPos[0], blankPos[1], function() {
+                        expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                        expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                        done();
+                    });
+                });
+            });
+        });
+
+        it('when set to \'reset\' should reset to set range on double click', function(done) {
+            mockCopy = setRanges(mockCopy);
+
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout, { doubleClick: 'reset' }).then(function() {
+                expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                Plotly.relayout(gd, update).then(function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(zoomRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(zoomRangeY);
+
+                    doubleClick(blankPos[0], blankPos[1], function() {
+                        expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                        expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                        done();
+                    });
+                });
+            });
+        });
+
+        it('when set to \'reset\' should reset on all double clicks', function(done) {
+            mockCopy = setRanges(mockCopy);
+
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout, { doubleClick: 'reset' }).then(function() {
+                expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                doubleClick(blankPos[0], blankPos[1], function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                    done();
+                });
+            });
+        });
+
+        it('when set to \'autosize\' should work when \'autorange\' is on', function(done) {
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout, { doubleClick: 'autosize' }).then(function() {
+                expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                Plotly.relayout(gd, update).then(function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(zoomRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(zoomRangeY);
+
+                    doubleClick(blankPos[0], blankPos[1], function() {
+                        expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                        expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                        done();
+                    });
+                });
+            });
+        });
+
+        it('when set to \'autosize\' should set to autorange on double click', function(done) {
+            mockCopy = setRanges(mockCopy);
+
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout, { doubleClick: 'autosize' }).then(function() {
+                expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                Plotly.relayout(gd, update).then(function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(zoomRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(zoomRangeY);
+
+                    doubleClick(blankPos[0], blankPos[1], function() {
+                        expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                        expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                        done();
+                    });
+                });
+            });
+        });
+
+        it('when set to \'autosize\' should reset on all double clicks', function(done) {
+            mockCopy = setRanges(mockCopy);
+
+            Plotly.plot(gd, mockCopy.data, mockCopy.layout, { doubleClick: 'autosize' }).then(function() {
+                expect(gd.layout.xaxis.range).toBeCloseToArray(setRangeX);
+                expect(gd.layout.yaxis.range).toBeCloseToArray(setRangeY);
+
+                doubleClick(blankPos[0], blankPos[1], function() {
+                    expect(gd.layout.xaxis.range).toBeCloseToArray(autoRangeX);
+                    expect(gd.layout.yaxis.range).toBeCloseToArray(autoRangeY);
+
+                    done();
+                });
+            });
+        });
+
+    });
 });