@amcharts/amcharts4
Version:
amCharts 4
592 lines • 22.2 kB
JavaScript
/**
* Duration formatting functionality.
*/
import { __extends } from "tslib";
import { Language } from "../utils/Language";
import { BaseObject } from "../Base";
import { getTextFormatter } from "../formatters/TextFormatter";
import { registry } from "../Registry";
import * as $strings from "../utils/Strings";
import * as $object from "../utils/Object";
import * as $utils from "../utils/Utils";
import * as $type from "../utils/Type";
import * as $math from "../utils/Math";
/**
* DurationFormatter class. Formats numbers as durations.
*
* `1000` as `16:40`
*
* @see {@link https://www.amcharts.com/docs/v4/concepts/formatters/formatting-duration/} Tutorial on duration formatting
*/
var DurationFormatter = /** @class */ (function (_super) {
__extends(DurationFormatter, _super);
/**
* Constructor
*/
function DurationFormatter() {
var _this = _super.call(this) || this;
/**
* A base value for negative numbers. Will treat all numbers below this value
* as negative numbers.
*/
_this._negativeBase = 0;
/**
* A base unit to consider values are in.
*
* @default "s"
*/
_this._baseUnit = "second";
/**
* Output format to produce. If the format calls for applying color to the
* formatted value, this setting will determine what markup to use: SVG or
* HTML.
*
* Available options: svg, html.
*
* @default "svg"
*/
_this._outputFormat = "svg";
/**
* How many milliseconds each unit represents.
*/
_this._unitValues = {
"millisecond": 1,
"second": 1000,
"minute": 60000,
"hour": 3600000,
"day": 86400000,
"week": 604800000,
"month": 2592000000,
"year": 31536000000,
};
/**
* Collection of aliases for units.
*/
_this._unitAliases = {
"Y": "y",
"D": "d",
"H": "h",
"K": "h",
"k": "h",
"n": "S"
};
_this.className = "DurationFormatter";
_this.applyTheme();
return _this;
}
/**
* Formats the number according as duration.
*
* For example `1000` (base unit seconds) would be converted to `16:40` as in
* 16 minutes and 40 seconds.
*
* @see {@link https://www.amcharts.com/docs/v4/concepts/formatters/formatting-duration/} Tutorial on duration formatting
* @param value Value to format
* @param format Format to apply
* @param base Override base unit
* @return Formatted number
*/
DurationFormatter.prototype.format = function (value, format, base) {
// no language?
if (!this.language) {
if (this.sprite) {
this.language = this.sprite.language;
}
else {
this.language = new Language;
}
}
// no base unit?
var baseUnit = base || this._baseUnit;
// no format passed in or empty
if (typeof format === "undefined" || format === "") {
if ($type.hasValue(this.durationFormat)) {
format = this.durationFormat;
}
else {
format = this.getFormat($type.toNumber(value), null, baseUnit);
}
}
// Clean format
format = $utils.cleanFormat(format);
// get format info (it will also deal with parser caching)
var info = this.parseFormat(format, baseUnit);
// cast to number just in case
// TODO: maybe use better casting
var source = Number(value);
// format and replace the number
var details;
if (source > this._negativeBase) {
details = info.positive;
}
else if (source < this._negativeBase) {
details = info.negative;
}
else {
details = info.zero;
}
// Format
var formatted = this.applyFormat(source, details);
// Apply color?
if (details.color !== "") {
if (this._outputFormat === "svg") {
formatted = "<tspan fill='" + details.color + "'>" + formatted + "</tspan>";
}
else if (this._outputFormat === "html") {
formatted = "<span style='color: " + details.color + ";'>" + formatted + "</span>";
}
}
return formatted;
};
/**
* Parses supplied format into structured object which can be used to format
* the number.
*
* @param format Format string, i.e. "#,###.00"
* @param base Override base unit
* @return Parsed information
*/
DurationFormatter.prototype.parseFormat = function (format, base) {
var _this = this;
// Check cache
var cached = this.getCache(format);
if ($type.hasValue(cached)) {
return cached;
}
// no base unit?
var baseUnit = base || this._baseUnit;
// Initialize duration parsing info
var info = {
"positive": {
"color": "",
"template": "",
"parts": [],
"source": "",
"baseUnit": baseUnit,
"parsed": false,
"absolute": false
},
"negative": {
"color": "",
"template": "",
"parts": [],
"source": "",
"baseUnit": baseUnit,
"parsed": false,
"absolute": false
},
"zero": {
"color": "",
"template": "",
"parts": [],
"source": "",
"baseUnit": baseUnit,
"parsed": false,
"absolute": false
}
};
// Escape double vertical bars (that mean display one vertical bar)
format = format.replace("||", $strings.PLACEHOLDER2);
// Split it up and deal with different formats
var parts = format.split("|");
info.positive.source = parts[0];
if (typeof parts[2] === "undefined") {
info.zero = info.positive;
}
else {
info.zero.source = parts[2];
}
if (typeof parts[1] === "undefined") {
info.negative = info.positive;
}
else {
info.negative.source = parts[1];
}
// Parse each
$object.each(info, function (part, item) {
// Already parsed
if (item.parsed) {
return;
}
// Check cached
if (typeof _this.getCache(item.source) !== "undefined") {
info[part] = _this.getCache(item.source);
return;
}
// Begin parsing
var partFormat = item.source;
// Check for [] directives
var dirs = [];
dirs = item.source.match(/^\[([^\]]*)\]/);
if (dirs && dirs.length && dirs[0] !== "") {
partFormat = item.source.substr(dirs[0].length);
item.color = dirs[1];
}
// Let TextFormatter split into chunks
var chunks = getTextFormatter().chunk(partFormat, true);
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
// replace back double vertical bar
chunk.text = chunk.text.replace($strings.PLACEHOLDER2, "|");
if (chunk.type === "value") {
// Just "Duration"?
// if (chunk.text.toLowerCase() === "duration") {
// chunk.text = durationFormat;
// }
// Check for "a" (absolute) modifier
if (chunk.text.match(/[yYMdDwhHKkmsSn]+a/)) {
item.absolute = true;
chunk.text = chunk.text.replace(/([yYMdDwhHKkmsSn]+)a/, "$1");
}
// Find all possible parts
var matches = chunk.text.match(/y+|Y+|M+|d+|D+|w+|h+|H+|K+|k+|m+|s+|S+|n+/g);
if (matches) {
// Populate template
for (var x = 0; x < matches.length; x++) {
// Is it an alias?
if (!$type.hasValue(matches[x])) {
matches[x] = _this._unitAliases[matches[x]];
}
item.parts.push(matches[x]);
chunk.text = chunk.text.replace(matches[x], $strings.PLACEHOLDER);
}
}
}
// Apply to template
item.template += chunk.text;
}
// Apply style formatting
//item.template = getTextFormatter().format(item.template, this.outputFormat);
// Save cache
_this.setCache(item.source, item);
// Mark this as parsed
item.parsed = true;
});
// Save cache (the whole thing)
this.setCache(format, info);
return info;
};
/**
* Applies parsed format to a numeric value.
*
* @param value Value
* @param details Parsed format as returned by {parseFormat}
* @return Formatted duration
*/
DurationFormatter.prototype.applyFormat = function (value, details) {
// Use absolute values
var negative = !details.absolute && (value < this._negativeBase);
value = Math.abs(value);
// Recalculate to milliseconds
var tstamp = this.toTimeStamp(value, details.baseUnit);
// Init return value
var res = details.template;
// Iterate through duration parts
for (var i = 0, len = details.parts.length; i < len; i++) {
// Gather the part
var part = details.parts[i];
var unit = this.toTimeUnit(part.substr(0, 1));
var digits = part.length;
// Calculate current unit value
var ints = Math.floor(tstamp / this._unitValues[unit]);
res = res.replace($strings.PLACEHOLDER, $utils.padString(ints, digits, "0"));
// Reduce timestamp
tstamp -= ints * this._unitValues[unit];
}
// Reapply negative sign
if (negative) {
res = "-" + res;
}
return res;
};
/**
* Converts numeric value to timestamp in milliseconds.
*
* @param value A source value
* @param baseUnit Base unit the source value is in: "q", "s", "i", "h", "d", "w", "m", "y"
* @return Value representation as a timestamp in milliseconds
*/
DurationFormatter.prototype.toTimeStamp = function (value, baseUnit) {
return value * this._unitValues[baseUnit];
};
DurationFormatter.prototype.toTimeUnit = function (code) {
switch (code) {
case "S":
return "millisecond";
case "s":
return "second";
case "m":
return "minute";
case "h":
return "hour";
case "d":
return "day";
case "w":
return "week";
case "M":
return "month";
case "y":
return "year";
}
;
};
/**
* Invalidates the parent [[Sprite]] object.
*/
DurationFormatter.prototype.invalidateSprite = function () {
if (this.sprite) {
this.sprite.invalidate();
}
};
Object.defineProperty(DurationFormatter.prototype, "baseUnit", {
/**
* @return Base unit
*/
get: function () {
return this._baseUnit;
},
/**
* Base unit the values are in.
*
* A base unit will be used to recalculate the numeric value to millisecond
* timestamps.
*
* Available options:
*
* * "millisecond"
* * "second"
* * "minute"
* * "hour"
* * "day"
* * "week"
* * "month"
* * "year"
*
* @default "s"
* @param baseUnit A base unit
*/
set: function (baseUnit) {
this._baseUnit = baseUnit;
this.invalidateSprite();
},
enumerable: true,
configurable: true
});
Object.defineProperty(DurationFormatter.prototype, "outputFormat", {
/**
* Getter for output format.
*
* @ignore Exclude from docs
* @return Output format
*/
get: function () {
return this._outputFormat;
},
/**
* Setter for output format: "svg" or "html.
*
* @ignore Exclude from docs
* @param value Output format
*/
set: function (outputFormat) {
this._outputFormat = outputFormat.toLowerCase();
this.invalidateSprite();
},
enumerable: true,
configurable: true
});
/**
* Returns appropriate default format for the value.
*
* If `maxValue` is sepcified, it will use that value to determine the time
* unit for the format.
*
* For example if your `baseUnit` is `"second"` and you pass in `10`, you
* will get `"10"`.
*
* However, you might want it to be formatted in the context of bigger scale,
* say 10 minutes (600 seconds). If you pass in `600` as `maxValue`, all
* values, including small ones will use format with minutes, e.g.:
* `00:10`, `00:50`, `12: 30`, etc.
*
* @param value Value to format
* @param maxValue Maximum value to be used to determine format
* @param baseUnit Base unit of the value
* @return Format
*/
DurationFormatter.prototype.getFormat = function (value, maxValue, baseUnit) {
// Is format override set?
if ($type.hasValue(this.durationFormat)) {
return this.durationFormat;
}
// Get base unit
if (!baseUnit) {
baseUnit = this.baseUnit;
}
if ($type.hasValue(maxValue) && value != maxValue) {
value = Math.abs(value);
maxValue = Math.abs(maxValue);
var maxUnit = this.getValueUnit($math.max(value, maxValue), baseUnit);
//let diffUnit = this.getValueUnit(Math.abs(maxValue - value), baseUnit);
//console.log(maxUnit, diffUnit);
return this.durationFormats[baseUnit][maxUnit];
}
else {
var unit = this.getValueUnit(value, baseUnit);
return this.durationFormats[baseUnit][unit];
}
};
/**
* Returns value's closest denominator time unit, e.g 100 seconds is
* `"minute"`, while 59 seconds would still be `second`.
*
* @param value Source duration value
* @param baseUnit Base unit
* @return Denominator
*/
DurationFormatter.prototype.getValueUnit = function (value, baseUnit) {
// Get base unit
if (!baseUnit) {
baseUnit = this.baseUnit;
}
// Convert to milliseconds
var currentUnit;
var ms = this.getMilliseconds(value, baseUnit);
$object.eachContinue(this._unitValues, function (key, val) {
if (key == baseUnit || currentUnit) {
var num = ms / val;
if (num <= 1) {
if (!currentUnit) {
currentUnit = key;
}
return false;
}
currentUnit = key;
}
return true;
});
return currentUnit;
};
/**
* Converts value to milliseconds according to `baseUnit`.
*
* @param value Source duration value
* @param baseUnit Base unit
* @return Value in milliseconds
*/
DurationFormatter.prototype.getMilliseconds = function (value, baseUnit) {
// Get base unit
if (!baseUnit) {
baseUnit = this.baseUnit;
}
return value * this._unitValues[baseUnit];
};
Object.defineProperty(DurationFormatter.prototype, "durationFormat", {
/**
* @return Format
*/
get: function () {
return this._durationFormat;
},
/**
* If set, this format will be used instead of the one determined dynamically
* based on the basedUnit and range of values.
*
* @see {@link https://www.amcharts.com/docs/v4/concepts/formatters/formatting-duration/} Available fomatting codes
* @param value Format
*/
set: function (value) {
if (this._durationFormat != value) {
this._durationFormat = value;
this.invalidateSprite();
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(DurationFormatter.prototype, "durationFormats", {
/**
* @return Formats
*/
get: function () {
if (!this._durationFormats) {
this._durationFormats = {
"millisecond": {
"millisecond": this.language.translate("_duration_millisecond"),
"second": this.language.translate("_duration_millisecond_second"),
"minute": this.language.translate("_duration_millisecond_minute"),
"hour": this.language.translate("_duration_millisecond_hour"),
"day": this.language.translate("_duration_millisecond_day"),
"week": this.language.translate("_duration_millisecond_week"),
"month": this.language.translate("_duration_millisecond_month"),
"year": this.language.translate("_duration_millisecond_year")
},
"second": {
"second": this.language.translate("_duration_second"),
"minute": this.language.translate("_duration_second_minute"),
"hour": this.language.translate("_duration_second_hour"),
"day": this.language.translate("_duration_second_day"),
"week": this.language.translate("_duration_second_week"),
"month": this.language.translate("_duration_second_month"),
"year": this.language.translate("_duration_second_year")
},
"minute": {
"minute": this.language.translate("_duration_minute"),
"hour": this.language.translate("_duration_minute_hour"),
"day": this.language.translate("_duration_minute_day"),
"week": this.language.translate("_duration_minute_week"),
"month": this.language.translate("_duration_minute_month"),
"year": this.language.translate("_duration_minute_year")
},
"hour": {
"hour": this.language.translate("_duration_hour"),
"day": this.language.translate("_duration_hour_day"),
"week": this.language.translate("_duration_hour_week"),
"month": this.language.translate("_duration_hour_month"),
"year": this.language.translate("_duration_hour_year")
},
"day": {
"day": this.language.translate("_duration_day"),
"week": this.language.translate("_duration_day_week"),
"month": this.language.translate("_duration_day_month"),
"year": this.language.translate("_duration_day_year")
},
"week": {
"week": this.language.translate("_duration_week"),
"month": this.language.translate("_duration_week_month"),
"year": this.language.translate("_duration_week_year")
},
"month": {
"month": this.language.translate("_duration_month"),
"year": this.language.translate("_duration_month_year")
},
"year": {
"year": this.language.translate("_duration_year")
}
};
}
return this._durationFormats;
},
/**
* Duration formats for various combination of base units.
*
* @param value Formats
*/
set: function (value) {
this._durationFormats = value;
this.invalidateSprite();
},
enumerable: true,
configurable: true
});
return DurationFormatter;
}(BaseObject));
export { DurationFormatter };
/**
* Register class in system, so that it can be instantiated using its name from
* anywhere.
*
* @ignore
*/
registry.registeredClasses["DurationFormatter"] = DurationFormatter;
//# sourceMappingURL=DurationFormatter.js.map