You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3930 lines
123 KiB
3930 lines
123 KiB
/** |
|
* @fileoverview |
|
* |
|
* JsWorld |
|
* |
|
* <p>Javascript library for localised formatting and parsing of: |
|
* <ul> |
|
* <li>Numbers |
|
* <li>Dates and times |
|
* <li>Currency |
|
* </ul> |
|
* |
|
* <p>The library classes are configured with standard POSIX locale definitions |
|
* derived from Unicode's Common Locale Data Repository (CLDR). |
|
* |
|
* <p>Website: <a href="http://software.dzhuvinov.com/jsworld.html">JsWorld</a> |
|
* |
|
* @author Vladimir Dzhuvinov |
|
* @version 2.5 (2011-12-23) |
|
*/ |
|
|
|
|
|
|
|
/** |
|
* @namespace Namespace container for the JsWorld library objects. |
|
*/ |
|
jsworld = {}; |
|
|
|
|
|
/** |
|
* @function |
|
* |
|
* @description Formats a JavaScript Date object as an ISO-8601 date/time |
|
* string. |
|
* |
|
* @param {Date} [d] A valid JavaScript Date object. If undefined the |
|
* current date/time will be used. |
|
* @param {Boolean} [withTZ] Include timezone offset, default false. |
|
* |
|
* @returns {String} The date/time formatted as YYYY-MM-DD HH:MM:SS. |
|
*/ |
|
jsworld.formatIsoDateTime = function(d, withTZ) { |
|
|
|
if (typeof d === "undefined") |
|
d = new Date(); // now |
|
|
|
if (typeof withTZ === "undefined") |
|
withTZ = false; |
|
|
|
var s = jsworld.formatIsoDate(d) + " " + jsworld.formatIsoTime(d); |
|
|
|
if (withTZ) { |
|
|
|
var diff = d.getHours() - d.getUTCHours(); |
|
var hourDiff = Math.abs(diff); |
|
|
|
var minuteUTC = d.getUTCMinutes(); |
|
var minute = d.getMinutes(); |
|
|
|
if (minute != minuteUTC && minuteUTC < 30 && diff < 0) |
|
hourDiff--; |
|
|
|
if (minute != minuteUTC && minuteUTC > 30 && diff > 0) |
|
hourDiff--; |
|
|
|
var minuteDiff; |
|
if (minute != minuteUTC) |
|
minuteDiff = ":30"; |
|
else |
|
minuteDiff = ":00"; |
|
|
|
var timezone; |
|
if (hourDiff < 10) |
|
timezone = "0" + hourDiff + minuteDiff; |
|
|
|
else |
|
timezone = "" + hourDiff + minuteDiff; |
|
|
|
if (diff < 0) |
|
timezone = "-" + timezone; |
|
|
|
else |
|
timezone = "+" + timezone; |
|
|
|
s = s + timezone; |
|
} |
|
|
|
return s; |
|
}; |
|
|
|
|
|
/** |
|
* @function |
|
* |
|
* @description Formats a JavaScript Date object as an ISO-8601 date string. |
|
* |
|
* @param {Date} [d] A valid JavaScript Date object. If undefined the current |
|
* date will be used. |
|
* |
|
* @returns {String} The date formatted as YYYY-MM-DD. |
|
*/ |
|
jsworld.formatIsoDate = function(d) { |
|
|
|
if (typeof d === "undefined") |
|
d = new Date(); // now |
|
|
|
var year = d.getFullYear(); |
|
var month = d.getMonth() + 1; |
|
var day = d.getDate(); |
|
|
|
return year + "-" + jsworld._zeroPad(month, 2) + "-" + jsworld._zeroPad(day, 2); |
|
}; |
|
|
|
|
|
/** |
|
* @function |
|
* |
|
* @description Formats a JavaScript Date object as an ISO-8601 time string. |
|
* |
|
* @param {Date} [d] A valid JavaScript Date object. If undefined the current |
|
* time will be used. |
|
* |
|
* @returns {String} The time formatted as HH:MM:SS. |
|
*/ |
|
jsworld.formatIsoTime = function(d) { |
|
|
|
if (typeof d === "undefined") |
|
d = new Date(); // now |
|
|
|
var hour = d.getHours(); |
|
var minute = d.getMinutes(); |
|
var second = d.getSeconds(); |
|
|
|
return jsworld._zeroPad(hour, 2) + ":" + jsworld._zeroPad(minute, 2) + ":" + jsworld._zeroPad(second, 2); |
|
}; |
|
|
|
|
|
/** |
|
* @function |
|
* |
|
* @description Parses an ISO-8601 formatted date/time string to a JavaScript |
|
* Date object. |
|
* |
|
* @param {String} isoDateTimeVal An ISO-8601 formatted date/time string. |
|
* |
|
* <p>Accepted formats: |
|
* |
|
* <ul> |
|
* <li>YYYY-MM-DD HH:MM:SS |
|
* <li>YYYYMMDD HHMMSS |
|
* <li>YYYY-MM-DD HHMMSS |
|
* <li>YYYYMMDD HH:MM:SS |
|
* </ul> |
|
* |
|
* @returns {Date} The corresponding Date object. |
|
* |
|
* @throws Error on a badly formatted date/time string or on a invalid date. |
|
*/ |
|
jsworld.parseIsoDateTime = function(isoDateTimeVal) { |
|
|
|
if (typeof isoDateTimeVal != "string") |
|
throw "Error: The parameter must be a string"; |
|
|
|
// First, try to match "YYYY-MM-DD HH:MM:SS" format |
|
var matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); |
|
|
|
// If unsuccessful, try to match "YYYYMMDD HHMMSS" format |
|
if (matches === null) |
|
matches = isoDateTimeVal.match(/^(\d\d\d\d)(\d\d)(\d\d)[T ](\d\d)(\d\d)(\d\d)/); |
|
|
|
// ... try to match "YYYY-MM-DD HHMMSS" format |
|
if (matches === null) |
|
matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d)(\d\d)(\d\d)/); |
|
|
|
// ... try to match "YYYYMMDD HH:MM:SS" format |
|
if (matches === null) |
|
matches = isoDateTimeVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)/); |
|
|
|
// Report bad date/time string |
|
if (matches === null) |
|
throw "Error: Invalid ISO-8601 date/time string"; |
|
|
|
// Force base 10 parse int as some values may have leading zeros! |
|
// (to avoid implicit octal base conversion) |
|
var year = parseInt(matches[1], 10); |
|
var month = parseInt(matches[2], 10); |
|
var day = parseInt(matches[3], 10); |
|
|
|
var hour = parseInt(matches[4], 10); |
|
var mins = parseInt(matches[5], 10); |
|
var secs = parseInt(matches[6], 10); |
|
|
|
// Simple value range check, leap years not checked |
|
// Note: the originial ISO time spec for leap hours (24:00:00) and seconds (00:00:60) is not supported |
|
if (month < 1 || month > 12 || |
|
day < 1 || day > 31 || |
|
hour < 0 || hour > 23 || |
|
mins < 0 || mins > 59 || |
|
secs < 0 || secs > 59 ) |
|
|
|
throw "Error: Invalid ISO-8601 date/time value"; |
|
|
|
var d = new Date(year, month - 1, day, hour, mins, secs); |
|
|
|
// Check if the input date was valid |
|
// (JS Date does automatic forward correction) |
|
if (d.getDate() != day || d.getMonth() +1 != month) |
|
throw "Error: Invalid date"; |
|
|
|
return d; |
|
}; |
|
|
|
|
|
/** |
|
* @function |
|
* |
|
* @description Parses an ISO-8601 formatted date string to a JavaScript |
|
* Date object. |
|
* |
|
* @param {String} isoDateVal An ISO-8601 formatted date string. |
|
* |
|
* <p>Accepted formats: |
|
* |
|
* <ul> |
|
* <li>YYYY-MM-DD |
|
* <li>YYYYMMDD |
|
* </ul> |
|
* |
|
* @returns {Date} The corresponding Date object. |
|
* |
|
* @throws Error on a badly formatted date string or on a invalid date. |
|
*/ |
|
jsworld.parseIsoDate = function(isoDateVal) { |
|
|
|
if (typeof isoDateVal != "string") |
|
throw "Error: The parameter must be a string"; |
|
|
|
// First, try to match "YYYY-MM-DD" format |
|
var matches = isoDateVal.match(/^(\d\d\d\d)-(\d\d)-(\d\d)/); |
|
|
|
// If unsuccessful, try to match "YYYYMMDD" format |
|
if (matches === null) |
|
matches = isoDateVal.match(/^(\d\d\d\d)(\d\d)(\d\d)/); |
|
|
|
// Report bad date/time string |
|
if (matches === null) |
|
throw "Error: Invalid ISO-8601 date string"; |
|
|
|
// Force base 10 parse int as some values may have leading zeros! |
|
// (to avoid implicit octal base conversion) |
|
var year = parseInt(matches[1], 10); |
|
var month = parseInt(matches[2], 10); |
|
var day = parseInt(matches[3], 10); |
|
|
|
// Simple value range check, leap years not checked |
|
if (month < 1 || month > 12 || |
|
day < 1 || day > 31 ) |
|
|
|
throw "Error: Invalid ISO-8601 date value"; |
|
|
|
var d = new Date(year, month - 1, day); |
|
|
|
// Check if the input date was valid |
|
// (JS Date does automatic forward correction) |
|
if (d.getDate() != day || d.getMonth() +1 != month) |
|
throw "Error: Invalid date"; |
|
|
|
return d; |
|
}; |
|
|
|
|
|
/** |
|
* @function |
|
* |
|
* @description Parses an ISO-8601 formatted time string to a JavaScript |
|
* Date object. |
|
* |
|
* @param {String} isoTimeVal An ISO-8601 formatted time string. |
|
* |
|
* <p>Accepted formats: |
|
* |
|
* <ul> |
|
* <li>HH:MM:SS |
|
* <li>HHMMSS |
|
* </ul> |
|
* |
|
* @returns {Date} The corresponding Date object, with year, month and day set |
|
* to zero. |
|
* |
|
* @throws Error on a badly formatted time string. |
|
*/ |
|
jsworld.parseIsoTime = function(isoTimeVal) { |
|
|
|
if (typeof isoTimeVal != "string") |
|
throw "Error: The parameter must be a string"; |
|
|
|
// First, try to match "HH:MM:SS" format |
|
var matches = isoTimeVal.match(/^(\d\d):(\d\d):(\d\d)/); |
|
|
|
// If unsuccessful, try to match "HHMMSS" format |
|
if (matches === null) |
|
matches = isoTimeVal.match(/^(\d\d)(\d\d)(\d\d)/); |
|
|
|
// Report bad date/time string |
|
if (matches === null) |
|
throw "Error: Invalid ISO-8601 date/time string"; |
|
|
|
// Force base 10 parse int as some values may have leading zeros! |
|
// (to avoid implicit octal base conversion) |
|
var hour = parseInt(matches[1], 10); |
|
var mins = parseInt(matches[2], 10); |
|
var secs = parseInt(matches[3], 10); |
|
|
|
// Simple value range check, leap years not checked |
|
if (hour < 0 || hour > 23 || |
|
mins < 0 || mins > 59 || |
|
secs < 0 || secs > 59 ) |
|
|
|
throw "Error: Invalid ISO-8601 time value"; |
|
|
|
return new Date(0, 0, 0, hour, mins, secs); |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Trims leading and trailing whitespace from a string. |
|
* |
|
* <p>Used non-regexp the method from http://blog.stevenlevithan.com/archives/faster-trim-javascript |
|
* |
|
* @param {String} str The string to trim. |
|
* |
|
* @returns {String} The trimmed string. |
|
*/ |
|
jsworld._trim = function(str) { |
|
|
|
var whitespace = ' \n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'; |
|
|
|
for (var i = 0; i < str.length; i++) { |
|
|
|
if (whitespace.indexOf(str.charAt(i)) === -1) { |
|
str = str.substring(i); |
|
break; |
|
} |
|
} |
|
|
|
for (i = str.length - 1; i >= 0; i--) { |
|
if (whitespace.indexOf(str.charAt(i)) === -1) { |
|
str = str.substring(0, i + 1); |
|
break; |
|
} |
|
} |
|
|
|
return whitespace.indexOf(str.charAt(0)) === -1 ? str : ''; |
|
}; |
|
|
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Returns true if the argument represents a decimal number. |
|
* |
|
* @param {Number|String} arg The argument to test. |
|
* |
|
* @returns {Boolean} true if the argument represents a decimal number, |
|
* otherwise false. |
|
*/ |
|
jsworld._isNumber = function(arg) { |
|
|
|
if (typeof arg == "number") |
|
return true; |
|
|
|
if (typeof arg != "string") |
|
return false; |
|
|
|
// ensure string |
|
var s = arg + ""; |
|
|
|
return (/^-?(\d+|\d*\.\d+)$/).test(s); |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Returns true if the argument represents a decimal integer. |
|
* |
|
* @param {Number|String} arg The argument to test. |
|
* |
|
* @returns {Boolean} true if the argument represents an integer, otherwise |
|
* false. |
|
*/ |
|
jsworld._isInteger = function(arg) { |
|
|
|
if (typeof arg != "number" && typeof arg != "string") |
|
return false; |
|
|
|
// convert to string |
|
var s = arg + ""; |
|
|
|
return (/^-?\d+$/).test(s); |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Returns true if the argument represents a decimal float. |
|
* |
|
* @param {Number|String} arg The argument to test. |
|
* |
|
* @returns {Boolean} true if the argument represents a float, otherwise false. |
|
*/ |
|
jsworld._isFloat = function(arg) { |
|
|
|
if (typeof arg != "number" && typeof arg != "string") |
|
return false; |
|
|
|
// convert to string |
|
var s = arg + ""; |
|
|
|
return (/^-?\.\d+?$/).test(s); |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Checks if the specified formatting option is contained |
|
* within the options string. |
|
* |
|
* @param {String} option The option to search for. |
|
* @param {String} optionsString The options string. |
|
* |
|
* @returns {Boolean} true if the flag is found, else false |
|
*/ |
|
jsworld._hasOption = function(option, optionsString) { |
|
|
|
if (typeof option != "string" || typeof optionsString != "string") |
|
return false; |
|
|
|
if (optionsString.indexOf(option) != -1) |
|
return true; |
|
else |
|
return false; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description String replacement function. |
|
* |
|
* @param {String} s The string to work on. |
|
* @param {String} target The string to search for. |
|
* @param {String} replacement The replacement. |
|
* |
|
* @returns {String} The new string. |
|
*/ |
|
jsworld._stringReplaceAll = function(s, target, replacement) { |
|
|
|
var out; |
|
|
|
if (target.length == 1 && replacement.length == 1) { |
|
// simple char/char case somewhat faster |
|
out = ""; |
|
|
|
for (var i = 0; i < s.length; i++) { |
|
|
|
if (s.charAt(i) == target.charAt(0)) |
|
out = out + replacement.charAt(0); |
|
else |
|
out = out + s.charAt(i); |
|
} |
|
|
|
return out; |
|
} |
|
else { |
|
// longer target and replacement strings |
|
out = s; |
|
|
|
var index = out.indexOf(target); |
|
|
|
while (index != -1) { |
|
|
|
out = out.replace(target, replacement); |
|
|
|
index = out.indexOf(target); |
|
} |
|
|
|
return out; |
|
} |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Tests if a string starts with the specified substring. |
|
* |
|
* @param {String} testedString The string to test. |
|
* @param {String} sub The string to match. |
|
* |
|
* @returns {Boolean} true if the test succeeds. |
|
*/ |
|
jsworld._stringStartsWith = function (testedString, sub) { |
|
|
|
if (testedString.length < sub.length) |
|
return false; |
|
|
|
for (var i = 0; i < sub.length; i++) { |
|
if (testedString.charAt(i) != sub.charAt(i)) |
|
return false; |
|
} |
|
|
|
return true; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Gets the requested precision from an options string. |
|
* |
|
* <p>Example: ".3" returns 3 decimal places precision. |
|
* |
|
* @param {String} optionsString The options string. |
|
* |
|
* @returns {integer Number} The requested precision, -1 if not specified. |
|
*/ |
|
jsworld._getPrecision = function (optionsString) { |
|
|
|
if (typeof optionsString != "string") |
|
return -1; |
|
|
|
var m = optionsString.match(/\.(\d)/); |
|
if (m) |
|
return parseInt(m[1], 10); |
|
else |
|
return -1; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Takes a decimal numeric amount (optionally as string) and |
|
* returns its integer and fractional parts packed into an object. |
|
* |
|
* @param {Number|String} amount The amount, e.g. "123.45" or "-56.78" |
|
* |
|
* @returns {object} Parsed amount object with properties: |
|
* {String} integer : the integer part |
|
* {String} fraction : the fraction part |
|
*/ |
|
jsworld._splitNumber = function (amount) { |
|
|
|
if (typeof amount == "number") |
|
amount = amount + ""; |
|
|
|
var obj = {}; |
|
|
|
// remove negative sign |
|
if (amount.charAt(0) == "-") |
|
amount = amount.substring(1); |
|
|
|
// split amount into integer and decimal parts |
|
var amountParts = amount.split("."); |
|
if (!amountParts[1]) |
|
amountParts[1] = ""; // we need "" instead of null |
|
|
|
obj.integer = amountParts[0]; |
|
obj.fraction = amountParts[1]; |
|
|
|
return obj; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Formats the integer part using the specified grouping |
|
* and thousands separator. |
|
* |
|
* @param {String} intPart The integer part of the amount, as string. |
|
* @param {String} grouping The grouping definition. |
|
* @param {String} thousandsSep The thousands separator. |
|
* |
|
* @returns {String} The formatted integer part. |
|
*/ |
|
jsworld._formatIntegerPart = function (intPart, grouping, thousandsSep) { |
|
|
|
// empty separator string? no grouping? |
|
// -> return immediately with no formatting! |
|
if (thousandsSep == "" || grouping == "-1") |
|
return intPart; |
|
|
|
// turn the semicolon-separated string of integers into an array |
|
var groupSizes = grouping.split(";"); |
|
|
|
// the formatted output string |
|
var out = ""; |
|
|
|
// the intPart string position to process next, |
|
// start at string end, e.g. "10000000<starts here" |
|
var pos = intPart.length; |
|
|
|
// process the intPart string backwards |
|
// "1000000000" |
|
// <---\ direction |
|
var size; |
|
|
|
while (pos > 0) { |
|
|
|
// get next group size (if any, otherwise keep last) |
|
if (groupSizes.length > 0) |
|
size = parseInt(groupSizes.shift(), 10); |
|
|
|
// int parse error? |
|
if (isNaN(size)) |
|
throw "Error: Invalid grouping"; |
|
|
|
// size is -1? -> no more grouping, so just copy string remainder |
|
if (size == -1) { |
|
out = intPart.substring(0, pos) + out; |
|
break; |
|
} |
|
|
|
pos -= size; // move to next sep. char. position |
|
|
|
// position underrun? -> just copy string remainder |
|
if (pos < 1) { |
|
out = intPart.substring(0, pos + size) + out; |
|
break; |
|
} |
|
|
|
// extract group and apply sep. char. |
|
out = thousandsSep + intPart.substring(pos, pos + size) + out; |
|
} |
|
|
|
return out; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Formats the fractional part to the specified decimal |
|
* precision. |
|
* |
|
* @param {String} fracPart The fractional part of the amount |
|
* @param {integer Number} precision The desired decimal precision |
|
* |
|
* @returns {String} The formatted fractional part. |
|
*/ |
|
jsworld._formatFractionPart = function (fracPart, precision) { |
|
|
|
// append zeroes up to precision if necessary |
|
for (var i=0; fracPart.length < precision; i++) |
|
fracPart = fracPart + "0"; |
|
|
|
return fracPart; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @desription Converts a number to string and pad it with leading zeroes if the |
|
* string is shorter than length. |
|
* |
|
* @param {integer Number} number The number value subjected to selective padding. |
|
* @param {integer Number} length If the number has fewer digits than this length |
|
* apply padding. |
|
* |
|
* @returns {String} The formatted string. |
|
*/ |
|
jsworld._zeroPad = function(number, length) { |
|
|
|
// ensure string |
|
var s = number + ""; |
|
|
|
while (s.length < length) |
|
s = "0" + s; |
|
|
|
return s; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* @description Converts a number to string and pads it with leading spaces if |
|
* the string is shorter than length. |
|
* |
|
* @param {integer Number} number The number value subjected to selective padding. |
|
* @param {integer Number} length If the number has fewer digits than this length |
|
* apply padding. |
|
* |
|
* @returns {String} The formatted string. |
|
*/ |
|
jsworld._spacePad = function(number, length) { |
|
|
|
// ensure string |
|
var s = number + ""; |
|
|
|
while (s.length < length) |
|
s = " " + s; |
|
|
|
return s; |
|
}; |
|
|
|
|
|
|
|
/** |
|
* @class |
|
* Represents a POSIX-style locale with its numeric, monetary and date/time |
|
* properties. Also provides a set of locale helper methods. |
|
* |
|
* <p>The locale properties follow the POSIX standards: |
|
* |
|
* <ul> |
|
* <li><a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_04">POSIX LC_NUMERIC</a> |
|
* <li><a href="http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_03_03">POSIX LC_MONETARY</a> |
|
* <li><a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_05">POSIX LC_TIME</a> |
|
* </ul> |
|
* |
|
* @public |
|
* @constructor |
|
* @description Creates a new locale object (POSIX-style) with the specified |
|
* properties. |
|
* |
|
* @param {object} properties An object containing the raw locale properties: |
|
* |
|
* @param {String} properties.decimal_point |
|
* |
|
* A string containing the symbol that shall be used as the decimal |
|
* delimiter (radix character) in numeric, non-monetary formatted |
|
* quantities. This property cannot be omitted and cannot be set to the |
|
* empty string. |
|
* |
|
* |
|
* @param {String} properties.thousands_sep |
|
* |
|
* A string containing the symbol that shall be used as a separator for |
|
* groups of digits to the left of the decimal delimiter in numeric, |
|
* non-monetary formatted monetary quantities. |
|
* |
|
* |
|
* @param {String} properties.grouping |
|
* |
|
* Defines the size of each group of digits in formatted non-monetary |
|
* quantities. The operand is a sequence of integers separated by |
|
* semicolons. Each integer specifies the number of digits in each group, |
|
* with the initial integer defining the size of the group immediately |
|
* preceding the decimal delimiter, and the following integers defining |
|
* the preceding groups. If the last integer is not -1, then the size of |
|
* the previous group (if any) shall be repeatedly used for the |
|
* remainder of the digits. If the last integer is -1, then no further |
|
* grouping shall be performed. |
|
* |
|
* |
|
* @param {String} properties.int_curr_symbol |
|
* |
|
* The first three letters signify the ISO-4217 currency code, |
|
* the fourth letter is the international symbol separation character |
|
* (normally a space). |
|
* |
|
* |
|
* @param {String} properties.currency_symbol |
|
* |
|
* The local shorthand currency symbol, e.g. "$" for the en_US locale |
|
* |
|
* |
|
* @param {String} properties.mon_decimal_point |
|
* |
|
* The symbol to be used as the decimal delimiter (radix character) |
|
* |
|
* |
|
* @param {String} properties.mon_thousands_sep |
|
* |
|
* The symbol to be used as a separator for groups of digits to the |
|
* left of the decimal delimiter. |
|
* |
|
* |
|
* @param {String} properties.mon_grouping |
|
* |
|
* A string that defines the size of each group of digits. The |
|
* operand is a sequence of integers separated by semicolons (";"). |
|
* Each integer specifies the number of digits in each group, with the |
|
* initial integer defining the size of the group preceding the |
|
* decimal delimiter, and the following integers defining the |
|
* preceding groups. If the last integer is not -1, then the size of |
|
* the previous group (if any) must be repeatedly used for the |
|
* remainder of the digits. If the last integer is -1, then no |
|
* further grouping is to be performed. |
|
* |
|
* |
|
* @param {String} properties.positive_sign |
|
* |
|
* The string to indicate a non-negative monetary amount. |
|
* |
|
* |
|
* @param {String} properties.negative_sign |
|
* |
|
* The string to indicate a negative monetary amount. |
|
* |
|
* |
|
* @param {integer Number} properties.frac_digits |
|
* |
|
* An integer representing the number of fractional digits (those to |
|
* the right of the decimal delimiter) to be written in a formatted |
|
* monetary quantity using currency_symbol. |
|
* |
|
* |
|
* @param {integer Number} properties.int_frac_digits |
|
* |
|
* An integer representing the number of fractional digits (those to |
|
* the right of the decimal delimiter) to be written in a formatted |
|
* monetary quantity using int_curr_symbol. |
|
* |
|
* |
|
* @param {integer Number} properties.p_cs_precedes |
|
* |
|
* An integer set to 1 if the currency_symbol precedes the value for a |
|
* monetary quantity with a non-negative value, and set to 0 if the |
|
* symbol succeeds the value. |
|
* |
|
* |
|
* @param {integer Number} properties.n_cs_precedes |
|
* |
|
* An integer set to 1 if the currency_symbol precedes the value for a |
|
* monetary quantity with a negative value, and set to 0 if the symbol |
|
* succeeds the value. |
|
* |
|
* |
|
* @param {integer Number} properties.p_sep_by_space |
|
* |
|
* Set to a value indicating the separation of the currency_symbol, |
|
* the sign string, and the value for a non-negative formatted monetary |
|
* quantity: |
|
* |
|
* <p>0 No space separates the currency symbol and value.</p> |
|
* |
|
* <p>1 If the currency symbol and sign string are adjacent, a space |
|
* separates them from the value; otherwise, a space separates |
|
* the currency symbol from the value.</p> |
|
* |
|
* <p>2 If the currency symbol and sign string are adjacent, a space |
|
* separates them; otherwise, a space separates the sign string |
|
* from the value.</p> |
|
* |
|
* |
|
* @param {integer Number} properties.n_sep_by_space |
|
* |
|
* Set to a value indicating the separation of the currency_symbol, |
|
* the sign string, and the value for a negative formatted monetary |
|
* quantity. Rules same as for p_sep_by_space. |
|
* |
|
* |
|
* @param {integer Number} properties.p_sign_posn |
|
* |
|
* An integer set to a value indicating the positioning of the |
|
* positive_sign for a monetary quantity with a non-negative value: |
|
* |
|
* <p>0 Parentheses enclose the quantity and the currency_symbol.</p> |
|
* |
|
* <p>1 The sign string precedes the quantity and the currency_symbol.</p> |
|
* |
|
* <p>2 The sign string succeeds the quantity and the currency_symbol.</p> |
|
* |
|
* <p>3 The sign string precedes the currency_symbol.</p> |
|
* |
|
* <p>4 The sign string succeeds the currency_symbol.</p> |
|
* |
|
* |
|
* @param {integer Number} properties.n_sign_posn |
|
* |
|
* An integer set to a value indicating the positioning of the |
|
* negative_sign for a negative formatted monetary quantity. Rules same |
|
* as for p_sign_posn. |
|
* |
|
* |
|
* @param {integer Number} properties.int_p_cs_precedes |
|
* |
|
* An integer set to 1 if the int_curr_symbol precedes the value for a |
|
* monetary quantity with a non-negative value, and set to 0 if the |
|
* symbol succeeds the value. |
|
* |
|
* |
|
* @param {integer Number} properties.int_n_cs_precedes |
|
* |
|
* An integer set to 1 if the int_curr_symbol precedes the value for a |
|
* monetary quantity with a negative value, and set to 0 if the symbol |
|
* succeeds the value. |
|
* |
|
* |
|
* @param {integer Number} properties.int_p_sep_by_space |
|
* |
|
* Set to a value indicating the separation of the int_curr_symbol, |
|
* the sign string, and the value for a non-negative internationally |
|
* formatted monetary quantity. Rules same as for p_sep_by_space. |
|
* |
|
* |
|
* @param {integer Number} properties.int_n_sep_by_space |
|
* |
|
* Set to a value indicating the separation of the int_curr_symbol, |
|
* the sign string, and the value for a negative internationally |
|
* formatted monetary quantity. Rules same as for p_sep_by_space. |
|
* |
|
* |
|
* @param {integer Number} properties.int_p_sign_posn |
|
* |
|
* An integer set to a value indicating the positioning of the |
|
* positive_sign for a positive monetary quantity formatted with the |
|
* international format. Rules same as for p_sign_posn. |
|
* |
|
* |
|
* @param {integer Number} properties.int_n_sign_posn |
|
* |
|
* An integer set to a value indicating the positioning of the |
|
* negative_sign for a negative monetary quantity formatted with the |
|
* international format. Rules same as for p_sign_posn. |
|
* |
|
* |
|
* @param {String[] | String} properties.abday |
|
* |
|
* The abbreviated weekday names, corresponding to the %a conversion |
|
* specification. The property must be either an array of 7 strings or |
|
* a string consisting of 7 semicolon-separated substrings, each |
|
* surrounded by double-quotes. The first must be the abbreviated name |
|
* of the day corresponding to Sunday, the second the abbreviated name |
|
* of the day corresponding to Monday, and so on. |
|
* |
|
* |
|
* @param {String[] | String} properties.day |
|
* |
|
* The full weekday names, corresponding to the %A conversion |
|
* specification. The property must be either an array of 7 strings or |
|
* a string consisting of 7 semicolon-separated substrings, each |
|
* surrounded by double-quotes. The first must be the full name of the |
|
* day corresponding to Sunday, the second the full name of the day |
|
* corresponding to Monday, and so on. |
|
* |
|
* |
|
* @param {String[] | String} properties.abmon |
|
* |
|
* The abbreviated month names, corresponding to the %b conversion |
|
* specification. The property must be either an array of 12 strings or |
|
* a string consisting of 12 semicolon-separated substrings, each |
|
* surrounded by double-quotes. The first must be the abbreviated name |
|
* of the first month of the year (January), the second the abbreviated |
|
* name of the second month, and so on. |
|
* |
|
* |
|
* @param {String[] | String} properties.mon |
|
* |
|
* The full month names, corresponding to the %B conversion |
|
* specification. The property must be either an array of 12 strings or |
|
* a string consisting of 12 semicolon-separated substrings, each |
|
* surrounded by double-quotes. The first must be the full name of the |
|
* first month of the year (January), the second the full name of the second |
|
* month, and so on. |
|
* |
|
* |
|
* @param {String} properties.d_fmt |
|
* |
|
* The appropriate date representation. The string may contain any |
|
* combination of characters and conversion specifications (%<char>). |
|
* |
|
* |
|
* @param {String} properties.t_fmt |
|
* |
|
* The appropriate time representation. The string may contain any |
|
* combination of characters and conversion specifications (%<char>). |
|
* |
|
* |
|
* @param {String} properties.d_t_fmt |
|
* |
|
* The appropriate date and time representation. The string may contain |
|
* any combination of characters and conversion specifications (%<char>). |
|
* |
|
* |
|
* @param {String[] | String} properties.am_pm |
|
* |
|
* The appropriate representation of the ante-meridiem and post-meridiem |
|
* strings, corresponding to the %p conversion specification. The property |
|
* must be either an array of 2 strings or a string consisting of 2 |
|
* semicolon-separated substrings, each surrounded by double-quotes. |
|
* The first string must represent the ante-meridiem designation, the |
|
* last string the post-meridiem designation. |
|
* |
|
* |
|
* @throws @throws Error on a undefined or invalid locale property. |
|
*/ |
|
jsworld.Locale = function(properties) { |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Identifies the class for internal library purposes. |
|
*/ |
|
this._className = "jsworld.Locale"; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Parses a day or month name definition list, which |
|
* could be a ready JS array, e.g. ["Mon", "Tue", "Wed"...] or |
|
* it could be a string formatted according to the classic POSIX |
|
* definition e.g. "Mon";"Tue";"Wed";... |
|
* |
|
* @param {String[] | String} namesAn array or string defining |
|
* the week/month names. |
|
* @param {integer Number} expectedItems The number of expected list |
|
* items, e.g. 7 for weekdays, 12 for months. |
|
* |
|
* @returns {String[]} The parsed (and checked) items. |
|
* |
|
* @throws Error on missing definition, unexpected item count or |
|
* missing double-quotes. |
|
*/ |
|
this._parseList = function(names, expectedItems) { |
|
|
|
var array = []; |
|
|
|
if (names == null) { |
|
throw "Names not defined"; |
|
} |
|
else if (typeof names == "object") { |
|
// we got a ready array |
|
array = names; |
|
} |
|
else if (typeof names == "string") { |
|
// we got the names in the classic POSIX form, do parse |
|
array = names.split(";", expectedItems); |
|
|
|
for (var i = 0; i < array.length; i++) { |
|
// check for and strip double quotes |
|
if (array[i][0] == "\"" && array[i][array[i].length - 1] == "\"") |
|
array[i] = array[i].slice(1, -1); |
|
else |
|
throw "Missing double quotes"; |
|
} |
|
} |
|
else { |
|
throw "Names must be an array or a string"; |
|
} |
|
|
|
if (array.length != expectedItems) |
|
throw "Expected " + expectedItems + " items, got " + array.length; |
|
|
|
return array; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Validates a date/time format string, such as "H:%M:%S". |
|
* Checks that the argument is of type "string" and is not empty. |
|
* |
|
* @param {String} formatString The format string. |
|
* |
|
* @returns {String} The validated string. |
|
* |
|
* @throws Error on null or empty string. |
|
*/ |
|
this._validateFormatString = function(formatString) { |
|
|
|
if (typeof formatString == "string" && formatString.length > 0) |
|
return formatString; |
|
else |
|
throw "Empty or no string"; |
|
}; |
|
|
|
|
|
// LC_NUMERIC |
|
|
|
if (properties == null || typeof properties != "object") |
|
throw "Error: Invalid/missing locale properties"; |
|
|
|
|
|
if (typeof properties.decimal_point != "string") |
|
throw "Error: Invalid/missing decimal_point property"; |
|
|
|
this.decimal_point = properties.decimal_point; |
|
|
|
|
|
if (typeof properties.thousands_sep != "string") |
|
throw "Error: Invalid/missing thousands_sep property"; |
|
|
|
this.thousands_sep = properties.thousands_sep; |
|
|
|
|
|
if (typeof properties.grouping != "string") |
|
throw "Error: Invalid/missing grouping property"; |
|
|
|
this.grouping = properties.grouping; |
|
|
|
|
|
// LC_MONETARY |
|
|
|
if (typeof properties.int_curr_symbol != "string") |
|
throw "Error: Invalid/missing int_curr_symbol property"; |
|
|
|
if (! /[A-Za-z]{3}.?/.test(properties.int_curr_symbol)) |
|
throw "Error: Invalid int_curr_symbol property"; |
|
|
|
this.int_curr_symbol = properties.int_curr_symbol; |
|
|
|
|
|
if (typeof properties.currency_symbol != "string") |
|
throw "Error: Invalid/missing currency_symbol property"; |
|
|
|
this.currency_symbol = properties.currency_symbol; |
|
|
|
|
|
if (typeof properties.frac_digits != "number" && properties.frac_digits < 0) |
|
throw "Error: Invalid/missing frac_digits property"; |
|
|
|
this.frac_digits = properties.frac_digits; |
|
|
|
|
|
// may be empty string/null for currencies with no fractional part |
|
if (properties.mon_decimal_point === null || properties.mon_decimal_point == "") { |
|
|
|
if (this.frac_digits > 0) |
|
throw "Error: Undefined mon_decimal_point property"; |
|
else |
|
properties.mon_decimal_point = ""; |
|
} |
|
|
|
if (typeof properties.mon_decimal_point != "string") |
|
throw "Error: Invalid/missing mon_decimal_point property"; |
|
|
|
this.mon_decimal_point = properties.mon_decimal_point; |
|
|
|
|
|
if (typeof properties.mon_thousands_sep != "string") |
|
throw "Error: Invalid/missing mon_thousands_sep property"; |
|
|
|
this.mon_thousands_sep = properties.mon_thousands_sep; |
|
|
|
|
|
if (typeof properties.mon_grouping != "string") |
|
throw "Error: Invalid/missing mon_grouping property"; |
|
|
|
this.mon_grouping = properties.mon_grouping; |
|
|
|
|
|
if (typeof properties.positive_sign != "string") |
|
throw "Error: Invalid/missing positive_sign property"; |
|
|
|
this.positive_sign = properties.positive_sign; |
|
|
|
|
|
if (typeof properties.negative_sign != "string") |
|
throw "Error: Invalid/missing negative_sign property"; |
|
|
|
this.negative_sign = properties.negative_sign; |
|
|
|
|
|
|
|
if (properties.p_cs_precedes !== 0 && properties.p_cs_precedes !== 1) |
|
throw "Error: Invalid/missing p_cs_precedes property, must be 0 or 1"; |
|
|
|
this.p_cs_precedes = properties.p_cs_precedes; |
|
|
|
|
|
if (properties.n_cs_precedes !== 0 && properties.n_cs_precedes !== 1) |
|
throw "Error: Invalid/missing n_cs_precedes, must be 0 or 1"; |
|
|
|
this.n_cs_precedes = properties.n_cs_precedes; |
|
|
|
|
|
if (properties.p_sep_by_space !== 0 && |
|
properties.p_sep_by_space !== 1 && |
|
properties.p_sep_by_space !== 2) |
|
throw "Error: Invalid/missing p_sep_by_space property, must be 0, 1 or 2"; |
|
|
|
this.p_sep_by_space = properties.p_sep_by_space; |
|
|
|
|
|
if (properties.n_sep_by_space !== 0 && |
|
properties.n_sep_by_space !== 1 && |
|
properties.n_sep_by_space !== 2) |
|
throw "Error: Invalid/missing n_sep_by_space property, must be 0, 1, or 2"; |
|
|
|
this.n_sep_by_space = properties.n_sep_by_space; |
|
|
|
|
|
if (properties.p_sign_posn !== 0 && |
|
properties.p_sign_posn !== 1 && |
|
properties.p_sign_posn !== 2 && |
|
properties.p_sign_posn !== 3 && |
|
properties.p_sign_posn !== 4) |
|
throw "Error: Invalid/missing p_sign_posn property, must be 0, 1, 2, 3 or 4"; |
|
|
|
this.p_sign_posn = properties.p_sign_posn; |
|
|
|
|
|
if (properties.n_sign_posn !== 0 && |
|
properties.n_sign_posn !== 1 && |
|
properties.n_sign_posn !== 2 && |
|
properties.n_sign_posn !== 3 && |
|
properties.n_sign_posn !== 4) |
|
throw "Error: Invalid/missing n_sign_posn property, must be 0, 1, 2, 3 or 4"; |
|
|
|
this.n_sign_posn = properties.n_sign_posn; |
|
|
|
|
|
if (typeof properties.int_frac_digits != "number" && properties.int_frac_digits < 0) |
|
throw "Error: Invalid/missing int_frac_digits property"; |
|
|
|
this.int_frac_digits = properties.int_frac_digits; |
|
|
|
|
|
if (properties.int_p_cs_precedes !== 0 && properties.int_p_cs_precedes !== 1) |
|
throw "Error: Invalid/missing int_p_cs_precedes property, must be 0 or 1"; |
|
|
|
this.int_p_cs_precedes = properties.int_p_cs_precedes; |
|
|
|
|
|
if (properties.int_n_cs_precedes !== 0 && properties.int_n_cs_precedes !== 1) |
|
throw "Error: Invalid/missing int_n_cs_precedes property, must be 0 or 1"; |
|
|
|
this.int_n_cs_precedes = properties.int_n_cs_precedes; |
|
|
|
|
|
if (properties.int_p_sep_by_space !== 0 && |
|
properties.int_p_sep_by_space !== 1 && |
|
properties.int_p_sep_by_space !== 2) |
|
throw "Error: Invalid/missing int_p_sep_by_spacev, must be 0, 1 or 2"; |
|
|
|
this.int_p_sep_by_space = properties.int_p_sep_by_space; |
|
|
|
|
|
if (properties.int_n_sep_by_space !== 0 && |
|
properties.int_n_sep_by_space !== 1 && |
|
properties.int_n_sep_by_space !== 2) |
|
throw "Error: Invalid/missing int_n_sep_by_space property, must be 0, 1, or 2"; |
|
|
|
this.int_n_sep_by_space = properties.int_n_sep_by_space; |
|
|
|
|
|
if (properties.int_p_sign_posn !== 0 && |
|
properties.int_p_sign_posn !== 1 && |
|
properties.int_p_sign_posn !== 2 && |
|
properties.int_p_sign_posn !== 3 && |
|
properties.int_p_sign_posn !== 4) |
|
throw "Error: Invalid/missing int_p_sign_posn property, must be 0, 1, 2, 3 or 4"; |
|
|
|
this.int_p_sign_posn = properties.int_p_sign_posn; |
|
|
|
|
|
if (properties.int_n_sign_posn !== 0 && |
|
properties.int_n_sign_posn !== 1 && |
|
properties.int_n_sign_posn !== 2 && |
|
properties.int_n_sign_posn !== 3 && |
|
properties.int_n_sign_posn !== 4) |
|
throw "Error: Invalid/missing int_n_sign_posn property, must be 0, 1, 2, 3 or 4"; |
|
|
|
this.int_n_sign_posn = properties.int_n_sign_posn; |
|
|
|
|
|
// LC_TIME |
|
|
|
if (properties == null || typeof properties != "object") |
|
throw "Error: Invalid/missing time locale properties"; |
|
|
|
|
|
// parse the supported POSIX LC_TIME properties |
|
|
|
// abday |
|
try { |
|
this.abday = this._parseList(properties.abday, 7); |
|
} |
|
catch (error) { |
|
throw "Error: Invalid abday property: " + error; |
|
} |
|
|
|
// day |
|
try { |
|
this.day = this._parseList(properties.day, 7); |
|
} |
|
catch (error) { |
|
throw "Error: Invalid day property: " + error; |
|
} |
|
|
|
// abmon |
|
try { |
|
this.abmon = this._parseList(properties.abmon, 12); |
|
} catch (error) { |
|
throw "Error: Invalid abmon property: " + error; |
|
} |
|
|
|
// mon |
|
try { |
|
this.mon = this._parseList(properties.mon, 12); |
|
} catch (error) { |
|
throw "Error: Invalid mon property: " + error; |
|
} |
|
|
|
// d_fmt |
|
try { |
|
this.d_fmt = this._validateFormatString(properties.d_fmt); |
|
} catch (error) { |
|
throw "Error: Invalid d_fmt property: " + error; |
|
} |
|
|
|
// t_fmt |
|
try { |
|
this.t_fmt = this._validateFormatString(properties.t_fmt); |
|
} catch (error) { |
|
throw "Error: Invalid t_fmt property: " + error; |
|
} |
|
|
|
// d_t_fmt |
|
try { |
|
this.d_t_fmt = this._validateFormatString(properties.d_t_fmt); |
|
} catch (error) { |
|
throw "Error: Invalid d_t_fmt property: " + error; |
|
} |
|
|
|
// am_pm |
|
try { |
|
var am_pm_strings = this._parseList(properties.am_pm, 2); |
|
this.am = am_pm_strings[0]; |
|
this.pm = am_pm_strings[1]; |
|
} catch (error) { |
|
// ignore empty/null string errors |
|
this.am = ""; |
|
this.pm = ""; |
|
} |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Returns the abbreviated name of the specified weekday. |
|
* |
|
* @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero |
|
* corresponds to Sunday, one to Monday, etc. If omitted the |
|
* method will return an array of all abbreviated weekday |
|
* names. |
|
* |
|
* @returns {String | String[]} The abbreviated name of the specified weekday |
|
* or an array of all abbreviated weekday names. |
|
* |
|
* @throws Error on invalid argument. |
|
*/ |
|
this.getAbbreviatedWeekdayName = function(weekdayNum) { |
|
|
|
if (typeof weekdayNum == "undefined" || weekdayNum === null) |
|
return this.abday; |
|
|
|
if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) |
|
throw "Error: Invalid weekday argument, must be an integer [0..6]"; |
|
|
|
return this.abday[weekdayNum]; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Returns the name of the specified weekday. |
|
* |
|
* @param {integer Number} [weekdayNum] An integer between 0 and 6. Zero |
|
* corresponds to Sunday, one to Monday, etc. If omitted the |
|
* method will return an array of all weekday names. |
|
* |
|
* @returns {String | String[]} The name of the specified weekday or an |
|
* array of all weekday names. |
|
* |
|
* @throws Error on invalid argument. |
|
*/ |
|
this.getWeekdayName = function(weekdayNum) { |
|
|
|
if (typeof weekdayNum == "undefined" || weekdayNum === null) |
|
return this.day; |
|
|
|
if (! jsworld._isInteger(weekdayNum) || weekdayNum < 0 || weekdayNum > 6) |
|
throw "Error: Invalid weekday argument, must be an integer [0..6]"; |
|
|
|
return this.day[weekdayNum]; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Returns the abbreviated name of the specified month. |
|
* |
|
* @param {integer Number} [monthNum] An integer between 0 and 11. Zero |
|
* corresponds to January, one to February, etc. If omitted the |
|
* method will return an array of all abbreviated month names. |
|
* |
|
* @returns {String | String[]} The abbreviated name of the specified month |
|
* or an array of all abbreviated month names. |
|
* |
|
* @throws Error on invalid argument. |
|
*/ |
|
this.getAbbreviatedMonthName = function(monthNum) { |
|
|
|
if (typeof monthNum == "undefined" || monthNum === null) |
|
return this.abmon; |
|
|
|
if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) |
|
throw "Error: Invalid month argument, must be an integer [0..11]"; |
|
|
|
return this.abmon[monthNum]; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Returns the name of the specified month. |
|
* |
|
* @param {integer Number} [monthNum] An integer between 0 and 11. Zero |
|
* corresponds to January, one to February, etc. If omitted the |
|
* method will return an array of all month names. |
|
* |
|
* @returns {String | String[]} The name of the specified month or an array |
|
* of all month names. |
|
* |
|
* @throws Error on invalid argument. |
|
*/ |
|
this.getMonthName = function(monthNum) { |
|
|
|
if (typeof monthNum == "undefined" || monthNum === null) |
|
return this.mon; |
|
|
|
if (! jsworld._isInteger(monthNum) || monthNum < 0 || monthNum > 11) |
|
throw "Error: Invalid month argument, must be an integer [0..11]"; |
|
|
|
return this.mon[monthNum]; |
|
}; |
|
|
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the decimal delimiter (radix) character for |
|
* numeric quantities. |
|
* |
|
* @returns {String} The radix character. |
|
*/ |
|
this.getDecimalPoint = function() { |
|
|
|
return this.decimal_point; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the local shorthand currency symbol. |
|
* |
|
* @returns {String} The currency symbol. |
|
*/ |
|
this.getCurrencySymbol = function() { |
|
|
|
return this.currency_symbol; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the internaltion currency symbol (ISO-4217 code). |
|
* |
|
* @returns {String} The international currency symbol. |
|
*/ |
|
this.getIntCurrencySymbol = function() { |
|
|
|
return this.int_curr_symbol.substring(0,3); |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the position of the local (shorthand) currency |
|
* symbol relative to the amount. Assumes a non-negative amount. |
|
* |
|
* @returns {Boolean} True if the symbol precedes the amount, false if |
|
* the symbol succeeds the amount. |
|
*/ |
|
this.currencySymbolPrecedes = function() { |
|
|
|
if (this.p_cs_precedes == 1) |
|
return true; |
|
else |
|
return false; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the position of the international (ISO-4217 code) |
|
* currency symbol relative to the amount. Assumes a non-negative |
|
* amount. |
|
* |
|
* @returns {Boolean} True if the symbol precedes the amount, false if |
|
* the symbol succeeds the amount. |
|
*/ |
|
this.intCurrencySymbolPrecedes = function() { |
|
|
|
if (this.int_p_cs_precedes == 1) |
|
return true; |
|
else |
|
return false; |
|
|
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the decimal delimiter (radix) for monetary |
|
* quantities. |
|
* |
|
* @returns {String} The radix character. |
|
*/ |
|
this.getMonetaryDecimalPoint = function() { |
|
|
|
return this.mon_decimal_point; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the number of fractional digits for local |
|
* (shorthand) symbol formatting. |
|
* |
|
* @returns {integer Number} The number of fractional digits. |
|
*/ |
|
this.getFractionalDigits = function() { |
|
|
|
return this.frac_digits; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the number of fractional digits for |
|
* international (ISO-4217 code) formatting. |
|
* |
|
* @returns {integer Number} The number of fractional digits. |
|
*/ |
|
this.getIntFractionalDigits = function() { |
|
|
|
return this.int_frac_digits; |
|
}; |
|
}; |
|
|
|
|
|
|
|
/** |
|
* @class |
|
* Class for localised formatting of numbers. |
|
* |
|
* <p>See: <a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_04"> |
|
* POSIX LC_NUMERIC</a>. |
|
* |
|
* |
|
* @public |
|
* @constructor |
|
* @description Creates a new numeric formatter for the specified locale. |
|
* |
|
* @param {jsworld.Locale} locale A locale object specifying the required |
|
* POSIX LC_NUMERIC formatting properties. |
|
* |
|
* @throws Error on constructor failure. |
|
*/ |
|
jsworld.NumericFormatter = function(locale) { |
|
|
|
if (typeof locale != "object" || locale._className != "jsworld.Locale") |
|
throw "Constructor error: You must provide a valid jsworld.Locale instance"; |
|
|
|
this.lc = locale; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Formats a decimal numeric value according to the preset |
|
* locale. |
|
* |
|
* @param {Number|String} number The number to format. |
|
* @param {String} [options] Options to modify the formatted output: |
|
* <ul> |
|
* <li>"^" suppress grouping |
|
* <li>"+" force positive sign for positive amounts |
|
* <li>"~" suppress positive/negative sign |
|
* <li>".n" specify decimal precision 'n' |
|
* </ul> |
|
* |
|
* @returns {String} The formatted number. |
|
* |
|
* @throws "Error: Invalid input" on bad input. |
|
*/ |
|
this.format = function(number, options) { |
|
|
|
if (typeof number == "string") |
|
number = jsworld._trim(number); |
|
|
|
if (! jsworld._isNumber(number)) |
|
throw "Error: The input is not a number"; |
|
|
|
var floatAmount = parseFloat(number, 10); |
|
|
|
// get the required precision |
|
var reqPrecision = jsworld._getPrecision(options); |
|
|
|
// round to required precision |
|
if (reqPrecision != -1) |
|
floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision); |
|
|
|
|
|
// convert the float number to string and parse into |
|
// object with properties integer and fraction |
|
var parsedAmount = jsworld._splitNumber(String(floatAmount)); |
|
|
|
// format integer part with grouping chars |
|
var formattedIntegerPart; |
|
|
|
if (floatAmount === 0) |
|
formattedIntegerPart = "0"; |
|
else |
|
formattedIntegerPart = jsworld._hasOption("^", options) ? |
|
parsedAmount.integer : |
|
jsworld._formatIntegerPart(parsedAmount.integer, |
|
this.lc.grouping, |
|
this.lc.thousands_sep); |
|
|
|
// format the fractional part |
|
var formattedFractionPart = |
|
reqPrecision != -1 ? |
|
jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision) : |
|
parsedAmount.fraction; |
|
|
|
|
|
// join the integer and fraction parts using the decimal_point property |
|
var formattedAmount = |
|
formattedFractionPart.length ? |
|
formattedIntegerPart + this.lc.decimal_point + formattedFractionPart : |
|
formattedIntegerPart; |
|
|
|
// prepend sign? |
|
if (jsworld._hasOption("~", options) || floatAmount === 0) { |
|
// suppress both '+' and '-' signs, i.e. return abs value |
|
return formattedAmount; |
|
} |
|
else { |
|
if (jsworld._hasOption("+", options) || floatAmount < 0) { |
|
if (floatAmount > 0) |
|
// force '+' sign for positive amounts |
|
return "+" + formattedAmount; |
|
else if (floatAmount < 0) |
|
// prepend '-' sign |
|
return "-" + formattedAmount; |
|
else |
|
// zero case |
|
return formattedAmount; |
|
} |
|
else { |
|
// positive amount with no '+' sign |
|
return formattedAmount; |
|
} |
|
} |
|
}; |
|
}; |
|
|
|
|
|
/** |
|
* @class |
|
* Class for localised formatting of dates and times. |
|
* |
|
* <p>See: <a href="http://www.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap07.html#tag_07_03_05"> |
|
* POSIX LC_TIME</a>. |
|
* |
|
* @public |
|
* @constructor |
|
* @description Creates a new date/time formatter for the specified locale. |
|
* |
|
* @param {jsworld.Locale} locale A locale object specifying the required |
|
* POSIX LC_TIME formatting properties. |
|
* |
|
* @throws Error on constructor failure. |
|
*/ |
|
jsworld.DateTimeFormatter = function(locale) { |
|
|
|
|
|
if (typeof locale != "object" || locale._className != "jsworld.Locale") |
|
throw "Constructor error: You must provide a valid jsworld.Locale instance."; |
|
|
|
this.lc = locale; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Formats a date according to the preset locale. |
|
* |
|
* @param {Date|String} date A valid Date object instance or a string |
|
* containing a valid ISO-8601 formatted date, e.g. "2010-31-03" |
|
* or "2010-03-31 23:59:59". |
|
* |
|
* @returns {String} The formatted date |
|
* |
|
* @throws Error on invalid date argument |
|
*/ |
|
this.formatDate = function(date) { |
|
|
|
var d = null; |
|
|
|
if (typeof date == "string") { |
|
// assume ISO-8601 date string |
|
try { |
|
d = jsworld.parseIsoDate(date); |
|
} catch (error) { |
|
// try full ISO-8601 date/time string |
|
d = jsworld.parseIsoDateTime(date); |
|
} |
|
} |
|
else if (date !== null && typeof date == "object") { |
|
// assume ready Date object |
|
d = date; |
|
} |
|
else { |
|
throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; |
|
} |
|
|
|
return this._applyFormatting(d, this.lc.d_fmt); |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Formats a time according to the preset locale. |
|
* |
|
* @param {Date|String} date A valid Date object instance or a string |
|
* containing a valid ISO-8601 formatted time, e.g. "23:59:59" |
|
* or "2010-03-31 23:59:59". |
|
* |
|
* @returns {String} The formatted time. |
|
* |
|
* @throws Error on invalid date argument. |
|
*/ |
|
this.formatTime = function(date) { |
|
|
|
var d = null; |
|
|
|
if (typeof date == "string") { |
|
// assume ISO-8601 time string |
|
try { |
|
d = jsworld.parseIsoTime(date); |
|
} catch (error) { |
|
// try full ISO-8601 date/time string |
|
d = jsworld.parseIsoDateTime(date); |
|
} |
|
} |
|
else if (date !== null && typeof date == "object") { |
|
// assume ready Date object |
|
d = date; |
|
} |
|
else { |
|
throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; |
|
} |
|
|
|
return this._applyFormatting(d, this.lc.t_fmt); |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Formats a date/time value according to the preset |
|
* locale. |
|
* |
|
* @param {Date|String} date A valid Date object instance or a string |
|
* containing a valid ISO-8601 formatted date/time, e.g. |
|
* "2010-03-31 23:59:59". |
|
* |
|
* @returns {String} The formatted time. |
|
* |
|
* @throws Error on invalid argument. |
|
*/ |
|
this.formatDateTime = function(date) { |
|
|
|
var d = null; |
|
|
|
if (typeof date == "string") { |
|
// assume ISO-8601 format |
|
d = jsworld.parseIsoDateTime(date); |
|
} |
|
else if (date !== null && typeof date == "object") { |
|
// assume ready Date object |
|
d = date; |
|
} |
|
else { |
|
throw "Error: Invalid date argument, must be a Date object or an ISO-8601 date/time string"; |
|
} |
|
|
|
return this._applyFormatting(d, this.lc.d_t_fmt); |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Apples formatting to the Date object according to the |
|
* format string. |
|
* |
|
* @param {Date} d A valid Date instance. |
|
* @param {String} s The formatting string with '%' placeholders. |
|
* |
|
* @returns {String} The formatted string. |
|
*/ |
|
this._applyFormatting = function(d, s) { |
|
|
|
s = s.replace(/%%/g, '%'); |
|
s = s.replace(/%a/g, this.lc.abday[d.getDay()]); |
|
s = s.replace(/%A/g, this.lc.day[d.getDay()]); |
|
s = s.replace(/%b/g, this.lc.abmon[d.getMonth()]); |
|
s = s.replace(/%B/g, this.lc.mon[d.getMonth()]); |
|
s = s.replace(/%d/g, jsworld._zeroPad(d.getDate(), 2)); |
|
s = s.replace(/%e/g, jsworld._spacePad(d.getDate(), 2)); |
|
s = s.replace(/%F/g, d.getFullYear() + |
|
"-" + |
|
jsworld._zeroPad(d.getMonth()+1, 2) + |
|
"-" + |
|
jsworld._zeroPad(d.getDate(), 2)); |
|
s = s.replace(/%h/g, this.lc.abmon[d.getMonth()]); // same as %b |
|
s = s.replace(/%H/g, jsworld._zeroPad(d.getHours(), 2)); |
|
s = s.replace(/%I/g, jsworld._zeroPad(this._hours12(d.getHours()), 2)); |
|
s = s.replace(/%k/g, d.getHours()); |
|
s = s.replace(/%l/g, this._hours12(d.getHours())); |
|
s = s.replace(/%m/g, jsworld._zeroPad(d.getMonth()+1, 2)); |
|
s = s.replace(/%n/g, "\n"); |
|
s = s.replace(/%M/g, jsworld._zeroPad(d.getMinutes(), 2)); |
|
s = s.replace(/%p/g, this._getAmPm(d.getHours())); |
|
s = s.replace(/%P/g, this._getAmPm(d.getHours()).toLocaleLowerCase()); // safe? |
|
s = s.replace(/%R/g, jsworld._zeroPad(d.getHours(), 2) + |
|
":" + |
|
jsworld._zeroPad(d.getMinutes(), 2)); |
|
s = s.replace(/%S/g, jsworld._zeroPad(d.getSeconds(), 2)); |
|
s = s.replace(/%T/g, jsworld._zeroPad(d.getHours(), 2) + |
|
":" + |
|
jsworld._zeroPad(d.getMinutes(), 2) + |
|
":" + |
|
jsworld._zeroPad(d.getSeconds(), 2)); |
|
s = s.replace(/%w/g, this.lc.day[d.getDay()]); |
|
s = s.replace(/%y/g, new String(d.getFullYear()).substring(2)); |
|
s = s.replace(/%Y/g, d.getFullYear()); |
|
|
|
s = s.replace(/%Z/g, ""); // to do: ignored until a reliable TMZ method found |
|
|
|
s = s.replace(/%[a-zA-Z]/g, ""); // ignore all other % sequences |
|
|
|
return s; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Does 24 to 12 hour conversion. |
|
* |
|
* @param {integer Number} hour24 Hour [0..23]. |
|
* |
|
* @returns {integer Number} Corresponding hour [1..12]. |
|
*/ |
|
this._hours12 = function(hour24) { |
|
|
|
if (hour24 === 0) |
|
return 12; // 00h is 12AM |
|
|
|
else if (hour24 > 12) |
|
return hour24 - 12; // 1PM to 11PM |
|
|
|
else |
|
return hour24; // 1AM to 12PM |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Gets the appropriate localised AM or PM string depending |
|
* on the day hour. Special cases: midnight is 12AM, noon is 12PM. |
|
* |
|
* @param {integer Number} hour24 Hour [0..23]. |
|
* |
|
* @returns {String} The corresponding localised AM or PM string. |
|
*/ |
|
this._getAmPm = function(hour24) { |
|
|
|
if (hour24 < 12) |
|
return this.lc.am; |
|
else |
|
return this.lc.pm; |
|
}; |
|
}; |
|
|
|
|
|
|
|
/** |
|
* @class Class for localised formatting of currency amounts. |
|
* |
|
* <p>See: <a href="http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_03_03"> |
|
* POSIX LC_MONETARY</a>. |
|
* |
|
* @public |
|
* @constructor |
|
* @description Creates a new monetary formatter for the specified locale. |
|
* |
|
* @param {jsworld.Locale} locale A locale object specifying the required |
|
* POSIX LC_MONETARY formatting properties. |
|
* @param {String} [currencyCode] Set the currency explicitly by |
|
* passing its international ISO-4217 code, e.g. "USD", "EUR", "GBP". |
|
* Use this optional parameter to override the default local currency |
|
* @param {String} [altIntSymbol] Non-local currencies are formatted |
|
* with their international ISO-4217 code to prevent ambiguity. |
|
* Use this optional argument to force a different symbol, such as the |
|
* currency's shorthand sign. This is mostly useful when the shorthand |
|
* sign is both internationally recognised and identifies the currency |
|
* uniquely (e.g. the Euro sign). |
|
* |
|
* @throws Error on constructor failure. |
|
*/ |
|
jsworld.MonetaryFormatter = function(locale, currencyCode, altIntSymbol) { |
|
|
|
if (typeof locale != "object" || locale._className != "jsworld.Locale") |
|
throw "Constructor error: You must provide a valid jsworld.Locale instance"; |
|
|
|
this.lc = locale; |
|
|
|
/** |
|
* @private |
|
* @description Lookup table to determine the fraction digits for a |
|
* specific currency; most currencies subdivide at 1/100 (2 fractional |
|
* digits), so we store only those that deviate from the default. |
|
* |
|
* <p>The data is from Unicode's CLDR version 1.7.0. The two currencies |
|
* with non-decimal subunits (MGA and MRO) are marked as having no |
|
* fractional digits as well as all currencies that have no subunits |
|
* in circulation. |
|
* |
|
* <p>It is "hard-wired" for referential convenience and is only looked |
|
* up when an overriding currencyCode parameter is supplied. |
|
*/ |
|
this.currencyFractionDigits = { |
|
"AFN" : 0, "ALL" : 0, "AMD" : 0, "BHD" : 3, "BIF" : 0, |
|
"BYR" : 0, "CLF" : 0, "CLP" : 0, "COP" : 0, "CRC" : 0, |
|
"DJF" : 0, "GNF" : 0, "GYD" : 0, "HUF" : 0, "IDR" : 0, |
|
"IQD" : 0, "IRR" : 0, "ISK" : 0, "JOD" : 3, "JPY" : 0, |
|
"KMF" : 0, "KRW" : 0, "KWD" : 3, "LAK" : 0, "LBP" : 0, |
|
"LYD" : 3, "MGA" : 0, "MMK" : 0, "MNT" : 0, "MRO" : 0, |
|
"MUR" : 0, "OMR" : 3, "PKR" : 0, "PYG" : 0, "RSD" : 0, |
|
"RWF" : 0, "SLL" : 0, "SOS" : 0, "STD" : 0, "SYP" : 0, |
|
"TND" : 3, "TWD" : 0, "TZS" : 0, "UGX" : 0, "UZS" : 0, |
|
"VND" : 0, "VUV" : 0, "XAF" : 0, "XOF" : 0, "XPF" : 0, |
|
"YER" : 0, "ZMK" : 0 |
|
}; |
|
|
|
|
|
// optional currencyCode argument? |
|
if (typeof currencyCode == "string") { |
|
// user wanted to override the local currency |
|
this.currencyCode = currencyCode.toUpperCase(); |
|
|
|
// must override the frac digits too, for some |
|
// currencies have 0, 2 or 3! |
|
var numDigits = this.currencyFractionDigits[this.currencyCode]; |
|
if (typeof numDigits != "number") |
|
numDigits = 2; // default for most currencies |
|
this.lc.frac_digits = numDigits; |
|
this.lc.int_frac_digits = numDigits; |
|
} |
|
else { |
|
// use local currency |
|
this.currencyCode = this.lc.int_curr_symbol.substring(0,3).toUpperCase(); |
|
} |
|
|
|
// extract intl. currency separator |
|
this.intSep = this.lc.int_curr_symbol.charAt(3); |
|
|
|
// flag local or intl. sign formatting? |
|
if (this.currencyCode == this.lc.int_curr_symbol.substring(0,3)) { |
|
// currency matches the local one? -> |
|
// formatting with local symbol and parameters |
|
this.internationalFormatting = false; |
|
this.curSym = this.lc.currency_symbol; |
|
} |
|
else { |
|
// currency doesn't match the local -> |
|
|
|
// do we have an overriding currency symbol? |
|
if (typeof altIntSymbol == "string") { |
|
// -> force formatting with local parameters, using alt symbol |
|
this.curSym = altIntSymbol; |
|
this.internationalFormatting = false; |
|
} |
|
else { |
|
// -> force formatting with intl. sign and parameters |
|
this.internationalFormatting = true; |
|
} |
|
} |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the currency symbol used in formatting. |
|
* |
|
* @returns {String} The currency symbol. |
|
*/ |
|
this.getCurrencySymbol = function() { |
|
|
|
return this.curSym; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the position of the currency symbol relative to |
|
* the amount. Assumes a non-negative amount and local formatting. |
|
* |
|
* @param {String} intFlag Optional flag to force international |
|
* formatting by passing the string "i". |
|
* |
|
* @returns {Boolean} True if the symbol precedes the amount, false if |
|
* the symbol succeeds the amount. |
|
*/ |
|
this.currencySymbolPrecedes = function(intFlag) { |
|
|
|
if (typeof intFlag == "string" && intFlag == "i") { |
|
// international formatting was forced |
|
if (this.lc.int_p_cs_precedes == 1) |
|
return true; |
|
else |
|
return false; |
|
|
|
} |
|
else { |
|
// check whether local formatting is on or off |
|
if (this.internationalFormatting) { |
|
if (this.lc.int_p_cs_precedes == 1) |
|
return true; |
|
else |
|
return false; |
|
} |
|
else { |
|
if (this.lc.p_cs_precedes == 1) |
|
return true; |
|
else |
|
return false; |
|
} |
|
} |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the decimal delimiter (radix) used in formatting. |
|
* |
|
* @returns {String} The radix character. |
|
*/ |
|
this.getDecimalPoint = function() { |
|
|
|
return this.lc.mon_decimal_point; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Gets the number of fractional digits. Assumes local |
|
* formatting. |
|
* |
|
* @param {String} intFlag Optional flag to force international |
|
* formatting by passing the string "i". |
|
* |
|
* @returns {integer Number} The number of fractional digits. |
|
*/ |
|
this.getFractionalDigits = function(intFlag) { |
|
|
|
if (typeof intFlag == "string" && intFlag == "i") { |
|
// international formatting was forced |
|
return this.lc.int_frac_digits; |
|
} |
|
else { |
|
// check whether local formatting is on or off |
|
if (this.internationalFormatting) |
|
return this.lc.int_frac_digits; |
|
else |
|
return this.lc.frac_digits; |
|
} |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Formats a monetary amount according to the preset |
|
* locale. |
|
* |
|
* <pre> |
|
* For local currencies the native shorthand symbol will be used for |
|
* formatting. |
|
* Example: |
|
* locale is en_US |
|
* currency is USD |
|
* -> the "$" symbol will be used, e.g. $123.45 |
|
* |
|
* For non-local currencies the international ISO-4217 code will be |
|
* used for formatting. |
|
* Example: |
|
* locale is en_US (which has USD as currency) |
|
* currency is EUR |
|
* -> the ISO three-letter code will be used, e.g. EUR 123.45 |
|
* |
|
* If the currency is non-local, but an alternative currency symbol was |
|
* provided, this will be used instead. |
|
* Example |
|
* locale is en_US (which has USD as currency) |
|
* currency is EUR |
|
* an alternative symbol is provided - "€" |
|
* -> the alternative symbol will be used, e.g. €123.45 |
|
* </pre> |
|
* |
|
* @param {Number|String} amount The amount to format as currency. |
|
* @param {String} [options] Options to modify the formatted output: |
|
* <ul> |
|
* <li>"^" suppress grouping |
|
* <li>"!" suppress the currency symbol |
|
* <li>"~" suppress the currency symbol and the sign (positive or negative) |
|
* <li>"i" force international sign (ISO-4217 code) formatting |
|
* <li>".n" specify decimal precision |
|
* |
|
* @returns The formatted currency amount as string. |
|
* |
|
* @throws "Error: Invalid amount" on bad amount. |
|
*/ |
|
this.format = function(amount, options) { |
|
|
|
// if the amount is passed as string, check that it parses to a float |
|
var floatAmount; |
|
|
|
if (typeof amount == "string") { |
|
amount = jsworld._trim(amount); |
|
floatAmount = parseFloat(amount); |
|
|
|
if (typeof floatAmount != "number" || isNaN(floatAmount)) |
|
throw "Error: Amount string not a number"; |
|
} |
|
else if (typeof amount == "number") { |
|
floatAmount = amount; |
|
} |
|
else { |
|
throw "Error: Amount not a number"; |
|
} |
|
|
|
// get the required precision, ".n" option arg overrides default locale config |
|
var reqPrecision = jsworld._getPrecision(options); |
|
|
|
if (reqPrecision == -1) { |
|
if (this.internationalFormatting || jsworld._hasOption("i", options)) |
|
reqPrecision = this.lc.int_frac_digits; |
|
else |
|
reqPrecision = this.lc.frac_digits; |
|
} |
|
|
|
// round |
|
floatAmount = Math.round(floatAmount * Math.pow(10, reqPrecision)) / Math.pow(10, reqPrecision); |
|
|
|
|
|
// convert the float amount to string and parse into |
|
// object with properties integer and fraction |
|
var parsedAmount = jsworld._splitNumber(String(floatAmount)); |
|
|
|
// format integer part with grouping chars |
|
var formattedIntegerPart; |
|
|
|
if (floatAmount === 0) |
|
formattedIntegerPart = "0"; |
|
else |
|
formattedIntegerPart = jsworld._hasOption("^", options) ? |
|
parsedAmount.integer : |
|
jsworld._formatIntegerPart(parsedAmount.integer, |
|
this.lc.mon_grouping, |
|
this.lc.mon_thousands_sep); |
|
|
|
|
|
// format the fractional part |
|
var formattedFractionPart; |
|
|
|
if (reqPrecision == -1) { |
|
// pad fraction with trailing zeros accoring to default locale [int_]frac_digits |
|
if (this.internationalFormatting || jsworld._hasOption("i", options)) |
|
formattedFractionPart = |
|
jsworld._formatFractionPart(parsedAmount.fraction, this.lc.int_frac_digits); |
|
else |
|
formattedFractionPart = |
|
jsworld._formatFractionPart(parsedAmount.fraction, this.lc.frac_digits); |
|
} |
|
else { |
|
// pad fraction with trailing zeros according to optional format parameter |
|
formattedFractionPart = |
|
jsworld._formatFractionPart(parsedAmount.fraction, reqPrecision); |
|
} |
|
|
|
|
|
// join integer and decimal parts using the mon_decimal_point property |
|
var quantity; |
|
|
|
if (this.lc.frac_digits > 0 || formattedFractionPart.length) |
|
quantity = formattedIntegerPart + this.lc.mon_decimal_point + formattedFractionPart; |
|
else |
|
quantity = formattedIntegerPart; |
|
|
|
|
|
// do final formatting with sign and symbol |
|
if (jsworld._hasOption("~", options)) { |
|
return quantity; |
|
} |
|
else { |
|
var suppressSymbol = jsworld._hasOption("!", options) ? true : false; |
|
|
|
var sign = floatAmount < 0 ? "-" : "+"; |
|
|
|
if (this.internationalFormatting || jsworld._hasOption("i", options)) { |
|
|
|
// format with ISO-4217 code (suppressed or not) |
|
if (suppressSymbol) |
|
return this._formatAsInternationalCurrencyWithNoSym(sign, quantity); |
|
else |
|
return this._formatAsInternationalCurrency(sign, quantity); |
|
} |
|
else { |
|
// format with local currency code (suppressed or not) |
|
if (suppressSymbol) |
|
return this._formatAsLocalCurrencyWithNoSym(sign, quantity); |
|
else |
|
return this._formatAsLocalCurrency(sign, quantity); |
|
} |
|
} |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Assembles the final string with sign, separator and symbol as local |
|
* currency. |
|
* |
|
* @param {String} sign The amount sign: "+" or "-". |
|
* @param {String} q The formatted quantity (unsigned). |
|
* |
|
* @returns {String} The final formatted string. |
|
*/ |
|
this._formatAsLocalCurrency = function (sign, q) { |
|
|
|
// assemble final formatted amount by going over all possible value combinations of: |
|
// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} |
|
if (sign == "+") { |
|
|
|
// parentheses |
|
if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return "(" + q + this.curSym + ")"; |
|
} |
|
else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return "(" + this.curSym + q + ")"; |
|
} |
|
else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return "(" + q + " " + this.curSym + ")"; |
|
} |
|
else if (this.lc.p_sign_posn === 0 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return "(" + this.curSym + " " + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q + this.curSym; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.curSym + q; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q + " " + this.curSym; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.curSym + " " + q; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return this.lc.positive_sign + " " + q + this.curSym; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + " " + this.curSym + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return q + this.curSym + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return this.curSym + q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return q + " " + this.curSym + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return this.curSym + " " + q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return q + this.curSym + " " + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return this.curSym + q + " " + this.lc.positive_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign + this.curSym; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.curSym + q; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return q + " " + this.lc.positive_sign + this.curSym; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.curSym + " " + q; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign + " " + this.curSym; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + " " + this.curSym + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return q + this.curSym + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return this.curSym + this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return q + " " + this.curSym + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return this.curSym + this.lc.positive_sign + " " + q; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return q + this.curSym + " " + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return this.curSym + " " + this.lc.positive_sign + q; |
|
} |
|
|
|
} |
|
else if (sign == "-") { |
|
|
|
// parentheses enclose q + sym |
|
if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return "(" + q + this.curSym + ")"; |
|
} |
|
else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return "(" + this.curSym + q + ")"; |
|
} |
|
else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return "(" + q + " " + this.curSym + ")"; |
|
} |
|
else if (this.lc.n_sign_posn === 0 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return "(" + this.curSym + " " + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q + this.curSym; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.curSym + q; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q + " " + this.curSym; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.curSym + " " + q; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return this.lc.negative_sign + " " + q + this.curSym; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + " " + this.curSym + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return q + this.curSym + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return this.curSym + q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.curSym + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return this.curSym + " " + q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return q + this.curSym + " " + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return this.curSym + q + " " + this.lc.negative_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign + this.curSym; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.curSym + q; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.lc.negative_sign + this.curSym; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.curSym + " " + q; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign + " " + this.curSym; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + " " + this.curSym + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return q + this.curSym + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return this.curSym + this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.curSym + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return this.curSym + this.lc.negative_sign + " " + q; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return q + this.curSym + " " + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return this.curSym + " " + this.lc.negative_sign + q; |
|
} |
|
} |
|
|
|
// throw error if we fall through |
|
throw "Error: Invalid POSIX LC MONETARY definition"; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Assembles the final string with sign, separator and ISO-4217 |
|
* currency code. |
|
* |
|
* @param {String} sign The amount sign: "+" or "-". |
|
* @param {String} q The formatted quantity (unsigned). |
|
* |
|
* @returns {String} The final formatted string. |
|
*/ |
|
this._formatAsInternationalCurrency = function (sign, q) { |
|
|
|
// assemble the final formatted amount by going over all possible value combinations of: |
|
// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} |
|
|
|
if (sign == "+") { |
|
|
|
// parentheses |
|
if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return "(" + q + this.currencyCode + ")"; |
|
} |
|
else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return "(" + this.currencyCode + q + ")"; |
|
} |
|
else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return "(" + q + this.intSep + this.currencyCode + ")"; |
|
} |
|
else if (this.lc.int_p_sign_posn === 0 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return "(" + this.currencyCode + this.intSep + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q + this.currencyCode; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.currencyCode + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q + this.intSep + this.currencyCode; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.currencyCode + this.intSep + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return this.lc.positive_sign + this.intSep + q + this.currencyCode; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.intSep + this.currencyCode + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.currencyCode + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return this.currencyCode + q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.currencyCode + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return this.currencyCode + this.intSep + q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.currencyCode + this.intSep + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return this.currencyCode + q + this.intSep + this.lc.positive_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign + this.currencyCode; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.currencyCode + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.positive_sign + this.currencyCode; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.currencyCode + this.intSep + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign + this.intSep + this.currencyCode; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.intSep + this.currencyCode + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.currencyCode + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return this.currencyCode + this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.currencyCode + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return this.currencyCode + this.lc.positive_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.currencyCode + this.intSep + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return this.currencyCode + this.intSep + this.lc.positive_sign + q; |
|
} |
|
|
|
} |
|
else if (sign == "-") { |
|
|
|
// parentheses enclose q + sym |
|
if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return "(" + q + this.currencyCode + ")"; |
|
} |
|
else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return "(" + this.currencyCode + q + ")"; |
|
} |
|
else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return "(" + q + this.intSep + this.currencyCode + ")"; |
|
} |
|
else if (this.lc.int_n_sign_posn === 0 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return "(" + this.currencyCode + this.intSep + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q + this.currencyCode; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.currencyCode + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q + this.intSep + this.currencyCode; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.currencyCode + this.intSep + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return this.lc.negative_sign + this.intSep + q + this.currencyCode; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.intSep + this.currencyCode + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.currencyCode + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return this.currencyCode + q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.currencyCode + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return this.currencyCode + this.intSep + q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.currencyCode + this.intSep + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return this.currencyCode + q + this.intSep + this.lc.negative_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign + this.currencyCode; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.currencyCode + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.negative_sign + this.currencyCode; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.currencyCode + this.intSep + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign + this.intSep + this.currencyCode; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.intSep + this.currencyCode + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.currencyCode + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return this.currencyCode + this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.currencyCode + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return this.currencyCode + this.lc.negative_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.currencyCode + this.intSep + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return this.currencyCode + this.intSep + this.lc.negative_sign + q; |
|
} |
|
} |
|
|
|
// throw error if we fall through |
|
throw "Error: Invalid POSIX LC MONETARY definition"; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Assembles the final string with sign and separator, but suppress the |
|
* local currency symbol. |
|
* |
|
* @param {String} sign The amount sign: "+" or "-". |
|
* @param {String} q The formatted quantity (unsigned). |
|
* |
|
* @returns {String} The final formatted string |
|
*/ |
|
this._formatAsLocalCurrencyWithNoSym = function (sign, q) { |
|
|
|
// assemble the final formatted amount by going over all possible value combinations of: |
|
// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} |
|
|
|
if (sign == "+") { |
|
|
|
// parentheses |
|
if (this.lc.p_sign_posn === 0) { |
|
return "(" + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return this.lc.positive_sign + " " + q; |
|
} |
|
else if (this.lc.p_sign_posn === 1 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + " " + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return q + " " + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 2 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return q + " " + this.lc.positive_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return q + " " + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + " " + q; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 3 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + " " + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 0 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 0) { |
|
return q + " " + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 1 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + " " + q; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 0) { |
|
return q + " " + this.lc.positive_sign; |
|
} |
|
else if (this.lc.p_sign_posn === 4 && this.lc.p_sep_by_space === 2 && this.lc.p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
|
|
} |
|
else if (sign == "-") { |
|
|
|
// parentheses enclose q + sym |
|
if (this.lc.n_sign_posn === 0) { |
|
return "(" + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + " " + q; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return this.lc.negative_sign + " " + q; |
|
} |
|
else if (this.lc.n_sign_posn === 1 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + " " + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 2 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return q + " " + this.lc.negative_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + " " + q; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 3 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + " " + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 0 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 1 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + " " + q; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 0) { |
|
return q + " " + this.lc.negative_sign; |
|
} |
|
else if (this.lc.n_sign_posn === 4 && this.lc.n_sep_by_space === 2 && this.lc.n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
} |
|
|
|
// throw error if we fall through |
|
throw "Error: Invalid POSIX LC MONETARY definition"; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Assembles the final string with sign and separator, but suppress |
|
* the ISO-4217 currency code. |
|
* |
|
* @param {String} sign The amount sign: "+" or "-". |
|
* @param {String} q The formatted quantity (unsigned). |
|
* |
|
* @returns {String} The final formatted string. |
|
*/ |
|
this._formatAsInternationalCurrencyWithNoSym = function (sign, q) { |
|
|
|
// assemble the final formatted amount by going over all possible value combinations of: |
|
// sign {+,-} , sign position {0,1,2,3,4} , separator {0,1,2} , symbol position {0,1} |
|
|
|
if (sign == "+") { |
|
|
|
// parentheses |
|
if (this.lc.int_p_sign_posn === 0) { |
|
return "(" + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return this.lc.positive_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 1 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.intSep + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 2 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return q + this.intSep + this.lc.positive_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 3 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.intSep + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 0 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 1 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.positive_sign; |
|
} |
|
else if (this.lc.int_p_sign_posn === 4 && this.lc.int_p_sep_by_space === 2 && this.lc.int_p_cs_precedes === 1) { |
|
return this.lc.positive_sign + q; |
|
} |
|
|
|
} |
|
else if (sign == "-") { |
|
|
|
// parentheses enclose q + sym |
|
if (this.lc.int_n_sign_posn === 0) { |
|
return "(" + q + ")"; |
|
} |
|
|
|
// sign before q + sym |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return this.lc.negative_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 1 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.intSep + q; |
|
} |
|
|
|
// sign after q + sym |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 2 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return q + this.intSep + this.lc.negative_sign; |
|
} |
|
|
|
// sign before sym |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 3 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.intSep + q; |
|
} |
|
|
|
// sign after symbol |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 0 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 1 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + this.intSep + q; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 0) { |
|
return q + this.intSep + this.lc.negative_sign; |
|
} |
|
else if (this.lc.int_n_sign_posn === 4 && this.lc.int_n_sep_by_space === 2 && this.lc.int_n_cs_precedes === 1) { |
|
return this.lc.negative_sign + q; |
|
} |
|
} |
|
|
|
// throw error if we fall through |
|
throw "Error: Invalid POSIX LC_MONETARY definition"; |
|
}; |
|
}; |
|
|
|
|
|
/** |
|
* @class |
|
* Class for parsing localised number strings. |
|
* |
|
* @public |
|
* @constructor |
|
* @description Creates a new numeric parser for the specified locale. |
|
* |
|
* @param {jsworld.Locale} locale A locale object specifying the required |
|
* POSIX LC_NUMERIC formatting properties. |
|
* |
|
* @throws Error on constructor failure. |
|
*/ |
|
jsworld.NumericParser = function(locale) { |
|
|
|
if (typeof locale != "object" || locale._className != "jsworld.Locale") |
|
throw "Constructor error: You must provide a valid jsworld.Locale instance"; |
|
|
|
this.lc = locale; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Parses a numeric string formatted according to the |
|
* preset locale. Leading and trailing whitespace is ignored; the number |
|
* may also be formatted without thousands separators. |
|
* |
|
* @param {String} formattedNumber The formatted number. |
|
* |
|
* @returns {Number} The parsed number. |
|
* |
|
* @throws Error on a parse exception. |
|
*/ |
|
this.parse = function(formattedNumber) { |
|
|
|
if (typeof formattedNumber != "string") |
|
throw "Parse error: Argument must be a string"; |
|
|
|
// trim whitespace |
|
var s = jsworld._trim(formattedNumber); |
|
|
|
// remove any thousand separator symbols |
|
s = jsworld._stringReplaceAll(formattedNumber, this.lc.thousands_sep, ""); |
|
|
|
// replace any local decimal point symbols with the symbol used |
|
// in JavaScript "." |
|
s = jsworld._stringReplaceAll(s, this.lc.decimal_point, "."); |
|
|
|
// test if the string represents a number |
|
if (jsworld._isNumber(s)) |
|
return parseFloat(s, 10); |
|
else |
|
throw "Parse error: Invalid number string"; |
|
}; |
|
}; |
|
|
|
|
|
/** |
|
* @class |
|
* Class for parsing localised date and time strings. |
|
* |
|
* @public |
|
* @constructor |
|
* @description Creates a new date/time parser for the specified locale. |
|
* |
|
* @param {jsworld.Locale} locale A locale object specifying the required |
|
* POSIX LC_TIME formatting properties. |
|
* |
|
* @throws Error on constructor failure. |
|
*/ |
|
jsworld.DateTimeParser = function(locale) { |
|
|
|
if (typeof locale != "object" || locale._className != "jsworld.Locale") |
|
throw "Constructor error: You must provide a valid jsworld.Locale instance."; |
|
|
|
this.lc = locale; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Parses a time string formatted according to the |
|
* POSIX LC_TIME t_fmt property of the preset locale. |
|
* |
|
* @param {String} formattedTime The formatted time. |
|
* |
|
* @returns {String} The parsed time in ISO-8601 format (HH:MM:SS), e.g. |
|
* "23:59:59". |
|
* |
|
* @throws Error on a parse exception. |
|
*/ |
|
this.parseTime = function(formattedTime) { |
|
|
|
if (typeof formattedTime != "string") |
|
throw "Parse error: Argument must be a string"; |
|
|
|
var dt = this._extractTokens(this.lc.t_fmt, formattedTime); |
|
|
|
var timeDefined = false; |
|
|
|
if (dt.hour !== null && dt.minute !== null && dt.second !== null) { |
|
timeDefined = true; |
|
} |
|
else if (dt.hourAmPm !== null && dt.am !== null && dt.minute !== null && dt.second !== null) { |
|
if (dt.am) { |
|
// AM [12(midnight), 1 .. 11] |
|
if (dt.hourAmPm == 12) |
|
dt.hour = 0; |
|
else |
|
dt.hour = parseInt(dt.hourAmPm, 10); |
|
} |
|
else { |
|
// PM [12(noon), 1 .. 11] |
|
if (dt.hourAmPm == 12) |
|
dt.hour = 12; |
|
else |
|
dt.hour = parseInt(dt.hourAmPm, 10) + 12; |
|
} |
|
timeDefined = true; |
|
} |
|
|
|
if (timeDefined) |
|
return jsworld._zeroPad(dt.hour, 2) + |
|
":" + |
|
jsworld._zeroPad(dt.minute, 2) + |
|
":" + |
|
jsworld._zeroPad(dt.second, 2); |
|
else |
|
throw "Parse error: Invalid/ambiguous time string"; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Parses a date string formatted according to the |
|
* POSIX LC_TIME d_fmt property of the preset locale. |
|
* |
|
* @param {String} formattedDate The formatted date, must be valid. |
|
* |
|
* @returns {String} The parsed date in ISO-8601 format (YYYY-MM-DD), |
|
* e.g. "2010-03-31". |
|
* |
|
* @throws Error on a parse exception. |
|
*/ |
|
this.parseDate = function(formattedDate) { |
|
|
|
if (typeof formattedDate != "string") |
|
throw "Parse error: Argument must be a string"; |
|
|
|
var dt = this._extractTokens(this.lc.d_fmt, formattedDate); |
|
|
|
var dateDefined = false; |
|
|
|
if (dt.year !== null && dt.month !== null && dt.day !== null) { |
|
dateDefined = true; |
|
} |
|
|
|
if (dateDefined) |
|
return jsworld._zeroPad(dt.year, 4) + |
|
"-" + |
|
jsworld._zeroPad(dt.month, 2) + |
|
"-" + |
|
jsworld._zeroPad(dt.day, 2); |
|
else |
|
throw "Parse error: Invalid date string"; |
|
}; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Parses a date/time string formatted according to the |
|
* POSIX LC_TIME d_t_fmt property of the preset locale. |
|
* |
|
* @param {String} formattedDateTime The formatted date/time, must be |
|
* valid. |
|
* |
|
* @returns {String} The parsed date/time in ISO-8601 format |
|
* (YYYY-MM-DD HH:MM:SS), e.g. "2010-03-31 23:59:59". |
|
* |
|
* @throws Error on a parse exception. |
|
*/ |
|
this.parseDateTime = function(formattedDateTime) { |
|
|
|
if (typeof formattedDateTime != "string") |
|
throw "Parse error: Argument must be a string"; |
|
|
|
var dt = this._extractTokens(this.lc.d_t_fmt, formattedDateTime); |
|
|
|
var timeDefined = false; |
|
var dateDefined = false; |
|
|
|
if (dt.hour !== null && dt.minute !== null && dt.second !== null) { |
|
timeDefined = true; |
|
} |
|
else if (dt.hourAmPm !== null && dt.am !== null && dt.minute !== null && dt.second !== null) { |
|
if (dt.am) { |
|
// AM [12(midnight), 1 .. 11] |
|
if (dt.hourAmPm == 12) |
|
dt.hour = 0; |
|
else |
|
dt.hour = parseInt(dt.hourAmPm, 10); |
|
} |
|
else { |
|
// PM [12(noon), 1 .. 11] |
|
if (dt.hourAmPm == 12) |
|
dt.hour = 12; |
|
else |
|
dt.hour = parseInt(dt.hourAmPm, 10) + 12; |
|
} |
|
timeDefined = true; |
|
} |
|
|
|
if (dt.year !== null && dt.month !== null && dt.day !== null) { |
|
dateDefined = true; |
|
} |
|
|
|
if (dateDefined && timeDefined) |
|
return jsworld._zeroPad(dt.year, 4) + |
|
"-" + |
|
jsworld._zeroPad(dt.month, 2) + |
|
"-" + |
|
jsworld._zeroPad(dt.day, 2) + |
|
" " + |
|
jsworld._zeroPad(dt.hour, 2) + |
|
":" + |
|
jsworld._zeroPad(dt.minute, 2) + |
|
":" + |
|
jsworld._zeroPad(dt.second, 2); |
|
else |
|
throw "Parse error: Invalid/ambiguous date/time string"; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Parses a string according to the specified format |
|
* specification. |
|
* |
|
* @param {String} fmtSpec The format specification, e.g. "%I:%M:%S %p". |
|
* @param {String} s The string to parse. |
|
* |
|
* @returns {object} An object with set properties year, month, day, |
|
* hour, minute and second if the corresponding values are |
|
* found in the parsed string. |
|
* |
|
* @throws Error on a parse exception. |
|
*/ |
|
this._extractTokens = function(fmtSpec, s) { |
|
|
|
// the return object containing the parsed date/time properties |
|
var dt = { |
|
// for date and date/time strings |
|
"year" : null, |
|
"month" : null, |
|
"day" : null, |
|
|
|
// for time and date/time strings |
|
"hour" : null, |
|
"hourAmPm" : null, |
|
"am" : null, |
|
"minute" : null, |
|
"second" : null, |
|
|
|
// used internally only |
|
"weekday" : null |
|
}; |
|
|
|
|
|
// extract and process each token in the date/time spec |
|
while (fmtSpec.length > 0) { |
|
|
|
// Do we have a valid "%\w" placeholder in stream? |
|
if (fmtSpec.charAt(0) == "%" && fmtSpec.charAt(1) != "") { |
|
|
|
// get placeholder |
|
var placeholder = fmtSpec.substring(0,2); |
|
|
|
if (placeholder == "%%") { |
|
// escaped '%'' |
|
s = s.substring(1); |
|
} |
|
else if (placeholder == "%a") { |
|
// abbreviated weekday name |
|
for (var i = 0; i < this.lc.abday.length; i++) { |
|
|
|
if (jsworld._stringStartsWith(s, this.lc.abday[i])) { |
|
dt.weekday = i; |
|
s = s.substring(this.lc.abday[i].length); |
|
break; |
|
} |
|
} |
|
|
|
if (dt.weekday === null) |
|
throw "Parse error: Unrecognised abbreviated weekday name (%a)"; |
|
} |
|
else if (placeholder == "%A") { |
|
// weekday name |
|
for (var i = 0; i < this.lc.day.length; i++) { |
|
|
|
if (jsworld._stringStartsWith(s, this.lc.day[i])) { |
|
dt.weekday = i; |
|
s = s.substring(this.lc.day[i].length); |
|
break; |
|
} |
|
} |
|
|
|
if (dt.weekday === null) |
|
throw "Parse error: Unrecognised weekday name (%A)"; |
|
} |
|
else if (placeholder == "%b" || placeholder == "%h") { |
|
// abbreviated month name |
|
for (var i = 0; i < this.lc.abmon.length; i++) { |
|
|
|
if (jsworld._stringStartsWith(s, this.lc.abmon[i])) { |
|
dt.month = i + 1; |
|
s = s.substring(this.lc.abmon[i].length); |
|
break; |
|
} |
|
} |
|
|
|
if (dt.month === null) |
|
throw "Parse error: Unrecognised abbreviated month name (%b)"; |
|
} |
|
else if (placeholder == "%B") { |
|
// month name |
|
for (var i = 0; i < this.lc.mon.length; i++) { |
|
|
|
if (jsworld._stringStartsWith(s, this.lc.mon[i])) { |
|
dt.month = i + 1; |
|
s = s.substring(this.lc.mon[i].length); |
|
break; |
|
} |
|
} |
|
|
|
if (dt.month === null) |
|
throw "Parse error: Unrecognised month name (%B)"; |
|
} |
|
else if (placeholder == "%d") { |
|
// day of the month [01..31] |
|
if (/^0[1-9]|[1-2][0-9]|3[0-1]/.test(s)) { |
|
dt.day = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised day of the month (%d)"; |
|
} |
|
else if (placeholder == "%e") { |
|
// day of the month [1..31] |
|
|
|
// Note: if %e is leading in fmt string -> space padded! |
|
|
|
var day = s.match(/^\s?(\d{1,2})/); |
|
dt.day = parseInt(day, 10); |
|
|
|
if (isNaN(dt.day) || dt.day < 1 || dt.day > 31) |
|
throw "Parse error: Unrecognised day of the month (%e)"; |
|
|
|
s = s.substring(day.length); |
|
} |
|
else if (placeholder == "%F") { |
|
// equivalent to %Y-%m-%d (ISO-8601 date format) |
|
|
|
// year [nnnn] |
|
if (/^\d\d\d\d/.test(s)) { |
|
dt.year = parseInt(s.substring(0,4), 10); |
|
s = s.substring(4); |
|
} |
|
else { |
|
throw "Parse error: Unrecognised date (%F)"; |
|
} |
|
|
|
// - |
|
if (jsworld._stringStartsWith(s, "-")) |
|
s = s.substring(1); |
|
else |
|
throw "Parse error: Unrecognised date (%F)"; |
|
|
|
// month [01..12] |
|
if (/^0[1-9]|1[0-2]/.test(s)) { |
|
dt.month = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised date (%F)"; |
|
|
|
// - |
|
if (jsworld._stringStartsWith(s, "-")) |
|
s = s.substring(1); |
|
else |
|
throw "Parse error: Unrecognised date (%F)"; |
|
|
|
// day of the month [01..31] |
|
if (/^0[1-9]|[1-2][0-9]|3[0-1]/.test(s)) { |
|
dt.day = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised date (%F)"; |
|
} |
|
else if (placeholder == "%H") { |
|
// hour [00..23] |
|
if (/^[0-1][0-9]|2[0-3]/.test(s)) { |
|
dt.hour = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised hour (%H)"; |
|
} |
|
else if (placeholder == "%I") { |
|
// hour [01..12] |
|
if (/^0[1-9]|1[0-2]/.test(s)) { |
|
dt.hourAmPm = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised hour (%I)"; |
|
} |
|
else if (placeholder == "%k") { |
|
// hour [0..23] |
|
var h = s.match(/^(\d{1,2})/); |
|
dt.hour = parseInt(h, 10); |
|
|
|
if (isNaN(dt.hour) || dt.hour < 0 || dt.hour > 23) |
|
throw "Parse error: Unrecognised hour (%k)"; |
|
|
|
s = s.substring(h.length); |
|
} |
|
else if (placeholder == "%l") { |
|
// hour AM/PM [1..12] |
|
var h = s.match(/^(\d{1,2})/); |
|
dt.hourAmPm = parseInt(h, 10); |
|
|
|
if (isNaN(dt.hourAmPm) || dt.hourAmPm < 1 || dt.hourAmPm > 12) |
|
throw "Parse error: Unrecognised hour (%l)"; |
|
|
|
s = s.substring(h.length); |
|
} |
|
else if (placeholder == "%m") { |
|
// month [01..12] |
|
if (/^0[1-9]|1[0-2]/.test(s)) { |
|
dt.month = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised month (%m)"; |
|
} |
|
else if (placeholder == "%M") { |
|
// minute [00..59] |
|
if (/^[0-5][0-9]/.test(s)) { |
|
dt.minute = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised minute (%M)"; |
|
} |
|
else if (placeholder == "%n") { |
|
// new line |
|
|
|
if (s.charAt(0) == "\n") |
|
s = s.substring(1); |
|
else |
|
throw "Parse error: Unrecognised new line (%n)"; |
|
} |
|
else if (placeholder == "%p") { |
|
// locale's equivalent of AM/PM |
|
if (jsworld._stringStartsWith(s, this.lc.am)) { |
|
dt.am = true; |
|
s = s.substring(this.lc.am.length); |
|
} |
|
else if (jsworld._stringStartsWith(s, this.lc.pm)) { |
|
dt.am = false; |
|
s = s.substring(this.lc.pm.length); |
|
} |
|
else |
|
throw "Parse error: Unrecognised AM/PM value (%p)"; |
|
} |
|
else if (placeholder == "%P") { |
|
// same as %p but forced lower case |
|
if (jsworld._stringStartsWith(s, this.lc.am.toLowerCase())) { |
|
dt.am = true; |
|
s = s.substring(this.lc.am.length); |
|
} |
|
else if (jsworld._stringStartsWith(s, this.lc.pm.toLowerCase())) { |
|
dt.am = false; |
|
s = s.substring(this.lc.pm.length); |
|
} |
|
else |
|
throw "Parse error: Unrecognised AM/PM value (%P)"; |
|
} |
|
else if (placeholder == "%R") { |
|
// same as %H:%M |
|
|
|
// hour [00..23] |
|
if (/^[0-1][0-9]|2[0-3]/.test(s)) { |
|
dt.hour = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised time (%R)"; |
|
|
|
// : |
|
if (jsworld._stringStartsWith(s, ":")) |
|
s = s.substring(1); |
|
else |
|
throw "Parse error: Unrecognised time (%R)"; |
|
|
|
// minute [00..59] |
|
if (/^[0-5][0-9]/.test(s)) { |
|
dt.minute = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised time (%R)"; |
|
|
|
} |
|
else if (placeholder == "%S") { |
|
// second [00..59] |
|
if (/^[0-5][0-9]/.test(s)) { |
|
dt.second = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised second (%S)"; |
|
} |
|
else if (placeholder == "%T") { |
|
// same as %H:%M:%S |
|
|
|
// hour [00..23] |
|
if (/^[0-1][0-9]|2[0-3]/.test(s)) { |
|
dt.hour = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised time (%T)"; |
|
|
|
// : |
|
if (jsworld._stringStartsWith(s, ":")) |
|
s = s.substring(1); |
|
else |
|
throw "Parse error: Unrecognised time (%T)"; |
|
|
|
// minute [00..59] |
|
if (/^[0-5][0-9]/.test(s)) { |
|
dt.minute = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised time (%T)"; |
|
|
|
// : |
|
if (jsworld._stringStartsWith(s, ":")) |
|
s = s.substring(1); |
|
else |
|
throw "Parse error: Unrecognised time (%T)"; |
|
|
|
// second [00..59] |
|
if (/^[0-5][0-9]/.test(s)) { |
|
dt.second = parseInt(s.substring(0,2), 10); |
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised time (%T)"; |
|
} |
|
else if (placeholder == "%w") { |
|
// weekday [0..6] |
|
if (/^\d/.test(s)) { |
|
dt.weekday = parseInt(s.substring(0,1), 10); |
|
s = s.substring(1); |
|
} |
|
else |
|
throw "Parse error: Unrecognised weekday number (%w)"; |
|
} |
|
else if (placeholder == "%y") { |
|
// year [00..99] |
|
if (/^\d\d/.test(s)) { |
|
var year2digits = parseInt(s.substring(0,2), 10); |
|
|
|
// this conversion to year[nnnn] is arbitrary!!! |
|
if (year2digits > 50) |
|
dt.year = 1900 + year2digits; |
|
else |
|
dt.year = 2000 + year2digits; |
|
|
|
s = s.substring(2); |
|
} |
|
else |
|
throw "Parse error: Unrecognised year (%y)"; |
|
} |
|
else if (placeholder == "%Y") { |
|
// year [nnnn] |
|
if (/^\d\d\d\d/.test(s)) { |
|
dt.year = parseInt(s.substring(0,4), 10); |
|
s = s.substring(4); |
|
} |
|
else |
|
throw "Parse error: Unrecognised year (%Y)"; |
|
} |
|
|
|
else if (placeholder == "%Z") { |
|
// time-zone place holder is not supported |
|
|
|
if (fmtSpec.length === 0) |
|
break; // ignore rest of fmt spec |
|
} |
|
|
|
// remove the spec placeholder that was just parsed |
|
fmtSpec = fmtSpec.substring(2); |
|
} |
|
else { |
|
// If we don't have a placeholder, the chars |
|
// at pos. 0 of format spec and parsed string must match |
|
|
|
// Note: Space chars treated 1:1 ! |
|
|
|
if (fmtSpec.charAt(0) != s.charAt(0)) |
|
throw "Parse error: Unexpected symbol \"" + s.charAt(0) + "\" in date/time string"; |
|
|
|
fmtSpec = fmtSpec.substring(1); |
|
s = s.substring(1); |
|
} |
|
} |
|
|
|
// parsing finished, return composite date/time object |
|
return dt; |
|
}; |
|
}; |
|
|
|
|
|
/** |
|
* @class |
|
* Class for parsing localised currency amount strings. |
|
* |
|
* @public |
|
* @constructor |
|
* @description Creates a new monetary parser for the specified locale. |
|
* |
|
* @param {jsworld.Locale} locale A locale object specifying the required |
|
* POSIX LC_MONETARY formatting properties. |
|
* |
|
* @throws Error on constructor failure. |
|
*/ |
|
jsworld.MonetaryParser = function(locale) { |
|
|
|
if (typeof locale != "object" || locale._className != "jsworld.Locale") |
|
throw "Constructor error: You must provide a valid jsworld.Locale instance"; |
|
|
|
|
|
this.lc = locale; |
|
|
|
|
|
/** |
|
* @public |
|
* |
|
* @description Parses a currency amount string formatted according to |
|
* the preset locale. Leading and trailing whitespace is ignored; the |
|
* amount may also be formatted without thousands separators. Both |
|
* the local (shorthand) symbol and the ISO 4217 code are accepted to |
|
* designate the currency in the formatted amount. |
|
* |
|
* @param {String} formattedCurrency The formatted currency amount. |
|
* |
|
* @returns {Number} The parsed amount. |
|
* |
|
* @throws Error on a parse exception. |
|
*/ |
|
this.parse = function(formattedCurrency) { |
|
|
|
if (typeof formattedCurrency != "string") |
|
throw "Parse error: Argument must be a string"; |
|
|
|
// Detect the format type and remove the currency symbol |
|
var symbolType = this._detectCurrencySymbolType(formattedCurrency); |
|
|
|
var formatType, s; |
|
|
|
if (symbolType == "local") { |
|
formatType = "local"; |
|
s = formattedCurrency.replace(this.lc.getCurrencySymbol(), ""); |
|
} |
|
else if (symbolType == "int") { |
|
formatType = "int"; |
|
s = formattedCurrency.replace(this.lc.getIntCurrencySymbol(), ""); |
|
} |
|
else if (symbolType == "none") { |
|
formatType = "local"; // assume local |
|
s = formattedCurrency; |
|
} |
|
else |
|
throw "Parse error: Internal assert failure"; |
|
|
|
// Remove any thousands separators |
|
s = jsworld._stringReplaceAll(s, this.lc.mon_thousands_sep, ""); |
|
|
|
// Replace any local radix char with JavaScript's "." |
|
s = s.replace(this.lc.mon_decimal_point, "."); |
|
|
|
// Remove all whitespaces |
|
s = s.replace(/\s*/g, ""); |
|
|
|
// Remove any local non-negative sign |
|
s = this._removeLocalNonNegativeSign(s, formatType); |
|
|
|
// Replace any local minus sign with JavaScript's "-" and put |
|
// it in front of the amount if necessary |
|
// (special parentheses rule checked too) |
|
s = this._normaliseNegativeSign(s, formatType); |
|
|
|
// Finally, we should be left with a bare parsable decimal number |
|
if (jsworld._isNumber(s)) |
|
return parseFloat(s, 10); |
|
else |
|
throw "Parse error: Invalid currency amount string"; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Tries to detect the symbol type used in the specified |
|
* formatted currency string: local(shorthand), |
|
* international (ISO-4217 code) or none. |
|
* |
|
* @param {String} formattedCurrency The the formatted currency string. |
|
* |
|
* @return {String} With possible values "local", "int" or "none". |
|
*/ |
|
this._detectCurrencySymbolType = function(formattedCurrency) { |
|
|
|
// Check for whichever sign (int/local) is longer first |
|
// to cover cases such as MOP/MOP$ and ZAR/R |
|
|
|
if (this.lc.getCurrencySymbol().length > this.lc.getIntCurrencySymbol().length) { |
|
|
|
if (formattedCurrency.indexOf(this.lc.getCurrencySymbol()) != -1) |
|
return "local"; |
|
else if (formattedCurrency.indexOf(this.lc.getIntCurrencySymbol()) != -1) |
|
return "int"; |
|
else |
|
return "none"; |
|
} |
|
else { |
|
if (formattedCurrency.indexOf(this.lc.getIntCurrencySymbol()) != -1) |
|
return "int"; |
|
else if (formattedCurrency.indexOf(this.lc.getCurrencySymbol()) != -1) |
|
return "local"; |
|
else |
|
return "none"; |
|
} |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Removes a local non-negative sign in a formatted |
|
* currency string if it is found. This is done according to the |
|
* locale properties p_sign_posn and int_p_sign_posn. |
|
* |
|
* @param {String} s The input string. |
|
* @param {String} formatType With possible values "local" or "int". |
|
* |
|
* @returns {String} The processed string. |
|
*/ |
|
this._removeLocalNonNegativeSign = function(s, formatType) { |
|
|
|
s = s.replace(this.lc.positive_sign, ""); |
|
|
|
// check for enclosing parentheses rule |
|
if (((formatType == "local" && this.lc.p_sign_posn === 0) || |
|
(formatType == "int" && this.lc.int_p_sign_posn === 0) ) && |
|
/\(\d+\.?\d*\)/.test(s)) { |
|
s = s.replace("(", ""); |
|
s = s.replace(")", ""); |
|
} |
|
|
|
return s; |
|
}; |
|
|
|
|
|
/** |
|
* @private |
|
* |
|
* @description Replaces a local negative sign with the standard |
|
* JavaScript minus ("-") sign placed in the correct position |
|
* (preceding the amount). This is done according to the locale |
|
* properties for negative sign symbol and relative position. |
|
* |
|
* @param {String} s The input string. |
|
* @param {String} formatType With possible values "local" or "int". |
|
* |
|
* @returns {String} The processed string. |
|
*/ |
|
this._normaliseNegativeSign = function(s, formatType) { |
|
|
|
// replace local negative symbol with JavaScript's "-" |
|
s = s.replace(this.lc.negative_sign, "-"); |
|
|
|
// check for enclosing parentheses rule and replace them |
|
// with negative sign before the amount |
|
if ((formatType == "local" && this.lc.n_sign_posn === 0) || |
|
(formatType == "int" && this.lc.int_n_sign_posn === 0) ) { |
|
|
|
if (/^\(\d+\.?\d*\)$/.test(s)) { |
|
|
|
s = s.replace("(", ""); |
|
s = s.replace(")", ""); |
|
return "-" + s; |
|
} |
|
} |
|
|
|
// check for rule negative sign succeeding the amount |
|
if (formatType == "local" && this.lc.n_sign_posn == 2 || |
|
formatType == "int" && this.lc.int_n_sign_posn == 2 ) { |
|
|
|
if (/^\d+\.?\d*-$/.test(s)) { |
|
s = s.replace("-", ""); |
|
return "-" + s; |
|
} |
|
} |
|
|
|
// check for rule cur. sym. succeeds and sign adjacent |
|
if (formatType == "local" && this.lc.n_cs_precedes === 0 && this.lc.n_sign_posn == 3 || |
|
formatType == "local" && this.lc.n_cs_precedes === 0 && this.lc.n_sign_posn == 4 || |
|
formatType == "int" && this.lc.int_n_cs_precedes === 0 && this.lc.int_n_sign_posn == 3 || |
|
formatType == "int" && this.lc.int_n_cs_precedes === 0 && this.lc.int_n_sign_posn == 4 ) { |
|
|
|
if (/^\d+\.?\d*-$/.test(s)) { |
|
s = s.replace("-", ""); |
|
return "-" + s; |
|
} |
|
} |
|
|
|
return s; |
|
}; |
|
}; |
|
|
|
// end-of-file
|
|
|