-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathplot_schema.js
373 lines (292 loc) · 10.6 KB
/
plot_schema.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
/**
* Copyright 2012-2016, 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');
var Lib = require('../lib');
var baseAttributes = require('../plots/attributes');
var baseLayoutAttributes = require('../plots/layout_attributes');
var frameAttributes = require('../plots/frame_attributes');
var animationAttributes = require('../plots/animation_attributes');
// polar attributes are not part of the Registry yet
var polarAreaAttrs = require('../plots/polar/area_attributes');
var polarAxisAttrs = require('../plots/polar/axis_attributes');
var extendFlat = Lib.extendFlat;
var extendDeep = Lib.extendDeep;
var IS_SUBPLOT_OBJ = '_isSubplotObj';
var IS_LINKED_TO_ARRAY = '_isLinkedToArray';
var DEPRECATED = '_deprecated';
var UNDERSCORE_ATTRS = [IS_SUBPLOT_OBJ, IS_LINKED_TO_ARRAY, DEPRECATED];
exports.IS_SUBPLOT_OBJ = IS_SUBPLOT_OBJ;
exports.IS_LINKED_TO_ARRAY = IS_LINKED_TO_ARRAY;
exports.DEPRECATED = DEPRECATED;
exports.UNDERSCORE_ATTRS = UNDERSCORE_ATTRS;
/** Outputs the full plotly.js plot schema
*
* @return {object}
* - defs
* - traces
* - layout
* - transforms
* - frames
* - animations
* - config (coming soon ...)
*/
exports.get = function() {
var traces = {};
Registry.allTypes.concat('area').forEach(function(type) {
traces[type] = getTraceAttributes(type);
});
var transforms = {};
Object.keys(Registry.transformsRegistry).forEach(function(type) {
transforms[type] = getTransformAttributes(type);
});
return {
defs: {
valObjects: Lib.valObjects,
metaKeys: UNDERSCORE_ATTRS.concat(['description', 'role'])
},
traces: traces,
layout: getLayoutAttributes(),
transforms: transforms,
frames: formatAttributes(frameAttributes),
animation: formatAttributes(animationAttributes)
};
};
/**
* Crawl the attribute tree, recursively calling a callback function
*
* @param {object} attrs
* The node of the attribute tree (e.g. the root) from which recursion originates
* @param {Function} callback
* A callback function with the signature:
* @callback callback
* @param {object} attr an attribute
* @param {String} attrName name string
* @param {object[]} attrs all the attributes
* @param {Number} level the recursion level, 0 at the root
* @param {Number} [specifiedLevel]
* The level in the tree, in order to let the callback function detect descend or backtrack,
* typically unsupplied (implied 0), just used by the self-recursive call.
* The necessity arises because the tree traversal is not controlled by callback return values.
* The decision to not use callback return values for controlling tree pruning arose from
* the goal of keeping the crawler backwards compatible. Observe that one of the pruning conditions
* precedes the callback call.
*
* @return {object} transformOut
* copy of transformIn that contains attribute defaults
*/
exports.crawl = function(attrs, callback, specifiedLevel) {
var level = specifiedLevel || 0;
Object.keys(attrs).forEach(function(attrName) {
var attr = attrs[attrName];
if(UNDERSCORE_ATTRS.indexOf(attrName) !== -1) return;
callback(attr, attrName, attrs, level);
if(exports.isValObject(attr)) return;
if(Lib.isPlainObject(attr)) exports.crawl(attr, callback, level + 1);
});
};
/** Is object a value object (or a container object)?
*
* @param {object} obj
* @return {boolean}
* returns true for a valid value object and
* false for tree nodes in the attribute hierarchy
*/
exports.isValObject = function(obj) {
return obj && obj.valType !== undefined;
};
/**
* Find all data array attributes in a given trace object - including
* `arrayOk` attributes.
*
* @param {object} trace
* full trace object that contains a reference to `_module.attributes`
*
* @return {array} arrayAttributes
* list of array attributes for the given trace
*/
exports.findArrayAttributes = function(trace) {
var arrayAttributes = [],
stack = [];
function callback(attr, attrName, attrs, level) {
stack = stack.slice(0, level).concat([attrName]);
var splittableAttr = attr.valType === 'data_array' || attr.arrayOk === true;
if(!splittableAttr) return;
var astr = toAttrString(stack);
var val = Lib.nestedProperty(trace, astr).get();
if(!Array.isArray(val)) return;
arrayAttributes.push(astr);
}
function toAttrString(stack) {
return stack.join('.');
}
exports.crawl(trace._module.attributes, callback);
if(trace.transforms) {
var transforms = trace.transforms;
for(var i = 0; i < transforms.length; i++) {
var transform = transforms[i];
stack = ['transforms[' + i + ']'];
exports.crawl(transform._module.attributes, callback, 1);
}
}
// Look into the fullInput module attributes for array attributes
// to make sure that 'custom' array attributes are detected.
//
// At the moment, we need this block to make sure that
// ohlc and candlestick 'open', 'high', 'low', 'close' can be
// used with filter ang groupby transforms.
if(trace._fullInput) {
exports.crawl(trace._fullInput._module.attributes, callback);
arrayAttributes = Lib.filterUnique(arrayAttributes);
}
return arrayAttributes;
};
function getTraceAttributes(type) {
var _module, basePlotModule;
if(type === 'area') {
_module = { attributes: polarAreaAttrs };
basePlotModule = {};
}
else {
_module = Registry.modules[type]._module,
basePlotModule = _module.basePlotModule;
}
var attributes = {};
// make 'type' the first attribute in the object
attributes.type = null;
// base attributes (same for all trace types)
extendDeep(attributes, baseAttributes);
// module attributes
extendDeep(attributes, _module.attributes);
// subplot attributes
if(basePlotModule.attributes) {
extendDeep(attributes, basePlotModule.attributes);
}
// 'type' gets overwritten by baseAttributes; reset it here
attributes.type = type;
var out = {
meta: _module.meta || {},
attributes: formatAttributes(attributes),
};
// trace-specific layout attributes
if(_module.layoutAttributes) {
var layoutAttributes = {};
extendDeep(layoutAttributes, _module.layoutAttributes);
out.layoutAttributes = formatAttributes(layoutAttributes);
}
return out;
}
function getLayoutAttributes() {
var layoutAttributes = {};
// global layout attributes
extendDeep(layoutAttributes, baseLayoutAttributes);
// add base plot module layout attributes
Object.keys(Registry.subplotsRegistry).forEach(function(k) {
var _module = Registry.subplotsRegistry[k];
if(!_module.layoutAttributes) return;
if(_module.name === 'cartesian') {
handleBasePlotModule(layoutAttributes, _module, 'xaxis');
handleBasePlotModule(layoutAttributes, _module, 'yaxis');
}
else {
var astr = _module.attr === 'subplot' ? _module.name : _module.attr;
handleBasePlotModule(layoutAttributes, _module, astr);
}
});
// polar layout attributes
layoutAttributes = assignPolarLayoutAttrs(layoutAttributes);
// add registered components layout attribute
Object.keys(Registry.componentsRegistry).forEach(function(k) {
var _module = Registry.componentsRegistry[k];
if(!_module.layoutAttributes) return;
if(Array.isArray(_module.layoutNodes)) {
_module.layoutNodes.forEach(function(v) {
handleRegisteredComponent(layoutAttributes, _module, v + _module.name);
});
}
else {
handleRegisteredComponent(layoutAttributes, _module, _module.name);
}
});
return {
layoutAttributes: formatAttributes(layoutAttributes)
};
}
function getTransformAttributes(type) {
var _module = Registry.transformsRegistry[type];
return {
attributes: formatAttributes(_module.attributes)
};
}
function formatAttributes(attrs) {
mergeValTypeAndRole(attrs);
formatArrayContainers(attrs);
return attrs;
}
function mergeValTypeAndRole(attrs) {
function makeSrcAttr(attrName) {
return {
valType: 'string',
role: 'info',
description: [
'Sets the source reference on plot.ly for ',
attrName, '.'
].join(' ')
};
}
function callback(attr, attrName, attrs) {
if(exports.isValObject(attr)) {
if(attr.valType === 'data_array') {
// all 'data_array' attrs have role 'data'
attr.role = 'data';
// all 'data_array' attrs have a corresponding 'src' attr
attrs[attrName + 'src'] = makeSrcAttr(attrName);
}
else if(attr.arrayOk === true) {
// all 'arrayOk' attrs have a corresponding 'src' attr
attrs[attrName + 'src'] = makeSrcAttr(attrName);
}
}
else if(Lib.isPlainObject(attr)) {
// all attrs container objects get role 'object'
attr.role = 'object';
}
}
exports.crawl(attrs, callback);
}
function formatArrayContainers(attrs) {
function callback(attr, attrName, attrs) {
if(!attr) return;
var itemName = attr[IS_LINKED_TO_ARRAY];
if(!itemName) return;
delete attr[IS_LINKED_TO_ARRAY];
attrs[attrName] = { items: {} };
attrs[attrName].items[itemName] = attr;
attrs[attrName].role = 'object';
}
exports.crawl(attrs, callback);
}
function assignPolarLayoutAttrs(layoutAttributes) {
extendFlat(layoutAttributes, {
radialaxis: polarAxisAttrs.radialaxis,
angularaxis: polarAxisAttrs.angularaxis
});
extendFlat(layoutAttributes, polarAxisAttrs.layout);
return layoutAttributes;
}
function handleBasePlotModule(layoutAttributes, _module, astr) {
var np = Lib.nestedProperty(layoutAttributes, astr),
attrs = extendDeep({}, _module.layoutAttributes);
attrs[IS_SUBPLOT_OBJ] = true;
np.set(attrs);
}
function handleRegisteredComponent(layoutAttributes, _module, astr) {
var np = Lib.nestedProperty(layoutAttributes, astr),
attrs = extendDeep(np.get() || {}, _module.layoutAttributes);
np.set(attrs);
}