From d2a5e4b641a91cd0e3f6a1f1156c5faa58dbab1b Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 2 Dec 2016 12:42:54 -0500 Subject: [PATCH 01/30] simpleMap utility so we don't pass unexpected args and hopefully faster than built-in .map --- src/lib/index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib/index.js b/src/lib/index.js index 023896b0e80..e8122b262b9 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -135,6 +135,22 @@ lib.identity = function(d) { return d; }; // minor convenience helper lib.noop = function() {}; +/* + * simpleMap: alternative to Array.map that only + * passes on the element and up to 2 extra args you + * provide (but not the array index or the whole array) + * + * array: the array to map it to + * func: the function to apply + * x1, x2: optional extra args + */ +lib.simpleMap = function(array, func, x1, x2) { + var len = array.length, + out = new Array(len); + for(var i = 0; i < len; i++) out[i] = func(array[i], x1, x2); + return out; +}; + // random string generator lib.randstr = function randstr(existing, bits, base) { /* From 6151575db5971d902a250a30fc30ad8689dac290 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 30 Nov 2016 17:51:21 -0500 Subject: [PATCH 02/30] change gl2d_date_axes mock to date string axis range I still can't tell why this is passing as a test when it fails for me in the dashboard (because of timezone conversion) --- test/image/mocks/gl2d_date_axes.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/image/mocks/gl2d_date_axes.json b/test/image/mocks/gl2d_date_axes.json index 4f6fff945b2..88fd88672ba 100644 --- a/test/image/mocks/gl2d_date_axes.json +++ b/test/image/mocks/gl2d_date_axes.json @@ -60,8 +60,8 @@ "layout": { "xaxis": { "range": [ - 1477434179998, - 1477434180004 + "2016-10-25 22:22:59.998", + "2016-10-25 22:23:00.004" ], "autorange": false } From 4b9edecd3831fa597d30d18632f2f0a370827c64 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Wed, 30 Nov 2016 17:10:20 -0500 Subject: [PATCH 03/30] add world calendar support part 1 --- package.json | 3 +- src/lib/coerce.js | 15 ++ src/lib/dates.js | 280 ++++++++++++++++++-- src/lib/index.js | 2 + src/lib/mod.js | 18 ++ src/plot_api/plot_api.js | 4 +- src/plots/cartesian/axes.js | 141 ++++------ src/plots/cartesian/axis_autotype.js | 8 +- src/plots/cartesian/axis_defaults.js | 11 +- src/plots/cartesian/layout_attributes.js | 10 + src/plots/cartesian/set_convert.js | 61 +++-- src/plots/gl3d/layout/axis_attributes.js | 1 + src/plots/layout_attributes.js | 9 +- src/plots/plots.js | 2 + src/traces/bar/attributes.js | 2 + src/traces/bar/defaults.js | 2 +- src/traces/box/attributes.js | 2 + src/traces/box/calc.js | 2 +- src/traces/box/defaults.js | 6 +- src/traces/candlestick/attributes.js | 1 + src/traces/candlestick/defaults.js | 4 +- src/traces/candlestick/transform.js | 1 + src/traces/contour/attributes.js | 2 + src/traces/heatmap/attributes.js | 3 + src/traces/heatmap/calc.js | 16 +- src/traces/heatmap/convert_column_xyz.js | 4 +- src/traces/heatmap/xyz_defaults.js | 3 + src/traces/histogram/attributes.js | 2 + src/traces/histogram/calc.js | 9 +- src/traces/histogram/clean_bins.js | 6 +- src/traces/histogram/defaults.js | 3 + src/traces/histogram2d/attributes.js | 2 + src/traces/histogram2d/sample_defaults.js | 3 + src/traces/histogram2dcontour/attributes.js | 2 + src/traces/ohlc/attributes.js | 1 + src/traces/ohlc/defaults.js | 4 +- src/traces/ohlc/ohlc_defaults.js | 4 +- src/traces/ohlc/transform.js | 1 + src/traces/scatter/attributes.js | 11 + src/traces/scatter/defaults.js | 2 +- src/traces/scatter/xy_defaults.js | 6 +- src/traces/scatter3d/attributes.js | 4 + src/traces/scattergl/attributes.js | 3 + src/traces/scattergl/convert.js | 6 +- src/traces/scattergl/defaults.js | 2 +- src/traces/surface/attributes.js | 5 + src/traces/surface/convert.js | 15 +- src/transforms/filter.js | 67 +++-- test/jasmine/tests/bar_test.js | 20 +- test/jasmine/tests/box_test.js | 16 +- test/jasmine/tests/histogram_test.js | 26 +- test/jasmine/tests/lib_date_test.js | 62 +++++ test/jasmine/tests/plotschema_test.js | 2 +- 53 files changed, 664 insertions(+), 233 deletions(-) create mode 100644 src/lib/mod.js diff --git a/package.json b/package.json index 17515ba7a89..68552a3d666 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,8 @@ "superscript-text": "^1.0.0", "tinycolor2": "^1.3.0", "topojson-client": "^2.1.0", - "webgl-context": "^2.2.0" + "webgl-context": "^2.2.0", + "world-calendars": "0.1.0" }, "devDependencies": { "brfs": "^1.4.3", diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 7d69b9e64c7..846869c2a20 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -9,6 +9,7 @@ 'use strict'; +var calendarList = Object.keys(require('world-calendars').calendars); var isNumeric = require('fast-isnumeric'); var tinycolor = require('tinycolor2'); @@ -267,6 +268,20 @@ exports.valObjects = { return true; } + }, + calendar: { + description: [ + 'A string, one of the calendar systems available', + 'in the `world-calendars` package, to be used in evaluating', + 'or displaying date data. Defaults to built-in (Gregorian) dates.', + 'available calendars:', '*' + calendarList.join('*, *') + '*' + ].join(' '), + requiredOpts: [], + otherOpts: ['dflt'], + coerceFunction: function(v, propOut, dflt) { + if(v && calendarList.indexOf(v) !== -1) propOut.set(v); + else propOut.set(dflt); + } } }; diff --git a/src/lib/dates.js b/src/lib/dates.js index a486bc797ee..5af2f465e0a 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -9,9 +9,12 @@ 'use strict'; +var calendars = require('world-calendars'); var d3 = require('d3'); +var isNumeric = require('fast-isnumeric'); var logError = require('./loggers').error; +var mod = require('./mod'); var constants = require('../constants/numerical'); var BADNUM = constants.BADNUM; @@ -20,11 +23,32 @@ var ONEHOUR = constants.ONEHOUR; var ONEMIN = constants.ONEMIN; var ONESEC = constants.ONESEC; -var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(0?[1-9]|1[012])(-([0-3]?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m; +var utcFormat = d3.time.format.utc; + +var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\d)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m; // for 2-digit years, the first year we map them onto var YFIRST = new Date().getFullYear() - 70; +// 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; +} + +function isWorldCalendar(calendar) { + return calendar && typeof calendar === 'string' && calendar !== 'gregorian'; +} + +// For fast conversion btwn world calendars and epoch ms, the Julian Day Number +// of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD() +var EPOCHJD = 2440587.5; + // is an object a javascript date? exports.isJSDate = function(v) { return typeof v === 'object' && v !== null && typeof v.getTime === 'function'; @@ -38,6 +62,7 @@ var MIN_MS, MAX_MS; /** * dateTime2ms - turn a date object or string s into milliseconds * (relative to 1970-01-01, per javascript standard) + * optional calendar (string) to use a non-gregorian calendar * * Returns BADNUM if it doesn't find a date * @@ -83,8 +108,7 @@ var MIN_MS, MAX_MS; * currently (2016) this range is: * 1946-2045 */ - -exports.dateTime2ms = function(s) { +exports.dateTime2ms = function(s, calendar) { // first check if s is a date object if(exports.isJSDate(s)) { // Convert to the UTC milliseconds that give the same @@ -104,16 +128,34 @@ exports.dateTime2ms = function(s) { H = Number(match[7] || 0), M = Number(match[9] || 0), S = Number(match[11] || 0); + + if(isWorldCalendar(calendar)) { + // disallow 2-digit years for world calendars + if(y.length === 2) return BADNUM; + + var cDate = getCal(calendar).newDate(Number(y), m, d); + if(!cDate) return BADNUM; + + return ((cDate.toJD() - EPOCHJD) * ONEDAY) + + (H * ONEHOUR) + (M * ONEMIN) + (S * ONESEC); + } + if(y.length === 2) { y = (Number(y) + 2000 - YFIRST) % 100 + YFIRST; } else y = Number(y); + // new Date uses months from 0; subtract 1 here just so we + // don't have to do it again during the validity test below + m -= 1; + // javascript takes new Date(0..99,m,d) to mean 1900-1999, so // to support years 0-99 we need to use setFullYear explicitly - var date = new Date(Date.UTC(2000, m - 1, d, H, M)); + // Note that 2000 is a leap year. + var date = new Date(Date.UTC(2000, m, d, H, M)); date.setUTCFullYear(y); + if(date.getUTCMonth() !== m) return BADNUM; if(date.getUTCDate() !== d) return BADNUM; return date.getTime() + S * ONESEC; @@ -123,8 +165,8 @@ MIN_MS = exports.MIN_MS = exports.dateTime2ms('-9999'); MAX_MS = exports.MAX_MS = exports.dateTime2ms('9999-12-31 23:59:59.9999'); // is string s a date? (see above) -exports.isDateTime = function(s) { - return (exports.dateTime2ms(s) !== BADNUM); +exports.isDateTime = function(s, calendar) { + return (exports.dateTime2ms(s, calendar) !== BADNUM); }; // pad a number with zeroes, to given # of digits before the decimal point @@ -143,21 +185,50 @@ function lpad(val, digits) { var NINETYDAYS = 90 * ONEDAY; var THREEHOURS = 3 * ONEHOUR; var FIVEMIN = 5 * ONEMIN; -exports.ms2DateTime = function(ms, r) { +exports.ms2DateTime = function(ms, r, calendar) { if(typeof ms !== 'number' || !(ms >= MIN_MS && ms <= MAX_MS)) return BADNUM; if(!r) r = 0; - var msecTenths = Math.round(((ms % 1) + 1) * 10) % 10, - d = new Date(Math.round(ms - msecTenths / 10)), - dateStr = d3.time.format.utc('%Y-%m-%d')(d), + var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10), + msRounded = Math.round(ms - msecTenths / 10), + dateStr, h, m, s, msec10; + + if(isWorldCalendar(calendar)) { + var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD, + timeMs = Math.floor(mod(ms, ONEDAY)); + dateStr = getCal(calendar).fromJD(dateJD).formatDate('yyyy-mm-dd'); + + // yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does + // other things for a few calendars, so we can't trust it. Just pad + // it manually (after the '-' if there is one) + if(dateStr.charAt(0) === '-') { + while(dateStr.length < 11) dateStr = '-0' + dateStr.substr(1); + } + else { + while(dateStr.length < 10) dateStr = '0' + dateStr; + } + + // TODO: if this is faster, we could use this block for extracting + // the time components of regular gregorian too + h = (r < NINETYDAYS) ? Math.floor(timeMs / ONEHOUR) : 0; + m = (r < NINETYDAYS) ? Math.floor((timeMs % ONEHOUR) / ONEMIN) : 0; + s = (r < THREEHOURS) ? Math.floor((timeMs % ONEMIN) / ONESEC) : 0; + msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0; + } + else { + var d = new Date(msRounded); + + dateStr = utcFormat('%Y-%m-%d')(d); + // <90 days: add hours and minutes - never *only* add hours - h = (r < NINETYDAYS) ? d.getUTCHours() : 0, - m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0, + h = (r < NINETYDAYS) ? d.getUTCHours() : 0; + m = (r < NINETYDAYS) ? d.getUTCMinutes() : 0; // <3 hours: add seconds - s = (r < THREEHOURS) ? d.getUTCSeconds() : 0, + s = (r < THREEHOURS) ? d.getUTCSeconds() : 0; // <5 minutes: add ms (plus one extra digit, this is msec*10) msec10 = (r < FIVEMIN) ? d.getUTCMilliseconds() * 10 + msecTenths : 0; + } return includeTime(dateStr, h, m, s, msec10); }; @@ -171,7 +242,7 @@ exports.ms2DateTime = function(ms, r) { exports.ms2DateTimeLocal = function(ms) { if(!(ms >= MIN_MS + ONEDAY && ms <= MAX_MS - ONEDAY)) return BADNUM; - var msecTenths = Math.round(((ms % 1) + 1) * 10) % 10, + var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10), d = new Date(Math.round(ms - msecTenths / 10)), dateStr = d3.time.format('%Y-%m-%d')(d), h = d.getHours(), @@ -204,17 +275,194 @@ function includeTime(dateStr, h, m, s, msec10) { // normalize date format to date string, in case it starts as // a Date object or milliseconds // optional dflt is the return value if cleaning fails -exports.cleanDate = function(v, dflt) { +exports.cleanDate = function(v, dflt, calendar) { if(exports.isJSDate(v) || typeof v === 'number') { + // do not allow milliseconds (old) or jsdate objects (inherently + // described as gregorian dates) with world calendars + if(isWorldCalendar(calendar)) { + logError('JS Dates and milliseconds are incompatible with world calendars', v); + return dflt; + } + // NOTE: if someone puts in a year as a number rather than a string, // this will mistakenly convert it thinking it's milliseconds from 1970 // that is: '2012' -> Jan. 1, 2012, but 2012 -> 2012 epoch milliseconds v = exports.ms2DateTimeLocal(+v); if(!v && dflt !== undefined) return dflt; } - else if(!exports.isDateTime(v)) { + else if(!exports.isDateTime(v, calendar)) { logError('unrecognized date', v); return dflt; } return v; }; + +/* + * Date formatting for ticks and hovertext + */ + +/* + * 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; +} + +/* + * modDateFormat: Support world calendars, and add one item to + * d3's vocabulary: + * %{n}f where n is the max number of digits of fractional seconds + */ +var fracMatch = /%(\d?)f/g; +function modDateFormat(fmt, x, calendar) { + var fm = fmt.match(fracMatch), + d = new Date(x); + if(fm) { + var digits = Math.min(+fm[1] || 6, 6), + fracSecs = String((x / 1000 % 1) + 2.0000005) + .substr(2, digits).replace(/0+$/, '') || '0'; + fmt = fmt.replace(fracMatch, fracSecs); + } + if(isWorldCalendar(calendar)) { + fmt = worldCalFmt(fmt, x, calendar); + } + return utcFormat(fmt)(d); +} + +/* + * formatTime: create a time string from: + * x: milliseconds + * tr: tickround ('M', 'S', or # digits) + * only supports UTC times (where every day is 24 hours and 0 is at midnight) + */ +function formatTime(x, tr) { + var timePart = mod(x, ONEDAY); + + var timeStr = lpad(Math.floor(timePart / ONEHOUR), 2) + ':' + + lpad(mod(Math.floor(timePart / ONEMIN), 60), 2); + + if(tr !== 'M') { + if(!isNumeric(tr)) tr = 0; // should only be 'S' + timeStr += ':' + String(100 + d3.round(mod(x / ONESEC, 60), tr)).substr(1); + } + return timeStr; +} + +var yearFormat = utcFormat('%Y'), + monthFormat = utcFormat('%b %Y'), + dayFormat = utcFormat('%b %-d'), + yearMonthDayFormat = utcFormat('%b %-d, %Y'); + +function yearFormatWorld(cDate) { return cDate.formatDate('yyyy'); } +function monthFormatWorld(cDate) { return cDate.formatDate('M yyyy'); } +function dayFormatWorld(cDate) { return cDate.formatDate('M d'); } +function yearMonthDayFormatWorld(cDate) { return cDate.formatDate('M d, yyyy'); } + +/* + * formatDate: turn a date into tick or hover label text. + * + * x: milliseconds, the value to convert + * fmt: optional, an explicit format string (d3 format, even for world calendars) + * tr: tickround ('y', 'm', 'd', 'M', 'S', or # digits) + * used if no explicit fmt is provided + * calendar: optional string, the world calendar system to use + * + * returns the date/time as a string, potentially with the leading portion + * on a separate line (after '\n') + * Note that this means if you provide an explicit format which includes '\n' + * the axis may choose to strip things after it when they don't change from + * one tick to the next (as it does with automatic formatting) + */ +exports.formatDate = function(x, fmt, tr, calendar) { + var headStr, + dateStr; + + calendar = isWorldCalendar(calendar) && calendar; + + if(fmt) return modDateFormat(fmt, x, calendar); + + if(calendar) { + var dateJD = Math.floor(x + 0.05 / ONEDAY) + EPOCHJD, + cDate = getCal(calendar).fromJD(dateJD); + + if(tr === 'y') dateStr = yearFormatWorld(cDate); + else if(tr === 'm') dateStr = monthFormatWorld(cDate); + else if(tr === 'd') { + headStr = yearFormatWorld(cDate); + dateStr = dayFormatWorld(cDate); + } + else { + headStr = yearMonthDayFormatWorld(cDate); + dateStr = formatTime(x, tr); + } + } + else { + var d = new Date(x); + + if(tr === 'y') dateStr = yearFormat(d); + else if(tr === 'm') dateStr = monthFormat(d); + else if(tr === 'd') { + headStr = yearFormat(d); + dateStr = dayFormat(d); + } + else { + headStr = yearMonthDayFormat(d); + dateStr = formatTime(x, tr); + } + } + + return dateStr + (headStr ? '\n' + headStr : ''); +}; diff --git a/src/lib/index.js b/src/lib/index.js index e8122b262b9..c6edfb4731b 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -16,6 +16,7 @@ var lib = module.exports = {}; lib.nestedProperty = require('./nested_property'); lib.isPlainObject = require('./is_plain_object'); lib.isArray = require('./is_array'); +lib.mod = require('./mod'); var coerceModule = require('./coerce'); lib.valObjects = coerceModule.valObjects; @@ -31,6 +32,7 @@ lib.ms2DateTime = datesModule.ms2DateTime; lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal; lib.cleanDate = datesModule.cleanDate; lib.isJSDate = datesModule.isJSDate; +lib.formatDate = datesModule.formatDate; lib.MIN_MS = datesModule.MIN_MS; lib.MAX_MS = datesModule.MAX_MS; diff --git a/src/lib/mod.js b/src/lib/mod.js new file mode 100644 index 00000000000..0ce9323b28f --- /dev/null +++ b/src/lib/mod.js @@ -0,0 +1,18 @@ +/** +* 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'; + +/** + * sanitized modulus function that always returns in the range [0, d) + * rather than (-d, 0] if v is negative + */ +module.exports = function mod(v, d) { + var out = v % d; + return out < 0 ? out + d : out; +}; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 1a5bd8d1fc0..ad3bdb8bb18 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1295,7 +1295,8 @@ function _restyle(gd, aobj, _traces) { 'domain.x[0]', 'domain.x[1]', 'domain.y[0]', 'domain.y[1]', 'tilt', 'tiltaxis', 'depth', 'direction', 'rotation', 'pull', 'line.showscale', 'line.cauto', 'line.autocolorscale', 'line.reversescale', - 'marker.line.showscale', 'marker.line.cauto', 'marker.line.autocolorscale', 'marker.line.reversescale' + 'marker.line.showscale', 'marker.line.cauto', 'marker.line.autocolorscale', 'marker.line.reversescale', + 'xcalendar', 'ycalendar' ]; for(i = 0; i < traces.length; i++) { @@ -2000,6 +2001,7 @@ function _relayout(gd, aobj) { p.parts[1] === 'rangemode' || p.parts[1] === 'type' || p.parts[1] === 'domain' || + ai.indexOf('calendar') !== -1 || ai.match(/^(bar|box|font)/)) { flags.docalc = true; } diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 9f9b7da42d6..b71156cf256 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -27,6 +27,7 @@ var ONEDAY = constants.ONEDAY; var ONEHOUR = constants.ONEHOUR; var ONEMIN = constants.ONEMIN; var ONESEC = constants.ONESEC; +var BADNUM = constants.BADNUM; var axes = module.exports = {}; @@ -121,7 +122,7 @@ axes.coercePosition = function(containerOut, gd, coerce, axRef, attr, dflt) { } } else if(ax.type === 'date') { - containerOut[attr] = Lib.cleanDate(pos); + containerOut[attr] = Lib.cleanDate(pos, BADNUM, ax.calendar); return; } } @@ -961,8 +962,8 @@ function autoTickRound(ax) { // not necessarily *all* the information in tick0 though, if it's really odd // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19 // take off a leading minus (year < 0 so length is consistent) - var tick0ms = Lib.dateTime2ms(ax.tick0), - tick0str = Lib.ms2DateTime(tick0ms).replace(/^-/, ''), + var tick0ms = ax.r2l(ax.tick0, 0, ax.calendar), + tick0str = ax.l2r(tick0ms, 0, ax.calendar).replace(/^-/, ''), tick0len = tick0str.length; if(String(dtick).charAt(0) === 'M') { @@ -975,9 +976,10 @@ function autoTickRound(ax) { else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M'; else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S'; else { + // tickround is a number of digits of fractional seconds // of any two adjacent ticks, at least one will have the maximum fractional digits // of all possible ticks - so take the max. length of tick0 and the next one - var tick1len = Lib.ms2DateTime(tick0ms + dtick).replace(/^-/, '').length; + var tick1len = ax.l2r(tick0ms + dtick, 0, ax.calendar).replace(/^-/, '').length; ax._tickround = Math.max(tick0len, tick1len) - 20; } } @@ -1047,7 +1049,7 @@ axes.tickIncrement = function(x, dtick, axrev) { else if(tType === 'D') { var tickset = (dtick === 'D2') ? roundLog2 : roundLog1, x2 = x + axSign * 0.01, - frac = Lib.roundUp(mod(x2, 1), tickset, axrev); + frac = Lib.roundUp(Lib.mod(x2, 1), tickset, axrev); return Math.floor(x2) + Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10; @@ -1105,7 +1107,7 @@ axes.tickFirst = function(ax) { } else if(tType === 'D') { var tickset = (dtick === 'D2') ? roundLog2 : roundLog1, - frac = Lib.roundUp(mod(r0, 1), tickset, axrev); + frac = Lib.roundUp(Lib.mod(r0, 1), tickset, axrev); return Math.floor(r0) + Math.log(d3.round(Math.pow(10, frac), 1)) / Math.LN10; @@ -1113,32 +1115,6 @@ axes.tickFirst = function(ax) { else throw 'unrecognized dtick ' + String(dtick); }; -var utcFormat = d3.time.format.utc, - yearFormat = utcFormat('%Y'), - monthFormat = utcFormat('%b %Y'), - dayFormat = utcFormat('%b %-d'), - yearMonthDayFormat = utcFormat('%b %-d, %Y'), - minuteFormat = utcFormat('%H:%M'), - secondFormat = utcFormat(':%S'); - -// add one item to d3's vocabulary: -// %{n}f where n is the max number of digits -// of fractional seconds -var fracMatch = /%(\d?)f/g; -function modDateFormat(fmt, x) { - var fm = fmt.match(fracMatch), - d = new Date(x); - if(fm) { - var digits = Math.min(+fm[1] || 6, 6), - fracSecs = String((x / 1000 % 1) + 2.0000005) - .substr(2, digits).replace(/0+$/, '') || '0'; - return utcFormat(fmt.replace(fracMatch, fracSecs))(d); - } - else { - return utcFormat(fmt)(d); - } -} - // draw the text for one tick. // px,py are the location on gd.paper // prefix is there so the x axis ticks can be dropped a line @@ -1208,76 +1184,59 @@ function tickTextObj(ax, x, text) { } function formatDate(ax, out, hover, extraPrecision) { - var x = out.x, - tr = ax._tickround, - d = new Date(x), - // headPart completes the full date info, to be included - // with only the first tick or if any info before what's - // shown has changed - headPart, - tt; - if(hover && ax.hoverformat) { - tt = modDateFormat(ax.hoverformat, x); - } - else if(ax.tickformat) { - tt = modDateFormat(ax.tickformat, x); - // TODO: potentially hunt for ways to automatically add more - // precision to the hover text? + var tr = ax._tickround, + fmt = (hover && ax.hoverformat) || ax.tickformat; + + if(extraPrecision) { + // second or sub-second precision: extra always shows max digits. + // for other fields, extra precision just adds one field. + if(isNumeric(tr)) tr = 4; + else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 4}[tr]; } - else { - if(extraPrecision) { - if(isNumeric(tr)) tr += 2; - else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 2}[tr]; - } - if(tr === 'y') tt = yearFormat(d); - else if(tr === 'm') tt = monthFormat(d); - else { - if(tr === 'd') { - headPart = yearFormat(d); - tt = dayFormat(d); - } - else { - headPart = yearMonthDayFormat(d); - - tt = minuteFormat(d); - if(tr !== 'M') { - tt += secondFormat(d); - if(tr !== 'S') { - tt += numFormat(d3.round(mod(x / 1000, 1), 4), ax, 'none', hover) - .substr(1); - } - } - } - } + var dateStr = Lib.formatDate(out.x, fmt, tr, ax.calendar), + headStr; + + var splitIndex = dateStr.indexOf('\n'); + if(splitIndex !== -1) { + headStr = dateStr.substr(splitIndex + 1); + dateStr = dateStr.substr(0, splitIndex); } - if(hover || ax.tickmode === 'array') { - // we get extra precision in array mode or hover, - // but it may be useless, strip it off - if(tt === '00:00:00' || tt === '00:00') { - tt = headPart; - headPart = ''; + + if(extraPrecision) { + // if extraPrecision led to trailing zeros, strip them off + // actually, this can lead to removing even more zeros than + // in the original rounding, but that's fine because in these + // contexts uniformity is not so important (if there's even + // anything to be uniform with!) + + // can we remove the whole time part? + if(dateStr === '00:00:00' || dateStr === '00:00') { + dateStr = headStr; + headStr = ''; } - else if(tt.length === 8) { + else if(dateStr.length === 8) { // strip off seconds if they're zero (zero fractional seconds // are already omitted) - tt = tt.replace(/:00$/, ''); + // but we never remove minutes and leave just hours + dateStr = dateStr.replace(/:00$/, ''); } } - if(headPart) { + if(headStr) { if(hover) { // hover puts it all on one line, so headPart works best up front // except for year headPart: turn this into "Jan 1, 2000" etc. - if(tr === 'd') tt += ', ' + headPart; - else tt = headPart + (tt ? ', ' + tt : ''); + if(tr === 'd') dateStr += ', ' + headStr; + else dateStr = headStr + (dateStr ? ', ' + dateStr : ''); } - else if(!ax._inCalcTicks || (headPart !== ax._prevDateHead)) { - tt += '
' + headPart; - ax._prevDateHead = headPart; + else if(!ax._inCalcTicks || (headStr !== ax._prevDateHead)) { + dateStr += '
' + headStr; + ax._prevDateHead = headStr; } } - out.text = tt; + + out.text = dateStr; } function formatLog(ax, out, hover, extraPrecision, hideexp) { @@ -1288,7 +1247,7 @@ function formatLog(ax, out, hover, extraPrecision, hideexp) { if(ax.tickformat || (typeof dtick === 'string' && dtick.charAt(0) === 'L')) { out.text = numFormat(Math.pow(10, x), ax, hideexp, extraPrecision); } - else if(isNumeric(dtick) || ((dtick.charAt(0) === 'D') && (mod(x + 0.01, 1) < 0.1))) { + else if(isNumeric(dtick) || ((dtick.charAt(0) === 'D') && (Lib.mod(x + 0.01, 1) < 0.1))) { if(['e', 'E', 'power'].indexOf(ax.exponentformat) !== -1) { var p = Math.round(x); if(p === 0) out.text = 1; @@ -1306,7 +1265,7 @@ function formatLog(ax, out, hover, extraPrecision, hideexp) { } } else if(dtick.charAt(0) === 'D') { - out.text = String(Math.round(Math.pow(10, mod(x, 1)))); + out.text = String(Math.round(Math.pow(10, Lib.mod(x, 1)))); out.fontSize *= 0.75; } else throw 'unrecognized dtick ' + String(dtick); @@ -2286,7 +2245,3 @@ function swapAxisAttrs(layout, key, xFullAxes, yFullAxes) { np(layout, yFullAxes[i]._name + '.' + key).set(xVal); } } - -// mod - version of modulus that always restricts to [0,divisor) -// rather than built-in % which gives a negative value for negative v -function mod(v, d) { return ((v % d) + d) % d; } diff --git a/src/plots/cartesian/axis_autotype.js b/src/plots/cartesian/axis_autotype.js index 00139887d28..7dec6b2fd52 100644 --- a/src/plots/cartesian/axis_autotype.js +++ b/src/plots/cartesian/axis_autotype.js @@ -14,8 +14,8 @@ var isNumeric = require('fast-isnumeric'); var Lib = require('../../lib'); var BADNUM = require('../../constants/numerical').BADNUM; -module.exports = function autoType(array) { - if(moreDates(array)) return 'date'; +module.exports = function autoType(array, calendar) { + if(moreDates(array, calendar)) return 'date'; if(category(array)) return 'category'; if(linearOK(array)) return 'linear'; else return '-'; @@ -38,7 +38,7 @@ function linearOK(array) { // 2- or 4-digit integers can be both, so require twice as many // dates as non-dates, to exclude cases with mostly 2 & 4 digit // numbers and a few dates -function moreDates(a) { +function moreDates(a, calendar) { var dcnt = 0, ncnt = 0, // test at most 1000 points, evenly spaced @@ -47,7 +47,7 @@ function moreDates(a) { for(var i = 0; i < a.length; i += inc) { ai = a[Math.round(i)]; - if(Lib.isDateTime(ai)) dcnt += 1; + if(Lib.isDateTime(ai, calendar)) dcnt += 1; if(isNumeric(ai)) ncnt += 1; } diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index 0bfdbde1ff2..aeed7c5e876 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -74,6 +74,8 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, } } + if(axType === 'date') coerce('calendar'); + setConvert(containerOut); var dfltColor = coerce('color'); @@ -166,6 +168,9 @@ function setAutoType(ax, data) { return; } + var calAttr = axLetter + 'calendar', + calendar = d0[calAttr]; + // check all boxes on this x axis to see // if they're dates, numbers, or categories if(isBoxWithoutPositionCoords(d0, axLetter)) { @@ -181,12 +186,14 @@ function setAutoType(ax, data) { if(trace[posLetter] !== undefined) boxPositions.push(trace[posLetter][0]); else if(trace.name !== undefined) boxPositions.push(trace.name); else boxPositions.push('text'); + + if(trace[calAttr] !== calendar) calendar = undefined; } - ax.type = autoType(boxPositions); + ax.type = autoType(boxPositions, calendar); } else { - ax.type = autoType(d0[axLetter] || [d0[axLetter + '0']]); + ax.type = autoType(d0[axLetter] || [d0[axLetter + '0']], calendar); } } diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index 630aa677855..bdcc2e42b31 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -52,6 +52,16 @@ module.exports = { 'the axis in question.' ].join(' ') }, + calendar: { + valType: 'calendar', + role: 'info', + description: [ + 'Sets the calendar system to use for `range` and `tick0`', + 'if this is a date axis. This does not set the calendar for', + 'interpreting data on this axis, that\'s specified in the trace', + 'or via the global `layout.calendar`' + ].join(' ') + }, autorange: { valType: 'enumerated', values: [true, false, 'reversed'], diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index 377c638ecf5..e5f074a8774 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -80,8 +80,8 @@ module.exports = function setConvert(ax) { ax.c2l = (ax.type === 'log') ? toLog : num; ax.l2c = (ax.type === 'log') ? fromLog : num; - ax.l2d = function(v) { return ax.c2d(ax.l2c(v)); }; - ax.p2d = function(v) { return ax.l2d(ax.p2l(v)); }; + ax.l2d = function(v) { return ax.c2d(ax.l2c(v), 0, ax.calendar); }; + ax.p2d = function(v) { return ax.l2d(ax.p2l(v), 0, ax.calendar); }; /* * fn to make sure range is a couplet of valid & distinct values @@ -95,6 +95,7 @@ module.exports = function setConvert(ax) { if(!rangeAttr) rangeAttr = 'range'; var range = ax[rangeAttr], axLetter = (ax._id || 'x').charAt(0), + calendar = ax.calendar, i, dflt; if(ax.type === 'date') dflt = constants.DFLTRANGEDATE; @@ -112,26 +113,23 @@ module.exports = function setConvert(ax) { if(ax.type === 'date') { // check if milliseconds or js date objects are provided for range // and convert to date strings - range[0] = Lib.cleanDate(range[0]); - range[1] = Lib.cleanDate(range[1]); + range[0] = Lib.cleanDate(range[0], BADNUM, calendar); + range[1] = Lib.cleanDate(range[1], BADNUM, calendar); } for(i = 0; i < 2; i++) { if(ax.type === 'date') { - if(!Lib.isDateTime(range[i])) { + if(!Lib.isDateTime(range[i], ax.calendar)) { ax[rangeAttr] = dflt; break; } - if(range[i] < Lib.MIN_MS) range[i] = Lib.MIN_MS; - if(range[i] > Lib.MAX_MS) range[i] = Lib.MAX_MS; - - if(ax.r2l(range[0]) === ax.r2l(range[1])) { + if(ax.r2l(range[0], calendar) === ax.r2l(range[1], calendar)) { // split by +/- 1 second - var linCenter = Lib.constrain(ax.r2l(range[0]), + var linCenter = Lib.constrain(ax.r2l(range[0], calendar), Lib.MIN_MS + 1000, Lib.MAX_MS - 1000); - range[0] = ax.l2r(linCenter - 1000); - range[1] = ax.l2r(linCenter + 1000); + range[0] = ax.l2r(linCenter - 1000, 0, calendar); + range[1] = ax.l2r(linCenter + 1000, 0, calendar); break; } } @@ -159,18 +157,22 @@ module.exports = function setConvert(ax) { } }; + // TODO (alexcjohnson): for now use the axis calendar for these (in case of dates) + // but that means annotations // find the range value at the specified (linear) fraction of the axis ax.fraction2r = function(v) { - var rl0 = ax.r2l(ax.range[0]), - rl1 = ax.r2l(ax.range[1]); - return ax.l2r(rl0 + v * (rl1 - rl0)); + var calendar = ax.calendar, + rl0 = ax.r2l(ax.range[0], calendar), + rl1 = ax.r2l(ax.range[1], calendar); + return ax.l2r(rl0 + v * (rl1 - rl0), BADNUM, calendar); }; // find the fraction of the range at the specified range value ax.r2fraction = function(v) { - var rl0 = ax.r2l(ax.range[0]), - rl1 = ax.r2l(ax.range[1]); - return (ax.r2l(v) - rl0) / (rl1 - rl0); + var calendar = ax.calendar, + rl0 = ax.r2l(ax.range[0], calendar), + rl1 = ax.r2l(ax.range[1], calendar); + return (ax.r2l(v, calendar) - rl0) / (rl1 - rl0); }; // set scaling to pixels @@ -192,11 +194,12 @@ module.exports = function setConvert(ax) { // issue if we transform the drawn layer *and* use the new axis range to // draw the data. This allows us to construct setConvert using the pre- // interaction values of the range: - var rangeAttr = (usePrivateRange && ax._r) ? '_r' : 'range'; + var rangeAttr = (usePrivateRange && ax._r) ? '_r' : 'range', + calendar = ax.calendar; ax.cleanRange(rangeAttr); - var rl0 = ax.r2l(ax[rangeAttr][0]), - rl1 = ax.r2l(ax[rangeAttr][1]); + var rl0 = ax.r2l(ax[rangeAttr][0], calendar), + rl1 = ax.r2l(ax[rangeAttr][1], calendar); if(axLetter === 'y') { ax._offset = gs.t + (1 - ax.domain[1]) * gs.h; @@ -261,12 +264,12 @@ module.exports = function setConvert(ax) { else if(ax.type === 'date') { ax.c2d = Lib.ms2DateTime; - ax.d2c = function(v) { + ax.d2c = function(v, cal) { // NOTE: Changed this behavior: previously we took any numeric value // to be a ms, even if it was a string that could be a bare year. // Now we convert it as a date if at all possible, and only try // as (local) ms if that fails. - var ms = Lib.dateTime2ms(v); + var ms = Lib.dateTime2ms(v, cal); if(ms === BADNUM) { if(isNumeric(v)) ms = Lib.dateTime2ms(new Date(v)); else return BADNUM; @@ -279,14 +282,14 @@ module.exports = function setConvert(ax) { ax.l2r = ax.c2d; ax.d2r = Lib.identity; ax.r2d = Lib.identity; - ax.cleanr = function(v) { + ax.cleanr = function(v, cal) { /* * If v is already a date string this is a noop, but running it * through d2c and back validates the value: * normalizes Date objects, milliseconds, and out-of-bounds dates * so we always end up with either a clean date string or BADNUM */ - return ax.c2d(ax.d2c(v)); + return ax.c2d(ax.d2c(v, cal), 0, cal); }; } else if(ax.type === 'category') { @@ -340,15 +343,19 @@ module.exports = function setConvert(ax) { ax.makeCalcdata = function(trace, axLetter) { var arrayIn, arrayOut, i; + var cal = ax.type === 'date' && trace[axLetter + 'calendar']; + if(axLetter in trace) { arrayIn = trace[axLetter]; arrayOut = new Array(arrayIn.length); - for(i = 0; i < arrayIn.length; i++) arrayOut[i] = ax.d2c(arrayIn[i]); + for(i = 0; i < arrayIn.length; i++) { + arrayOut[i] = ax.d2c(arrayIn[i], cal); + } } else { var v0 = ((axLetter + '0') in trace) ? - ax.d2c(trace[axLetter + '0']) : 0, + ax.d2c(trace[axLetter + '0'], cal) : 0, dv = (trace['d' + axLetter]) ? Number(trace['d' + axLetter]) : 1; diff --git a/src/plots/gl3d/layout/axis_attributes.js b/src/plots/gl3d/layout/axis_attributes.js index faad75ece28..de5839d830f 100644 --- a/src/plots/gl3d/layout/axis_attributes.js +++ b/src/plots/gl3d/layout/axis_attributes.js @@ -73,6 +73,7 @@ module.exports = { title: axesAttrs.title, titlefont: axesAttrs.titlefont, type: axesAttrs.type, + calendar: axesAttrs.calendar, autorange: axesAttrs.autorange, rangemode: axesAttrs.rangemode, range: axesAttrs.range, diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index f98ffc9606f..5ab35397ec4 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -170,7 +170,14 @@ module.exports = { role: 'info', description: 'Determines whether or not a legend is drawn.' }, - + calendar: { + valType: 'calendar', + role: 'info', + description: [ + 'Sets the default calendar system to use for interpreting and', + 'displaying dates throughout the plot.' + ].join(' ') + }, dragmode: { valType: 'enumerated', role: 'info', diff --git a/src/plots/plots.js b/src/plots/plots.js index 3ad048addd5..dadee1b553c 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -941,6 +941,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) { coerce('separators'); coerce('hidesources'); coerce('smith'); + + coerce('calendar'); }; plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) { diff --git a/src/traces/bar/attributes.js b/src/traces/bar/attributes.js index bba0d30611f..a5843177880 100644 --- a/src/traces/bar/attributes.js +++ b/src/traces/bar/attributes.js @@ -47,6 +47,8 @@ module.exports = { y: scatterAttrs.y, y0: scatterAttrs.y0, dy: scatterAttrs.dy, + xcalendar: scatterAttrs.xcalendar, + ycalendar: scatterAttrs.ycalendar, text: scatterAttrs.text, diff --git a/src/traces/bar/defaults.js b/src/traces/bar/defaults.js index e907b40ad06..ada3fe7116b 100644 --- a/src/traces/bar/defaults.js +++ b/src/traces/bar/defaults.js @@ -25,7 +25,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var coerceFont = Lib.coerceFont; - var len = handleXYDefaults(traceIn, traceOut, coerce); + var len = handleXYDefaults(traceIn, traceOut, layout, coerce); if(!len) { traceOut.visible = false; return; diff --git a/src/traces/box/attributes.js b/src/traces/box/attributes.js index c01f7914850..41d7170f7bf 100644 --- a/src/traces/box/attributes.js +++ b/src/traces/box/attributes.js @@ -47,6 +47,8 @@ module.exports = { 'See overview for more info.' ].join(' ') }, + xcalendar: scatterAttrs.xcalendar, + ycalendar: scatterAttrs.ycalendar, whiskerwidth: { valType: 'number', min: 0, diff --git a/src/traces/box/calc.js b/src/traces/box/calc.js index cafd8eb16c7..481a5a2ea67 100644 --- a/src/traces/box/calc.js +++ b/src/traces/box/calc.js @@ -62,7 +62,7 @@ module.exports = function calc(gd, trace) { pos0 = trace.name; } else pos0 = gd.numboxes; - pos0 = posAxis.d2c(pos0); + pos0 = posAxis.d2c(pos0, trace[posLetter + 'calendar']); pos = val.map(function() { return pos0; }); } return pos; diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js index 61d9653d8a9..7f75a66d4eb 100644 --- a/src/traces/box/defaults.js +++ b/src/traces/box/defaults.js @@ -13,7 +13,7 @@ var Color = require('../../components/color'); var attributes = require('./attributes'); -module.exports = function supplyDefaults(traceIn, traceOut, defaultColor) { +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } @@ -33,6 +33,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor) { return; } + var dfltCalendar = layout.calendar; + coerce('xcalendar', dfltCalendar); + coerce('ycalendar', dfltCalendar); + coerce('orientation', defaultOrientation); coerce('line.color', (traceIn.marker || {}).color || defaultColor); diff --git a/src/traces/candlestick/attributes.js b/src/traces/candlestick/attributes.js index 64b7be83203..f2b61402251 100644 --- a/src/traces/candlestick/attributes.js +++ b/src/traces/candlestick/attributes.js @@ -27,6 +27,7 @@ var directionAttrs = { module.exports = { x: OHLCattrs.x, + xcalendar: OHLCattrs.xcalendar, open: OHLCattrs.open, high: OHLCattrs.high, low: OHLCattrs.low, diff --git a/src/traces/candlestick/defaults.js b/src/traces/candlestick/defaults.js index c6b4538ea53..6668cd15cd0 100644 --- a/src/traces/candlestick/defaults.js +++ b/src/traces/candlestick/defaults.js @@ -15,14 +15,14 @@ var handleDirectionDefaults = require('../ohlc/direction_defaults'); var helpers = require('../ohlc/helpers'); var attributes = require('./attributes'); -module.exports = function supplyDefaults(traceIn, traceOut) { +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { helpers.pushDummyTransformOpts(traceIn, traceOut); function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var len = handleOHLC(traceIn, traceOut, coerce); + var len = handleOHLC(traceIn, traceOut, coerce, layout); if(len === 0) { traceOut.visible = false; return; diff --git a/src/traces/candlestick/transform.js b/src/traces/candlestick/transform.js index 6d32870e3ce..f89dd7a46a4 100644 --- a/src/traces/candlestick/transform.js +++ b/src/traces/candlestick/transform.js @@ -70,6 +70,7 @@ function makeTrace(traceIn, state, direction) { // to make autotype catch date axes soon!! x: traceIn.x || [0], + xcalendar: traceIn.xcalendar, // concat low and high to get correct autorange y: [].concat(traceIn.low).concat(traceIn.high), diff --git a/src/traces/contour/attributes.js b/src/traces/contour/attributes.js index 9849d3f06bb..9d776e00991 100644 --- a/src/traces/contour/attributes.js +++ b/src/traces/contour/attributes.js @@ -28,6 +28,8 @@ module.exports = extendFlat({}, { transpose: heatmapAttrs.transpose, xtype: heatmapAttrs.xtype, ytype: heatmapAttrs.ytype, + xcalendar: heatmapAttrs.xcalendar, + ycalendar: heatmapAttrs.ycalendar, connectgaps: heatmapAttrs.connectgaps, diff --git a/src/traces/heatmap/attributes.js b/src/traces/heatmap/attributes.js index da86b34d004..19be6407e11 100644 --- a/src/traces/heatmap/attributes.js +++ b/src/traces/heatmap/attributes.js @@ -25,6 +25,9 @@ module.exports = extendFlat({}, { y: scatterAttrs.y, y0: scatterAttrs.y0, dy: scatterAttrs.dy, + xcalendar: scatterAttrs.xcalendar, + ycalendar: scatterAttrs.ycalendar, + text: { valType: 'data_array', description: 'Sets the text elements associated with each z value.' diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index 29d878f5aee..1abc4e0db28 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -125,9 +125,13 @@ module.exports = function calc(gd, trace) { colorscaleCalc(trace, z, '', 'z'); if(isContour && trace.contours && trace.contours.coloring === 'heatmap') { - var hmType = trace.type === 'contour' ? 'heatmap' : 'histogram2d'; - cd0.xfill = makeBoundArray(hmType, xIn, x0, dx, xlen, xa); - cd0.yfill = makeBoundArray(hmType, yIn, y0, dy, z.length, ya); + var dummyTrace = { + type: trace.type === 'contour' ? 'heatmap' : 'histogram2d', + xcalendar: trace.xcalendar, + ycalendar: trace.ycalendar + }; + cd0.xfill = makeBoundArray(dummyTrace, xIn, x0, dx, xlen, xa); + cd0.yfill = makeBoundArray(dummyTrace, yIn, y0, dy, z.length, ya); } return [cd0]; @@ -222,10 +226,12 @@ function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, ax) { else { dv = dvIn || 1; - if(isHist || ax.type === 'category') v0 = ax.r2c(v0In) || 0; + var calendar = trace[ax._id.charAt(0) + 'calendar']; + + if(isHist || ax.type === 'category') v0 = ax.r2c(v0In, calendar) || 0; else if(Array.isArray(arrayIn) && arrayIn.length === 1) v0 = arrayIn[0]; else if(v0In === undefined) v0 = 0; - else v0 = ax.d2c(v0In); + else v0 = ax.d2c(v0In, calendar); for(i = (isContour || isGL2D) ? 0 : -0.5; i < numbricks; i++) { arrayOut.push(v0 + dv * i); diff --git a/src/traces/heatmap/convert_column_xyz.js b/src/traces/heatmap/convert_column_xyz.js index 1f18b6a5474..e3f39f2b227 100644 --- a/src/traces/heatmap/convert_column_xyz.js +++ b/src/traces/heatmap/convert_column_xyz.js @@ -26,8 +26,8 @@ module.exports = function convertColumnXYZ(trace, xa, ya) { if(colLen < yCol.length) yCol = yCol.slice(0, colLen); for(i = 0; i < colLen; i++) { - xCol[i] = xa.d2c(xCol[i]); - yCol[i] = ya.d2c(yCol[i]); + xCol[i] = xa.d2c(xCol[i], trace.xcalendar); + yCol[i] = ya.d2c(yCol[i], trace.ycalendar); } var xColdv = Lib.distinctVals(xCol), diff --git a/src/traces/heatmap/xyz_defaults.js b/src/traces/heatmap/xyz_defaults.js index abc9d03f08f..c6d55739730 100644 --- a/src/traces/heatmap/xyz_defaults.js +++ b/src/traces/heatmap/xyz_defaults.js @@ -37,6 +37,9 @@ module.exports = function handleXYZDefaults(traceIn, traceOut, coerce) { coerce('transpose'); } + coerce('xcalendar'); + coerce('ycalendar'); + return traceOut.z.length; }; diff --git a/src/traces/histogram/attributes.js b/src/traces/histogram/attributes.js index b455fd4b98a..53ccddb1184 100644 --- a/src/traces/histogram/attributes.js +++ b/src/traces/histogram/attributes.js @@ -24,6 +24,8 @@ module.exports = { 'Sets the sample data to be binned on the y axis.' ].join(' ') }, + xcalendar: barAttrs.xcalendar, + ycalendar: barAttrs.ycalendar, text: barAttrs.text, orientation: barAttrs.orientation, diff --git a/src/traces/histogram/calc.js b/src/traces/histogram/calc.js index 5248979368a..ece6f851f75 100644 --- a/src/traces/histogram/calc.js +++ b/src/traces/histogram/calc.js @@ -64,7 +64,8 @@ module.exports = function calc(gd, trace) { binfunc = binFunctions.count, normfunc = normFunctions[norm], doavg = false, - rawCounterData; + rawCounterData, + calendar = trace[maindata + 'calendar']; if(Array.isArray(trace[counterdata]) && func !== 'count') { rawCounterData = trace[counterdata]; @@ -74,7 +75,7 @@ module.exports = function calc(gd, trace) { // create the bins (and any extra arrays needed) // assume more than 5000 bins is an error, so we don't crash the browser - i = pa.r2c(binspec.start); + i = pa.r2c(binspec.start, calendar); // decrease end a little in case of rounding errors binend = pa.r2c(binspec.end) + (i - Axes.tickIncrement(i, binspec.size)) / 1e6; @@ -96,8 +97,8 @@ module.exports = function calc(gd, trace) { // we already have this, but uniform with start/end/size they're still strings. if(!nonuniformBins && pa.type === 'date') { bins = { - start: pa.r2c(bins.start), - end: pa.r2c(bins.end), + start: pa.r2c(bins.start, calendar), + end: pa.r2c(bins.end, calendar), size: bins.size }; } diff --git a/src/traces/histogram/clean_bins.js b/src/traces/histogram/clean_bins.js index d6c4d00e5d7..d23e6383a3e 100644 --- a/src/traces/histogram/clean_bins.js +++ b/src/traces/histogram/clean_bins.js @@ -10,7 +10,9 @@ 'use strict'; var isNumeric = require('fast-isnumeric'); var cleanDate = require('../../lib').cleanDate; -var ONEDAY = require('../../constants/numerical').ONEDAY; +var constants = require('../../constants/numerical'); +var ONEDAY = constants.ONEDAY; +var BADNUM = constants.BADNUM; /* * cleanBins: validate attributes autobin[xy] and [xy]bins.(start, end, size) @@ -29,7 +31,7 @@ module.exports = function cleanBins(trace, ax, binDirection) { if(!bins) bins = trace[binAttr] = {}; var cleanBound = (axType === 'date') ? - function(v) { return (v || v === 0) ? cleanDate(v) : null; } : + function(v) { return (v || v === 0) ? cleanDate(v, BADNUM, bins.calendar) : null; } : function(v) { return isNumeric(v) ? Number(v) : null; }; bins.start = cleanBound(bins.start); diff --git a/src/traces/histogram/defaults.js b/src/traces/histogram/defaults.js index a415ff00797..68ddc1f1d76 100644 --- a/src/traces/histogram/defaults.js +++ b/src/traces/histogram/defaults.js @@ -26,6 +26,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var x = coerce('x'), y = coerce('y'); + coerce('xcalendar'); + coerce('ycalendar'); + coerce('text'); var orientation = coerce('orientation', (y && !x) ? 'h' : 'v'), diff --git a/src/traces/histogram2d/attributes.js b/src/traces/histogram2d/attributes.js index c6c6d87445a..3a69e20041e 100644 --- a/src/traces/histogram2d/attributes.js +++ b/src/traces/histogram2d/attributes.js @@ -19,6 +19,8 @@ module.exports = extendFlat({}, { x: histogramAttrs.x, y: histogramAttrs.y, + xcalendar: histogramAttrs.xcalendar, + ycalendar: histogramAttrs.ycalendar, z: { valType: 'data_array', diff --git a/src/traces/histogram2d/sample_defaults.js b/src/traces/histogram2d/sample_defaults.js index b6197d490f7..f804ccd2dcb 100644 --- a/src/traces/histogram2d/sample_defaults.js +++ b/src/traces/histogram2d/sample_defaults.js @@ -16,6 +16,9 @@ module.exports = function handleSampleDefaults(traceIn, traceOut, coerce) { var x = coerce('x'), y = coerce('y'); + coerce('xcalendar'); + coerce('ycalendar'); + // we could try to accept x0 and dx, etc... // but that's a pretty weird use case. // for now require both x and y explicitly specified. diff --git a/src/traces/histogram2dcontour/attributes.js b/src/traces/histogram2dcontour/attributes.js index 49a1ca72596..995e5b9d9bf 100644 --- a/src/traces/histogram2dcontour/attributes.js +++ b/src/traces/histogram2dcontour/attributes.js @@ -18,6 +18,8 @@ var extendFlat = require('../../lib/extend').extendFlat; module.exports = extendFlat({}, { x: histogram2dAttrs.x, y: histogram2dAttrs.y, + xcalendar: histogram2dAttrs.xcalendar, + ycalendar: histogram2dAttrs.ycalendar, z: histogram2dAttrs.z, marker: histogram2dAttrs.marker, diff --git a/src/traces/ohlc/attributes.js b/src/traces/ohlc/attributes.js index b4168829c97..58551eed920 100644 --- a/src/traces/ohlc/attributes.js +++ b/src/traces/ohlc/attributes.js @@ -53,6 +53,7 @@ module.exports = { 'If absent, linear coordinate will be generated.' ].join(' ') }, + xcalendar: scatterAttrs.xcalendar, open: { valType: 'data_array', diff --git a/src/traces/ohlc/defaults.js b/src/traces/ohlc/defaults.js index 791535de251..cf1a32ed970 100644 --- a/src/traces/ohlc/defaults.js +++ b/src/traces/ohlc/defaults.js @@ -15,14 +15,14 @@ var handleDirectionDefaults = require('./direction_defaults'); var attributes = require('./attributes'); var helpers = require('./helpers'); -module.exports = function supplyDefaults(traceIn, traceOut) { +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { helpers.pushDummyTransformOpts(traceIn, traceOut); function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var len = handleOHLC(traceIn, traceOut, coerce); + var len = handleOHLC(traceIn, traceOut, coerce, layout); if(len === 0) { traceOut.visible = false; return; diff --git a/src/traces/ohlc/ohlc_defaults.js b/src/traces/ohlc/ohlc_defaults.js index 33e335f4188..51ec3a2cba1 100644 --- a/src/traces/ohlc/ohlc_defaults.js +++ b/src/traces/ohlc/ohlc_defaults.js @@ -9,7 +9,7 @@ 'use strict'; -module.exports = function handleOHLC(traceIn, traceOut, coerce) { +module.exports = function handleOHLC(traceIn, traceOut, coerce, layout) { var len; var x = coerce('x'), @@ -18,6 +18,8 @@ module.exports = function handleOHLC(traceIn, traceOut, coerce) { low = coerce('low'), close = coerce('close'); + coerce('xcalendar', layout.calendar); + len = Math.min(open.length, high.length, low.length, close.length); if(x) { diff --git a/src/traces/ohlc/transform.js b/src/traces/ohlc/transform.js index 93ea567cea9..fc0e5bbc946 100644 --- a/src/traces/ohlc/transform.js +++ b/src/traces/ohlc/transform.js @@ -73,6 +73,7 @@ function makeTrace(traceIn, state, direction) { // to make autotype catch date axes soon!! x: traceIn.x || [0], + xcalendar: traceIn.xcalendar, // concat low and high to get correct autorange y: [].concat(traceIn.low).concat(traceIn.high), diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 1a728047d4d..f23484bac8e 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -41,8 +41,14 @@ module.exports = { 'See `x0` for more info.' ].join(' ') }, + xcalendar: { + valType: 'calendar', + role: 'info', + description: 'Sets the calendar system to use with `x` date data' + }, y: { valType: 'data_array', + role: 'info', description: 'Sets the y coordinates.' }, y0: { @@ -65,6 +71,11 @@ module.exports = { 'See `y0` for more info.' ].join(' ') }, + ycalendar: { + valType: 'calendar', + role: 'info', + description: 'Sets the calendar system to use with `y` date data' + }, ids: { valType: 'data_array', description: 'A list of keys for object constancy of data points during animation' diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index 115ef4c757e..c5fc7259dc8 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -28,7 +28,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var len = handleXYDefaults(traceIn, traceOut, coerce), + var len = handleXYDefaults(traceIn, traceOut, layout, coerce), // TODO: default mode by orphan points... defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; if(!len) { diff --git a/src/traces/scatter/xy_defaults.js b/src/traces/scatter/xy_defaults.js index d85a0aea2b1..15bfa324cf3 100644 --- a/src/traces/scatter/xy_defaults.js +++ b/src/traces/scatter/xy_defaults.js @@ -10,11 +10,15 @@ 'use strict'; -module.exports = function handleXYDefaults(traceIn, traceOut, coerce) { +module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { var len, x = coerce('x'), y = coerce('y'); + var dfltCalendar = layout.calendar; + coerce('xcalendar', dfltCalendar); + coerce('ycalendar', dfltCalendar); + if(x) { if(y) { len = Math.min(x.length, y.length); diff --git a/src/traces/scatter3d/attributes.js b/src/traces/scatter3d/attributes.js index 5208068b5de..44c6a3d83c9 100644 --- a/src/traces/scatter3d/attributes.js +++ b/src/traces/scatter3d/attributes.js @@ -65,6 +65,10 @@ module.exports = { valType: 'data_array', description: 'Sets the z coordinates.' }, + xcalendar: scatterAttrs.xcalendar, + ycalendar: scatterAttrs.ycalendar, + zcalendar: scatterAttrs.zcalendar, + text: extendFlat({}, scatterAttrs.text, { description: [ 'Sets text elements associated with each (x,y,z) triplet.', diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js index 55c8b2681a8..f6e736f6ad5 100644 --- a/src/traces/scattergl/attributes.js +++ b/src/traces/scattergl/attributes.js @@ -27,6 +27,9 @@ module.exports = { y: scatterAttrs.y, y0: scatterAttrs.y0, dy: scatterAttrs.dy, + xcalendar: scatterAttrs.xcalendar, + ycalendar: scatterAttrs.ycalendar, + text: extendFlat({}, scatterAttrs.text, { description: [ 'Sets text elements associated with each (x,y) pair to appear on hover.', diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 60725bb1aa8..3ce15b2e7fc 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -298,8 +298,10 @@ proto.updateFast = function(options) { var xx, yy; + var xcalendar = options.xcalendar; + var fastType = allFastTypesLikely(x); - var isDateTime = !fastType && autoType(x) === 'date'; + var isDateTime = !fastType && autoType(x, xcalendar) === 'date'; // TODO add 'very fast' mode that bypasses this loop // TODO bypass this on modebar +/- zoom @@ -312,7 +314,7 @@ proto.updateFast = function(options) { if(isNumeric(yy)) { if(!fastType) { - xx = Lib.dateTime2ms(xx); + xx = Lib.dateTime2ms(xx, xcalendar); } idToIndex[pId++] = i; diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js index b1a8effb015..3d15c0d0a3e 100644 --- a/src/traces/scattergl/defaults.js +++ b/src/traces/scattergl/defaults.js @@ -27,7 +27,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var len = handleXYDefaults(traceIn, traceOut, coerce); + var len = handleXYDefaults(traceIn, traceOut, layout, coerce); if(!len) { traceOut.visible = false; return; diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js index 7bd693a1894..fa2bf9a17ed 100644 --- a/src/traces/surface/attributes.js +++ b/src/traces/surface/attributes.js @@ -11,6 +11,7 @@ var Color = require('../../components/color'); var colorscaleAttrs = require('../../components/colorscale/attributes'); var colorbarAttrs = require('../../components/colorbar/attributes'); +var scatterAttrs = require('../scatter/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -109,6 +110,10 @@ module.exports = { valType: 'data_array', description: 'Sets the y coordinates.' }, + xcalendar: scatterAttrs.xcalendar, + ycalendar: scatterAttrs.ycalendar, + zcalendar: scatterAttrs.zcalendar, + text: { valType: 'data_array', description: 'Sets the text elements associated with each z value.' diff --git a/src/traces/surface/convert.js b/src/traces/surface/convert.js index 2b3fbbda9ee..fdea7f9599d 100644 --- a/src/traces/surface/convert.js +++ b/src/traces/surface/convert.js @@ -213,31 +213,36 @@ proto.update = function(data) { * and that the sub-array entries correspond to a x-coords, * which is the transpose of 'gl-surface-plot'. */ + + var xcalendar = data.xcalendar, + ycalendar = data.ycalendar, + zcalendar = data.zcalendar; + fill(coords[2], function(row, col) { - return zaxis.d2l(z[col][row]) * scaleFactor[2]; + return zaxis.d2l(z[col][row], zcalendar) * scaleFactor[2]; }); // coords x if(Array.isArray(x[0])) { fill(xc, function(row, col) { - return xaxis.d2l(x[col][row]) * scaleFactor[0]; + return xaxis.d2l(x[col][row], xcalendar) * scaleFactor[0]; }); } else { // ticks x fill(xc, function(row) { - return xaxis.d2l(x[row]) * scaleFactor[0]; + return xaxis.d2l(x[row], xcalendar) * scaleFactor[0]; }); } // coords y if(Array.isArray(y[0])) { fill(yc, function(row, col) { - return yaxis.d2l(y[col][row]) * scaleFactor[1]; + return yaxis.d2l(y[col][row], ycalendar) * scaleFactor[1]; }); } else { // ticks y fill(yc, function(row, col) { - return yaxis.d2l(y[col]) * scaleFactor[1]; + return yaxis.d2l(y[col], ycalendar) * scaleFactor[1]; }); } diff --git a/src/transforms/filter.js b/src/transforms/filter.js index b1c6d704b2e..bef0cb540a9 100644 --- a/src/transforms/filter.js +++ b/src/transforms/filter.js @@ -100,6 +100,16 @@ exports.attributes = { '*value* is expected to be an array with as many items as', 'the desired set elements.' ].join(' ') + }, + calendar: { + valType: 'calendar', + role: 'info', + description: [ + 'Sets the calendar system to use for `value`, if it is a date.', + 'Note that this is not necessarily the same calendar as is used', + 'for the target data; that is set by its own calendar attribute,', + 'ie `trace.x` uses `trace.xcalendar` etc.' + ].join(' ') } }; @@ -116,6 +126,7 @@ exports.supplyDefaults = function(transformIn) { coerce('operation'); coerce('value'); coerce('target'); + coerce('calendar'); } return transformOut; @@ -130,8 +141,9 @@ exports.calcTransform = function(gd, trace, opts) { if(!len) return; - var dataToCoord = getDataToCoordFunc(gd, trace, target), - filterFunc = getFilterFunc(opts, dataToCoord), + var targetCalendar = Lib.nestedProperty(trace, target + 'calendar').get(), + dataToCoord = getDataToCoordFunc(gd, trace, target), + filterFunc = getFilterFunc(opts, dataToCoord, targetCalendar), arrayAttrs = PlotSchema.findArrayAttributes(trace), originalArrays = {}; @@ -188,9 +200,11 @@ function getDataToCoordFunc(gd, trace, target) { setConvert(ax); - // build up ax._categories (usually done during ax.makeCalcdata() - for(var i = 0; i < target.length; i++) { - ax.d2c(target[i]); + if(ax.type === 'category') { + // build up ax._categories (usually done during ax.makeCalcdata() + for(var i = 0; i < target.length; i++) { + ax.d2c(target[i]); + } } } else { @@ -210,7 +224,7 @@ function getDataToCoordFunc(gd, trace, target) { return function(v) { return +v; }; } -function getFilterFunc(opts, d2c) { +function getFilterFunc(opts, d2c, targetCalendar) { var operation = opts.operation, value = opts.value, hasArrayValue = Array.isArray(value); @@ -219,93 +233,96 @@ function getFilterFunc(opts, d2c) { return array.indexOf(operation) !== -1; } + var d2cValue = opts.calendar ? function(v) { return d2c(v, opts.calendar); } : d2c, + d2cTarget = targetCalendar ? function(v) { return d2c(v, targetCalendar); } : d2c; + var coercedValue; if(isOperationIn(INEQUALITY_OPS)) { - coercedValue = hasArrayValue ? d2c(value[0]) : d2c(value); + coercedValue = hasArrayValue ? d2cValue(value[0]) : d2cValue(value); } else if(isOperationIn(INTERVAL_OPS)) { coercedValue = hasArrayValue ? - [d2c(value[0]), d2c(value[1])] : - [d2c(value), d2c(value)]; + [d2cValue(value[0]), d2cValue(value[1])] : + [d2cValue(value), d2cValue(value)]; } else if(isOperationIn(SET_OPS)) { - coercedValue = hasArrayValue ? value.map(d2c) : [d2c(value)]; + coercedValue = hasArrayValue ? value.map(d2cValue) : [d2cValue(value)]; } switch(operation) { case '=': - return function(v) { return d2c(v) === coercedValue; }; + return function(v) { return d2cTarget(v) === coercedValue; }; case '<': - return function(v) { return d2c(v) < coercedValue; }; + return function(v) { return d2cTarget(v) < coercedValue; }; case '<=': - return function(v) { return d2c(v) <= coercedValue; }; + return function(v) { return d2cTarget(v) <= coercedValue; }; case '>': - return function(v) { return d2c(v) > coercedValue; }; + return function(v) { return d2cTarget(v) > coercedValue; }; case '>=': - return function(v) { return d2c(v) >= coercedValue; }; + return function(v) { return d2cTarget(v) >= coercedValue; }; case '[]': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv >= coercedValue[0] && cv <= coercedValue[1]; }; case '()': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv > coercedValue[0] && cv < coercedValue[1]; }; case '[)': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv >= coercedValue[0] && cv < coercedValue[1]; }; case '(]': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv > coercedValue[0] && cv <= coercedValue[1]; }; case '][': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv <= coercedValue[0] || cv >= coercedValue[1]; }; case ')(': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv < coercedValue[0] || cv > coercedValue[1]; }; case '](': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv <= coercedValue[0] || cv > coercedValue[1]; }; case ')[': return function(v) { - var cv = d2c(v); + var cv = d2cTarget(v); return cv < coercedValue[0] || cv >= coercedValue[1]; }; case '{}': return function(v) { - return coercedValue.indexOf(d2c(v)) !== -1; + return coercedValue.indexOf(d2cTarget(v)) !== -1; }; case '}{': return function(v) { - return coercedValue.indexOf(d2c(v)) === -1; + return coercedValue.indexOf(d2cTarget(v)) === -1; }; } } diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js index 75f8bc01926..562cb0ad675 100644 --- a/test/jasmine/tests/bar_test.js +++ b/test/jasmine/tests/bar_test.js @@ -27,14 +27,14 @@ describe('Bar.supplyDefaults', function() { it('should set visible to false when x and y are empty', function() { traceIn = {}; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); traceIn = { x: [], y: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); }); @@ -42,27 +42,27 @@ describe('Bar.supplyDefaults', function() { traceIn = { x: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); traceIn = { x: [], y: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); traceIn = { y: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); traceIn = { x: [1, 2, 3], y: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); }); @@ -70,7 +70,7 @@ describe('Bar.supplyDefaults', function() { traceIn = { y: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.base).toBeUndefined(); expect(traceOut.offset).toBeUndefined(); expect(traceOut.width).toBeUndefined(); @@ -81,7 +81,7 @@ describe('Bar.supplyDefaults', function() { width: -1, y: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.width).toBeUndefined(); }); @@ -89,7 +89,7 @@ describe('Bar.supplyDefaults', function() { traceIn = { y: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.textposition).toBe('none'); expect(traceOut.texfont).toBeUndefined(); expect(traceOut.insidetexfont).toBeUndefined(); @@ -106,7 +106,7 @@ describe('Bar.supplyDefaults', function() { font: {family: 'arial', color: '#AAA', size: 13} }; - supplyDefaults(traceIn, traceOut, defaultColor, layout); + supplyDefaults(traceIn, traceOut, defaultColor, layout, {}); expect(traceOut.textposition).toBe('inside'); expect(traceOut.textfont).toEqual(layout.font); diff --git a/test/jasmine/tests/box_test.js b/test/jasmine/tests/box_test.js index 5c357f26ad5..829f6343d4e 100644 --- a/test/jasmine/tests/box_test.js +++ b/test/jasmine/tests/box_test.js @@ -25,7 +25,7 @@ describe('Test boxes', function() { x: [], y: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); }); @@ -33,27 +33,27 @@ describe('Test boxes', function() { traceIn = { x: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); traceIn = { x: [], y: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); traceIn = { y: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); traceIn = { x: [1, 2, 3], y: [] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.visible).toBe(false); }); @@ -61,14 +61,14 @@ describe('Test boxes', function() { traceIn = { y: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.orientation).toBe('v'); traceIn = { x: [1, 1, 1], y: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.orientation).toBe('v'); }); @@ -76,7 +76,7 @@ describe('Test boxes', function() { traceIn = { x: [1, 2, 3] }; - supplyDefaults(traceIn, traceOut, defaultColor); + supplyDefaults(traceIn, traceOut, defaultColor, {}); expect(traceOut.orientation).toBe('h'); }); diff --git a/test/jasmine/tests/histogram_test.js b/test/jasmine/tests/histogram_test.js index 2fd1140df7e..60898c9295a 100644 --- a/test/jasmine/tests/histogram_test.js +++ b/test/jasmine/tests/histogram_test.js @@ -20,13 +20,13 @@ describe('Test histogram', function() { traceIn = { x: [] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.visible).toBe(false); traceIn = { y: [] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.visible).toBe(false); }); @@ -36,7 +36,7 @@ describe('Test histogram', function() { x: [], y: [1, 2, 2] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.visible).toBe(false); traceIn = { @@ -44,7 +44,7 @@ describe('Test histogram', function() { x: [1, 2, 2], y: [] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.visible).toBe(false); traceIn = { @@ -52,7 +52,7 @@ describe('Test histogram', function() { x: [], y: [] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.visible).toBe(false); traceIn = { @@ -60,7 +60,7 @@ describe('Test histogram', function() { x: [], y: [1, 2, 2] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.visible).toBe(false); }); @@ -68,14 +68,14 @@ describe('Test histogram', function() { traceIn = { x: [1, 2, 2] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.orientation).toBe('v'); traceIn = { x: [1, 2, 2], y: [1, 2, 2] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.orientation).toBe('v'); }); @@ -83,7 +83,7 @@ describe('Test histogram', function() { traceIn = { y: [1, 2, 2] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.orientation).toBe('h'); }); @@ -100,13 +100,13 @@ describe('Test histogram', function() { size: 1 } }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.autobinx).toBeUndefined(); traceIn = { x: [1, 2, 2] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.autobinx).toBeUndefined(); }); @@ -119,13 +119,13 @@ describe('Test histogram', function() { size: 1 } }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.autobiny).toBeUndefined(); traceIn = { y: [1, 2, 2] }; - supplyDefaults(traceIn, traceOut); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.autobiny).toBeUndefined(); }); diff --git a/test/jasmine/tests/lib_date_test.js b/test/jasmine/tests/lib_date_test.js index a73f79c7b25..b7b43a37ee1 100644 --- a/test/jasmine/tests/lib_date_test.js +++ b/test/jasmine/tests/lib_date_test.js @@ -222,6 +222,68 @@ describe('dates', function() { expect(Lib.ms2DateTime(Lib.dateTime2ms(v[0]), v[1])).toBe(v[2], v); }); }); + + it('should work right with inputs beyond our precision', function() { + for(var i = -1; i <= 1; i += 0.001) { + var tenths = Math.round(i * 10), + base = i < -0.05 ? '1969-12-31 23:59:59.99' : '1970-01-01 00:00:00.00', + expected = (base + String(tenths + 200).substr(1)) + .replace(/0+$/, '') + .replace(/ 00:00:00[\.]$/, ''); + expect(Lib.ms2DateTime(i)).toBe(expected, i); + } + }); + }); + + describe('world calendar inputs', function() { + it('should give the right values near epoch zero', function() { + [ + [undefined, '1970-01-01'], + ['gregorian', '1970-01-01'], + ['coptic', '1686-04-23'], + ['discworld', '1798-12-27'], + ['ethiopian', '1962-04-23'], + ['hebrew', '5730-10-23'], + ['islamic', '1389-10-22'], + ['julian', '1969-12-19'], + ['mayan', '5156-07-05'], + ['nanakshahi', '0501-10-19'], + ['nepali', '2026-09-17'], + ['persian', '1348-10-11'], + ['jalali', '1348-10-11'], + ['taiwan', '0059-01-01'], + ['thai', '2513-01-01'], + ['ummalqura', '1389-10-23'] + ].forEach(function(v) { + var calendar = v[0], + dateStr = v[1]; + expect(Lib.ms2DateTime(0, 0, calendar)).toBe(dateStr, calendar); + expect(Lib.dateTime2ms(dateStr, calendar)).toBe(0, calendar); + + var expected_p1ms = dateStr + ' 00:00:00.0001', + expected_1s = dateStr + ' 00:00:01', + expected_1m = dateStr + ' 00:01', + expected_1h = dateStr + ' 01:00', + expected_lastinstant = dateStr + ' 23:59:59.9999'; + + var oneSec = 1000, + oneMin = 60 * oneSec, + oneHour = 60 * oneMin, + lastInstant = 24 * oneHour - 0.1; + + expect(Lib.ms2DateTime(0.1, 0, calendar)).toBe(expected_p1ms, calendar); + expect(Lib.ms2DateTime(oneSec, 0, calendar)).toBe(expected_1s, calendar); + expect(Lib.ms2DateTime(oneMin, 0, calendar)).toBe(expected_1m, calendar); + expect(Lib.ms2DateTime(oneHour, 0, calendar)).toBe(expected_1h, calendar); + expect(Lib.ms2DateTime(lastInstant, 0, calendar)).toBe(expected_lastinstant, calendar); + + expect(Lib.dateTime2ms(expected_p1ms, calendar)).toBe(0.1, calendar); + expect(Lib.dateTime2ms(expected_1s, calendar)).toBe(oneSec, calendar); + expect(Lib.dateTime2ms(expected_1m, calendar)).toBe(oneMin, calendar); + expect(Lib.dateTime2ms(expected_1h, calendar)).toBe(oneHour, calendar); + expect(Lib.dateTime2ms(expected_lastinstant, calendar)).toBe(lastInstant, calendar); + }); + }); }); describe('cleanDate', function() { diff --git a/test/jasmine/tests/plotschema_test.js b/test/jasmine/tests/plotschema_test.js index eb0983b4857..50503259c84 100644 --- a/test/jasmine/tests/plotschema_test.js +++ b/test/jasmine/tests/plotschema_test.js @@ -39,7 +39,7 @@ describe('plot schema', function() { assertPlotSchema( function(attr) { if(isValObject(attr)) { - expect(ROLES.indexOf(attr.role) !== -1).toBe(true); + expect(ROLES.indexOf(attr.role) !== -1).toBe(true, attr); } } ); From 4d8f79ab608ab02c74269d8c8348a8f67586aea0 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 2 Dec 2016 01:38:43 -0500 Subject: [PATCH 04/30] overhaul set_convert so I can tell where calendars go --- src/plots/cartesian/set_convert.js | 326 ++++++++++++++++------------- 1 file changed, 180 insertions(+), 146 deletions(-) diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index e5f074a8774..54e2caf3b24 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -13,6 +13,10 @@ var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); var Lib = require('../../lib'); +var cleanNumber = Lib.cleanNumber; +var ms2DateTime = Lib.ms2DateTime; +var dateTime2ms = Lib.dateTime2ms; + var numConstants = require('../../constants/numerical'); var FP_SAFE = numConstants.FP_SAFE; var BADNUM = numConstants.BADNUM; @@ -20,6 +24,16 @@ var BADNUM = numConstants.BADNUM; var constants = require('./constants'); var axisIds = require('./axis_ids'); +function fromLog(v) { + return Math.pow(10, v); +} + +function num(v) { + if(!isNumeric(v)) return BADNUM; + v = Number(v); + if(v < -FP_SAFE || v > FP_SAFE) return BADNUM; + return isNumeric(v) ? Number(v) : BADNUM; +} /** * Define the conversion functions for an axis data is used in 5 ways: @@ -41,8 +55,9 @@ var axisIds = require('./axis_ids'); * shapes will work the same way as ranges, tick0, and annotations * so they can use this conversion too. * - * Creates/updates these conversion functions, as well as cleaner functions: - * ax.d2d and ax.clean2r + * Creates/updates these conversion functions, and a few more utilities + * like cleanRange, and makeCalcdata + * * also clears the autorange bounds ._min and ._max * and the autotick constraints ._minDtick, ._forceTick0 */ @@ -67,24 +82,170 @@ module.exports = function setConvert(ax) { else return BADNUM; } - function fromLog(v) { - return Math.pow(10, v); + /* + * wrapped dateTime2ms that: + * - accepts ms numbers for backward compatibility + * - inserts a dummy arg so calendar is the 3rd arg (see notes below). + * - defaults to ax.calendar + */ + function dt2ms(v, _, calendar) { + // NOTE: Changed this behavior: previously we took any numeric value + // to be a ms, even if it was a string that could be a bare year. + // Now we convert it as a date if at all possible, and only try + // as (local) ms if that fails. + var ms = dateTime2ms(v, calendar || ax.calendar); + if(ms === BADNUM) { + if(isNumeric(v)) ms = dateTime2ms(new Date(+v)); + else return BADNUM; + } + return ms; + } + + // wrapped ms2DateTime to insert default ax.calendar + function ms2dt(v, r, calendar) { + return ms2DateTime(v, r, calendar || ax.calendar); + } + + function getCategoryName(v) { + return ax._categories[Math.round(v)]; + } + + /* + * setCategoryIndex: return the index of category v, + * inserting it in the list if it's not already there + * + * this will enter the categories in the order it + * encounters them, ie all the categories from the + * first data set, then all the ones from the second + * that aren't in the first etc. + * + * it is assumed that this function is being invoked in the + * already sorted category order; otherwise there would be + * a disconnect between the array and the index returned + */ + function setCategoryIndex(v) { + if(v !== null && v !== undefined) { + var c = ax._categories.indexOf(v); + if(c === -1) { + ax._categories.push(v); + return ax._categories.length - 1; + } + return c; + } + return BADNUM; + } + + function getCategoryIndex(v) { + // d2l/d2c variant that that won't add categories but will also + // allow numbers to be mapped to the linearized axis positions + var index = ax._categories.indexOf(v); + if(index !== -1) return index; + if(typeof v === 'number') return v; } - function num(v) { + function l2p(v) { if(!isNumeric(v)) return BADNUM; - v = Number(v); - if(v < -FP_SAFE || v > FP_SAFE) return BADNUM; - return isNumeric(v) ? Number(v) : BADNUM; + + // include 2 fractional digits on pixel, for PDF zooming etc + return d3.round(ax._b + ax._m * v, 2); } + function p2l(px) { return (px - ax._b) / ax._m; } + + // conversions among c/l/p are fairly simple - do them together for all axis types ax.c2l = (ax.type === 'log') ? toLog : num; ax.l2c = (ax.type === 'log') ? fromLog : num; - ax.l2d = function(v) { return ax.c2d(ax.l2c(v), 0, ax.calendar); }; - ax.p2d = function(v) { return ax.l2d(ax.p2l(v), 0, ax.calendar); }; + + ax.l2p = l2p; + ax.p2l = p2l; + + ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p; + ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l; /* - * fn to make sure range is a couplet of valid & distinct values + * now type-specific conversions for **ALL** other combinations + * they're all written out, instead of being combinations of each other, for + * both clarity and speed. + */ + if(['linear', '-'].indexOf(ax.type) !== -1) { + // all are data vals, but d and r need cleaning + ax.d2r = ax.r2d = ax.d2c = ax.r2c = ax.d2l = ax.r2l = cleanNumber; + ax.c2d = ax.c2r = ax.l2d = ax.l2r = num; + + ax.d2p = ax.r2p = function(v) { return l2p(cleanNumber(v)); }; + ax.p2d = ax.p2r = p2l; + } + else if(ax.type === 'log') { + // d and c are data vals, r and l are logged (but d and r need cleaning) + ax.d2r = ax.d2l = function(v, clip) { return toLog(cleanNumber(v), clip); }; + ax.r2d = ax.r2c = function(v) { return fromLog(cleanNumber(v)); }; + + ax.d2c = ax.r2l = cleanNumber; + ax.c2d = ax.l2r = num; + + ax.c2r = toLog; + ax.l2d = fromLog; + + ax.d2p = function(v, clip) { return l2p(ax.d2r(v, clip)); }; + ax.p2d = function(px) { return fromLog(p2l(px)); }; + + ax.r2p = function(v) { return l2p(cleanNumber(v)); }; + ax.p2r = p2l; + } + else if(ax.type === 'date') { + // r and d are date strings, l and c are ms + + /* + * Any of these functions with r and d on either side, calendar is the + * **3rd** argument. log has reserved the second argument. + * + * Unless you need the special behavior of the second arg (ms2DateTime + * uses this to limit precision, toLog uses true to clip negatives + * to offscreen low rather than undefined), it's safe to pass 0. + */ + ax.d2r = ax.r2d = Lib.identity; + + ax.d2c = ax.r2c = ax.d2l = ax.r2l = dt2ms; + ax.c2d = ax.c2r = ax.l2d = ax.l2r = ms2dt; + + ax.d2p = ax.r2p = function(v, _, calendar) { return l2p(dt2ms(v, 0, calendar)); }; + ax.p2d = ax.p2r = function(px, r, calendar) { return ms2dt(p2l(px), r, calendar); }; + } + else if(ax.type === 'category') { + // d is categories; r, c, and l are indices + // TODO: should r accept category names too? + // ie r2c and r2l would be getCategoryIndex (and r2p would change) + + ax.d2r = ax.d2c = ax.d2l = setCategoryIndex; + ax.r2d = ax.c2d = ax.l2d = getCategoryName; + + // special d2l variant that won't add categories + ax.d2l_noadd = getCategoryIndex; + + ax.r2l = ax.l2r = ax.r2c = ax.c2r = num; + + ax.d2p = function(v) { return l2p(getCategoryIndex(v)); }; + ax.p2d = function(px) { return getCategoryName(p2l(px)); }; + ax.r2p = l2p; + ax.p2r = p2l; + } + + // find the range value at the specified (linear) fraction of the axis + ax.fraction2r = function(v) { + var rl0 = ax.r2l(ax.range[0]), + rl1 = ax.r2l(ax.range[1]); + return ax.l2r(rl0 + v * (rl1 - rl0)); + }; + + // find the fraction of the range at the specified range value + ax.r2fraction = function(v) { + var rl0 = ax.r2l(ax.range[0]), + rl1 = ax.r2l(ax.range[1]); + return (ax.r2l(v) - rl0) / (rl1 - rl0); + }; + + /* + * cleanRange: make sure range is a couplet of valid & distinct values * keep numbers away from the limits of floating point numbers, * and dates away from the ends of our date system (+/- 9999 years) * @@ -95,7 +256,6 @@ module.exports = function setConvert(ax) { if(!rangeAttr) rangeAttr = 'range'; var range = ax[rangeAttr], axLetter = (ax._id || 'x').charAt(0), - calendar = ax.calendar, i, dflt; if(ax.type === 'date') dflt = constants.DFLTRANGEDATE; @@ -113,8 +273,8 @@ module.exports = function setConvert(ax) { if(ax.type === 'date') { // check if milliseconds or js date objects are provided for range // and convert to date strings - range[0] = Lib.cleanDate(range[0], BADNUM, calendar); - range[1] = Lib.cleanDate(range[1], BADNUM, calendar); + range[0] = Lib.cleanDate(range[0]); + range[1] = Lib.cleanDate(range[1]); } for(i = 0; i < 2; i++) { @@ -124,12 +284,12 @@ module.exports = function setConvert(ax) { break; } - if(ax.r2l(range[0], calendar) === ax.r2l(range[1], calendar)) { + if(ax.r2l(range[0]) === ax.r2l(range[1])) { // split by +/- 1 second - var linCenter = Lib.constrain(ax.r2l(range[0], calendar), + var linCenter = Lib.constrain(ax.r2l(range[0]), Lib.MIN_MS + 1000, Lib.MAX_MS - 1000); - range[0] = ax.l2r(linCenter - 1000, 0, calendar); - range[1] = ax.l2r(linCenter + 1000, 0, calendar); + range[0] = ax.l2r(linCenter - 1000); + range[1] = ax.l2r(linCenter + 1000); break; } } @@ -157,24 +317,6 @@ module.exports = function setConvert(ax) { } }; - // TODO (alexcjohnson): for now use the axis calendar for these (in case of dates) - // but that means annotations - // find the range value at the specified (linear) fraction of the axis - ax.fraction2r = function(v) { - var calendar = ax.calendar, - rl0 = ax.r2l(ax.range[0], calendar), - rl1 = ax.r2l(ax.range[1], calendar); - return ax.l2r(rl0 + v * (rl1 - rl0), BADNUM, calendar); - }; - - // find the fraction of the range at the specified range value - ax.r2fraction = function(v) { - var calendar = ax.calendar, - rl0 = ax.r2l(ax.range[0], calendar), - rl1 = ax.r2l(ax.range[1], calendar); - return (ax.r2l(v, calendar) - rl0) / (rl1 - rl0); - }; - // set scaling to pixels ax.setScale = function(usePrivateRange) { var gs = ax._gd._fullLayout._size, @@ -223,114 +365,6 @@ module.exports = function setConvert(ax) { } }; - ax.l2p = function(v) { - if(!isNumeric(v)) return BADNUM; - - // include 2 fractional digits on pixel, for PDF zooming etc - return d3.round(ax._b + ax._m * v, 2); - }; - - ax.p2l = function(px) { return (px - ax._b) / ax._m; }; - - ax.c2p = function(v, clip) { return ax.l2p(ax.c2l(v, clip)); }; - ax.p2c = function(px) { return ax.l2c(ax.p2l(px)); }; - - // clip doesn't do anything here yet, but in v2.0 when log axes get - // refactored it will... so including it now so we don't forget. - ax.r2p = function(v, clip) { return ax.l2p(ax.r2l(v, clip)); }; - ax.p2r = function(px) { return ax.l2r(ax.p2l(px)); }; - - ax.r2c = function(v) { return ax.l2c(ax.r2l(v)); }; - ax.c2r = function(v) { return ax.l2r(ax.c2l(v)); }; - - if(['linear', 'log', '-'].indexOf(ax.type) !== -1) { - ax.c2d = num; - ax.d2c = Lib.cleanNumber; - if(ax.type === 'log') { - ax.d2l = function(v, clip) { - return ax.c2l(ax.d2c(v), clip); - }; - ax.d2r = ax.d2l; - ax.r2d = ax.l2d; - } - else { - ax.d2l = Lib.cleanNumber; - ax.d2r = Lib.cleanNumber; - ax.r2d = num; - } - ax.r2l = num; - ax.l2r = num; - } - else if(ax.type === 'date') { - ax.c2d = Lib.ms2DateTime; - - ax.d2c = function(v, cal) { - // NOTE: Changed this behavior: previously we took any numeric value - // to be a ms, even if it was a string that could be a bare year. - // Now we convert it as a date if at all possible, and only try - // as (local) ms if that fails. - var ms = Lib.dateTime2ms(v, cal); - if(ms === BADNUM) { - if(isNumeric(v)) ms = Lib.dateTime2ms(new Date(v)); - else return BADNUM; - } - return Lib.constrain(ms, Lib.MIN_MS, Lib.MAX_MS); - }; - - ax.d2l = ax.d2c; - ax.r2l = ax.d2c; - ax.l2r = ax.c2d; - ax.d2r = Lib.identity; - ax.r2d = Lib.identity; - ax.cleanr = function(v, cal) { - /* - * If v is already a date string this is a noop, but running it - * through d2c and back validates the value: - * normalizes Date objects, milliseconds, and out-of-bounds dates - * so we always end up with either a clean date string or BADNUM - */ - return ax.c2d(ax.d2c(v, cal), 0, cal); - }; - } - else if(ax.type === 'category') { - - ax.c2d = function(v) { - return ax._categories[Math.round(v)]; - }; - - ax.d2c = function(v) { - // create the category list - // this will enter the categories in the order it - // encounters them, ie all the categories from the - // first data set, then all the ones from the second - // that aren't in the first etc. - // it is assumed that this function is being invoked in the - // already sorted category order; otherwise there would be - // a disconnect between the array and the index returned - - if(v !== null && v !== undefined && ax._categories.indexOf(v) === -1) { - ax._categories.push(v); - } - - var c = ax._categories.indexOf(v); - return c === -1 ? BADNUM : c; - }; - - ax.d2l_noadd = function(v) { - // d2c variant that that won't add categories but will also - // allow numbers to be mapped to the linearized axis positions - var index = ax._categories.indexOf(v); - if(index !== -1) return index; - if(typeof v === 'number') return v; - }; - - ax.d2l = ax.d2c; - ax.r2l = num; - ax.l2r = num; - ax.d2r = ax.d2c; - ax.r2d = ax.c2d; - } - // makeCalcdata: takes an x or y array and converts it // to a position on the axis object "ax" // inputs: @@ -350,12 +384,12 @@ module.exports = function setConvert(ax) { arrayOut = new Array(arrayIn.length); for(i = 0; i < arrayIn.length; i++) { - arrayOut[i] = ax.d2c(arrayIn[i], cal); + arrayOut[i] = ax.d2c(arrayIn[i], 0, cal); } } else { var v0 = ((axLetter + '0') in trace) ? - ax.d2c(trace[axLetter + '0'], cal) : 0, + ax.d2c(trace[axLetter + '0'], 0, cal) : 0, dv = (trace['d' + axLetter]) ? Number(trace['d' + axLetter]) : 1; From 84a51c2b91557814ba69eac310fb169f5e4161a0 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Fri, 2 Dec 2016 12:44:01 -0500 Subject: [PATCH 05/30] add world calendar support part 2 --- src/components/annotations/calc_autorange.js | 4 +- src/components/rangeslider/defaults.js | 9 +- src/lib/dates.js | 269 +++++++++++++++++-- src/lib/index.js | 4 + src/plots/cartesian/axes.js | 169 +++++------- src/plots/cartesian/constants.js | 5 +- src/plots/cartesian/dragbox.js | 2 +- src/plots/cartesian/graph_interact.js | 5 + src/plots/cartesian/set_convert.js | 6 +- src/plots/cartesian/tick_value_defaults.js | 2 +- src/plots/gl3d/scene.js | 12 +- src/plots/layout_attributes.js | 1 + src/traces/bar/calc.js | 13 +- src/traces/box/calc.js | 2 +- src/traces/heatmap/calc.js | 4 +- src/traces/heatmap/convert_column_xyz.js | 8 +- src/traces/histogram/calc.js | 19 +- src/traces/histogram2d/calc.js | 54 ++-- src/traces/mesh3d/convert.js | 10 +- src/traces/ohlc/transform.js | 10 +- src/traces/scatter/plot.js | 4 +- src/traces/scatter3d/convert.js | 9 +- src/traces/scattergl/convert.js | 8 +- src/traces/surface/convert.js | 16 +- src/transforms/filter.js | 4 +- 25 files changed, 431 insertions(+), 218 deletions(-) diff --git a/src/components/annotations/calc_autorange.js b/src/components/annotations/calc_autorange.js index a5969ae181b..9046863bf81 100644 --- a/src/components/annotations/calc_autorange.js +++ b/src/components/annotations/calc_autorange.js @@ -69,14 +69,14 @@ function annAutorange(gd) { } if(xa && xa.autorange) { - Axes.expand(xa, [xa.l2c(xa.r2l(ann.x))], { + Axes.expand(xa, [xa.r2c(ann.x)], { ppadplus: rightSize, ppadminus: leftSize }); } if(ya && ya.autorange) { - Axes.expand(ya, [ya.l2c(ya.r2l(ann.y))], { + Axes.expand(ya, [ya.r2c(ann.y)], { ppadplus: bottomSize, ppadminus: topSize }); diff --git a/src/components/rangeslider/defaults.js b/src/components/rangeslider/defaults.js index 01c07091e01..ab448bb022a 100644 --- a/src/components/rangeslider/defaults.js +++ b/src/components/rangeslider/defaults.js @@ -37,13 +37,12 @@ module.exports = function handleDefaults(layoutIn, layoutOut, axName, counterAxe // Expand slider range to the axis range if(containerOut.range && !axOut.autorange) { + // TODO: what if the ranges are reversed? var outRange = containerOut.range, - axRange = axOut.range, - l2r = axOut.l2r, - r2l = axOut.r2l; + axRange = axOut.range; - outRange[0] = l2r(Math.min(r2l(outRange[0]), r2l(axRange[0]))); - outRange[1] = l2r(Math.max(r2l(outRange[1]), r2l(axRange[1]))); + outRange[0] = axOut.l2r(Math.min(axOut.r2l(outRange[0]), axOut.r2l(axRange[0]))); + outRange[1] = axOut.l2r(Math.max(axOut.r2l(outRange[1]), axOut.r2l(axRange[1]))); } else { axOut._needsExpand = true; } diff --git a/src/lib/dates.js b/src/lib/dates.js index 5af2f465e0a..c0a996e8668 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -49,6 +49,85 @@ function isWorldCalendar(calendar) { // of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD() var EPOCHJD = 2440587.5; +// 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 = { + gregorian: '2000-01-01', + 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 = { + gregorian: '2000-01-02', + 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 = { + gregorian: ['2000-01-01', '2001-01-01'], + 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'] +}; + +/* + * dateTick0: get the canonical tick for this calendar + * + * bool sunday is for week ticks, shift it to a Sunday. + */ +exports.dateTick0 = function(calendar, sunday) { + calendar = (isWorldCalendar(calendar) && calendar) || 'gregorian'; + if(sunday) return CANONICAL_SUNDAY[calendar]; + return CANONICAL_TICK[calendar]; +}; + +/* + * dfltRange: for each calendar, give a valid default range + */ +exports.dfltRange = function(calendar) { + calendar = (isWorldCalendar(calendar) && calendar) || 'gregorian'; + return DFLTRANGE[calendar]; +}; + // is an object a javascript date? exports.isJSDate = function(v) { return typeof v === 'object' && v !== null && typeof v.getTime === 'function'; @@ -87,6 +166,10 @@ var MIN_MS, MAX_MS; * Note that we follow ISO 8601:2004: there *is* a year 0, which * is 1BC/BCE, and -1===2BC etc. * + * World calendars: not all of these *have* agreed extensions to this full range, + * if you have another calendar system but want a date range outside its validity, + * you can use a gregorian date string prefixed with 'G' or 'g'. + * * Where to cut off 2-digit years between 1900s and 2000s? * from http://support.microsoft.com/kb/244664: * 1930-2029 (the most retro of all...) @@ -120,7 +203,19 @@ exports.dateTime2ms = function(s, calendar) { // otherwise only accept strings and numbers if(typeof s !== 'string' && typeof s !== 'number') return BADNUM; - var match = String(s).match(DATETIME_REGEXP); + s = String(s); + + var isWorld = isWorldCalendar(calendar); + + // to handle out-of-range dates in international calendars, accept + // 'G' as a prefix to force the built-in gregorian calendar. + var s0 = s.charAt(0); + if(isWorld && (s0 === 'G' || s0 === 'g')) { + s = s.substr(1); + calendar = ''; + } + + var match = s.match(DATETIME_REGEXP); if(!match) return BADNUM; var y = match[1], m = Number(match[3] || 1), @@ -129,11 +224,14 @@ exports.dateTime2ms = function(s, calendar) { M = Number(match[9] || 0), S = Number(match[11] || 0); - if(isWorldCalendar(calendar)) { + if(isWorld) { // disallow 2-digit years for world calendars if(y.length === 2) return BADNUM; - var cDate = getCal(calendar).newDate(Number(y), m, d); + var cDate; + try { cDate = getCal(calendar).newDate(Number(y), m, d); } + catch(e) { return BADNUM; } // Invalid ... date + if(!cDate) return BADNUM; return ((cDate.toJD() - EPOCHJD) * ONEDAY) + @@ -192,12 +290,18 @@ exports.ms2DateTime = function(ms, r, calendar) { var msecTenths = Math.floor(mod(ms + 0.05, 1) * 10), msRounded = Math.round(ms - msecTenths / 10), - dateStr, h, m, s, msec10; + dateStr, h, m, s, msec10, d; if(isWorldCalendar(calendar)) { var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD, timeMs = Math.floor(mod(ms, ONEDAY)); - dateStr = getCal(calendar).fromJD(dateJD).formatDate('yyyy-mm-dd'); + try { + dateStr = getCal(calendar).fromJD(dateJD).formatDate('yyyy-mm-dd'); + } + catch(e) { + // invalid date in this calendar - fall back to Gyyyy-mm-dd + dateStr = utcFormat('G%Y-%m-%d')(new Date(msRounded)); + } // yyyy does NOT guarantee 4-digit years. YYYY mostly does, but does // other things for a few calendars, so we can't trust it. Just pad @@ -217,7 +321,7 @@ exports.ms2DateTime = function(ms, r, calendar) { msec10 = (r < FIVEMIN) ? (timeMs % ONESEC) * 10 + msecTenths : 0; } else { - var d = new Date(msRounded); + d = new Date(msRounded); dateStr = utcFormat('%Y-%m-%d')(d); @@ -377,7 +481,12 @@ function modDateFormat(fmt, x, calendar) { fmt = fmt.replace(fracMatch, fracSecs); } if(isWorldCalendar(calendar)) { - fmt = worldCalFmt(fmt, x, calendar); + try { + fmt = worldCalFmt(fmt, x, calendar); + } + catch(e) { + return 'Invalid'; + } } return utcFormat(fmt)(d); } @@ -435,19 +544,22 @@ exports.formatDate = function(x, fmt, tr, calendar) { if(fmt) return modDateFormat(fmt, x, calendar); if(calendar) { - var dateJD = Math.floor(x + 0.05 / ONEDAY) + EPOCHJD, - cDate = getCal(calendar).fromJD(dateJD); - - if(tr === 'y') dateStr = yearFormatWorld(cDate); - else if(tr === 'm') dateStr = monthFormatWorld(cDate); - else if(tr === 'd') { - headStr = yearFormatWorld(cDate); - dateStr = dayFormatWorld(cDate); - } - else { - headStr = yearMonthDayFormatWorld(cDate); - dateStr = formatTime(x, tr); + try { + var dateJD = Math.floor((x + 0.05) / ONEDAY) + EPOCHJD, + cDate = getCal(calendar).fromJD(dateJD); + + if(tr === 'y') dateStr = yearFormatWorld(cDate); + else if(tr === 'm') dateStr = monthFormatWorld(cDate); + else if(tr === 'd') { + headStr = yearFormatWorld(cDate); + dateStr = dayFormatWorld(cDate); + } + else { + headStr = yearMonthDayFormatWorld(cDate); + dateStr = formatTime(x, tr); + } } + catch(e) { return 'Invalid'; } } else { var d = new Date(x); @@ -466,3 +578,122 @@ exports.formatDate = function(x, fmt, tr, calendar) { return dateStr + (headStr ? '\n' + headStr : ''); }; + +/* + * incrementMonth: make a new milliseconds value from the given one, + * having changed the month + * + * special case for world calendars: multiples of 12 are treated as years, + * even for calendar systems that don't have (always or ever) 12 months/year + * TODO: perhaps we need a different code for year increments to support this? + * + * ms (number): the initial millisecond value + * dMonth (int): the (signed) number of months to shift + * calendar (string): the calendar system to use + * + * changing month does not (and CANNOT) always preserve day, since + * months have different lengths. The worst example of this is: + * d = new Date(1970,0,31); d.setMonth(1) -> Feb 31 turns into Mar 3 + * + * But we want to be able to iterate over the last day of each month, + * regardless of what its number is. + * So shift 3 days forward, THEN set the new month, then unshift: + * 1/31 -> 2/28 (or 29) -> 3/31 -> 4/30 -> ... + * + * Note that odd behavior still exists if you start from the 26th-28th: + * 1/28 -> 2/28 -> 3/31 + * but at least you can't shift any dates into the wrong month, + * and ticks on these days incrementing by month would be very unusual + */ +var THREEDAYS = 3 * ONEDAY; +exports.incrementMonth = function(ms, dMonth, calendar) { + calendar = isWorldCalendar(calendar) && calendar; + + // pull time out and operate on pure dates, then add time back at the end + // this gives maximum precision - not that we *normally* care if we're + // incrementing by month, but better to be safe! + var timeMs = mod(ms, ONEDAY); + ms = Math.round(ms - timeMs); + + if(calendar) { + try { + var dateJD = Math.round(ms / ONEDAY) + EPOCHJD, + calInstance = getCal(calendar), + cDate = calInstance.fromJD(dateJD); + + if(dMonth % 12) calInstance.add(cDate, dMonth, 'm'); + else calInstance.add(cDate, dMonth / 12, 'y'); + + return (cDate.toJD() - EPOCHJD) * ONEDAY + timeMs; + } + catch(e) { + logError('invalid ms ' + ms + ' in calendar ' + calendar); + // then keep going in gregorian even though the result will be 'Invalid' + } + } + + var y = new Date(ms + THREEDAYS); + return y.setUTCMonth(y.getUTCMonth() + dMonth) + timeMs - THREEDAYS; +}; + +/* + * findExactDates: what fraction of data is exact days, months, or years? + * + * data: array of millisecond values + * calendar (string) the calendar to test against + */ +exports.findExactDates = function(data, calendar) { + var exactYears = 0, + exactMonths = 0, + exactDays = 0, + blankCount = 0, + d, + di; + + var calInstance = isWorldCalendar(calendar) && getCal(calendar); + + for(var i = 0; i < data.length; i++) { + di = data[i]; + + // not date data at all + if(!isNumeric(di)) { + blankCount ++; + continue; + } + + // not an exact date + if(di % ONEDAY) continue; + + if(calInstance) { + try { + d = calInstance.fromJD(di / ONEDAY + EPOCHJD); + if(d.day() === 1) { + if(d.month() === 1) exactYears++; + else exactMonths++; + } + else exactDays++; + } + catch(e) { + // invalid date in this calendar - ignore it here. + } + } + else { + d = new Date(di); + if(d.getUTCDate() === 1) { + if(d.getUTCMonth() === 0) exactYears++; + else exactMonths++; + } + else exactDays++; + } + } + exactMonths += exactYears; + exactDays += exactMonths; + + var dataCount = data.length - blankCount; + + return { + exactYears: exactYears / dataCount, + exactMonths: exactMonths / dataCount, + exactDays: exactDays / dataCount + }; +}; diff --git a/src/lib/index.js b/src/lib/index.js index c6edfb4731b..5a66ac4c01b 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -33,6 +33,10 @@ lib.ms2DateTimeLocal = datesModule.ms2DateTimeLocal; lib.cleanDate = datesModule.cleanDate; lib.isJSDate = datesModule.isJSDate; lib.formatDate = datesModule.formatDate; +lib.incrementMonth = datesModule.incrementMonth; +lib.dateTick0 = datesModule.dateTick0; +lib.dfltRange = datesModule.dfltRange; +lib.findExactDates = datesModule.findExactDates; lib.MIN_MS = datesModule.MIN_MS; lib.MAX_MS = datesModule.MAX_MS; diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index b71156cf256..32e9cd595de 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -221,7 +221,7 @@ axes.getAutoRange = function(ax) { axReverse = false; if(ax.range) { - var rng = ax.range.map(ax.r2l); + var rng = Lib.simpleMap(ax.range, ax.r2l); axReverse = rng[1] < rng[0]; } @@ -314,7 +314,7 @@ axes.getAutoRange = function(ax) { // maintain reversal if(axReverse) newRange.reverse(); - return newRange.map(ax.l2r || Number); + return Lib.simpleMap(newRange, ax.l2r || Number); }; axes.doAutoRange = function(ax) { @@ -486,10 +486,12 @@ axes.expand = function(ax, data, options) { }; -axes.autoBin = function(data, ax, nbins, is2d) { +axes.autoBin = function(data, ax, nbins, is2d, calendar) { var dataMin = Lib.aggNums(Math.min, null, data), dataMax = Lib.aggNums(Math.max, null, data); + if(!calendar) calendar = ax.calendar; + if(ax.type === 'category') { return { start: dataMin - 0.5, @@ -519,23 +521,21 @@ axes.autoBin = function(data, ax, nbins, is2d) { if(ax.type === 'log') { dummyAx = { type: 'linear', - range: [dataMin, dataMax], - r2l: Number + range: [dataMin, dataMax] }; } else { dummyAx = { type: ax.type, - // conversion below would be ax.c2r but that's only different from l2r - // for log, and this is the only place (so far?) we would want c2r. - range: [dataMin, dataMax].map(ax.l2r), - r2l: ax.r2l + range: Lib.simpleMap([dataMin, dataMax], ax.c2r, 0, calendar), + calendar: calendar }; } + axes.setConvert(dummyAx); axes.autoTicks(dummyAx, size0); var binStart = axes.tickIncrement( - axes.tickFirst(dummyAx), dummyAx.dtick, 'reverse'), + axes.tickFirst(dummyAx), dummyAx.dtick, 'reverse', calendar), binEnd; // check for too many data points right at the edges of bins @@ -554,20 +554,20 @@ axes.autoBin = function(data, ax, nbins, is2d) { // we bin it on a linear axis (which one could argue against, but that's // a separate issue) if(dummyAx.dtick.charAt(0) === 'M') { - binStart = autoShiftMonthBins(binStart, data, dummyAx.dtick, dataMin); + binStart = autoShiftMonthBins(binStart, data, dummyAx.dtick, dataMin, calendar); } // calculate the endpoint for nonlinear ticks - you have to // just increment until you're done binEnd = binStart; while(binEnd <= dataMax) { - binEnd = axes.tickIncrement(binEnd, dummyAx.dtick); + binEnd = axes.tickIncrement(binEnd, dummyAx.dtick, false, calendar); } } return { - start: ax.c2r(binStart), - end: ax.c2r(binEnd), + start: ax.c2r(binStart, 0, calendar), + end: ax.c2r(binEnd, 0, calendar), size: dummyAx.dtick }; }; @@ -619,59 +619,22 @@ function autoShiftNumericBins(binStart, data, ax, dataMin, dataMax) { } -function autoShiftMonthBins(binStart, data, dtick, dataMin) { - var exactYears = 0, - exactMonths = 0, - exactDays = 0, - blankCount = 0, - dataCount, - di, - d, - year, - month; - - for(var i = 0; i < data.length; i++) { - di = data[i]; - if(!isNumeric(di)) { - blankCount ++; - continue; - } - d = new Date(di), - year = d.getUTCFullYear(); - if(di === Date.UTC(year, 0, 1)) { - exactYears ++; - } - else { - month = d.getUTCMonth(); - if(di === Date.UTC(year, month, 1)) { - exactMonths ++; - } - else if(di === Date.UTC(year, month, d.getUTCDate())) { - exactDays ++; - } - } - } - - dataCount = data.length - blankCount; - - // include bigger exact dates in the smaller ones - exactMonths += exactYears; - exactDays += exactMonths; - - // unmber of data points that needs to be an exact value +function autoShiftMonthBins(binStart, data, dtick, dataMin, calendar) { + var stats = Lib.findExactDates(data, calendar); + // number of data points that needs to be an exact value // to shift that increment to (near) the bin center - var threshold = 0.8 * dataCount; + var threshold = 0.8; - if(exactDays > threshold) { + if(stats.exactDays > threshold) { var numMonths = Number(dtick.substr(1)); - if((exactYears > threshold) && (numMonths % 12 === 0)) { + if((stats.exactYears > threshold) && (numMonths % 12 === 0)) { // The exact middle of a non-leap-year is 1.5 days into July // so if we start the bins here, all but leap years will // get hover-labeled as exact years. binStart = axes.tickIncrement(binStart, 'M6', 'reverse') + ONEDAY * 1.5; } - else if(exactMonths > threshold) { + else if(stats.exactMonths > threshold) { // Months are not as clean, but if we shift half the *longest* // month (31/2 days) then 31-day months will get labeled exactly // and shorter months will get labeled with the correct month @@ -701,7 +664,7 @@ function autoShiftMonthBins(binStart, data, dtick, dataMin) { // in any case, set tickround to # of digits to round tick labels to, // or codes to this effect for log and date scales axes.calcTicks = function calcTicks(ax) { - var rng = ax.range.map(ax.r2l); + var rng = Lib.simpleMap(ax.range, ax.r2l); // calculate max number of (auto) ticks to display based on plot size if(ax.tickmode === 'auto' || !ax.dtick) { @@ -758,7 +721,7 @@ axes.calcTicks = function calcTicks(ax) { } for(var x = ax._tmin; (axrev) ? (x >= endtick) : (x <= endtick); - x = axes.tickIncrement(x, ax.dtick, axrev)) { + x = axes.tickIncrement(x, ax.dtick, axrev, ax.calendar)) { vals.push(x); // prevent infinite loops @@ -788,7 +751,7 @@ function arrayTicks(ax) { var vals = ax.tickvals, text = ax.ticktext, ticksOut = new Array(vals.length), - rng = ax.range.map(ax.r2l), + rng = Lib.simpleMap(ax.range, ax.r2l), r0expanded = rng[0] * 1.0001 - rng[1] * 0.0001, r1expanded = rng[1] * 1.0001 - rng[0] * 0.0001, tickMin = Math.min(r0expanded, r1expanded), @@ -858,7 +821,7 @@ axes.autoTicks = function(ax, roughDTick) { var base; if(ax.type === 'date') { - ax.tick0 = '2000-01-01'; + ax.tick0 = Lib.dateTick0(ax.calendar); // the criteria below are all based on the rough spacing we calculate // being > half of the final unit - so precalculate twice the rough val var roughX2 = 2 * roughDTick; @@ -877,7 +840,7 @@ axes.autoTicks = function(ax, roughDTick) { // get week ticks on sunday // this will also move the base tick off 2000-01-01 if dtick is // 2 or 3 days... but that's a weird enough case that we'll ignore it. - ax.tick0 = '2000-01-02'; + ax.tick0 = Lib.dateTick0(ax.calendar, true); } else if(roughX2 > ONEHOUR) { ax.dtick = roundDTick(roughDTick, ONEHOUR, roundBase24); @@ -896,7 +859,7 @@ axes.autoTicks = function(ax, roughDTick) { } else if(ax.type === 'log') { ax.tick0 = 0; - var rng = ax.range.map(ax.r2l); + var rng = Lib.simpleMap(ax.range, ax.r2l); if(roughDTick > 0.7) { // only show powers of 10 @@ -962,8 +925,8 @@ function autoTickRound(ax) { // not necessarily *all* the information in tick0 though, if it's really odd // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19 // take off a leading minus (year < 0 so length is consistent) - var tick0ms = ax.r2l(ax.tick0, 0, ax.calendar), - tick0str = ax.l2r(tick0ms, 0, ax.calendar).replace(/^-/, ''), + var tick0ms = ax.r2l(ax.tick0), + tick0str = ax.l2r(tick0ms).replace(/^-/, ''), tick0len = tick0str.length; if(String(dtick).charAt(0) === 'M') { @@ -979,7 +942,7 @@ function autoTickRound(ax) { // tickround is a number of digits of fractional seconds // of any two adjacent ticks, at least one will have the maximum fractional digits // of all possible ticks - so take the max. length of tick0 and the next one - var tick1len = ax.l2r(tick0ms + dtick, 0, ax.calendar).replace(/^-/, '').length; + var tick1len = ax.l2r(tick0ms + dtick).replace(/^-/, '').length; ax._tickround = Math.max(tick0len, tick1len) - 20; } } @@ -1010,36 +973,18 @@ function autoTickRound(ax) { // for pure powers of 10 // numeric ticks always have constant differences, other datetime ticks // can all be calculated as constant number of milliseconds -var THREEDAYS = 3 * ONEDAY; -axes.tickIncrement = function(x, dtick, axrev) { +axes.tickIncrement = function(x, dtick, axrev, calendar) { var axSign = axrev ? -1 : 1; - // includes all dates smaller than month, and pure 10^n in log + // includes linear, all dates smaller than month, and pure 10^n in log if(isNumeric(dtick)) return x + axSign * dtick; + // everything else is a string, one character plus a number var tType = dtick.charAt(0), dtSigned = axSign * Number(dtick.substr(1)); - // Dates: months (or years) - if(tType === 'M') { - /* - * set(UTC)Month does not (and CANNOT) always preserve day, since - * months have different lengths. The worst example of this is: - * d = new Date(1970,0,31); d.setMonth(1) -> Feb 31 turns into Mar 3 - * - * But we want to be able to iterate over the last day of each month, - * regardless of what its number is. - * So shift 3 days forward, THEN set the new month, then unshift: - * 1/31 -> 2/28 (or 29) -> 3/31 -> 4/30 -> ... - * - * Note that odd behavior still exists if you start from the 26th-28th: - * 1/28 -> 2/28 -> 3/31 - * but at least you can't shift any dates into the wrong month, - * and ticks on these days incrementing by month would be very unusual - */ - var y = new Date(x + THREEDAYS); - return y.setUTCMonth(y.getUTCMonth() + dtSigned) - THREEDAYS; - } + // Dates: months (or years - see Lib.incrementMonth) + if(tType === 'M') return Lib.incrementMonth(x, dtSigned, calendar); // Log scales: Linear, Digits else if(tType === 'L') return Math.log(Math.pow(10, x) + dtSigned) / Math.LN10; @@ -1060,7 +1005,7 @@ axes.tickIncrement = function(x, dtick, axrev) { // calculate the first tick on an axis axes.tickFirst = function(ax) { var r2l = ax.r2l || Number, - rng = ax.range.map(r2l), + rng = Lib.simpleMap(ax.range, r2l), axrev = rng[1] < rng[0], sRound = axrev ? Math.floor : Math.ceil, // add a tiny extra bit to make sure we get ticks @@ -1080,24 +1025,32 @@ axes.tickFirst = function(ax) { } var tType = dtick.charAt(0), - dtNum = Number(dtick.substr(1)), - t0, - mdif, - t1; + dtNum = Number(dtick.substr(1)); // Dates: months (or years) if(tType === 'M') { - t0 = new Date(tick0); - r0 = new Date(r0); - mdif = (r0.getUTCFullYear() - t0.getUTCFullYear()) * 12 + - r0.getUTCMonth() - t0.getUTCMonth(); - t1 = t0.setUTCMonth(t0.getUTCMonth() + - (Math.round(mdif / dtNum) + (axrev ? 1 : -1)) * dtNum); - - while(axrev ? t1 > r0 : t1 < r0) { - t1 = axes.tickIncrement(t1, dtick, axrev); + var cnt = 0, + t0 = tick0, + t1, + mult, + newDTick; + + // This algorithm should work for *any* nonlinear (but close to linear!) + // tick spacing. Limit to 10 iterations, for gregorian months it's normally <=3. + while(cnt < 10) { + t1 = axes.tickIncrement(t0, dtick, axrev, ax.calendar); + if((t1 - r0) * (t0 - r0) <= 0) { + // t1 and t0 are on opposite sides of r0! we've succeeded! + if(axrev) return Math.min(t0, t1); + return Math.max(t0, t1); + } + mult = (r0 - ((t0 + t1) / 2)) / (t1 - t0); + newDTick = tType + ((Math.abs(Math.round(mult)) || 1) * dtNum); + t0 = axes.tickIncrement(t0, newDTick, mult < 0 ? !axrev : axrev, ax.calendar); + cnt++; } - return t1; + Lib.error('tickFirst did not converge', ax); + return t0; } // Log scales: Linear, Digits @@ -1130,7 +1083,7 @@ axes.tickText = function(ax, x, hover) { tickVal2l = ax.type === 'category' ? ax.d2l_noadd : ax.d2l; if(arrayMode && Array.isArray(ax.ticktext)) { - var rng = ax.range.map(ax.r2l), + var rng = Lib.simpleMap(ax.range, ax.r2l), minDiff = Math.abs(rng[1] - rng[0]) / 10000; for(i = 0; i < ax.ticktext.length; i++) { if(Math.abs(x - tickVal2l(ax.tickvals[i])) < minDiff) break; @@ -1618,7 +1571,7 @@ axes.doTicks = function(gd, axid, skipTitle) { var axDone = axes.doTicks(gd, ax._id); if(axid === 'redraw') { ax._r = ax.range.slice(); - ax._rl = ax._r.map(ax.r2l); + ax._rl = Lib.simpleMap(ax._r, ax.r2l); } return axDone; }; @@ -2025,7 +1978,7 @@ axes.doTicks = function(gd, axid, skipTitle) { break; } } - var rng = ax.range.map(ax.r2l), + var rng = Lib.simpleMap(ax.range, ax.r2l), showZl = (rng[0] * rng[1] <= 0) && ax.zeroline && (ax.type === 'linear' || ax.type === '-') && gridvals.length && (hasBarsOrFill || clipEnds({x: 0}) || !ax.showline); diff --git a/src/plots/cartesian/constants.js b/src/plots/cartesian/constants.js index 765241182a6..dc7d444c477 100644 --- a/src/plots/cartesian/constants.js +++ b/src/plots/cartesian/constants.js @@ -66,8 +66,7 @@ module.exports = { // delay before a redraw (relayout) after smooth panning and zooming REDRAWDELAY: 50, - // last resort axis ranges for x, y, and date axes if we have no data + // last resort axis ranges for x and y axes if we have no data DFLTRANGEX: [-1, 6], - DFLTRANGEY: [-1, 4], - DFLTRANGEDATE: ['2000-01-01', '2001-01-01'], + DFLTRANGEY: [-1, 4] }; diff --git a/src/plots/cartesian/dragbox.js b/src/plots/cartesian/dragbox.js index 2927becef5a..43c9e7a1610 100644 --- a/src/plots/cartesian/dragbox.js +++ b/src/plots/cartesian/dragbox.js @@ -424,7 +424,7 @@ module.exports = function dragBox(gd, plotinfo, x, y, w, h, ns, ew) { function zoomWheelOneAxis(ax, centerFraction, zoom) { if(ax.fixedrange) return; - var axRange = ax.range.map(ax.r2l), + var axRange = Lib.simpleMap(ax.range, ax.r2l), v0 = axRange[0] + (axRange[1] - axRange[0]) * centerFraction; function doZoom(v) { return ax.l2r(v0 + (v - v0) * zoom); } ax.range = axRange.map(doZoom); diff --git a/src/plots/cartesian/graph_interact.js b/src/plots/cartesian/graph_interact.js index 10408f47ac7..57c65b99b13 100644 --- a/src/plots/cartesian/graph_interact.js +++ b/src/plots/cartesian/graph_interact.js @@ -666,6 +666,10 @@ function cleanPoint(d, hovermode) { if(d.xLabelVal === 0) d.xLabel = '0'; else d.xLabel = '-' + xLabelObj.text; } + // TODO: should we do something special if the axis calendar and + // the data calendar are different? Somehow display both dates with + // their system names? Right now it will just display in the axis calendar + // but users could add the other one as text. else d.xLabel = xLabelObj.text; d.xVal = d.xa.c2d(d.xLabelVal); } @@ -678,6 +682,7 @@ function cleanPoint(d, hovermode) { if(d.yLabelVal === 0) d.yLabel = '0'; else d.yLabel = '-' + yLabelObj.text; } + // TODO: see above TODO else d.yLabel = yLabelObj.text; d.yVal = d.ya.c2d(d.yLabelVal); } diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index 54e2caf3b24..7202411e980 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -258,7 +258,7 @@ module.exports = function setConvert(ax) { axLetter = (ax._id || 'x').charAt(0), i, dflt; - if(ax.type === 'date') dflt = constants.DFLTRANGEDATE; + if(ax.type === 'date') dflt = Lib.dfltRange(ax.calendar); else if(axLetter === 'y') dflt = constants.DFLTRANGEY; else dflt = constants.DFLTRANGEX; @@ -273,8 +273,8 @@ module.exports = function setConvert(ax) { if(ax.type === 'date') { // check if milliseconds or js date objects are provided for range // and convert to date strings - range[0] = Lib.cleanDate(range[0]); - range[1] = Lib.cleanDate(range[1]); + range[0] = Lib.cleanDate(range[0], BADNUM, ax.calendar); + range[1] = Lib.cleanDate(range[1], BADNUM, ax.calendar); } for(i = 0; i < 2; i++) { diff --git a/src/plots/cartesian/tick_value_defaults.js b/src/plots/cartesian/tick_value_defaults.js index f441cb5e055..0c8790eb346 100644 --- a/src/plots/cartesian/tick_value_defaults.js +++ b/src/plots/cartesian/tick_value_defaults.js @@ -61,7 +61,7 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe // tick0 can have different valType for different axis types, so // validate that now. Also for dates, change milliseconds to date strings - var tick0Dflt = (axType === 'date') ? '2000-01-01' : 0; + var tick0Dflt = (axType === 'date') ? Lib.dateTick0(containerOut.calendar) : 0; var tick0 = coerce('tick0', tick0Dflt); if(axType === 'date') { containerOut.tick0 = Lib.cleanDate(tick0, tick0Dflt); diff --git a/src/plots/gl3d/scene.js b/src/plots/gl3d/scene.js index 28750ec3208..02188822a9c 100644 --- a/src/plots/gl3d/scene.js +++ b/src/plots/gl3d/scene.js @@ -292,12 +292,12 @@ proto.recoverContext = function() { var axisProperties = [ 'xaxis', 'yaxis', 'zaxis' ]; -function coordinateBound(axis, coord, d, bounds) { +function coordinateBound(axis, coord, d, bounds, calendar) { var x; for(var i = 0; i < coord.length; ++i) { if(Array.isArray(coord[i])) { for(var j = 0; j < coord[i].length; ++j) { - x = axis.d2l(coord[i][j]); + x = axis.d2l(coord[i][j], 0, calendar); if(!isNaN(x) && isFinite(x)) { bounds[0][d] = Math.min(bounds[0][d], x); bounds[1][d] = Math.max(bounds[1][d], x); @@ -305,7 +305,7 @@ function coordinateBound(axis, coord, d, bounds) { } } else { - x = axis.d2l(coord[i]); + x = axis.d2l(coord[i], 0, calendar); if(!isNaN(x) && isFinite(x)) { bounds[0][d] = Math.min(bounds[0][d], x); bounds[1][d] = Math.max(bounds[1][d], x); @@ -316,9 +316,9 @@ function coordinateBound(axis, coord, d, bounds) { function computeTraceBounds(scene, trace, bounds) { var sceneLayout = scene.fullSceneLayout; - coordinateBound(sceneLayout.xaxis, trace.x, 0, bounds); - coordinateBound(sceneLayout.yaxis, trace.y, 1, bounds); - coordinateBound(sceneLayout.zaxis, trace.z, 2, bounds); + coordinateBound(sceneLayout.xaxis, trace.x, 0, bounds, trace.xcalendar); + coordinateBound(sceneLayout.yaxis, trace.y, 1, bounds, trace.ycalendar); + coordinateBound(sceneLayout.zaxis, trace.z, 2, bounds, trace.zcalendar); } proto.plot = function(sceneData, fullLayout, layout) { diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 5ab35397ec4..21f377e1ccf 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -173,6 +173,7 @@ module.exports = { calendar: { valType: 'calendar', role: 'info', + dflt: 'gregorian', description: [ 'Sets the default calendar system to use for interpreting and', 'displaying dates throughout the plot.' diff --git a/src/traces/bar/calc.js b/src/traces/bar/calc.js index 22c2a1a9871..52f45c4b69a 100644 --- a/src/traces/bar/calc.js +++ b/src/traces/bar/calc.js @@ -25,17 +25,24 @@ module.exports = function calc(gd, trace) { var xa = Axes.getFromId(gd, trace.xaxis || 'x'), ya = Axes.getFromId(gd, trace.yaxis || 'y'), orientation = trace.orientation || ((trace.x && !trace.y) ? 'h' : 'v'), - sa, pos, size, i; + sa, pos, size, i, scalendar; if(orientation === 'h') { sa = xa; size = xa.makeCalcdata(trace, 'x'); pos = ya.makeCalcdata(trace, 'y'); + + // not sure if it really makes sense to have dates for bar size data... + // ideally if we want to make gantt charts or something we'd treat + // the actual size (trace.x or y) as time delta but base as absolute + // time. But included here for completeness. + scalendar = trace.xcalendar; } else { sa = ya; size = ya.makeCalcdata(trace, 'y'); pos = xa.makeCalcdata(trace, 'x'); + scalendar = trace.ycalendar; } // create the "calculated data" to plot @@ -60,7 +67,7 @@ module.exports = function calc(gd, trace) { if(Array.isArray(base)) { for(i = 0; i < Math.min(base.length, cd.length); i++) { - b = sa.d2c(base[i]); + b = sa.d2c(base[i], 0, scalendar); cd[i].b = (isNumeric(b)) ? b : 0; } for(; i < cd.length; i++) { @@ -68,7 +75,7 @@ module.exports = function calc(gd, trace) { } } else { - b = sa.d2c(base); + b = sa.d2c(base, 0, scalendar); b = (isNumeric(b)) ? b : 0; for(i = 0; i < cd.length; i++) { cd[i].b = b; diff --git a/src/traces/box/calc.js b/src/traces/box/calc.js index 481a5a2ea67..c8bffeeff5e 100644 --- a/src/traces/box/calc.js +++ b/src/traces/box/calc.js @@ -62,7 +62,7 @@ module.exports = function calc(gd, trace) { pos0 = trace.name; } else pos0 = gd.numboxes; - pos0 = posAxis.d2c(pos0, trace[posLetter + 'calendar']); + pos0 = posAxis.d2c(pos0, 0, trace[posLetter + 'calendar']); pos = val.map(function() { return pos0; }); } return pos; diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index 1abc4e0db28..59af96a2b7a 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -228,10 +228,10 @@ function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, ax) { var calendar = trace[ax._id.charAt(0) + 'calendar']; - if(isHist || ax.type === 'category') v0 = ax.r2c(v0In, calendar) || 0; + if(isHist || ax.type === 'category') v0 = ax.r2c(v0In, 0, calendar) || 0; else if(Array.isArray(arrayIn) && arrayIn.length === 1) v0 = arrayIn[0]; else if(v0In === undefined) v0 = 0; - else v0 = ax.d2c(v0In, calendar); + else v0 = ax.d2c(v0In, 0, calendar); for(i = (isContour || isGL2D) ? 0 : -0.5; i < numbricks; i++) { arrayOut.push(v0 + dv * i); diff --git a/src/traces/heatmap/convert_column_xyz.js b/src/traces/heatmap/convert_column_xyz.js index e3f39f2b227..1798b4e93b8 100644 --- a/src/traces/heatmap/convert_column_xyz.js +++ b/src/traces/heatmap/convert_column_xyz.js @@ -18,7 +18,9 @@ module.exports = function convertColumnXYZ(trace, xa, ya) { zCol = trace.z, textCol = trace.text, colLen = Math.min(xCol.length, yCol.length, zCol.length), - hasColumnText = (textCol !== undefined && !Array.isArray(textCol[0])); + hasColumnText = (textCol !== undefined && !Array.isArray(textCol[0])), + xcalendar = trace.xcalendar, + ycalendar = trace.ycalendar; var i; @@ -26,8 +28,8 @@ module.exports = function convertColumnXYZ(trace, xa, ya) { if(colLen < yCol.length) yCol = yCol.slice(0, colLen); for(i = 0; i < colLen; i++) { - xCol[i] = xa.d2c(xCol[i], trace.xcalendar); - yCol[i] = ya.d2c(yCol[i], trace.ycalendar); + xCol[i] = xa.d2c(xCol[i], 0, xcalendar); + yCol[i] = ya.d2c(yCol[i], 0, ycalendar); } var xColdv = Lib.distinctVals(xCol), diff --git a/src/traces/histogram/calc.js b/src/traces/histogram/calc.js index ece6f851f75..c4333d979b3 100644 --- a/src/traces/histogram/calc.js +++ b/src/traces/histogram/calc.js @@ -32,7 +32,8 @@ module.exports = function calc(gd, trace) { pa = Axes.getFromId(gd, trace.orientation === 'h' ? (trace.yaxis || 'y') : (trace.xaxis || 'x')), maindata = trace.orientation === 'h' ? 'y' : 'x', - counterdata = {x: 'y', y: 'x'}[maindata]; + counterdata = {x: 'y', y: 'x'}[maindata], + calendar = trace[maindata + 'calendar']; cleanBins(trace, pa, maindata); @@ -40,7 +41,7 @@ module.exports = function calc(gd, trace) { var pos0 = pa.makeCalcdata(trace, maindata); // calculate the bins if((trace['autobin' + maindata] !== false) || !(maindata + 'bins' in trace)) { - trace[maindata + 'bins'] = Axes.autoBin(pos0, pa, trace['nbins' + maindata]); + trace[maindata + 'bins'] = Axes.autoBin(pos0, pa, trace['nbins' + maindata], false, calendar); // copy bin info back to the source data. trace._input[maindata + 'bins'] = trace[maindata + 'bins']; @@ -64,8 +65,8 @@ module.exports = function calc(gd, trace) { binfunc = binFunctions.count, normfunc = normFunctions[norm], doavg = false, - rawCounterData, - calendar = trace[maindata + 'calendar']; + pr2c = function(v) { return pa.r2c(v, 0, calendar); }, + rawCounterData; if(Array.isArray(trace[counterdata]) && func !== 'count') { rawCounterData = trace[counterdata]; @@ -75,13 +76,13 @@ module.exports = function calc(gd, trace) { // create the bins (and any extra arrays needed) // assume more than 5000 bins is an error, so we don't crash the browser - i = pa.r2c(binspec.start, calendar); + i = pr2c(binspec.start); // decrease end a little in case of rounding errors - binend = pa.r2c(binspec.end) + (i - Axes.tickIncrement(i, binspec.size)) / 1e6; + binend = pr2c(binspec.end) + (i - Axes.tickIncrement(i, binspec.size, false, calendar)) / 1e6; while(i < binend && pos.length < 5000) { - i2 = Axes.tickIncrement(i, binspec.size); + i2 = Axes.tickIncrement(i, binspec.size, false, calendar); pos.push((i + i2) / 2); size.push(sizeinit); // nonuniform bins (like months) we need to search, @@ -97,8 +98,8 @@ module.exports = function calc(gd, trace) { // we already have this, but uniform with start/end/size they're still strings. if(!nonuniformBins && pa.type === 'date') { bins = { - start: pa.r2c(bins.start, calendar), - end: pa.r2c(bins.end, calendar), + start: pr2c(bins.start), + end: pr2c(bins.end), size: bins.size }; } diff --git a/src/traces/histogram2d/calc.js b/src/traces/histogram2d/calc.js index 1f524e34ee0..e9550ec9bef 100644 --- a/src/traces/histogram2d/calc.js +++ b/src/traces/histogram2d/calc.js @@ -23,6 +23,12 @@ module.exports = function calc(gd, trace) { x = trace.x ? xa.makeCalcdata(trace, 'x') : [], ya = Axes.getFromId(gd, trace.yaxis || 'y'), y = trace.y ? ya.makeCalcdata(trace, 'y') : [], + xcalendar = trace.xcalendar, + ycalendar = trace.ycalendar, + xr2c = function(v) { return xa.r2c(v, 0, xcalendar); }, + yr2c = function(v) { return ya.r2c(v, 0, ycalendar); }, + xc2r = function(v) { return xa.c2r(v, 0, xcalendar); }, + yc2r = function(v) { return ya.c2r(v, 0, ycalendar); }, x0, dx, y0, @@ -40,22 +46,26 @@ module.exports = function calc(gd, trace) { // calculate the bins if(trace.autobinx || !('xbins' in trace)) { - trace.xbins = Axes.autoBin(x, xa, trace.nbinsx, '2d'); + trace.xbins = Axes.autoBin(x, xa, trace.nbinsx, '2d', xcalendar); if(trace.type === 'histogram2dcontour') { // the "true" last argument reverses the tick direction (which we can't // just do with a minus sign because of month bins) - trace.xbins.start = xa.c2r(Axes.tickIncrement(xa.r2c(trace.xbins.start), trace.xbins.size, true)); - trace.xbins.end = xa.c2r(Axes.tickIncrement(xa.r2c(trace.xbins.end), trace.xbins.size)); + trace.xbins.start = xc2r(Axes.tickIncrement( + xr2c(trace.xbins.start), trace.xbins.size, true, xcalendar)); + trace.xbins.end = xc2r(Axes.tickIncrement( + xr2c(trace.xbins.end), trace.xbins.size, false, xcalendar)); } // copy bin info back to the source data. trace._input.xbins = trace.xbins; } if(trace.autobiny || !('ybins' in trace)) { - trace.ybins = Axes.autoBin(y, ya, trace.nbinsy, '2d'); + trace.ybins = Axes.autoBin(y, ya, trace.nbinsy, '2d', ycalendar); if(trace.type === 'histogram2dcontour') { - trace.ybins.start = ya.c2r(Axes.tickIncrement(ya.r2c(trace.ybins.start), trace.ybins.size, true)); - trace.ybins.end = ya.c2r(Axes.tickIncrement(ya.r2c(trace.ybins.end), trace.ybins.size)); + trace.ybins.start = yc2r(Axes.tickIncrement( + yr2c(trace.ybins.start), trace.ybins.size, true, ycalendar)); + trace.ybins.end = yc2r(Axes.tickIncrement( + yr2c(trace.ybins.end), trace.ybins.size, false, ycalendar)); } trace._input.ybins = trace.ybins; } @@ -99,11 +109,11 @@ module.exports = function calc(gd, trace) { // decrease end a little in case of rounding errors var binspec = trace.xbins, - binStart = xa.r2c(binspec.start), - binEnd = xa.r2c(binspec.end) + - (binStart - Axes.tickIncrement(binStart, binspec.size)) / 1e6; + binStart = xr2c(binspec.start), + binEnd = xr2c(binspec.end) + + (binStart - Axes.tickIncrement(binStart, binspec.size, false, xcalendar)) / 1e6; - for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size)) { + for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size, false, xcalendar)) { onecol.push(sizeinit); if(nonuniformBinsX) xbins.push(i); if(doavg) zerocol.push(0); @@ -112,16 +122,16 @@ module.exports = function calc(gd, trace) { var nx = onecol.length; x0 = trace.xbins.start; - var x0c = xa.r2c(x0); + var x0c = xr2c(x0); dx = (i - x0c) / nx; - x0 = xa.c2r(x0c + dx / 2); + x0 = xc2r(x0c + dx / 2); binspec = trace.ybins; - binStart = ya.r2c(binspec.start); - binEnd = ya.r2c(binspec.end) + - (binStart - Axes.tickIncrement(binStart, binspec.size)) / 1e6; + binStart = yr2c(binspec.start); + binEnd = yr2c(binspec.end) + + (binStart - Axes.tickIncrement(binStart, binspec.size, false, ycalendar)) / 1e6; - for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size)) { + for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size, false, ycalendar)) { z.push(onecol.concat()); if(nonuniformBinsY) ybins.push(i); if(doavg) counts.push(zerocol.concat()); @@ -130,9 +140,9 @@ module.exports = function calc(gd, trace) { var ny = z.length; y0 = trace.ybins.start; - var y0c = ya.r2c(y0); + var y0c = yr2c(y0); dy = (i - y0c) / ny; - y0 = ya.c2r(y0c + dy / 2); + y0 = yc2r(y0c + dy / 2); if(densitynorm) { xinc = onecol.map(function(v, i) { @@ -149,15 +159,15 @@ module.exports = function calc(gd, trace) { // we already have this, but uniform with start/end/size they're still strings. if(!nonuniformBinsX && xa.type === 'date') { xbins = { - start: xa.r2c(xbins.start), - end: xa.r2c(xbins.end), + start: xr2c(xbins.start), + end: xr2c(xbins.end), size: xbins.size }; } if(!nonuniformBinsY && ya.type === 'date') { ybins = { - start: ya.r2c(ybins.start), - end: ya.r2c(ybins.end), + start: yr2c(ybins.start), + end: yr2c(ybins.end), size: ybins.size }; } diff --git a/src/traces/mesh3d/convert.js b/src/traces/mesh3d/convert.js index 9652842315a..7435526fc9f 100644 --- a/src/traces/mesh3d/convert.js +++ b/src/traces/mesh3d/convert.js @@ -75,16 +75,16 @@ proto.update = function(data) { this.data = data; // Unpack position data - function toDataCoords(axis, coord, scale) { + function toDataCoords(axis, coord, scale, calendar) { return coord.map(function(x) { - return axis.d2l(x) * scale; + return axis.d2l(x, 0, calendar) * scale; }); } var positions = zip3( - toDataCoords(layout.xaxis, data.x, scene.dataScale[0]), - toDataCoords(layout.yaxis, data.y, scene.dataScale[1]), - toDataCoords(layout.zaxis, data.z, scene.dataScale[2])); + toDataCoords(layout.xaxis, data.x, scene.dataScale[0], data.xcalendar), + toDataCoords(layout.yaxis, data.y, scene.dataScale[1], data.ycalendar), + toDataCoords(layout.zaxis, data.z, scene.dataScale[2], data.zcalendar)); var cells; if(data.i && data.j && data.k) { diff --git a/src/traces/ohlc/transform.js b/src/traces/ohlc/transform.js index fc0e5bbc946..c1e90ddd00f 100644 --- a/src/traces/ohlc/transform.js +++ b/src/traces/ohlc/transform.js @@ -139,12 +139,13 @@ exports.calcTransform = function calcTransform(gd, trace, opts) { if(trace._fullInput.x) { appendX = function(i) { var xi = trace.x[i], - xcalc = xa.d2c(xi); + xcalendar = trace.xcalendar, + xcalc = xa.d2c(xi, 0, xcalendar); x.push( - xa.c2d(xcalc - tickWidth), + xa.c2d(xcalc - tickWidth, 0, xcalendar), xi, xi, xi, xi, - xa.c2d(xcalc + tickWidth), + xa.c2d(xcalc + tickWidth, 0, xcalendar), null); }; } @@ -237,7 +238,8 @@ function convertTickWidth(gd, xa, trace) { // - handle trace of length 1 separately. if(_trace.x && _trace.x.length > 1) { - var _minDiff = Lib.distinctVals(_trace.x.map(xa.d2c)).minDiff; + var xcalc = Lib.simpleMap(_trace.x, xa.d2c, 0, trace.xcalendar), + _minDiff = Lib.distinctVals(xcalc).minDiff; minDiff = Math.min(minDiff, _minDiff); } } diff --git a/src/traces/scatter/plot.js b/src/traces/scatter/plot.js index 3cd4663ea40..a026a3dd2fa 100644 --- a/src/traces/scatter/plot.js +++ b/src/traces/scatter/plot.js @@ -496,8 +496,8 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition function selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll) { var xa = plotinfo.xaxis, ya = plotinfo.yaxis, - xr = d3.extent(xa.range.map(xa.r2l).map(xa.l2c)), - yr = d3.extent(ya.range.map(ya.r2l).map(ya.l2c)); + xr = d3.extent(Lib.simpleMap(xa.range, xa.r2c)), + yr = d3.extent(Lib.simpleMap(ya.range, ya.r2c)); var trace = cdscatter[0].trace; if(!subTypes.hasMarkers(trace)) return; diff --git a/src/traces/scatter3d/convert.js b/src/traces/scatter3d/convert.js index c58aa8a2674..b16a08d667c 100644 --- a/src/traces/scatter3d/convert.js +++ b/src/traces/scatter3d/convert.js @@ -177,14 +177,17 @@ function convertPlotlyOptions(scene, data) { yc, y = data.y || [], zc, z = data.z || [], len = x.length, + xcalendar = data.xcalendar, + ycalendar = data.ycalendar, + zcalendar = data.zcalendar, text; // Convert points for(i = 0; i < len; i++) { // sanitize numbers and apply transforms based on axes.type - xc = xaxis.d2l(x[i]) * scaleFactor[0]; - yc = yaxis.d2l(y[i]) * scaleFactor[1]; - zc = zaxis.d2l(z[i]) * scaleFactor[2]; + xc = xaxis.d2l(x[i], 0, xcalendar) * scaleFactor[0]; + yc = yaxis.d2l(y[i], 0, ycalendar) * scaleFactor[1]; + zc = zaxis.d2l(z[i], 0, zcalendar) * scaleFactor[2]; points[i] = [xc, yc, zc]; } diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 3ce15b2e7fc..a5e07c095c5 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -399,12 +399,8 @@ proto.updateFancy = function(options) { ptrX = 0, ptrY = 0; - var getX = (xaxis.type === 'log') ? - function(x) { return xaxis.d2l(x); } : - function(x) { return x; }; - var getY = (yaxis.type === 'log') ? - function(y) { return yaxis.d2l(y); } : - function(y) { return y; }; + var getX = (xaxis.type === 'log') ? xaxis.d2l : function(x) { return x; }; + var getY = (yaxis.type === 'log') ? yaxis.d2l : function(y) { return y; }; var i, j, xx, yy, ex0, ex1, ey0, ey1; diff --git a/src/traces/surface/convert.js b/src/traces/surface/convert.js index fdea7f9599d..a430cd4bef1 100644 --- a/src/traces/surface/convert.js +++ b/src/traces/surface/convert.js @@ -61,9 +61,9 @@ proto.handlePick = function(selection) { var sceneLayout = this.scene.fullSceneLayout; selection.dataCoordinate = [ - sceneLayout.xaxis.d2l(traceCoordinate[0]) * this.scene.dataScale[0], - sceneLayout.yaxis.d2l(traceCoordinate[1]) * this.scene.dataScale[1], - sceneLayout.zaxis.d2l(traceCoordinate[2]) * this.scene.dataScale[2] + sceneLayout.xaxis.d2l(traceCoordinate[0], 0, this.data.xcalendar) * this.scene.dataScale[0], + sceneLayout.yaxis.d2l(traceCoordinate[1], 0, this.data.ycalendar) * this.scene.dataScale[1], + sceneLayout.zaxis.d2l(traceCoordinate[2], 0, this.data.zcalendar) * this.scene.dataScale[2] ]; var text = this.data.text; @@ -219,30 +219,30 @@ proto.update = function(data) { zcalendar = data.zcalendar; fill(coords[2], function(row, col) { - return zaxis.d2l(z[col][row], zcalendar) * scaleFactor[2]; + return zaxis.d2l(z[col][row], 0, zcalendar) * scaleFactor[2]; }); // coords x if(Array.isArray(x[0])) { fill(xc, function(row, col) { - return xaxis.d2l(x[col][row], xcalendar) * scaleFactor[0]; + return xaxis.d2l(x[col][row], 0, xcalendar) * scaleFactor[0]; }); } else { // ticks x fill(xc, function(row) { - return xaxis.d2l(x[row], xcalendar) * scaleFactor[0]; + return xaxis.d2l(x[row], 0, xcalendar) * scaleFactor[0]; }); } // coords y if(Array.isArray(y[0])) { fill(yc, function(row, col) { - return yaxis.d2l(y[col][row], ycalendar) * scaleFactor[1]; + return yaxis.d2l(y[col][row], 0, ycalendar) * scaleFactor[1]; }); } else { // ticks y fill(yc, function(row, col) { - return yaxis.d2l(y[col], ycalendar) * scaleFactor[1]; + return yaxis.d2l(y[col], 0, ycalendar) * scaleFactor[1]; }); } diff --git a/src/transforms/filter.js b/src/transforms/filter.js index bef0cb540a9..ec6454dd0e8 100644 --- a/src/transforms/filter.js +++ b/src/transforms/filter.js @@ -233,8 +233,8 @@ function getFilterFunc(opts, d2c, targetCalendar) { return array.indexOf(operation) !== -1; } - var d2cValue = opts.calendar ? function(v) { return d2c(v, opts.calendar); } : d2c, - d2cTarget = targetCalendar ? function(v) { return d2c(v, targetCalendar); } : d2c; + var d2cValue = function(v) { return d2c(v, 0, opts.calendar); }, + d2cTarget = function(v) { return d2c(v, 0, targetCalendar); }; var coercedValue; From 13cf6b17e06f945a4c8b06586e346d7dabb5dad0 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 3 Dec 2016 23:57:20 -0500 Subject: [PATCH 06/30] world-cals image mock --- test/image/baselines/world-cals.png | Bin 0 -> 72310 bytes test/image/mocks/world-cals.json | 443 ++++++++++++++++++++++++++++ 2 files changed, 443 insertions(+) create mode 100644 test/image/baselines/world-cals.png create mode 100644 test/image/mocks/world-cals.json diff --git a/test/image/baselines/world-cals.png b/test/image/baselines/world-cals.png new file mode 100644 index 0000000000000000000000000000000000000000..a5263a73199c65fe788cbfda711bc27c2d51af8d GIT binary patch literal 72310 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 filtered", + "transforms": [ + { + "type": "filter", + "operation": "[]", + "calendar": "jalali", + "value": [ + "0818-08", + "0819-06" + ], + "target": "y" + } + ], + "uid": "cf8dad", + "ybins": { + "start": "5200-12-29 12:00", + "end": "5200-01-03 12:00", + "size": 86400000 + } + } + ], + "layout": { + "xaxis": { + "calendar": "ummalqura", + "title": "ummalqura axis", + "domain": [ + 0, + 0.8 + ], + "type": "date", + "range": [ + "1388-08-05 16:38:43.314", + "1392-01-29 11:07:58.7911" + ], + "autorange": true + }, + "xaxis2": { + "domain": [ + 0.9, + 1 + ], + "range": [ + 0, + 3.1578947368421053 + ], + "autorange": true + }, + "yaxis2": { + "anchor": "x2", + "calendar": "nepali", + "title": "nepali axis", + "type": "date", + "range": [ + "1496-11-30 12:00", + "1496-12-03 12:00" + ], + "autorange": true + }, + "width": 1200, + "height": 400, + "annotations": [ + { + "x": "1389-06-05", + "y": 4, + "text": "ethiopian
contour", + "showarrow": false + }, + { + "x": "1390-01-10", + "y": 4, + "text": "hebrew
heatmap", + "showarrow": false + }, + { + "x": "1389-08-01", + "y": 2, + "text": "islamic
hist2d", + "showarrow": false + }, + { + "x": "1390-03-15", + "y": 2, + "text": "julian
hist2dcontour", + "showarrow": false + } + ], + "yaxis": { + "type": "linear", + "range": [ + -3.5, + 5 + ], + "autorange": true + } + } +} \ No newline at end of file From 165125b4dd7779ee66f3c626549bc57d2dc9f3af Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sun, 4 Dec 2016 00:39:21 -0500 Subject: [PATCH 07/30] fix 3d calendar attributes --- src/traces/mesh3d/attributes.js | 3 +++ src/traces/scatter3d/attributes.js | 4 +++- src/traces/surface/attributes.js | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/traces/mesh3d/attributes.js b/src/traces/mesh3d/attributes.js index c726e23c03d..f49f1a8ec62 100644 --- a/src/traces/mesh3d/attributes.js +++ b/src/traces/mesh3d/attributes.js @@ -37,6 +37,9 @@ module.exports = { 'jointly represent the X, Y and Z coordinates of the nth vertex.' ].join(' ') }, + xcalendar: surfaceAtts.xcalendar, + ycalendar: surfaceAtts.ycalendar, + zcalendar: surfaceAtts.zcalendar, i: { valType: 'data_array', diff --git a/src/traces/scatter3d/attributes.js b/src/traces/scatter3d/attributes.js index 44c6a3d83c9..ebc7fcdb040 100644 --- a/src/traces/scatter3d/attributes.js +++ b/src/traces/scatter3d/attributes.js @@ -67,7 +67,9 @@ module.exports = { }, xcalendar: scatterAttrs.xcalendar, ycalendar: scatterAttrs.ycalendar, - zcalendar: scatterAttrs.zcalendar, + zcalendar: extendFlat({}, scatterAttrs.xcalendar, { + description: 'Sets the calendar system to use with `z` date data' + }), text: extendFlat({}, scatterAttrs.text, { description: [ diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js index fa2bf9a17ed..c442abced7f 100644 --- a/src/traces/surface/attributes.js +++ b/src/traces/surface/attributes.js @@ -112,7 +112,9 @@ module.exports = { }, xcalendar: scatterAttrs.xcalendar, ycalendar: scatterAttrs.ycalendar, - zcalendar: scatterAttrs.zcalendar, + zcalendar: extendFlat({}, scatterAttrs.xcalendar, { + description: 'Sets the calendar system to use with `z` date data' + }), text: { valType: 'data_array', From 86b31eaff0ce6243a7f445e27d1bbb91d7f88ace Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 5 Dec 2016 13:09:18 -0500 Subject: [PATCH 08/30] fix gl3d with world calendars --- src/traces/mesh3d/defaults.js | 6 +- src/traces/scatter3d/defaults.js | 8 +- src/traces/surface/defaults.js | 4 + test/image/baselines/gl3d_scatter-date.png | Bin 22638 -> 0 bytes test/image/baselines/gl3d_world-cals.png | Bin 0 -> 57621 bytes test/image/mocks/gl3d_scatter-date.json | 13 --- test/image/mocks/gl3d_world-cals.json | 129 +++++++++++++++++++++ 7 files changed, 143 insertions(+), 17 deletions(-) delete mode 100644 test/image/baselines/gl3d_scatter-date.png create mode 100644 test/image/baselines/gl3d_world-cals.png delete mode 100644 test/image/mocks/gl3d_scatter-date.json create mode 100644 test/image/mocks/gl3d_world-cals.json diff --git a/src/traces/mesh3d/defaults.js b/src/traces/mesh3d/defaults.js index 54feaaad32f..47c24d484b5 100644 --- a/src/traces/mesh3d/defaults.js +++ b/src/traces/mesh3d/defaults.js @@ -20,10 +20,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } // read in face/vertex properties - function readComponents(array) { + function readComponents(array, doCalendar) { var ret = array.map(function(attr) { var result = coerce(attr); + if(doCalendar) coerce(attr + 'calendar', layout.calendar); + if(result && Array.isArray(result)) return result; return null; }); @@ -33,7 +35,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout }) && ret; } - var coords = readComponents(['x', 'y', 'z']); + var coords = readComponents(['x', 'y', 'z'], true); var indices = readComponents(['i', 'j', 'k']); if(!coords) { diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js index 9dcf04788f8..45b420c149b 100644 --- a/src/traces/scatter3d/defaults.js +++ b/src/traces/scatter3d/defaults.js @@ -26,7 +26,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var len = handleXYZDefaults(traceIn, traceOut, coerce); + var len = handleXYZDefaults(traceIn, traceOut, coerce, layout); if(!len) { traceOut.visible = false; return; @@ -66,12 +66,16 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout errorBarsSupplyDefaults(traceIn, traceOut, defaultColor, {axis: 'x', inherit: 'z'}); }; -function handleXYZDefaults(traceIn, traceOut, coerce) { +function handleXYZDefaults(traceIn, traceOut, coerce, layout) { var len = 0, x = coerce('x'), y = coerce('y'), z = coerce('z'); + coerce('xcalendar', layout.calendar); + coerce('ycalendar', layout.calendar); + coerce('zcalendar', layout.calendar); + if(x && y && z) { len = Math.min(x.length, y.length, z.length); if(len < x.length) traceOut.x = x.slice(0, len); diff --git a/src/traces/surface/defaults.js b/src/traces/surface/defaults.js index a03da51d3a5..c104c93af6d 100644 --- a/src/traces/surface/defaults.js +++ b/src/traces/surface/defaults.js @@ -34,6 +34,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('x'); coerce('y'); + coerce('xcalendar', layout.calendar); + coerce('ycalendar', layout.calendar); + coerce('zcalendar', layout.calendar); + if(!Array.isArray(traceOut.x)) { // build a linearly scaled x traceOut.x = []; diff --git a/test/image/baselines/gl3d_scatter-date.png b/test/image/baselines/gl3d_scatter-date.png deleted file mode 100644 index 2a60ee8b50ebdeea2386b3f4b3887bdfcdb634da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22638 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
MMCb@E}5X3BJWwUM!7*9W*zJ5y@r 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 LtDnm{r-UW|H$lL0 diff --git a/test/image/baselines/gl3d_world-cals.png b/test/image/baselines/gl3d_world-cals.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd0581a7e44af05bea762529f8b3323f45b7bf7 GIT binary patch literal 57621 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�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 b-J(_A!4NZr@0x>Wx}zZbT&7sc#Q*;QRzVhb literal 0 HcmV?d00001 diff --git a/test/image/mocks/gl3d_scatter-date.json b/test/image/mocks/gl3d_scatter-date.json deleted file mode 100644 index 65bdf83a472..00000000000 --- a/test/image/mocks/gl3d_scatter-date.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "data":[ - { - "y":[1.1,1.16,1.16,1.2,1.18,1.31,1.3,1.34,1.38,1.39,1.35,1.37,1.39], - "z":[1.1,1.16,1.16,1.2,1.18,1.31,1.3,1.34,1.38,1.39,1.35,1.37,1.39], - "x":["2013-07","2013-08","2013-09","2013-10","2013-11","2013-12","2014-01","2014-02","2014-03","2014-04","2014-05","2014-06","2014-07"], - "type":"scatter3d" - } - ], - "layout": { - "title": "Date values" - } -} diff --git a/test/image/mocks/gl3d_world-cals.json b/test/image/mocks/gl3d_world-cals.json new file mode 100644 index 00000000000..303b222e9bf --- /dev/null +++ b/test/image/mocks/gl3d_world-cals.json @@ -0,0 +1,129 @@ +{ + "data": [ + { + "i": [ + 0, + 0, + 0, + 1 + ], + "j": [ + 1, + 2, + 3, + 2 + ], + "k": [ + 2, + 3, + 1, + 3 + ], + "facecolor": [ + "rgb(0, 0, 0)", + "rgb(255,0,0)", + "rgb(0,255,0)", + "rgb(0,0,255)" + ], + "type": "mesh3d", + "x": [ + "2000-01-01", + "2000-02-01", + "2000-03-01", + "2000-01-01" + ], + "xcalendar": "ethiopian", + "y": [ + "0100-01-01", + "0100-01-01", + "0100-02-01", + "0100-03-01" + ], + "ycalendar": "taiwan", + "z": [ + "2550-01-01", + "2550-03-01", + "2550-01-01", + "2550-02-01" + ], + "zcalendar": "thai" + }, + { + "x": [ + "2000-01-01", + "2000-01-01", + "2000-03-01", + "2000-03-01" + ], + "xcalendar": "ethiopian", + "y": [ + "0100-01-01", + "0100-03-01", + "0100-01-01", + "0100-03-01" + ], + "ycalendar": "taiwan", + "z": [ + "2550-01-01", + "2550-03-01", + "2550-03-01", + "2550-01-01" + ], + "zcalendar": "thai", + "type": "scatter3d" + }, + { + "x": [ + "2000-01-01", + "2000-03-01" + ], + "xcalendar": "ethiopian", + "y": [ + "0100-01-01", + "0100-03-01" + ], + "ycalendar": "taiwan", + "z": [ + [ + "2550-01-25", + "2550-02-10" + ], + [ + "2550-02-10", + "2550-01-25" + ] + ], + "zcalendar": "thai", + "type": "surface", + "showscale": false, + "surfacecolor": [[0, 1],[2, 3]] + } + ], + "layout": { + "scene": { + "xaxis": { + "title": "taiwan", + "calendar": "taiwan", + "type": "date" + }, + "yaxis": { + "title": "gregorian", + "type": "date" + }, + "zaxis": { + "title": "ethiopian", + "calendar": "ethiopian", + "type": "date" + }, + "camera": { + "eye": { + "x": -1.8, + "y": 1.38, + "z": 0.75 + } + } + }, + "width": 800, + "height": 700 + } +} \ No newline at end of file From 78b6646c912e0c150492c709e9c980edb45a423e Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 5 Dec 2016 15:45:40 -0500 Subject: [PATCH 09/30] test world-cals in scattergl & gl2d axes --- test/image/baselines/gl2d_date_axes.png | Bin 21361 -> 22424 bytes test/image/mocks/gl2d_date_axes.json | 58 ++++++++++++++---------- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/test/image/baselines/gl2d_date_axes.png b/test/image/baselines/gl2d_date_axes.png index bc2b828d88a59ec03436d18146cbffb13b52646c..96fd04f20abfffe31f868c08e3ab7a5dd865d6e2 100644 GIT binary patch literal 22424 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

o?K&Q|qQbB+5Lql0_ z#`>{)pwV;SBuT4(l;0vKDzWLL&)Wl5rTGF@Nb=<(m)5Fv%Jc<_#7|e=qnAcpi*}CW z@80*uK_pt;P4xGVdsdwZB59`!Rg;hWA*n7a?PIv~R+xW3o(|i-w#!+b_UepAd=8h?>9h?=<~qP1B5izrd@+a~+ZK S;ETHg#||4G!W^)>{(k_H3;&V; literal 21361 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 r-!Si=7y74%z7CN8_hX2{6DmLqTT9kXT7s=K1 Date: Mon, 5 Dec 2016 16:17:17 -0500 Subject: [PATCH 10/30] test world calendars with finance charts --- test/image/baselines/finance_style.png | Bin 20690 -> 24471 bytes test/image/mocks/finance_style.json | 50 ++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/test/image/baselines/finance_style.png b/test/image/baselines/finance_style.png index 6bf7d6fa7080290174fbefb24eab6aafeea5b818..c9902e3eb3f10130d867ab558a89ffc5f29e8298 100644 GIT binary patch literal 24471 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 BrON;S diff --git a/test/image/mocks/finance_style.json b/test/image/mocks/finance_style.json index c7fb15e7def..7adc4f2bdd6 100644 --- a/test/image/mocks/finance_style.json +++ b/test/image/mocks/finance_style.json @@ -90,6 +90,29 @@ 20.609242660033345, 20.9832297017119 ], + "x": [ + "0550-01-01", + "0550-01-02", + "0550-01-03", + "0550-01-04", + "0550-01-05", + "0550-01-06", + "0550-01-07", + "0550-01-08", + "0550-01-09", + "0550-01-10", + "0550-01-11", + "0550-01-12", + "0550-01-13", + "0550-01-14", + "0550-01-15", + "0550-01-16", + "0550-01-17", + "0550-01-18", + "0550-01-19", + "0550-01-20" + ], + "xcalendar": "nanakshahi", "increasing": { "line": { "color": "rgb(150, 200, 250)" @@ -196,6 +219,29 @@ 25.398644940922246, 25.093220170315078 ], + "x": [ + "0550-01-01", + "0550-01-02", + "0550-01-03", + "0550-01-04", + "0550-01-05", + "0550-01-06", + "0550-01-07", + "0550-01-08", + "0550-01-09", + "0550-01-10", + "0550-01-11", + "0550-01-12", + "0550-01-13", + "0550-01-14", + "0550-01-15", + "0550-01-16", + "0550-01-17", + "0550-01-18", + "0550-01-19", + "0550-01-20" + ], + "xcalendar": "nanakshahi", "increasing": { "line": { "color": "#d3d3d3" @@ -218,7 +264,9 @@ "xaxis": { "rangeslider": { "visible": false - } + }, + "calendar": "islamic", + "title": "Islamic dates" }, "showlegend": false, "height": 450, From 4e9a632aff0eac49e63852b91abd5b68ca1e4a0d Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 5 Dec 2016 21:51:11 -0500 Subject: [PATCH 11/30] remove mistaken paste --- src/traces/scatter/attributes.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index f23484bac8e..8b804332a3f 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -48,7 +48,6 @@ module.exports = { }, y: { valType: 'data_array', - role: 'info', description: 'Sets the y coordinates.' }, y0: { From c1c24e8acbcea7f4cb3be00765def0f7f207ed4f Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 5 Dec 2016 22:40:17 -0500 Subject: [PATCH 12/30] support world cals in rangesliders --- src/components/rangeslider/draw.js | 6 +- .../candlestick_rangeslider_thai.png | Bin 0 -> 80886 bytes .../mocks/candlestick_double-y-axis.json | 4 +- .../mocks/candlestick_rangeslider_thai.json | 1975 +++++++++++++++++ 4 files changed, 1981 insertions(+), 4 deletions(-) create mode 100644 test/image/baselines/candlestick_rangeslider_thai.png create mode 100644 test/image/mocks/candlestick_rangeslider_thai.json diff --git a/src/components/rangeslider/draw.js b/src/components/rangeslider/draw.js index 2a92d8d3091..e1f361acfe0 100644 --- a/src/components/rangeslider/draw.js +++ b/src/components/rangeslider/draw.js @@ -342,7 +342,8 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) { xaxis: { type: axisOpts.type, domain: [0, 1], - range: opts.range.slice() + range: opts.range.slice(), + calendar: axisOpts.calendar }, width: opts._width, height: opts._height, @@ -352,7 +353,8 @@ function drawRangePlot(rangeSlider, gd, axisOpts, opts) { mockFigure.layout[oppAxisName] = { domain: [0, 1], - range: oppAxisOpts.range.slice() + range: oppAxisOpts.range.slice(), + calendar: oppAxisOpts.calendar }; Plots.supplyDefaults(mockFigure); diff --git a/test/image/baselines/candlestick_rangeslider_thai.png b/test/image/baselines/candlestick_rangeslider_thai.png new file mode 100644 index 0000000000000000000000000000000000000000..60880772a075ac150740ba4ea47cf9cc4773ddcb GIT binary patch literal 80886 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 z z5IU(Y=L;}i`?SZR2Zdcyt`HTl|)5XjEogr7C5{(Fmj2hg$_F*uIw7&V%g)L#H zv*;}Rh9}~Vx+|JDr$WO1rd+@M>-j+&Tjb&P)c8^P=+%m43vm`pB20l0Moi_>otL*y zp9CTGcK^)kBp%FNT5;VqTmDVEZ?F25+Gp|pU|b|7MGTm*u^SOnck1^DJD>{CZE|K1 zbok@J`Z&6hcxY@z51iSGEf1lATyUVw-kgty!kkfr7#e3EI*A{awu3Jixb$~2BRR>y z=0fvI1`porAVOS_`ywxniePOG4}lI;bQHt`9Qq#1({Bf1+uoB+vh}b{BIHWmT6TVO zV4uAP9$eOksLm-N1P|ja8T*iF;E8Uf$ky0$6$29s9m2(*Y>XxRS0t;+5^ZizTTUvD z3DHDn_kyqSmfm`qWVNNC_OY0VI=aOAYgyHmhnuoM?K(TFar|jERZ)+GK{@UPNmmFL z-0ThM57^b+D({ZXrhwrdzu>|_RtVOtzaCx}@T=c$H&t|}u;YWDQ6_d#=N& z*fj*=;{Ve=-Jq?Am~Wrr#sCHSh8n!3`?5( zstz*Z<~cePD2_`H6G;!_<$3ofAZU2f=ggz(QKx&Y7r`LCtY%Eqb7r|Af~?~uC&b8z`upJfkAc7 z@mL1~Iv>0}nkaLhR%a&t5mA>!2^m*|J#!RIN*ecaz4@eh4Gnwh`Zlho&f zY|)PjF_9IXRf(T}+vSr)E;$d3|NXP;+-bCi@@RbIiOrboSu`bqEB9#7C#=fqo2B54 zSKqhOgK~cB@^TiFoX(&~yBGd21$G!Qp9^wusv>5S=6$8kOGdWJFHDGLw(8C$76gh%ZZ2A8J6aj4dB_I=!gWd)*LYvtXmT*q zkD_BgV_hc}kXyFLlflL<3Oa2z*Qk}MS1Q8eD87F7jB~b@eJs-n!eJXrLzYRHaF7z# zH_{n|KsI5l+%rqyY2zg5_LK)kRmViMyZhK$j%&D*cftZXUQGV0h&9w`w5w}Pxt@qg;BcYy=1!C(}hvIr;wVG>tVef9jGWOT~d zW}}2v8!imey5yU-i9VNa=6eFl7xt67zXRWXx;>`peCQ^z37!W}B0YH5p$Ckf4|+b=F8_bw{ZCm|*)xd_c`L-TpJ$_L zXwo3K(!6g85YMuvrVKpoS1yDKyUlx25|`W6u!jT<;PSFNuH6je3g`g3vCd&GDqlIp zE7eVx_R2C_MF6FiHeJY>prv!gU_b$_#D=$oegHk5_2G7dlhFpaXG?(Cl5D<_#fL|9 z01Ym2GwK(#3dA;4b9#g`ZI%z^*E&UP)lem5KgQa=4zT_+@5=YO!4gR;Xay%G&wW}M zyrN>6h5*H^6sk?;3q2$=6t0XBZU-KvjArveRxZb=MT3fvkh zD-Q-V_Zp3dta(d?`o0vX+CxUxcv=+wKWu#kRF>V=E!`n4NJ&Zwh;*t*mvnc7bT>$M zNT-0bG}7IIbW3-4=iRU0Irl&Ry=M%D19 zBoKtL;1oYR%znxI$zYG&-T3TR z?|09p)QY!-#tr+NveidW6_0lOjT}0M_H#_sQT8R}?||2hcxH4oEtx|_P4oH5cj2v~ z0P;P2bjYU%-{UE0T>lVKzY{h#s_+MT;p@AZ`=CWnDv6JK%9&oI44#BH(!KJp8zje; z>BV1^OHM`oY#4`?(xf(?c$|+wY;8%{^O#0vSjXTlSj%-+-VYm?{=Ir|Z*VBWcYIOf z;q^uO!-y%rpM8){z*(RUiK3tb3!MJAQGOQVF5@$; zpK;_)+6&;)c6iHk$K^47%k;!LtQd}9f&NvK?F*?3978oIau`3J3NcmUO`{Cj_=)Y$<+lFVVa=9%wA zz)Mm%`(jf7OcpgX`3~r1o?{F!K7b{#JCV`TtJAiIfs|?PN%@#Q=S>7 z54?wg*RlP;P^XbuYhcUn^R_qxqt)S#++*dXAh=mtt!Id!7V%7f^joeE3X1S}hcH~8 z;UO&mfk@~lQMCjhYY6PHg~U={V91+|8Qs-AE5H28*ZYbvD2)dscGf4t;tg^uNOI3& z^6>CMS$UOKlU1sOchC0v=ab*V08D#X>OHqM&$+M|UD4!Nnp$G{atWgt1L~05+63Y~ zf*JbHRv~3ec?*qtE33ufB0EN@4lF1^Ri^j++C&sS`bqvpuO>0`VZ;%oh=_>V=54iI zR^YcU+$JU_makf$_?_FaHUM(Re_-Riwl=BiJGPfEG##Koz~Z{+$A8S>jd#eU4}#hWHG6YV!hi#Ag0M5*Boxge zvkS$HVL|&|=XZ%FSV(otISRD_jq)wR=gcI=MY6$}pmMEFffHn;B5)F8BJXz++$DN$ z8?Z{)F{dFNnKVAiD*~X=rzJHUWtBN0_m3VE*u8WiOPgg;LT&3i)h>!Kj@RR4?Z`V& z!X)k5;R$TQ;?B<<9vi786PEX@hDE%*1OVh~Xf3^b)qw|s_6jFUjfwf@;z_X*TUBpL z>BRFCRPjYLdtuR+Nb*H#Nn^jsU_X*n(#>`jJdR%3&o<@B&{K9)ydMUP_3PDtc%t>B zL_w)`^Qd*uL_nf){k-1Q+m4UtXmJ+g!y$P6+0;!ihGXX)oGj}XJqY^Uqaz{j68me{ z<*TEiyrJe>mz884W8nMTBjQtL=}B-V0f1QzGLU9aDz;j&yRQh6g5AtLlOw-hQ0Zbz z7GvG3f_#ZXvIq+*!T7a@s`KO>S^{X~rjG9LWo)Ek<=N%JPB6DF#hc3lTT;nyjP_?{ zndhZZG>pNg-gZv`{J(W{r4B>tYzBWr6L6`4NC2lTRAAcwZ~@(BLUq}xuI*BDHy7rS z8Bd;E8TbF81H6QQ0x{%NMTirqgX*`-+e&HTkX5uC@k*O7)nyaXfadt}N|5IoCtPC^ zK)q{7a0HGD zqe9nintLYq2`UelS06fQo^9)D9!~Y^PBKBK!>jluD7DL1YXzF7(y)jpp+>qLn|S*w zIl5)24t#Xcj3~m`NiO;PMpgl^uyJN}hD}F70a&vJdK~A!eF%mv@Lua;7pK6%pjGf3 zKK9_53Ye=WA*pCWOQGEubM5lpj67!%&t${R8~tLtK05rVqAmg!N6(XJXy3_qO3LH- z+2K2LC>*-4MrRr>{k8%h=rrAVI5s?>a@wI&JAK9nN)hY}2@62}Zt&n&V>y?9h~j;> zo*{Vv3f(gX2ufcsf$ASZfxL4H=se4JKJsCv-k!GhjUKCHo=p0$>4M72*ry*YEuef6`+Bng!U!i04cGFy$Je=wZT<_ns)B>vplU<-;W9DjC=gh zrh7+c(t5Adg1aC?1q%SroHF~~lyetUcl@wE4CMHDL>M{%;-t(-)D6WrC6&kkWD5|; zGji{c%adop*!hSbs9k2shk`tm>-X&s6M*m}D<^>;q~~~{pjvZ>%u?Hs{4sw;R>Ldo zO9DR&;h3%Z> z{JUB?iPf6p9V|`uiXjZmd)mDQ&1%9uKev{6SSf7xUSQd=JpdPuZV%~I%ok7_zE^!S_hGDh2MMCH$7o)7c#Vq5O4@&>GNV2#wfTMIW*%AEW{ z)F}X$*LF(D{(*&_NW3G>PV4wey&+1?u?s|}Q?CYS25k0#ZE!_og6E1!bC=1 z_l?t5NZ-4Pt><^6PG~K55`CaEZpDoahngPafJm`ATLT88J`K_4^pT^o~KRY8{EB77daikvbH2mv@ zyCLW-%wk~!DJpmhbg85bqAFED%N=^H|N7+}Q|8HX&OVQhVMf;vu0p`3SiK)JKewrO zC-(_yY(z{^@d~#t_Un&*4dQ1>tm5Su`XdJdT>FGCOH3%t0V5v~4{dQQv z+%|u!8kjZ3Fjj7OG@0=czpcy5@5L=2TlJCiyO02J%l(w?D8DfjQEzlpxCcni5~cBr zk2s((*a65BYfbtey#uo~Ol!9vSV{*Pd<QEXgTvWKH)lL%uqTuxNfd?F*$@{I{U`^jDOk7~4jxH~mE2_s+`V5k%O3Yw?YlC4c zN}NaL#be_tdcA3V2nj328*s@YwRcrqDPsmx{r^R+9!_I0*>wT=U2V z@RaS}`%h>bh+_e+(I>}07PZrQ?q|HFwbKTmB+SyB^UmA}e*%hzM1>o=7GcMGTK~RB zC)>VLFT?6N9Z&A?>+WL2$IZ3Vyu-ou+8$6BhL%dg`!jliSkGHGf9-K<@=-Jv@b#;K zq5wAh21wvxO3l2157om4Aoe#+{%m+w0IY`kdH`nQvd*$ktiLyRYJ5dN_e)M8YT!Gw z?V;`V)dqV|%0MB&+>ON*gMtU8gmNw#MLz{0D4Z`Gco-_2AeqPYKw(>5>t5;Z$cE;+ z8SN92^DWE-4|iA%GvCzWR4zNk2E>e5k65NlXYE#`xUfH|nfw_b3jXICrNfY1oL>)|Ijt;cTDz z&Q`?wXF;Cqqhrw{a5^fxu+}OU9W8)? z_ly9Qj8ZnOSdWu2OFnaG9B7lQX*+MxlMPDg%!twqWzyi+@~^o8LQ=W0BI=1>16Eme z*JvlK-yxaYfLwPhelfy&VDV%&XCKv|>Oi%JwEp4M+_qi;5KOFm8?w)k;zL=&a0#FC z@y^8fj)H{FGajRpLM~1mXfMQFYXb?EidL^d3B@k^J+}>^5x}DIUPAdCigEkSjITH= zrxX^xHvMf@fcQX{v0~k{Ygb6#MhqAO&h0&fw`H>-^Qg307by0bS!E#SJSvLwh*fC+ z0u*#}UZwki4%Nx;DXHMs=>P#UL#Bu{9<5C{XR__U&VB)?alobN;WG}#QmX?!5{w_M zXae7?D7;^DgHp7!Uu1VybrQ5W2dQD5XDT}{fOLs!A`;V);Ks+tR$d|w zJ^e)~QkGa05O=?Vh<3bL(EpwQMMKx%Fq2LK7*-6Z?o!@T- z%mQRdI2QANyWFYbR+B6G;)+4AD8hOxo$yjAIt$RAc~}j84j$1r5}DNl@!9v&V^N;d9>kk%L{x(o^`V9)lBo)1Z5j6i~y1vc5 z^2~d))%Tqhlss7vz6vA)XXEnl`=hw;41~moJM~htt&2CP9;s>4=tx9ef_|={G1{`t z&}Ea6_+aip{D`Az6V8ww;zwIKQyDp$<8NM{FS;ss&FOx);K%se&mnR0z46o`$pWoe6B~jf{ z@dqevZim}&eQrts&{i0l>VsG^ZLs)!YF|K}GdS+G#+6MvJfW@71*OD02x$*Er9pv3qe}RS5b5 zS6K{2YG$}*D(YG+RO$>j_0jprY8f*N$Wk3UJ>Vddbau(bBL7)5<}zm-NkP*Ud}7`Z zFa0)$Ps55R)z=|>QI+5mvKuS^CD+FiJ{OuMKr#|4LBbcAx6B#s5$baf56g7su$E5N zo)}yGdY?8S4+|DxFeDZaKz~kwwwsRuzPb3g(YJwFe9kuElBI$YHdYD?-C`ndi-XU4 z@}9PcDf>FE7&xVe-+J;T{rN@g|DwwvZloSC{~wNjs>&INS?{~QT>->s%1d)pd5Jge zt7c;31m3JGHMvkU6x$5;{d7DViW!Fq&8mY8ygCs-#`Dqb`d=odm0L2Jf5pAl_O*e= znQC$lJ-?z~^8ZW*jkD8ZKnAaF85C_m{HrQcPMi^5eQ6&3bLb*lVTRKSI|3!#5>g5&hDqL;ce^JHw49%Itb?eKFH7jNB(A)yby02Gsz{4qL(^ECO!x-mw*3-WJo%yjr6K9Z35`nm$ShvHk znXTCPpG4ElS(3|TI06fnIL1uZ%*+Ie3$wpD@H-J3(*psmMie-GE2EBW$`8;u_}B4x z+?WA&foX5sx)?ldL7_sgl5R>y++5GwEC7hy)0pu3JA`ZMo=SImk4^%yCvI8;IdfYv zPmzW<_3Ogww!2ZdTzNv%Dm5URbAF%AltVH-skKfS6YEa-g__YGVIZshjY$#)Zg!L7 z*ZhTMwL2EaOEvLk+^-Z|CyAM!x|Lq;xBz|U^L>WY;pSwX$S{i{fRREnvQ`q~3-(y0 z@o=Wx=qY^9W?C?6?>sS5eecs;j)lua=3fjq+;;C_g)waNUpz$9$lu#a0@gZZsa$D&rV z2P9qYkTu^P-Uff)r_seWW#0h`WYaZSIk2tw8_Xwdu$WmTC1_nCsC%> z*1ZtUph<>nAXOZ|+*5Tit=Mq+S?P(c;os|Z#wW43@H|>y+BZGITVLG9J?_!+`dJO@ z1UO>|M7~|CsHjonwOtGrY`a}~=T)~;tygqT`MGO$4E;_;^qcr(=fc8TZ5b@d3!AKF z&LS>`PBvo$m)rfUp0zSEJ-zJ7?G?MLW&inrKGp=cxr6(AmTKkvEff4}MvKuluO_CS zo!1RFO0wx`Ul%Xc_k(U)OCDI`Y2T0U`mNm|od;sgjzslcCJ&clMcv(l_8WOwn*$h+ z4LeG*trCu?4GK{F@jq-7f4WUok6U*C)H})^u==PT8p6r#+;d|Ly)PX7pyfZ4ts`p zE>SheG&vvMWw8Dswn}3^#|Ylyr5<)HLK2FIhIxa_^4x9h&T_GdkjI%xxmVX$aD zR`PW_ASeYSB;elzdT384rXXWZ_c{-Rv9Px=I*R3hipb=CyqX~jC}l*65eJ^5P1ntZ z_e&~6eE7b++kwsW_ZCZWGpbMcLnORpH3>NdrlxKDd@OW9zK$gW@DLn%T{o5y(bEVGdj{0eimcIwMjJAyn`ga z-`h|@_E~f{AO~^3_V=eRBnM^gk^N%?IY07y@`uhl352XiU3@}}O?jM5Jc90i-ea%X z<=ZV>Mct*!rk^gfVwkt=FOwupd)_K%YmB5a+6g>d>J*j=sL<13^=K8-g<>6?}IC+0zJ&W zd1w5f^&$TJ+VxDwh5ZW$I)(~C-cK!`ujUc?bW>4zj330uZIwi~mydXu4topdq#PO< z!9<57uOO3?s$^5o=zhPd)6J*##NRz?8P4E;s(GT*lzWuaWI0u7dQp>U{oP<`>YjD3 zqH~Kfl4zJ}Y%jLCE26COabrh2jbZghrr%zxd{3x14n_h`O!mlATc_2FzvX%rb>6m@ zBqPu3VNd5QGhK$(>nwGNoRc&0>1i;T?IcX+9;UFcP@+*f$qh(97}8Bf>U9hP*%Yte zuu_*CoCdRW%z>UiA2p5M4D)QV4Iz-TLo2VUua;$IkP&?vy@j4TxOkpwPXy?vS<|Gv z{qc3{;9z-49M-gG$-ADjR+0iQfKT%{*gt90ub1w1dy=WkRC|h>+^vuw;K{FsNSa98 zjB!Xx+c4I6@_Ck5psg#D*a&Tv?vi#uzx_IDuDrYn(&hHXxQ2#?=!66W<7aIp(yEqmd!>!9iwq(IhLDrG zI_RO~?gAkyoT@s__*D|UYo?-<`;nTOo|pVn}EZ--xz;1PwM2hr^ntK~Uk9!1nmv59{`{B<^Lv3*VkO9l;nl8kpzTur!GB7}@{Bau+TGziJvMud^^@TGkzu*Jfj0(ourwdh zhd*q6nEr9m#WK2;`YBx@sDpopE4R3#i%muWJ;~!?gl&7*ykn-yJMGh4{!aZMKN$1lV|p|UDS#9SYp%_*2f-UC%YzK57?Wd?(_YYZ z!|D*X9bIce*w~v2LcBxAj7*@SGi#T_(_XfQIMQ>*Z#FUvgq zT8fM==zBYa`>zWz=$@gXyk%#%CGU_&9!}-NbiX?M=;XxVJh^jtxY@1~gh>_*?c zY;t%IfOi}Jq`DP?AqAwkzBTsx0hV~uxf(LOl+5)#TCTB)8ze=9BW*wL=_9_I)IL%N zz2_Xkby!|m@@DY7%3w|JYngWNIZl)jROmVfzO5E^KXp(t{q$ea*7a{d@k?{aCM_;T zlXf=@H<%VUoc=B95KN+d=8zoZ)uz-3=r{5x@5#_=qQ9cpM30sugQBe4s#hVp z)Qt8+)~?xebc`8d1l*L~?Cus4nOanyh=e^-4Bb_t(yifAkjlKN?-01gOT`gna+SAQ zhs!)4`!4zE(|^CTsH%Mm7q9 zl%uc{=F%PlbPC~J`ZzIs8knSVp8}$048W9Kk$w1j^c}X0#^?E+pFiZiBpZ52FF&vd>@oEDv%8G%3!rs?Ng|Z_CzUh<)Ic!cClF}H zx}eP(UT)iwo)9*GEJ{((wXSf2>dFk=1P$G@MsU55m>T5X0X5*+UIqwxjOyoVi=UWO zlJq~P(I13K3Wh8Pwcj(vIFa^mE{(-0Dd6=XLcqVK+V3(<1T1NEv!z6e@I>G{07VJS z$YrfDk(JvUCZ$BWz;r$+-oyjDhw#%lkll>7{Ch7v%)j@Fv+0NM!%j1s1r!qU)Dwyk zK@6otf4QuY!(2-h&OMd#i~#QSqLK$ zIq%y=D{EiR#ED^#X^KJ1lmCL;|zNN&8Igum44@C85J zRa*Itm?#+V?c+sED5X|6)Z2Rp!@<`d?*cnVw(`Fn5a-B3qV%HAd%^6u;I;L?Fy<)t>mEEsH_+dAZq289fHJS21{!j@=Iy6YWO!*#Lr2o@Rl9 z$`Vujfh!6p<-9I!#KN(u*yaW`$et9KTp^UMJQ&9BHdmKC8S1JABkvRS;KFKH<@9zO z4iGim6*h?+MvRDnmJ}+T@RifUC_!%L&thnw7bB+cCG?1ys9~YwC=D=mz5log@WsTJ z+AFXRfBkSGnO$SvKWPnsihrh+8?gX+0vGHI-C4Spc^aT+L=>4FjG!6 z@N-S5#0B(|bfZiI7XNNogIOMl-QIWZBRfFG>bqe=OF7^;^_WvzXPU#tVPf#Z6QuC@ znX@7V``-cy~<*Kc!Tu8iZ>+x@<7!aX~O~^qfQ8D zG`aNFKEtQU{?@TH=xc9`FqSGml4tf|C3m!->E3aXnL(8Ff!IKxiNfs#WmiVBMSQn* z6i8;AC3Kjv;;zaS@<4|A8w^Ns8ON+T)m@jUl@x$Ywn~x|TpvQ6VVR71wsI~2N`t+s z{n8}Ao~U)PgJ?ezW2$}Dg+yi}D_lOH;5VTjNwL5}Y+!GK!xFN_|Na9lyuKC~$BLHs zFT;T~F!Tv=G~p-8d_aM4Y_B?y#p<((^N1qy?IqP|Q}8{J*vC+U%1IQ8V?B`P@reXSWX@rt25;Qs7_O{mnRvIku_=+IuG9Xei2S z$IwhA@Kyd@KJX6+nJNexnCe_Xw+H7~wy;)FP6Es~7#z~nq0eV%TVH;09&7#C5@=h8 z&=c4-&4X~s;l+Ffz{V6`mc~-j&7N*wSX|IkPeg}&A+B<|vDWV^JtX97LRrasU|oQfdjmI7R|PMLg$k=tA-0St0( zn}0W61Ch^76$Ap?HGHo=7kq?@d+W*3;HJ*sCS3i8jAdtWp$&F7FLFS^ zi1^#kmtESQkcTg%jvwO3wLbDBbW)*}uQt9noX`iU8^MZ?xwA2#AfpHoT~fN~{Nwu8 zH{@y%8#T#6cO~iW4hllA9;tMrwZoSZ`inho-O9gPcx zjvxjQBZv2cr?K2^pF&OyKt}CR|5H!G=!6vZ;>{R4VnbP!k?9_$AzY(}gs*t{!^lal z4>r6@6PqZ16lK@1b3e(pH|Pi&kQbG9FFTcn@RnW^IrH~5P^o_0aRHY{QU;5+ogQDb z*IVIE z^w4u0$sB55^M}CONwFGVxet&fVS@sPDhP8qI>$Zcn-nIZrTGgs z&~YJ`<1wCZ$%dR>ymey&WbFA+e{jU1!{@#`n|=P(Z~}OR5tBIrU~9B#fX72_BAW?w z&pjJ!EWzL^ViG7K0#&=XLUQ#=QPdKiEN zz%VyVbma0`U<7jKpD6a5ze{e*$pZX^Clr62q={;>?#Mvtq$ExpaKMaU1EEeRx@XD| z9ZSoF2E1YZE8>)t6ggGZ;9gSq@GuQ6tq9N^J*UDaQgiFySbz#Fo^%XBAbDilLuhi` zdN5ms7{zyrl#-gts^5h)?>Md1_nZIn`>T>-9Q`%GGJilQzMKL4bxvLr064FiafNbjrvzbWD=ShpJtvq-+(veb5I>>hnTaWrX3Jgzf;4} zI0GK$j??@6(UbN^iM@xU3FT zoRbFPoA8}b-~KzLsLmfdr-M$4Q_0U`*r|I=WIHLrh7^_uYLWe*KWA9gu)qs8<8$_f z#>d+|+r1yPcBt6OKs7Y{>8|zZOSk^Y^7=n0)vx*i9yZ4#;XK_l*uU!RftymY{pfC`d9N~b#4MOxtC zKyWtDcuoJ4QB67VP-f%F!+x=O^47{xdL!34LsZwyB>&#n%K9?>=q@imkG;{Wiyve$ zt_KV29Cw!*YLbih7C$)JBcsU62&>O8TUkh2{6H&nwa8`qZVG>Q(e)8TxKG8`{;KCt18O1qPG$?3)G3{+V z@4j5jb<&KyD|?ht?uJ=vH!RrVEk_`=DDH}LhT#Fv5X7H-Dl_l#xi%7>ffgE@+&4oe z%SZ($#p_i76D4%WI4sUArLF8kegKNJls@qsQECi}CyPECHlwZCm1**N29vGiC)G11 zk=%o*`^~~4W_6T2S!FznrDE}lhmjHa@4G}X4c@Y3>Oae(p3Q79c8rBi&s|)W&o60Q zYTu!~VDsc#{h0d#E>7xhafbHpSm1rk0+nyyvuJ0HybCc~k zRKCJ`X*geJTToWko9?_=sGr1@E@Pzi#%^8A14zNnlmMCM=bZO}>;}+nTxENr zaa*Qg-5*okpdg;gum8dRvA(yWqbJ29J(lvFj`xteoKdR0!-s2h17s>87^;ylWCu{* zSZ15f0hRcWh<6&28bYj6)$Y|R(RH+`28j+~l0yaOuax@e&oMcubJWkFvR${X0?7bWu4joIL^y3R)DRT zmaU5!x9iH>TmFKnj88?8Wx4}o$rYe=Xr)tfWrkf(`)MbsZCklFzA7ApM&K{m*=u@- zDN$%vFk_h84G)Hl4p3T5E(Z=GYtBscu)Qo)|33FQI*b-|lGw6}vn{%%xYLaN`1#t> z7c8l?J?D=4cAOk4FGZUD4`XuM>ZWFT+37twlW|Vx+gXSSe&Qp2E+06bA*ry$4EMZf zozE+gGSwPGhwY;m_kkNRfx|(bXfWu-2$qyqQi`vn$Kp(DYT`=+qR~m5)}$g4L;*cL zB1WI=fCP~4QS+6utu$3eh?7KQ7nkd@FJiX-t{e-pchY-2Dbkx;x4}qWG`!Eea--G+ zUDQD#a1fh9&H{PzB#oR&25*AmsF?|uu9?py9O}Y2)Jfr&a5zWXUx`s@NGOZ~wcjt- z)$+4GJ$jxtV7RXjbol#;2hAk;0=pE4d`{R7Ry5!3b`E{8#ryX#n?vcvk=P06K%_?R z@&3lt^q5$~Y2uS;;kXSI9zOmm&_7hs9~k-eAwDTKE^fTP6FYg8F3EqGRk3}2s>~}# zML@4j5i;uVlH}0X+aKTt6UclW_;F*pRq9j1q+cnMsk>^?C3W@Ywav=sN3!He%8;`K zxHiN>!N-;6{%!!T#`8re6HAH*R$G?2iWZs1!r3!WU4^j6h&WJD^qjcZf;!2Nc1vH> z6WODFU5;gEClljm6nK|fEYm{UY%^(Y{qfZp76FF!!;k7Lgeysf^Ip|dhk_9U&SY$@ zcTwMh*-|7Um0!chMF*vEI#EEQ(4-?Jegq570U3O+f_1o39VUI)f9EX)NjOZGLW-RsZfDj$CYj}J3QN@wPk$jXKXFaQxfBiD zmpv>ylsEyv_K-?}t6`F+*`$iJQ)&8vAvz|kgu?~{8%zTe^GvGiG^g4M9wA@q)a(Ka zfLBpyM&B0SYHRE*u+J#g;MeX)?r>ON4kRV9cVhdqZdWFJf{W|*YJy=ewIk+TwcC&9 zZ#-%@i~@gk{nA&q%BHeJg(IHj;X^uF(jJG9130r zDTq}Qg5l2ucgqkIvD6!;l~b)jI7=K>cpsE2H1*z&C&Av6dY+oSoVuqyBQ-B_H&1u$ ztT&a3%6Abm7|f(vki}*7$}c48+v}Or*PBm#n4ROjE4yY_dcv(IE1ZGUz#M@u2Oi(s zkKIL)>z+Xl>RQz-J8!EQ>w>V5XaYmtE=->+<4U>BWL0ugjr4+t-RXvvX{Wkxhs>;c zzktUjBiJ22aBd8cDReZPWo854q~U-^AC&=5Q5}`_@$P1f^l@1pjMmG44UwsATcWP~ z9{rcb-yv_^IaV!^p&BT;X@1j8u-gDce#>qIb3UHURW z$s)#5_PeB5@IF{9NRhP=Qw@cDO}#A1^6k1E`2$P~khOTYA+;>h4nw6~>bPK}MFz$^ z5hqJ`Y@<(2?=4n2wv=#ik@NIz_?*mMhAENJ&v?iSG$cT_)yD6+>>wXFFGqsuwgf|! zcRI)YaZ@`km=t~ ze!#s-a$xXMUL1$~$qr@L<=s8&K}oDV_pH7O0c;No za38J@Vr^iH;>ch^_6xbM7?>SwvuwsP&SxWVIJUi5SHK=O>nYA6Vh4MHzt8qYoBbj~ z1x6QVrPB289?JoDKOglqsbvCpUx9hFqwf3QBBnYzv8H+amLYol_EAHpJu98@VP}!f{zF)iXHoubGB>%IOqJ{b(;}IUod1(ie5-~#LH39J zP35^Z#Pl;i7k2u`bUgFj{3Y_1kzSceV$K;Up?>MpYwnPW9aGCfV9~zxgi7zIgBv#_ za9ZrS!+G8A(DDJNyr8hKeS0*=wiBI`95iP4y$0v-Ne3!(+lJkW4=hF-=r{nM`g`h% zJlRdEJ$G0Z8fH5fJPNSLJdmM`7Al8(%Me$cBf1t;sX_;o(4%kMslik<+o{eLlD^L> zEyWrP=+W zmBEk;Plu6_USH_*HzqZrm~Wo2yUei&hFOJ7)?$P&^buiD~~iFX6Ab+ZPA! zsYW!AN2v2_BN^`|agY=h6$|R9R-NMFq3Muw>Br86y9^Xl4Gs8YyH6o0QCql|uH~o@!NIi}*4q)OCaI6bPWl z6u156GRSIRX1MLr@d4pUdgFeez4)W;?c2A@)#EZb0mx7&G@fTw9omTRz#@KDp7i(h z5F;lC7l!HRlJX1(t4x4ndEcrQd#1&c_whJLP#3s|E+Dcmx8IV{wCkrXk_N4-Isp_Q zJRfv>*Z4DoL!YlZh9lzpd3zLMthQJ5)*$AcoE{jrkY9QRIlRf>sxkgh86_FTep#*F@YuCvwoy*VZkuOf2FDEIH{Dx@tJ7R^GYkYoMJTK zuaEG5|M$PYabxxspiF|G1$NVR2-%GHSyLWML!wV76l7OG+^BF>T~kxImI$FOQqj^D zw@yw@UM$YGXZ&k!{$tF9nS8%~{aWzZUDnhExcf4o0-NT%=z0wZYs*fJwu7v_1!Urn zrA(^jLTX>v3A&jXS;z0#dT<6shk(LmbkdQZTd#{zs=lX`!bsYS%PekgZt9l( zP;F+75_j^~J+2mHxH?P9cs5oDK90HN8s-Uf50Fz( z_~P|&y8;T-z3csPO0%a|fHMbQQBkpbg+(c{4B2f@kF8I4f+gxT64v7&MG}R|+$5)U zx09a$LIGTzaYbZp-AjJ zZ%ZP8T9Sd|fiG-mdj7s)b(TDyos+Y;{-U2YGwb_zc+gKtVHnPpw5F!!U5n1T+PCi- z*3EcqCPURtn+`maR~_KySs6?wnVuXNg7?RLF#2X(XM=1fiLLiWQAd$BBdR}eN~U0x zry=&NFAC&1A>A5)b>G_yv6=hj{s#xiK>)b4b;{dXY`dVxmBR3mK_oX#HVLX@hUyQp zy1el1XBT}SwR+yC7qTc4qDz4)uU3tavaSd52Aq?$NR@*~-2f4VI2cqB)__(;&t6$; zDgJ9&z7sea=@kFjDwCs6k&ITPV0ZERM>jO^)Ew6XHSd_r%(7qV&2H?6K=d5rPX`() z%&9z8cK16(l3e^&b$TYIZvLzJFQ=!c@Wl^T@Hv-cz(1Crs67k$WTqpc!uKLqBw2%EF2e>CJ0&FKJL-|l zY8RYR8)!NXfB?nScGhG~qP80{9vzBp>2kZ1ZwvI>J6CLU7n@wob2fA1WKiC`++plXb< z-x}&>8|KUTWw@K<=K{lS9opk~Ex`_^@E;R2QkoRo|iZCx5G3|6fnr{x8gVuXG*Nb%n&sR$Fjkj<# z*2_RTJGd^py@tI~1;2#wpi8wlB8coa`w)Y2iO>FZ@C@BBw0I?XLgEq-G@)lG2W>Tp zU0q$hUeHj?VTls^0yNpusoL@pce}~E^&U+Ix+D`wWhZ__NlWWTN%Z=i9ynmQfKwNL z5lmzF2xh-05dFnO+G!$()qH`r)dqEKL4la(QLwI+jPi78-71Pva=~xzXh>)GTN0zw+#vc{6t4b=K@F2q)AJTq2}KSCIn|b z^`ESFU(U6xk~t}cLgBF$43Xh@$Y31%SJYotu8qfk?>}>&DERbCONY^?#-t*6Ngw{T z^7mLndzqG}4SV_>0FYC*Kv&!9uX;a~Z|B+JkL;C7w#|1^>$RLx$zURKHTkhU_ipCu zSIPr2O{Hl(0l94e7AKNgxBz(7Uq+%*hcet7xR6)?WG|@e@)+m z_(~f$on(($_`UkEOkwnDrWRKNe&T~wAFE>!kB_{rcrUVF<0CD3Kk-9K8_lHkG_6s@ z+?6Vkvbns}%pWP_n=hs`%=LJIuR+bHWz!B1@fIgAer(1nsHzqSV`1g2OUm|q&_Ejq ztKOgK2TAqf(`jWo742|L?)7I)H=C&vUt`h*u}r(v#qEC|J4-ty7{>3BHz;ma{c^C z=P$wZ6e)ckw;PE%$6#@2Go@@a>MeO*DJ`*`mXtTpoXKUdok~TYFE}sJC6cL=i8y7z z-TE>5^-rqk{Kz1q$PE#Dz4j`*c9dj%p686GeMw#D@XT4-pRa;(tc&o#m1b4oUvrNK ztZDH6wS$t`X-UFxt)+HoSi0(S3#)TwzYxo&z7+Y>J~r(hofcjUu4Sl!JVYMZdx)Xk zFG?P5z5v-4?Z@U)CwsIw;O8TH->;EPN{$XNx0bXrhTP0qd!4u3Vdl8-^mYJwX;{kks_!a|9ur?wuf6jCs)Z|6Cwq_Pqt zO8V)hYfxu3qYp-0Ka0gtN=Q4DMkcL5zb#monzu@ym7KY;vMv5*nISMl6{AnJkE>r_ z_3>x`rEeZ4gpT}7tVgOxeUP2{I@N>QoEQD{lu;*Ic{*3?l+BY%a|{f$X{J_JvIc-nOrwy-kwX4|+pV0*Aeb zGXr}b>GN5DHD>32Ddy}XF)T_uP+JhLcU;>YyLYrZ6S^uTj#(>yd$sJKp{&aU$++o` zn&|#n9i*UgSSTIJwwRopN`RRKd@O7RYxPbZTCeJUW`-&epZc@C?pJbe%>lLCTg`IH zTgo^nVLYCLk&I#0T&3QhY14Jjmlup!XKqu;%l|Z+SKj$DB2_f-_U+JWvgUtz0ir;) z3sXY#>_>HK3@nuiTP`d)+@pB|H_fSGv5bo14|TF56;%iYDmNRPk1JV~oN$Qvcc_2*jU!)(+ap z;w7k#YyyqHsTGU2h|#7Vx`rTj%g0lIFM}D^@r>yTbjZ^?DYjZALhX&Jyo0K+qitZy z-GbXfBOO60(Z-9kcGLQ=KvE%8@O-3vSTFbRsF||+0(i-SE4Md5&#$t~6`6O5>r+v6 z`jYgX$wP6x+=nHjwL>FjPi9Kh{9uthvhn9J^wlJ zR2EILGlIlaqT7|>%n3!Ugg+q%J2tq~#KeSj%vf00(mP)@Y7EOQf;w${aYKcvVEwov zJn6e3h?VRtP>o}N|4<8#q)d~-O44I|-J3i%Z>0%xjNnXweuHq@q^$Q1=V{_L8&iRS z?77uUHD`&nk`AQwXqEx2Vi72_Q*%7!`0HBOSvc%-k5J(*N{Cx%xE$-TNru5G&i_mU)@Wb6w}Dk`JfuPdgf7!KeIt3t1n*5GY2Q{ zWiS0efrQL5ZX2N46Az(zngh1R{jy>={SKg~(f$ajMQNa7pK_+`*KIN$VH+2*Jg4B% zoRX&4oFy=I{4w@3UZYBaZ$e~F7|+~-*PpQ%rIM<6YpTM?Vvvp##8^OC6$Y_R4|yd^ zq9ZM*=c`kAkp_cWm*1C~2lVd9_QrorwAd=t>bUvPB@Pr2Ep019kb#>B4JESR$SV*< zYvdV~rM$CPEL0($MS0OLC6!vobC?c6bH1M4R7wwb<1C= z&NL{dWoR#Pk~E&KogEnga*;3;F-5J%rv{P(1Uk%I%~A_p{|>xeRRhVbBOBXUK@PwM z=648pwr`ze8M#gIjIxihQpFf&#UGL=Q)D-QWqdYHu~Rgzq(!mv>f%J%a(<}Su8X9l z!3CYIkP7)W)#=u@6wQ*a6QJNSDz6h7CtC_+^ROq~xzqLGnGz6sr3`b3X7`^F;W6}r z;jshu;coTs^-#18IAXsdQh`>6UHTskW`0Pqv$tdX3o1m6@VHvgZ6vVJBoc5-m{rm` zO>Et=RPoip&=9&Nh+E=Ci7T+iUxvaxHr8QbxMjoDy%aP27D|8S6ka&utLCXn#liSj zif6frpL3f2MC5#9{AxDuIF`vfv>}39g4LwP&-C#%t>$Y0udk87tZg}94U5a4K?798 z36N%JFccO`6H+a8RtEC)N36_Xs4uNj;PT!MgQR^^&9peamfZWL!g`qCvh;e?-= zQB~ndc8R;k^zw;sWDX3M9-t3INdtR`s8iPpMnzySp`(7NbDRo3w%YyzrU1}{Hd`b{ z%K>;`C6TPlm*C6F2<|oRVkGCA!FR)KUYH4~CXs67QDm4xrt##(CIm`@wOI0|Ptq$y zn}b@$?hH8=0pqZ{S+kr%C?M`9Rn^>agh7UltLLX{;Y*?vV$oY>R^6b8?I7*g8|O(Z zEqqG4Y|VZJ^Bi)}mgGge);6Yo^P4m)FsZ4ILWkr|>#l zDqF1|H{J~h;0yb=)$yQ6vexR-NS}ZMY=UCvkc^utZLoQnqztzz6j%Q0QQ$AGA@Gqt zApIkS3AE&Y&}o&+wLkAdF@|9bF4L|U3eTvZGUbpIcgCCtWWVu}h(8+}bT;D6%r@TV z*ODdYQ0vE^oMs*)DOHt;!7}q_=63%i175wf$&?3X%TrEZ!Z@WR_Lj{~P7x<}?F+^Q zBIeY^cSAl2I@q@4OJriKiULr9ZX9coCs9Eiv$vOCWyQzTWo{_B_4+MXi7ydg{>BQ1 z>;6fX_+q_HC8}lq1cG6JH8!B`PS+-7y+nsKj-i!$k`N4J2hT|c-zM+iVhBVzoEjKjTam0)8yvQ6Mg^VPIlBSz4HaSh((rsmcm5X0hqb5`6 zMSCSB@paGoPu$E;?iZ?r;6GC(U=To-s0IJ0p9_2uZ_xiY_68w2WR=In*d;9{?q5wF9Z|Se0B!kq(tpuS|7*R#Gkg6XS3(I^2*jn%B>#VW`2YVv#of^b045}!X+nJo z!e+v1F_x)V84~utcx&+#5rFvJ8P6eI6w`T+1Sj)rBZ>w64Bea?3z!!HIS&e!fxPnK z{~zD#LhOa*{+%oNDHS+m+?If+Ve<{33c+q2{*RSTpuA|OVIl9s4D!HOYq=_{NO7cq zMsw@dcSz;o8(TEHjqiKk^}@tQ(`!@z+usmj0i6t$NlhU9;)b7Z+}GGFBc^S(gTd-U z|M}Gi1EZ`Tun~37+NJHUCRk+WFKd_TqLlZK?`S`|2LTErn||-?fPfna z!H^pTP?F=T!ngmOw7$TCWf;QSe6hHAyYvcqn8=N??5)$qy2>}x!yGkeL@2nCZPrH) zv~PY5^kx#1o3zXMy;?;2)v5a?@aR*~5qV9%li&~`cgfHp6FRDgqLiElwlPs{&7Y#K z6x-`X*6T*y`U6|_dCEB9$$|oRjy1jKOvApw0wVPfwpyKkiqLzI^2afnHtp;{`!H;5 zplST6qt4@wg(EEZ#K63?VL|yPuYcbn z!RVjCOvhlUkEPZc?o(huEKL~49qLOjZ<6eEe+oCsr<-iP@6bQ6q@Ao@bsLqq>m;OJ zc09gS+V5QFxHpp@O0Sh7t_%8cT0cMeg{w`k6Vd8(jJmMAU}B&OtBh-*v12cO`!}b> z!LOu7G8J-5d#iVap^+U-%Td{NC>uw_pjM5jF*@ot0l?xTgkp=!$qW0430hwm)v;_#h3 zNFnLpc1y(dZPRx7I@s*icy=LDhl3UHzz7lA8?p#_^xiLgu+A9D$eJ-^ZT1;m9r!|A zQuUa0L6QCbEXmM3H&~`S)RqUqN1q>xV=}!(>`f4Y%<)_M2-5~j^7{CD_s~+-u<~Y^ zD$zkLdJ=b1-k3&Zg|9i95K(EYe-;-~OmJjRR_M2e#EH2XRA?V7&I$cWdzbpbcP-t-BS&W9IQ_E=_>6+AZOO~U< zxo-W#e!as5d+d`t7ZFwE-SNjGe$dyds}_NcI#3luLNqhO+5IU>Two7wbrr9(+T`(q z6Xs#33Tu3DKMe``^_A`dU&JS4T>1A>{J-t3LIt@nBdKIWK1QvL#e}5pJ;#QE*&Cl} zQDsM`hUNX4aGfNwjW)vzKi#VImbhqq5_>N7yfLpHz*BMbD_{k-8U2IOAQCn>)S(!R z^(mJz_P^5!NL*yYJ8A6}9*f%44If1uTlgUot0Vng2^e&(`RGHu7r-cS7oX2J>ZgZt zr~7n)#l!6t@KFD);MN0-iDCSj{RFw~6F-BmBH#r~bVvuZdQ z0+((1@)uFMk8GRG-@NtwnlnR}F~5UVzk0cfNTdUvN7@86HiSWDho1{Lk6OECxE~Zl zD?fQKCXg}8`vXI#W(nNNGFN8wO{u&p?VbJck)`xOODjwz9$K5{Du#HuETpHe42g#V75E@_1WmdwkE0~aqKLP9P6i_ zAJjRM_nl-5bSz>!#T*P+gJnWnOm(8EA?lA7Oz%@{KbB9UE&qh`b(=vxuw@LP;3E0_ z#7C@(w7^;<~PwLwEy)6m#I|%M#`YD!BY3o?R>DslW#-nMyNr8 zzg9xgg4c1NnsK?ay#w(mh1!&QhiuW_Pd8fgYim7j<;GYY|Exb3mZoTX{eBy7>W;PjwF-#2+5=2-VQ(|!!m2UZ+XYa z_pa?Ksqlps2_2xc_1eu_wm`S02Ep=VI>Lf=?o)Lk^$$V?Cj0vOMzX=|FOl17X!Co@ z37v}1kF^2$eAGRIEYi3R&+2Q3KYGdLA1jbd-+&K*d zkBt6xLVL2+ZBCgOndlIvNskZrN0toxl>d;0POxrxk3}C-gXWJuXYOB}NA}bDN{0u- zOxKhdN8SZ|bP?pzs8qQ|TW3Z5BkUh-*f*3obaHnPP`+NNZ_{EeAKi~U&q6PDUPVoL z%nvsk$*FDBtl#?w!t;~xgC;gdx}IQg4-{tl5dn@MK!dB=wKiHVmn|Kj6HrV`{6J z7DWzu?II$r!icohboF8v-K0v{pO3tWQjgDR+8= z@ux+gbhiZ&(Fr9h=$-b&YF%z%nM_YEY#@23%zX_DR(P{JzaeD?@^^hkFUNhpG;pYZ zb0+pAS! zjdUdjU}kMqiohFxffYiY`Ywad^nVoHriK=td~O@q*-)s9ef{d|N@}PU;{6Y_DV2yT z$}po6%C6^kgC+IaQI5{M+mV8hsk3vdTLsn+V^KTB;(6beC3D7Hh{|D6y6Fd0CrimZ zI;-=_{i>nL>st&CqcSS1*wq;1aSqXnczaDN0*}I4Porg*ax78zO z%9J!=hr7gfv5K?A@*L_GpM=+mXH0w=5wkc=-$x7iK*MDs*EfFW2ZaCoi~p~b0v;+d zbv*UZne~ALs3ItuVjeVX$0QqWF8JZKnimhZD)bL zNA+5wPH_5<_Ltc&DU{lF20P4dAT@I_66WXCS-~9+%owz9V62^zNh%uoK+Fyx35EvM z@n9_|fInW&FK8de>7S*rq8wpgcf0B0dj0H!Fv!hV&#&$(4+PzQE z&k6e0>6yQ+rvo9nzS3^zEe>!aqVCwVqkvD8j(#`%k8<4xwoY+cb*Mt1vrBW7nYWLJ z3grjWgn@HGML^C$%E&&JlBYf9N{HFtP5@y#l~rolYq3mUu01i3#G zFVwBL$vq_2^Hwz(qDCYss)&CtXCkH%%tA{w&u3mNj(K{$5*Z^+b} zdws?&E=7hClj;s+>OhqvA21RuLkr_md8fb4sHw@SJ0$a-RJ9FW?sM4y&~h}fLkv*F9I&O(qn!vCM#(>24>2|`?uCt>aZ;gB~jnC+X+y98@PIBpR6t9E;&2ZDTB&LNBsCJY0r(EX-~`jxc(N zyq++8^Ke^`O?kkv;MtF{#b~N!|A`F0!IKP?YdcKi`(BF6K;O<)Bu+y)i?dS=F2L#> z0a%gajEa${{rOoCrSO7FgiQhZiaK&Mg&6JDHOAu)#^Vt%0rX z=W+nweVj}^(;&K!J$UjD!+Y*eR=2|O1T0aN`09K=_Utg@#pTjYZ(H0@0O0BVQh5FK zgR6i`wA`yyVO1$Et*XaXS2zE9P6Mj8`1mj`Mb)PzZ`6j7s|SA%-_>4DldwxyQ`nOj^+ z&FN%zGi2~%E(ctqBsVy2x7gT!z-f4un2~47L;(OOdG-GV1tm7F zh|#FDYqkwcY0&z<-O@BLBm~P)B7Tpyq&fMxSl-eQeyg4)0*}oRqSUxn{wx!Un~Rn_ z29h7;dma1i#i&WSIJE(j3K!tgBSec6>Eory!b+o~Usd8u+E7It@tulnEhYNS{8+^s zpuKexdS6L+nQ7Nwyl!t2^~!hLTI-M?#ncj7KWiNFSy1|}@_gc)V`l}i?P>obTxkT{ zy2G4TFHZJAxC*3!qCj`+^rrN1!q0M1?U!SM8J7ym`QSRI6V-On+twnos>#+ga9c_; z5AtGUz8{z2ckTG7Q8wl#V#EDO=z{u!@j<)u0+pt0Pl9T@!(%M*^3j5+NpXy*5sgDm zV0_dY?>P$I(vlmyWRJ?=chTb)O?Ov);$4(&Dge;=7si1e{tJAD{0q(;eklNBA$2oS zEFrmugY$4xTN793{GD{pC%unBlx*mgF@`WyhnDLo!bU*`60E1%m8K@0L=K_13H(A* zz3Tz$Zcur-e$p|2tBgErrs*N>du-Epg&nK~s<>6?{i%+%X%kjifdS z9W~MeT=3jq{u}Q!ty-Ep;408U#DiIfBivz)QU#*IQ;=t4JUy_?8?q}stQ5Nc(Au1f zca1A$9dU<8>U-Fi2A3Bg#IAgR{<+7%$NK^XMQQQcNy!0m9P5|E+I8y3iov=>=hIdi zrSQjMNx@^gIHB@qesEX8WXu^2?*27zRM{jwS94uc<`PCg^OgpHg?q_wX?|58X_b2a z%NytY2F697*`#_TIPz52#hbyOpjr7*1`a8pxce4Mo|5YuCFW~ueKVldOfr*+k`E*_Dn$pGlRntq51JJ6&csWzpF`aN2L7F|e`DV!Rc_99g|bvh0}FRSH;XgcDBoVq|A$m~wh*wb*@oBz(fMkh1UOcWPExX!apT#LNxV z;d~6?SZwrYF*z|G(L`+%)8q%d;Hz|;H22NI-c;jb-0@OG=U~NI-a+rQ*NHBVUkD}1 zrIsdlu887jt>)kCD|8#hET$aR7gP3|@^n#xkjK9^pxr~BlxV83v;F{jWGMFEjZ64rh zAS;*xtka(l_r_!Ui}o6%>xwrf9Y;z}R58peHG zGGAW$5ngsf)2E*daX-=9173F`IH_qQ&?FCtAy`OJ1VH-}+qt&|F>v*WwDQ9uZyAqT z2*1}fIuoc86F}Jh{t6ry008((E-P@#+90hyh?f29L#1pQ*bKGq^RNTKvarJn*1WJ+ zd!A6}B6wUbgo&+9#IBsaL(-{-+gr-+Z(Pr3!wUaYa4FFDVbF<-)V0+x8_;WGLoz?y zwx1q#5kPY&h!|p@R{6|atO1{~x60|cQ|xonJ9q7PGJ(zdgX{wy!JoUVJNwL0`;gX= z*4z&WotzmaG>80fPH6&#WD8Cv{|H*FdNhi3E#K6>!2yA&Ps}TEehF+jq)ypaDAi`? zyio|~#9GMIeX;7iQMLI_AV)vv;I0&p8u0mYMC2*t*ogHO zw^Pa&sPip#+0-K7W|-rBKKpy53lg@b%c_5P3=QL|=kyviB&J~h%IY3Co-U+wMQ?7G z6O@INyuf;9QQz$YJ5r&s_9p@kXjky`oT$V>!R2kynray$Ro*&5;IMJ!b?h$LpKYkZ zl_&Z*^_%`G9rj+2F=M&mtbn~uXxZk zJ9TJVy0O@JquUqIhS8ZC6e(j#1KD!23Ev_+F}eLX6uRt1D`;&?Nplux?~DlpKxW{3 z%piF|QPz0-51d*(t4naLOfYO2dIC173;570xf8;-bEy*H?g?L^#EF;j1#^w&IB^nk zS(q3FR)rqKxMg6hpx@C;pck6j^(BwYE3(Qg3X|5#tY!U9WjCCV15PoK<$0`INx%0L z6%EhPNIMP8cOSWQKQikTGy2Vi299{<7G%`_@&YWfi+9pd_Xf?pKKC>J&=O+%9T@q` zwgxtr>?-K^hE@2+UQ{jv+bUKxMN~TmXO@LtGHPcS_1AsykBebIf(uQaTE@#gs{{yU zGuWyeZ|rm|!6;RRRHEjp;11mRpKO$9m2S*Q4^9BpjrMciW&W;#H8Kf&cAPjb;p^Qv z!%NPuyV2QRYR2Y%D0FLaGuHllVZ$%Qfz&8ep;1CQTCQi{;`QSlRH`kA@#{;~0+RoE zu;Dqsu^j&pRh&@iW#)O2+qnx63+-IlcYFZ-L|;vWw|+ctLOwnd>ZHT7-D>4caMteJ zW#k{zBGHR~8mIQ?FO}YE<<_Ei_?12h{o;e}hIV9AULkbB_3?s`AvF`x zF)o=&RW;9sMTZKQ(%cD&@#eyq6_t>`NDW&=$e9L?3N!U-Xoq@mTLrZU9+i8ppJsJO zjQr!!e;uVdh%c+gxn*U(z1nL)sVUl!iYBeWsLFZDT_;=WL!&!)Sv%PCy0!R|XXVC= zu*&sDM$H*E7|81fMC!pt?48#-iiSgm12SjOw;xEoNR@jr$KH(wM-udw$vPb1eg~*A z8NCcC5xH6mip6XKl>~E?#HLlvN8?o%P4=gR+R;I$H2joCU=cV>VL(uDC;*OhKZe%2 zN1kEuEs@)IF#ExJsiS?FxD?Ib392_h$Jq{a>+loiSG;vQwCq<_m&`QoADE_xmlu^I zpJcMbL^k6TXg%#wu_R6NN^7t}?6YH%bUI92GoQzTAjBT{^h@qPxuB8374hC_X( z+7WE!s-r>m8HH2HR19_2Qcr&ZB3?T0v?oUS5iro6938bCKFGD=^EB-}>EY0hXNQ3Y zauUOxiY1BpVv%yh)j!q^Q5rwxTEM;g>HTSoFf#YgH8)|6*bh=& zgys(J)R{v;9mM2A1r$0%6b#)%zw>2VRRelqTK&dr^G>x&daVTZBP5j zWb`PRb+^qx0FNEXb~#EKb#lcrzOluI_d8SOX){LJ7OVC{($rLDFfDC%;AY@d4>0QUSjCJpU!Y#bkX+4Z9~hIgbtVjz-m5mIP2^ znf?_-y=hs1-z8y9@19N8T5yN#vpP|*$us{Ox2P{)>l{eVNCc7VrgsUcN$4PSi>A3b-zjJ!AUEImVd)XfzW9c0S+RybrrvYhbx!MW;M9X4N1u>ipcci4`3 z+g3JVe(1yZR%m2e5YANS+3!}M{cNZRIcWxWuaof4 zby%W<=+1SviP=3dak@q(v~G2HpUD8Gz?=p6 z=LVh;a)8JECgeF4&cg~#)dEA!=~aEw2RGJ)V(-^T2-csBqXn|BPgDSv&lu9ayS5zD z)kmqk&^-n39fIFe;L~69W1hLVM&SNj0O#Q7>jO3$g$Js&M?aktwBVD=rTrU+S88>! zJwmTBX5(!G{_-aY-Vq}hekXOVS|4Yj2fAK{l7Tb_mftb$yd5$A1VpNv#Cayhso}?c z4pKmDqpM3q*c?I90;O?y}u1 zradc=S5d~@nb{pbdopP3Seci=#Jzb+UpxH|+AllC>Ahx4DhL!c?(?$a$rs^Cs+lXP zGXUf!|`;B0WMDB%c zfLL(f|3mqA>?B-~Vj>Yf5opyfaZe$e+xZz$|M;j2DN%FQZC|$%)OlC?UK)4`IXi&F zY$4l4>1hts4JLC(MW2z+=`D8bo4W7|KsBkIDLv^ z^ZU20*m0H9Yf_ER*fgg3P7NMH(Y~9mou{!Chy(`rpoZEB7s*hj-H1S}KAUz>urPOZ zHY4Q1^g1o}=Cd=9tZ8Q|jsgBvKnLP7#g{@ntxWDS`t{@p_+?J?L!3b zF)HEhWWWT0Yas;YJG5`20KM6&?WP06;hp#xeeAuRSLs&QbR5q?gU#f}>1n|42xi17 z6`WaZM^q%nr8?z-t4S9k&&G>NwvaP&qnSMAtVewnLHd%zNSh(%* zuPiAdijwnJ?vyGAW4$FaM~)Do?Jnn~mXiv05<%PTlOHl0I_YMSlDqdw@i5_FD(BL3 zI21e)VPv>}uM-&(kCn5gh>xbux?57cw1OFAx@lFtzgP+MYFAH>NCR>r`E~5ai+e`+ z#P*#2nkjuZXN_E%c-I+!!((q)EDY%1M!RM6Vq;(eMUfx!-vn%uPIqQruS;yq^#j4e?v$KBdJCk3 zMz{s7CFS!)$@&9p@oor=fABQOg2?ZP|c z#})V(%Op3F8p+-w{y&2wXMMi|&H0MnM0R-3(Mf)v!CQJ3+GBeeoqUAlLjixUZbW=< z4rG4f=O|Xa-=wz7xyIQ`wVQU>D+S!(&X%P`Zg1!hv&r@n?sOSxbRn+C#l8T9Z-?1< z%1Zpu;X^p1&;ani7y>VW=(mHCa8TclF0>C?%7fZR+3#RiWdL`$;xIZy`3R{k@ejz# z<^&*iPhwAPoiQ6VI%IN}`X-0T7srhMNB8Q6SZ62<&+mv~zXV3TGY-vE&v84_1$G2% z#WP~xIu1B&lY^iw9WPp+tyXlr_n#Dz*6cyKSzD9=%;PJ;U;Onhh-%pYwclR33K{ER zbe#@l-bD$F8c^qp0>?#4rQ48A97JNw?Ac1Xc)k9|=!&INiM>{lXI&m!cl8gN#zzZp zd}A?QyE-rF0&@glT~ok$heaMKy8j}!UQh8O$bO~uFFo2&`c z)Un3jYI=yGOyGu8gz-uXPPlxDU|J0F=x>k1AY7BwoFa=p;QnUdc+nbl^l|wk;Vgpa zT91@opEMgXHBxP)r+;rwe2x%qXM<6&QS2E}A|uP5z+$mYc8nzXK^avlXdma;sj?3j z?!9zVE0cUnxUTCAKpgS)mHzkvaIu~g@-NWUb0^K>AqA)&%AUUC9!>F49&#YIt{LLg z1C!>b&O18gg>N^8q5>*Y$)nPHJ&yj)ki~5rx0hO1o?VkPG2<|W6T=1YKT*l0g3aav zF5S`fv*Oxg`G#pYkWOwE5;A5R%8OSC+LK%ecWv1<`~?u_=Mi4fpc&6bC6A6OMzB=^EQ#_a$zJ%KZ&T?U(ZrkecsBKWLI$AFc}RH;jxIL&88 zdt7AMSj@i`{X?Hd3CGY7XtVM02|-VuXiL4(>G2x5;Y;9A;&EPhj$nVh99Am9t>Ia+ zX@}kOTqQG_px-NT^(oaO;n?0rr`fVmio1q&IyAFv+2)@67`637=epU;@obcPCByw! z0l26zZs}>T)Nif2#OG|nG}dkmem8NwSwS+tkO6#VG?Gd$lNj1mfLfkJxHCACI>oZr zf+|+bJ+D9Jr%n^f_PFN;g>+pVE3c&iIx<&LnQv@jUG3xbrKRI6XfXZ@)W=%IPc5p2 z>!!=r_h}jJ?WAZdM!VzCC^@`LL-)5sZE4?lz0G4Z9oESE9KBeQE~wAAMy-N-dowaY zF>VPy%~Bq@>eFW56J%7s`7Nr;*?se42I*SRs*mGqeUqVyugZna{mIJ{ncIR)))yGl z@mc#`yI7q*zwvsEq)O|H?zZ%}pcak{Zpu+k)0bM7n)W{;I7jf*N; zS>nwyFKtg3p=HU-GK-&F%x(DND?0Z9cps2j32T^CYsj z@*=BXe0(GE?bhMHdvszI=6+L4Q^Ngv?z(bIjI=9+P-Hkh!?_TiBfLy~ zc=Z)h_%bjA34`q=T)CBkvG}=Bs8W;fY@JqExY}m#$eq<(lvP`aQx6|SpsUUxatI}p zl6a5l_>o56y_K}z;y0F*&!fU5#npn!20(J%fgbcNr}|%GN@~ST>D5@@q?X9gYXPrK z!ulm^JymUHi20_UeVjp#H7F{8)(6M#1d*kgPHnBFjCNd; z)oaib?_bIKik1vSIPVHahlo#Se-kohc1?9;LTcxKJ^~BVQ5o0C2oE_XDSd(B1Wy}- z3$A0!(rH3)6ziv?!W2Z3OYtorU%2!NAXta8|J^P0TNd$GOSqa4e;#Yp@ROVa)Gy(kKu{ZY}U?z#Y{gDaV)Lz4z(( zNi{gOC~(cRw;qppV}0c7P{YdB_j-2#)UyyUKSzJyZG!`h3j6A{R1o+lY!R0BE8%GH zV3A5E*r%<3)okJn(@|&>?aAB-13ClG0=cpIJTiIT(L(gmL@M`NB|1kxNtkp$2r(4q z{DK))a$bmmfp!z~yPaH_#w$gpw2%S>`fl;y0&S8nh* z*Z6SD^+`*?scEO#xv9E!8hyE(-xZ$!InRy=)AhJMpTe&_T1t#nhE_>#}+qL~N2 zw3z>rOE>NPn^jCw?w9(7YdOm?be@j_KPtM#;fL`ifdiabFFA{}#JnG>&WGZWp^!GX z!RJS+--Ot&-yQ3%j&+;fFYsLqpJtNaTnAyBVFVP%%D+3eKc9^Jc2GdD+#})|g#c`x%*$zx7GmV_!#@SstV zAbc*`xq0W5JwB{m>WaGWaKL1J6>P}Uu}G$lYl-1lwbb+bo;$PM&T(D0FHgt7%&gNR z$`RY_@>GJ<YC+-X7=U8Y?pO^f|dH2K=tx{EBvV=ND-&WRNHo!=!O z*Dc4mPzo{l)kcGb5+#MwJ&PVkbN02R08@z0yl}P6*+kOIr;eS z4xXxxJNd3qj&C$?fyA>$3k0(`BcRIj(PP?LaeR@tg>Y+kk-SH-Zr5mY@!=D(ulZ2# z(az!W6IuGEoCsC4ABiOkj*9_HmNXJ|gONj5BfmiZ$G-LX?t;?W%D2ez?Gm6;RrB^re^UgfV)m!<# z=#$22{H;l*);WhN)s4ZQ`&VjCw!T!OQxdJz8|l^JT?IY6KT!?De?ByGwASEfOfE`Y zgx@*OVV3pbm<`>LHwJ*p*3Jm*IW;xs(=e#F{ByJ92TrR&r#A9)r|ETpan`_)L zGiA!j?vX5u2q@U#z7 z3JkZ??X@=ZNJup{lG!k~?KlxM_|@3k57Sy?p$&%c+EX^>< z>AcLd3opfUA3#~hqi%7=2HV%(wOSghmCyV{T?J3~pcy7cReNR0@_5IQ?bkT+NjIl` z^f*bZPGXJk0pJKT&_3OYZM$Ph<(~F<`nfo+r^tHTN)b_QB;l>EV|YwM#*zWt z_JTLR>n&*VA0Wt0Qa?b4wc60i`R*89JyY>u^;{N<_>%H`cJZXFPT%YycgaVGQbXe1}y>Sy5RA>4E zn;60~C?jlp@{HRdfHiH?!>7szB>Cg0ICW8fGB>zZwk)Rl=Xe_Ft!-m6ShcgH$zbB@ zhQe8SpINKFzPSI77)m%p#s|qDz5BHZ`T}qr&7DY?;G(ZC*fFtnA}&L}01msBmhB7W z(@u-ae`dA7Uz0qaBAiSc8Ejn}QN&XJ(qugQ<=YAmLp%oFuYWy|wyX-w8W zx<&Vo@Oy8(6o0&(kNJ(^^}2*waZvP%Y8s?bN#EZz{ired=Eiec`0|W`n1y^MJYzHO zf#cT9SLm0E#tNHFEHvm?39t&hk6m#U@SZzgNNH6w02+%XWB+KZe`}QADpUw+Z9sb5 zcvJqW(uu3O!J_1G;t1P@aItcJYAo6~>2kTm-|Yt<`5dioopTPG{|`MRQmmeW`Y|7M z@49wjqT1|Hyz=}dfb+LeWK2ovjE%k#cxRhEtth{!QfnwYSarK^;OM`FnL{Zd#)`?N zwA?(P_@3wz)&sDj=emqCLg46qOLD839VWEWFwUk`tnFNXxC~xizLNU!{$>t2`n|VVWJ?IviD7I{smUiFC zh24;G?amJbh}jNc0X-hQq!^gR);012;0i+L`b25}d*fF>~nFaskBY=+Pag$5=wJd+3ZMnyF&5e%*O@za2Z$dc@0j6V1h)L${Jm4aesP{7O+Io@p z&PJI@`)ty3%zru+_qWRc@v-i_VY|fJhx0B=0OoN#6Ll2U>ERl$A!ra+{x8x?am9@cZ2RUsRq!B8;V z@QKiv$dVBol{v&1W-0#iP%v&}AInds+Wi`<3sq=8--K>j;NuVC{Aug49~3UYZv52& z+rIc7*2k$e=lZ`x0G-}07hP`acK8f8LErDSeZ zVQz|U(4foh{+E&{iNMuAM^E;e^jXd~{QWl~{xjL&k+PcwuZxrNVk930!}(d==MIe{ z&V)?8vWX82B>a1FWGGWBY@3(bJl_=Yk*U8WZ2Ad?iBJMmoJI4W`(foZ<+9kvcDf^G zpkYAEASvtDrB0Z(&gpA_>4_EgJ;YJV2Riut>l?ik_ZLnF#F7LzVx}8zVff!u`zS`l z{}}>!;9KIQycW{Q8U2vV0$AT4ejV8?kmGLY5kkzT28cn=w7IcJ@O&BVXgsmXehPSr zunf%pH|m?T=it5H2lK9Gp>j^)>X@{VT3=Y4?%>^=&bIh3PGM7Zn~S<= z#~0Wb_ab84xJ825fo`op-BKM2fbA<-fZ<`i&I=p@QgH_R{HJ@exa7}XQXkaUZA~bk zzFzk^onnvOSd`-<5@XM!bDdydZ1G4dQ6CSBb+P@An~ABpXlskoHMsnnwwV`acjw8fTg-Ly(HaA0KnT#>6be;_BBwzK4+fy^vB}!DhfD2+Bb2jb-3gou+ zBh+aWr)x-|@cYil#vw^m8Ub(TVFi6KvsQ=Ca=Ku*x?DL*Yl5uG{#sBI#M4v@LrOo9 zCb!j=ol*9xrAqPkRGG5Z;_u~clmYelzCm^M%qlO{tLhY0^Z}`*3IQBctqrrbsbifA z!g^5yl5{?VA0rpYH=-UDA%uX8Ig-II8UN>^I<^Pu(blCN!whD#li>k9Tjx~Pr5C?m zAg|ee^@>nMMqE@~*5%Xk$`tt@>2st;0jJqnRTnEJeTg-bD6f{-RB9j=S>T>~Aix08 z_a6qn&aeMq$@6bV6j%}ba8ffJ;S=|Y5Hh)Tssg=rTXWyqRpHjp@t#gM6*?CAz=eYi zvtO4XDm$@7!<3rU0x#CcX=Twa_Kw6_l!f-RgA^d%SNSSO0t= zn4vMRa5N@JhQjgZQvPLLnF)Q3clYzf`Xe!w9j^RHlwp3egH1UaSB3>S)9%1G?l&_R zcfGxqmFs?tpMaMNarox6yjA`r#GVFpv}i;lYBA8`GW^JF#qdQ+{4R*A1N6EkB5EU1 zZ};js+U;zYHG{g-M;{veBk<1#5}td?&PbKNauQpNT(+D>T=u@WC_Qe22Y-NQdN-+4 zlOdbe^|9GdS}Z%HomL90%gY^hzPqCn8pwg~F3kcOGr@j(rx>w7twA`KqNwH`E%9>M zZ=*cwL6=ckFk>AlW)!-4I@J%wmegBmVB+{!Qq(4n~fnuux@ z;9Ay1UTpOM*pXqKeeo?u(gP!>3U7+M|Ji@%kDpTh+W5X%~ zgNkO2H<((Jb8OS%#9L8f?BAeQ^RycT3T(ov8iyyX(Y5yd{I!fBX&e*~Kf;4(mD+0z0o_kW`=(7s?-Ed2OLM|$HDSg zQvkBC&x4mt+;G{`Bh1D?x`)d|=M-O}2x8t{#c6PmmsfJ~I0&aUV z&ko09A?5Vb0sx8gF4fEl`Rqz#@_BlM%`b9&fMdE7H(#dpS%E4qiV{d_B*0 zSsV8X%P?BI_R>@8-?jRVMaEj|Lkvgl?jM5%`AcDCnHNyIv%M`L&_n548u0{ezp>0c zSHx(VI@)GmK;KhNsdVjn;1D(vQW;|981n7$D9gt&Y8@q@3lLV$KCx0 ze1G`thxhi__1^1zySE$(cjhRR zFs>FU?X{{ZDq9$mA+=;aY;4%wjC@^_UToaiJ^Ofox-$pU4EwwI#}wktLmAm)S*n+& z0$qmoaegSjKkaQpdJLR$&v!EYCmsb=W5$Jd= zdUGo58pxFE)s1mAn;JTA6+to%A{P$0CQwm@f4PI(NZ;g1>Kv z0m6-Jm433u0G_C6ldYb#2LP=Hyb5|H3BR>AS6wJ<r~&>-lA_rw|XfnD^PnV|@Bv3F1L5djh64-?)5i9?SCpSe6* ztG5VOyO1)Z^pmr_w@^zVMOvng*lVh|VBn{hP$-{p!Pv=Y=bCkhke;$}Kng5uX+>2a zVgHX;QLx<3~L&x)|Bs`>-|F&nkG)e}S?p?NzOm6!w6nCgOecQ*HrkP^> zQ)e;By9i2S*qf)N9t(Kq4STCPoZ=w$zxrFK*^W1YQ}MGXN+s7cY$m<@bjXMN=97|&`b;xcShR&~-Am1PJEDb%2tpu6V_ z^-6uxz3$EgY4tBZ&ChnnFAMtk1S;8LT1@treV?rDK?a}@w_LNS=~)LPhX;~#1LDg6thWA?^KE4NiVTTzQMJC{Kbja47t?cqFb zms$a<(C_;yWgOG-$;X}yS(>Dj5kHFx)r;vnsMI{ppzMTPPfYfN zcb(zheIhIavnmwdiNHhg-|JyqmWztqGh=2_4Z=xqLRSZ+I)pEb09pgp|0 zQQXn|-coVrUQ^GJkUc-eQ(qs!^?^(k+(~5)LT7Tl2>1VYV;a`1Bmr zK4%IoHVT%_^Y^(%N`NlSXWo7lskuO|S-vxu)v=wMAlG@}bNa!DU<Th^^@FmFo* zb(fb@zR5*?*FPjF%Y1PUt<4|^eZSI_($HMuG8<=$Jn;@?h*>VBu?aTLZ1rVAI=n&o zs!MccSye0W>UU0zG$}|Yw>}DJxOg2GEqI>wk$=4_he6r>=<0>atJD_>C~CIM$hmm2 zZiNLd3t`wX!$$!R;I8WKS(opilip^os>o+~`5nxz((nzB){NZWb$^Kk_O0LLY{2<5 z?KalK+B+!E-A;31zcD6zh<$YdQ@@6xwP?l z+EFEDTos4dC-H2v*OToB`6y$WhoySjr=BH?LfGzTYmG^dP!{IC=y0!kepuKoF8x>3 z;`q5~>N#m97g?uPH)rDI*H@9lXZT@1BQ=o0HEy5Grma)zsz-^Kq+NvJ(8a-4j$}m@ zj49dcm4w8i(MnWm6A2?-2w&zOdBRUf9Z@S|#&KS^w=o~wuD>7}ELF&zF{a+1(DPJ8#v5AtarrcfEqn@k9o< zN(Y6(?Tq=l!dF)0ToMJnA*c*z;bkGV5f@ZYu6;fy9O9z7|l zNWzOOk6L|gh{!jS3zLIW9Kdg}Yd&UWDdr-UpS=X$=F5dBe$o;2(aPBx_uxht=jljJ z(OJM=}V2QwN5W^}?3GZc03w4D1kSW(pX zo^sW?=)AVLV}^*hg#aVX`O`$A*o!Q2*s9jVF`Kq;@)6u^hD)hGE!lMT zsf3DW9T78{GziP^s*f`4Z*ZvXyyd{cFQJ8QLaXp2jKNgfi32S+tfZnTQN&2a(BACqXFr1Ki!-8;~&#!JiWotcN;|jc&xSM*F_pMp*z#0ymuPA z2<`7>*DcX}BJ8sT`0~%?>j@?ZRGQop-6jTszI8{SuM8p4S4NPxe@kI`_m>vB??e8V znI&EHl^^xsFt%bBx*>rf>mZLHeEKUY6}2vz@jja%T1<;B)_#-7UnWjH1;c~Y>WIbh zb%TH3p$g1W)x5aL(qHm?Si#9gZSl;=ZiWXNdfMq}q*3@c1>l!FHM;$!_>=gJz%cZb z7=lu&4{QCy4MFy3sqsy&S|JCUeoFJ(b!d%8IG2pu}?(v#@4=X&RsLcpm8U3`(wO#~*fEqHcmZ+MK2$HfW z`UIF2h4Zjg)lo;Or2L);@v~{VB9^@+R{?~v^WxOy!j+jC;UW#OjRZP^^gQHRPt9Xt z7qH_ecEM00I1SwO`_&vR~AsJ}7{58#hIXjD8E;IT#) z6etCSh2g`6k<}nRM*x)cV7wJ(KB}Uk;*;`FoV9Plz`)fP>FE&wRMdjJx?p}=ov42s z&}US83=!L^dz89Q{7M7%2`Jxvyz5RoMfjMb&zw6a0s!d~=EmAiE8J{H-&+N?>1A5J zua!5+w3K9LXKx~zD|X~L#CF|oP`_4q+R`%K9{t+BO^Q#|EkoIgIixODF}CF6(Ch;U zlv7i#YoE;3z`{xZt`1vX_|CCaRYe%V96lwg{5P4F23SGQPmq@#Z<7G9`iEMA$8ci6 z0HErFT6~nf)u*d}@?^xRSOtAg&mwk1rHxJkfqrCk2weff4t_a3M>1`&k;bJqL%6I)*EPaB0NoACYP69rwf^{!Z-zFM&~|a|yltV{eDm4FOjTwm zrzstlnPTrg-cGq>QjY6%d|T%yYIS$|L5xQd*03|rfp06LVU_5Ea@)bSUT%R(a7xGi zWJKt8;?H*Lu$T_v3s#t1nkOV?(-@#yL4N^fHeX-Jn+k+&wBMm39%Lq{ zDlb4_XA6#_D5=5~KW0W#G6MbcBhz}r1Cde6-?Nj;V6WuGt9eYVsdDOG3vz97 z@XNk0PMo=bPI4%6`+fuR=xgHEE3QZ)8tn|YCs7F+nA)ZTq_8CIN&pXSX^&2FJtyZw z=@4<0)w@Td^3oRj!>E9m5{EqKq3g1jlaHf-7 zrESNoKQ{9b?1b7^R_=PtQII+fGPef}pjYkCeLt4#X82cKO-!Pdk@#kV1Pyf_g4)Vd zc^WxrZ&HhnZQ}65eVne_xDH+#GH<<-TZ5Oz)6vxHZ!0sc`~6NDjG9$<1Fm0v`#Ijn zoN^(b5616w7r>v-02uUG;tjd3MyhhSL=}Z9>lyDa3Bc`Ta}n{F1+zCT*_Oz1{L##J zXI>Src|yr{r5ylYUEgIW?rVeqG4!=tlxYaKr zFCVoF%h)wW<$lNhJ^a9$(pJLdGQz)yALtYU^nz#q_vqu+@+tXGIRzZ>a=%@^3~lS~ z(-qLW&2Gj%+O&>kw`Rq6w_(8DZU5n}JJU4+{P=$?1Z=?q%DO*Y*i?<1q7RvX)WR?O{Z_Z} zYA5I@{w#k6ZqshxtfY%xKx`W${ Date: Mon, 5 Dec 2016 23:11:41 -0500 Subject: [PATCH 13/30] partial support for range selectors on world calendars --- src/components/rangeselector/get_update_object.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/rangeselector/get_update_object.js b/src/components/rangeselector/get_update_object.js index 71978d0a310..e01624b7c61 100644 --- a/src/components/rangeselector/get_update_object.js +++ b/src/components/rangeselector/get_update_object.js @@ -11,9 +11,6 @@ var d3 = require('d3'); -var Lib = require('../../lib'); - - module.exports = function getUpdateObject(axisLayout, buttonLayout) { var axName = axisLayout._name; var update = {}; @@ -33,7 +30,7 @@ module.exports = function getUpdateObject(axisLayout, buttonLayout) { function getXRange(axisLayout, buttonLayout) { var currentRange = axisLayout.range; - var base = new Date(Lib.dateTime2ms(currentRange[1])); + var base = new Date(axisLayout.r2l(currentRange[1])); var step = buttonLayout.step, count = buttonLayout.count; @@ -42,13 +39,13 @@ function getXRange(axisLayout, buttonLayout) { switch(buttonLayout.stepmode) { case 'backward': - range0 = Lib.ms2DateTime(+d3.time[step].utc.offset(base, -count)); + range0 = axisLayout.l2r(+d3.time[step].utc.offset(base, -count)); break; case 'todate': var base2 = d3.time[step].utc.offset(base, -count); - range0 = Lib.ms2DateTime(+d3.time[step].utc.ceil(base2)); + range0 = axisLayout.l2r(+d3.time[step].utc.ceil(base2)); break; } From 6653da7dbbe79591d6eeb6fdb5203eebc9fa09a8 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Tue, 6 Dec 2016 10:48:29 -0500 Subject: [PATCH 14/30] get the right date string replacement for candlestick mock --- test/image/mocks/candlestick_double-y-axis.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/image/mocks/candlestick_double-y-axis.json b/test/image/mocks/candlestick_double-y-axis.json index 07ad6772355..ecb8a71b398 100644 --- a/test/image/mocks/candlestick_double-y-axis.json +++ b/test/image/mocks/candlestick_double-y-axis.json @@ -1947,8 +1947,8 @@ "layout": { "xaxis": { "range": [ - "2016-01-10 14:24:21.917", - "2016-08-15 01:54:22.978" + "2016-01-10 19:24:21.917", + "2016-08-15 05:54:22.978" ] }, "yaxis": { From 8e1747fbb3d1ad7fc155c63e2ced169c9d7bc072 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Tue, 6 Dec 2016 13:34:03 -0500 Subject: [PATCH 15/30] bigger tolerance on updatemenus_test width test --- test/jasmine/tests/updatemenus_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jasmine/tests/updatemenus_test.js b/test/jasmine/tests/updatemenus_test.js index ef2128feaa2..37a3a415e2d 100644 --- a/test/jasmine/tests/updatemenus_test.js +++ b/test/jasmine/tests/updatemenus_test.js @@ -698,7 +698,7 @@ describe('update menus interactions', function() { // must compare with a tolerance as the exact result // is browser/font dependent (via getBBox) - expect(Math.abs(actualWidth - width)).toBeLessThan(12); + expect(Math.abs(actualWidth - width)).toBeLessThan(16); // height is determined by 'fontsize', // so no such tolerance is needed From 00ae2dd3f8e72987ffae8bf2f8ac0f6a6e848666 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Tue, 6 Dec 2016 13:34:36 -0500 Subject: [PATCH 16/30] fix and test layout.calendar inheritance --- src/components/colorbar/draw.js | 3 +- src/plots/cartesian/axis_defaults.js | 2 +- src/plots/cartesian/layout_defaults.js | 5 +-- src/plots/gl3d/layout/axis_defaults.js | 3 +- src/plots/gl3d/layout/defaults.js | 6 ++-- src/traces/contour/defaults.js | 2 +- src/traces/heatmap/defaults.js | 2 +- src/traces/heatmap/xyz_defaults.js | 7 +++-- src/traces/histogram/defaults.js | 7 +++-- src/traces/histogram2d/defaults.js | 4 +-- src/traces/histogram2d/sample_defaults.js | 7 +++-- src/traces/histogram2dcontour/defaults.js | 2 +- test/jasmine/tests/axes_test.js | 28 +++++++++++++++++ test/jasmine/tests/bar_test.js | 28 ++++++++++++++++- test/jasmine/tests/box_test.js | 26 ++++++++++++++++ test/jasmine/tests/contour_test.js | 30 ++++++++++++++++++ test/jasmine/tests/finance_test.js | 36 +++++++++++++++++++++ test/jasmine/tests/gl3daxes_test.js | 30 ++++++++++++++++++ test/jasmine/tests/heatmap_test.js | 29 +++++++++++++++++ test/jasmine/tests/histogram2d_test.js | 38 ++++++++++++++++++++--- test/jasmine/tests/histogram_test.js | 25 +++++++++++++++ test/jasmine/tests/range_selector_test.js | 19 ++++++++---- test/jasmine/tests/scatter3d_test.js | 27 ++++++++++++++-- test/jasmine/tests/scatter_test.js | 26 ++++++++++++++++ test/jasmine/tests/surface_test.js | 27 ++++++++++++++++ 25 files changed, 384 insertions(+), 35 deletions(-) diff --git a/src/components/colorbar/draw.js b/src/components/colorbar/draw.js index a6c28680724..e31b43873f3 100644 --- a/src/components/colorbar/draw.js +++ b/src/components/colorbar/draw.js @@ -174,7 +174,8 @@ module.exports = function draw(gd, id) { axisOptions = { letter: 'y', font: fullLayout.font, - noHover: true + noHover: true, + calendar: fullLayout.calendar // not really necessary (yet?) }; // Coerce w.r.t. Axes layoutAttributes: diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index aeed7c5e876..a69df3f66c9 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -74,7 +74,7 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, } } - if(axType === 'date') coerce('calendar'); + if(axType === 'date') coerce('calendar', options.calendar); setConvert(containerOut); diff --git a/src/plots/cartesian/layout_defaults.js b/src/plots/cartesian/layout_defaults.js index a3078d94897..aaf9673bc6c 100644 --- a/src/plots/cartesian/layout_defaults.js +++ b/src/plots/cartesian/layout_defaults.js @@ -126,7 +126,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { showGrid: !noGrids[axName], name: axName, data: fullData, - bgColor: bgColor + bgColor: bgColor, + calendar: layoutOut.calendar }, positioningOptions = { letter: axLetter, @@ -140,7 +141,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { return Lib.coerce(axLayoutIn, axLayoutOut, layoutAttributes, attr, dflt); } - handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions); + handleAxisDefaults(axLayoutIn, axLayoutOut, coerce, defaultOptions, layoutOut); handlePositionDefaults(axLayoutIn, axLayoutOut, coerce, positioningOptions); layoutOut[axName] = axLayoutOut; diff --git a/src/plots/gl3d/layout/axis_defaults.js b/src/plots/gl3d/layout/axis_defaults.js index df6857ec47a..981c7b042a3 100644 --- a/src/plots/gl3d/layout/axis_defaults.js +++ b/src/plots/gl3d/layout/axis_defaults.js @@ -46,7 +46,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) { letter: axName[0], data: options.data, showGrid: true, - bgColor: options.bgColor + bgColor: options.bgColor, + calendar: options.calendar }); coerce('gridcolor', colorMix(containerOut.color, options.bgColor, gridLightness).toRgbString()); diff --git a/src/plots/gl3d/layout/defaults.js b/src/plots/gl3d/layout/defaults.js index 291d8dcd572..a8aa30dd0de 100644 --- a/src/plots/gl3d/layout/defaults.js +++ b/src/plots/gl3d/layout/defaults.js @@ -41,7 +41,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { font: layoutOut.font, fullData: fullData, getDfltFromLayout: getDfltFromLayout, - paper_bgcolor: layoutOut.paper_bgcolor + paper_bgcolor: layoutOut.paper_bgcolor, + calendar: layoutOut.calendar }); }; @@ -97,7 +98,8 @@ function handleGl3dDefaults(sceneLayoutIn, sceneLayoutOut, coerce, opts) { font: opts.font, scene: opts.id, data: opts.fullData, - bgColor: bgColorCombined + bgColor: bgColorCombined, + calendar: opts.calendar }); coerce('dragmode', opts.getDfltFromLayout('dragmode')); diff --git a/src/traces/contour/defaults.js b/src/traces/contour/defaults.js index e88b43cbe7f..0f4107e5796 100644 --- a/src/traces/contour/defaults.js +++ b/src/traces/contour/defaults.js @@ -22,7 +22,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var len = handleXYZDefaults(traceIn, traceOut, coerce); + var len = handleXYZDefaults(traceIn, traceOut, coerce, layout); if(!len) { traceOut.visible = false; return; diff --git a/src/traces/heatmap/defaults.js b/src/traces/heatmap/defaults.js index f41624cf57e..cc4c33aae0e 100644 --- a/src/traces/heatmap/defaults.js +++ b/src/traces/heatmap/defaults.js @@ -22,7 +22,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - var len = handleXYZDefaults(traceIn, traceOut, coerce); + var len = handleXYZDefaults(traceIn, traceOut, coerce, layout); if(!len) { traceOut.visible = false; return; diff --git a/src/traces/heatmap/xyz_defaults.js b/src/traces/heatmap/xyz_defaults.js index c6d55739730..9d808a61819 100644 --- a/src/traces/heatmap/xyz_defaults.js +++ b/src/traces/heatmap/xyz_defaults.js @@ -14,7 +14,7 @@ var isNumeric = require('fast-isnumeric'); var hasColumns = require('./has_columns'); -module.exports = function handleXYZDefaults(traceIn, traceOut, coerce) { +module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout) { var z = coerce('z'); var x, y; @@ -37,8 +37,9 @@ module.exports = function handleXYZDefaults(traceIn, traceOut, coerce) { coerce('transpose'); } - coerce('xcalendar'); - coerce('ycalendar'); + var dfltCalendar = layout.calendar; + coerce('xcalendar', dfltCalendar); + coerce('ycalendar', dfltCalendar); return traceOut.z.length; }; diff --git a/src/traces/histogram/defaults.js b/src/traces/histogram/defaults.js index 68ddc1f1d76..4c5975f2cd2 100644 --- a/src/traces/histogram/defaults.js +++ b/src/traces/histogram/defaults.js @@ -26,9 +26,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var x = coerce('x'), y = coerce('y'); - coerce('xcalendar'); - coerce('ycalendar'); - coerce('text'); var orientation = coerce('orientation', (y && !x) ? 'h' : 'v'), @@ -39,6 +36,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } + var dfltCalendar = layout.calendar; + coerce('xcalendar', dfltCalendar); + coerce('ycalendar', dfltCalendar); + var hasAggregationData = traceOut[orientation === 'h' ? 'x' : 'y']; if(hasAggregationData) coerce('histfunc'); diff --git a/src/traces/histogram2d/defaults.js b/src/traces/histogram2d/defaults.js index 563c59563d1..8d9d1582fec 100644 --- a/src/traces/histogram2d/defaults.js +++ b/src/traces/histogram2d/defaults.js @@ -16,12 +16,12 @@ var colorscaleDefaults = require('../../components/colorscale/defaults'); var attributes = require('./attributes'); -module.exports = function supplyDefaults(traceIn, traceOut, layout) { +module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function coerce(attr, dflt) { return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - handleSampleDefaults(traceIn, traceOut, coerce); + handleSampleDefaults(traceIn, traceOut, coerce, layout); var zsmooth = coerce('zsmooth'); if(zsmooth === false) { diff --git a/src/traces/histogram2d/sample_defaults.js b/src/traces/histogram2d/sample_defaults.js index f804ccd2dcb..0101ee4352b 100644 --- a/src/traces/histogram2d/sample_defaults.js +++ b/src/traces/histogram2d/sample_defaults.js @@ -12,12 +12,13 @@ var handleBinDefaults = require('../histogram/bin_defaults'); -module.exports = function handleSampleDefaults(traceIn, traceOut, coerce) { +module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout) { var x = coerce('x'), y = coerce('y'); - coerce('xcalendar'); - coerce('ycalendar'); + var dfltCalendar = layout.calendar; + coerce('xcalendar', dfltCalendar); + coerce('ycalendar', dfltCalendar); // we could try to accept x0 and dx, etc... // but that's a pretty weird use case. diff --git a/src/traces/histogram2dcontour/defaults.js b/src/traces/histogram2dcontour/defaults.js index 18331ac99ff..be0d9f457f8 100644 --- a/src/traces/histogram2dcontour/defaults.js +++ b/src/traces/histogram2dcontour/defaults.js @@ -21,7 +21,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return Lib.coerce(traceIn, traceOut, attributes, attr, dflt); } - handleSampleDefaults(traceIn, traceOut, coerce); + handleSampleDefaults(traceIn, traceOut, coerce, layout); var contourStart = Lib.coerce2(traceIn, traceOut, attributes, 'contours.start'), contourEnd = Lib.coerce2(traceIn, traceOut, attributes, 'contours.end'), diff --git a/test/jasmine/tests/axes_test.js b/test/jasmine/tests/axes_test.js index 7378c50b376..d08273bfa58 100644 --- a/test/jasmine/tests/axes_test.js +++ b/test/jasmine/tests/axes_test.js @@ -385,6 +385,34 @@ describe('Test axes', function() { expect(layoutOut.yaxis2.gridcolor) .toEqual(tinycolor.mix('#444', bgColor, frac).toRgbString()); }); + + it('should inherit calendar from the layout', function() { + layoutOut.calendar = 'nepali'; + layoutIn = { + calendar: 'nepali', + xaxis: {type: 'date'}, + yaxis: {type: 'date'} + }; + + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + + expect(layoutOut.xaxis.calendar).toBe('nepali'); + expect(layoutOut.yaxis.calendar).toBe('nepali'); + }); + + it('should allow its own calendar', function() { + layoutOut.calendar = 'nepali'; + layoutIn = { + calendar: 'nepali', + xaxis: {type: 'date', calendar: 'coptic'}, + yaxis: {type: 'date', calendar: 'thai'} + }; + + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + + expect(layoutOut.xaxis.calendar).toBe('coptic'); + expect(layoutOut.yaxis.calendar).toBe('thai'); + }); }); describe('categoryorder', function() { diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js index 562cb0ad675..8edf4e1d608 100644 --- a/test/jasmine/tests/bar_test.js +++ b/test/jasmine/tests/bar_test.js @@ -106,7 +106,7 @@ describe('Bar.supplyDefaults', function() { font: {family: 'arial', color: '#AAA', size: 13} }; - supplyDefaults(traceIn, traceOut, defaultColor, layout, {}); + supplyDefaults(traceIn, traceOut, defaultColor, layout); expect(traceOut.textposition).toBe('inside'); expect(traceOut.textfont).toEqual(layout.font); @@ -116,6 +116,32 @@ describe('Bar.supplyDefaults', function() { expect(traceOut.insidetextfont).not.toBe(traceOut.textfont); expect(traceOut.outsidetexfont).toBeUndefined(); }); + + it('should inherit layout.calendar', function() { + traceIn = { + x: [1, 2, 3], + y: [1, 2, 3] + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + traceIn = { + x: [1, 2, 3], + y: [1, 2, 3], + xcalendar: 'coptic', + ycalendar: 'ethiopian' + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('ethiopian'); + }); }); describe('heatmap calc / setPositions', function() { diff --git a/test/jasmine/tests/box_test.js b/test/jasmine/tests/box_test.js index 829f6343d4e..c292fc356c9 100644 --- a/test/jasmine/tests/box_test.js +++ b/test/jasmine/tests/box_test.js @@ -81,6 +81,32 @@ describe('Test boxes', function() { }); + it('should inherit layout.calendar', function() { + traceIn = { + y: [1, 2, 3] + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + traceIn = { + y: [1, 2, 3], + xcalendar: 'coptic', + ycalendar: 'ethiopian' + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('ethiopian'); + }); + }); }); diff --git a/test/jasmine/tests/contour_test.js b/test/jasmine/tests/contour_test.js index 0cc1fd0c1be..2f41da50af1 100644 --- a/test/jasmine/tests/contour_test.js +++ b/test/jasmine/tests/contour_test.js @@ -51,6 +51,36 @@ describe('contour defaults', function() { supplyDefaults(traceIn, traceOut, defaultColor, layout); expect(traceOut.autocontour).toBe(true); }); + + it('should inherit layout.calendar', function() { + traceIn = { + x: [1, 2], + y: [1, 2], + z: [[1, 2], [3, 4]] + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + traceIn = { + x: [1, 2], + y: [1, 2], + z: [[1, 2], [3, 4]], + xcalendar: 'coptic', + ycalendar: 'ethiopian' + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('ethiopian'); + }); }); describe('contour makeColorMap', function() { diff --git a/test/jasmine/tests/finance_test.js b/test/jasmine/tests/finance_test.js index 992beda676b..87caef0648a 100644 --- a/test/jasmine/tests/finance_test.js +++ b/test/jasmine/tests/finance_test.js @@ -331,6 +331,42 @@ describe('finance charts defaults:', function() { expect(out1.layout.xaxis.rangeslider).toBeDefined(); expect(out1._fullLayout.xaxis.rangeslider.visible).toBe(false); }); + + it('pushes layout.calendar to all output traces', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc' + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick' + }); + + var out = _supply([trace0, trace1], {calendar: 'nanakshahi'}); + + + out._fullData.forEach(function(fullTrace) { + expect(fullTrace.xcalendar).toBe('nanakshahi'); + }); + }); + + it('accepts a calendar per input trace', function() { + var trace0 = Lib.extendDeep({}, mock0, { + type: 'ohlc', + xcalendar: 'hebrew' + }); + + var trace1 = Lib.extendDeep({}, mock1, { + type: 'candlestick', + xcalendar: 'julian' + }); + + var out = _supply([trace0, trace1], {calendar: 'nanakshahi'}); + + + out._fullData.forEach(function(fullTrace, i) { + expect(fullTrace.xcalendar).toBe(i < 2 ? 'hebrew' : 'julian'); + }); + }); }); describe('finance charts calc transforms:', function() { diff --git a/test/jasmine/tests/gl3daxes_test.js b/test/jasmine/tests/gl3daxes_test.js index 1e4b61912bd..fad1f8c222b 100644 --- a/test/jasmine/tests/gl3daxes_test.js +++ b/test/jasmine/tests/gl3daxes_test.js @@ -75,5 +75,35 @@ describe('Test Gl3dAxes', function() { checkKeys(expected[axis], layoutOut[axis]); }); }); + + it('should inherit layout.calendar', function() { + layoutIn = { + xaxis: {type: 'date'}, + yaxis: {type: 'date'}, + zaxis: {type: 'date'} + }; + options.calendar = 'taiwan'; + + supplyLayoutDefaults(layoutIn, layoutOut, options); + + expect(layoutOut.xaxis.calendar).toBe('taiwan'); + expect(layoutOut.yaxis.calendar).toBe('taiwan'); + expect(layoutOut.zaxis.calendar).toBe('taiwan'); + }); + + it('should accept its own calendar', function() { + layoutIn = { + xaxis: {type: 'date', calendar: 'hebrew'}, + yaxis: {type: 'date', calendar: 'ummalqura'}, + zaxis: {type: 'date', calendar: 'discworld'} + }; + options.calendar = 'taiwan'; + + supplyLayoutDefaults(layoutIn, layoutOut, options); + + expect(layoutOut.xaxis.calendar).toBe('hebrew'); + expect(layoutOut.yaxis.calendar).toBe('ummalqura'); + expect(layoutOut.zaxis.calendar).toBe('discworld'); + }); }); }); diff --git a/test/jasmine/tests/heatmap_test.js b/test/jasmine/tests/heatmap_test.js index 61ccf74f4bf..9141ff5edf1 100644 --- a/test/jasmine/tests/heatmap_test.js +++ b/test/jasmine/tests/heatmap_test.js @@ -125,6 +125,35 @@ describe('heatmap supplyDefaults', function() { expect(traceOut.ygap).toBe(undefined); }); + it('should inherit layout.calendar', function() { + traceIn = { + x: [1, 2], + y: [1, 2], + z: [[1, 2], [3, 4]] + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + traceIn = { + x: [1, 2], + y: [1, 2], + z: [[1, 2], [3, 4]], + xcalendar: 'coptic', + ycalendar: 'ethiopian' + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('ethiopian'); + }); }); describe('heatmap convertColumnXYZ', function() { diff --git a/test/jasmine/tests/histogram2d_test.js b/test/jasmine/tests/histogram2d_test.js index ae3922708bf..e6e421fad84 100644 --- a/test/jasmine/tests/histogram2d_test.js +++ b/test/jasmine/tests/histogram2d_test.js @@ -18,7 +18,7 @@ describe('Test histogram2d', function() { it('should set zsmooth to false when zsmooth is empty', function() { traceIn = {}; - supplyDefaults(traceIn, traceOut, {}); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.zsmooth).toBe(false); }); @@ -26,13 +26,13 @@ describe('Test histogram2d', function() { traceIn = { zsmooth: 'fast' }; - supplyDefaults(traceIn, traceOut, {}); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.zsmooth).toBe('fast'); }); it('should set xgap and ygap to 0 when xgap and ygap are empty', function() { traceIn = {}; - supplyDefaults(traceIn, traceOut, {}); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.xgap).toBe(0); expect(traceOut.ygap).toBe(0); }); @@ -42,7 +42,7 @@ describe('Test histogram2d', function() { xgap: 10, ygap: 5 }; - supplyDefaults(traceIn, traceOut, {}); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.xgap).toBe(10); expect(traceOut.ygap).toBe(5); }); @@ -53,11 +53,39 @@ describe('Test histogram2d', function() { ygap: 5, zsmooth: 'best' }; - supplyDefaults(traceIn, traceOut, {}); + supplyDefaults(traceIn, traceOut, '', {}); expect(traceOut.xgap).toBe(undefined); expect(traceOut.ygap).toBe(undefined); }); + + it('should inherit layout.calendar', function() { + traceIn = { + x: [1, 2, 3], + y: [1, 2, 3] + }; + supplyDefaults(traceIn, traceOut, '', {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + traceIn = { + x: [1, 2, 3], + y: [1, 2, 3], + xcalendar: 'coptic', + ycalendar: 'ethiopian' + }; + supplyDefaults(traceIn, traceOut, '', {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('ethiopian'); + }); }); diff --git a/test/jasmine/tests/histogram_test.js b/test/jasmine/tests/histogram_test.js index 60898c9295a..684d2320939 100644 --- a/test/jasmine/tests/histogram_test.js +++ b/test/jasmine/tests/histogram_test.js @@ -129,6 +129,31 @@ describe('Test histogram', function() { expect(traceOut.autobiny).toBeUndefined(); }); + it('should inherit layout.calendar', function() { + traceIn = { + x: [1, 2, 3] + }; + supplyDefaults(traceIn, traceOut, '', {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + // size axis calendar is weird, but *might* be able to happen if + // we're using histfunc=min or max (does this work?) + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + traceIn = { + x: [1, 2, 3], + xcalendar: 'coptic', + ycalendar: 'nepali' + }; + supplyDefaults(traceIn, traceOut, '', {calendar: 'islamic'}); + + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('nepali'); + }); }); diff --git a/test/jasmine/tests/range_selector_test.js b/test/jasmine/tests/range_selector_test.js index cae2373e79b..85a024c6ba2 100644 --- a/test/jasmine/tests/range_selector_test.js +++ b/test/jasmine/tests/range_selector_test.js @@ -9,6 +9,7 @@ var createGraphDiv = require('../assets/create_graph_div'); var destroyGraphDiv = require('../assets/destroy_graph_div'); var getRectCenter = require('../assets/get_rect_center'); var mouseEvent = require('../assets/mouse_event'); +var setConvert = require('@src/plots/cartesian/set_convert'); describe('range selector defaults:', function() { @@ -185,14 +186,20 @@ describe('range selector getUpdateObject:', function() { expect(update['xaxis.range[1]']).toEqual(range1); } + function setupAxis(opts) { + var axisOut = Lib.extendFlat({type: 'date'}, opts); + setConvert(axisOut); + return axisOut; + } + // buttonLayout: {step, stepmode, count} // range0out: expected resulting range[0] (input is always '1948-01-01') // range1: input range[1], expected to also be the output function assertUpdateCase(buttonLayout, range0out, range1) { - var axisLayout = { + var axisLayout = setupAxis({ _name: 'xaxis', range: ['1948-01-01', range1] - }; + }); var update = getUpdateObject(axisLayout, buttonLayout); @@ -280,10 +287,10 @@ describe('range selector getUpdateObject:', function() { }); it('should return update object (reset case)', function() { - var axisLayout = { + var axisLayout = setupAxis({ _name: 'xaxis', range: ['1948-01-01', '2015-11-30'] - }; + }); var buttonLayout = { step: 'all' @@ -375,10 +382,10 @@ describe('range selector getUpdateObject:', function() { }); it('should return update object with correct axis names', function() { - var axisLayout = { + var axisLayout = setupAxis({ _name: 'xaxis5', range: ['1948-01-01', '2015-11-30'] - }; + }); var buttonLayout = { step: 'month', diff --git a/test/jasmine/tests/scatter3d_test.js b/test/jasmine/tests/scatter3d_test.js index 1b165b7cff6..7518da16166 100644 --- a/test/jasmine/tests/scatter3d_test.js +++ b/test/jasmine/tests/scatter3d_test.js @@ -8,9 +8,9 @@ describe('Scatter3D defaults', function() { var defaultColor = '#d3d3d3'; - function _supply(traceIn) { + function _supply(traceIn, layoutEdits) { var traceOut = { visible: true }, - layout = { _dataLength: 1 }; + layout = Lib.extendFlat({ _dataLength: 1 }, layoutEdits); Scatter3D.supplyDefaults(traceIn, traceOut, defaultColor, layout); return traceOut; @@ -65,4 +65,27 @@ describe('Scatter3D defaults', function() { expect(out.marker.color).toBe(color); expect(out.marker.line.color).toBe(Color.defaultLine); }); + + it('should inherit layout.calendar', function() { + var out = _supply(base, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(out.xcalendar).toBe('islamic'); + expect(out.ycalendar).toBe('islamic'); + expect(out.zcalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + var traceIn = Lib.extendFlat({}, base, { + xcalendar: 'coptic', + ycalendar: 'ethiopian', + zcalendar: 'mayan' + }); + var out = _supply(traceIn, {calendar: 'islamic'}); + + expect(out.xcalendar).toBe('coptic'); + expect(out.ycalendar).toBe('ethiopian'); + expect(out.zcalendar).toBe('mayan'); + }); }); diff --git a/test/jasmine/tests/scatter_test.js b/test/jasmine/tests/scatter_test.js index 8c948779459..1dcb3abbee3 100644 --- a/test/jasmine/tests/scatter_test.js +++ b/test/jasmine/tests/scatter_test.js @@ -92,6 +92,32 @@ describe('Test scatter', function() { supplyDefaults(traceIn, traceOut, defaultColor, layout); expect(traceOut.hoveron).toBe('points'); }); + + it('should inherit layout.calendar', function() { + traceIn = { + x: [1, 2, 3], + y: [1, 2, 3] + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + traceIn = { + x: [1, 2, 3], + y: [1, 2, 3], + xcalendar: 'coptic', + ycalendar: 'ethiopian' + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('ethiopian'); + }); }); describe('isBubble', function() { diff --git a/test/jasmine/tests/surface_test.js b/test/jasmine/tests/surface_test.js index 73905747f22..96aa26f0193 100644 --- a/test/jasmine/tests/surface_test.js +++ b/test/jasmine/tests/surface_test.js @@ -150,5 +150,32 @@ describe('Test surface', function() { expect(traceOut.cmin).toBeUndefined(); expect(traceOut.cmax).toBeUndefined(); }); + + it('should inherit layout.calendar', function() { + traceIn = { + z: [[1, 2, 3], [2, 1, 2]] + }; + supplyDefaults(traceIn, traceOut, defaultColor, {calendar: 'islamic'}); + + // we always fill calendar attributes, because it's hard to tell if + // we're on a date axis at this point. + expect(traceOut.xcalendar).toBe('islamic'); + expect(traceOut.ycalendar).toBe('islamic'); + expect(traceOut.zcalendar).toBe('islamic'); + }); + + it('should take its own calendars', function() { + var traceIn = { + z: [[1, 2, 3], [2, 1, 2]], + xcalendar: 'coptic', + ycalendar: 'ethiopian', + zcalendar: 'mayan' + }; + + supplyDefaults(traceIn, traceOut, defaultColor, layout); + expect(traceOut.xcalendar).toBe('coptic'); + expect(traceOut.ycalendar).toBe('ethiopian'); + expect(traceOut.zcalendar).toBe('mayan'); + }); }); }); From 31430999a77ab8af1d90fffa425260acb13cf16d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Dec 2016 15:38:37 -0500 Subject: [PATCH 17/30] move world-calendar logic in lib/dates.js to new 'calendars' component - use Registry in lib.dates.js to invoke world-calendar logic on-demand. --- src/components/calendars/index.js | 158 +++++++++++++++++++++++++ src/constants/numerical.js | 8 +- src/lib/dates.js | 184 ++++++------------------------ 3 files changed, 201 insertions(+), 149 deletions(-) create mode 100644 src/components/calendars/index.js diff --git a/src/components/calendars/index.js b/src/components/calendars/index.js new file mode 100644 index 00000000000..c5c6a690f10 --- /dev/null +++ b/src/components/calendars/index.js @@ -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 +}; diff --git a/src/constants/numerical.js b/src/constants/numerical.js index d10ffa42a51..85155e8d16c 100644 --- a/src/constants/numerical.js +++ b/src/constants/numerical.js @@ -36,5 +36,11 @@ module.exports = { ONEDAY: 86400000, ONEHOUR: 3600000, ONEMIN: 60000, - ONESEC: 1000 + ONESEC: 1000, + + /* + * For fast conversion btwn world calendars and epoch ms, the Julian Day Number + * of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD() + */ + EPOCHJD: 2440587.5 }; diff --git a/src/lib/dates.js b/src/lib/dates.js index c0a996e8668..2dde90b0187 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -9,7 +9,6 @@ 'use strict'; -var calendars = require('world-calendars'); var d3 = require('d3'); var isNumeric = require('fast-isnumeric'); @@ -22,6 +21,9 @@ var ONEDAY = constants.ONEDAY; var ONEHOUR = constants.ONEHOUR; var ONEMIN = constants.ONEMIN; var ONESEC = constants.ONESEC; +var EPOCHJD = constants.EPOCHJD; + +var Registry = require('../registry'); var utcFormat = d3.time.format.utc; @@ -30,102 +32,40 @@ var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\d)(-(\d?\d)([ Tt]([01]?\d|2[0 // for 2-digit years, the first year we map them onto var YFIRST = new Date().getFullYear() - 70; -// 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; -} - function isWorldCalendar(calendar) { - return calendar && typeof calendar === 'string' && calendar !== 'gregorian'; + return ( + calendar && + Registry.componentsRegistry.calendars && + typeof calendar === 'string' && calendar !== 'gregorian' + ); } -// For fast conversion btwn world calendars and epoch ms, the Julian Day Number -// of the unix epoch. From calendars.instance().newDate(1970, 1, 1).toJD() -var EPOCHJD = 2440587.5; - -// 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 = { - gregorian: '2000-01-01', - 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 = { - gregorian: '2000-01-02', - 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 = { - gregorian: ['2000-01-01', '2001-01-01'], - 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'] -}; - /* * dateTick0: get the canonical tick for this calendar * * bool sunday is for week ticks, shift it to a Sunday. */ exports.dateTick0 = function(calendar, sunday) { - calendar = (isWorldCalendar(calendar) && calendar) || 'gregorian'; - if(sunday) return CANONICAL_SUNDAY[calendar]; - return CANONICAL_TICK[calendar]; + if(isWorldCalendar(calendar)) { + return sunday ? + Registry.getComponentMethod('calendars', 'CANONICAL_SUNDAY')[calendar] : + Registry.getComponentMethod('calendars', 'CANONICAL_TICK')[calendar]; + } + else { + return sunday ? '2000-01-02' : '2000-01-01'; + } }; /* * dfltRange: for each calendar, give a valid default range */ exports.dfltRange = function(calendar) { - calendar = (isWorldCalendar(calendar) && calendar) || 'gregorian'; - return DFLTRANGE[calendar]; + if(isWorldCalendar(calendar)) { + return Registry.getComponentMethod('calendars', 'DFLTRANGE')[calendar]; + } + else { + return ['2000-01-01', '2001-01-01']; + } }; // is an object a javascript date? @@ -229,7 +169,10 @@ exports.dateTime2ms = function(s, calendar) { if(y.length === 2) return BADNUM; var cDate; - try { cDate = getCal(calendar).newDate(Number(y), m, d); } + try { + cDate = Registry.getComponentMethod('calendars', 'getCal')(calendar) + .newDate(Number(y), m, d); + } catch(e) { return BADNUM; } // Invalid ... date if(!cDate) return BADNUM; @@ -296,7 +239,8 @@ exports.ms2DateTime = function(ms, r, calendar) { var dateJD = Math.floor(msRounded / ONEDAY) + EPOCHJD, timeMs = Math.floor(mod(ms, ONEDAY)); try { - dateStr = getCal(calendar).fromJD(dateJD).formatDate('yyyy-mm-dd'); + dateStr = Registry.getComponentMethod('calendars', 'getCal')(calendar) + .fromJD(dateJD).formatDate('yyyy-mm-dd'); } catch(e) { // invalid date in this calendar - fall back to Gyyyy-mm-dd @@ -405,66 +349,6 @@ exports.cleanDate = function(v, dflt, calendar) { * Date formatting for ticks and hovertext */ -/* - * 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; -} - /* * modDateFormat: Support world calendars, and add one item to * d3's vocabulary: @@ -482,7 +366,7 @@ function modDateFormat(fmt, x, calendar) { } if(isWorldCalendar(calendar)) { try { - fmt = worldCalFmt(fmt, x, calendar); + fmt = Registry.getComponentMethod('calendars', 'worldCalFmt')(fmt, x, calendar); } catch(e) { return 'Invalid'; @@ -546,7 +430,8 @@ exports.formatDate = function(x, fmt, tr, calendar) { if(calendar) { try { var dateJD = Math.floor((x + 0.05) / ONEDAY) + EPOCHJD, - cDate = getCal(calendar).fromJD(dateJD); + cDate = Registry.getComponentMethod('calendars', 'getCal')(calendar) + .fromJD(dateJD); if(tr === 'y') dateStr = yearFormatWorld(cDate); else if(tr === 'm') dateStr = monthFormatWorld(cDate); @@ -618,7 +503,7 @@ exports.incrementMonth = function(ms, dMonth, calendar) { if(calendar) { try { var dateJD = Math.round(ms / ONEDAY) + EPOCHJD, - calInstance = getCal(calendar), + calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar), cDate = calInstance.fromJD(dateJD); if(dMonth % 12) calInstance.add(cDate, dMonth, 'm'); @@ -650,7 +535,10 @@ exports.findExactDates = function(data, calendar) { d, di; - var calInstance = isWorldCalendar(calendar) && getCal(calendar); + var calInstance = ( + isWorldCalendar(calendar) && + Registry.getComponentMethod('calendars', 'getCal')(calendar) + ); for(var i = 0; i < data.length; i++) { di = data[i]; From 5984106fa16f69d7e14703a58c14178260e2d910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Dec 2016 15:42:11 -0500 Subject: [PATCH 18/30] coerce calendar attributes in calenders component - ... only when calendars is registered. - replace 'calendar' valType with the appropriate 'enumarated' val object - --- src/components/calendars/index.js | 23 +++++++++++++++++++++ src/lib/coerce.js | 15 -------------- src/plots/cartesian/axis_defaults.js | 5 ++++- src/plots/cartesian/layout_attributes.js | 10 --------- src/plots/gl3d/layout/axis_attributes.js | 1 - src/plots/layout_attributes.js | 9 -------- src/plots/plots.js | 3 ++- src/traces/bar/attributes.js | 2 -- src/traces/box/defaults.js | 6 +++--- src/traces/candlestick/attributes.js | 1 - src/traces/contour/attributes.js | 2 -- src/traces/heatmap/attributes.js | 2 -- src/traces/heatmap/xyz_defaults.js | 6 +++--- src/traces/histogram/attributes.js | 2 -- src/traces/histogram/defaults.js | 8 +++---- src/traces/histogram2d/attributes.js | 2 -- src/traces/histogram2d/sample_defaults.js | 6 +++--- src/traces/histogram2dcontour/attributes.js | 2 -- src/traces/mesh3d/attributes.js | 3 --- src/traces/mesh3d/defaults.js | 10 +++++---- src/traces/ohlc/attributes.js | 1 - src/traces/ohlc/ohlc_defaults.js | 6 +++++- src/traces/scatter/attributes.js | 10 --------- src/traces/scatter/xy_defaults.js | 7 ++++--- src/traces/scatter3d/attributes.js | 5 ----- src/traces/scatter3d/defaults.js | 6 +++--- src/traces/scattergl/attributes.js | 2 -- src/traces/surface/attributes.js | 6 ------ src/traces/surface/defaults.js | 6 +++--- src/transforms/filter.js | 15 ++++---------- 30 files changed, 67 insertions(+), 115 deletions(-) diff --git a/src/components/calendars/index.js b/src/components/calendars/index.js index c5c6a690f10..9e65164ba72 100644 --- a/src/components/calendars/index.js +++ b/src/components/calendars/index.js @@ -16,6 +16,25 @@ var constants = require('../../constants/numerical'); var EPOCHJD = constants.EPOCHJD; var ONEDAY = constants.ONEDAY; +var attributes = { + valType: 'enumerated', + values: Object.keys(calendars.calendars), + role: 'info', + dflt: 'gregorian' +}; + +var handleDefaults = function(contIn, contOut, attr, dflt) { + var attrs = {}; + attrs[attr] = attributes; + + return Lib.coerce(contIn, contOut, attrs, attr, dflt); +}; + +var handleTraceDefaults = function(traceIn, traceOut, coords, layout) { + for(var i = 0; i < coords.length; i++) { + handleDefaults(traceIn, traceOut, coords[i] + 'calendar', layout.calendar); + } +}; // 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 @@ -149,6 +168,10 @@ module.exports = { moduleType: 'component', name: 'calendars', + + handleDefaults: handleDefaults, + handleTraceDefaults: handleTraceDefaults, + CANONICAL_SUNDAY: CANONICAL_SUNDAY, CANONICAL_TICK: CANONICAL_TICK, DFLTRANGE: DFLTRANGE, diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 846869c2a20..7d69b9e64c7 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -9,7 +9,6 @@ 'use strict'; -var calendarList = Object.keys(require('world-calendars').calendars); var isNumeric = require('fast-isnumeric'); var tinycolor = require('tinycolor2'); @@ -268,20 +267,6 @@ exports.valObjects = { return true; } - }, - calendar: { - description: [ - 'A string, one of the calendar systems available', - 'in the `world-calendars` package, to be used in evaluating', - 'or displaying date data. Defaults to built-in (Gregorian) dates.', - 'available calendars:', '*' + calendarList.join('*, *') + '*' - ].join(' '), - requiredOpts: [], - otherOpts: ['dflt'], - coerceFunction: function(v, propOut, dflt) { - if(v && calendarList.indexOf(v) !== -1) propOut.set(v); - else propOut.set(dflt); - } } }; diff --git a/src/plots/cartesian/axis_defaults.js b/src/plots/cartesian/axis_defaults.js index a69df3f66c9..a0ab9aed0b8 100644 --- a/src/plots/cartesian/axis_defaults.js +++ b/src/plots/cartesian/axis_defaults.js @@ -74,7 +74,10 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce, } } - if(axType === 'date') coerce('calendar', options.calendar); + if(axType === 'date') { + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); + handleCalendarDefaults(containerIn, containerOut, 'calendar', options.calendar); + } setConvert(containerOut); diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index bdcc2e42b31..630aa677855 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -52,16 +52,6 @@ module.exports = { 'the axis in question.' ].join(' ') }, - calendar: { - valType: 'calendar', - role: 'info', - description: [ - 'Sets the calendar system to use for `range` and `tick0`', - 'if this is a date axis. This does not set the calendar for', - 'interpreting data on this axis, that\'s specified in the trace', - 'or via the global `layout.calendar`' - ].join(' ') - }, autorange: { valType: 'enumerated', values: [true, false, 'reversed'], diff --git a/src/plots/gl3d/layout/axis_attributes.js b/src/plots/gl3d/layout/axis_attributes.js index de5839d830f..faad75ece28 100644 --- a/src/plots/gl3d/layout/axis_attributes.js +++ b/src/plots/gl3d/layout/axis_attributes.js @@ -73,7 +73,6 @@ module.exports = { title: axesAttrs.title, titlefont: axesAttrs.titlefont, type: axesAttrs.type, - calendar: axesAttrs.calendar, autorange: axesAttrs.autorange, rangemode: axesAttrs.rangemode, range: axesAttrs.range, diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 21f377e1ccf..235ff39250c 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -170,15 +170,6 @@ module.exports = { role: 'info', description: 'Determines whether or not a legend is drawn.' }, - calendar: { - valType: 'calendar', - role: 'info', - dflt: 'gregorian', - description: [ - 'Sets the default calendar system to use for interpreting and', - 'displaying dates throughout the plot.' - ].join(' ') - }, dragmode: { valType: 'enumerated', role: 'info', diff --git a/src/plots/plots.js b/src/plots/plots.js index dadee1b553c..1b7190f89c7 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -942,7 +942,8 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut) { coerce('hidesources'); coerce('smith'); - coerce('calendar'); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); + handleCalendarDefaults(layoutIn, layoutOut, 'calendar'); }; plots.plotAutoSize = function plotAutoSize(gd, layout, fullLayout) { diff --git a/src/traces/bar/attributes.js b/src/traces/bar/attributes.js index a5843177880..bba0d30611f 100644 --- a/src/traces/bar/attributes.js +++ b/src/traces/bar/attributes.js @@ -47,8 +47,6 @@ module.exports = { y: scatterAttrs.y, y0: scatterAttrs.y0, dy: scatterAttrs.dy, - xcalendar: scatterAttrs.xcalendar, - ycalendar: scatterAttrs.ycalendar, text: scatterAttrs.text, diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js index 7f75a66d4eb..9f33075d88b 100644 --- a/src/traces/box/defaults.js +++ b/src/traces/box/defaults.js @@ -9,6 +9,7 @@ 'use strict'; var Lib = require('../../lib'); +var Registry = require('../../registry'); var Color = require('../../components/color'); var attributes = require('./attributes'); @@ -33,9 +34,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } - var dfltCalendar = layout.calendar; - coerce('xcalendar', dfltCalendar); - coerce('ycalendar', dfltCalendar); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); coerce('orientation', defaultOrientation); diff --git a/src/traces/candlestick/attributes.js b/src/traces/candlestick/attributes.js index f2b61402251..64b7be83203 100644 --- a/src/traces/candlestick/attributes.js +++ b/src/traces/candlestick/attributes.js @@ -27,7 +27,6 @@ var directionAttrs = { module.exports = { x: OHLCattrs.x, - xcalendar: OHLCattrs.xcalendar, open: OHLCattrs.open, high: OHLCattrs.high, low: OHLCattrs.low, diff --git a/src/traces/contour/attributes.js b/src/traces/contour/attributes.js index 9d776e00991..9849d3f06bb 100644 --- a/src/traces/contour/attributes.js +++ b/src/traces/contour/attributes.js @@ -28,8 +28,6 @@ module.exports = extendFlat({}, { transpose: heatmapAttrs.transpose, xtype: heatmapAttrs.xtype, ytype: heatmapAttrs.ytype, - xcalendar: heatmapAttrs.xcalendar, - ycalendar: heatmapAttrs.ycalendar, connectgaps: heatmapAttrs.connectgaps, diff --git a/src/traces/heatmap/attributes.js b/src/traces/heatmap/attributes.js index 19be6407e11..2ec094818f9 100644 --- a/src/traces/heatmap/attributes.js +++ b/src/traces/heatmap/attributes.js @@ -25,8 +25,6 @@ module.exports = extendFlat({}, { y: scatterAttrs.y, y0: scatterAttrs.y0, dy: scatterAttrs.dy, - xcalendar: scatterAttrs.xcalendar, - ycalendar: scatterAttrs.ycalendar, text: { valType: 'data_array', diff --git a/src/traces/heatmap/xyz_defaults.js b/src/traces/heatmap/xyz_defaults.js index 9d808a61819..f07805e88f5 100644 --- a/src/traces/heatmap/xyz_defaults.js +++ b/src/traces/heatmap/xyz_defaults.js @@ -11,6 +11,7 @@ var isNumeric = require('fast-isnumeric'); +var Registry = require('../../registry'); var hasColumns = require('./has_columns'); @@ -37,9 +38,8 @@ module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout) { coerce('transpose'); } - var dfltCalendar = layout.calendar; - coerce('xcalendar', dfltCalendar); - coerce('ycalendar', dfltCalendar); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); return traceOut.z.length; }; diff --git a/src/traces/histogram/attributes.js b/src/traces/histogram/attributes.js index 53ccddb1184..b455fd4b98a 100644 --- a/src/traces/histogram/attributes.js +++ b/src/traces/histogram/attributes.js @@ -24,8 +24,6 @@ module.exports = { 'Sets the sample data to be binned on the y axis.' ].join(' ') }, - xcalendar: barAttrs.xcalendar, - ycalendar: barAttrs.ycalendar, text: barAttrs.text, orientation: barAttrs.orientation, diff --git a/src/traces/histogram/defaults.js b/src/traces/histogram/defaults.js index 4c5975f2cd2..aab5a6f65cb 100644 --- a/src/traces/histogram/defaults.js +++ b/src/traces/histogram/defaults.js @@ -9,6 +9,7 @@ 'use strict'; +var Registry = require('../../registry'); var Lib = require('../../lib'); var Color = require('../../components/color'); @@ -26,6 +27,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var x = coerce('x'), y = coerce('y'); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + coerce('text'); var orientation = coerce('orientation', (y && !x) ? 'h' : 'v'), @@ -36,10 +40,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } - var dfltCalendar = layout.calendar; - coerce('xcalendar', dfltCalendar); - coerce('ycalendar', dfltCalendar); - var hasAggregationData = traceOut[orientation === 'h' ? 'x' : 'y']; if(hasAggregationData) coerce('histfunc'); diff --git a/src/traces/histogram2d/attributes.js b/src/traces/histogram2d/attributes.js index 3a69e20041e..c6c6d87445a 100644 --- a/src/traces/histogram2d/attributes.js +++ b/src/traces/histogram2d/attributes.js @@ -19,8 +19,6 @@ module.exports = extendFlat({}, { x: histogramAttrs.x, y: histogramAttrs.y, - xcalendar: histogramAttrs.xcalendar, - ycalendar: histogramAttrs.ycalendar, z: { valType: 'data_array', diff --git a/src/traces/histogram2d/sample_defaults.js b/src/traces/histogram2d/sample_defaults.js index 0101ee4352b..daeacb66ed7 100644 --- a/src/traces/histogram2d/sample_defaults.js +++ b/src/traces/histogram2d/sample_defaults.js @@ -9,6 +9,7 @@ 'use strict'; +var Registry = require('../../registry'); var handleBinDefaults = require('../histogram/bin_defaults'); @@ -16,9 +17,8 @@ module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout var x = coerce('x'), y = coerce('y'); - var dfltCalendar = layout.calendar; - coerce('xcalendar', dfltCalendar); - coerce('ycalendar', dfltCalendar); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); // we could try to accept x0 and dx, etc... // but that's a pretty weird use case. diff --git a/src/traces/histogram2dcontour/attributes.js b/src/traces/histogram2dcontour/attributes.js index 995e5b9d9bf..49a1ca72596 100644 --- a/src/traces/histogram2dcontour/attributes.js +++ b/src/traces/histogram2dcontour/attributes.js @@ -18,8 +18,6 @@ var extendFlat = require('../../lib/extend').extendFlat; module.exports = extendFlat({}, { x: histogram2dAttrs.x, y: histogram2dAttrs.y, - xcalendar: histogram2dAttrs.xcalendar, - ycalendar: histogram2dAttrs.ycalendar, z: histogram2dAttrs.z, marker: histogram2dAttrs.marker, diff --git a/src/traces/mesh3d/attributes.js b/src/traces/mesh3d/attributes.js index f49f1a8ec62..c726e23c03d 100644 --- a/src/traces/mesh3d/attributes.js +++ b/src/traces/mesh3d/attributes.js @@ -37,9 +37,6 @@ module.exports = { 'jointly represent the X, Y and Z coordinates of the nth vertex.' ].join(' ') }, - xcalendar: surfaceAtts.xcalendar, - ycalendar: surfaceAtts.ycalendar, - zcalendar: surfaceAtts.zcalendar, i: { valType: 'data_array', diff --git a/src/traces/mesh3d/defaults.js b/src/traces/mesh3d/defaults.js index 47c24d484b5..82fd1d4ad84 100644 --- a/src/traces/mesh3d/defaults.js +++ b/src/traces/mesh3d/defaults.js @@ -9,6 +9,7 @@ 'use strict'; +var Registry = require('../../registry'); var Lib = require('../../lib'); var colorbarDefaults = require('../../components/colorbar/defaults'); var attributes = require('./attributes'); @@ -20,12 +21,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } // read in face/vertex properties - function readComponents(array, doCalendar) { + function readComponents(array) { var ret = array.map(function(attr) { var result = coerce(attr); - if(doCalendar) coerce(attr + 'calendar', layout.calendar); - if(result && Array.isArray(result)) return result; return null; }); @@ -35,9 +34,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout }) && ret; } - var coords = readComponents(['x', 'y', 'z'], true); + var coords = readComponents(['x', 'y', 'z']); var indices = readComponents(['i', 'j', 'k']); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout); + if(!coords) { traceOut.visible = false; return; diff --git a/src/traces/ohlc/attributes.js b/src/traces/ohlc/attributes.js index 58551eed920..b4168829c97 100644 --- a/src/traces/ohlc/attributes.js +++ b/src/traces/ohlc/attributes.js @@ -53,7 +53,6 @@ module.exports = { 'If absent, linear coordinate will be generated.' ].join(' ') }, - xcalendar: scatterAttrs.xcalendar, open: { valType: 'data_array', diff --git a/src/traces/ohlc/ohlc_defaults.js b/src/traces/ohlc/ohlc_defaults.js index 51ec3a2cba1..0fe5abdfda7 100644 --- a/src/traces/ohlc/ohlc_defaults.js +++ b/src/traces/ohlc/ohlc_defaults.js @@ -9,6 +9,9 @@ 'use strict'; +var Registry = require('../../registry'); + + module.exports = function handleOHLC(traceIn, traceOut, coerce, layout) { var len; @@ -18,7 +21,8 @@ module.exports = function handleOHLC(traceIn, traceOut, coerce, layout) { low = coerce('low'), close = coerce('close'); - coerce('xcalendar', layout.calendar); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x'], layout); len = Math.min(open.length, high.length, low.length, close.length); diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 8b804332a3f..1a728047d4d 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -41,11 +41,6 @@ module.exports = { 'See `x0` for more info.' ].join(' ') }, - xcalendar: { - valType: 'calendar', - role: 'info', - description: 'Sets the calendar system to use with `x` date data' - }, y: { valType: 'data_array', description: 'Sets the y coordinates.' @@ -70,11 +65,6 @@ module.exports = { 'See `y0` for more info.' ].join(' ') }, - ycalendar: { - valType: 'calendar', - role: 'info', - description: 'Sets the calendar system to use with `y` date data' - }, ids: { valType: 'data_array', description: 'A list of keys for object constancy of data points during animation' diff --git a/src/traces/scatter/xy_defaults.js b/src/traces/scatter/xy_defaults.js index 15bfa324cf3..7e6ff7409f7 100644 --- a/src/traces/scatter/xy_defaults.js +++ b/src/traces/scatter/xy_defaults.js @@ -9,15 +9,16 @@ 'use strict'; +var Registry = require('../../registry'); + module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { var len, x = coerce('x'), y = coerce('y'); - var dfltCalendar = layout.calendar; - coerce('xcalendar', dfltCalendar); - coerce('ycalendar', dfltCalendar); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); if(x) { if(y) { diff --git a/src/traces/scatter3d/attributes.js b/src/traces/scatter3d/attributes.js index ebc7fcdb040..836c4511cb4 100644 --- a/src/traces/scatter3d/attributes.js +++ b/src/traces/scatter3d/attributes.js @@ -65,11 +65,6 @@ module.exports = { valType: 'data_array', description: 'Sets the z coordinates.' }, - xcalendar: scatterAttrs.xcalendar, - ycalendar: scatterAttrs.ycalendar, - zcalendar: extendFlat({}, scatterAttrs.xcalendar, { - description: 'Sets the calendar system to use with `z` date data' - }), text: extendFlat({}, scatterAttrs.text, { description: [ diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js index 45b420c149b..4bd5185b682 100644 --- a/src/traces/scatter3d/defaults.js +++ b/src/traces/scatter3d/defaults.js @@ -9,6 +9,7 @@ 'use strict'; +var Registry = require('../../registry'); var Lib = require('../../lib'); var subTypes = require('../scatter/subtypes'); @@ -72,9 +73,8 @@ function handleXYZDefaults(traceIn, traceOut, coerce, layout) { y = coerce('y'), z = coerce('z'); - coerce('xcalendar', layout.calendar); - coerce('ycalendar', layout.calendar); - coerce('zcalendar', layout.calendar); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout); if(x && y && z) { len = Math.min(x.length, y.length, z.length); diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js index f6e736f6ad5..c5bb9c12bc5 100644 --- a/src/traces/scattergl/attributes.js +++ b/src/traces/scattergl/attributes.js @@ -27,8 +27,6 @@ module.exports = { y: scatterAttrs.y, y0: scatterAttrs.y0, dy: scatterAttrs.dy, - xcalendar: scatterAttrs.xcalendar, - ycalendar: scatterAttrs.ycalendar, text: extendFlat({}, scatterAttrs.text, { description: [ diff --git a/src/traces/surface/attributes.js b/src/traces/surface/attributes.js index c442abced7f..47a0eaf725d 100644 --- a/src/traces/surface/attributes.js +++ b/src/traces/surface/attributes.js @@ -11,7 +11,6 @@ var Color = require('../../components/color'); var colorscaleAttrs = require('../../components/colorscale/attributes'); var colorbarAttrs = require('../../components/colorbar/attributes'); -var scatterAttrs = require('../scatter/attributes'); var extendFlat = require('../../lib/extend').extendFlat; @@ -110,11 +109,6 @@ module.exports = { valType: 'data_array', description: 'Sets the y coordinates.' }, - xcalendar: scatterAttrs.xcalendar, - ycalendar: scatterAttrs.ycalendar, - zcalendar: extendFlat({}, scatterAttrs.xcalendar, { - description: 'Sets the calendar system to use with `z` date data' - }), text: { valType: 'data_array', diff --git a/src/traces/surface/defaults.js b/src/traces/surface/defaults.js index c104c93af6d..3e88b83674a 100644 --- a/src/traces/surface/defaults.js +++ b/src/traces/surface/defaults.js @@ -9,6 +9,7 @@ 'use strict'; +var Registry = require('../../registry'); var Lib = require('../../lib'); var colorscaleDefaults = require('../../components/colorscale/defaults'); @@ -34,9 +35,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('x'); coerce('y'); - coerce('xcalendar', layout.calendar); - coerce('ycalendar', layout.calendar); - coerce('zcalendar', layout.calendar); + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout); if(!Array.isArray(traceOut.x)) { // build a linearly scaled x diff --git a/src/transforms/filter.js b/src/transforms/filter.js index ec6454dd0e8..1467620d7cf 100644 --- a/src/transforms/filter.js +++ b/src/transforms/filter.js @@ -9,6 +9,7 @@ 'use strict'; var Lib = require('../lib'); +var Registry = require('../registry'); var PlotSchema = require('../plot_api/plot_schema'); var axisIds = require('../plots/cartesian/axis_ids'); var autoType = require('../plots/cartesian/axis_autotype'); @@ -100,16 +101,6 @@ exports.attributes = { '*value* is expected to be an array with as many items as', 'the desired set elements.' ].join(' ') - }, - calendar: { - valType: 'calendar', - role: 'info', - description: [ - 'Sets the calendar system to use for `value`, if it is a date.', - 'Note that this is not necessarily the same calendar as is used', - 'for the target data; that is set by its own calendar attribute,', - 'ie `trace.x` uses `trace.xcalendar` etc.' - ].join(' ') } }; @@ -126,7 +117,9 @@ exports.supplyDefaults = function(transformIn) { coerce('operation'); coerce('value'); coerce('target'); - coerce('calendar'); + + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleDefaults'); + handleCalendarDefaults(transformIn, transformOut, 'calendar', null); } return transformOut; From 03ab34f37cfc421d514f8f9f8fa4ecfa33d893d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Dec 2016 15:42:47 -0500 Subject: [PATCH 19/30] add requirable 'calendars' module + include it in main bundle --- lib/calendars.js | 11 +++++++++++ lib/index.js | 5 +++++ 2 files changed, 16 insertions(+) create mode 100644 lib/calendars.js diff --git a/lib/calendars.js b/lib/calendars.js new file mode 100644 index 00000000000..93eb53e3ba6 --- /dev/null +++ b/lib/calendars.js @@ -0,0 +1,11 @@ +/** +* 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'; + +module.exports = require('../src/components/calendars'); diff --git a/lib/index.js b/lib/index.js index ea309cde21a..2f4c821fb27 100644 --- a/lib/index.js +++ b/lib/index.js @@ -54,4 +54,9 @@ Plotly.register([ require('./groupby') ]); +// components +Plotly.register([ + require('./calendars') +]); + module.exports = Plotly; From db3d18b29e22e8cb5a30041a03186bd4dde9063d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Dec 2016 15:43:15 -0500 Subject: [PATCH 20/30] fix typo in getComponentMethod call --- src/plot_api/plot_api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index ad3bdb8bb18..8f0eb013b2c 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -312,7 +312,7 @@ Plotly.plot = function(gd, data, layout, config) { // show annotations and shapes Registry.getComponentMethod('shapes', 'draw')(gd); - Registry.getComponentMethod('annoations', 'draw')(gd); + Registry.getComponentMethod('annotations', 'draw')(gd); // source links Plots.addLinks(gd); From 05b2f96bf752d6b86ed1da5529c3ab0b9893467f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Dec 2016 15:43:39 -0500 Subject: [PATCH 21/30] skip over calendar attribute in findArrayAttributes --- src/plot_api/plot_schema.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index c5a55ef34ee..3ccb629a49a 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -141,7 +141,7 @@ exports.findArrayAttributes = function(trace) { function callback(attr, attrName, attrs, level) { stack = stack.slice(0, level).concat([attrName]); - var splittableAttr = attr.valType === 'data_array' || attr.arrayOk === true; + var splittableAttr = attr && (attr.valType === 'data_array' || attr.arrayOk === true); if(!splittableAttr) return; var astr = toAttrString(stack); @@ -164,6 +164,7 @@ exports.findArrayAttributes = function(trace) { var transform = transforms[i]; stack = ['transforms[' + i + ']']; + exports.crawl(transform._module.attributes, callback, 1); } } From 61ecd42c71aa8319485dd40505148aa37e31a973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Dec 2016 16:36:39 -0500 Subject: [PATCH 22/30] generalise plot schema handling of component attributes - replace layoutNodes list with _module.schema object which allow multiple distinct attribute (e.g. with different description) for each nodes --- src/components/rangeselector/index.js | 7 ++++++- src/components/rangeslider/index.js | 7 ++++++- src/plot_api/plot_schema.js | 17 ++++++++--------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/components/rangeselector/index.js b/src/components/rangeselector/index.js index 600a7c8e608..baad450c296 100644 --- a/src/components/rangeselector/index.js +++ b/src/components/rangeselector/index.js @@ -12,7 +12,12 @@ module.exports = { moduleType: 'component', name: 'rangeselector', - layoutNodes: ['xaxis.'], + schema: { + layout: { + 'xaxis.rangeselector': require('./attributes') + } + }, + layoutAttributes: require('./attributes'), handleDefaults: require('./defaults'), diff --git a/src/components/rangeslider/index.js b/src/components/rangeslider/index.js index 7b652dc43c3..cd929887fc3 100644 --- a/src/components/rangeslider/index.js +++ b/src/components/rangeslider/index.js @@ -12,7 +12,12 @@ module.exports = { moduleType: 'component', name: 'rangeslider', - layoutNodes: ['xaxis.'], + schema: { + layout: { + 'xaxis.rangeslider': require('./attributes') + } + }, + layoutAttributes: require('./attributes'), handleDefaults: require('./defaults'), diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index 3ccb629a49a..e69682998fa 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -257,19 +257,19 @@ function getLayoutAttributes() { // polar layout attributes layoutAttributes = assignPolarLayoutAttrs(layoutAttributes); - // add registered components layout attribute + // add registered components layout attributes 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); + if(_module.schema && _module.schema.layout) { + Object.keys(_module.schema.layout).forEach(function(v) { + insertAttrs(layoutAttributes, _module.schema.layout[v], v); }); } else { - handleRegisteredComponent(layoutAttributes, _module, _module.name); + insertAttrs(layoutAttributes, _module.layoutAttributes, _module.name); } }); @@ -366,9 +366,8 @@ function handleBasePlotModule(layoutAttributes, _module, astr) { np.set(attrs); } -function handleRegisteredComponent(layoutAttributes, _module, astr) { - var np = Lib.nestedProperty(layoutAttributes, astr), - attrs = extendDeep(np.get() || {}, _module.layoutAttributes); +function insertAttrs(baseAttrs, newAttrs, astr) { + var np = Lib.nestedProperty(baseAttrs, astr); - np.set(attrs); + np.set(extendDeep(np.get() || {}, newAttrs)); } From 68af28745fba7551e57ae166a95e64bad76bf6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Tue, 6 Dec 2016 16:37:55 -0500 Subject: [PATCH 23/30] add schema attributes in calendars module - ++ allow trace and transform attributes to be filled by component modules. --- src/components/calendars/index.js | 68 +++++++++++++++++++++++++++ src/plot_api/plot_schema.js | 25 +++++++++- test/jasmine/tests/plotschema_test.js | 11 +++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/src/components/calendars/index.js b/src/components/calendars/index.js index 9e65164ba72..eb44e3528ea 100644 --- a/src/components/calendars/index.js +++ b/src/components/calendars/index.js @@ -35,6 +35,7 @@ var handleTraceDefaults = function(traceIn, traceOut, coords, layout) { handleDefaults(traceIn, traceOut, coords[i] + 'calendar', layout.calendar); } }; + // 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 @@ -164,10 +165,77 @@ function getCal(calendar) { return calendarObj; } +function makeAttrs(description) { + return Lib.extendFlat({}, attributes, { description: description }); +} + +function makeTraceAttrsDescription(coord) { + return 'Sets the calendar system to use with `' + coord + '` date data.'; +} + +var xAttrs = { + xcalendar: makeAttrs(makeTraceAttrsDescription('x')) +}; + +var xyAttrs = Lib.extendFlat({}, xAttrs, { + ycalendar: makeAttrs(makeTraceAttrsDescription('y')) +}); + +var xyzAttrs = Lib.extendFlat({}, xyAttrs, { + zcalendar: makeAttrs(makeTraceAttrsDescription('z')) +}); + +var axisAttrs = makeAttrs([ + 'Sets the calendar system to use for `range` and `tick0`', + 'if this is a date axis. This does not set the calendar for', + 'interpreting data on this axis, that\'s specified in the trace', + 'or via the global `layout.calendar`' +].join(' ')); + module.exports = { moduleType: 'component', name: 'calendars', + schema: { + traces: { + scatter: xyAttrs, + bar: xyAttrs, + heatmap: xyAttrs, + contour: xyAttrs, + histogram: xyAttrs, + histogram2d: xyAttrs, + histogram2dcontour: xyAttrs, + scatter3d: xyzAttrs, + surface: xyzAttrs, + mesh3d: xyzAttrs, + scattergl: xyAttrs, + ohlc: xAttrs, + candlestick: xAttrs + }, + layout: { + calendar: makeAttrs([ + 'Sets the default calendar system to use for interpreting and', + 'displaying dates throughout the plot.' + ].join(' ')), + 'xaxis.calendar': axisAttrs, + 'yaxis.calendar': axisAttrs, + 'scene.xaxis.calendar': axisAttrs, + 'scene.yaxis.calendar': axisAttrs, + 'scene.zaxis.calendar': axisAttrs + }, + transforms: { + filter: { + calendar: makeAttrs([ + 'Sets the calendar system to use for `value`, if it is a date.', + 'Note that this is not necessarily the same calendar as is used', + 'for the target data; that is set by its own calendar attribute,', + 'ie `trace.x` uses `trace.xcalendar` etc.' + ].join(' ')) + } + } + }, + + layoutAttributes: attributes, handleDefaults: handleDefaults, handleTraceDefaults: handleTraceDefaults, diff --git a/src/plot_api/plot_schema.js b/src/plot_api/plot_schema.js index e69682998fa..1be1b01ba24 100644 --- a/src/plot_api/plot_schema.js +++ b/src/plot_api/plot_schema.js @@ -212,6 +212,17 @@ function getTraceAttributes(type) { extendDeep(attributes, basePlotModule.attributes); } + // add registered components trace attributes + Object.keys(Registry.componentsRegistry).forEach(function(k) { + var _module = Registry.componentsRegistry[k]; + + if(_module.schema && _module.schema.traces && _module.schema.traces[type]) { + Object.keys(_module.schema.traces[type]).forEach(function(v) { + insertAttrs(attributes, _module.schema.traces[type][v], v); + }); + } + }); + // 'type' gets overwritten by baseAttributes; reset it here attributes.type = type; @@ -280,9 +291,21 @@ function getLayoutAttributes() { function getTransformAttributes(type) { var _module = Registry.transformsRegistry[type]; + var attributes = extendDeep({}, _module.attributes); + + // add registered components transform attributes + Object.keys(Registry.componentsRegistry).forEach(function(k) { + var _module = Registry.componentsRegistry[k]; + + if(_module.schema && _module.schema.transforms && _module.schema.transforms[type]) { + Object.keys(_module.schema.transforms[type]).forEach(function(v) { + insertAttrs(attributes, _module.schema.transforms[type][v], v); + }); + } + }); return { - attributes: formatAttributes(_module.attributes) + attributes: formatAttributes(attributes) }; } diff --git a/test/jasmine/tests/plotschema_test.js b/test/jasmine/tests/plotschema_test.js index 50503259c84..2420632f3bf 100644 --- a/test/jasmine/tests/plotschema_test.js +++ b/test/jasmine/tests/plotschema_test.js @@ -188,6 +188,17 @@ describe('plot schema', function() { }); }); + it('should work with registered components', function() { + expect(plotSchema.traces.scatter.attributes.xcalendar.valType).toEqual('enumerated'); + expect(plotSchema.traces.scatter3d.attributes.zcalendar.valType).toEqual('enumerated'); + + expect(plotSchema.layout.layoutAttributes.calendar.valType).toEqual('enumerated'); + expect(plotSchema.layout.layoutAttributes.xaxis.calendar.valType).toEqual('enumerated'); + expect(plotSchema.layout.layoutAttributes.scene.xaxis.calendar.valType).toEqual('enumerated'); + + expect(plotSchema.transforms.filter.attributes.calendar.valType).toEqual('enumerated'); + }); + it('should list correct defs', function() { expect(plotSchema.defs.valObjects).toBeDefined(); From cb2c54ba96c7997b022346258d8dc4617794aea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 7 Dec 2016 11:11:37 -0500 Subject: [PATCH 24/30] move calendar defaults after early return --- src/traces/histogram/defaults.js | 6 +++--- src/traces/histogram2d/sample_defaults.js | 6 +++--- src/traces/mesh3d/defaults.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/traces/histogram/defaults.js b/src/traces/histogram/defaults.js index aab5a6f65cb..c90508f8620 100644 --- a/src/traces/histogram/defaults.js +++ b/src/traces/histogram/defaults.js @@ -27,9 +27,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var x = coerce('x'), y = coerce('y'); - var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); - handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); - coerce('text'); var orientation = coerce('orientation', (y && !x) ? 'h' : 'v'), @@ -40,6 +37,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + var hasAggregationData = traceOut[orientation === 'h' ? 'x' : 'y']; if(hasAggregationData) coerce('histfunc'); diff --git a/src/traces/histogram2d/sample_defaults.js b/src/traces/histogram2d/sample_defaults.js index daeacb66ed7..37070082c48 100644 --- a/src/traces/histogram2d/sample_defaults.js +++ b/src/traces/histogram2d/sample_defaults.js @@ -17,9 +17,6 @@ module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout var x = coerce('x'), y = coerce('y'); - var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); - handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); - // we could try to accept x0 and dx, etc... // but that's a pretty weird use case. // for now require both x and y explicitly specified. @@ -28,6 +25,9 @@ module.exports = function handleSampleDefaults(traceIn, traceOut, coerce, layout return; } + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + // if marker.color is an array, we can use it in aggregation instead of z var hasAggregationData = coerce('z') || coerce('marker.color'); diff --git a/src/traces/mesh3d/defaults.js b/src/traces/mesh3d/defaults.js index 82fd1d4ad84..f366a2dab0b 100644 --- a/src/traces/mesh3d/defaults.js +++ b/src/traces/mesh3d/defaults.js @@ -37,9 +37,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var coords = readComponents(['x', 'y', 'z']); var indices = readComponents(['i', 'j', 'k']); - var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); - handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout); - if(!coords) { traceOut.visible = false; return; @@ -52,6 +49,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout }); } + var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); + handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout); + // Coerce remaining properties [ 'lighting.ambient', From 1444f55c4f7e7bc6654ad315223149b17dad9a95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89tienne=20T=C3=A9treault-Pinard?= Date: Wed, 7 Dec 2016 15:33:50 -0500 Subject: [PATCH 25/30] use only the calendars we need from 'world-calendars' --- src/components/calendars/calendars.js | 30 +++++++++++++++++++++++++++ src/components/calendars/index.js | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/components/calendars/calendars.js diff --git a/src/components/calendars/calendars.js b/src/components/calendars/calendars.js new file mode 100644 index 00000000000..05194b874b7 --- /dev/null +++ b/src/components/calendars/calendars.js @@ -0,0 +1,30 @@ +/** +* 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'; + +// a trimmed down version of: +// https://github.com/alexcjohnson/world-calendars/blob/master/dist/index.js + +module.exports = require('world-calendars/dist/main'); + +require('world-calendars/dist/plus'); + +require('world-calendars/dist/calendars/coptic'); +require('world-calendars/dist/calendars/discworld'); +require('world-calendars/dist/calendars/ethiopian'); +require('world-calendars/dist/calendars/hebrew'); +require('world-calendars/dist/calendars/islamic'); +require('world-calendars/dist/calendars/julian'); +require('world-calendars/dist/calendars/mayan'); +require('world-calendars/dist/calendars/nanakshahi'); +require('world-calendars/dist/calendars/nepali'); +require('world-calendars/dist/calendars/persian'); +require('world-calendars/dist/calendars/taiwan'); +require('world-calendars/dist/calendars/thai'); +require('world-calendars/dist/calendars/ummalqura'); diff --git a/src/components/calendars/index.js b/src/components/calendars/index.js index eb44e3528ea..ac29f7ce5c1 100644 --- a/src/components/calendars/index.js +++ b/src/components/calendars/index.js @@ -8,7 +8,7 @@ 'use strict'; -var calendars = require('world-calendars'); +var calendars = require('./calendars'); var Lib = require('../../lib'); var constants = require('../../constants/numerical'); From dcddceef1cffaa2699fde2935041619d0b155b88 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Thu, 8 Dec 2016 15:24:02 -0500 Subject: [PATCH 26/30] support chinese calendar --- package.json | 2 +- src/components/calendars/calendars.js | 1 + src/components/calendars/index.js | 8 ++- src/lib/dates.js | 20 ++++-- src/plots/cartesian/axes.js | 4 +- test/image/mocks/gl3d_world-cals.json | 30 ++++---- test/jasmine/tests/lib_date_test.js | 100 ++++++++++++++++++++++++++ 7 files changed, 141 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 68552a3d666..bb9325e5d71 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "tinycolor2": "^1.3.0", "topojson-client": "^2.1.0", "webgl-context": "^2.2.0", - "world-calendars": "0.1.0" + "world-calendars": "^1.0.0" }, "devDependencies": { "brfs": "^1.4.3", diff --git a/src/components/calendars/calendars.js b/src/components/calendars/calendars.js index 05194b874b7..02dfbfefdc1 100644 --- a/src/components/calendars/calendars.js +++ b/src/components/calendars/calendars.js @@ -15,6 +15,7 @@ module.exports = require('world-calendars/dist/main'); require('world-calendars/dist/plus'); +require('world-calendars/dist/calendars/chinese'); require('world-calendars/dist/calendars/coptic'); require('world-calendars/dist/calendars/discworld'); require('world-calendars/dist/calendars/ethiopian'); diff --git a/src/components/calendars/index.js b/src/components/calendars/index.js index ac29f7ce5c1..076411f4739 100644 --- a/src/components/calendars/index.js +++ b/src/components/calendars/index.js @@ -41,6 +41,7 @@ var handleTraceDefaults = function(traceIn, traceOut, coords, layout) { // 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 = { + chinese: '2000-01-01', coptic: '2000-01-01', discworld: '2000-01-01', ethiopian: '2000-01-01', @@ -58,11 +59,13 @@ var CANONICAL_TICK = { }; // Start on a Sunday - for week ticks -// Discworld and Mayan calendars don't have 7-day weeks anyway so don't change them. +// Discworld and Mayan calendars don't have 7-day weeks but we're going to give them +// 7-day week ticks so start on our Sundays. // If anyone really cares we can customize the auto tick spacings for these calendars. var CANONICAL_SUNDAY = { + chinese: '2000-01-02', coptic: '2000-01-03', - discworld: '2000-01-01', + discworld: '2000-01-03', ethiopian: '2000-01-05', hebrew: '5000-01-01', islamic: '1000-01-02', @@ -78,6 +81,7 @@ var CANONICAL_SUNDAY = { }; var DFLTRANGE = { + chinese: ['2000-01-01', '2001-01-01'], coptic: ['1700-01-01', '1701-01-01'], discworld: ['1800-01-01', '1801-01-01'], ethiopian: ['2000-01-01', '2001-01-01'], diff --git a/src/lib/dates.js b/src/lib/dates.js index 2dde90b0187..e366fce590c 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -28,6 +28,8 @@ var Registry = require('../registry'); var utcFormat = d3.time.format.utc; var DATETIME_REGEXP = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\d)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m; +// special regex for chinese calendars to support yyyy-mmi-dd etc for intercalary months +var DATETIME_REGEXP_CN = /^\s*(-?\d\d\d\d|\d\d)(-(\d?\di?)(-(\d?\d)([ Tt]([01]?\d|2[0-3])(:([0-5]\d)(:([0-5]\d(\.\d+)?))?(Z|z|[+\-]\d\d:?\d\d)?)?)?)?)?\s*$/m; // for 2-digit years, the first year we map them onto var YFIRST = new Date().getFullYear() - 70; @@ -155,10 +157,12 @@ exports.dateTime2ms = function(s, calendar) { calendar = ''; } - var match = s.match(DATETIME_REGEXP); + var isChinese = calendar && calendar.substr(0, 7) === 'chinese'; + + var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP); if(!match) return BADNUM; var y = match[1], - m = Number(match[3] || 1), + m = match[3] || '1', d = Number(match[5] || 1), H = Number(match[7] || 0), M = Number(match[9] || 0), @@ -167,11 +171,19 @@ exports.dateTime2ms = function(s, calendar) { if(isWorld) { // disallow 2-digit years for world calendars if(y.length === 2) return BADNUM; + y = Number(y); var cDate; try { - cDate = Registry.getComponentMethod('calendars', 'getCal')(calendar) - .newDate(Number(y), m, d); + var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar); + if(isChinese) { + var isIntercalary = m.charAt(m.length - 1) === 'i'; + m = Number(isIntercalary ? m.substr(0, m.length - 1) : m); + cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d); + } + else { + cDate = calInstance.newDate(y, Number(m), d); + } } catch(e) { return BADNUM; } // Invalid ... date diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 32e9cd595de..44556f88eb3 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -924,9 +924,9 @@ function autoTickRound(ax) { // If tick0 is unusual, give tickround a bit more information // not necessarily *all* the information in tick0 though, if it's really odd // minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19 - // take off a leading minus (year < 0 so length is consistent) + // take off a leading minus (year < 0) and i (intercalary month) so length is consistent var tick0ms = ax.r2l(ax.tick0), - tick0str = ax.l2r(tick0ms).replace(/^-/, ''), + tick0str = ax.l2r(tick0ms).replace(/(^-|i)/g, ''), tick0len = tick0str.length; if(String(dtick).charAt(0) === 'M') { diff --git a/test/image/mocks/gl3d_world-cals.json b/test/image/mocks/gl3d_world-cals.json index 303b222e9bf..f54d1ff49cf 100644 --- a/test/image/mocks/gl3d_world-cals.json +++ b/test/image/mocks/gl3d_world-cals.json @@ -27,12 +27,12 @@ ], "type": "mesh3d", "x": [ - "2000-01-01", - "2000-02-01", - "2000-03-01", - "2000-01-01" + "2001-04-01", + "2001-04i-01", + "2001-05-01", + "2001-04-01" ], - "xcalendar": "ethiopian", + "xcalendar": "chinese", "y": [ "0100-01-01", "0100-01-01", @@ -50,12 +50,12 @@ }, { "x": [ - "2000-01-01", - "2000-01-01", - "2000-03-01", - "2000-03-01" + "2001-04-01", + "2001-04-01", + "2001-05-01", + "2001-05-01" ], - "xcalendar": "ethiopian", + "xcalendar": "chinese", "y": [ "0100-01-01", "0100-03-01", @@ -74,10 +74,10 @@ }, { "x": [ - "2000-01-01", - "2000-03-01" + "2001-04-01", + "2001-05-01" ], - "xcalendar": "ethiopian", + "xcalendar": "chinese", "y": [ "0100-01-01", "0100-03-01" @@ -111,8 +111,8 @@ "type": "date" }, "zaxis": { - "title": "ethiopian", - "calendar": "ethiopian", + "title": "chinese", + "calendar": "chinese", "type": "date" }, "camera": { diff --git a/test/jasmine/tests/lib_date_test.js b/test/jasmine/tests/lib_date_test.js index b7b43a37ee1..9df1ecfd99a 100644 --- a/test/jasmine/tests/lib_date_test.js +++ b/test/jasmine/tests/lib_date_test.js @@ -1,5 +1,10 @@ var isNumeric = require('fast-isnumeric'); + var Lib = require('@src/lib'); +var calComponent = require('@src/components/calendars'); + +// use only the parts of world-calendars that we've imported for our tests +var calendars = require('@src/components/calendars/calendars'); describe('dates', function() { 'use strict'; @@ -240,6 +245,7 @@ describe('dates', function() { [ [undefined, '1970-01-01'], ['gregorian', '1970-01-01'], + ['chinese', '1969-11-24'], ['coptic', '1686-04-23'], ['discworld', '1798-12-27'], ['ethiopian', '1962-04-23'], @@ -284,6 +290,55 @@ describe('dates', function() { expect(Lib.dateTime2ms(expected_lastinstant, calendar)).toBe(lastInstant, calendar); }); }); + + it('should contain canonical ticks sundays, ranges for all calendars', function() { + var calList = Object.keys(calendars.calendars).filter(function(v) { + return v !== 'gregorian'; + }); + + var canonicalTick = calComponent.CANONICAL_TICK, + canonicalSunday = calComponent.CANONICAL_SUNDAY, + dfltRange = calComponent.DFLTRANGE; + expect(Object.keys(canonicalTick).length).toBe(calList.length); + expect(Object.keys(canonicalSunday).length).toBe(calList.length); + expect(Object.keys(dfltRange).length).toBe(calList.length); + + calList.forEach(function(calendar) { + expect(Lib.dateTime2ms(canonicalTick[calendar], calendar)).toBeDefined(calendar); + var sunday = Lib.dateTime2ms(canonicalSunday[calendar], calendar); + // convert back implicitly with gregorian calendar + expect(Lib.formatDate(sunday, '%A')).toBe('Sunday', calendar); + + expect(Lib.dateTime2ms(dfltRange[calendar][0], calendar)).toBeDefined(calendar); + expect(Lib.dateTime2ms(dfltRange[calendar][1], calendar)).toBeDefined(calendar); + }); + }); + + it('should handle Chinese intercalary months correctly', function() { + var intercalaryDates = [ + '1995-08i-01', + '1995-08i-29', + '1984-10i-15', + '2023-02i-29' + ]; + intercalaryDates.forEach(function(v) { + var ms = Lib.dateTime2ms(v, 'chinese'); + expect(Lib.ms2DateTime(ms, 0, 'chinese')).toBe(v); + + // should also work without leading zeros + var vShort = v.replace(/-0/g, '-'); + expect(Lib.dateTime2ms(vShort, 'chinese')).toBe(ms, vShort); + }); + + var badIntercalaryDates = [ + '1995-07i-01', + '1995-08i-30', + '1995-09i-01' + ]; + badIntercalaryDates.forEach(function(v) { + expect(Lib.dateTime2ms(v, 'chinese')).toBeUndefined(v); + }); + }); }); describe('cleanDate', function() { @@ -341,6 +396,51 @@ describe('dates', function() { }); }); + describe('incrementMonth', function() { + it('should include Chinese intercalary months', function() { + var start = '1995-06-01'; + var expected = [ + '1995-07-01', + '1995-08-01', + '1995-08i-01', + '1995-09-01', + '1995-10-01', + '1995-11-01', + '1995-12-01', + '1996-01-01' + ]; + var tick = Lib.dateTime2ms(start, 'chinese'); + expected.forEach(function(v) { + tick = Lib.incrementMonth(tick, 1, 'chinese'); + expect(tick).toBe(Lib.dateTime2ms(v, 'chinese'), v); + }); + }); + + it('should increment years even over leap years', function() { + var start = '1995-06-01'; + var expected = [ + '1996-06-01', + '1997-06-01', + '1998-06-01', + '1999-06-01', + '2000-06-01', + '2001-06-01', + '2002-06-01', + '2003-06-01', + '2004-06-01', + '2005-06-01', + '2006-06-01', + '2007-06-01', + '2008-06-01' + ]; + var tick = Lib.dateTime2ms(start, 'chinese'); + expected.forEach(function(v) { + tick = Lib.incrementMonth(tick, 12, 'chinese'); + expect(tick).toBe(Lib.dateTime2ms(v, 'chinese'), v); + }); + }); + }); + describe('isJSDate', function() { it('should return true for any Date object but not the equivalent numbers', function() { [ From 8d8e936a3aa3766a71f6841319d5e6f07058cbc0 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Thu, 8 Dec 2016 15:59:34 -0500 Subject: [PATCH 27/30] fix some tests for chinese, and robustify dateTime2ms --- src/lib/dates.js | 2 +- test/jasmine/tests/annotations_test.js | 3 ++- test/jasmine/tests/finance_test.js | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/dates.js b/src/lib/dates.js index e366fce590c..7cd13a80cbd 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -157,7 +157,7 @@ exports.dateTime2ms = function(s, calendar) { calendar = ''; } - var isChinese = calendar && calendar.substr(0, 7) === 'chinese'; + var isChinese = isWorld && calendar.substr(0, 7) === 'chinese'; var match = s.match(isChinese ? DATETIME_REGEXP_CN : DATETIME_REGEXP); if(!match) return BADNUM; diff --git a/test/jasmine/tests/annotations_test.js b/test/jasmine/tests/annotations_test.js index eeac5f13c9a..fd99e6c6881 100644 --- a/test/jasmine/tests/annotations_test.js +++ b/test/jasmine/tests/annotations_test.js @@ -237,7 +237,8 @@ describe('annotations autosize', function() { expect(fullLayout.xaxis.range).toBeCloseToArray(x, PREC, '- xaxis'); expect(fullLayout.yaxis.range).toBeCloseToArray(y, PREC, '- yaxis'); - expect(dateAx.range.map(dateAx.r2l)).toBeCloseToArray(x2.map(dateAx.r2l), PRECX2, 'xaxis2 ' + dateAx.range); + expect(Lib.simpleMap(dateAx.range, dateAx.r2l)) + .toBeCloseToArray(Lib.simpleMap(x2, dateAx.r2l), PRECX2, 'xaxis2 ' + dateAx.range); expect(fullLayout.yaxis2.range).toBeCloseToArray(y2, PRECY2, 'yaxis2'); expect(fullLayout.xaxis3.range).toBeCloseToArray(x3, PREC, 'xaxis3'); expect(fullLayout.yaxis3.range).toBeCloseToArray(y3, PREC, 'yaxis3'); diff --git a/test/jasmine/tests/finance_test.js b/test/jasmine/tests/finance_test.js index 87caef0648a..f6e292cf4dc 100644 --- a/test/jasmine/tests/finance_test.js +++ b/test/jasmine/tests/finance_test.js @@ -688,10 +688,10 @@ describe('finance charts calc transforms:', function() { var out = _calc([trace0, trace1]); - var x0 = out[0].x.map(Lib.dateTime2ms); + var x0 = Lib.simpleMap(out[0].x, Lib.dateTime2ms); expect(x0[x0.length - 2] - x0[0]).toEqual(1); - var x2 = out[2].x.map(Lib.dateTime2ms); + var x2 = Lib.simpleMap(out[2].x, Lib.dateTime2ms); expect(x2[x2.length - 2] - x2[0]).toEqual(1); expect(out[1].x).toEqual([]); From 0e05f9597c5556447bc833b5183bbbddf4812ca4 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Thu, 8 Dec 2016 16:32:21 -0500 Subject: [PATCH 28/30] update baseline image with chinese calendar --- test/image/baselines/gl3d_world-cals.png | Bin 57621 -> 60698 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/image/baselines/gl3d_world-cals.png b/test/image/baselines/gl3d_world-cals.png index 9fd0581a7e44af05bea762529f8b3323f45b7bf7..ffbc6fc552eac9508cdd421aa3acd4d4d064f98f 100644 GIT binary patch literal 60698 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

vznw!W*BJ4CPL$i&6hFPf87L={&fCn&$pwQ{ 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 literal 57621 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�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 b-J(_A!4NZr@0x>Wx}zZbT&7sc#Q*;QRzVhb From 509f287032f561d54c24b5e2dccdb1b9ab5e7a31 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Thu, 8 Dec 2016 19:21:35 -0500 Subject: [PATCH 29/30] perf: parseInt for string-leading int extraction --- src/lib/dates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dates.js b/src/lib/dates.js index 7cd13a80cbd..503acd6ef48 100644 --- a/src/lib/dates.js +++ b/src/lib/dates.js @@ -178,7 +178,7 @@ exports.dateTime2ms = function(s, calendar) { var calInstance = Registry.getComponentMethod('calendars', 'getCal')(calendar); if(isChinese) { var isIntercalary = m.charAt(m.length - 1) === 'i'; - m = Number(isIntercalary ? m.substr(0, m.length - 1) : m); + m = parseInt(m, 10); cDate = calInstance.newDate(y, calInstance.toMonthIndex(y, m, isIntercalary), d); } else { From 7bd501fbae2bdf6112df98685292584515e1ba78 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Thu, 8 Dec 2016 19:27:03 -0500 Subject: [PATCH 30/30] prevent non-gregorian month/year todate range selectors --- .../rangeselector/button_attributes.js | 4 +- src/components/rangeselector/defaults.js | 14 +++-- src/plots/cartesian/layout_defaults.js | 3 +- test/jasmine/tests/range_selector_test.js | 55 +++++++++++++++++-- 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/components/rangeselector/button_attributes.js b/src/components/rangeselector/button_attributes.js index 5ebfd7a7faa..db0f69cb017 100644 --- a/src/components/rangeselector/button_attributes.js +++ b/src/components/rangeselector/button_attributes.js @@ -33,7 +33,9 @@ module.exports = { '*step* milliseconds back.', 'For example, with `step` set to *year* and `count` set to *1*', 'the range update shifts the start of the range back to', - 'January 01 of the current year.' + 'January 01 of the current year.', + 'Month and year *todate* are currently available only', + 'for the built-in (Gregorian) calendar.' ].join(' ') }, count: { diff --git a/src/components/rangeselector/defaults.js b/src/components/rangeselector/defaults.js index bb568c2479c..4f9d4efd18a 100644 --- a/src/components/rangeselector/defaults.js +++ b/src/components/rangeselector/defaults.js @@ -16,7 +16,7 @@ var buttonAttrs = require('./button_attributes'); var constants = require('./constants'); -module.exports = function handleDefaults(containerIn, containerOut, layout, counterAxes) { +module.exports = function handleDefaults(containerIn, containerOut, layout, counterAxes, calendar) { var selectorIn = containerIn.rangeselector || {}, selectorOut = containerOut.rangeselector = {}; @@ -24,7 +24,7 @@ module.exports = function handleDefaults(containerIn, containerOut, layout, coun return Lib.coerce(selectorIn, selectorOut, attributes, attr, dflt); } - var buttons = buttonsDefaults(selectorIn, selectorOut); + var buttons = buttonsDefaults(selectorIn, selectorOut, calendar); var visible = coerce('visible', buttons.length > 0); if(!visible) return; @@ -45,7 +45,7 @@ module.exports = function handleDefaults(containerIn, containerOut, layout, coun coerce('borderwidth'); }; -function buttonsDefaults(containerIn, containerOut) { +function buttonsDefaults(containerIn, containerOut, calendar) { var buttonsIn = containerIn.buttons || [], buttonsOut = containerOut.buttons = []; @@ -63,7 +63,13 @@ function buttonsDefaults(containerIn, containerOut) { var step = coerce('step'); if(step !== 'all') { - coerce('stepmode'); + if(calendar && calendar !== 'gregorian' && (step === 'month' || step === 'year')) { + buttonOut.stepmode = 'backward'; + } + else { + coerce('stepmode'); + } + coerce('count'); } diff --git a/src/plots/cartesian/layout_defaults.js b/src/plots/cartesian/layout_defaults.js index aaf9673bc6c..625cb9e171f 100644 --- a/src/plots/cartesian/layout_defaults.js +++ b/src/plots/cartesian/layout_defaults.js @@ -167,7 +167,8 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { rangeSliderDefaults(layoutIn, layoutOut, axName, counterAxes); if(axLetter === 'x' && axLayoutOut.type === 'date') { - rangeSelectorDefaults(axLayoutIn, axLayoutOut, layoutOut, counterAxes); + rangeSelectorDefaults(axLayoutIn, axLayoutOut, layoutOut, counterAxes, + axLayoutOut.calendar); } }); }; diff --git a/test/jasmine/tests/range_selector_test.js b/test/jasmine/tests/range_selector_test.js index 85a024c6ba2..1a306e4b24d 100644 --- a/test/jasmine/tests/range_selector_test.js +++ b/test/jasmine/tests/range_selector_test.js @@ -17,7 +17,7 @@ describe('range selector defaults:', function() { var handleDefaults = RangeSelector.handleDefaults; - function supply(containerIn, containerOut) { + function supply(containerIn, containerOut, calendar) { containerOut.domain = [0, 1]; var layout = { @@ -26,7 +26,7 @@ describe('range selector defaults:', function() { var counterAxes = ['yaxis']; - handleDefaults(containerIn, containerOut, layout, counterAxes); + handleDefaults(containerIn, containerOut, layout, counterAxes, calendar); } it('should set \'visible\' to false when no buttons are present', function() { @@ -94,7 +94,7 @@ describe('range selector defaults:', function() { }; var containerOut = {}; - supply(containerIn, containerOut, {}, []); + supply(containerIn, containerOut); expect(containerOut.rangeselector.visible).toBe(true); expect(containerOut.rangeselector.buttons).toEqual([ @@ -114,7 +114,7 @@ describe('range selector defaults:', function() { }; var containerOut = {}; - supply(containerIn, containerOut, {}, []); + supply(containerIn, containerOut); expect(containerOut.rangeselector.buttons).toEqual([{ step: 'all', @@ -176,6 +176,53 @@ describe('range selector defaults:', function() { expect(containerOut.rangeselector.x).toEqual(0.5); expect(containerOut.rangeselector.y).toBeCloseTo(0.87); }); + + it('should not allow month/year todate with calendars other than Gregorian', function() { + var containerIn = { + rangeselector: { + buttons: [{ + step: 'year', + count: 1, + stepmode: 'todate' + }, { + step: 'month', + count: 6, + stepmode: 'todate' + }, { + step: 'day', + count: 1, + stepmode: 'todate' + }, { + step: 'hour', + count: 1, + stepmode: 'todate' + }] + } + }; + var containerOut; + function getStepmode(button) { return button.stepmode; } + + containerOut = {}; + supply(containerIn, containerOut); + + expect(containerOut.rangeselector.buttons.map(getStepmode)).toEqual([ + 'todate', 'todate', 'todate', 'todate' + ]); + + containerOut = {}; + supply(containerIn, containerOut, 'gregorian'); + + expect(containerOut.rangeselector.buttons.map(getStepmode)).toEqual([ + 'todate', 'todate', 'todate', 'todate' + ]); + + containerOut = {}; + supply(containerIn, containerOut, 'chinese'); + + expect(containerOut.rangeselector.buttons.map(getStepmode)).toEqual([ + 'backward', 'backward', 'todate', 'todate' + ]); + }); }); describe('range selector getUpdateObject:', function() {