Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit bc457a9

Browse filesBrowse files
authored
Merge pull request plotly#1230 from plotly/world-cals-component
"calendars" component
2 parents 00ae2dd + cb2c54b commit bc457a9
Copy full SHA for bc457a9

File tree

Expand file treeCollapse file tree

39 files changed

+410
-278
lines changed
Filter options
Expand file treeCollapse file tree

39 files changed

+410
-278
lines changed

‎lib/calendars.js

Copy file name to clipboard
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
module.exports = require('../src/components/calendars');

‎lib/index.js

Copy file name to clipboardExpand all lines: lib/index.js
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,9 @@ Plotly.register([
5454
require('./groupby')
5555
]);
5656

57+
// components
58+
Plotly.register([
59+
require('./calendars')
60+
]);
61+
5762
module.exports = Plotly;

‎src/components/calendars/index.js

Copy file name to clipboard
+249Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/**
2+
* Copyright 2012-2016, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var calendars = require('world-calendars');
12+
13+
var Lib = require('../../lib');
14+
var constants = require('../../constants/numerical');
15+
16+
var EPOCHJD = constants.EPOCHJD;
17+
var ONEDAY = constants.ONEDAY;
18+
19+
var attributes = {
20+
valType: 'enumerated',
21+
values: Object.keys(calendars.calendars),
22+
role: 'info',
23+
dflt: 'gregorian'
24+
};
25+
26+
var handleDefaults = function(contIn, contOut, attr, dflt) {
27+
var attrs = {};
28+
attrs[attr] = attributes;
29+
30+
return Lib.coerce(contIn, contOut, attrs, attr, dflt);
31+
};
32+
33+
var handleTraceDefaults = function(traceIn, traceOut, coords, layout) {
34+
for(var i = 0; i < coords.length; i++) {
35+
handleDefaults(traceIn, traceOut, coords[i] + 'calendar', layout.calendar);
36+
}
37+
};
38+
39+
// each calendar needs its own default canonical tick. I would love to use
40+
// 2000-01-01 (or even 0000-01-01) for them all but they don't necessarily
41+
// all support either of those dates. Instead I'll use the most significant
42+
// number they *do* support, biased toward the present day.
43+
var CANONICAL_TICK = {
44+
coptic: '2000-01-01',
45+
discworld: '2000-01-01',
46+
ethiopian: '2000-01-01',
47+
hebrew: '5000-01-01',
48+
islamic: '1000-01-01',
49+
julian: '2000-01-01',
50+
mayan: '5000-01-01',
51+
nanakshahi: '1000-01-01',
52+
nepali: '2000-01-01',
53+
persian: '1000-01-01',
54+
jalali: '1000-01-01',
55+
taiwan: '1000-01-01',
56+
thai: '2000-01-01',
57+
ummalqura: '1400-01-01'
58+
};
59+
60+
// Start on a Sunday - for week ticks
61+
// Discworld and Mayan calendars don't have 7-day weeks anyway so don't change them.
62+
// If anyone really cares we can customize the auto tick spacings for these calendars.
63+
var CANONICAL_SUNDAY = {
64+
coptic: '2000-01-03',
65+
discworld: '2000-01-01',
66+
ethiopian: '2000-01-05',
67+
hebrew: '5000-01-01',
68+
islamic: '1000-01-02',
69+
julian: '2000-01-03',
70+
mayan: '5000-01-01',
71+
nanakshahi: '1000-01-05',
72+
nepali: '2000-01-05',
73+
persian: '1000-01-01',
74+
jalali: '1000-01-01',
75+
taiwan: '1000-01-04',
76+
thai: '2000-01-04',
77+
ummalqura: '1400-01-06'
78+
};
79+
80+
var DFLTRANGE = {
81+
coptic: ['1700-01-01', '1701-01-01'],
82+
discworld: ['1800-01-01', '1801-01-01'],
83+
ethiopian: ['2000-01-01', '2001-01-01'],
84+
hebrew: ['5700-01-01', '5701-01-01'],
85+
islamic: ['1400-01-01', '1401-01-01'],
86+
julian: ['2000-01-01', '2001-01-01'],
87+
mayan: ['5200-01-01', '5201-01-01'],
88+
nanakshahi: ['0500-01-01', '0501-01-01'],
89+
nepali: ['2000-01-01', '2001-01-01'],
90+
persian: ['1400-01-01', '1401-01-01'],
91+
jalali: ['1400-01-01', '1401-01-01'],
92+
taiwan: ['0100-01-01', '0101-01-01'],
93+
thai: ['2500-01-01', '2501-01-01'],
94+
ummalqura: ['1400-01-01', '1401-01-01']
95+
};
96+
97+
/*
98+
* convert d3 templates to world-calendars templates, so our users only need
99+
* to know d3's specifiers. Map space padding to no padding, and unknown fields
100+
* to an ugly placeholder
101+
*/
102+
var UNKNOWN = '##';
103+
var d3ToWorldCalendars = {
104+
'd': {'0': 'dd', '-': 'd'}, // 2-digit or unpadded day of month
105+
'a': {'0': 'D', '-': 'D'}, // short weekday name
106+
'A': {'0': 'DD', '-': 'DD'}, // full weekday name
107+
'j': {'0': 'oo', '-': 'o'}, // 3-digit or unpadded day of the year
108+
'W': {'0': 'ww', '-': 'w'}, // 2-digit or unpadded week of the year (Monday first)
109+
'm': {'0': 'mm', '-': 'm'}, // 2-digit or unpadded month number
110+
'b': {'0': 'M', '-': 'M'}, // short month name
111+
'B': {'0': 'MM', '-': 'MM'}, // full month name
112+
'y': {'0': 'yy', '-': 'yy'}, // 2-digit year (map unpadded to zero-padded)
113+
'Y': {'0': 'yyyy', '-': 'yyyy'}, // 4-digit year (map unpadded to zero-padded)
114+
'U': UNKNOWN, // Sunday-first week of the year
115+
'w': UNKNOWN, // day of the week [0(sunday),6]
116+
// combined format, we replace the date part with the world-calendar version
117+
// and the %X stays there for d3 to handle with time parts
118+
'%c': {'0': 'D M m %X yyyy', '-': 'D M m %X yyyy'},
119+
'%x': {'0': 'mm/dd/yyyy', '-': 'mm/dd/yyyy'}
120+
};
121+
122+
function worldCalFmt(fmt, x, calendar) {
123+
var dateJD = Math.floor(x + 0.05 / ONEDAY) + EPOCHJD,
124+
cDate = getCal(calendar).fromJD(dateJD),
125+
i = 0,
126+
modifier, directive, directiveLen, directiveObj, replacementPart;
127+
while((i = fmt.indexOf('%', i)) !== -1) {
128+
modifier = fmt.charAt(i + 1);
129+
if(modifier === '0' || modifier === '-' || modifier === '_') {
130+
directiveLen = 3;
131+
directive = fmt.charAt(i + 1);
132+
if(modifier === '_') modifier = '-';
133+
}
134+
else {
135+
directive = modifier;
136+
modifier = '0';
137+
directiveLen = 2;
138+
}
139+
directiveObj = d3ToWorldCalendars[directive];
140+
if(!directiveObj) {
141+
i += directiveLen;
142+
}
143+
else {
144+
// code is recognized as a date part but world-calendars doesn't support it
145+
if(directiveObj === UNKNOWN) replacementPart = UNKNOWN;
146+
147+
// format the cDate according to the translated directive
148+
else replacementPart = cDate.formatDate(directiveObj[modifier]);
149+
150+
fmt = fmt.substr(0, i) + replacementPart + fmt.substr(i + directiveLen);
151+
i += replacementPart.length;
152+
}
153+
}
154+
return fmt;
155+
}
156+
157+
// cache world calendars, so we don't have to reinstantiate
158+
// during each date-time conversion
159+
var allCals = {};
160+
function getCal(calendar) {
161+
var calendarObj = allCals[calendar];
162+
if(calendarObj) return calendarObj;
163+
164+
calendarObj = allCals[calendar] = calendars.instance(calendar);
165+
return calendarObj;
166+
}
167+
168+
function makeAttrs(description) {
169+
return Lib.extendFlat({}, attributes, { description: description });
170+
}
171+
172+
function makeTraceAttrsDescription(coord) {
173+
return 'Sets the calendar system to use with `' + coord + '` date data.';
174+
}
175+
176+
var xAttrs = {
177+
xcalendar: makeAttrs(makeTraceAttrsDescription('x'))
178+
};
179+
180+
var xyAttrs = Lib.extendFlat({}, xAttrs, {
181+
ycalendar: makeAttrs(makeTraceAttrsDescription('y'))
182+
});
183+
184+
var xyzAttrs = Lib.extendFlat({}, xyAttrs, {
185+
zcalendar: makeAttrs(makeTraceAttrsDescription('z'))
186+
});
187+
188+
var axisAttrs = makeAttrs([
189+
'Sets the calendar system to use for `range` and `tick0`',
190+
'if this is a date axis. This does not set the calendar for',
191+
'interpreting data on this axis, that\'s specified in the trace',
192+
'or via the global `layout.calendar`'
193+
].join(' '));
194+
195+
module.exports = {
196+
moduleType: 'component',
197+
name: 'calendars',
198+
199+
schema: {
200+
traces: {
201+
scatter: xyAttrs,
202+
bar: xyAttrs,
203+
heatmap: xyAttrs,
204+
contour: xyAttrs,
205+
histogram: xyAttrs,
206+
histogram2d: xyAttrs,
207+
histogram2dcontour: xyAttrs,
208+
scatter3d: xyzAttrs,
209+
surface: xyzAttrs,
210+
mesh3d: xyzAttrs,
211+
scattergl: xyAttrs,
212+
ohlc: xAttrs,
213+
candlestick: xAttrs
214+
},
215+
layout: {
216+
calendar: makeAttrs([
217+
'Sets the default calendar system to use for interpreting and',
218+
'displaying dates throughout the plot.'
219+
].join(' ')),
220+
'xaxis.calendar': axisAttrs,
221+
'yaxis.calendar': axisAttrs,
222+
'scene.xaxis.calendar': axisAttrs,
223+
'scene.yaxis.calendar': axisAttrs,
224+
'scene.zaxis.calendar': axisAttrs
225+
},
226+
transforms: {
227+
filter: {
228+
calendar: makeAttrs([
229+
'Sets the calendar system to use for `value`, if it is a date.',
230+
'Note that this is not necessarily the same calendar as is used',
231+
'for the target data; that is set by its own calendar attribute,',
232+
'ie `trace.x` uses `trace.xcalendar` etc.'
233+
].join(' '))
234+
}
235+
}
236+
},
237+
238+
layoutAttributes: attributes,
239+
240+
handleDefaults: handleDefaults,
241+
handleTraceDefaults: handleTraceDefaults,
242+
243+
CANONICAL_SUNDAY: CANONICAL_SUNDAY,
244+
CANONICAL_TICK: CANONICAL_TICK,
245+
DFLTRANGE: DFLTRANGE,
246+
247+
getCal: getCal,
248+
worldCalFmt: worldCalFmt
249+
};

