-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
"calendars" component #1230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
"calendars" component #1230
Changes from 1 commit
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
3143099
move world-calendar logic in lib/dates.js to new 'calendars' component
etpinard 5984106
coerce calendar attributes in calenders component
etpinard 03ab34f
add requirable 'calendars' module + include it in main bundle
etpinard db3d18b
fix typo in getComponentMethod call
etpinard 05b2f96
skip over calendar attribute in findArrayAttributes
etpinard 61ecd42
generalise plot schema handling of component attributes
etpinard 68af287
add schema attributes in calendars module
etpinard cb2c54b
move calendar defaults after early return
etpinard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/** | ||
* 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 calendars = require('world-calendars'); | ||
|
||
var Lib = require('../../lib'); | ||
var constants = require('../../constants/numerical'); | ||
|
||
var EPOCHJD = constants.EPOCHJD; | ||
var ONEDAY = constants.ONEDAY; | ||
|
||
// each calendar needs its own default canonical tick. I would love to use | ||
// 2000-01-01 (or even 0000-01-01) for them all but they don't necessarily | ||
// all support either of those dates. Instead I'll use the most significant | ||
// number they *do* support, biased toward the present day. | ||
var CANONICAL_TICK = { | ||
coptic: '2000-01-01', | ||
discworld: '2000-01-01', | ||
ethiopian: '2000-01-01', | ||
hebrew: '5000-01-01', | ||
islamic: '1000-01-01', | ||
julian: '2000-01-01', | ||
mayan: '5000-01-01', | ||
nanakshahi: '1000-01-01', | ||
nepali: '2000-01-01', | ||
persian: '1000-01-01', | ||
jalali: '1000-01-01', | ||
taiwan: '1000-01-01', | ||
thai: '2000-01-01', | ||
ummalqura: '1400-01-01' | ||
}; | ||
|
||
// Start on a Sunday - for week ticks | ||
// Discworld and Mayan calendars don't have 7-day weeks anyway so don't change them. | ||
// If anyone really cares we can customize the auto tick spacings for these calendars. | ||
var CANONICAL_SUNDAY = { | ||
coptic: '2000-01-03', | ||
discworld: '2000-01-01', | ||
ethiopian: '2000-01-05', | ||
hebrew: '5000-01-01', | ||
islamic: '1000-01-02', | ||
julian: '2000-01-03', | ||
mayan: '5000-01-01', | ||
nanakshahi: '1000-01-05', | ||
nepali: '2000-01-05', | ||
persian: '1000-01-01', | ||
jalali: '1000-01-01', | ||
taiwan: '1000-01-04', | ||
thai: '2000-01-04', | ||
ummalqura: '1400-01-06' | ||
}; | ||
|
||
var DFLTRANGE = { | ||
coptic: ['1700-01-01', '1701-01-01'], | ||
discworld: ['1800-01-01', '1801-01-01'], | ||
ethiopian: ['2000-01-01', '2001-01-01'], | ||
hebrew: ['5700-01-01', '5701-01-01'], | ||
islamic: ['1400-01-01', '1401-01-01'], | ||
julian: ['2000-01-01', '2001-01-01'], | ||
mayan: ['5200-01-01', '5201-01-01'], | ||
nanakshahi: ['0500-01-01', '0501-01-01'], | ||
nepali: ['2000-01-01', '2001-01-01'], | ||
persian: ['1400-01-01', '1401-01-01'], | ||
jalali: ['1400-01-01', '1401-01-01'], | ||
taiwan: ['0100-01-01', '0101-01-01'], | ||
thai: ['2500-01-01', '2501-01-01'], | ||
ummalqura: ['1400-01-01', '1401-01-01'] | ||
}; | ||
|
||
/* | ||
* convert d3 templates to world-calendars templates, so our users only need | ||
* to know d3's specifiers. Map space padding to no padding, and unknown fields | ||
* to an ugly placeholder | ||
*/ | ||
var UNKNOWN = '##'; | ||
var d3ToWorldCalendars = { | ||
'd': {'0': 'dd', '-': 'd'}, // 2-digit or unpadded day of month | ||
'a': {'0': 'D', '-': 'D'}, // short weekday name | ||
'A': {'0': 'DD', '-': 'DD'}, // full weekday name | ||
'j': {'0': 'oo', '-': 'o'}, // 3-digit or unpadded day of the year | ||
'W': {'0': 'ww', '-': 'w'}, // 2-digit or unpadded week of the year (Monday first) | ||
'm': {'0': 'mm', '-': 'm'}, // 2-digit or unpadded month number | ||
'b': {'0': 'M', '-': 'M'}, // short month name | ||
'B': {'0': 'MM', '-': 'MM'}, // full month name | ||
'y': {'0': 'yy', '-': 'yy'}, // 2-digit year (map unpadded to zero-padded) | ||
'Y': {'0': 'yyyy', '-': 'yyyy'}, // 4-digit year (map unpadded to zero-padded) | ||
'U': UNKNOWN, // Sunday-first week of the year | ||
'w': UNKNOWN, // day of the week [0(sunday),6] | ||
// combined format, we replace the date part with the world-calendar version | ||
// and the %X stays there for d3 to handle with time parts | ||
'%c': {'0': 'D M m %X yyyy', '-': 'D M m %X yyyy'}, | ||
'%x': {'0': 'mm/dd/yyyy', '-': 'mm/dd/yyyy'} | ||
}; | ||
|
||
function worldCalFmt(fmt, x, calendar) { | ||
var dateJD = Math.floor(x + 0.05 / ONEDAY) + EPOCHJD, | ||
cDate = getCal(calendar).fromJD(dateJD), | ||
i = 0, | ||
modifier, directive, directiveLen, directiveObj, replacementPart; | ||
while((i = fmt.indexOf('%', i)) !== -1) { | ||
modifier = fmt.charAt(i + 1); | ||
if(modifier === '0' || modifier === '-' || modifier === '_') { | ||
directiveLen = 3; | ||
directive = fmt.charAt(i + 1); | ||
if(modifier === '_') modifier = '-'; | ||
} | ||
else { | ||
directive = modifier; | ||
modifier = '0'; | ||
directiveLen = 2; | ||
} | ||
directiveObj = d3ToWorldCalendars[directive]; | ||
if(!directiveObj) { | ||
i += directiveLen; | ||
} | ||
else { | ||
// code is recognized as a date part but world-calendars doesn't support it | ||
if(directiveObj === UNKNOWN) replacementPart = UNKNOWN; | ||
|
||
// format the cDate according to the translated directive | ||
else replacementPart = cDate.formatDate(directiveObj[modifier]); | ||
|
||
fmt = fmt.substr(0, i) + replacementPart + fmt.substr(i + directiveLen); | ||
i += replacementPart.length; | ||
} | ||
} | ||
return fmt; | ||
} | ||
|
||
// cache world calendars, so we don't have to reinstantiate | ||
// during each date-time conversion | ||
var allCals = {}; | ||
function getCal(calendar) { | ||
var calendarObj = allCals[calendar]; | ||
if(calendarObj) return calendarObj; | ||
|
||
calendarObj = allCals[calendar] = calendars.instance(calendar); | ||
return calendarObj; | ||
} | ||
|
||
module.exports = { | ||
moduleType: 'component', | ||
name: 'calendars', | ||
|
||
CANONICAL_SUNDAY: CANONICAL_SUNDAY, | ||
CANONICAL_TICK: CANONICAL_TICK, | ||
DFLTRANGE: DFLTRANGE, | ||
|
||
getCal: getCal, | ||
worldCalFmt: worldCalFmt | ||
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not super happy about this, but I had to find a way to make
EPOCHJD
requirable in bothlib/dates.js
andcomponents/calendars/
in a non-circular wayThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the whole contents of the
if(calendar)
blocks went into the component, you wouldn't needEPOCHJD
in dates at all, would you?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. This will lead to some return object destructuring though. I'll see how bad it gets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I want to go through with this.
Moving all the logic from the
if(calendar)
blocks to the calendars component would involved passing oddly named variables to new routines and return sets of values and even mutating them infindExactDates
.I understand that if would be nice to put all the world-calendar logic in one place, but it would make the code less readable, unless we start duplicating some
dates.js
logic in the calendars component.The current implementation is both DRY and readable - which gets my vote.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, lets leave it as is.