|
| 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 | +// each calendar needs its own default canonical tick. I would love to use |
| 20 | +// 2000-01-01 (or even 0000-01-01) for them all but they don't necessarily |
| 21 | +// all support either of those dates. Instead I'll use the most significant |
| 22 | +// number they *do* support, biased toward the present day. |
| 23 | +var CANONICAL_TICK = { |
| 24 | + coptic: '2000-01-01', |
| 25 | + discworld: '2000-01-01', |
| 26 | + ethiopian: '2000-01-01', |
| 27 | + hebrew: '5000-01-01', |
| 28 | + islamic: '1000-01-01', |
| 29 | + julian: '2000-01-01', |
| 30 | + mayan: '5000-01-01', |
| 31 | + nanakshahi: '1000-01-01', |
| 32 | + nepali: '2000-01-01', |
| 33 | + persian: '1000-01-01', |
| 34 | + jalali: '1000-01-01', |
| 35 | + taiwan: '1000-01-01', |
| 36 | + thai: '2000-01-01', |
| 37 | + ummalqura: '1400-01-01' |
| 38 | +}; |
| 39 | + |
| 40 | +// Start on a Sunday - for week ticks |
| 41 | +// Discworld and Mayan calendars don't have 7-day weeks anyway so don't change them. |
| 42 | +// If anyone really cares we can customize the auto tick spacings for these calendars. |
| 43 | +var CANONICAL_SUNDAY = { |
| 44 | + coptic: '2000-01-03', |
| 45 | + discworld: '2000-01-01', |
| 46 | + ethiopian: '2000-01-05', |
| 47 | + hebrew: '5000-01-01', |
| 48 | + islamic: '1000-01-02', |
| 49 | + julian: '2000-01-03', |
| 50 | + mayan: '5000-01-01', |
| 51 | + nanakshahi: '1000-01-05', |
| 52 | + nepali: '2000-01-05', |
| 53 | + persian: '1000-01-01', |
| 54 | + jalali: '1000-01-01', |
| 55 | + taiwan: '1000-01-04', |
| 56 | + thai: '2000-01-04', |
| 57 | + ummalqura: '1400-01-06' |
| 58 | +}; |
| 59 | + |
| 60 | +var DFLTRANGE = { |
| 61 | + coptic: ['1700-01-01', '1701-01-01'], |
| 62 | + discworld: ['1800-01-01', '1801-01-01'], |
| 63 | + ethiopian: ['2000-01-01', '2001-01-01'], |
| 64 | + hebrew: ['5700-01-01', '5701-01-01'], |
| 65 | + islamic: ['1400-01-01', '1401-01-01'], |
| 66 | + julian: ['2000-01-01', '2001-01-01'], |
| 67 | + mayan: ['5200-01-01', '5201-01-01'], |
| 68 | + nanakshahi: ['0500-01-01', '0501-01-01'], |
| 69 | + nepali: ['2000-01-01', '2001-01-01'], |
| 70 | + persian: ['1400-01-01', '1401-01-01'], |
| 71 | + jalali: ['1400-01-01', '1401-01-01'], |
| 72 | + taiwan: ['0100-01-01', '0101-01-01'], |
| 73 | + thai: ['2500-01-01', '2501-01-01'], |
| 74 | + ummalqura: ['1400-01-01', '1401-01-01'] |
| 75 | +}; |
| 76 | + |
| 77 | +/* |
| 78 | + * convert d3 templates to world-calendars templates, so our users only need |
| 79 | + * to know d3's specifiers. Map space padding to no padding, and unknown fields |
| 80 | + * to an ugly placeholder |
| 81 | + */ |
| 82 | +var UNKNOWN = '##'; |
| 83 | +var d3ToWorldCalendars = { |
| 84 | + 'd': {'0': 'dd', '-': 'd'}, // 2-digit or unpadded day of month |
| 85 | + 'a': {'0': 'D', '-': 'D'}, // short weekday name |
| 86 | + 'A': {'0': 'DD', '-': 'DD'}, // full weekday name |
| 87 | + 'j': {'0': 'oo', '-': 'o'}, // 3-digit or unpadded day of the year |
| 88 | + 'W': {'0': 'ww', '-': 'w'}, // 2-digit or unpadded week of the year (Monday first) |
| 89 | + 'm': {'0': 'mm', '-': 'm'}, // 2-digit or unpadded month number |
| 90 | + 'b': {'0': 'M', '-': 'M'}, // short month name |
| 91 | + 'B': {'0': 'MM', '-': 'MM'}, // full month name |
| 92 | + 'y': {'0': 'yy', '-': 'yy'}, // 2-digit year (map unpadded to zero-padded) |
| 93 | + 'Y': {'0': 'yyyy', '-': 'yyyy'}, // 4-digit year (map unpadded to zero-padded) |
| 94 | + 'U': UNKNOWN, // Sunday-first week of the year |
| 95 | + 'w': UNKNOWN, // day of the week [0(sunday),6] |
| 96 | + // combined format, we replace the date part with the world-calendar version |
| 97 | + // and the %X stays there for d3 to handle with time parts |
| 98 | + '%c': {'0': 'D M m %X yyyy', '-': 'D M m %X yyyy'}, |
| 99 | + '%x': {'0': 'mm/dd/yyyy', '-': 'mm/dd/yyyy'} |
| 100 | +}; |
| 101 | + |
| 102 | +function worldCalFmt(fmt, x, calendar) { |
| 103 | + var dateJD = Math.floor(x + 0.05 / ONEDAY) + EPOCHJD, |
| 104 | + cDate = getCal(calendar).fromJD(dateJD), |
| 105 | + i = 0, |
| 106 | + modifier, directive, directiveLen, directiveObj, replacementPart; |
| 107 | + while((i = fmt.indexOf('%', i)) !== -1) { |
| 108 | + modifier = fmt.charAt(i + 1); |
| 109 | + if(modifier === '0' || modifier === '-' || modifier === '_') { |
| 110 | + directiveLen = 3; |
| 111 | + directive = fmt.charAt(i + 1); |
| 112 | + if(modifier === '_') modifier = '-'; |
| 113 | + } |
| 114 | + else { |
| 115 | + directive = modifier; |
| 116 | + modifier = '0'; |
| 117 | + directiveLen = 2; |
| 118 | + } |
| 119 | + directiveObj = d3ToWorldCalendars[directive]; |
| 120 | + if(!directiveObj) { |
| 121 | + i += directiveLen; |
| 122 | + } |
| 123 | + else { |
| 124 | + // code is recognized as a date part but world-calendars doesn't support it |
| 125 | + if(directiveObj === UNKNOWN) replacementPart = UNKNOWN; |
| 126 | + |
| 127 | + // format the cDate according to the translated directive |
| 128 | + else replacementPart = cDate.formatDate(directiveObj[modifier]); |
| 129 | + |
| 130 | + fmt = fmt.substr(0, i) + replacementPart + fmt.substr(i + directiveLen); |
| 131 | + i += replacementPart.length; |
| 132 | + } |
| 133 | + } |
| 134 | + return fmt; |
| 135 | +} |
| 136 | + |
| 137 | +// cache world calendars, so we don't have to reinstantiate |
| 138 | +// during each date-time conversion |
| 139 | +var allCals = {}; |
| 140 | +function getCal(calendar) { |
| 141 | + var calendarObj = allCals[calendar]; |
| 142 | + if(calendarObj) return calendarObj; |
| 143 | + |
| 144 | + calendarObj = allCals[calendar] = calendars.instance(calendar); |
| 145 | + return calendarObj; |
| 146 | +} |
| 147 | + |
| 148 | +module.exports = { |
| 149 | + moduleType: 'component', |
| 150 | + name: 'calendars', |
| 151 | + |
| 152 | + CANONICAL_SUNDAY: CANONICAL_SUNDAY, |
| 153 | + CANONICAL_TICK: CANONICAL_TICK, |
| 154 | + DFLTRANGE: DFLTRANGE, |
| 155 | + |
| 156 | + getCal: getCal, |
| 157 | + worldCalFmt: worldCalFmt |
| 158 | +}; |
0 commit comments