‎src/components/rangeselector/index.js

Copy file name to clipboardExpand all lines: src/components/rangeselector/index.js
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ module.exports = {
1212
moduleType: 'component',
1313
name: 'rangeselector',
1414

15-
layoutNodes: ['xaxis.'],
15+
schema: {
16+
layout: {
17+
'xaxis.rangeselector': require('./attributes')
18+
}
19+
},
20+
1621
layoutAttributes: require('./attributes'),
1722
handleDefaults: require('./defaults'),
1823

‎src/components/rangeslider/index.js

Copy file name to clipboardExpand all lines: src/components/rangeslider/index.js
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ module.exports = {
1212
moduleType: 'component',
1313
name: 'rangeslider',
1414

15-
layoutNodes: ['xaxis.'],
15+
schema: {
16+
layout: {
17+
'xaxis.rangeslider': require('./attributes')
18+
}
19+
},
20+
1621
layoutAttributes: require('./attributes'),
1722
handleDefaults: require('./defaults'),
1823

‎src/constants/numerical.js

Copy file name to clipboardExpand all lines: src/constants/numerical.js
+7-1Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,11 @@ module.exports = {
3636
ONEDAY: 86400000,
3737
ONEHOUR: 3600000,
3838
ONEMIN: 60000,
39-
ONESEC: 1000
39+
ONESEC: 1000,
40+
41+
/*
42+
* For fast conversion btwn world calendars and epoch ms, the Julian Day Number
43+
* of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD()
44+
*/
45+
EPOCHJD: 2440587.5
4046
};

‎src/lib/coerce.js

Copy file name to clipboardExpand all lines: src/lib/coerce.js
-15Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
'use strict';
1111

12-
var calendarList = Object.keys(require('world-calendars').calendars);
1312
var isNumeric = require('fast-isnumeric');
1413
var tinycolor = require('tinycolor2');
1514

@@ -268,20 +267,6 @@ exports.valObjects = {
268267

269268
return true;
270269
}
271-
},
272-
calendar: {
273-
description: [
274-
'A string, one of the calendar systems available',
275-
'in the `world-calendars` package, to be used in evaluating',
276-
'or displaying date data. Defaults to built-in (Gregorian) dates.',
277-
'available calendars:', '*' + calendarList.join('*, *') + '*'
278-
].join(' '),
279-
requiredOpts: [],
280-
otherOpts: ['dflt'],
281-
coerceFunction: function(v, propOut, dflt) {
282-
if(v && calendarList.indexOf(v) !== -1) propOut.set(v);
283-
else propOut.set(dflt);
284-
}
285270
}
286271
};
287272

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.