-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
Copy pathregistry.js
399 lines (339 loc) · 13.3 KB
/
registry.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
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
'use strict';
var Loggers = require('./lib/loggers');
var noop = require('./lib/noop');
var pushUnique = require('./lib/push_unique');
var isPlainObject = require('./lib/is_plain_object');
var addStyleRule = require('./lib/dom').addStyleRule;
var ExtendModule = require('./lib/extend');
var basePlotAttributes = require('./plots/attributes');
var baseLayoutAttributes = require('./plots/layout_attributes');
var extendFlat = ExtendModule.extendFlat;
var extendDeepAll = ExtendModule.extendDeepAll;
exports.modules = {};
exports.allCategories = {};
exports.allTypes = [];
exports.subplotsRegistry = {};
exports.componentsRegistry = {};
exports.layoutArrayContainers = [];
exports.layoutArrayRegexes = [];
exports.traceLayoutAttributes = {};
exports.localeRegistry = {};
exports.apiMethodRegistry = {};
exports.collectableSubplotTypes = null;
/**
* Top-level register routine, exported as Plotly.register
*
* @param {object array or array of objects} _modules :
* module object or list of module object to register.
*
* A valid `moduleType: 'trace'` module has fields:
* - name {string} : the trace type
* - categories {array} : categories associated with this trace type,
* tested with Register.traceIs()
* - meta {object} : meta info (mostly for plot-schema)
*
* A valid `moduleType: 'locale'` module has fields:
* - name {string} : the locale name. Should be a 2-digit language string ('en', 'de')
* optionally with a country/region code ('en-GB', 'de-CH'). If a country
* code is used but the base language locale has not yet been supplied,
* we will use this locale for the base as well.
* - dictionary {object} : the dictionary mapping input strings to localized strings
* generally the keys should be the literal input strings, but
* if default translations are provided you can use any string as a key.
* - format {object} : a `d3.locale` format specifier for this locale
* any omitted keys we'll fall back on en-US.
*
* A valid `moduleType: 'transform'` module has fields:
* - name {string} : transform name
* - transform {function} : default-level transform function
* - calcTransform {function} : calc-level transform function
* - attributes {object} : transform attributes declarations
* - supplyDefaults {function} : attributes default-supply function
*
* A valid `moduleType: 'component'` module has fields:
* - name {string} : the component name, used it with Register.getComponentMethod()
* to employ component method.
*
* A valid `moduleType: 'apiMethod'` module has fields:
* - name {string} : the api method name.
* - fn {function} : the api method called with Register.call();
*
*/
exports.register = function register(_modules) {
exports.collectableSubplotTypes = null;
if(!_modules) {
throw new Error('No argument passed to Plotly.register.');
} else if(_modules && !Array.isArray(_modules)) {
_modules = [_modules];
}
for(var i = 0; i < _modules.length; i++) {
var newModule = _modules[i];
if(!newModule) {
throw new Error('Invalid module was attempted to be registered!');
}
switch(newModule.moduleType) {
case 'trace':
registerTraceModule(newModule);
break;
case 'transform':
registerTransformModule(newModule);
break;
case 'component':
registerComponentModule(newModule);
break;
case 'locale':
registerLocale(newModule);
break;
case 'apiMethod':
var name = newModule.name;
exports.apiMethodRegistry[name] = newModule.fn;
break;
default:
throw new Error('Invalid module was attempted to be registered!');
}
}
};
/**
* Get registered module using trace object or trace type
*
* @param {object||string} trace
* trace object with prop 'type' or trace type as a string
* @return {object}
* module object corresponding to trace type
*/
exports.getModule = function(trace) {
var _module = exports.modules[getTraceType(trace)];
if(!_module) return false;
return _module._module;
};
/**
* Determine if this trace type is in a given category
*
* @param {object||string} traceType
* a trace (object) or trace type (string)
* @param {string} category
* category in question
* @return {boolean}
*/
exports.traceIs = function(traceType, category) {
traceType = getTraceType(traceType);
// old Chart Studio Cloud workspace hack, nothing to see here
if(traceType === 'various') return false;
var _module = exports.modules[traceType];
if(!_module) {
if(traceType) {
Loggers.log('Unrecognized trace type ' + traceType + '.');
}
_module = exports.modules[basePlotAttributes.type.dflt];
}
return !!_module.categories[category];
};
/**
* Retrieve component module method. Falls back on noop if either the
* module or the method is missing, so the result can always be safely called
*
* @param {string} name
* name of component (as declared in component module)
* @param {string} method
* name of component module method
* @return {function}
*/
exports.getComponentMethod = function(name, method) {
var _module = exports.componentsRegistry[name];
if(!_module) return noop;
return _module[method] || noop;
};
/**
* Call registered api method.
*
* @param {string} name : api method name
* @param {...array} args : arguments passed to api method
* @return {any} : returns api method output
*/
exports.call = function() {
var name = arguments[0];
var args = [].slice.call(arguments, 1);
return exports.apiMethodRegistry[name].apply(null, args);
};
function registerTraceModule(_module) {
var thisType = _module.name;
var categoriesIn = _module.categories;
var meta = _module.meta;
if(exports.modules[thisType]) {
Loggers.log('Type ' + thisType + ' already registered');
return;
}
if(!exports.subplotsRegistry[_module.basePlotModule.name]) {
registerSubplot(_module.basePlotModule);
}
var categoryObj = {};
for(var i = 0; i < categoriesIn.length; i++) {
categoryObj[categoriesIn[i]] = true;
exports.allCategories[categoriesIn[i]] = true;
}
exports.modules[thisType] = {
_module: _module,
categories: categoryObj
};
if(meta && Object.keys(meta).length) {
exports.modules[thisType].meta = meta;
}
exports.allTypes.push(thisType);
for(var componentName in exports.componentsRegistry) {
mergeComponentAttrsToTrace(componentName, thisType);
}
/*
* Collect all trace layout attributes in one place for easier lookup later
* but don't merge them into the base schema as it would confuse the docs
* (at least after https://github.com/plotly/documentation/issues/202 gets done!)
*/
if(_module.layoutAttributes) {
extendFlat(exports.traceLayoutAttributes, _module.layoutAttributes);
}
var basePlotModule = _module.basePlotModule;
var bpmName = basePlotModule.name;
// add mapbox-gl CSS here to avoid console warning on instantiation
if(bpmName === 'mapbox') {
var styleRules = basePlotModule.constants.styleRules;
for(var k in styleRules) {
addStyleRule('.js-plotly-plot .plotly .mapboxgl-' + k, styleRules[k]);
}
}
// add maplibre-gl CSS here to avoid console warning on instantiation
if(bpmName === 'map') {
require('maplibre-gl/dist/maplibre-gl.css');
}
// if `plotly-geo-assets.js` is not included,
// add `PlotlyGeoAssets` global to stash references to all fetched
// topojson / geojson data
if((bpmName === 'geo' || bpmName === 'mapbox' || bpmName === 'map') &&
(window.PlotlyGeoAssets === undefined)
) {
window.PlotlyGeoAssets = {topojson: {}};
}
}
function registerSubplot(_module) {
var plotType = _module.name;
if(exports.subplotsRegistry[plotType]) {
Loggers.log('Plot type ' + plotType + ' already registered.');
return;
}
// relayout array handling will look for component module methods with this
// name and won't find them because this is a subplot module... but that
// should be fine, it will just fall back on redrawing the plot.
findArrayRegexps(_module);
// not sure what's best for the 'cartesian' type at this point
exports.subplotsRegistry[plotType] = _module;
for(var componentName in exports.componentsRegistry) {
mergeComponentAttrsToSubplot(componentName, _module.name);
}
}
function registerComponentModule(_module) {
if(typeof _module.name !== 'string') {
throw new Error('Component module *name* must be a string.');
}
var name = _module.name;
exports.componentsRegistry[name] = _module;
if(_module.layoutAttributes) {
if(_module.layoutAttributes._isLinkedToArray) {
pushUnique(exports.layoutArrayContainers, name);
}
findArrayRegexps(_module);
}
for(var traceType in exports.modules) {
mergeComponentAttrsToTrace(name, traceType);
}
for(var subplotName in exports.subplotsRegistry) {
mergeComponentAttrsToSubplot(name, subplotName);
}
if(_module.schema && _module.schema.layout) {
extendDeepAll(baseLayoutAttributes, _module.schema.layout);
}
}
function registerTransformModule(_module) {
if(typeof _module.name !== 'string') {
throw new Error('Transform module *name* must be a string.');
}
var prefix = 'Transform module ' + _module.name;
var hasTransform = typeof _module.transform === 'function';
var hasCalcTransform = typeof _module.calcTransform === 'function';
if(!hasTransform && !hasCalcTransform) {
throw new Error(prefix + ' is missing a *transform* or *calcTransform* method.');
}
if(hasTransform && hasCalcTransform) {
Loggers.log([
prefix + ' has both a *transform* and *calcTransform* methods.',
'Please note that all *transform* methods are executed',
'before all *calcTransform* methods.'
].join(' '));
}
if(!isPlainObject(_module.attributes)) {
Loggers.log(prefix + ' registered without an *attributes* object.');
}
if(typeof _module.supplyDefaults !== 'function') {
Loggers.log(prefix + ' registered without a *supplyDefaults* method.');
}
}
function registerLocale(_module) {
var locale = _module.name;
var baseLocale = locale.split('-')[0];
var newDict = _module.dictionary;
var newFormat = _module.format;
var hasDict = newDict && Object.keys(newDict).length;
var hasFormat = newFormat && Object.keys(newFormat).length;
var locales = exports.localeRegistry;
var localeObj = locales[locale];
if(!localeObj) locales[locale] = localeObj = {};
// Should we use this dict for the base locale?
// In case we're overwriting a previous dict for this locale, check
// whether the base matches the full locale dict now. If we're not
// overwriting, locales[locale] is undefined so this just checks if
// baseLocale already had a dict or not.
// Same logic for dateFormats
if(baseLocale !== locale) {
var baseLocaleObj = locales[baseLocale];
if(!baseLocaleObj) locales[baseLocale] = baseLocaleObj = {};
if(hasDict && baseLocaleObj.dictionary === localeObj.dictionary) {
baseLocaleObj.dictionary = newDict;
}
if(hasFormat && baseLocaleObj.format === localeObj.format) {
baseLocaleObj.format = newFormat;
}
}
if(hasDict) localeObj.dictionary = newDict;
if(hasFormat) localeObj.format = newFormat;
}
function findArrayRegexps(_module) {
if(_module.layoutAttributes) {
var arrayAttrRegexps = _module.layoutAttributes._arrayAttrRegexps;
if(arrayAttrRegexps) {
for(var i = 0; i < arrayAttrRegexps.length; i++) {
pushUnique(exports.layoutArrayRegexes, arrayAttrRegexps[i]);
}
}
}
}
function mergeComponentAttrsToTrace(componentName, traceType) {
var componentSchema = exports.componentsRegistry[componentName].schema;
if(!componentSchema || !componentSchema.traces) return;
var traceAttrs = componentSchema.traces[traceType];
if(traceAttrs) {
extendDeepAll(exports.modules[traceType]._module.attributes, traceAttrs);
}
}
function mergeComponentAttrsToSubplot(componentName, subplotName) {
var componentSchema = exports.componentsRegistry[componentName].schema;
if(!componentSchema || !componentSchema.subplots) return;
var subplotModule = exports.subplotsRegistry[subplotName];
var subplotAttrs = subplotModule.layoutAttributes;
var subplotAttr = subplotModule.attr === 'subplot' ? subplotModule.name : subplotModule.attr;
if(Array.isArray(subplotAttr)) subplotAttr = subplotAttr[0];
var componentLayoutAttrs = componentSchema.subplots[subplotAttr];
if(subplotAttrs && componentLayoutAttrs) {
extendDeepAll(subplotAttrs, componentLayoutAttrs);
}
}
function getTraceType(traceType) {
if(typeof traceType === 'object') traceType = traceType.type;
return traceType;
}