Skip to content

Expose Plotly.purge method #300

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 11, 2016
Merged
21 changes: 10 additions & 11 deletions devtools/test_dashboard/buttons.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

var Lib = require('@src/lib');

var plotlist = document.getElementById('plot-list');
var plotList = document.getElementById('plot-list');
var anchor = document.getElementById('embedded-graph');
var image = document.getElementById('embedded-image');

@@ -14,31 +14,30 @@ anchor.style.height = '600px';
anchor.style.width = '1000px';

function plotButtons(plots, figDir) {

Object.keys(plots).forEach(function(plotname) {

var button = document.createElement('button');

button.style.cssFloat = 'left';
button.style.width = '100px';
button.style.height = '40px';

button.innerHTML = plotname;

plotlist.appendChild(button);
plotList.appendChild(button);

button.addEventListener('click', function() {

var myImage = new Image();
myImage.src = figDir + plotname + '.png';

image.innerHTML = '';
image.appendChild(myImage);


anchor.innerHTML = '';
var currentGraphDiv = Tabs.getGraph();
if(currentGraphDiv) Plotly.purge(currentGraphDiv);

gd = document.createElement('div');
gd.id = 'graph';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mdtusz not sure why I didn't do this before, now you can use Plotly.plot('graph', [], {}) in the test-dashboard.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🍻


anchor.innerHTML = '';
anchor.appendChild(gd);

var plot = plots[plotname];
@@ -58,7 +57,7 @@ function plotButtons(plots, figDir) {
snapshot.innerHTML = 'snapshot';
snapshot.style.background = 'blue';

plotlist.appendChild(snapshot);
plotList.appendChild(snapshot);

snapshot.addEventListener('click', function() {

@@ -111,7 +110,7 @@ function plotButtons(plots, figDir) {
pummelButton.style.marginLeft = '25px';
pummelButton.innerHTML = 'pummel3d';
pummelButton.style.background = 'blue';
plotlist.appendChild(pummelButton);
plotList.appendChild(pummelButton);

var i = 0;
var mock = require('@mocks/gl3d_marker-color.json');
@@ -147,7 +146,7 @@ function plotButtons(plots, figDir) {
scrapeButton.style.marginLeft = '25px';
scrapeButton.innerHTML = 'scrape SVG';
scrapeButton.style.background = 'blue';
plotlist.appendChild(scrapeButton);
plotList.appendChild(scrapeButton);

scrapeButton.addEventListener('click', function() {
Plotly.Snapshot.toSVG(Tabs.get());
2 changes: 2 additions & 0 deletions devtools/test_dashboard/index.html
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
getGraph: function() {
return document.getElementById('embedded-graph').children[0];
},

fresh: function() {
var anchor = document.getElementById('embedded-graph'),
graphDiv = Tabs.getGraph();
@@ -37,6 +38,7 @@

return graphDiv;
},

plotMock: function(mockName) {
var mockURL = '../../test/image/mocks/' + mockName + '.json';

1 change: 1 addition & 0 deletions src/core.js
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ exports.prependTraces = Plotly.prependTraces;
exports.addTraces = Plotly.addTraces;
exports.deleteTraces = Plotly.deleteTraces;
exports.moveTraces = Plotly.moveTraces;
exports.purge = Plotly.purge;
exports.setPlotConfig = require('./plot_api/set_plot_config');
exports.register = Plotly.register;

12 changes: 12 additions & 0 deletions src/lib/events.js
Original file line number Diff line number Diff line change
@@ -114,7 +114,19 @@ var Events = {
*/
return jQueryHandlerValue !== undefined ? jQueryHandlerValue :
nodeEventHandlerValue;
},

purge: function(plotObj) {
delete plotObj._ev;
delete plotObj.on;
delete plotObj.once;
delete plotObj.removeListener;
delete plotObj.removeAllListeners;
delete plotObj.emit;

return plotObj;
}

};

module.exports = Events;
35 changes: 34 additions & 1 deletion src/plot_api/plot_api.js
Original file line number Diff line number Diff line change
@@ -2406,6 +2406,39 @@ Plotly.relayout = function relayout(gd, astr, val) {
});
};

/**
* Purge a graph container div back to its initial pre-Plotly.plot state
*
* @param {string id or DOM element} gd
* the id or DOM element of the graph container div
*/
Plotly.purge = function purge(gd) {
gd = getGraphDiv(gd);

var fullLayout = gd._fullLayout || {},
fullData = gd._fullData || [];

// remove gl contexts
Plots.cleanPlot([], {}, fullData, fullLayout);

// purge properties
Plots.purge(gd);

// purge event emitter methods
Events.purge(gd);

// remove plot container
if(fullLayout._container) fullLayout._container.remove();

delete gd._context;
delete gd._replotPending;
delete gd._mouseDownTime;
delete gd._hmpixcount;
delete gd._hmlumcount;

return gd;
};

/**
* Reduce all reserved margin objects to a single required margin reservation.
*
@@ -2505,7 +2538,7 @@ function makePlotFramework(gd) {
// Make the svg container
fullLayout._paperdiv = fullLayout._container.selectAll('.svg-container').data([0]);
fullLayout._paperdiv.enter().append('div')
.classed('svg-container',true)
.classed('svg-container', true)
.style('position','relative');

// Initial autosize
14 changes: 14 additions & 0 deletions src/plots/gl2d/index.js
Original file line number Diff line number Diff line change
@@ -65,3 +65,17 @@ exports.plot = function plotGl2d(gd) {
scene.plot(fullSubplotData, fullLayout, gd.layout);
}
};

exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
var oldSceneKeys = Plots.getSubplotIds(oldFullLayout, 'gl2d');

for(var i = 0; i < oldSceneKeys.length; i++) {
var oldSubplot = oldFullLayout._plots[oldSceneKeys[i]],
xaName = oldSubplot.xaxis._name,
yaName = oldSubplot.yaxis._name;

if(!!oldSubplot._scene2d && (!newFullLayout[xaName] || !newFullLayout[yaName])) {
oldSubplot._scene2d.destroy();
}
}
};
8 changes: 8 additions & 0 deletions src/plots/gl2d/scene2d.js
Original file line number Diff line number Diff line change
@@ -292,6 +292,13 @@ proto.cameraChanged = function() {

proto.destroy = function() {
this.glplot.dispose();

this.container.removeChild(this.canvas);
this.container.removeChild(this.svgContainer);
this.container.removeChild(this.mouseContainer);

this.glplot = null;
this.stopped = true;
};

proto.plot = function(fullData, fullLayout) {
@@ -405,6 +412,7 @@ proto.plot = function(fullData, fullLayout) {

proto.draw = function() {
if(this.stopped) return;

requestAnimationFrame(this.redraw);

var glplot = this.glplot,
13 changes: 5 additions & 8 deletions src/plots/plots.js
Original file line number Diff line number Diff line change
@@ -492,7 +492,7 @@ plots.supplyDefaults = function(gd) {
plots.supplyLayoutModuleDefaults(newLayout, newFullLayout, newFullData);

// clean subplots and other artifacts from previous plot calls
cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);
plots.cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout);

/*
* Relink functions and underscore attributes to promote consistency between
@@ -520,7 +520,7 @@ plots.supplyDefaults = function(gd) {
}
};

function cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout) {
plots.cleanPlot = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
var i, j;

var plotTypes = Object.keys(subplotsRegistry);
@@ -560,7 +560,7 @@ function cleanPlot(newFullData, newFullLayout, oldFullData, oldFullLayout) {
oldFullLayout._infolayer.selectAll('.cb' + oldUid).remove();
}
}
}
};

/**
* Relink private _keys and keys with a function value from one layout
@@ -755,16 +755,13 @@ plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData) {
}
};

// Remove all plotly attributes from a div so it can be replotted fresh
// TODO: these really need to be encapsulated into a much smaller set...
plots.purge = function(gd) {
// remove all plotly attributes from a div so it can be replotted fresh
// TODO: these really need to be encapsulated into a much smaller set...

// note: we DO NOT remove _context because it doesn't change when we insert
// a new plot, and may have been set outside of our scope.

// clean up the gl and geo containers
// TODO unify subplot creation/update with d3.selection.order
// and/or subplot ids
var fullLayout = gd._fullLayout || {};
if(fullLayout._glcontainer !== undefined) fullLayout._glcontainer.remove();
if(fullLayout._geocontainer !== undefined) fullLayout._geocontainer.remove();
7 changes: 7 additions & 0 deletions test/jasmine/tests/events_test.js
Original file line number Diff line number Diff line change
@@ -181,8 +181,15 @@ describe('Events', function() {
expect(eventBaton).toBe(3);
expect(result).toBe('pong');
});
});

describe('purge', function() {
it('should remove all method from the plotObj', function() {
Events.init(plotObj);
Events.purge(plotObj);

expect(plotObj).toEqual({});
});
});

});
50 changes: 47 additions & 3 deletions test/jasmine/tests/gl_plot_interact_test.js
Original file line number Diff line number Diff line change
@@ -36,14 +36,19 @@ describe('Test gl plot interactions', function() {

sceneIds = Plots.getSubplotIds(fullLayout, 'gl3d');
sceneIds.forEach(function(id) {
fullLayout[id]._scene.destroy();
var scene = fullLayout[id]._scene;

if(scene.glplot) scene.destroy();
});

sceneIds = Plots.getSubplotIds(fullLayout, 'gl2d');
sceneIds.forEach(function(id) {
var scene2d = fullLayout._plots[id]._scene2d;
scene2d.stopped = true;
scene2d.destroy();

if(scene2d.glplot) {
scene2d.stopped = true;
scene2d.destroy();
}
});

destroyGraphDiv();
@@ -369,4 +374,43 @@ describe('Test gl plot interactions', function() {
});

});

describe('Plots.cleanPlot', function() {

it('should remove gl context from the graph div of a gl3d plot', function(done) {
gd = createGraphDiv();

var mockData = [{
type: 'scatter3d'
}];

Plotly.plot(gd, mockData).then(function() {
expect(gd._fullLayout.scene._scene.glplot).toBeDefined();

Plots.cleanPlot([], {}, gd._fullData, gd._fullLayout);
expect(gd._fullLayout.scene._scene.glplot).toBe(null);

done();
});
});

it('should remove gl context from the graph div of a gl2d plot', function(done) {
gd = createGraphDiv();

var mockData = [{
type: 'scattergl',
x: [1,2,3],
y: [1,2,3]
}];

Plotly.plot(gd, mockData).then(function() {
expect(gd._fullLayout._plots.xy._scene2d.glplot).toBeDefined();

Plots.cleanPlot([], {}, gd._fullData, gd._fullLayout);
expect(gd._fullLayout._plots.xy._scene2d.glplot).toBe(null);

done();
});
});
});
});
21 changes: 21 additions & 0 deletions test/jasmine/tests/plot_api_test.js
Original file line number Diff line number Diff line change
@@ -620,6 +620,27 @@ describe('Test plot api', function() {
});
});

describe('Plotly.purge', function() {

afterEach(destroyGraphDiv);

it('should return the graph div in its original state', function(done) {
var gd = createGraphDiv();
var initialKeys = Object.keys(gd);
var intialHTML = gd.innerHTML;
var mockData = [{ x: [1,2,3], y: [2,3,4] }];

Plotly.plot(gd, mockData).then(function() {
Plotly.purge(gd);

expect(Object.keys(gd)).toEqual(initialKeys);
expect(gd.innerHTML).toEqual(intialHTML);

done();
});
});
});

describe('cleanData', function() {
var gd;