1 /*
  2  * Country.js - Country class to get country name corresponding to country code in locale assigned
  3  *
  4  * Copyright © 2017, 2018, JEDLSoft
  5  *
  6  * Licensed under the Apache License, Version 2.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at
  9  *
 10  *     http://www.apache.org/licenses/LICENSE-2.0
 11  *
 12  * Unless required by applicable law or agreed to in writing, software
 13  * distributed under the License is distributed on an "AS IS" BASIS,
 14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  *
 16  * See the License for the specific language governing permissions and
 17  * limitations under the License.
 18  */
 19 
 20 // !data ctryreverse
 21 
 22 var ilib = require("../index.js");
 23 var Utils = require("./Utils.js");
 24 var Locale = require("./Locale.js");
 25 var LocaleInfo = require("./LocaleInfo.js");
 26 var ResBundle = require("./ResBundle.js");
 27 
 28 /**
 29  * @class
 30  * Create a new country information instance. Instances of this class encode
 31  * information about country name.<p>
 32  *
 33  * The options can contain any of the following properties:
 34  *
 35  * <ul>
 36  * <li><i>locale</i> - specify the locale for this instance. Country names are provided
 37  * in the language of this locale.
 38  *
 39  * <li><i>onLoad</i> - a callback function to call when the country name data is fully
 40  * loaded. When the onLoad option is given, this class will attempt to
 41  * load any missing locale data using the ilib loader callback.
 42  * When the constructor is done (even if the data is already preassembled), the
 43  * onLoad function is called with the current instance as a parameter, so this
 44  * callback can be used with preassembled or dynamic loading or a mix of the two.
 45  *
 46  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
 47  * asynchronously. If this option is given as "false", then the "onLoad"
 48  * callback must be given, as the instance returned from this constructor will
 49  * not be usable for a while.
 50  *
 51  * <li><i>loadParams</i> - an object containing parameters to pass to the
 52  * loader callback function when locale data is missing. The parameters are not
 53  * interpretted or modified in any way. They are simply passed along. The object
 54  * may contain any property/value pairs as long as the calling code is in
 55  * agreement with the loader callback function as to what those parameters mean.
 56  * </ul>
 57  *
 58  * If the locale is not set, the default locale(en-US) will be used.<p>
 59  *
 60  * @constructor
 61  * @param options {Object} a set of properties to govern how this instance is constructed.
 62  */
 63 var Country = function (options) {
 64     var sync = true,
 65         loadParams = undefined,
 66         locale;
 67 
 68     if (options) {
 69         if (options.locale) {
 70             this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
 71         }
 72         if (typeof(options.sync) !== 'undefined') {
 73             sync = !!options.sync;
 74         }
 75         if (options.loadParams) {
 76             loadParams = options.loadParams;
 77         }
 78     }
 79 
 80     this.locale = this.locale || new Locale();
 81     new LocaleInfo(this.locale, {
 82         sync: sync,
 83         loadParams: loadParams,
 84         onLoad: ilib.bind(this, function (li) {
 85             this.locinfo = li;
 86             if (this.locinfo.getRegionName() === undefined) {
 87                 locale = 'en-US';
 88             } else {
 89                 locale = this.locale;
 90             }
 91 
 92             if (!this.codeToCountry) {
 93                 Utils.loadData({
 94                     name: "ctryreverse.json",
 95                     object: "Country",
 96                     locale: locale,
 97                     sync: sync,
 98                     loadParams: loadParams,
 99                     callback: ilib.bind(this, function(countries) {
100                         this.codeToCountry = countries;
101                         this._calculateCountryToCode();
102                         if (options && typeof(options.onLoad) === 'function') {
103                             options.onLoad(this);
104                         }
105                     })
106                 });
107             } else {
108                 this._calculateCountryToCode();
109                 if (options && typeof(options.onLoad) === 'function') {
110                     options.onLoad(this);
111                 }
112             }
113         })
114     });
115 };
116 
117 /**
118  * Return an array of the ids for all ISO 3166-1 alpha-2 code that
119  * this copy of ilib knows about.
120  *
121  * @static
122  * @return {Object} an object of country code that this copy of ilib knows about.
123  */
124 Country.getAvailableCode = function() {
125     var countries = new ResBundle({
126             name: "ctryreverse"
127         }).getResObj();
128 
129     return countries && Object.keys(countries);
130 };
131 
132 /**
133  * Return an array of country names that this copy of ilib knows about.
134  *
135  * @static
136  * @return {Object} an object of country code that this copy of ilib knows about.
137  */
138 Country.getAvailableCountry = function() {
139     var ret = [],
140         code,
141         countries = new ResBundle({
142             name: "ctryreverse"
143         }).getResObj();
144 
145     for (code in countries) {
146         if (code && countries[code]) {
147             ret.push(countries[code]);
148         }
149     }
150 
151     return ret;
152 };
153 
154 Country.prototype = {
155     /**
156      * @private
157      */
158     _calculateCountryToCode: function() {
159         var temp = this.codeToCountry,
160                 code;
161 
162         this.countryToCode = {};
163 
164         for (code in temp) {
165             if (code && temp[code]) {
166                 this.countryToCode[temp[code]] = code;
167             }
168         }
169     },
170 
171     /**
172      * Return the country code corresponding to the country name given.
173      * If the country name is given, but it is not found in the list of known countries, this
174      * method will throw an exception.
175      * @param {string} ctryname The country name in the language of the locale of this instance
176      * @return {string} the country code corresponding to the country name
177      * @throws "Country xx is unknown" when the given country name is not in the list of
178      * known country names. xx is replaced with the requested country name.
179      */
180     getCode: function (ctryname) {
181         if (!this.countryToCode[ctryname]) {
182             throw "Country " + ctryname + " is unknown";
183         }
184         return this.countryToCode[ctryname];
185     },
186 
187     /**
188      * Return the country name corresponding to the country code given.
189      * If the code is given, but it is not found in the list of known countries, this
190      * method will throw an exception.
191      * @param {string} code The country code to get the country name
192      * @return {string} the country name in the language of the locale of this instance
193      * @throws "Country xx is unknown" when the given country code is not in the list of
194      * known country codes. xx is replaced with the requested country code.
195      */
196     getName: function (code) {
197         if (!this.codeToCountry[code]) {
198             throw "Country " + code + " is unknown";
199         }
200         return this.codeToCountry[code];
201     },
202 
203     /**
204      * Return the locale for this country. If the options to the constructor
205      * included a locale property in order to find the country that is appropriate
206      * for that locale, then the locale is returned here. If the options did not
207      * include a locale, then this method returns undefined.
208      * @return {Locale} the locale used in the constructor of this instance,
209      * or undefined if no locale was given in the constructor
210      */
211     getLocale: function () {
212         return this.locale;
213     }
214 };
215 
216 module.exports = Country;
217