diff --git a/src/components/annotations/click.js b/src/components/annotations/click.js index 8bdc8688a21..38de6b64a36 100644 --- a/src/components/annotations/click.js +++ b/src/components/annotations/click.js @@ -9,8 +9,6 @@ 'use strict'; -var Plotly = require('../../plotly'); - module.exports = { hasClickToShow: hasClickToShow, @@ -59,7 +57,7 @@ function onClick(gd, hoverData) { update['annotations[' + offSet[i] + '].visible'] = false; } - return Plotly.update(gd, {}, update); + return gd._plotAPI.update({}, update); } /* diff --git a/src/components/annotations/draw.js b/src/components/annotations/draw.js index 3bd4875456b..229275ec439 100644 --- a/src/components/annotations/draw.js +++ b/src/components/annotations/draw.js @@ -11,7 +11,6 @@ var d3 = require('d3'); -var Plotly = require('../../plotly'); var Plots = require('../../plots/plots'); var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); @@ -594,7 +593,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { }, doneFn: function(dragged) { if(dragged) { - Plotly.relayout(gd, update); + gd._plotAPI.relayout(update); var notesBox = document.querySelector('.js-notes-box-panel'); if(notesBox) notesBox.redraw(notesBox.selectedObj); } @@ -675,7 +674,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { doneFn: function(dragged) { setCursor(annTextGroupInner); if(dragged) { - Plotly.relayout(gd, update); + gd._plotAPI.relayout(update); var notesBox = document.querySelector('.js-notes-box-panel'); if(notesBox) notesBox.redraw(notesBox.selectedObj); } @@ -701,7 +700,7 @@ function drawRaw(gd, options, index, subplotId, xa, ya) { update[ya._name + '.autorange'] = true; } - Plotly.relayout(gd, update); + gd._plotAPI.relayout(update); }); } else annText.call(textLayout); diff --git a/src/components/colorbar/draw.js b/src/components/colorbar/draw.js index 9aa7f5be855..d85758c9d7d 100644 --- a/src/components/colorbar/draw.js +++ b/src/components/colorbar/draw.js @@ -12,7 +12,6 @@ var d3 = require('d3'); var tinycolor = require('tinycolor2'); -var Plotly = require('../../plotly'); var Plots = require('../../plots/plots'); var Registry = require('../../registry'); var Axes = require('../../plots/cartesian/axes'); @@ -584,7 +583,7 @@ module.exports = function draw(gd, id) { setCursor(container); if(dragged && xf !== undefined && yf !== undefined) { - Plotly.restyle(gd, + gd._plotAPI.restyle( {'colorbar.x': xf, 'colorbar.y': yf}, getTrace().index); } diff --git a/src/components/dragelement/index.js b/src/components/dragelement/index.js index 6b0d47b3b42..8dd13c89bd4 100644 --- a/src/components/dragelement/index.js +++ b/src/components/dragelement/index.js @@ -12,7 +12,6 @@ var mouseOffset = require('mouse-event-offset'); var hasHover = require('has-hover'); -var Plotly = require('../../plotly'); var Lib = require('../../lib'); var constants = require('../../plots/cartesian/constants'); @@ -216,7 +215,7 @@ dragElement.coverSlip = coverSlip; function finishDrag(gd) { gd._dragging = false; - if(gd._replotPending) Plotly.plot(gd); + if(gd._replotPending) gd._plotAPI.plot(); } function pointerOffset(e) { diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 6e6def4260d..a1eccab0447 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -10,7 +10,6 @@ var d3 = require('d3'); -var Plotly = require('../../plotly'); var Lib = require('../../lib'); var Plots = require('../../plots/plots'); var Registry = require('../../registry'); @@ -336,7 +335,7 @@ module.exports = function draw(gd) { }, doneFn: function(dragged, numClicks, e) { if(dragged && xf !== undefined && yf !== undefined) { - Plotly.relayout(gd, {'legend.x': xf, 'legend.y': yf}); + gd._plotAPI.relayout({'legend.x': xf, 'legend.y': yf}); } else { var clickedTrace = fullLayout._infolayer.selectAll('g.traces').filter(function() { @@ -425,7 +424,7 @@ function drawTexts(g, gd) { update.name = text; } - return Plotly.restyle(gd, update, traceIndex); + return gd._plotAPI.restyle(update, traceIndex); }); } else { text.call(textLayout); diff --git a/src/components/legend/handle_click.js b/src/components/legend/handle_click.js index 46ce749f16f..5a290e95c27 100644 --- a/src/components/legend/handle_click.js +++ b/src/components/legend/handle_click.js @@ -8,7 +8,6 @@ 'use strict'; -var Plotly = require('../../plotly'); var Lib = require('../../lib'); var Registry = require('../../registry'); @@ -112,7 +111,7 @@ module.exports = function handleClick(g, gd, numClicks) { } } - Plotly.relayout(gd, 'hiddenlabels', hiddenSlices); + gd._plotAPI.relayout('hiddenlabels', hiddenSlices); } else { var hasLegendgroup = legendgroup && legendgroup.length; var traceIndicesInGroup = []; @@ -218,6 +217,6 @@ module.exports = function handleClick(g, gd, numClicks) { } } - Plotly.restyle(gd, attrUpdate, attrIndices); + gd._plotAPI.restyle(attrUpdate, attrIndices); } }; diff --git a/src/components/modebar/buttons.js b/src/components/modebar/buttons.js index 2fa852e70de..b0d78ca9715 100644 --- a/src/components/modebar/buttons.js +++ b/src/components/modebar/buttons.js @@ -9,7 +9,6 @@ 'use strict'; -var Plotly = require('../../plotly'); var Plots = require('../../plots/plots'); var Axes = require('../../plots/cartesian/axes'); var Lib = require('../../lib'); @@ -251,7 +250,7 @@ function handleCartesian(gd, ev) { aobj[astr] = val; } - Plotly.relayout(gd, aobj); + gd._plotAPI.relayout(aobj); } modeBarButtons.zoom3d = { @@ -304,7 +303,7 @@ function handleDrag3d(gd, ev) { layoutUpdate[sceneIds[i] + '.' + parts[1]] = val; } - Plotly.relayout(gd, layoutUpdate); + gd._plotAPI.relayout(layoutUpdate); } modeBarButtons.resetCameraDefault3d = { @@ -343,7 +342,7 @@ function handleCamera3d(gd, ev) { } } - Plotly.relayout(gd, aobj); + gd._plotAPI.relayout(aobj); } modeBarButtons.hoverClosest3d = { @@ -404,7 +403,7 @@ function handleHover3d(gd, ev) { button._previousVal = Lib.extendDeep({}, currentSpikes); } - Plotly.relayout(gd, layoutUpdate); + gd._plotAPI.relayout(layoutUpdate); } modeBarButtons.zoomInGeo = { @@ -498,7 +497,7 @@ function toggleHover(gd) { var newHover = gd._fullLayout.hovermode ? false : onHoverVal; - Plotly.relayout(gd, 'hovermode', newHover); + gd._plotAPI.relayout('hovermode', newHover); } // buttons when more then one plot types are present @@ -555,7 +554,7 @@ modeBarButtons.toggleSpikelines = { var aobj = setSpikelineVisibility(gd); aobj.hovermode = 'closest'; - Plotly.relayout(gd, aobj); + gd._plotAPI.relayout(aobj); } }; @@ -597,6 +596,6 @@ modeBarButtons.resetViewMapbox = { } } - Plotly.relayout(gd, aObj); + gd._plotAPI.relayout(aObj); } }; diff --git a/src/components/rangeselector/draw.js b/src/components/rangeselector/draw.js index 382d16f7697..b2313c82bcd 100644 --- a/src/components/rangeselector/draw.js +++ b/src/components/rangeselector/draw.js @@ -11,7 +11,6 @@ var d3 = require('d3'); -var Plotly = require('../../plotly'); var Plots = require('../../plots/plots'); var Color = require('../color'); var Drawing = require('../drawing'); @@ -66,7 +65,7 @@ module.exports = function draw(gd) { button.on('click', function() { if(gd._dragged) return; - Plotly.relayout(gd, update); + gd._plotAPI.relayout(update); }); button.on('mouseover', function() { diff --git a/src/components/rangeslider/draw.js b/src/components/rangeslider/draw.js index 0e68681356b..5167d92a605 100644 --- a/src/components/rangeslider/draw.js +++ b/src/components/rangeslider/draw.js @@ -10,7 +10,6 @@ var d3 = require('d3'); -var Plotly = require('../../plotly'); var Plots = require('../../plots/plots'); var Lib = require('../../lib'); @@ -247,7 +246,7 @@ function setDataRange(rangeSlider, gd, axisOpts, opts) { dataMax = clamp(opts.p2d(opts._pixelMax)); window.requestAnimationFrame(function() { - Plotly.relayout(gd, axisOpts._name + '.range', [dataMin, dataMax]); + gd._plotAPI.relayout(axisOpts._name + '.range', [dataMin, dataMax]); }); } diff --git a/src/components/shapes/draw.js b/src/components/shapes/draw.js index 6b51ce15461..04e95427f67 100644 --- a/src/components/shapes/draw.js +++ b/src/components/shapes/draw.js @@ -9,7 +9,6 @@ 'use strict'; -var Plotly = require('../../plotly'); var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); var Color = require('../color'); @@ -214,7 +213,7 @@ function setupDragElement(gd, shapePath, shapeOptions, index) { function endDrag(dragged) { setCursor(shapePath); if(dragged) { - Plotly.relayout(gd, update); + gd._plotAPI.relayout(update); } } diff --git a/src/components/titles/index.js b/src/components/titles/index.js index a2819784c7b..96866b25130 100644 --- a/src/components/titles/index.js +++ b/src/components/titles/index.js @@ -12,8 +12,8 @@ var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); -var Plotly = require('../../plotly'); -var Plots = require('../../plots/plots'); +var previousPromises = require('../../plots/previous_promises'); +var fontWeight = require('../../plots/font_weight'); var Lib = require('../../lib'); var Drawing = require('../drawing'); var Color = require('../color'); @@ -124,12 +124,12 @@ Titles.draw = function(gd, titleClass, options) { 'font-size': d3.round(fontSize, 2) + 'px', fill: Color.rgb(fontColor), opacity: opacity * Color.opacity(fontColor), - 'font-weight': Plots.fontWeight + 'font-weight': fontWeight }) .attr(attributes) .call(svgTextUtils.convertToTspans, gd); - return Plots.previousPromises(gd); + return previousPromises(gd); } function scootTitle(titleElIn) { @@ -222,8 +222,8 @@ Titles.draw = function(gd, titleClass, options) { el.call(svgTextUtils.makeEditable, {gd: gd}) .on('edit', function(text) { - if(traceIndex !== undefined) Plotly.restyle(gd, prop, text, traceIndex); - else Plotly.relayout(gd, prop, text); + if(traceIndex !== undefined) gd._plotAPI.restyle(prop, text, traceIndex); + else gd._plotAPI.relayout(prop, text); }) .on('cancel', function() { this.text(this.attr('data-unformatted')) diff --git a/src/plot_api/bind_plot_api.js b/src/plot_api/bind_plot_api.js new file mode 100644 index 00000000000..2bea8c1f50f --- /dev/null +++ b/src/plot_api/bind_plot_api.js @@ -0,0 +1,44 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + + +module.exports = function bindPlotAPI(gd, Plotly) { + var i; + + if(gd._plotAPI) return; + + gd._plotAPI = {}; + var methods = [ + 'plot', 'newPlot', 'restyle', 'relayout', 'redraw', + 'update', 'extendTraces', 'prependTraces', 'addTraces', + 'deleteTraces', 'moveTraces', 'purge', 'addFrames', + 'deleteFrames', 'animate' + ]; + + function bindMethod(method) { + return function() { + var i; + var args = [gd]; + for(i = 0; i < arguments.length; i++) { + args[i + 1] = arguments[i]; + } + return method.apply(null, args); + }; + } + + for(i = 0; i < methods.length; i++) { + gd._plotAPI[methods[i]] = bindMethod(Plotly[methods[i]]); + } + + // This is needed for snapshot/toimage, which needs to plot to a *different* gd. + // In other words, it's just regular Plotly plot: + gd._plotAPI.plotWithGd = Plotly.plot; +}; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 58244368e0e..7ad5c25e2c0 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -13,8 +13,8 @@ var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); var hasHover = require('has-hover'); +var defaultConfig = require('./plot_config'); -var Plotly = require('../plotly'); var Lib = require('../lib'); var Events = require('../lib/events'); var Queue = require('../lib/queue'); @@ -23,6 +23,7 @@ var Registry = require('../registry'); var PlotSchema = require('./plot_schema'); var Plots = require('../plots/plots'); var Polar = require('../plots/polar'); +var Axes = require('../plots/cartesian/axes'); var initInteractions = require('../plots/cartesian/graph_interact'); var Drawing = require('../components/drawing'); @@ -41,8 +42,14 @@ var axisConstraints = require('../plots/cartesian/constraints'); var enforceAxisConstraints = axisConstraints.enforce; var cleanAxisConstraints = axisConstraints.clean; var axisIds = require('../plots/cartesian/axis_ids'); +var bindPlotAPI = require('./bind_plot_api'); +var Plotly = {}; +module.exports = Plotly; + +Plotly.purge = require('./purge'); + /** * Main plot-creation function * @@ -61,6 +68,7 @@ Plotly.plot = function(gd, data, layout, config) { var frames; gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); // Events.init is idempotent and bails early if gd has already been init'd Events.init(gd); @@ -85,7 +93,7 @@ Plotly.plot = function(gd, data, layout, config) { function addFrames() { if(frames) { - return Plotly.addFrames(gd, frames); + return gd._plotAPI.addFrames(frames); } } @@ -164,7 +172,7 @@ Plotly.plot = function(gd, data, layout, config) { Drawing.initGradients(gd); // save initial show spikes once per graph - if(graphWasEmpty) Plotly.Axes.saveShowSpikeInitial(gd); + if(graphWasEmpty) Axes.saveShowSpikeInitial(gd); // prepare the data and find the autorange @@ -279,24 +287,24 @@ Plotly.plot = function(gd, data, layout, config) { function doAutoRangeAndConstraints() { if(gd._transitioning) return; - var axList = Plotly.Axes.list(gd, '', true); + var axList = Axes.list(gd, '', true); for(var i = 0; i < axList.length; i++) { var ax = axList[i]; cleanAxisConstraints(gd, ax); - Plotly.Axes.doAutoRange(ax); + Axes.doAutoRange(ax); } enforceAxisConstraints(gd); // store initial ranges *after* enforcing constraints, otherwise // we will never look like we're at the initial ranges - if(graphWasEmpty) Plotly.Axes.saveRangeInitial(gd); + if(graphWasEmpty) Axes.saveRangeInitial(gd); } // draw ticks, titles, and calculate axis scaling (._b, ._m) function drawAxes() { - return Plotly.Axes.doTicks(gd, 'redraw'); + return Axes.doTicks(gd, 'redraw'); } // Now plot the data @@ -414,7 +422,7 @@ function opaqueSetBackground(gd, bgColor) { } function setPlotContext(gd, config) { - if(!gd._context) gd._context = Lib.extendDeep({}, Plotly.defaultConfig); + if(!gd._context) gd._context = Lib.extendDeep({}, defaultConfig); var context = gd._context; var i, keys, key; @@ -582,6 +590,7 @@ function plotPolar(gd, data, layout) { // convenience function to force a full redraw, mostly for use by plotly.js Plotly.redraw = function(gd) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); if(!Lib.isPlotDiv(gd)) { throw new Error('This element is not a Plotly plot: ' + gd); @@ -591,7 +600,7 @@ Plotly.redraw = function(gd) { helpers.cleanLayout(gd.layout); gd.calcdata = undefined; - return Plotly.plot(gd).then(function() { + return gd._plotAPI.plot().then(function() { gd.emit('plotly_redraw'); return gd; }); @@ -607,12 +616,13 @@ Plotly.redraw = function(gd) { */ Plotly.newPlot = function(gd, data, layout, config) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); // remove gl contexts Plots.cleanPlot([], {}, gd._fullData || {}, gd._fullLayout || {}); Plots.purge(gd); - return Plotly.plot(gd, data, layout, config); + return gd._plotAPI.plot(data, layout, config); }; /** @@ -960,6 +970,7 @@ function spliceTraces(gd, update, indices, maxPoints, lengthenArray, spliceArray */ Plotly.extendTraces = function extendTraces(gd, update, indices, maxPoints) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); var undo = spliceTraces(gd, update, indices, maxPoints, @@ -977,16 +988,17 @@ Plotly.extendTraces = function extendTraces(gd, update, indices, maxPoints) { return target.splice(0, target.length - maxPoints); }); - var promise = Plotly.redraw(gd); + var promise = gd._plotAPI.redraw(); - var undoArgs = [gd, undo.update, indices, undo.maxPoints]; - Queue.add(gd, Plotly.prependTraces, undoArgs, extendTraces, arguments); + var undoArgs = [undo.update, indices, undo.maxPoints]; + Queue.add(gd, gd._plotAPI.prependTraces, undoArgs, gd._plotAPI.extendTraces, Array.prototype.slice.call(arguments, 1)); return promise; }; Plotly.prependTraces = function prependTraces(gd, update, indices, maxPoints) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); var undo = spliceTraces(gd, update, indices, maxPoints, @@ -1004,10 +1016,10 @@ Plotly.prependTraces = function prependTraces(gd, update, indices, maxPoints) { return target.splice(maxPoints, target.length); }); - var promise = Plotly.redraw(gd); + var promise = gd._plotAPI.redraw(); - var undoArgs = [gd, undo.update, indices, undo.maxPoints]; - Queue.add(gd, Plotly.extendTraces, undoArgs, prependTraces, arguments); + var undoArgs = [undo.update, indices, undo.maxPoints]; + Queue.add(gd, gd._plotAPI.extendTraces, undoArgs, gd._plotAPI.prependTraces, Array.prototype.slice.call(arguments, 1)); return promise; }; @@ -1023,12 +1035,13 @@ Plotly.prependTraces = function prependTraces(gd, update, indices, maxPoints) { */ Plotly.addTraces = function addTraces(gd, traces, newIndices) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); var currentIndices = [], - undoFunc = Plotly.deleteTraces, - redoFunc = addTraces, - undoArgs = [gd, currentIndices], - redoArgs = [gd, traces], // no newIndices here + undoFunc = gd._plotAPI.deleteTraces, + redoFunc = gd._plotAPI.addTraces, + undoArgs = [currentIndices], + redoArgs = [traces], // no newIndices here i, promise; @@ -1060,7 +1073,7 @@ Plotly.addTraces = function addTraces(gd, traces, newIndices) { // if the user didn't define newIndices, they just want the traces appended // i.e., we can simply redraw and be done if(typeof newIndices === 'undefined') { - promise = Plotly.redraw(gd); + promise = gd._plotAPI.redraw(); Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); return promise; } @@ -1086,7 +1099,7 @@ Plotly.addTraces = function addTraces(gd, traces, newIndices) { // this requires some extra work that moveTraces will do Queue.startSequence(gd); Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); - promise = Plotly.moveTraces(gd, currentIndices, newIndices); + promise = gd._plotAPI.moveTraces(currentIndices, newIndices); Queue.stopSequence(gd); return promise; }; @@ -1100,12 +1113,13 @@ Plotly.addTraces = function addTraces(gd, traces, newIndices) { */ Plotly.deleteTraces = function deleteTraces(gd, indices) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); var traces = [], - undoFunc = Plotly.addTraces, - redoFunc = deleteTraces, - undoArgs = [gd, traces, indices], - redoArgs = [gd, indices], + undoFunc = gd._plotAPI.addTraces, + redoFunc = gd._plotAPI.deleteTraces, + undoArgs = [traces, indices], + redoArgs = [indices], i, deletedTrace; @@ -1127,7 +1141,7 @@ Plotly.deleteTraces = function deleteTraces(gd, indices) { traces.push(deletedTrace); } - var promise = Plotly.redraw(gd); + var promise = gd._plotAPI.redraw(); Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); return promise; @@ -1166,13 +1180,14 @@ Plotly.deleteTraces = function deleteTraces(gd, indices) { */ Plotly.moveTraces = function moveTraces(gd, currentIndices, newIndices) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); var newData = [], movingTraceMap = [], - undoFunc = moveTraces, - redoFunc = moveTraces, - undoArgs = [gd, newIndices, currentIndices], - redoArgs = [gd, currentIndices, newIndices], + undoFunc = gd._plotAPI.moveTraces, + redoFunc = gd._plotAPI.moveTraces, + undoArgs = [newIndices, currentIndices], + redoArgs = [currentIndices, newIndices], i; // to reduce complexity here, check args elsewhere @@ -1225,7 +1240,7 @@ Plotly.moveTraces = function moveTraces(gd, currentIndices, newIndices) { gd.data = newData; - var promise = Plotly.redraw(gd); + var promise = gd._plotAPI.redraw(); Queue.add(gd, undoFunc, undoArgs, redoFunc, redoArgs); return promise; @@ -1264,6 +1279,7 @@ Plotly.moveTraces = function moveTraces(gd, currentIndices, newIndices) { Plotly.restyle = function restyle(gd, astr, val, _traces) { gd = helpers.getGraphDiv(gd); helpers.clearPromiseQueue(gd); + bindPlotAPI(gd, Plotly); var aobj = {}; if(typeof astr === 'string') aobj[astr] = val; @@ -1292,7 +1308,7 @@ Plotly.restyle = function restyle(gd, astr, val, _traces) { var seq = []; if(flags.fullReplot) { - seq.push(Plotly.plot); + seq.push(function() { return gd._plotAPI.plot(); }); } else { seq.push(Plots.previousPromises); @@ -1305,8 +1321,8 @@ Plotly.restyle = function restyle(gd, astr, val, _traces) { seq.push(Plots.rehover); Queue.add(gd, - restyle, [gd, specs.undoit, specs.traces], - restyle, [gd, specs.redoit, specs.traces] + gd._plotAPI.restyle, [specs.undoit, specs.traces], + gd._plotAPI.restyle, [specs.redoit, specs.traces] ); var plotDone = Lib.syncOrAsync(seq, gd); @@ -1346,7 +1362,7 @@ function _restyle(gd, aobj, traces) { // for autoranging multiple axes function addToAxlist(axid) { - var axName = Plotly.Axes.id2name(axid); + var axName = Axes.id2name(axid); if(axlist.indexOf(axName) === -1) axlist.push(axName); } @@ -1546,7 +1562,7 @@ function _restyle(gd, aobj, traces) { // swap the data attributes of the relevant x and y axes? if(['swapxyaxes', 'orientationaxes'].indexOf(ai) !== -1) { - Plotly.Axes.swap(gd, traces); + Axes.swap(gd, traces); } // swap hovermode if set to "compare x/y data" @@ -1583,7 +1599,7 @@ function _restyle(gd, aobj, traces) { // do we need to force a recalc? var autorangeOn = false; - var axList = Plotly.Axes.list(gd); + var axList = Axes.list(gd); for(i = 0; i < axList.length; i++) { if(axList[i].autorange) { autorangeOn = true; @@ -1608,7 +1624,7 @@ function _restyle(gd, aobj, traces) { } // no data on this axis - delete it. - doextra('LAYOUT' + Plotly.Axes.id2name(axId), null, 0); + doextra('LAYOUT' + Axes.id2name(axId), null, 0); } // combine a few flags together; @@ -1651,6 +1667,7 @@ function _restyle(gd, aobj, traces) { Plotly.relayout = function relayout(gd, astr, val) { gd = helpers.getGraphDiv(gd); helpers.clearPromiseQueue(gd); + bindPlotAPI(gd, Plotly); if(gd.framework && gd.framework.isPolar) { return Promise.resolve(gd); @@ -1697,8 +1714,8 @@ Plotly.relayout = function relayout(gd, astr, val) { seq.push(Plots.rehover); Queue.add(gd, - relayout, [gd, specs.undoit], - relayout, [gd, specs.redoit] + gd._plotAPI.relayout, [specs.undoit], + gd._plotAPI.relayout, [specs.redoit] ); var plotDone = Lib.syncOrAsync(seq, gd); @@ -1714,7 +1731,7 @@ function _relayout(gd, aobj) { var layout = gd.layout, fullLayout = gd._fullLayout, keys = Object.keys(aobj), - axes = Plotly.Axes.list(gd), + axes = Axes.list(gd), arrayEdits = {}, arrayStr, i, @@ -1769,7 +1786,7 @@ function _relayout(gd, aobj) { function refAutorange(obj, axLetter) { if(!Lib.isPlainObject(obj)) return false; var axRef = obj[axLetter + 'ref'] || axLetter, - ax = Plotly.Axes.getFromId(gd, axRef); + ax = Axes.getFromId(gd, axRef); if(!ax && axRef.charAt(0) === axLetter) { // fall back on the primary axis in case we've referenced a @@ -1779,7 +1796,7 @@ function _relayout(gd, aobj) { // The only thing this is used for is to determine whether to // do a full `recalc`, so the only ill effect of this error is // to waste some time. - ax = Plotly.Axes.getFromId(gd, axLetter); + ax = Axes.getFromId(gd, axLetter); } return (ax || {}).autorange; } @@ -2081,6 +2098,7 @@ function _relayout(gd, aobj) { Plotly.update = function update(gd, traceUpdate, layoutUpdate, _traces) { gd = helpers.getGraphDiv(gd); helpers.clearPromiseQueue(gd); + bindPlotAPI(gd, Plotly); if(gd.framework && gd.framework.isPolar) { return Promise.resolve(gd); @@ -2116,10 +2134,10 @@ Plotly.update = function update(gd, traceUpdate, layoutUpdate, _traces) { gd.data = undefined; gd.layout = undefined; - seq.push(function() { return Plotly.plot(gd, data, layout); }); + seq.push(function() { return gd._plotAPI.plot(data, layout); }); } else if(restyleFlags.fullReplot) { - seq.push(Plotly.plot); + seq.push(function() { return gd._plotAPI.plot(); }); } else if(relayoutFlags.layoutReplot) { seq.push(subroutines.layoutReplot); @@ -2140,8 +2158,8 @@ Plotly.update = function update(gd, traceUpdate, layoutUpdate, _traces) { seq.push(Plots.rehover); Queue.add(gd, - update, [gd, restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces], - update, [gd, restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces] + gd._plotAPI.update, [restyleSpecs.undoit, relayoutSpecs.undoit, restyleSpecs.traces], + gd._plotAPI.update, [restyleSpecs.redoit, relayoutSpecs.redoit, restyleSpecs.traces] ); var plotDone = Lib.syncOrAsync(seq, gd); @@ -2186,6 +2204,7 @@ Plotly.update = function update(gd, traceUpdate, layoutUpdate, _traces) { */ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); if(!Lib.isPlotDiv(gd)) { throw new Error( @@ -2550,6 +2569,7 @@ Plotly.animate = function(gd, frameOrGroupNameOrFrameList, animationOpts) { */ Plotly.addFrames = function(gd, frameList, indices) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); var numericNameWarningCount = 0; @@ -2674,6 +2694,7 @@ Plotly.addFrames = function(gd, frameList, indices) { */ Plotly.deleteFrames = function(gd, frameList) { gd = helpers.getGraphDiv(gd); + bindPlotAPI(gd, Plotly); if(!Lib.isPlotDiv(gd)) { throw new Error('This element is not a Plotly plot: ' + gd); @@ -2710,40 +2731,6 @@ Plotly.deleteFrames = function(gd, frameList) { return Plots.modifyFrames(gd, ops); }; -/** - * 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 = helpers.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._legendMouseDownTime; - delete gd._hmpixcount; - delete gd._hmlumcount; - - return gd; -}; - // ------------------------------------------------------- // makePlotFramework: Create the plot container and axes // ------------------------------------------------------- diff --git a/src/plot_api/purge.js b/src/plot_api/purge.js new file mode 100644 index 00000000000..406ff440e54 --- /dev/null +++ b/src/plot_api/purge.js @@ -0,0 +1,53 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +var Plots = require('../plots/plots'); +var Events = require('../lib/events'); +var helpers = require('./helpers'); + +/** + * Purge a graph container div back to its initial pre-Plotly.plot state + * + * This method is contained in its own file since it needs to be run internally + * on things that may or may not be a plot, so that it's difficult to require + * without creating a circular dependency. (see: plot_api/to_image.js) + * + * @param {string id or DOM element} gd + * the id or DOM element of the graph container div + */ +module.exports = function purge(gd) { + gd = helpers.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._legendMouseDownTime; + delete gd._hmpixcount; + delete gd._hmlumcount; + delete gd._plotAPI; + + return gd; +}; diff --git a/src/plot_api/subroutines.js b/src/plot_api/subroutines.js index 4e907028c32..b758146f824 100644 --- a/src/plot_api/subroutines.js +++ b/src/plot_api/subroutines.js @@ -10,10 +10,10 @@ 'use strict'; var d3 = require('d3'); -var Plotly = require('../plotly'); var Registry = require('../registry'); var Plots = require('../plots/plots'); var Lib = require('../lib'); +var Axes = require('../plots/cartesian/axes'); var Color = require('../components/color'); var Drawing = require('../components/drawing'); @@ -45,7 +45,7 @@ exports.lsInner = function(gd) { var fullLayout = gd._fullLayout; var gs = fullLayout._size; var pad = gs.p; - var axList = Plotly.Axes.list(gd); + var axList = Axes.list(gd); // _has('cartesian') means SVG specifically, not GL2D - but GL2D // can still get here because it makes some of the SVG structure @@ -343,7 +343,7 @@ exports.lsInner = function(gd) { if(showFreeY) freeFinished[ya._id] = 1; }); - Plotly.Axes.makeClipPaths(gd); + Axes.makeClipPaths(gd); exports.drawMainTitle(gd); ModeBar.manage(gd); @@ -474,7 +474,7 @@ exports.doColorBars = function(gd) { exports.layoutReplot = function(gd) { var layout = gd.layout; gd.layout = undefined; - return Plotly.plot(gd, '', layout); + return gd._plotAPI.plot('', layout); }; exports.doLegend = function(gd) { @@ -483,7 +483,7 @@ exports.doLegend = function(gd) { }; exports.doTicksRelayout = function(gd) { - Plotly.Axes.doTicks(gd, 'redraw'); + Axes.doTicks(gd, 'redraw'); exports.drawMainTitle(gd); return Plots.previousPromises(gd); }; diff --git a/src/plot_api/to_image.js b/src/plot_api/to_image.js index 059cd973c50..504ddf8cfa1 100644 --- a/src/plot_api/to_image.js +++ b/src/plot_api/to_image.js @@ -8,8 +8,8 @@ 'use strict'; -var Plotly = require('../plotly'); var Lib = require('../lib'); +var purge = require('./purge'); var helpers = require('../snapshot/helpers'); var toSVG = require('../snapshot/tosvg'); @@ -157,7 +157,7 @@ function toImage(gd, opts) { var width = clonedGd._fullLayout.width; var height = clonedGd._fullLayout.height; - Plotly.purge(clonedGd); + purge(clonedGd); document.body.removeChild(clonedGd); if(format === 'svg') { @@ -198,7 +198,7 @@ function toImage(gd, opts) { } return new Promise(function(resolve, reject) { - Plotly.plot(clonedGd, data, layoutImage, configImage) + gd._plotAPI.plotWithGd(clonedGd, data, layoutImage, configImage) .then(redrawFunc) .then(wait) .then(convert) diff --git a/src/plotly.js b/src/plotly.js index 1eae9f219a6..25337d1ecf0 100644 --- a/src/plotly.js +++ b/src/plotly.js @@ -19,14 +19,20 @@ */ // configuration -exports.defaultConfig = require('./plot_api/plot_config'); +module.exports.defaultConfig = require('./plot_api/plot_config'); // plots -exports.Plots = require('./plots/plots'); -exports.Axes = require('./plots/cartesian/axes'); +module.exports.Plots = require('./plots/plots'); +module.exports.Axes = require('./plots/cartesian/axes'); // components -exports.ModeBar = require('./components/modebar'); +module.exports.ModeBar = require('./components/modebar'); // plot api -require('./plot_api/plot_api'); +var API = require('./plot_api/plot_api'); + +for(var method in API) { + if(API.hasOwnProperty(method)) { + module.exports[method] = API[method]; + } +} diff --git a/src/plots/cartesian/axis_ids.js b/src/plots/cartesian/axis_ids.js index 63b9fae6e77..3d4ad979969 100644 --- a/src/plots/cartesian/axis_ids.js +++ b/src/plots/cartesian/axis_ids.js @@ -9,10 +9,10 @@ 'use strict'; var Registry = require('../../registry'); -var Plots = require('../plots'); var Lib = require('../../lib'); var constants = require('./constants'); +var getSubplotIds = require('../get_subplot_ids'); // convert between axis names (xaxis, xaxis2, etc, elements of gd.layout) @@ -65,7 +65,7 @@ function listNames(gd, axLetter, only2d) { var names = filterAxis(fullLayout, ''); if(only2d) return names; - var sceneIds3D = Plots.getSubplotIds(fullLayout, 'gl3d') || []; + var sceneIds3D = getSubplotIds(fullLayout, 'gl3d') || []; for(var i = 0; i < sceneIds3D.length; i++) { var sceneId = sceneIds3D[i]; names = names.concat( diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index caacaaa43dc..33422e8d2bb 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -12,7 +12,6 @@ var d3 = require('d3'); var tinycolor = require('tinycolor2'); -var Plotly = require('../../plotly'); var Registry = require('../../registry'); var Lib = require('../../lib'); var svgTextUtils = require('../../lib/svg_text_utils'); @@ -332,7 +331,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { .on('edit', function(text) { var v = ax.d2r(text); if(v !== undefined) { - Plotly.relayout(gd, attrStr, v); + gd._plotAPI.relayout(attrStr, v); } }); } @@ -646,7 +645,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { } gd.emit('plotly_doubleclick', null); - Plotly.relayout(gd, attrs); + gd._plotAPI.relayout(attrs); } // dragTail - finish a drag event with a redraw @@ -662,7 +661,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { // accumulated MathJax promises - wait for them before we relayout. Lib.syncOrAsync([ Plots.previousPromises, - function() { Plotly.relayout(gd, updates); } + function() { gd._plotAPI.relayout(updates); } ], gd); } diff --git a/src/plots/cartesian/transition_axes.js b/src/plots/cartesian/transition_axes.js index 181ce35cb19..addd2b34a34 100644 --- a/src/plots/cartesian/transition_axes.js +++ b/src/plots/cartesian/transition_axes.js @@ -11,7 +11,6 @@ var d3 = require('d3'); -var Plotly = require('../../plotly'); var Registry = require('../../registry'); var Drawing = require('../../components/drawing'); var Axes = require('./axes'); @@ -275,7 +274,7 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo // Signal that this transition has completed: onComplete && onComplete(); - return Plotly.relayout(gd, aobj).then(function() { + return gd._plotAPI.relayout(aobj).then(function() { for(var i = 0; i < affectedSubplots.length; i++) { unsetSubplotTransform(affectedSubplots[i]); } @@ -292,7 +291,7 @@ module.exports = function transitionAxes(gd, newLayout, transitionOpts, makeOnCo axi.range = axi._r.slice(); } - return Plotly.relayout(gd, aobj).then(function() { + return gd._plotAPI.relayout(aobj).then(function() { for(var i = 0; i < affectedSubplots.length; i++) { unsetSubplotTransform(affectedSubplots[i]); } diff --git a/src/plots/command.js b/src/plots/command.js index 8799ee07fad..be9749c5b8a 100644 --- a/src/plots/command.js +++ b/src/plots/command.js @@ -9,7 +9,6 @@ 'use strict'; -var Plotly = require('../plotly'); var Lib = require('../lib'); /* @@ -264,9 +263,9 @@ function bindingValueHasChanged(gd, binding, cache) { exports.executeAPICommand = function(gd, method, args) { if(method === 'skip') return Promise.resolve(); - var apiMethod = Plotly[method]; + var apiMethod = gd._plotAPI[method]; - var allArgs = [gd]; + var allArgs = []; if(!Array.isArray(args)) args = []; diff --git a/src/plots/font_weight.js b/src/plots/font_weight.js new file mode 100644 index 00000000000..403f8dc6609 --- /dev/null +++ b/src/plots/font_weight.js @@ -0,0 +1,13 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +// TODO make this a plot attribute? +module.exports.fontWeight = 'normal'; diff --git a/src/plots/get_subplot_ids.js b/src/plots/get_subplot_ids.js new file mode 100644 index 00000000000..b36f0c0eef9 --- /dev/null +++ b/src/plots/get_subplot_ids.js @@ -0,0 +1,54 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + + +var Registry = require('../registry'); + +/** + * Get the ids of the current subplots. + * + * @param {object} layout plotly full layout object. + * @param {string} type subplot type to look for. + * + * @return {array} list of ordered subplot ids (strings). + * + */ +module.exports = function getSubplotIds(layout, type) { + var _module = Registry.subplotsRegistry[type]; + + if(!_module) return []; + + // layout must be 'fullLayout' here + if(type === 'cartesian' && (!layout._has || !layout._has('cartesian'))) return []; + if(type === 'gl2d' && (!layout._has || !layout._has('gl2d'))) return []; + if(type === 'cartesian' || type === 'gl2d') { + return Object.keys(layout._plots || {}); + } + + var attrRegex = _module.attrRegex, + layoutKeys = Object.keys(layout), + subplotIds = []; + + for(var i = 0; i < layoutKeys.length; i++) { + var layoutKey = layoutKeys[i]; + + if(attrRegex.test(layoutKey)) subplotIds.push(layoutKey); + } + + // order the ids + var idLen = _module.idRoot.length; + subplotIds.sort(function(a, b) { + var aNum = +(a.substr(idLen) || 1), + bNum = +(b.substr(idLen) || 1); + return aNum - bNum; + }); + + return subplotIds; +}; diff --git a/src/plots/plots.js b/src/plots/plots.js index 64bc8952617..c851fe87acd 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -12,12 +12,12 @@ var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); -var Plotly = require('../plotly'); var PlotSchema = require('../plot_api/plot_schema'); var Registry = require('../registry'); var Lib = require('../lib'); var Color = require('../components/color'); var BADNUM = require('../constants/numerical').BADNUM; +var Axes = require('./cartesian/axes'); var plots = module.exports = {}; @@ -29,16 +29,16 @@ var relinkPrivateKeys = Lib.relinkPrivateKeys; // Expose registry methods on Plots for backward-compatibility Lib.extendFlat(plots, Registry); +plots.previousPromises = require('./previous_promises'); plots.attributes = require('./attributes'); plots.attributes.type.values = plots.allTypes; plots.fontAttrs = require('./font_attributes'); plots.layoutAttributes = require('./layout_attributes'); +plots.fontWeight = require('./font_weight'); +plots.getSubplotIds = require('./get_subplot_ids'); -// TODO make this a plot attribute? -plots.fontWeight = 'normal'; - -var subplotsRegistry = plots.subplotsRegistry; -var transformsRegistry = plots.transformsRegistry; +var subplotsRegistry = Registry.subplotsRegistry; +var transformsRegistry = Registry.transformsRegistry; var ErrorBars = require('../components/errorbars'); @@ -67,14 +67,14 @@ plots.hasSimpleAPICommandBindings = commandModule.hasSimpleAPICommandBindings; plots.findSubplotIds = function findSubplotIds(data, type) { var subplotIds = []; - if(!plots.subplotsRegistry[type]) return subplotIds; + if(!subplotsRegistry[type]) return subplotIds; - var attr = plots.subplotsRegistry[type].attr; + var attr = subplotsRegistry[type].attr; for(var i = 0; i < data.length; i++) { var trace = data[i]; - if(plots.traceIs(trace, type) && subplotIds.indexOf(trace[attr]) === -1) { + if(Registry.traceIs(trace, type) && subplotIds.indexOf(trace[attr]) === -1) { subplotIds.push(trace[attr]); } } @@ -82,48 +82,6 @@ plots.findSubplotIds = function findSubplotIds(data, type) { return subplotIds; }; -/** - * Get the ids of the current subplots. - * - * @param {object} layout plotly full layout object. - * @param {string} type subplot type to look for. - * - * @return {array} list of ordered subplot ids (strings). - * - */ -plots.getSubplotIds = function getSubplotIds(layout, type) { - var _module = plots.subplotsRegistry[type]; - - if(!_module) return []; - - // layout must be 'fullLayout' here - if(type === 'cartesian' && (!layout._has || !layout._has('cartesian'))) return []; - if(type === 'gl2d' && (!layout._has || !layout._has('gl2d'))) return []; - if(type === 'cartesian' || type === 'gl2d') { - return Object.keys(layout._plots || {}); - } - - var attrRegex = _module.attrRegex, - layoutKeys = Object.keys(layout), - subplotIds = []; - - for(var i = 0; i < layoutKeys.length; i++) { - var layoutKey = layoutKeys[i]; - - if(attrRegex.test(layoutKey)) subplotIds.push(layoutKey); - } - - // order the ids - var idLen = _module.idRoot.length; - subplotIds.sort(function(a, b) { - var aNum = +(a.substr(idLen) || 1), - bNum = +(b.substr(idLen) || 1); - return aNum - bNum; - }); - - return subplotIds; -}; - /** * Get the data trace(s) associated with a given subplot. * @@ -135,17 +93,17 @@ plots.getSubplotIds = function getSubplotIds(layout, type) { * */ plots.getSubplotData = function getSubplotData(data, type, subplotId) { - if(!plots.subplotsRegistry[type]) return []; + if(!subplotsRegistry[type]) return []; - var attr = plots.subplotsRegistry[type].attr, + var attr = subplotsRegistry[type].attr, subplotData = [], trace; for(var i = 0; i < data.length; i++) { trace = data[i]; - if(type === 'gl2d' && plots.traceIs(trace, 'gl2d')) { - var spmatch = Plotly.Axes.subplotMatch, + if(type === 'gl2d' && Registry.traceIs(trace, 'gl2d')) { + var spmatch = Axes.subplotMatch, subplotX = 'x' + subplotId.match(spmatch)[1], subplotY = 'y' + subplotId.match(spmatch)[2]; @@ -171,9 +129,9 @@ plots.getSubplotData = function getSubplotData(data, type, subplotId) { * @return {array} array of calcdata traces */ plots.getSubplotCalcData = function(calcData, type, subplotId) { - if(!plots.subplotsRegistry[type]) return []; + if(!subplotsRegistry[type]) return []; - var attr = plots.subplotsRegistry[type].attr; + var attr = subplotsRegistry[type].attr; var subplotCalcData = []; for(var i = 0; i < calcData.length; i++) { @@ -234,7 +192,7 @@ plots.resize = function(gd) { // nor should it be included in the undo queue gd.autoplay = true; - Plotly.relayout(gd, { autosize: true }).then(function() { + gd._plotAPI.relayout({ autosize: true }).then(function() { gd.changed = oldchanged; resolve(gd); }); @@ -243,15 +201,6 @@ plots.resize = function(gd) { }; -// for use in Lib.syncOrAsync, check if there are any -// pending promises in this plot and wait for them -plots.previousPromises = function(gd) { - if((gd._promises || []).length) { - return Promise.all(gd._promises) - .then(function() { gd._promises = []; }); - } -}; - /** * Adds the 'Edit chart' link. * Note that now Plotly.plot() calls this so it can regenerate whenever it replots @@ -497,7 +446,7 @@ plots.supplyDefaults = function(gd) { plots.doAutoMargin(gd); // set scale after auto margin routine - var axList = Plotly.Axes.list(gd); + var axList = Axes.list(gd); for(i = 0; i < axList.length; i++) { var ax = axList[i]; ax.setScale(); @@ -639,15 +588,15 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa _fullLayout: newFullLayout }; - var ids = Plotly.Axes.getSubplots(mockGd); + var ids = Axes.getSubplots(mockGd); var i; for(i = 0; i < ids.length; i++) { var id = ids[i]; var oldSubplot = oldSubplots[id]; - var xaxis = Plotly.Axes.getFromId(mockGd, id, 'x'); - var yaxis = Plotly.Axes.getFromId(mockGd, id, 'y'); + var xaxis = Axes.getFromId(mockGd, id, 'x'); + var yaxis = Axes.getFromId(mockGd, id, 'y'); var plotinfo; if(oldSubplot) { @@ -696,13 +645,13 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa // while we're at it, link overlaying axes to their main axes and // anchored axes to the axes they're anchored to - var axList = Plotly.Axes.list(mockGd, null, true); + var axList = Axes.list(mockGd, null, true); for(i = 0; i < axList.length; i++) { var ax = axList[i]; var mainAx = null; if(ax.overlaying) { - mainAx = Plotly.Axes.getFromId(mockGd, ax.overlaying); + mainAx = Axes.getFromId(mockGd, ax.overlaying); // you cannot overlay an axis that's already overlaying another if(mainAx && mainAx.overlaying) { @@ -724,7 +673,7 @@ plots.linkSubplots = function(newFullData, newFullLayout, oldFullData, oldFullLa ax._anchorAxis = ax.anchor === 'free' ? null : - Plotly.Axes.getFromId(mockGd, ax.anchor); + Axes.getFromId(mockGd, ax.anchor); } }; @@ -953,10 +902,10 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde } function coerceSubplotAttr(subplotType, subplotAttr) { - if(!plots.traceIs(traceOut, subplotType)) return; + if(!Registry.traceIs(traceOut, subplotType)) return; return Lib.coerce(traceIn, traceOut, - plots.subplotsRegistry[subplotType].attributes, subplotAttr); + Registry.subplotsRegistry[subplotType].attributes, subplotAttr); } var visible = coerce('visible'); @@ -986,7 +935,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde var _module = plots.getModule(traceOut); traceOut._module = _module; - if(plots.traceIs(traceOut, 'showLegend')) { + if(Registry.traceIs(traceOut, 'showLegend')) { coerce('showlegend'); coerce('legendgroup'); } @@ -1003,7 +952,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde Lib.coerceHoverinfo(traceIn, traceOut, layout); } - if(!plots.traceIs(traceOut, 'noOpacity')) coerce('opacity'); + if(!Registry.traceIs(traceOut, 'noOpacity')) coerce('opacity'); coerceSubplotAttr('cartesian', 'xaxis'); coerceSubplotAttr('cartesian', 'yaxis'); @@ -1011,7 +960,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOutIndex, layout, traceInInde coerceSubplotAttr('gl2d', 'xaxis'); coerceSubplotAttr('gl2d', 'yaxis'); - if(plots.traceIs(traceOut, 'notLegendIsolatable')) { + if(Registry.traceIs(traceOut, 'notLegendIsolatable')) { // This clears out the legendonly state for traces like carpet that // cannot be isolated in the legend traceOut.visible = !!traceOut.visible; @@ -1231,7 +1180,7 @@ plots.supplyLayoutModuleDefaults = function(layoutIn, layoutOut, fullData, trans // can't be be part of basePlotModules loop // in order to handle the orphan axes case - Plotly.Axes.supplyLayoutDefaults(layoutIn, layoutOut, fullData); + Axes.supplyLayoutDefaults(layoutIn, layoutOut, fullData); // base plot module layout defaults var basePlotModules = layoutOut._basePlotModules; @@ -1498,7 +1447,7 @@ plots.doAutoMargin = function(gd) { // if things changed and we're not already redrawing, trigger a redraw if(!fullLayout._replotting && oldmargins !== '{}' && oldmargins !== JSON.stringify(fullLayout._size)) { - return Plotly.plot(gd); + return gd._plotAPI.plot(); } }; @@ -1840,9 +1789,6 @@ plots.extendObjectWithContainers = function(dest, src, containerPaths) { return dest; }; -plots.dataArrayContainers = ['transforms']; -plots.layoutArrayContainers = Registry.layoutArrayContainers; - /* * Extend a trace definition. This method: * @@ -1852,7 +1798,7 @@ plots.layoutArrayContainers = Registry.layoutArrayContainers; * The result is the original object reference with the new contents merged in. */ plots.extendTrace = function(destTrace, srcTrace) { - return plots.extendObjectWithContainers(destTrace, srcTrace, plots.dataArrayContainers); + return plots.extendObjectWithContainers(destTrace, srcTrace, Registry.dataArrayContainers); }; /* @@ -1865,7 +1811,7 @@ plots.extendTrace = function(destTrace, srcTrace) { * The result is the original object reference with the new contents merged in. */ plots.extendLayout = function(destLayout, srcLayout) { - return plots.extendObjectWithContainers(destLayout, srcLayout, plots.layoutArrayContainers); + return plots.extendObjectWithContainers(destLayout, srcLayout, Registry.layoutArrayContainers); }; /** @@ -1990,7 +1936,7 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) if(frameOpts.redraw) { gd._transitionData._interruptCallbacks.push(function() { - return Plotly.redraw(gd); + return gd._plotAPI.redraw(); }); } @@ -2062,7 +2008,7 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) return Promise.resolve().then(function() { if(frameOpts.redraw) { - return Plotly.redraw(gd); + return gd._plotAPI.redraw(); } }).then(function() { // Set transitioning false again once the redraw has occurred. This is used, for example, @@ -2117,7 +2063,7 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) }; plots.doCalcdata = function(gd, traces) { - var axList = Plotly.Axes.list(gd), + var axList = Axes.list(gd), fullData = gd._fullData, fullLayout = gd._fullLayout; diff --git a/src/plots/previous_promises.js b/src/plots/previous_promises.js new file mode 100644 index 00000000000..7ad887bcc0a --- /dev/null +++ b/src/plots/previous_promises.js @@ -0,0 +1,19 @@ +/** +* Copyright 2012-2017, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + + +'use strict'; + +// for use in Lib.syncOrAsync, check if there are any +// pending promises in this plot and wait for them +module.exports = function(gd) { + if((gd._promises || []).length) { + return Promise.all(gd._promises) + .then(function() { gd._promises = []; }); + } +}; diff --git a/src/registry.js b/src/registry.js index 70b6a2015c6..0a5276d2947 100644 --- a/src/registry.js +++ b/src/registry.js @@ -25,6 +25,7 @@ exports.allTypes = []; exports.subplotsRegistry = {}; exports.transformsRegistry = {}; exports.componentsRegistry = {}; +exports.dataArrayContainers = ['transforms']; exports.layoutArrayContainers = []; exports.layoutArrayRegexes = []; exports.traceLayoutAttributes = {}; diff --git a/tasks/test_syntax.js b/tasks/test_syntax.js index c00daed44c0..16baf4d5080 100644 --- a/tasks/test_syntax.js +++ b/tasks/test_syntax.js @@ -195,7 +195,7 @@ function assertCircularDeps() { var logs = []; // see https://github.com/plotly/plotly.js/milestone/9 - var MAX_ALLOWED_CIRCULAR_DEPS = 17; + var MAX_ALLOWED_CIRCULAR_DEPS = 0; if(circularDeps.length > MAX_ALLOWED_CIRCULAR_DEPS) { console.log(circularDeps.join('\n')); diff --git a/test/jasmine/tests/command_test.js b/test/jasmine/tests/command_test.js index e3bef641bcf..1660c027f61 100644 --- a/test/jasmine/tests/command_test.js +++ b/test/jasmine/tests/command_test.js @@ -1,11 +1,13 @@ var Plotly = require('@lib/index'); var PlotlyInternal = require('@src/plotly'); +var bindPlotAPI = require('@src/plot_api/bind_plot_api'); var Plots = Plotly.Plots; var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var fail = require('../assets/fail_test'); var Lib = require('@src/lib'); + describe('Plots.executeAPICommand', function() { 'use strict'; @@ -24,6 +26,7 @@ describe('Plots.executeAPICommand', function() { spyOn(PlotlyInternal, 'restyle').and.callFake(function() { return Promise.resolve('resolution'); }); + bindPlotAPI(gd, PlotlyInternal); }); it('calls the API method and resolves', function(done) { @@ -44,6 +47,7 @@ describe('Plots.executeAPICommand', function() { spyOn(PlotlyInternal, 'restyle').and.callFake(function() { return Promise.reject('rejection'); }); + bindPlotAPI(gd, PlotlyInternal); }); it('calls the API method and rejects', function(done) { diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index 4e97759e6b8..f4e63661cf8 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -2008,15 +2008,15 @@ describe('Queue', function() { }) .then(function() { expect(gd.undoQueue.index).toEqual(1); - expect(gd.undoQueue.queue[0].undo.args[0][1]['marker.color']).toEqual([null]); - expect(gd.undoQueue.queue[0].redo.args[0][1]['marker.color']).toEqual('red'); + expect(gd.undoQueue.queue[0].undo.args[0][0]['marker.color']).toEqual([null]); + expect(gd.undoQueue.queue[0].redo.args[0][0]['marker.color']).toEqual('red'); return Plotly.relayout(gd, 'title', 'A title'); }) .then(function() { expect(gd.undoQueue.index).toEqual(2); - expect(gd.undoQueue.queue[1].undo.args[0][1].title).toEqual(null); - expect(gd.undoQueue.queue[1].redo.args[0][1].title).toEqual('A title'); + expect(gd.undoQueue.queue[1].undo.args[0][0].title).toEqual(null); + expect(gd.undoQueue.queue[1].redo.args[0][0].title).toEqual('A title'); return Plotly.restyle(gd, 'mode', 'markers'); }) @@ -2024,43 +2024,43 @@ describe('Queue', function() { expect(gd.undoQueue.index).toEqual(2); expect(gd.undoQueue.queue[2]).toBeUndefined(); - expect(gd.undoQueue.queue[1].undo.args[0][1].mode).toEqual([null]); - expect(gd.undoQueue.queue[1].redo.args[0][1].mode).toEqual('markers'); + expect(gd.undoQueue.queue[1].undo.args[0][0].mode).toEqual([null]); + expect(gd.undoQueue.queue[1].redo.args[0][0].mode).toEqual('markers'); - expect(gd.undoQueue.queue[0].undo.args[0][1].title).toEqual(null); - expect(gd.undoQueue.queue[0].redo.args[0][1].title).toEqual('A title'); + expect(gd.undoQueue.queue[0].undo.args[0][0].title).toEqual(null); + expect(gd.undoQueue.queue[0].redo.args[0][0].title).toEqual('A title'); return Plotly.restyle(gd, 'transforms[0]', { type: 'filter' }); }) .then(function() { - expect(gd.undoQueue.queue[1].undo.args[0][1]) + expect(gd.undoQueue.queue[1].undo.args[0][0]) .toEqual({ 'transforms[0]': null }); - expect(gd.undoQueue.queue[1].redo.args[0][1]) + expect(gd.undoQueue.queue[1].redo.args[0][0]) .toEqual({ 'transforms[0]': { type: 'filter' } }); return Plotly.relayout(gd, 'updatemenus[0]', { buttons: [] }); }) .then(function() { - expect(gd.undoQueue.queue[1].undo.args[0][1]) + expect(gd.undoQueue.queue[1].undo.args[0][0]) .toEqual({ 'updatemenus[0]': null }); - expect(gd.undoQueue.queue[1].redo.args[0][1]) + expect(gd.undoQueue.queue[1].redo.args[0][0]) .toEqual({ 'updatemenus[0]': { buttons: [] } }); return Plotly.relayout(gd, 'updatemenus[0]', null); }) .then(function() { // buttons have been stripped out because it's an empty container array... - expect(gd.undoQueue.queue[1].undo.args[0][1]) + expect(gd.undoQueue.queue[1].undo.args[0][0]) .toEqual({ 'updatemenus[0]': {} }); - expect(gd.undoQueue.queue[1].redo.args[0][1]) + expect(gd.undoQueue.queue[1].redo.args[0][0]) .toEqual({ 'updatemenus[0]': null }); return Plotly.restyle(gd, 'transforms[0]', null); }) .then(function() { - expect(gd.undoQueue.queue[1].undo.args[0][1]) + expect(gd.undoQueue.queue[1].undo.args[0][0]) .toEqual({ 'transforms[0]': [ { type: 'filter' } ]}); - expect(gd.undoQueue.queue[1].redo.args[0][1]) + expect(gd.undoQueue.queue[1].redo.args[0][0]) .toEqual({ 'transforms[0]': null }); }) .catch(failTest) diff --git a/test/jasmine/tests/plot_api_test.js b/test/jasmine/tests/plot_api_test.js index 600321d0064..d941be19366 100644 --- a/test/jasmine/tests/plot_api_test.js +++ b/test/jasmine/tests/plot_api_test.js @@ -10,6 +10,7 @@ var pkg = require('../../../package.json'); var subroutines = require('@src/plot_api/subroutines'); var helpers = require('@src/plot_api/helpers'); var editTypes = require('@src/plot_api/edit_types'); +var bindPlotAPI = require('@src/plot_api/bind_plot_api'); var d3 = require('d3'); var createGraphDiv = require('../assets/create_graph_div'); @@ -17,6 +18,11 @@ var destroyGraphDiv = require('../assets/destroy_graph_div'); var fail = require('../assets/fail_test'); var checkTicks = require('../assets/custom_assertions').checkTicks; +function withoutAPI(gd) { + gd = Lib.extendFlat({}, gd); + delete gd._plotAPI; + return gd; +} describe('Test plot api', function() { 'use strict'; @@ -580,6 +586,7 @@ describe('Test plot api', function() { gd.calcdata = gd._fullData.map(function(trace) { return [{x: 1, y: 1, trace: trace}]; }); + bindPlotAPI(gd, PlotlyInternal); } it('calls Scatter.arraysToCalcdata and Plots.style on scatter styling', function() { @@ -1226,6 +1233,7 @@ describe('Test plot api', function() { ] }; spyOn(PlotlyInternal, 'redraw'); + bindPlotAPI(gd, PlotlyInternal); }); it('should throw an error when indices are omitted', function() { @@ -1324,6 +1332,7 @@ describe('Test plot api', function() { gd = { data: [{'name': 'a'}, {'name': 'b'}] }; spyOn(PlotlyInternal, 'redraw'); spyOn(PlotlyInternal, 'moveTraces'); + bindPlotAPI(gd, PlotlyInternal); }); it('should throw an error when traces is not an object or an array of objects', function() { @@ -1341,7 +1350,7 @@ describe('Test plot api', function() { }).toThrowError(Error, 'all values in traces array must be non-array objects'); // make sure we didn't muck with gd.data if things failed! - expect(gd).toEqual(expected); + expect(withoutAPI(gd)).toEqual(withoutAPI(expected)); }); it('should throw an error when traces and newIndices arrays are unequal', function() { @@ -1360,7 +1369,7 @@ describe('Test plot api', function() { }).toThrow(new Error('newIndices must be valid indices for gd.data.')); // make sure we didn't muck with gd.data if things failed! - expect(gd).toEqual(expected); + expect(withoutAPI(gd)).toEqual(withoutAPI(expected)); }); it('should work when newIndices is undefined', function() { @@ -1429,6 +1438,7 @@ describe('Test plot api', function() { ] }; spyOn(PlotlyInternal, 'redraw'); + bindPlotAPI(gd, PlotlyInternal); }); it('throw an error when index arrays are unequal', function() { @@ -1565,6 +1575,7 @@ describe('Test plot api', function() { spyOn(PlotlyInternal, 'redraw'); spyOn(Plotly.Queue, 'add'); + bindPlotAPI(gd, PlotlyInternal); }); it('should throw an error when gd.data isn\'t an array.', function() { @@ -1734,7 +1745,7 @@ describe('Test plot api', function() { var undoArgs = Plotly.Queue.add.calls.first().args[2]; - Plotly.prependTraces.apply(null, undoArgs); + gd._plotAPI.prependTraces.apply(null, undoArgs); expect(gd.data).toEqual(cachedData); }); @@ -1752,7 +1763,7 @@ describe('Test plot api', function() { var undoArgs = Plotly.Queue.add.calls.first().args[2]; - Plotly.extendTraces.apply(null, undoArgs); + gd._plotAPI.extendTraces.apply(null, undoArgs); expect(gd.data).toEqual(cachedData); }); @@ -1771,7 +1782,7 @@ describe('Test plot api', function() { var undoArgs = Plotly.Queue.add.calls.first().args[2]; - Plotly.prependTraces.apply(null, undoArgs); + gd._plotAPI.prependTraces.apply(null, undoArgs); expect(gd.data).toEqual(cachedData); }); diff --git a/test/jasmine/tests/plots_test.js b/test/jasmine/tests/plots_test.js index b95b6482db0..c9aeb5fa520 100644 --- a/test/jasmine/tests/plots_test.js +++ b/test/jasmine/tests/plots_test.js @@ -415,7 +415,7 @@ describe('Test Plots', function() { it('should unset everything in the gd except _context', function() { var expectedKeys = [ '_ev', '_internalEv', 'on', 'once', 'removeListener', 'removeAllListeners', - '_internalOn', '_internalOnce', '_removeInternalListener', + '_internalOn', '_internalOnce', '_removeInternalListener', '_plotAPI', '_removeAllInternalListeners', 'emit', '_context', '_replotPending', '_hmpixcount', '_hmlumcount', '_mouseDownTime', '_legendMouseDownTime', ];