/*
* Currency.js - Currency definition
*
* Copyright © 2012-2015, 2018, 2022 JEDLSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// !data currency
var ilib = require("../index.js");
var Utils = require("./Utils.js");
var Locale = require("./Locale.js");
var LocaleInfo = require("./LocaleInfo.js");
var ResBundle = require("./ResBundle.js");
/**
* @class
* Create a new currency information instance. Instances of this class encode
* information about a particular currency.<p>
*
* Note: that if you are looking to format currency for display, please see
* the number formatting class {NumFmt}. This class only gives information
* about currencies.<p>
*
* The options can contain any of the following properties:
*
* <ul>
* <li><i>locale</i> - specify the locale for this instance
* <li><i>code</i> - find info on a specific currency with the given ISO 4217 code
* <li><i>sign</i> - search for a currency that uses this sign
* <li><i>onLoad</i> - a callback function to call when the currency data is fully
* loaded. When the onLoad option is given, this class will attempt to
* load any missing locale data using the ilib loader callback.
* When the constructor is done (even if the data is already preassembled), the
* onLoad function is called with the current instance as a parameter, so this
* callback can be used with preassembled or dynamic loading or a mix of the two.
* <li><i>sync</i> - tell whether to load any missing locale data synchronously or
* asynchronously. If this option is given as "false", then the "onLoad"
* callback must be given, as the instance returned from this constructor will
* not be usable for a while.
* <li><i>loadParams</i> - an object containing parameters to pass to the
* loader callback function when locale data is missing. The parameters are not
* interpretted or modified in any way. They are simply passed along. The object
* may contain any property/value pairs as long as the calling code is in
* agreement with the loader callback function as to what those parameters mean.
* </ul>
*
* When searching for a currency by its sign, this class cannot guarantee
* that it will return info about a specific currency. The reason is that currency
* signs are sometimes shared between different currencies and the sign is
* therefore ambiguous. If you need a
* guarantee, find the currency using the code instead.<p>
*
* The way this class finds a currency by sign is the following. If the sign is
* unambiguous, then
* the currency is returned. If there are multiple currencies that use the same
* sign, and the current locale uses that sign, then the default currency for
* the current locale is returned. If there are multiple, but the current locale
* does not use that sign, then the currency with the largest circulation is
* returned. For example, if you are in the en-GB locale, and the sign is "$",
* then this class will notice that there are multiple currencies with that
* sign (USD, CAD, AUD, HKD, MXP, etc.) Since "$" is not used in en-GB, it will
* pick the one with the largest circulation, which in this case is the US Dollar
* (USD).<p>
*
* If neither the code or sign property is set, the currency that is most common
* for the locale
* will be used instead. If the locale is not set, the default locale will be used.
* If the code is given, but it is not found in the list of known currencies, this
* constructor will throw an exception. If the sign is given, but it is not found,
* this constructor will default to the currency for the current locale. If both
* the code and sign properties are given, then the sign property will be ignored
* and only the code property used. If the locale is given, but it is not a known
* locale, this class will default to the default locale instead.<p>
*
*
* @constructor
* @param options {Object} a set of properties to govern how this instance is constructed.
* @throws "currency xxx is unknown" when the given currency code is not in the list of
* known currencies. xxx is replaced with the requested code.
*/
var Currency = function (options) {
if (options) {
if (options.code) {
this.code = options.code;
}
if (options.locale) {
this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
}
if (options.sign) {
this.sign = options.sign;
}
if (options.loadParams) {
this.loadParams = options.loadParams;
}
options.sync = (typeof(options.sync) !== 'undefined') ? options.sync : true;
} else {
options = {sync: true};
}
this.locale = this.locale || new Locale();
if (typeof(ilib.data.currency) === 'undefined') {
Utils.loadData({
name: "currency.json",
object: "Currency",
locale: "-",
sync: options.sync,
loadParams: this.loadParams,
callback: ilib.bind(this, function(currency) {
ilib.data.currency = currency;
this._loadLocinfo(options);
})
});
} else {
this._loadLocinfo(options);
}
};
/**
* Return an array of the ids for all ISO 4217 currencies that
* this copy of ilib knows about.
*
* @static
* @return {Array.<string>} an array of currency ids that this copy of ilib knows about.
*/
Currency.getAvailableCurrencies = function() {
var ret = [],
cur,
currencies = new ResBundle({
name: "currency"
}).getResObj();
for (cur in currencies) {
if (cur && currencies[cur]) {
ret.push(cur);
}
}
return ret;
};
Currency.prototype = {
/**
* @private
*/
_loadLocinfo: function(options) {
new LocaleInfo(this.locale, {
sync: options.sync,
loadParams: options.loadParams,
onLoad: ilib.bind(this, function (li) {
var currInfo;
this.locinfo = li;
if (this.code) {
currInfo = ilib.data.currency[this.code];
if (!currInfo) {
if (options.sync) {
throw "currency " + this.code + " is unknown";
} else if (typeof(options.onLoad) === "function") {
options.onLoad(undefined);
return;
}
}
} else if (this.sign) {
currInfo = ilib.data.currency[this.sign]; // maybe it is really a code...
if (typeof(currInfo) !== 'undefined') {
this.code = this.sign;
} else {
this.code = this.locinfo.getCurrency();
currInfo = ilib.data.currency[this.code];
if (currInfo.sign !== this.sign) {
// current locale does not use the sign, so search for it
for (var cur in ilib.data.currency) {
if (cur && ilib.data.currency[cur]) {
currInfo = ilib.data.currency[cur];
if (currInfo.sign === this.sign) {
// currency data is already ordered so that the currency with the
// largest circulation is at the beginning, so all we have to do
// is take the first one in the list that matches
this.code = cur;
break;
}
}
}
}
}
}
if (!currInfo || !this.code) {
this.code = this.locinfo.getCurrency();
currInfo = ilib.data.currency[this.code];
}
this.name = currInfo.name;
this.fractionDigits = currInfo.decimals;
this.sign = currInfo.sign;
if (typeof(options.onLoad) === 'function') {
options.onLoad(this);
}
})
});
},
/**
* Return the ISO 4217 currency code for this instance.
* @return {string} the ISO 4217 currency code for this instance
*/
getCode: function () {
return this.code;
},
/**
* Return the default number of fraction digits that is typically used
* with this type of currency.
* @return {number} the number of fraction digits for this currency
*/
getFractionDigits: function () {
return this.fractionDigits;
},
/**
* Return the sign commonly used to represent this currency.
* @return {string} the sign commonly used to represent this currency
*/
getSign: function () {
return this.sign;
},
/**
* Return the name of the currency in English.
* @return {string} the name of the currency in English
*/
getName: function () {
return this.name;
},
/**
* Return the locale for this currency. If the options to the constructor
* included a locale property in order to find the currency that is appropriate
* for that locale, then the locale is returned here. If the options did not
* include a locale, then this method returns undefined.
* @return {Locale} the locale used in the constructor of this instance,
* or undefined if no locale was given in the constructor
*/
getLocale: function () {
return this.locale;
}
};
module.exports = Currency;
Source