mirror of
https://github.com/nasa/openmct.git
synced 2024-12-27 00:31:06 +00:00
c932e953bc
WTD-1239
483 lines
12 KiB
JavaScript
483 lines
12 KiB
JavaScript
/*! Moment Duration Format v1.3.0
|
|
* https://github.com/jsmreese/moment-duration-format
|
|
* Date: 2014-07-15
|
|
*
|
|
* Duration format plugin function for the Moment.js library
|
|
* http://momentjs.com/
|
|
*
|
|
* Copyright 2014 John Madhavan-Reese
|
|
* Released under the MIT license
|
|
*/
|
|
|
|
(function (root, undefined) {
|
|
|
|
// repeatZero(qty)
|
|
// returns "0" repeated qty times
|
|
function repeatZero(qty) {
|
|
var result = "";
|
|
|
|
// exit early
|
|
// if qty is 0 or a negative number
|
|
// or doesn't coerce to an integer
|
|
qty = parseInt(qty, 10);
|
|
if (!qty || qty < 1) { return result; }
|
|
|
|
while (qty) {
|
|
result += "0";
|
|
qty -= 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// padZero(str, len [, isRight])
|
|
// pads a string with zeros up to a specified length
|
|
// will not pad a string if its length is aready
|
|
// greater than or equal to the specified length
|
|
// default output pads with zeros on the left
|
|
// set isRight to `true` to pad with zeros on the right
|
|
function padZero(str, len, isRight) {
|
|
if (str == null) { str = ""; }
|
|
str = "" + str;
|
|
|
|
return (isRight ? str : "") + repeatZero(len - str.length) + (isRight ? "" : str);
|
|
}
|
|
|
|
// isArray
|
|
function isArray(array) {
|
|
return Object.prototype.toString.call(array) === "[object Array]";
|
|
}
|
|
|
|
// isObject
|
|
function isObject(obj) {
|
|
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
}
|
|
|
|
// findLast
|
|
function findLast(array, callback) {
|
|
var index = array.length;
|
|
|
|
while (index -= 1) {
|
|
if (callback(array[index])) { return array[index]; }
|
|
}
|
|
}
|
|
|
|
// find
|
|
function find(array, callback) {
|
|
var index = 0,
|
|
max = array.length,
|
|
match;
|
|
|
|
if (typeof callback !== "function") {
|
|
match = callback;
|
|
callback = function (item) {
|
|
return item === match;
|
|
};
|
|
}
|
|
|
|
while (index < max) {
|
|
if (callback(array[index])) { return array[index]; }
|
|
index += 1;
|
|
}
|
|
}
|
|
|
|
// each
|
|
function each(array, callback) {
|
|
var index = 0,
|
|
max = array.length;
|
|
|
|
if (!array || !max) { return; }
|
|
|
|
while (index < max) {
|
|
if (callback(array[index], index) === false) { return; }
|
|
index += 1;
|
|
}
|
|
}
|
|
|
|
// map
|
|
function map(array, callback) {
|
|
var index = 0,
|
|
max = array.length,
|
|
ret = [];
|
|
|
|
if (!array || !max) { return ret; }
|
|
|
|
while (index < max) {
|
|
ret[index] = callback(array[index], index);
|
|
index += 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// pluck
|
|
function pluck(array, prop) {
|
|
return map(array, function (item) {
|
|
return item[prop];
|
|
});
|
|
}
|
|
|
|
// compact
|
|
function compact(array) {
|
|
var ret = [];
|
|
|
|
each(array, function (item) {
|
|
if (item) { ret.push(item); }
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
// unique
|
|
function unique(array) {
|
|
var ret = [];
|
|
|
|
each(array, function (_a) {
|
|
if (!find(ret, _a)) { ret.push(_a); }
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
// intersection
|
|
function intersection(a, b) {
|
|
var ret = [];
|
|
|
|
each(a, function (_a) {
|
|
each(b, function (_b) {
|
|
if (_a === _b) { ret.push(_a); }
|
|
});
|
|
});
|
|
|
|
return unique(ret);
|
|
}
|
|
|
|
// rest
|
|
function rest(array, callback) {
|
|
var ret = [];
|
|
|
|
each(array, function (item, index) {
|
|
if (!callback(item)) {
|
|
ret = array.slice(index);
|
|
return false;
|
|
}
|
|
});
|
|
|
|
return ret;
|
|
}
|
|
|
|
// initial
|
|
function initial(array, callback) {
|
|
var reversed = array.slice().reverse();
|
|
|
|
return rest(reversed, callback).reverse();
|
|
}
|
|
|
|
// extend
|
|
function extend(a, b) {
|
|
for (var key in b) {
|
|
if (b.hasOwnProperty(key)) { a[key] = b[key]; }
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
// define internal moment reference
|
|
var moment;
|
|
|
|
if (typeof require === "function") {
|
|
try { moment = require('moment'); }
|
|
catch (e) {}
|
|
}
|
|
|
|
if (!moment && root.moment) {
|
|
moment = root.moment;
|
|
}
|
|
|
|
if (!moment) {
|
|
throw "Moment Duration Format cannot find Moment.js";
|
|
}
|
|
|
|
// moment.duration.format([template] [, precision] [, settings])
|
|
moment.duration.fn.format = function () {
|
|
|
|
var tokenizer, tokens, types, typeMap, momentTypes, foundFirst, trimIndex,
|
|
args = [].slice.call(arguments),
|
|
settings = extend({}, this.format.defaults),
|
|
// keep a shadow copy of this moment for calculating remainders
|
|
remainder = moment.duration(this);
|
|
|
|
// add a reference to this duration object to the settings for use
|
|
// in a template function
|
|
settings.duration = this;
|
|
|
|
// parse arguments
|
|
each(args, function (arg) {
|
|
if (typeof arg === "string" || typeof arg === "function") {
|
|
settings.template = arg;
|
|
return;
|
|
}
|
|
|
|
if (typeof arg === "number") {
|
|
settings.precision = arg;
|
|
return;
|
|
}
|
|
|
|
if (isObject(arg)) {
|
|
extend(settings, arg);
|
|
}
|
|
});
|
|
|
|
// types
|
|
types = settings.types = (isArray(settings.types) ? settings.types : settings.types.split(" "));
|
|
|
|
// template
|
|
if (typeof settings.template === "function") {
|
|
settings.template = settings.template.apply(settings);
|
|
}
|
|
|
|
// tokenizer regexp
|
|
tokenizer = new RegExp(map(types, function (type) {
|
|
return settings[type].source;
|
|
}).join("|"), "g");
|
|
|
|
// token type map function
|
|
typeMap = function (token) {
|
|
return find(types, function (type) {
|
|
return settings[type].test(token);
|
|
});
|
|
};
|
|
|
|
// tokens array
|
|
tokens = map(settings.template.match(tokenizer), function (token, index) {
|
|
var type = typeMap(token),
|
|
length = token.length;
|
|
|
|
return {
|
|
index: index,
|
|
length: length,
|
|
|
|
// replace escaped tokens with the non-escaped token text
|
|
token: (type === "escape" ? token.replace(settings.escape, "$1") : token),
|
|
|
|
// ignore type on non-moment tokens
|
|
type: ((type === "escape" || type === "general") ? null : type)
|
|
|
|
// calculate base value for all moment tokens
|
|
//baseValue: ((type === "escape" || type === "general") ? null : this.as(type))
|
|
};
|
|
}, this);
|
|
|
|
// unique moment token types in the template (in order of descending magnitude)
|
|
momentTypes = intersection(types, unique(compact(pluck(tokens, "type"))));
|
|
|
|
// exit early if there are no momentTypes
|
|
if (!momentTypes.length) {
|
|
return pluck(tokens, "token").join("");
|
|
}
|
|
|
|
// calculate values for each token type in the template
|
|
each(momentTypes, function (momentType, index) {
|
|
var value, wholeValue, decimalValue, isLeast, isMost;
|
|
|
|
// calculate integer and decimal value portions
|
|
value = remainder.as(momentType);
|
|
wholeValue = (value > 0 ? Math.floor(value) : Math.ceil(value));
|
|
decimalValue = value - wholeValue;
|
|
|
|
// is this the least-significant moment token found?
|
|
isLeast = ((index + 1) === momentTypes.length);
|
|
|
|
// is this the most-significant moment token found?
|
|
isMost = (!index);
|
|
|
|
// update tokens array
|
|
// using this algorithm to not assume anything about
|
|
// the order or frequency of any tokens
|
|
each(tokens, function (token) {
|
|
if (token.type === momentType) {
|
|
extend(token, {
|
|
value: value,
|
|
wholeValue: wholeValue,
|
|
decimalValue: decimalValue,
|
|
isLeast: isLeast,
|
|
isMost: isMost
|
|
});
|
|
|
|
if (isMost) {
|
|
// note the length of the most-significant moment token:
|
|
// if it is greater than one and forceLength is not set, default forceLength to `true`
|
|
if (settings.forceLength == null && token.length > 1) {
|
|
settings.forceLength = true;
|
|
}
|
|
|
|
// rationale is this:
|
|
// if the template is "h:mm:ss" and the moment value is 5 minutes, the user-friendly output is "5:00", not "05:00"
|
|
// shouldn't pad the `minutes` token even though it has length of two
|
|
// if the template is "hh:mm:ss", the user clearly wanted everything padded so we should output "05:00"
|
|
// if the user wanted the full padded output, they can set `{ trim: false }` to get "00:05:00"
|
|
}
|
|
}
|
|
});
|
|
|
|
// update remainder
|
|
remainder.subtract(wholeValue, momentType);
|
|
});
|
|
|
|
// trim tokens array
|
|
if (settings.trim) {
|
|
tokens = (settings.trim === "left" ? rest : initial)(tokens, function (token) {
|
|
// return `true` if:
|
|
// the token is not the least moment token (don't trim the least moment token)
|
|
// the token is a moment token that does not have a value (don't trim moment tokens that have a whole value)
|
|
return !(token.isLeast || (token.type != null && token.wholeValue));
|
|
});
|
|
}
|
|
|
|
|
|
// build output
|
|
|
|
// the first moment token can have special handling
|
|
foundFirst = false;
|
|
|
|
// run the map in reverse order if trimming from the right
|
|
if (settings.trim === "right") {
|
|
tokens.reverse();
|
|
}
|
|
|
|
tokens = map(tokens, function (token) {
|
|
var val,
|
|
decVal;
|
|
|
|
if (!token.type) {
|
|
// if it is not a moment token, use the token as its own value
|
|
return token.token;
|
|
}
|
|
|
|
// apply negative precision formatting to the least-significant moment token
|
|
if (token.isLeast && (settings.precision < 0)) {
|
|
val = (Math.floor(token.wholeValue * Math.pow(10, settings.precision)) * Math.pow(10, -settings.precision)).toString();
|
|
} else {
|
|
val = token.wholeValue.toString();
|
|
}
|
|
|
|
// remove negative sign from the beginning
|
|
val = val.replace(/^\-/, "");
|
|
|
|
// apply token length formatting
|
|
// special handling for the first moment token that is not the most significant in a trimmed template
|
|
if (token.length > 1 && (foundFirst || token.isMost || settings.forceLength)) {
|
|
val = padZero(val, token.length);
|
|
}
|
|
|
|
// add decimal value if precision > 0
|
|
if (token.isLeast && (settings.precision > 0)) {
|
|
decVal = token.decimalValue.toString().replace(/^\-/, "").split(/\.|e\-/);
|
|
switch (decVal.length) {
|
|
case 1:
|
|
val += "." + padZero(decVal[0], settings.precision, true).slice(0, settings.precision);
|
|
break;
|
|
|
|
case 2:
|
|
val += "." + padZero(decVal[1], settings.precision, true).slice(0, settings.precision);
|
|
break;
|
|
|
|
case 3:
|
|
val += "." + padZero(repeatZero((+decVal[2]) - 1) + (decVal[0] || "0") + decVal[1], settings.precision, true).slice(0, settings.precision);
|
|
break;
|
|
|
|
default:
|
|
throw "Moment Duration Format: unable to parse token decimal value.";
|
|
}
|
|
}
|
|
|
|
// add a negative sign if the value is negative and token is most significant
|
|
if (token.isMost && token.value < 0) {
|
|
val = "-" + val;
|
|
}
|
|
|
|
foundFirst = true;
|
|
|
|
return val;
|
|
});
|
|
|
|
// undo the reverse if trimming from the right
|
|
if (settings.trim === "right") {
|
|
tokens.reverse();
|
|
}
|
|
|
|
return tokens.join("");
|
|
};
|
|
|
|
moment.duration.fn.format.defaults = {
|
|
// token definitions
|
|
escape: /\[(.+?)\]/,
|
|
years: /[Yy]+/,
|
|
months: /M+/,
|
|
weeks: /[Ww]+/,
|
|
days: /[Dd]+/,
|
|
hours: /[Hh]+/,
|
|
minutes: /m+/,
|
|
seconds: /s+/,
|
|
milliseconds: /S+/,
|
|
general: /.+?/,
|
|
|
|
// token type names
|
|
// in order of descending magnitude
|
|
// can be a space-separated token name list or an array of token names
|
|
types: "escape years months weeks days hours minutes seconds milliseconds general",
|
|
|
|
// format options
|
|
|
|
// trim
|
|
// "left" - template tokens are trimmed from the left until the first moment token that has a value >= 1
|
|
// "right" - template tokens are trimmed from the right until the first moment token that has a value >= 1
|
|
// (the final moment token is not trimmed, regardless of value)
|
|
// `false` - template tokens are not trimmed
|
|
trim: "left",
|
|
|
|
// precision
|
|
// number of decimal digits to include after (to the right of) the decimal point (positive integer)
|
|
// or the number of digits to truncate to 0 before (to the left of) the decimal point (negative integer)
|
|
precision: 0,
|
|
|
|
// force first moment token with a value to render at full length even when template is trimmed and first moment token has length of 1
|
|
forceLength: null,
|
|
|
|
// template used to format duration
|
|
// may be a function or a string
|
|
// template functions are executed with the `this` binding of the settings object
|
|
// so that template strings may be dynamically generated based on the duration object
|
|
// (accessible via `this.duration`)
|
|
// or any of the other settings
|
|
template: function () {
|
|
var types = this.types,
|
|
dur = this.duration,
|
|
lastType = findLast(types, function (type) {
|
|
return dur._data[type];
|
|
});
|
|
|
|
// default template strings for each duration dimension type
|
|
switch (lastType) {
|
|
case "seconds":
|
|
return "h:mm:ss";
|
|
case "minutes":
|
|
return "d[d] h:mm";
|
|
case "hours":
|
|
return "d[d] h[h]";
|
|
case "days":
|
|
return "M[m] d[d]";
|
|
case "weeks":
|
|
return "y[y] w[w]";
|
|
case "months":
|
|
return "y[y] M[m]";
|
|
case "years":
|
|
return "y[y]";
|
|
default:
|
|
return "y[y] M[m] d[d] h:mm:ss";
|
|
}
|
|
}
|
|
};
|
|
|
|
})(this);
|