diff --git a/devtools/.eslintrc b/devtools/.eslintrc
index aabacb13e13..78444da8aa0 100644
--- a/devtools/.eslintrc
+++ b/devtools/.eslintrc
@@ -3,5 +3,8 @@
"env": {
"node": true,
"browser": true
+ },
+ "globals": {
+ "Promise": true
}
}
diff --git a/devtools/test_dashboard/buttons.js b/devtools/test_dashboard/buttons.js
deleted file mode 100644
index 3dab386271c..00000000000
--- a/devtools/test_dashboard/buttons.js
+++ /dev/null
@@ -1,158 +0,0 @@
-/* global Plotly:false Tabs:false */
-
-var Lib = require('@src/lib');
-
-var plotList = document.getElementById('plot-list');
-var anchor = document.getElementById('embedded-graph');
-var image = document.getElementById('embedded-image');
-
-var gd = null;
-
-anchor.style.position = 'relative';
-anchor.style.top = '80px';
-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);
-
- button.addEventListener('click', function() {
- var myImage = new Image();
- myImage.src = figDir + plotname + '.png';
-
- image.innerHTML = '';
- image.appendChild(myImage);
-
- var currentGraphDiv = Tabs.getGraph();
- if(currentGraphDiv) Plotly.purge(currentGraphDiv);
-
- gd = document.createElement('div');
- gd.id = 'graph';
-
- anchor.innerHTML = '';
- anchor.appendChild(gd);
-
- var plot = plots[plotname];
- var data = Lib.extendDeep([], plot.data);
- var layout = Lib.extendDeep({}, plot.layout);
-
- Plotly.plot(gd, data, layout);
- });
- });
-
- var snapshot = document.createElement('button');
-
- snapshot.style.cssFloat = 'left';
- snapshot.style.width = '100px';
- snapshot.style.height = '40px';
- snapshot.style.marginLeft = '25px';
- snapshot.innerHTML = 'snapshot';
- snapshot.style.background = 'blue';
-
- plotList.appendChild(snapshot);
-
- snapshot.addEventListener('click', function() {
-
- /*
- * Grab the currently loaded plot and make an image - replacing the plot.
- */
- if(!gd) return;
-
- var layout = gd.layout;
- var data = gd.data;
-
- if(!layout || !data) return;
-
- Plotly.Plots.getSubplotIds(gd._fullLayout, 'gl3d').forEach(function(key) {
- var scene = gd._fullLayout[key]._scene;
- scene.destroy();
- });
-
- // create a fresh gd
- anchor.innerHTML = '';
- gd = document.createElement('div');
- anchor.appendChild(gd);
-
- /*
- * Replot with staticPlot
- */
- Plotly.plot(gd, data, layout, {staticPlot: true, plotGlPixelRatio: 2}).then(function() {
- Plotly.Plots.getSubplotIds(gd._fullLayout, 'gl3d').forEach(function(key) {
- var scene = gd._fullLayout[key]._scene;
- var dataURL = scene.toImage();
-
- var myImage = new Image();
- myImage.src = dataURL;
-
- myImage.onload = function() {
- myImage.height = scene.container.clientHeight;
- myImage.width = scene.container.clientWidth;
- };
-
- image.innerHTML = '';
- image.appendChild(myImage);
- });
- });
- });
-
- var pummelButton = document.createElement('button');
- pummelButton.style.cssFloat = 'left';
- pummelButton.style.width = '100px';
- pummelButton.style.height = '40px';
- pummelButton.style.marginLeft = '25px';
- pummelButton.innerHTML = 'pummel3d';
- pummelButton.style.background = 'blue';
- plotList.appendChild(pummelButton);
-
- var i = 0;
- var mock = require('@mocks/gl3d_marker-color.json');
- var statusDiv = document.getElementById('status-info');
-
- pummelButton.addEventListener('click', function() {
- setInterval(function() {
- var plotDiv = document.createElement('div');
- window.plotDiv = plotDiv;
-
- plotDiv.id = 'div' + i;
- document.body.appendChild(plotDiv);
-
- Plotly.plot(plotDiv, mock.data, mock.layout, {staticPlot: true}).then(function() {
-
- Plotly.Plots.getSubplotIds(plotDiv._fullLayout, 'gl3d').forEach(function(key) {
- var scene = plotDiv._fullLayout[key]._scene;
- scene.destroy();
- i ++;
- statusDiv.innerHTML = 'Created ' + i + ' webgl contexts.';
- });
-
- document.body.removeChild(plotDiv);
- });
-
- }, 500);
- });
-
- var scrapeButton = document.createElement('button');
- scrapeButton.style.cssFloat = 'left';
- scrapeButton.style.width = '100px';
- scrapeButton.style.height = '40px';
- scrapeButton.style.marginLeft = '25px';
- scrapeButton.innerHTML = 'scrape SVG';
- scrapeButton.style.background = 'blue';
- plotList.appendChild(scrapeButton);
-
- scrapeButton.addEventListener('click', function() {
- Plotly.Snapshot.toSVG(Tabs.get());
- return;
- });
-
-}
-
-module.exports = plotButtons;
diff --git a/devtools/test_dashboard/devtools.js b/devtools/test_dashboard/devtools.js
new file mode 100644
index 00000000000..089df205024
--- /dev/null
+++ b/devtools/test_dashboard/devtools.js
@@ -0,0 +1,160 @@
+var Fuse = require('fuse.js');
+var mocks = require('../../build/test_dashboard_mocks.json');
+
+
+// Our gracious testing object
+var Tabs = {
+
+ // Return the specified plot container (or default one)
+ getGraph: function(id) {
+ id = id || 'graph';
+ return document.getElementById(id);
+ },
+
+ // Create a new plot container
+ fresh: function(id) {
+ id = id || 'graph';
+
+ var graphDiv = Tabs.getGraph(id);
+
+ if(graphDiv) {
+ graphDiv.remove();
+ }
+
+ graphDiv = document.createElement('div');
+ graphDiv.className = 'dashboard-plot';
+ graphDiv.id = id;
+
+ var plotArea = document.getElementById('plots');
+ plotArea.appendChild(graphDiv);
+
+ return graphDiv;
+ },
+
+ // Plot a mock by name (without .json) to the default or specified container
+ plotMock: function(mockName, id) {
+ var mockURL = '/test/image/mocks/' + mockName + '.json';
+
+ window.Plotly.d3.json(mockURL, function(err, fig) {
+ window.Plotly.plot(Tabs.fresh(id), fig.data, fig.layout);
+
+ console.warn('Plotting:', mockURL);
+ });
+ },
+
+ // Save a png snapshot and display it below the plot
+ snapshot: function(id) {
+ var gd = Tabs.getGraph(id);
+
+ if(!gd._fullLayout || !gd._fullData) {
+ return;
+ }
+
+ var image = new Image();
+
+ window.Plotly.Snapshot.toImage(gd, { format: 'png' }).on('success', function(img) {
+ image.src = img;
+
+ var imageDiv = document.getElementById('snapshot');
+ imageDiv.appendChild(image);
+
+ console.warn('Snapshot complete!');
+ });
+ },
+
+ // Remove all plots and snapshots from the page
+ purge: function() {
+ var plots = document.getElementsByClassName('dashboard-plot');
+ var images = document.getElementById('snapshot');
+
+ while(images.firstChild) {
+ images.removeChild(images.firstChild);
+ }
+
+ for(var i = 0; i < plots.length; i++) {
+ window.Plotly.purge(plots[i]);
+ }
+ },
+
+ // Specify what to do after each plotly.js script reload
+ onReload: function() {
+ return;
+ },
+
+ // Refreshes the plotly.js source without needing to refresh the page
+ reload: function() {
+ var source = document.getElementById('source');
+ var reloaded = document.getElementById('reload-time');
+
+ source.remove();
+
+ window.Plotly = null;
+
+ source = document.createElement('script');
+ source.id = 'source';
+ source.src = '../../build/plotly.js';
+
+ document.body.appendChild(source);
+
+ var reloadTime = new Date().toLocaleTimeString();
+ console.warn('plotly.js reloaded at ' + reloadTime);
+ reloaded.textContent = 'last reload at ' + reloadTime;
+
+ Tabs.onReload();
+ }
+};
+
+
+// Bind things to the window
+window.Tabs = Tabs;
+setInterval(function() {
+ window.gd = Tabs.getGraph() || Tabs.fresh();
+ window.fullLayout = window.gd._fullLayout;
+ window.fullData = window.gd._fullData;
+}, 1000);
+
+
+// Mocks search and plotting
+var f = new Fuse(mocks, {
+ keys: [{
+ name: 'name',
+ weight: 0.7
+ }, {
+ name: 'keywords',
+ weight: 0.3
+ }]
+});
+
+var searchBar = document.getElementById('mocks-search');
+var mocksList = document.getElementById('mocks-list');
+var plotArea = document.getElementById('plots');
+
+searchBar.addEventListener('keyup', function(e) {
+
+ // Clear results.
+ while(mocksList.firstChild) {
+ mocksList.removeChild(mocksList.firstChild);
+ }
+
+
+ var results = f.search(e.target.value);
+
+ results.forEach(function(r) {
+ var result = document.createElement('span');
+ result.className = 'search-result';
+ result.innerText = r.name;
+
+ result.addEventListener('click', function() {
+
+ // Clear plots and plot selected.
+ Tabs.purge();
+ Tabs.plotMock(r.file.slice(0, -5));
+ });
+
+ mocksList.appendChild(result);
+
+ var listWidth = mocksList.getBoundingClientRect().width;
+ var plotAreaWidth = Math.floor(window.innerWidth - listWidth);
+ plotArea.setAttribute('style', 'width: ' + plotAreaWidth + 'px;');
+ });
+});
diff --git a/devtools/test_dashboard/index.html b/devtools/test_dashboard/index.html
index 20410f98f7b..1972b481951 100644
--- a/devtools/test_dashboard/index.html
+++ b/devtools/test_dashboard/index.html
@@ -1,53 +1,25 @@
+
-
+ Plotly.js Devtools
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/devtools/test_dashboard/server.js b/devtools/test_dashboard/server.js
index 98d5aeb4c3a..e49d2513138 100644
--- a/devtools/test_dashboard/server.js
+++ b/devtools/test_dashboard/server.js
@@ -1,56 +1,159 @@
var fs = require('fs');
-var http = require('http');
var path = require('path');
-
-var browserify = require('browserify');
+var http = require('http');
var ecstatic = require('ecstatic');
-var _open = require('open');
+var open = require('open');
+var browserify = require('browserify');
+var watchify = require('watchify');
-var makeWatchifiedBundle = require('../../tasks/util/make_watchified_bundle');
-var shortcutPaths = require('../../tasks/util/shortcut_paths');
var constants = require('../../tasks/util/constants');
+var compress = require('../../tasks/util/compress_attributes');
+var PORT = process.argv[2] || 3000;
+
+
+// Create server
+var server = http.createServer(ecstatic({
+ root: constants.pathToRoot,
+ cache: 0,
+ gzip: true
+}));
+
+// Bundle development source code
+var b = browserify(constants.pathToPlotlyIndex, {
+ debug: true,
+ standalone: 'Plotly',
+ transform: [compress],
+ cache: {},
+ packageCache: {},
+ plugin: [watchify]
+});
+b.on('update', bundlePlotly);
-// TODO make this an optional argument
-var PORT = '8080';
+// Bundle devtools code
+var devtoolsPath = path.join(constants.pathToRoot, 'devtools/test_dashboard');
+var devtools = browserify(path.join(devtoolsPath, 'devtools.js'), {});
-var testFile;
-switch(process.argv[2]) {
- case 'cartesian':
- testFile = 'test_cartesian';
- break;
- case 'geo':
- testFile = 'test_geo';
- break;
- case 'gl3d':
- testFile = 'test_gl3d';
- break;
- default:
- testFile = 'test_gl2d';
+var firstBundle = true;
+
+
+// Start the server up!
+server.listen(PORT);
+
+// Build and bundle all the things!
+console.log('Building the first bundle. This might take a little while...\n');
+getMockFiles()
+ .then(readFiles)
+ .then(createMocksList)
+ .then(saveToFile)
+ .then(bundleDevtools)
+ .then(bundlePlotly);
+
+
+function getMockFiles() {
+ return new Promise(function(resolve, reject) {
+ fs.readdir(constants.pathToTestImageMocks, function(err, files) {
+ if(err) {
+ reject(err);
+ } else {
+ resolve(files);
+ }
+ });
+ });
}
-console.log('Using ' + testFile);
-console.log('Listening on :' + PORT + '\n');
+function readFiles(files) {
+ var promises = files.map(function(file) {
+ var filePath = path.join(constants.pathToTestImageMocks, file);
+ return readFilePromise(filePath);
+ });
-// watch plotly.js
-var watchifiedBundle = makeWatchifiedBundle(function onFirstBundleCallback() {
- _open('http://localhost:' + PORT + '/devtools/test_dashboard');
-});
-watchifiedBundle();
-
-// build the test examples
-fs.unlink(constants.pathToTestDashboardBundle, function() {
- browserify(path.join(__dirname, testFile), {
- debug: true,
- transform: [shortcutPaths]
- })
- .bundle(function(err) {
- if(err) throw err;
- })
- .pipe(fs.createWriteStream(constants.pathToTestDashboardBundle));
-});
+ return Promise.all(promises);
+}
-// boot up server
-http.createServer(
- ecstatic({ root: constants.pathToRoot })
-).listen(PORT);
+function createMocksList(files) {
+ var mocksList = files.map(function(file) {
+ var contents = JSON.parse(file.contents);
+
+ // get plot type keywords from mocks
+ var types = contents.data.map(function(trace) {
+ return trace.type || 'scatter';
+ }).reduce(function(acc, type, i, arr) {
+ if(arr.lastIndexOf(type) === i) {
+ acc.push(type);
+ }
+ return acc;
+ }, []);
+
+ var filename = file.name.split('/').pop();
+
+ return {
+ name: filename.slice(0, -5),
+ file: filename,
+ keywords: types.join(', ')
+ };
+ });
+
+ return mocksList;
+}
+
+function saveToFile(mocksList) {
+ var filePath = path.join(constants.pathToBuild, 'test_dashboard_mocks.json');
+ var content = JSON.stringify(mocksList, null, 4);
+
+ return writeFilePromise(filePath, content);
+}
+
+function readFilePromise(file) {
+ return new Promise(function(resolve, reject) {
+ fs.readFile(file, { encoding: 'utf-8' }, function(err, contents) {
+ if(err) {
+ reject(err);
+ } else {
+ resolve({
+ name: file,
+ contents: contents
+ });
+ }
+ });
+ });
+}
+
+function writeFilePromise(path, contents) {
+ return new Promise(function(resolve, reject) {
+ fs.writeFile(path, contents, function(err) {
+ if(err) {
+ reject(err);
+ } else {
+ resolve(path);
+ }
+ });
+ });
+}
+
+function bundlePlotly() {
+ b.bundle(function(err) {
+ if(err) {
+ console.error('Error while bundling!', err);
+ }
+
+ if(firstBundle) {
+ open('http://localhost:' + PORT + '/devtools/test_dashboard');
+ firstBundle = false;
+ }
+ console.log('Bundle updated at ' + new Date().toLocaleTimeString());
+ }).pipe(fs.createWriteStream(constants.pathToPlotlyBuild));
+}
+
+function bundleDevtools() {
+ return new Promise(function(resolve, reject) {
+ devtools.bundle(function(err) {
+ if(err) {
+ console.error('Error while bundling!', err);
+ return reject(err);
+ } else {
+ return resolve();
+ }
+ }).pipe(fs.createWriteStream(constants.pathToTestDashboardBundle));
+ });
+}
diff --git a/devtools/test_dashboard/style.css b/devtools/test_dashboard/style.css
new file mode 100644
index 00000000000..bd6f17f1270
--- /dev/null
+++ b/devtools/test_dashboard/style.css
@@ -0,0 +1,64 @@
+html, body{
+ margin: 0;
+ padding: 40px 0 0 0;
+ font-family: helvetica, arial, sans-serif;
+ background-color: #fafafa;
+}
+header{
+ position: fixed;
+ top: 0;
+ height: 40px;
+ width: 100%;
+ border-bottom: 1px solid #4983EC;
+ background-color: #ffffff;
+ z-index: 100000;
+}
+header img{
+ height: 100%;
+ display: inline-block;
+ vertical-align: top;
+}
+header input{
+ float: right;
+ background-color: none;
+ border: none;
+ border-bottom: 1px solid #ddd;
+ padding: 5px;
+ outline: none;
+ margin-top: 6px;
+ margin-right: 10px;
+}
+header span{
+ color: #bbb;
+ font-size: 10px;
+ line-height: 40px;
+}
+#mocks-list{
+ position: fixed;
+ right: 0px;
+ top: 40px;
+ bottom: 0px;
+ z-index: 100000;
+ background-color: #fff;
+ border-left: 1px solid #4983EC;
+ border-bottom: 1px solid #4983EC;
+ border-top: 1px solid #4983EC;
+ overflow: scroll;
+}
+.search-result{
+ display: block;
+ margin-bottom: 2px;
+ padding: 5px 10px;
+ text-align: right;
+ font-size: 12px;
+}
+.search-result:hover{
+ color: #fff;
+ background-color: #4983EC;
+}
+#plots{
+ overflow: scroll;
+}
+.dashboard-plot{
+ margin-bottom: 30px;
+}
diff --git a/devtools/test_dashboard/test_cartesian.js b/devtools/test_dashboard/test_cartesian.js
deleted file mode 100644
index ec01fc98bb3..00000000000
--- a/devtools/test_dashboard/test_cartesian.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*eslint dot-notation: 0*/
-
-var plotButtons = require('./buttons');
-
-var figDir = '../../test/image/baselines/';
-
-var plots = {};
-
-plots['10'] = require('@mocks/10.json');
-plots['14'] = require('@mocks/14.json');
-plots['12'] = require('@mocks/12.json');
-plots['17'] = require('@mocks/17.json');
-plots['22'] = require('@mocks/22.json');
-plots['24'] = require('@mocks/24.json');
-plots['28'] = require('@mocks/28.json');
-plots['30'] = require('@mocks/30.json');
-plots['32'] = require('@mocks/32.json');
-plots['axes_booleans'] = require('@mocks/axes_booleans.json');
-plots['axes_labels'] = require('@mocks/axes_labels.json');
-plots['axes_lines'] = require('@mocks/axes_lines.json');
-plots['axes_range_manual'] = require('@mocks/axes_range_manual.json');
-plots['axes_range_type'] = require('@mocks/axes_range_type.json');
-plots['axes_range_mode'] = require('@mocks/axes_range_mode.json');
-plots['basic_error_bar'] = require('@mocks/basic_error_bar.json');
-plots['bubble_markersize0'] = require('@mocks/bubble_markersize0.json');
-plots['bubble_nonnumeric-sizes'] = require('@mocks/bubble_nonnumeric-sizes.json');
-plots['date_axes'] = require('@mocks/date_axes.json');
-plots['error_bar_asymmetric_array'] = require('@mocks/error_bar_asymmetric_array.json');
-plots['error_bar_asymmetric_constant'] = require('@mocks/error_bar_asymmetric_constant.json');
-plots['error_bar_horizontal'] = require('@mocks/error_bar_horizontal.json');
-plots['error_bar_style'] = require('@mocks/error_bar_style.json');
-plots['fonts'] = require('@mocks/fonts.json');
-plots['global_font'] = require('@mocks/global_font.json');
-plots['legend_inside'] = require('@mocks/legend_inside.json');
-plots['legend_labels'] = require('@mocks/legend_labels.json');
-plots['legend_outside'] = require('@mocks/legend_outside.json');
-plots['legend_style'] = require('@mocks/legend_style.json');
-plots['line_style'] = require('@mocks/line_style.json');
-plots['multiple_subplots'] = require('@mocks/multiple_subplots.json');
-plots['scatter-colorscale-colorbar'] = require('@mocks/scatter-colorscale-colorbar.json');
-plots['scatter-marker-line-colorscales'] = require('@mocks/scatter-marker-line-colorscales.json');
-plots['show_legend'] = require('@mocks/show_legend.json');
-plots['simple_inset'] = require('@mocks/simple_inset.json');
-plots['size_margins'] = require('@mocks/size_margins.json');
-plots['stacked_coupled_subplots'] = require('@mocks/stacked_coupled_subplots.json');
-plots['stacked_subplots'] = require('@mocks/stacked_subplots.json');
-
-plotButtons(plots, figDir);
diff --git a/devtools/test_dashboard/test_geo.js b/devtools/test_dashboard/test_geo.js
deleted file mode 100644
index a9e3bd7075c..00000000000
--- a/devtools/test_dashboard/test_geo.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/*eslint dot-notation: 0*/
-
-var plotButtons = require('./buttons');
-
-var figDir = '../../test/image/baselines/geo_';
-
-var plots = {};
-
-plots['first'] = require('@mocks/geo_first.json');
-plots['second'] = require('@mocks/geo_second.json');
-plots['kavrayskiy7'] = require('@mocks/geo_kavrayskiy7.json');
-plots['custom-colorscale'] = require('@mocks/geo_custom-colorscale.json');
-plots['scattergeo-locations'] = require('@mocks/geo_scattergeo-locations.json');
-plots['multi-geos'] = require('@mocks/geo_multi-geos.json');
-plots['usa-states'] = require('@mocks/geo_usa-states.json');
-plots['legendonly'] = require('@mocks/geo_legendonly.json');
-plots['europe-bubbles'] = require('@mocks/geo_europe-bubbles.json');
-plots['orthographic'] = require('@mocks/geo_orthographic.json');
-plots['big-frame'] = require('@mocks/geo_big-frame.json');
-plots['bg-color'] = require('@mocks/geo_bg-color.json');
-plots['canadian-cites'] = require('@mocks/geo_canadian-cites.json');
-plots['conic-conformal'] = require('@mocks/geo_conic-conformal.json');
-plots['stereographic'] = require('@mocks/geo_stereographic.json');
-plots['choropleth-text'] = require('@mocks/geo_choropleth-text.json');
-plots['choropleth-usa'] = require('@mocks/geo_choropleth-usa.json');
-plots['country-names'] = require('@mocks/geo_country-names.json');
-
-plotButtons(plots, figDir);
diff --git a/devtools/test_dashboard/test_gl2d.js b/devtools/test_dashboard/test_gl2d.js
deleted file mode 100644
index d0e8f8a1501..00000000000
--- a/devtools/test_dashboard/test_gl2d.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/*eslint dot-notation: 0*/
-
-var plotButtons = require('./buttons');
-
-var figDir = '../../test/image/baselines/gl2d_';
-
-var plots = {};
-
-plots['10'] = require('@mocks/gl2d_10.json');
-plots['14'] = require('@mocks/gl2d_14.json');
-plots['12'] = require('@mocks/gl2d_12.json');
-plots['17'] = require('@mocks/gl2d_17.json');
-plots['22'] = require('@mocks/gl2d_22.json');
-plots['24'] = require('@mocks/gl2d_24.json');
-plots['28'] = require('@mocks/gl2d_28.json');
-plots['30'] = require('@mocks/gl2d_30.json');
-plots['32'] = require('@mocks/gl2d_32.json');
-plots['axes_booleans'] = require('@mocks/gl2d_axes_booleans.json');
-plots['axes_labels'] = require('@mocks/gl2d_axes_labels.json');
-plots['axes_lines'] = require('@mocks/gl2d_axes_lines.json');
-plots['axes_range_manual'] = require('@mocks/gl2d_axes_range_manual.json');
-plots['axes_range_type'] = require('@mocks/gl2d_axes_range_type.json');
-plots['axes_range_mode'] = require('@mocks/gl2d_axes_range_mode.json');
-plots['basic_error_bar'] = require('@mocks/gl2d_basic_error_bar.json');
-plots['bubble_markersize0'] = require('@mocks/gl2d_bubble_markersize0.json');
-plots['bubble_nonnumeric-sizes'] = require('@mocks/gl2d_bubble_nonnumeric-sizes.json');
-plots['date_axes'] = require('@mocks/gl2d_date_axes.json');
-plots['error_bar_asymmetric_array'] = require('@mocks/gl2d_error_bar_asymmetric_array.json');
-plots['error_bar_asymmetric_constant'] = require('@mocks/gl2d_error_bar_asymmetric_constant.json');
-plots['error_bar_horizontal'] = require('@mocks/gl2d_error_bar_horizontal.json');
-plots['error_bar_style'] = require('@mocks/gl2d_error_bar_style.json');
-plots['fonts'] = require('@mocks/gl2d_fonts.json');
-plots['global_font'] = require('@mocks/gl2d_global_font.json');
-plots['legend_inside'] = require('@mocks/gl2d_legend_inside.json');
-plots['legend_labels'] = require('@mocks/gl2d_legend_labels.json');
-plots['legend_outside'] = require('@mocks/gl2d_legend_outside.json');
-plots['legend_style'] = require('@mocks/gl2d_legend_style.json');
-plots['line_style'] = require('@mocks/gl2d_line_style.json');
-plots['multiple_subplots'] = require('@mocks/gl2d_multiple_subplots.json');
-plots['scatter-colorscale-colorbar'] = require('@mocks/gl2d_scatter-colorscale-colorbar.json');
-plots['scatter-marker-line-colorscales'] = require('@mocks/gl2d_scatter-marker-line-colorscales.json');
-plots['show_legend'] = require('@mocks/gl2d_show_legend.json');
-plots['simple_inset'] = require('@mocks/gl2d_simple_inset.json');
-plots['size_margins'] = require('@mocks/gl2d_size_margins.json');
-plots['stacked_coupled_subplots'] = require('@mocks/gl2d_stacked_coupled_subplots.json');
-plots['stacked_subplots'] = require('@mocks/gl2d_stacked_subplots.json');
-
-plotButtons(plots, figDir);
diff --git a/devtools/test_dashboard/test_gl3d.js b/devtools/test_dashboard/test_gl3d.js
deleted file mode 100644
index 7ae784ad2ea..00000000000
--- a/devtools/test_dashboard/test_gl3d.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*eslint dot-notation: 0*/
-
-var plotButtons = require('./buttons');
-
-var figDir = '../../test/image/baselines/gl3d_';
-
-var plots = {};
-
-plots['bunny-hull'] = require('@mocks/gl3d_bunny-hull.json');
-plots['ibm-plot'] = require('@mocks/gl3d_ibm-plot.json');
-plots['marker-color'] = require('@mocks/gl3d_marker-color.json');
-plots['log-axis-big'] = require('@mocks/gl3d_log-axis-big.json');
-plots['delaunay'] = require('@mocks/gl3d_delaunay.json');
-plots['log-axis'] = require('@mocks/gl3d_log-axis.json');
-plots['multi-scene'] = require('@mocks/gl3d_multi-scene.json');
-plots['surface-lighting'] = require('@mocks/gl3d_surface-lighting.json');
-plots['z-range'] = require('@mocks/gl3d_z-range.json');
-plots['mirror-ticks'] = require('@mocks/gl3d_mirror-ticks.json');
-plots['autorange-zero'] = require('@mocks/gl3d_autorange-zero.json');
-plots['contour-lines'] = require('@mocks/gl3d_contour-lines.json');
-plots['xy-defined-ticks'] = require('@mocks/gl3d_xy-defined-ticks.json');
-plots['opacity-surface'] = require('@mocks/gl3d_opacity-surface.json');
-plots['projection-traces'] = require('@mocks/gl3d_projection-traces.json');
-plots['opacity-scaling-spikes'] = require('@mocks/gl3d_opacity-scaling-spikes.json');
-plots['text-weirdness'] = require('@mocks/gl3d_text-weirdness.json');
-plots['wire-surface'] = require('@mocks/gl3d_wire-surface.json');
-plots['triangle'] = require('@mocks/gl3d_triangle.json');
-plots['snowden'] = require('@mocks/gl3d_snowden.json');
-plots['bunny'] = require('@mocks/gl3d_bunny.json');
-plots['ribbons'] = require('@mocks/gl3d_ribbons.json');
-plots['scatter-date'] = require('@mocks/gl3d_scatter-date.json');
-plots['cufflinks'] = require('@mocks/gl3d_cufflinks.json');
-plots['chrisp-nan-1'] = require('@mocks/gl3d_chrisp-nan-1.json');
-plots['marker-arrays'] = require('@mocks/gl3d_marker-arrays.json');
-plots['scatter3d-colorscale'] = require('@mocks/gl3d_scatter3d-colorscale.json');
-plots['autocolorscale'] = require('@mocks/gl3d_autocolorscale.json');
-plots['nan-holes'] = require('@mocks/gl3d_nan-holes.json');
-plots['scatter3d-connectgaps'] = require('@mocks/gl3d_scatter3d-connectgaps.json');
-plots['tet'] = require('@mocks/gl3d_tet.json');
-plots['surface_intensity'] = require('@mocks/gl3d_surface_intensity.json');
-
-plotButtons(plots, figDir);
diff --git a/package.json b/package.json
index e7e4f37b441..7775eeda109 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
"ecstatic": "^1.4.0",
"eslint": "^2.1.0",
"falafel": "^1.2.0",
+ "fuse.js": "^2.2.0",
"glob": "^7.0.0",
"jasmine-core": "^2.3.4",
"karma": "^0.13.15",
diff --git a/tasks/util/constants.js b/tasks/util/constants.js
index 68ad619c9e6..fb78cc0c79b 100644
--- a/tasks/util/constants.js
+++ b/tasks/util/constants.js
@@ -19,6 +19,7 @@ module.exports = {
pathToRoot: pathToRoot,
pathToSrc: pathToSrc,
pathToLib: pathToLib,
+ pathToBuild: pathToBuild,
pathToPlotlyIndex: path.join(pathToLib, 'index.js'),
pathToPlotlyCore: path.join(pathToSrc, 'core.js'),