datalib
Version:
JavaScript utilites for loading, summarizing and working with data.
232 lines (196 loc) • 6.89 kB
JavaScript
var util = require('./util'),
d3_time = require('d3-time'),
d3_timeF = require('d3-time-format'),
d3_numberF = require('d3-format'),
numberF = d3_numberF, // defaults to EN-US
timeF = d3_timeF, // defaults to EN-US
tmpDate = new Date(2000, 0, 1),
monthFull, monthAbbr, dayFull, dayAbbr;
module.exports = {
// Update number formatter to use provided locale configuration.
// For more see https://github.com/d3/d3-format
numberLocale: numberLocale,
number: function(f) { return numberF.format(f); },
numberPrefix: function(f, v) { return numberF.formatPrefix(f, v); },
// Update time formatter to use provided locale configuration.
// For more see https://github.com/d3/d3-time-format
timeLocale: timeLocale,
time: function(f) { return timeF.format(f); },
utc: function(f) { return timeF.utcFormat(f); },
// Set number and time locale simultaneously.
locale: function(l) { numberLocale(l); timeLocale(l); },
// automatic formatting functions
auto: {
number: autoNumberFormat,
linear: linearNumberFormat,
time: function() { return timeAutoFormat(); },
utc: function() { return utcAutoFormat(); }
},
month: monthFormat, // format month name from integer code
day: dayFormat, // format week day name from integer code
quarter: quarterFormat, // format quarter name from timestamp
utcQuarter: utcQuarterFormat // format quarter name from utc timestamp
};
// -- Locales ----
// transform 'en-US' style locale string to match d3-format v0.4+ convention
function localeRef(l) {
return l.length > 4 && 'locale' + (
l[0].toUpperCase() + l[1].toLowerCase() +
l[3].toUpperCase() + l[4].toLowerCase()
);
}
function numberLocale(l) {
var f = util.isString(l) ? d3_numberF[localeRef(l)] : d3_numberF.locale(l);
if (f == null) throw Error('Unrecognized locale: ' + l);
numberF = f;
}
function timeLocale(l) {
var f = util.isString(l) ? d3_timeF[localeRef(l)] : d3_timeF.locale(l);
if (f == null) throw Error('Unrecognized locale: ' + l);
timeF = f;
monthFull = monthAbbr = dayFull = dayAbbr = null;
}
// -- Number Formatting ----
var e10 = Math.sqrt(50),
e5 = Math.sqrt(10),
e2 = Math.sqrt(2);
function linearRange(domain, count) {
if (!domain.length) domain = [0];
if (count == null) count = 10;
var start = domain[0],
stop = domain[domain.length - 1];
if (stop < start) { error = stop; stop = start; start = error; }
var span = (stop - start) || (count = 1, start || stop || 1),
step = Math.pow(10, Math.floor(Math.log(span / count) / Math.LN10)),
error = span / count / step;
// Filter ticks to get closer to the desired count.
if (error >= e10) step *= 10;
else if (error >= e5) step *= 5;
else if (error >= e2) step *= 2;
// Round start and stop values to step interval.
return [
Math.ceil(start / step) * step,
Math.floor(stop / step) * step + step / 2, // inclusive
step
];
}
function trimZero(f, decimal) {
return function(x) {
var s = f(x),
n = s.indexOf(decimal);
if (n < 0) return s;
var idx = rightmostDigit(s, n),
end = idx < s.length ? s.slice(idx) : '';
while (--idx > n) {
if (s[idx] !== '0') { ++idx; break; }
}
return s.slice(0, idx) + end;
};
}
function rightmostDigit(s, n) {
var i = s.lastIndexOf('e'), c;
if (i > 0) return i;
for (i=s.length; --i > n;) {
c = s.charCodeAt(i);
if (c >= 48 && c <= 57) return i+1; // is digit
}
}
function autoNumberFormat(f) {
var decimal = numberF.format('.1f')(1)[1]; // get decimal char
if (f == null) f = ',';
f = d3_numberF.formatSpecifier(f);
if (f.precision == null) f.precision = 12;
switch (f.type) {
case '%': f.precision -= 2; break;
case 'e': f.precision -= 1; break;
}
return trimZero(numberF.format(f), decimal);
}
function linearNumberFormat(domain, count, f) {
var range = linearRange(domain, count);
if (f == null) f = ',f';
switch (f = d3_numberF.formatSpecifier(f), f.type) {
case 's': {
var value = Math.max(Math.abs(range[0]), Math.abs(range[1]));
if (f.precision == null) f.precision = d3_numberF.precisionPrefix(range[2], value);
return numberF.formatPrefix(f, value);
}
case '':
case 'e':
case 'g':
case 'p':
case 'r': {
if (f.precision == null) f.precision = d3_numberF.precisionRound(range[2], Math.max(Math.abs(range[0]), Math.abs(range[1]))) - (f.type === 'e');
break;
}
case 'f':
case '%': {
if (f.precision == null) f.precision = d3_numberF.precisionFixed(range[2]) - 2 * (f.type === '%');
break;
}
}
return numberF.format(f);
}
// -- Datetime Formatting ----
function timeAutoFormat() {
var f = timeF.format,
formatMillisecond = f('.%L'),
formatSecond = f(':%S'),
formatMinute = f('%I:%M'),
formatHour = f('%I %p'),
formatDay = f('%a %d'),
formatWeek = f('%b %d'),
formatMonth = f('%B'),
formatYear = f('%Y');
return function(date) {
var d = +date;
return (d3_time.second(date) < d ? formatMillisecond
: d3_time.minute(date) < d ? formatSecond
: d3_time.hour(date) < d ? formatMinute
: d3_time.day(date) < d ? formatHour
: d3_time.month(date) < d ?
(d3_time.week(date) < d ? formatDay : formatWeek)
: d3_time.year(date) < d ? formatMonth
: formatYear)(date);
};
}
function utcAutoFormat() {
var f = timeF.utcFormat,
formatMillisecond = f('.%L'),
formatSecond = f(':%S'),
formatMinute = f('%I:%M'),
formatHour = f('%I %p'),
formatDay = f('%a %d'),
formatWeek = f('%b %d'),
formatMonth = f('%B'),
formatYear = f('%Y');
return function(date) {
var d = +date;
return (d3_time.utcSecond(date) < d ? formatMillisecond
: d3_time.utcMinute(date) < d ? formatSecond
: d3_time.utcHour(date) < d ? formatMinute
: d3_time.utcDay(date) < d ? formatHour
: d3_time.utcMonth(date) < d ?
(d3_time.utcWeek(date) < d ? formatDay : formatWeek)
: d3_time.utcYear(date) < d ? formatMonth
: formatYear)(date);
};
}
function monthFormat(month, abbreviate) {
var f = abbreviate ?
(monthAbbr || (monthAbbr = timeF.format('%b'))) :
(monthFull || (monthFull = timeF.format('%B')));
return (tmpDate.setMonth(month), f(tmpDate));
}
function dayFormat(day, abbreviate) {
var f = abbreviate ?
(dayAbbr || (dayAbbr = timeF.format('%a'))) :
(dayFull || (dayFull = timeF.format('%A')));
return (tmpDate.setMonth(0), tmpDate.setDate(2 + day), f(tmpDate));
}
function quarterFormat(date) {
return Math.floor(date.getMonth() / 3) + 1;
}
function utcQuarterFormat(date) {
return Math.floor(date.getUTCMonth() / 3) + 1;
}