Skip to content

Commit e020bb1

Browse files
authored
Merge pull request #499 from plotly/transform-plugins
Introducing transform plugins
2 parents a3aebfe + 3e6e9c1 commit e020bb1

17 files changed

+1672
-96
lines changed

devtools/test_dashboard/devtools.js

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
var Fuse = require('fuse.js');
66
var mocks = require('../../build/test_dashboard_mocks.json');
77
var credentials = require('../../build/credentials.json');
8+
var Lib = require('@src/lib');
89

910
// put d3 in window scope
1011
var d3 = window.d3 = Plotly.d3;
@@ -149,6 +150,7 @@ var Tabs = {
149150

150151
// Bind things to the window
151152
window.Tabs = Tabs;
153+
window.Lib = Lib;
152154
setInterval(function() {
153155
window.gd = Tabs.getGraph() || Tabs.fresh();
154156
window.fullLayout = window.gd._fullLayout;

devtools/test_dashboard/server.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var watchify = require('watchify');
88

99
var constants = require('../../tasks/util/constants');
1010
var compress = require('../../tasks/util/compress_attributes');
11+
var shortcutPaths = require('../../tasks/util/shortcut_paths');
1112

1213
var PORT = process.argv[2] || 3000;
1314

@@ -32,7 +33,9 @@ b.on('update', bundlePlotly);
3233

3334
// Bundle devtools code
3435
var devtoolsPath = path.join(constants.pathToRoot, 'devtools/test_dashboard');
35-
var devtools = browserify(path.join(devtoolsPath, 'devtools.js'), {});
36+
var devtools = browserify(path.join(devtoolsPath, 'devtools.js'), {
37+
transform: [shortcutPaths]
38+
});
3639

3740
var firstBundle = true;
3841

src/plot_api/plot_api.js

+11-9
Original file line numberDiff line numberDiff line change
@@ -850,9 +850,8 @@ Plotly.newPlot = function(gd, data, layout, config) {
850850
function doCalcdata(gd) {
851851
var axList = Plotly.Axes.list(gd),
852852
fullData = gd._fullData,
853-
fullLayout = gd._fullLayout;
854-
855-
var i, trace, module, cd;
853+
fullLayout = gd._fullLayout,
854+
i;
856855

857856
var calcdata = gd.calcdata = new Array(fullData.length);
858857

@@ -878,12 +877,12 @@ function doCalcdata(gd) {
878877
}
879878

880879
for(i = 0; i < fullData.length; i++) {
881-
trace = fullData[i];
882-
module = trace._module;
883-
cd = [];
880+
var trace = fullData[i],
881+
_module = trace._module,
882+
cd = [];
884883

885-
if(module && trace.visible === true) {
886-
if(module.calc) cd = module.calc(gd, trace);
884+
if(_module && trace.visible === true) {
885+
if(_module.calc) cd = _module.calc(gd, trace);
887886
}
888887

889888
// make sure there is a first point
@@ -1555,7 +1554,7 @@ Plotly.restyle = function restyle(gd, astr, val, traces) {
15551554

15561555
if(isNumeric(traces)) traces = [traces];
15571556
else if(!Array.isArray(traces) || !traces.length) {
1558-
traces = gd._fullData.map(function(v, i) { return i; });
1557+
traces = gd.data.map(function(v, i) { return i; });
15591558
}
15601559

15611560
// recalcAttrs attributes need a full regeneration of calcdata
@@ -1730,6 +1729,9 @@ Plotly.restyle = function restyle(gd, astr, val, traces) {
17301729
continue;
17311730
}
17321731

1732+
// take no chances on transforms
1733+
if(ai.substr(0, 10) === 'transforms') docalc = true;
1734+
17331735
// set attribute in gd.data
17341736
undoit[ai] = a0();
17351737
for(i = 0; i < traces.length; i++) {

src/plot_api/plot_schema.js

+17
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var UNDERSCORE_ATTRS = [IS_SUBPLOT_OBJ, IS_LINKED_TO_ARRAY, DEPRECATED];
2929
var plotSchema = {
3030
traces: {},
3131
layout: {},
32+
transforms: {},
3233
defs: {}
3334
};
3435

@@ -45,7 +46,11 @@ PlotSchema.get = function() {
4546
.forEach(getTraceAttributes);
4647

4748
getLayoutAttributes();
49+
50+
Object.keys(Plots.transformsRegistry).forEach(getTransformAttributes);
51+
4852
getDefs();
53+
4954
return plotSchema;
5055
};
5156

@@ -136,6 +141,18 @@ function getLayoutAttributes() {
136141
plotSchema.layout = { layoutAttributes: layoutAttributes };
137142
}
138143

144+
function getTransformAttributes(name) {
145+
var _module = Plots.transformsRegistry[name],
146+
_schema = {};
147+
148+
_schema = coupleAttrs(_schema, _module.attributes || {}, 'attributes', '*');
149+
_schema = removeUnderscoreAttrs(_schema);
150+
mergeValTypeAndRole(_schema);
151+
handleLinkedToArray(_schema);
152+
153+
plotSchema.transforms[name] = { attributes: _schema };
154+
}
155+
139156
function getDefs() {
140157
plotSchema.defs = {
141158
valObjects: Lib.valObjects,

src/plot_api/validate.js

+37-1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,39 @@ module.exports = function valiate(data, layout) {
102102
}
103103

104104
crawl(traceIn, traceOut, traceSchema, errorList, base);
105+
106+
var transformsIn = traceIn.transforms,
107+
transformsOut = traceOut.transforms;
108+
109+
if(transformsIn) {
110+
if(!isArray(transformsIn)) {
111+
errorList.push(format('array', base, ['transforms']));
112+
}
113+
114+
base.push('transforms');
115+
116+
for(var j = 0; j < transformsIn.length; j++) {
117+
var path = ['transforms', j],
118+
transformType = transformsIn[j].type;
119+
120+
if(!isPlainObject(transformsIn[j])) {
121+
errorList.push(format('object', base, path));
122+
continue;
123+
}
124+
125+
var transformSchema = schema.transforms[transformType] ?
126+
schema.transforms[transformType].attributes :
127+
{};
128+
129+
// add 'type' to transform schema to validate the transform type
130+
transformSchema.type = {
131+
valType: 'enumerated',
132+
values: Object.keys(schema.transforms)
133+
};
134+
135+
crawl(transformsIn[j], transformsOut[j], transformSchema, errorList, base, path);
136+
}
137+
}
105138
}
106139

107140
var layoutOut = gd._fullLayout,
@@ -121,6 +154,9 @@ function crawl(objIn, objOut, schema, list, base, path) {
121154
for(var i = 0; i < keys.length; i++) {
122155
var k = keys[i];
123156

157+
// transforms are handled separately
158+
if(k === 'transforms') continue;
159+
124160
var p = path.slice();
125161
p.push(k);
126162

@@ -184,7 +220,7 @@ var code2msgFunc = {
184220
var prefix;
185221

186222
if(base === 'layout' && astr === '') prefix = 'The layout argument';
187-
else if(base[0] === 'data') {
223+
else if(base[0] === 'data' && astr === '') {
188224
prefix = 'Trace ' + base[1] + ' in the data argument';
189225
}
190226
else prefix = inBase(base) + 'key ' + astr;

src/plotly.js

+38-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
require('es6-promise').polyfill();
2323

2424
// lib functions
25-
exports.Lib = require('./lib');
25+
var Lib = exports.Lib = require('./lib');
2626
exports.util = require('./lib/svg_text_utils');
2727
exports.Queue = require('./lib/queue');
2828

@@ -55,21 +55,51 @@ exports.ModeBar = require('./components/modebar');
5555
exports.register = function register(_modules) {
5656
if(!_modules) {
5757
throw new Error('No argument passed to Plotly.register.');
58-
} else if(_modules && !Array.isArray(_modules)) {
58+
}
59+
else if(_modules && !Array.isArray(_modules)) {
5960
_modules = [_modules];
6061
}
6162

6263
for(var i = 0; i < _modules.length; i++) {
6364
var newModule = _modules[i];
6465

65-
if(newModule && newModule.moduleType !== 'trace') {
66+
if(!newModule) {
6667
throw new Error('Invalid module was attempted to be registered!');
67-
} else {
68-
Plots.register(newModule, newModule.name, newModule.categories, newModule.meta);
68+
}
69+
70+
switch(newModule.moduleType) {
71+
case 'trace':
72+
Plots.register(newModule, newModule.name, newModule.categories, newModule.meta);
73+
74+
if(!Plots.subplotsRegistry[newModule.basePlotModule.name]) {
75+
Plots.registerSubplot(newModule.basePlotModule);
76+
}
77+
78+
break;
79+
80+
case 'transform':
81+
if(typeof newModule.name !== 'string') {
82+
throw new Error('Transform module *name* must be a string.');
83+
}
84+
85+
var prefix = 'Transform module ' + newModule.name;
86+
87+
if(typeof newModule.transform !== 'function') {
88+
throw new Error(prefix + ' is missing a *transform* function.');
89+
}
90+
if(!Lib.isPlainObject(newModule.attributes)) {
91+
Lib.log(prefix + ' registered without an *attributes* object.');
92+
}
93+
if(typeof newModule.supplyDefaults !== 'function') {
94+
Lib.log(prefix + ' registered without a *supplyDefaults* function.');
95+
}
96+
97+
Plots.transformsRegistry[newModule.name] = newModule;
98+
99+
break;
69100

70-
if(!Plots.subplotsRegistry[newModule.basePlotModule.name]) {
71-
Plots.registerSubplot(newModule.basePlotModule);
72-
}
101+
default:
102+
throw new Error('Invalid module was attempted to be registered!');
73103
}
74104
}
75105
};

0 commit comments

Comments
 (0)