diff --git a/src/lib/loggers.js b/src/lib/loggers.js index 048298964f6..db03e4a59be 100644 --- a/src/lib/loggers.js +++ b/src/lib/loggers.js @@ -11,6 +11,7 @@ /* eslint-disable no-console */ var config = require('../plot_api/plot_config'); +var loggingDflt = config.dflt; var loggers = module.exports = {}; @@ -21,7 +22,7 @@ var loggers = module.exports = {}; */ loggers.log = function() { - if(config.logging > 1) { + if(loggingDflt > 1) { var messages = ['LOG:']; for(var i = 0; i < arguments.length; i++) { @@ -37,7 +38,7 @@ loggers.log = function() { }; loggers.warn = function() { - if(config.logging > 0) { + if(loggingDflt > 0) { var messages = ['WARN:']; for(var i = 0; i < arguments.length; i++) { @@ -53,7 +54,7 @@ loggers.warn = function() { }; loggers.error = function() { - if(config.logging > 0) { + if(loggingDflt > 0) { var messages = ['ERROR:']; for(var i = 0; i < arguments.length; i++) { diff --git a/src/lib/queue.js b/src/lib/queue.js index 2425b67564e..9cc59ebd3a3 100644 --- a/src/lib/queue.js +++ b/src/lib/queue.js @@ -10,7 +10,6 @@ 'use strict'; var Lib = require('../lib'); -var config = require('../plot_api/plot_config'); /** @@ -91,7 +90,7 @@ queue.add = function(gd, undoFunc, undoArgs, redoFunc, redoArgs) { queueObj.redo.args.push(redoArgs); } - if(gd.undoQueue.queue.length > config.queueLength) { + if(gd.undoQueue.queue.length > gd._context.queueLength) { gd.undoQueue.queue.shift(); gd.undoQueue.index--; } diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 1a5bd8d1fc0..ee1611bd0dc 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -28,6 +28,7 @@ var ErrorBars = require('../components/errorbars'); var xmlnsNamespaces = require('../constants/xmlns_namespaces'); var svgTextUtils = require('../lib/svg_text_utils'); +var defaultConfig = require('./plot_config'); var helpers = require('./helpers'); var subroutines = require('./subroutines'); @@ -80,6 +81,7 @@ Plotly.plot = function(gd, data, layout, config) { // transfer configuration options to gd until we move over to // a more OO like model + setPlotContext(gd, config); if(!layout) layout = {}; @@ -363,32 +365,49 @@ Plotly.plot = function(gd, data, layout, config) { }); }; - function opaqueSetBackground(gd, bgColor) { gd._fullLayout._paperdiv.style('background', 'white'); - Plotly.defaultConfig.setBackground(gd, bgColor); + defaultConfig.setBackground.dflt(gd, bgColor); } function setPlotContext(gd, config) { - if(!gd._context) gd._context = Lib.extendFlat({}, Plotly.defaultConfig); - var context = gd._context; + var keys, i; - if(config) { - Object.keys(config).forEach(function(key) { - if(key in context) { - if(key === 'setBackground' && config[key] === 'opaque') { - context[key] = opaqueSetBackground; - } - else context[key] = config[key]; - } - }); + if(!gd._context) { + gd._context = {}; + keys = Object.keys(defaultConfig); - // map plot3dPixelRatio to plotGlPixelRatio for backward compatibility - if(config.plot3dPixelRatio && !context.plotGlPixelRatio) { - context.plotGlPixelRatio = context.plot3dPixelRatio; + // init context + for(i = 0; i < keys.length; i++) { + var k = keys[i]; + gd._context[k] = defaultConfig[k].dflt; } } + var context = gd._context; + + config = Lib.isPlainObject(config) ? config : {}; + keys = Object.keys(config); + + function coerce(key, dflt) { + return Lib.coerce(config, context, defaultConfig, key, dflt); + } + + // extend context with config + for(i = 0; i < keys.length; i++) { + coerce(keys[i]); + } + + // 'opaque' is special value for setBackground + if(config.setBackground === 'opaque') { + context.setBackground = opaqueSetBackground; + } + + // map plot3dPixelRatio to plotGlPixelRatio for backward compatibility + if(config.plot3dPixelRatio && !context.plotGlPixelRatio) { + context.plotGlPixelRatio = context.plot3dPixelRatio; + } + // staticPlot forces a bunch of others: if(context.staticPlot) { context.editable = false; diff --git a/src/plot_api/plot_config.js b/src/plot_api/plot_config.js index 07fa832976e..d9108b60b76 100644 --- a/src/plot_api/plot_config.js +++ b/src/plot_api/plot_config.js @@ -20,88 +20,248 @@ module.exports = { - // no interactivity, for export or image generation - staticPlot: false, + staticPlot: { + valType: 'boolean', + dflt: false, + description: [ + 'Determines whether the graphs are interactive or not.', + 'If *false*, no interactivity, for export or image generation.' + ].join(' ') + }, - // we can edit titles, move annotations, etc - editable: false, + editable: { + valType: 'boolean', + dflt: false, + description: [ + 'Determines whether the graphs are editable', + 'e.g. where titles and annotations can be moved around', + 'and edited.' + ].join(' ') + }, - // DO autosize once regardless of layout.autosize - // (use default width or height values otherwise) - autosizable: false, + autosizable: { + valType: 'boolean', + dflt: false, + description: [ + 'Determines whether the graphs are plotted with respect to', + 'layout.autosize=true and infer its container size.' + ].join(' ') + }, - // set the length of the undo/redo queue - queueLength: 0, + queueLength: { + valType: 'number', + dflt: 0, + min: 0, + description: [ + 'Sets the length of the undo/redo queue.' + ].join(' ') + }, - // if we DO autosize, do we fill the container or the screen? - fillFrame: false, + fillFrame: { + valType: 'boolean', + dflt: false, + description: [ + 'When `layout.autosize` is turned on, determines whether the graph', + 'fills the container (the default) or the screen (if set to *true*).' + ].join(' ') + }, - // if we DO autosize, set the frame margins in percents of plot size - frameMargins: 0, + frameMargins: { + valType: 'number', + dflt: 0, + min: 0, + max: 0.5, + description: [ + 'When `layout.autosize` is turned on, set the frame margins', + 'in fraction of the graph size.' + ].join(' ') + }, - // mousewheel or two-finger scroll zooms the plot - scrollZoom: false, + scrollZoom: { + valType: 'boolean', + dftl: false, + description: [ + 'Determines whether mouse wheel or two-finger scroll zooms is', + 'enable. Has an effect only in cartesian plots.' + ].join(' ') + }, - // double click interaction (false, 'reset', 'autosize' or 'reset+autosize') - doubleClick: 'reset+autosize', + doubleClick: { + valType: 'enumerated', + values: [false, 'reset', 'autosize', 'reset+autosize'], + dflt: 'reset+autosize', + description: [ + 'Sets the double click interaction mode.', + 'Has an effect only in cartesian plots.', + 'If *false*, double click is disable.', + 'If *reset*, double click resets the axis ranges to their initial values.', + 'If *autosize*, double click set the axis ranges to their autorange values.', + 'If *reset+autosize, the odd double clicks resets the axis ranges', + 'to their initial values and even double clicks set the axis ranges', + 'to their autorange values.' + ].join(' ') + }, - // new users see some hints about interactivity - showTips: true, + showTips: { + valType: 'boolean', + dflt: true, + description: [ + 'Determines whether or not tips are shown while interacting', + 'with the resulting graphs.' + ].join(' ') + }, - // link to open this plot in plotly - showLink: false, + showLink: { + valType: 'boolean', + dflt: false, + description: [ + 'Determines whether a link to plot.ly is displayed', + 'at the bottom right corner of resulting graphs.', + 'Use with `sendData` and `linkText`.' + ].join(' ') + }, - // if we show a link, does it contain data or just link to a plotly file? - sendData: true, + sendData: { + valType: 'boolean', + dflt: true, + description: [ + 'If *showLink* is true, does it contain data', + 'just link to a plot.ly file?' + ].join(' ') + }, - // text appearing in the sendData link - linkText: 'Edit chart', + linkText: { + valType: 'string', + dflt: 'Edit chart', + noBlank: true, + description: [ + 'Sets the text appearing in the `showLink` link.' + ].join(' ') + }, - // false or function adding source(s) to linkText - showSources: false, + showSources: { + valType: 'any', + dflt: false, + description: [ + 'Adds a source-displaying function to show sources on', + 'the resulting graphs.' + ].join(' ') + }, - // display the mode bar (true, false, or 'hover') - displayModeBar: 'hover', + displayModeBar: { + valType: 'enumerated', + values: ['hover', true, false], + dflt: 'hover', + description: [ + 'Determines the mode bar display mode.', + 'If *true*, the mode bar is always visible.', + 'If *false*, the mode bar is always hidden.', + 'If *hover*, the mode bar is visible while the mouse cursor', + 'is on the graph container.' + ].join(' ') + }, - // remove mode bar button by name - // (see ./components/modebar/buttons.js for the list of names) - modeBarButtonsToRemove: [], + modeBarButtonsToRemove: { + valType: 'any', + dflt: [], + description: [ + 'Remove mode bar buttons by name.', + 'See ./components/modebar/buttons.js for the list of names.' + ].join(' ') + }, - // add mode bar button using config objects - // (see ./components/modebar/buttons.js for list of arguments) - modeBarButtonsToAdd: [], + modeBarButtonsToAdd: { + valType: 'any', + dflt: [], + description: [ + 'Add mode bar button using config objects', + 'See ./components/modebar/buttons.js for list of arguments.' + ].join(' ') + }, - // fully custom mode bar buttons as nested array, - // where the outer arrays represents button groups, and - // the inner arrays have buttons config objects or names of default buttons - // (see ./components/modebar/buttons.js for more info) - modeBarButtons: false, + modeBarButtons: { + valType: 'any', + dflt: false, + description: [ + 'Define fully custom mode bar buttons as nested array,', + 'where the outer arrays represents button groups, and', + 'the inner arrays have buttons config objects or names of default buttons', + 'See ./components/modebar/buttons.js for more info.' + ].join(' ') + }, - // add the plotly logo on the end of the mode bar - displaylogo: true, + displaylogo: { + valType: 'boolean', + dflt: true, + description: [ + 'Determines whether or not the plotly logo is displayed', + 'on the end of the mode bar.' + ].join(' ') + }, - // increase the pixel ratio for Gl plot images - plotGlPixelRatio: 2, + plotGlPixelRatio: { + valType: 'number', + dflt: 2, + min: 0, + max: 4, + description: [ + 'Set the pixel ratio during WebGL image export.', + 'This config option was formally named `plot3dPixelRatio`', + 'which is now deprecated.' + ].join(' ') + }, - // function to add the background color to a different container - // or 'opaque' to ensure there's white behind it - setBackground: defaultSetBackground, + // valType 'any' but really it is a 'function' + setBackground: { + valType: 'any', + dflt: defaultSetBackground, + description: [ + 'Set function to add the background color to a different container', + 'or *opaque* to ensure there is white behind it.' + ].join(' ') + }, - // URL to topojson files used in geo charts - topojsonURL: 'https://cdn.plot.ly/', + topojsonURL: { + valType: 'string', + noBlank: true, + dflt: 'https://cdn.plot.ly/', + description: [ + 'Set the URL to topojson used in geo charts.', + 'By default, the topojson files are fetched from cdn.plot.ly.', + 'For example, set this option to:', + '/dist/topojson/', + 'to render geographical feature using the topojson files', + 'that ship with the plotly.js module.' + ].join(' ') + }, - // Mapbox access token (required to plot mapbox trace types) - // If using an Mapbox Atlas server, set this option to '', - // so that plotly.js won't attempt to authenticate to the public Mapbox server. - mapboxAccessToken: null, + mapboxAccessToken: { + valType: 'string', + dflt: null, + description: [ + 'Mapbox access token (required to plot mapbox trace types)', + 'If using an Mapbox Atlas server, set this option to \'\'', + 'so that plotly.js won\'t attempt to authenticate to the public Mapbox server.' + ].join(' ') + }, - // Turn all console logging on or off (errors will be thrown) - // This should ONLY be set via Plotly.setPlotConfig - logging: false, + logging: { + valType: 'boolean', + dflt: false, + description: [ + 'Turn all console logging on or off (errors will be thrown)', + 'This should ONLY be set via Plotly.setPlotConfig' + ].join(' ') + }, - // Set global transform to be applied to all traces with no - // specification needed - globalTransforms: [] + globalTransforms: { + valType: 'any', + dflt: [], + description: [ + 'Set global transform to be applied to all traces with no', + 'specification needed' + ].join(' ') + } }; // where and how the background gets set can be overridden by context diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index c5a55ef34ee..6804227e48b 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -16,6 +16,7 @@ var baseAttributes = require('../plots/attributes'); var baseLayoutAttributes = require('../plots/layout_attributes'); var frameAttributes = require('../plots/frame_attributes'); var animationAttributes = require('../plots/animation_attributes'); +var plotConfig = require('./plot_config'); // polar attributes are not part of the Registry yet var polarAreaAttrs = require('../plots/polar/area_attributes'); @@ -43,7 +44,7 @@ exports.UNDERSCORE_ATTRS = UNDERSCORE_ATTRS; * - transforms * - frames * - animations - * - config (coming soon ...) + * - config */ exports.get = function() { var traces = {}; @@ -70,7 +71,9 @@ exports.get = function() { transforms: transforms, frames: formatAttributes(frameAttributes), - animation: formatAttributes(animationAttributes) + animation: formatAttributes(animationAttributes), + + config: formatAttributes(plotConfig) }; }; diff --git a/src/plot_api/set_plot_config.js b/src/plot_api/set_plot_config.js index 02c98addce7..98dc705a56e 100644 --- a/src/plot_api/set_plot_config.js +++ b/src/plot_api/set_plot_config.js @@ -9,8 +9,7 @@ 'use strict'; -var Plotly = require('../plotly'); -var Lib = require('../lib'); +var defaultConfig = require('./plot_config'); /** * Extends the plot config @@ -20,5 +19,13 @@ var Lib = require('../lib'); * */ module.exports = function setPlotConfig(configObj) { - return Lib.extendFlat(Plotly.defaultConfig, configObj); + var keys = Object.keys(configObj); + + for(var i = 0; i < keys.length; i++) { + var key = keys[i]; + + defaultConfig[key].dflt = configObj[key]; + } + + return defaultConfig; }; diff --git a/src/plotly.js b/src/plotly.js index 2ac2da1bec8..45070546fec 100644 --- a/src/plotly.js +++ b/src/plotly.js @@ -18,9 +18,6 @@ * */ -// configuration -exports.defaultConfig = require('./plot_api/plot_config'); - // plots exports.Plots = require('./plots/plots'); exports.Axes = require('./plots/cartesian/axes'); diff --git a/src/plots/plots.js b/src/plots/plots.js index b8003c66c81..735d5448319 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -963,7 +963,7 @@ plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) { // just hide it document.body.style.overflow = 'hidden'; } - else if(isNumeric(frameMargins) && frameMargins > 0) { + else if(frameMargins) { var reservedMargins = calculateReservedMargins(gd._boundingBoxMargins), reservedWidth = reservedMargins.left + reservedMargins.right, reservedHeight = reservedMargins.bottom + reservedMargins.top, diff --git a/test/jasmine/tests/frame_api_test.js b/test/jasmine/tests/frame_api_test.js index 9e006fa018d..1bab9800998 100644 --- a/test/jasmine/tests/frame_api_test.js +++ b/test/jasmine/tests/frame_api_test.js @@ -12,11 +12,11 @@ describe('Test frame api', function() { beforeEach(function(done) { mock = [{x: [1, 2, 3], y: [2, 1, 3]}, {x: [1, 2, 3], y: [6, 4, 5]}]; gd = createGraphDiv(); + Plotly.setPlotConfig({ queueLength: 10 }); + Plotly.plot(gd, mock).then(function() { f = gd._transitionData._frames; h = gd._transitionData._frameHash; - }).then(function() { - Plotly.setPlotConfig({ queueLength: 10 }); }).then(done); }); diff --git a/test/jasmine/tests/plot_api_test.js b/test/jasmine/tests/plot_api_test.js index 80acfbb1b7e..fab61acd8e8 100644 --- a/test/jasmine/tests/plot_api_test.js +++ b/test/jasmine/tests/plot_api_test.js @@ -176,6 +176,7 @@ describe('Test plot api', function() { }); function mockDefaultsAndCalc(gd) { + gd._context = {}; Plots.supplyDefaults(gd); gd.calcdata = gd._fullData.map(function(trace) { return [{x: 1, y: 1, trace: trace}]; @@ -295,7 +296,9 @@ describe('Test plot api', function() { {'name': 'b'}, {'name': 'c'}, {'name': 'd'} - ] + ], + + _context: {} }; spyOn(PlotlyInternal, 'redraw'); }); @@ -393,7 +396,10 @@ describe('Test plot api', function() { var gd; beforeEach(function() { - gd = { data: [{'name': 'a'}, {'name': 'b'}] }; + gd = { + data: [{'name': 'a'}, {'name': 'b'}], + _context: {} + }; spyOn(PlotlyInternal, 'redraw'); spyOn(PlotlyInternal, 'moveTraces'); }); @@ -498,7 +504,9 @@ describe('Test plot api', function() { {'name': 'b'}, {'name': 'c'}, {'name': 'd'} - ] + ], + + _context: {} }; spyOn(PlotlyInternal, 'redraw'); });